【问题标题】:learning Git: tracking vs. setting upstream (-u) for remotes?学习 Git:跟踪与为遥控器设置上游(-u)?
【发布时间】:2015-11-03 15:35:23
【问题描述】:

我正在学习 Git,并试图了解“跟踪”远程与定义“上游”关系(使用 -u 标签)之间的区别。

对于master to origin/master,我一直在使用

git push origin master

这似乎自动定义了一种关系(虽然我不确定它是什么)。

对于分支,我一直在使用

git branch newbranch
git push -u origin newbranch

我知道这设置了上游关系,但我还是不明白其中的区别。

有人能解释一下区别吗?

【问题讨论】:

  • 简短回答:没有区别。 Longer 回答:你跟踪一个上游。优步很长,但信息量很大的答案:喝杯咖啡和read on

标签: git github


【解决方案1】:

这里的两个答案都是正确的,但我将描述底层机制,因为在我发现它是什么之前,我发现“跟踪”的整个概念非常神秘。

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 pullgit pull 命令实际上只是一个捷径,它首先运行git fetch,然后运行git merge(或者,如果你重定向它,git rebase)。要分别执行这些步骤,请运行 git fetch origin,然后运行 ​​git merge origin/mastergit 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 mergegit rebase:它们需要某种方式来知道要合并什么,或者要基于什么重新定位。你可以明确命名它,但是如果你告诉你的 git 你的分支X 正在跟踪origin/X,那么无论何时你在分支Xgit mergegit rebase 上都会知道该怎么做。

  • git branch --set-upstream-to origin/X:这是设置或更改您的 current 分支正在跟踪的内容的主要命令。换句话说,如果您现在在分支X 上,这将为您更新branch.X.remotebranch.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 fetch3git pull 完成——然后您会看到来自git status 等命令的更多信息;和git rebase 之类的命令使用它来了解如何进行变基,而无需您告诉它更多信息。

还有一个更有趣的转折:任何分支的“上游”都可以在您自己的本地存储库中。为此,您将该分支的remote 设置为.(文字点),并将merge 设置为分支的名称。您不必知道如何执行此操作,因为您可以执行 git branch --set-upstream-to master,例如,让您当前的分支跟踪您自己的 master

“传入”和“传出”

Mercurial 用户可能想知道如何获得hg incominghg outgoing 的效果。前者告诉你上游有什么,你没有。后者告诉你你有什么,而他们没有。事实证明,这在现代 git 中很容易做到,因为 git 有一个特殊的语法,@{u},可以找到当前分支的上游。

换句话说,如果您在mastermaster 跟踪origin/master@{u}(您可以拼写为@{upstream})只是写origin/master 的另一种方式。所以origin/master..master 只是写@{u}..master 的更长的方式。 而且,如果你在masterHEAD 也命名为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-loggit-branchgit-fetch 等时,别名是如何起作用的,但它一定不那么复杂...... :-)

【讨论】:

    【解决方案2】:

    git push origin master 明确表示“将本地分支 'master' 推送到名为 'origin' 的远程分支”。这并没有定义持久关系,它只是执行一次推送。请注意,假定远程分支被命名为“master”。

    git push -u origin master 是一样的,只是它首先在本地分支“master”和名为“origin”的远程分支之间添加了持久跟踪关系。和之前一样,假设远程分支被命名为“master”。

    如果您已经使用-u 进行了推送,则关系已经定义。以后你可以简单的说git push或者git pull,git会自动使用定义好的远程跟踪分支,不需要明确告知。

    您可以使用git branch -vv 查看您的跟踪关系,这将列出您的本地分支及其当前的 HEAD 提交,如果已设置,还会列出远程跟踪分支。这是一个例子。

    $ git branch -vv
    * master                   58a0d68 [origin/master] Fix CSS regression bug
      migration_tool           2a24ff7 [origin/migration_tool] [#906] Fix table layout problem
      topic-ajax-timeouts      fe854f2 Adjust timeouts to be more realistic
    

    这里显示了 3 个分支以及它们当前的 HEAD 提交和提交消息。在前两个上,指定了一个跟踪分支,但第三个分支没有跟踪远程分支。

    【讨论】:

    • 那么tracking和upstream是一回事吗? git push origin master 是否隐式包含 -u 标签?
    • @eabates 不,这不是隐含的。我编辑了我的答案以更清晰,并展示您如何查看您当前的上游关系。
    【解决方案3】:

    “跟踪”和“上游”是相关术语。 “跟踪”意味着将本地存储库的分支与上游分支相关联,即位于远程存储库中的分支。例如,

    git push -u origin newbranch
    

    设置本地newbranch 的跟踪信息,以便将位于名为origin 的远程仓库中的newbranch(即本地仓库知道的昵称origin 的远程仓库)视为本地newbranch...的上游分支并执行推送。

    如果你通过克隆获得本地仓库,则无需运行

    git push -u origin master
    

    因为origin/master 已经设置为master 的上游分支。换句话说,master 已经设置为跟踪 origin/master

    【讨论】:

    • 所以它们基本上是一回事?有没有默认关系,比如写git push origin master的时候?那是否将原点/主控置于上游和本地主控置于下游?
    • @eabates 不,除了将master 推送到origin 之外,它什么都不做。
    猜你喜欢
    • 1970-01-01
    • 2014-10-28
    • 2012-11-24
    • 2020-09-11
    • 2020-09-26
    • 2011-11-25
    • 2011-05-16
    • 2023-01-14
    • 2020-05-31
    相关资源
    最近更新 更多