在之前的内容中,有学习总结基本操作、修改撤回、分支 branch、贮藏 stash 、标签 tag 以及部分远程操作。
涉及了 Git 的几个工作区,工作目录、暂存区、本地仓库 以及 远程仓库
他们之间的用 Git 操作的关系可总结如下图:
从本期开始进入Git 的进一步学习
本想先介绍下远程仓库的,有几个好友说很期待看 fetch,所以进阶首篇,学习 fetch 命令,也会区分下 git fetch
和 git pull
的使用。
fetch 概念
git fetch
和 git pull
是两个比较相近的命令
git pull
我们已经知道,是从远程仓库拉取最新的内容到本地,在 pull 的过程中如果没有冲突,Git 会自动将最新的内容合并到本地仓库中,如果有冲突也是需要解决冲突。
fetch 可译为获取,官方给出如下说明
git fetch
:Download objects and refs from another repositorygit pull
:Fetch from and integrate with another repository or a local branch
fetch
直译过来就是从另一个存储库下载对象和引用(refs),而 pull
可以看到除了 fetch
之外 还有 integrate(集成),所以大家习惯理解成 pull = fetch + merge,用于本地存储库与远程存储库同步。
fetch & pull
我们来具体消化下上面的解释
在我们进行 commit
的时候,Git 会自动生成一个 commitid(即 SHA1 校验和),此时如果进行 push
操作,远程仓库的对应分支会同步这个 commitid 的内容,同时本地也会保存一份远程副本。
在入门(一)的时候,我们有提到过,由 Git 管理的目录会自动生成一个.git
文件夹,.git
文件夹包含着 Git 操作所需要的内容。
在这个文件夹中就保存了 Git 本地仓库 以及 跟踪的远程仓库的 commitid。
在上图目录 .git/refs
里面有几个文件夹,比如
- heads 存放着本地分支
- remotes 正在跟踪的远程仓库
- tags 标签
通过 git branch -a
查看分支,和 .git
文件夹本地以及远程分支是一致的。
这些文件里面存放的就是对应分支指向的 commitid (如下图)
HEAD 文件里面存放的是当前的指向(这里也和前面 git branch -a
查看的内容是一样的)
下面进入正题
- 假设当前操作的分支为 master 分支
使用 Git 进行提交的时候,会使用 add
和 commit
命令将修改的文件提交 Git 版本库,同时在 heads/master(即,本地的当前分支)生成对应的 commitid(图示中用 cid 表示)。然后,通过 push
同步到远程仓库对应的分支中,同时本地也会保存一份远程的副本,即 origin/master
如果远程仓库被其他协同者更新了,若要更新本地内容,使用 push
,此时会从远程获取的最新内容并合并到本地
与 git pull
不同的是 fetch
是从远程仓库 origin 的 master 分支获取最新的内容到 origin/master 分支上,可以比较本地 master 和 origin/master 的差别之后,操作者视情况决定是否进行合并
fetch 语法
git fetch
命令语法,详见官方文档 https://git-scm.com/docs/git-fetch
git fetch [<options>] [<repository> [<refspec>…]]
git fetch [<options>] <group>
git fetch --multiple [<options>] [(<repository> | <group>)…]
git fetch --all [<options>]
常用语法
git fetch
该命令将某个远程服务器的更新,全部取回本地。默认情况下,git fetch
取回所有分支的更新。
如果只想获取特定分支的更新,可以指定分支名git fetch <repository> <branch>
如git fetch origin master
这里有个陌生的标识 FETCH_HEAD
FETCH_HEAD 是什么?
FETCH_HEAD
指的是某个 branch 在远程服务器上的最新状态
每一个执行过 fetch 操作的项目都会在 .git/FETCH_HEAD
文件中保存一个 FETCH_HEAD
列表,其中每一行对应着远程服务器的一个分支,而文件第一行就是当前分支指向的 FETCH_HEAD
执行 git fetch origin master
之后, FETCH_HEAD
就指向了 master 分支,
- 如果没有指定远程分支,那么当前操作的分支对应的远程分支作为默认的
FETCH_HEAD
(假设当前在develop 分支 执行命令git fetch
,那么FETCH_HEAD
指向的就是远程端 develop 分支的最新提交) - 如果远程没有对应分支,也就没有
FETCH_HEAD
- 如果指定了分支,就将这个分支作为
FETCH_HEAD
这里先插入介绍下可能用到的命令
便于实战,提前做些数据
- 将之前创建的 feat_6 分支 push 到了远程服务器
- 同时通过 GitHub 创建了一次描述为 “learn fetch” 的提交(添加了一行 add line : learn fetch),模拟多人协同时,远程仓库有更新
执行一次 git fetch origin feat_6
FETCH_HEAD 和 origin/feat_6 都更新了
- fetch 之后,如果想查看 fetch 日志,可以使用
git log FETCH_HEAD
这里便于查看 log 的区别,将本地分支切换为 feat_6
最新的一次提交就是从远端获取的,但是是本地没有的日志
- 也可以使用 diff 查看具体的改动
git diff FETCH_HEAD [HEAD]
查看的是HEAD
(当前分支)相对于FETCH_HEAD
的区别- 如果使用
git diff HEAD FETCH_HEAD
查看的就是,FETCH_HEAD
相对于HEAD
的改变,注意和上边做区分
讲到这里,对于 pull = fetch + merge 的说法便是指在同一分支
git pull
等同于
git fetch
git merge FETCH_HEAD
除了上面的内容,在 fetch 获取远程更新之后,可以在它的基础上创建一个新的分支,比如:git checkout -b newBranch origin/master
当然也可以 merge 或 rebase 在本地分支上合并远程分支git merge origin/master
或 git rebase origin/master
(rebase 我们后面也会介绍到)
后记
fetch 这个功能在我平时的工作中也是疏于使用,但是不论使用 GitHub 还是 SourceTree 都可以看到 Fetch,Fetch 之后会提示是否可 pull
from:https://www.jianshu.com/p/b0a00b4bed89