这里的两个答案都是正确的,但我将描述底层机制,因为在我发现它是什么之前,我发现“跟踪”的整个概念非常神秘。
Git 将这个“跟踪”信息分成两部分:遥控器的名称——通常是单词 origin,就像你正在使用的那样——然后是 git 命令在那个遥控器上命令的名称 用于命名分支。1 换句话说,如果您有远程登录权限,并且您在那里登录并进入存储库,您可以运行git log master 来查看有什么已提交。
如果您查看您的.git/config 文件,您将看到,对于每个“跟踪”某物的本地分支,这两个部分。例如,假设您有一个名为 experiment 的本地分支,您已将其设置为跟踪 origin/master。这将导致:
[branch "experiment"]
remote = origin
merge = master
但是这个跟踪分支的东西还有一个部分:当你运行git fetch origin,并且在origin 上的分支master 上有一些新的东西,fetch 步骤会更新你的本地origin/master。这个名字——首先是远程名称origin,然后是斜线/,然后是远程出现的分支名称——你可以看到远程发生了什么。在您的git fetch 完成后,它会将远程的分支名称(及其分支提示的相应 SHA-1)复制到您的本地存储库,并使用前面的远程名称重命名它们。
实际上是git fetch 步骤更新了origin/master 等等,并且只有在完成之后,这个“跟踪”的东西才会有任何有用的效果。 Git 现在可以告诉你你领先和/或落后了一些提交。而且,您现在可以运行类似git log origin/master 之类的命令来查看那里发生了什么——或者更有趣的是,git log --oneline master..origin/master 来查看您还没有的“他们的”提交:本质上,“获取了什么”——以及@ 987654340@ 查看他们尚未提交的“您的”提交。 (如果您已经进行了合并或变基,那么要查看您的 fetch 带来了什么为时已晚,因为现在您拥有了他们当时拥有的东西,这是您的合并或变基的结果。)
这一切中最奇怪的是git pull。 git pull 命令实际上只是一个捷径,它首先运行git fetch,然后运行git merge(或者,如果你重定向它,git rebase)。要分别执行这些步骤,请运行 git fetch origin,然后运行 git merge origin/master 或 git rebase origin/master。由于历史原因,2git pull 使用 remote 的分支名称,在本例中为 master,而不是最终重命名为的名称在您的存储库中。
那么,以此为背景,让我们看看一些命令:
git fetch <em>remote</em>:这根本不需要任何分支名称。它调用给定的远程,询问它所有分支现在在哪里,并更新您的存储库,将所有这些更新记录在origin/ 名称下(以免影响您的任何本地分支)。换句话说,这会更新您的分支可能(或可能不会)跟踪的名称,但它不需要知道什么是跟踪什么或不跟踪什么。
git status:如果它说您“在分支 X”上,并且分支 X 正在跟踪 origin/<em>X</em>,git status 也可以告诉您是否有,在您的 X 上,提交不在 origin/<em>X</em> 上的提交,反之亦然。
git merge 和 git rebase:它们需要某种方式来知道要合并什么,或者要基于什么重新定位。你可以明确命名它,但是如果你告诉你的 git 你的分支X 正在跟踪origin/X,那么无论何时你在分支X、git merge 或git rebase 上都会知道该怎么做。
git branch --set-upstream-to origin/X:这是设置或更改您的 current 分支正在跟踪的内容的主要命令。换句话说,如果您现在在分支X 上,这将为您更新branch.X.remote 和branch.X.merge,这样您就不必使用两个单独的git config 命令。您也可以使用git branch --unset-upstream删除跟踪信息。
git push:如果你没有给它额外的信息,它会使用当前分支的“远程”——它的前半部分跟踪信息——来决定调用哪个远程。不管你是否给git push 一个远程名称,下一部分取决于你是否给它一个“refspec”。如果你不这样做,git push 使用 push.default 来决定使用什么 refspec。
等等,什么是 refspec?
第二个最简单的 refspec 形式只是两个分支名称,它们之间有一个冒号,例如 master:master。对于git push,左边的名字是你的分支名称,右边的名字是他们的——另一个git的——分支名称。如果您省略:,您将得到最简单的形式,其中远程端名称(在: 之后的名称)是由一个有点复杂的过程(在git push documentation 中描述)选择的,这实际上取决于更多配置变量以及是否设置了上游。
git push -u 呢?这只是一个方便的快捷方式:就像git branch --set-upstream-to 是执行两个git config 命令的快捷方式一样,git push -u <em>refspec</em> 是执行推送的快捷方式,然后也是执行git branch --set-upstream-to 的快捷方式。你必须给 push 一个 refspec 才能做任何有用的事情。
如果您给出像master 这样的“半参考规范”怎么办?好吧,如上所述,你的 git 选择给远程 git 的名称是通过一个复杂的过程找到的,但是 如果你还没有设置上游(如果你是首先做git push -u)它将与您的本地名称相同。所以git push -u origin master 可能“意味着”git push -u origin master:master,而 that 最后意味着git branch --set-upstream-to origin/master。
如果您提供更完整的 refspec,例如 git push -u origin experiment:feature,这会将您的 experiment 分支推送到 origin,要求 origin 将其称为 feature,然后执行 --set-upstream-to origin/feature。请注意,此时本地分支的上游名称与本地名称不同。 Git对此很好;只要确定你也是。 :-)
git 还有更多巧妙的技巧:
如果您运行 git checkout <em>branch</em> 并且 branch 尚不存在,并且有一个“明显的”远程跟踪分支,例如 @987654400 @,git 将创建一个新的本地 branch,它已经在跟踪 origin/<em>branch</em>。 (也就是说,本地分支会将其remote 设置为origin,并将其merge 设置为branch。)
如果你运行git branch <em>local-name</em> <em>remote-tracking-name</em>,git会自动设置本地分支来跟踪对应的远程跟踪分支。 (你可以配置 git 是否需要这个,但这是默认设置。)
总结:git fetch 更新跟踪使用的内容(origin/* 条目,用于远程 origin)。一旦完成——包括是否使用运行git fetch3的git pull 完成——然后您会看到来自git status 等命令的更多信息;和git rebase 之类的命令使用它来了解如何进行变基,而无需您告诉它更多信息。
还有一个更有趣的转折:任何分支的“上游”都可以在您自己的本地存储库中。为此,您将该分支的remote 设置为.(文字点),并将merge 设置为分支的名称。您不必知道如何执行此操作,因为您可以执行 git branch --set-upstream-to master,例如,让您当前的分支跟踪您自己的 master。
“传入”和“传出”
Mercurial 用户可能想知道如何获得hg incoming 或hg outgoing 的效果。前者告诉你上游有什么,你没有。后者告诉你你有什么,而他们没有。事实证明,这在现代 git 中很容易做到,因为 git 有一个特殊的语法,@{u},可以找到当前分支的上游。
换句话说,如果您在master 和master 跟踪origin/master,@{u}(您可以拼写为@{upstream})只是写origin/master 的另一种方式。所以origin/master..master 只是写@{u}..master 的更长的方式。 而且,如果你在master,HEAD 也命名为master,并且省略分支名称告诉 git 使用HEAD,所以@{u}.. 就足够了。
如上所述,在适当的遥控器上运行git fetch 后,您可以使用git log 来查找“他们有什么你没有”和“你有什么他们没有”。您必须运行此git fetch 步骤(并且不希望此时发生合并或变基)。
所以:
git config --global alias.incoming '!git fetch && git log --oneline ..@{u}'
git config --global alias.outgoing '!git fetch && git log --oneline @{u}..'
(在某些 shell 中,您可能需要在 ! 前面加上 \ 或其他引用技巧,通过运行 git config --global --edit 在编辑器中插入别名可能更容易)。
当然,您可以将--oneline 部分更改为您喜欢的任何选项。 (而且我喜欢将git fetch 步骤留给我自己手动运行,例如,这会将别名简化为alias.incoming = log --oneline ..@{u}。4 这主要是为了避免不断纠缠上游。)
1如果你保持你的分支名称和他们的一样,你就没有机会看到这个。但是一旦你开始大量使用分支,你可能会发现几个分支都跟踪相同的上游,然后这真的很重要。
2git pull 实际上早于远程和远程跟踪分支。因此,它仍然有各种各样的怪异。
3如果您的 git 版本早于 1.8.4,当 git pull 运行 git fetch 时,fetch 步骤不会更新远程跟踪分支机构。这是一个功能,但它是一个不好的功能,并且更新了较新的 git 版本。不过,这确实意味着,如果你有一个旧的 git,你应该小心使用 pull 脚本:这很不方便。
4在编辑中修复:我不小心写了alias.incoming = git log ...。 Git 别名被假定为其他 git 命令(如 log),因此您希望省略 git 部分,除非整个别名以感叹号开头指向!,在这种情况下,整个别名将传递给shell 以运行。我现在实际上忘记了,当所有命令都拼写为git-log、git-branch、git-fetch 等时,别名是如何起作用的,但它一定不那么复杂...... :-)