我知道git pull
实际上是git fetch
和git merge
的结合,它基本上将远程仓库中的内容带入到了本地仓库中。
- 但是,这是否意味着在使用
git pull
之后,我的工作树将与远程仓库相同? - 我发现有些情况下,使用
git pull
并没有改变本地仓库或创建任何新的提交。这是什么原因? git pull
只对索引进行更改,这有意义吗?- 如果有意义,那么我如何使索引中的更改向前移动到工作树?
我知道git pull
实际上是git fetch
和git merge
的结合,它基本上将远程仓库中的内容带入到了本地仓库中。
git pull
之后,我的工作树将与远程仓库相同?git pull
并没有改变本地仓库或创建任何新的提交。这是什么原因?git pull
只对索引进行更改,这有意义吗?确切地这一部分真的很难。通常说,git pull
运行 git fetch
然后是 git merge
或者 git rebase
,并且事实上,git pull
曾经是一个 shell 脚本,现在是一个 C 程序,它确实直接运行了 git fetch
,但现在它直接调用实现 git fetch
的 C 代码。
然而,下一步非常棘手。此外,在评论中,你补充道:
[fetch] 从远程仓库获取更改。它把它们放在哪里?
要正确理解这一点,您必须了解 Git 的对象系统。
git fetch
每个提交记录都是一种独立的实体。每个提交记录都有一个唯一的哈希 ID:b06d364...
或其他。该哈希 ID 是该提交记录内容的加密校验和。例如:
$ git cat-file -p HEAD | sed 's/@/ /g'
tree a15b54eb544033f8c1ad04dd0a5278a59cc36cc9
parent 951ea7656ebb3f30e6c5e941e625a1318ac58298
author Junio C Hamano <gitster pobox.com> 1494339962 +0900
committer Junio C Hamano <gitster pobox.com> 1494339962 +0900
Git 2.13
Signed-off-by: Junio C Hamano <gitster pobox.com>
git cat-file -p v2.13.0^{commit}
以查看相同的数据。注意:标签v2.13.0
转换为074ffb61b4b507b3bde7dcf6006e5660a0430860
,这是一个标签对象;标签对象本身引用提交b06d364...
:$ git cat-file -p v2.13.0
object b06d3643105c8758ed019125a4399cb7efdcce2c
type commit
tag v2.13.0
[snip]
git fetch
或git push
期间看到Git计数和压缩的objects
。
parent
行告诉哪个提交(对于合并,是多个提交)是该特定提交的前任。为了拥有完整的提交集,Git还必须拥有父提交(--shallow
克隆可以故意省略各种父级,其ID记录在“浅移植”的特殊文件中,但正常克隆始终会拥有所有内容)。git cat-file -p <hash>
,例如,以模糊的人类可读格式显示它们。(大多数情况下,除了解压缩外,几乎不需要做任何事情,尽管树形对象具有必须首先格式化的二进制数据。)git fetch
——或者让git pull
代替您运行时——您的Git会从另一个Git获取一些初始对象的哈希ID,然后使用Git传输协议来确定需要哪些其他对象才能完成您的Git仓库。如果您已经拥有某个对象,则无需再次获取它,并且如果该对象是提交对象,则也不需要其任何父对象。1因此,您只获得您还没有的提交(和树和blob)。然后,您的Git将这些内容填充到您的存储库的对象数据库中。FETCH_HEAD
文件中。如果您的Git版本至少为1.8.4,则此时它还会更新任何相应的远程跟踪分支名称,例如它可能会更新您的origin/master
。git fetch
,则您的Git将遵守所有常规的refspec更新规则,如 git fetch文档所述。这是由git pull
传递的附加参数阻止了某些操作,具体取决于您的Git版本。)git fetch
将这些对象存储在Git的对象数据库中,可以通过它们的哈希ID检索。它将哈希ID添加到.git/FETCH_HEAD
(始终),并经常更新一些引用 - refs/tags/
中的标签名称以及refs/remotes/
中的远程跟踪分支名称。
1除了将一个浅克隆(unshallow)变成非浅克隆之外。
git pull
的其余部分运行git fetch
会获取对象,但不会将这些对象合并到任何你的工作中。如果你想要使用获取的提交或其他数据,你需要进行第二步操作。
你可以执行的两个主要操作是git merge
或git rebase
。最好的理解它们的方法是在其他地方阅读有关它们的内容(其他SO文章、其他文档等)。这两个命令都很复杂,而且git pull
有一个特殊情况没有被这两个命令覆盖:特别地,你可以将git pull
拉取到一个不存在的分支中。你有一个不存在的分支(Git也称之为孤立分支或未出生分支),有两种情况:
git checkout --orphan newbranch
之后git merge
也不是一个好主意。现在git rebase
命令有了一个自动存储选项(rebase.autoStash
),因此您可以让Git自动运行git stash save
将这些未提交的工作创建为一些离线分支提交。然后再运行rebase,之后Git可以自动应用并删除存储。
git merge
命令没有这个自动选项,但是您当然可以手动执行。git stash
实际上创建提交的事实自然相符)。您可以在任何时候运行git fetch
,因为它只会向对象数据库添加新对象;但是当索引处于此状态时,您无法合并或重新设置基础。
- 但是,“git pull”后,我的工作区是否与远程 repo 相同?
不一定。你在拉取的分支上有任何本地提交将会与上游的更改合并。使用 git pull --rebase
,将本地更改放置在上游提交之上。如果没有使用 --rebase
,你可能会得到一些非常奇怪的合并路径。
- 我发现有些情况下执行“git pull”不会改变本地 repo 或者创建新的提交?
如果上游没有新的提交,本地副本也不会发生任何变化。
- “git pull”仅对索引进行更改,这有意义吗?
据我所知,不是这样的。也许如果它无法与你的本地提交合并,但那么你至少应该在过程中得到一些错误提示。
- 如果是这样的话,如何使索引中的更改向前移动到工作树中?
git pull
:) 或者 git rebase <upstream> <branchname>
。这将基于分支 <branchname>
上游提交的基础上,将本地提交变基。
fetch
命令,然后将远程跟踪分支checkout
到本地分支上,并对其进行变基操作。但这样会重写历史记录。 - QuentinNo :
if you have local commits, which you haven't pushed yet, or some indexed changes (git add
ed), you will still have those local changes on top of the last public commit (or merged with the last public commit) ;
Yes :
if nothing was pushed to the remote repo since your last git pull
, you are already up to date, so nothing will change ;
No :
if you see changes in the index after a git pull, the files were already indexed before you ran git pull
;
git
already will, with the following caveat : if one of your indexed files should be updated by the merge
, git will not perform the merge, and print a message :
error: Your local changes to the following files would be overwritten
by merge:
bb
Please commit your changes or stash them before you merge.
Aborting
In that case : you should probably create a commit from your index, and run git merge origin/current/branch
(or git rebase origin/current/branch
) to incorporate the remote modifications with your local modifications.
git fetch [origin]
的默认行为是读取存储在远程仓库中的所有分支,并更新存储在 refs/remotes/[origin]/*
下的所有本地引用。
然后,您可以在所有标准的 git
命令中使用 origin/branch/name
作为有效的树形名称:
# difference with remote "master" branch :
$ git diff HEAD origin/master
# history of remote branch "feature" alongside your local branch "feature" :
$ git log --oneline --graphe feature origin/feature
# merge changes from remote "master" :
$ git merge origin/master
# rebase your local commits on top of remote "develop" branch :
$ git rebase origin/develop
# etc ...
你还可以使用一种快捷方式表示“与我当前所在分支关联的远程分支”:@{u}
$ git diff @{u}
$ git log --oneline --graph HEAD @{u}
$ git merge @{u}
$ git rebase @{u}
# etc ...
“git pull” 究竟是做什么的?
在执行 git fetch
后,git
会更新一个特殊的引用,名为 FETCH_HEAD
,它通常与当前分支的 @{u}
匹配;
git pull
执行的是 git fetch && git merge FETCH_HEAD
。
我尝试在上面的段落中用自己的话解释了 git fetch
。