说一说git的几个命令remote、checkout、fetch、pull

我们从服务器上clone下来项目后,在项目的根目录都会自动生成一个.git目录,这个目录的结构如下:

其中的config文件包含了当前仓库在本地的源、分支等等信息,它的基本内容如下,我这里配置了两个remote:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:shinancao/Xib2ObjC.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "github"] //github是我给这个remote的命名
url = git@github.com:shinancao/Xib2ObjC.git
fetch = +refs/heads/*:refs/remotes/github/*
[branch "master"]
remote = origin
merge = refs/heads/master
[user]
email = shinancao666@163.com
name = shinancao

当然,config还可以配置其他项。

再说一下refs文件夹:

refs/remotes存放着当前各remote在本地的,以分支命名的文件,文件中记录着拉取时最近一次的commit id,使用git branch -r查看的就是它下面的所有分支。

refs/heads存放着本地已创建的分支,使用git branch查看的就是它下面的所有分支:

其实大部分git命令的结果到会反馈到.git中相应文件的改变,那么接下来看一下remotecheckoutfetchpull这几个命令到底改变了什么。

remote

remote修改的就是config中的[remote "xxx"]部分,添加一个叫testRemote的remote:

此时在config最下面多了几行,git remote -v列出的也是config中记录的所有remote:

checkout

testRemote对应的远程仓库git@github.com:shinancao/Xib2ObjC.git有两个分支mastertest,现在要基于master创建一个新分支test1

报错了,提示没有testRemote/master这个分支。实际上执行git checkout后,会在refs/下找testRemote文件夹,然后再找master文件,找不到就会报上面的错误。在checkout之前可以执行git branch -a查看一下refs/remotesrefs/heads下所有的分支,基于列出的分支创建新分支就不会有问题了。

如果直接checkout一个分支名,会先查找本地是否有该分支,如果没有就会查找refs/remotes下是否有匹配的分支:

有一点很重要,checkout是不会真的从远程服务器拉取下来什么的!所以像刚刚创建的test分支,它当前最新的代码取决于refs/remotes/github/test记录的commit id,并不能保证就是服务器上最新的代码。因为可能它很早之前就被拉取了,现在远程该分支又有了新的提交。

fetch

其实git的命令中能真正地从拉取回远程更新的只有fetchpullfetch可以把远程分支的更新取回,并且不会影响本地的代码。解决上面的报错,可以在checkout之前,用fetch拉取回testRemote的远程分支:

此时.git/refs/remotes下多了一个文件夹testRemote,用git branch -a也能查看到新的分支。

同时.git/FETCH_HEAD会记录下最近一次fetch回的每个分支对应的最新commit id,是否merge等。

1
2
6cfe07be9d3c3052cea92dd9d209e4cb4dfe1bdb	not-for-merge branch 'master' of github.com:shinancao/Xib2ObjC
6cfe07be9d3c3052cea92dd9d209e4cb4dfe1bdb not-for-merge branch 'test' of github.com:shinancao/Xib2ObjC

not-for-merge标记的是该分支是否已经被本地的分支合并过,当我再次执行git fetch testRemote时,可以看到.git/FETCH_HEAD变成如下:

1
2
6cfe07be9d3c3052cea92dd9d209e4cb4dfe1bdb		branch 'master' of github.com:shinancao/Xib2ObjC
6cfe07be9d3c3052cea92dd9d209e4cb4dfe1bdb not-for-merge branch 'test' of github.com:shinancao/Xib2ObjC

因为本地的test3分支就是基于testRemote/master创建的,并且最新代码和fetch回来的一样。

可以在用git fetch <remote的名字> <分支名>来拉取指定分支的更新,否则项目很大,分支很多,会导致在本地存储很多东西,占用磁盘空间。

pull

git fetch拉取回来的代码,还需要合并到本地的代码里,可以用git mergegit rebase

1
2
3
$ git merge testRemote/master
# 或者
$ git rebase testRemote/master

上面的命令在当前分支,合并testRemote/master

那其实git pull做就是先取回远程某个分支的代码,然后再与本地指定分支合并:

1
2
3
4
$ git pull origin next:master #取回origin主机的next分支,与本地的master分支合并
$ git pull origin next #与当前的分支合并
$ git pull origin #当前分支与远程分支存在追踪关系,可省略分支名
$ git pull #如果当前分支只有一个追踪分支,远程主机名也可以省略

上面的命令相当于,先执行git fetch,再执行git merge

1
2
$ git fetch origin
$ git merge origin/next

如果在git fetch后要使用git rebase合并,则要指定--rebase

1
$ git pull --rebase <remote的名字> <远程分支名>:<本地分支名>

参考链接

转载请注明出处:

作者:意林

原文链接:http://www.shinancao.cn/2019/04/01/Functional-JavaScript-Part1

–End–