特别说明 :
尽管我们要求使用命令行的形式进行git的操作,但在一些实际的使用或问题场景中,适当的结合
Git图形化界面操作工具
将更有帮助。
fetch(获取)和pull(拉取)的区别
在讲解本节内容前,你需要了解一个概念FETCH_HEAD
FETCH_HEAD简单来说就是一个存储指向从远端获取到的分支最新的commitid的集合的引用
听这个解释可能比较抽象,其实FETCH_HEAD本质上算是一个文件,它存储在本地仓库.git/FETCH_HEAD
# 进入.git
cd .git
# 列举当前目录下文件
ls -l
total 6496
-rw-r--r-- 1 gavin staff 1857 Mar 26 14:27 COMMIT_EDITMSG
-rw-r--r-- 1 gavin staff 2422 Mar 26 22:02 FETCH_HEAD
-rw-r--r-- 1 gavin staff 34 Mar 26 21:31 HEAD
-rw-r--r-- 1 gavin staff 41 Mar 26 18:53 ORIG_HEAD
-rw-r--r-- 1 gavin staff 41 Mar 26 14:27 REBASE_HEAD
-rw-r--r-- 1 gavin staff 795 Mar 26 21:28 config
-rw-r--r-- 1 gavin staff 73 Feb 23 2021 description
drwxr-xr-x 14 gavin staff 448 Feb 23 2021 hooks
-rw-r--r-- 1 gavin staff 2940194 Mar 26 21:31 index
drwxr-xr-x 4 gavin staff 128 Mar 17 13:58 info
drwxr-xr-x 4 gavin staff 128 Mar 17 13:58 logs
drwxr-xr-x 256 gavin staff 8192 Mar 26 22:02 objects
-rw-r--r-- 1 gavin staff 14698 Mar 26 21:28 packed-refs
drwxr-xr-x 7 gavin staff 224 Mar 26 21:08 refs
-rw-r--r--@ 1 gavin staff 309 Jan 6 01:47 sourcetreeconfig
# 使用编辑打开FETCH_HEAD
vim FETCH_HEAD
7d0ee867e02385ab89197f1c803a4b910085fdc2 branch 'dev' of url
2435dd179cf7edd0d7df66878094b1d930686fa1 not-for-merge branch 'build/nginx-shanghai' of url
d35230737eee391ef001f5210fb51ec2c1846125 not-for-merge branch 'dev/shanghai' of url
efe637e00924ac99285c2bd59833101ec71b3194 not-for-merge branch 'feature/shanghai-watermark' of url
c89ed4f0d14973e19a20b3db46ea82c3a25200f1 not-for-merge branch 'master' of url
# 可以看到此处第一列是各个远端分支最新的commitid
# 之所以这里会有这么多行的原因是,仅执行了git fetch
复制代码
了解以上的FETCH_HEAD
之后,我们开始具体讲讲获取(git fetch)和拉取(git pull)的区别
-
获取(git fetch)更新
假如在获取操作前是这样的,本地master上只有A<-B两次提交,在执行获取操作后,获取到了远端的两次更新C、D就变成了这样
仅仅是将remote的更新同步至repository,不涉及到workspace
因此当你正在编辑代码的时候,也可以进行获取操作,将不会有任何影响。
食用使用方法:
-
默认将remote的更新,全部取回本地:
# 默认将remote的更新,全部取回本地 git fetch git fetch origin 复制代码
-
获取指定的remote分支更新
# 获取remote的dev分支更新 git fetch origin dev/test * branch dev -> FETCH_HEAD # 此时指针FETCH_HEAD中仅包含会指向dev的最新commit-id 复制代码
-
获取指定的remote分支到本地指定分支
# 获取remote的dev分支更新到本地dev分支 * [new branch] dev -> dev # 此时指针FETCH_HEAD中仅包含会指向dev的最新commit-id,并且在本地创建新分支dev 复制代码
-
拉取(git pull)更新
其实拉取操作在前几步与获取是相同的,但是拉取操作会将本地分支合并
FETCH_HEAD
,多了一个合并的过程。从结果看pull = fetch + merge(/rebase)
拉取下来的更新,最后会被同步到workspace。因此,当你正在编辑代码即workspace“不干净”时,将可能会发生代码冲突或报错。
如何安全的拉取更新
在上一个问题中我们了解了拉取(git pull)操作最后会将更新同步至workspace,因此清理workspace的变更内容保证其”干净“的状态就是关键。
在这里通常会有两种做法来“清理”workspace:
方法一:通过贮藏(stash):
贮藏(stash)会处理工作目录的脏的状态——即跟踪文件的修改与暂存的改动——然后将未完成的修改保存到一个栈上, 而你可以在任何时候重新应用这些改动(甚至在不同的分支上)。
贮藏(git stash)会将当前workspace中所有被git跟踪监听(tracked)的文件保存至栈结构上。
-
使用贮藏前先查看当前workspace状态
# 查看工作区状态 git status # 一般可能会有以下输出 On branch dev Your branch is up to date with 'origin/dev'. Changes to be committed: (use "git restore --staged <file>..." to unstage) # 此处列出的是从工作区添加至暂存区的内容,可以被提交。 Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) # 此处列出的是工作区中已经被tracked的内容,可以通过add添加至index。 Untracked files: (use "git add <file>..." to include in what will be committed) # 此处列出的是未被git跟踪监听的文件,一般为新增的文件。需要使用add将其添加至index 复制代码
-
使用add将变更添加至index
# 快速添加所有的变化到index git add . # 查看工作区状态 git status On branch dev Your branch is up to date with 'origin/dev'. Changes to be committed: (use "git restore --staged <file>..." to unstage) # 此处列出的是从工作区添加至暂存区的内容,可以被提交。 复制代码
-
使用stash贮藏
# 使用贮藏 git stash Saved working directory and index state WIP on dev: 7d0ee867 test(测试): 贮藏测试 # 或者在贮藏的时候添加一段备注信息以便于记忆 git stash push -m '这是一段备注信息' Saved working directory and index state On dev: 这是一段备注信息 # 再次查看工作区状态 git status On branch dev Your branch is up to date with 'origin/dev'. nothing to commit, working tree clean # 此时会提示你workspace干净 复制代码
特别注意:
默认情况下,
git stash
会缓存下列文件: 1.添加到index的修改(staged changes)
2.Git跟踪的但并未添加到index的修改(unstaged changes)
但不会缓存一下文件:
1.在workspace中新的文件(untracked files)
2.被忽略的文件(ignored files)
git stash
命令提供了参数用于缓存上面两种类型的文件:使用
-u
或者--include-untracked
可以stash untracked文件。使用
-a
或者--all
命令可以stash当前workspace的所有修改。 -
拉取更新(git pull)
# 执行通常的拉取更新 git pull 复制代码
-
恢复之前的贮藏
# 查看当前贮藏栈 git stash list stash@{0}: On thales-device: 这是一段备注信息 stash@{1}: WIP on 12.7.4: 2de5796e docs(文档): 更新项目文档 # 其中类似 stash@{0} 的叫做贮藏单元索引 # 应用贮藏 # 1.快速应用最新的贮藏(栈顶) git stash pop On branch dev Your branch is up to date with 'origin/dev'. Changes to be committed: (use "git restore --staged <file>..." to unstage) # ... Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) # ... Dropped refs/stash@{0} (e571ba7595ea6fda3ea8c17332e1a21b160008ee) # 这个命令会直接应用贮藏栈顶单元的内容,并在执行成功后删除该贮藏单元 # 如果执行结果发生了错误(一般出现在工作区有变更且变更内容与贮藏单元中的发生冲突),会保留该贮藏单元。 # 或者使用下面的方式 # 应用贮藏 # 2.应用指定的贮藏(通过指定贮藏单元索引) git stash apply stash@{0} On branch dev Your branch is up to date with 'origin/dev'. Changes to be committed: (use "git restore --staged <file>..." to unstage) # ... Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) # ... # 此时不会删除stash@{0}的贮藏单元,需要手动删除 git stash drop stash@{0} Dropped stash@{0} (60ef88308ac81900249091f0e6ed394d36db3574) 复制代码
综上即通过贮藏的方式完成了拉取更新操作。
方法二:通过提交(commit)
我们也可以通过提交的方式,清理workspace。操作过程这里不再赘述。
特别说明:
根据提交规范,一般情况下通过提交(commit)方式清理工作区拉取更新,在开发的下游分支上可以使用,
在上游分支上禁止使用,或者在特殊情况下在非下游开发分支上使用此方法,需要在推送前压缩提交(squash commit)。
方法三:通过手动备份工作区的变更文件(手艺人专用)
大佬必备,我不会。略。
总结
欢迎大佬批评。