Git可以跟踪祖先分支和自己的分支吗



如果这是一个noob问题,很抱歉,但我已经搜索了一个小时以上,无法得到明确的答案。

我想做的是:

  • 签出远程分支git checkout origin/feature
  • 基于该分支git checkout -b feature_my_tweak创建本地分支
  • 所以现在我有一个feature的本地副本,名为feature_my_tweak,我可以玩它
  • commit -m "added my tweak"进行一些更改,所以现在我的本地feature_my_tweak已经偏离了feature
  • 现在执行git push --set-upstream origin feature_my_tweak将我的本地分支推送到远程服务器
  • 现在,一位同事在远程服务器上推送对feature的更改

此时,我希望能够使用git pull,并让git看到feature发生了更改,并将其拉入本地feature_my_tweak分支(获取/合并)。有没有办法自动做到这一点?或者,我必须手动执行git merge feature才能将该分支的所有更改获取到我的本地feature_my_tweaks分支?我想我认为有一种方法可以让一个分支跟踪另一个分支。

其中的另一部分是,我仍然希望能够对feature_my_tweaks进行本地更改,并且仍然可以对git push进行更改,并且只向上推到远程origin/feature_my_tweak分支,而不会污染origin/feature分支。

有办法做到这一切吗?据我所知,如果任何分支_A正在跟踪远程分支_B,则任何推送都将转到分支_B上,这是我不想要的。

我希望这是有道理的。

TL;DR

答案的简短版本可以归结为不,但没关系,不用担心,只需手动操作;这很容易。如果你有一个经常使用的特定分支,那么写一个脚本或Git别名来完成它

中等

要手动执行此操作,对于您想知道origin/feature上是否有新内容的分支B,与上游设置为origin/feature_my_tweaks的您自己的分支feature_my_tweaks相比,只需运行:

git rev-list --count --left-right feature_my_tweaks...origin/feature

这打印出两个数字。左边的数字是您在feature_my_tweaks上提交的不属于origin/feature的数量。右边的数字是在origin/feature上但不在feature_my_tweaks上的提交数。

如果第二个数字不为零,并且您愿意,现在可以运行git checkout feature_my_tweaks; git rebase origin/featuregit checkout feature_my_tweaks; git merge origin/feature

是否以及何时使用git rebase取决于您,但在重新定基后,您需要git push --force origin feature_my_tweaks(或git push --force-with-lease origin feature_my_tweaks)才能在origin处获得Git,以丢弃git rebase在将您的提交复制到新的和改进的提交时丢弃的旧提交。

Long

这里最大的问题可能是术语。跟踪这个词对你来说意味着什么?Git至少以三种不同的方式使用这个词,1所以其中最多有一种可以与你想的匹配。(可能没有一个完全匹配,这取决于你认为这里发生了什么。)

无论你使用哪个词,这里都是正确的思考方式:

  • 重要的是提交。每个提交都有一个唯一的哈希ID。每个地方的Git——这个存储库的每个克隆——都使用该提交的哈希ID。如果您有一个哈希ID为的提交,那么它就是同一个提交。如果你没有,你就没有承诺。

  • Git中的名称有多种形式。每个名称包含一个散列ID——只有一个!

    您使用的三种形式是:

    • 分支名称:masterdevelopfeaturefeature/one等等。这些都是您的,您可以随意使用。(不过,你可能会希望你的名字与其他人的名字相匹配,至少在不同的时候是这样。)

    • 标记名:v2.1等等。当你的是你的时,你的Git会尝试与其他Git存储库共享:如果你的Gits发现他们的有v2.1,而你没有,你的Git很可能会将他们的复制到你的。

    • 远程跟踪名称:origin/masterorigin/feature等等。Git调用这些的远程跟踪分支名称,有些人将其缩短为,但它们与分支名称不同。你的Git将这些从属于其他Git的分支名称。你让你的Git调用他们的Git,并从他们那里获得任何新提交。然后,你的Git会更新你的远程跟踪名称,使其与他们的名称相匹配。

      远程跟踪名称是通过将远程名称(在本例中为origin)粘贴在其分支的名称前面,并用斜线将它们隔开来构建的。这就是为什么origin/master"跟踪"它们的master,每次运行git fetch origin时都会更新2


    所有这些名称的工作原理相似。它们都有长形式:例如,refs/heads/mastermaster的全拼写,而refs/remotes/origin/masterorigin/master的全拼写。这就是Git知道哪一个是哪种名称的方式。Git通常会缩短显示给您的名称,去掉分支名称的refs/heads/部分,或远程跟踪名称的refs/remotes/部分。

注意,git fetchgit push的相反方向一样接近。看起来这些应该是pushpull,但由于历史事故3,它是pushfetch。Pull只是指:运行fetch,然后运行第二个Git命令,默认为git merge,以与当前分支的上游合并

获取和推送非常相似,但有两个关键区别:

  1. Fetch获取东西。您告诉您的Git:调用您以名称origin(或您在此处使用的任何远程名称)存储其URL的Git。您的Git调用该URL上的服务器,该服务器必须作为Git存储库进行应答。然后这个服务器告诉你它的分支名称和它们的提交,你的Git检查你是否有这些提交。如果没有,你的Git会要求他们的Git提交这些提交,如果你没有,他们的父提交,如果需要,他们的父母提交,等等。他们会给你他们所有的提交,你没有,你需要Git来完成这些提交。

    获得提交后,git fetch通过重命名其分支来更新远程跟踪名称

  2. 推送发送内容。和以前一样,你让你的Git调用另一个Git,用一个类似origin的远程名称。然后你的Git给他们提交,而不是从他们那里获得提交。但在这里,情况略有不同:

    • Git提供的提交是您推送的任何分支的提示提交。(如果你只是推送一个分支,那只是一个提交。)如果他们没有这些提交,你的Git必须提供这些提示提交的父级。(大多数提交只有一个父级,所以这是一个多提交。)如果他们没有这些,你的Git必须提供更多的父级,依此类推。通过这个过程,你的Git会找到他们的Git需要的所有提交,以便对你发送的提示提交有一个完整的了解:它的所有历史。

      当你提取时,他们提供所有他们的分支。不同之处在于:你只推你指定的分支。

    • 在你的Git发送了你有的任何提交,他们没有,他们需要完成你推送的分支的提示提交后,你的Git会礼貌地请求他们设置分支名称。

      当你获取时,你的Git会设置你的远程跟踪名称。您现在要求他们设置分支名称。

总结这些关键点:

  • fetch获取(默认情况下,除非您限制,否则全部)提交并从中分支,并将其分支重命名为您的远程跟踪名称
  • push发送(默认为一个)4分支提示提交(以及它的祖先,如果需要的话),然后要求他们设置分支名称

您可以让git push要求他们设置一个与您用于选择要发送的提交的名称不同的名称。例如:

git push origin test-xyz:new-feature

发送您的test-xyz分支(以及它的父级和其他祖先,如果需要)的提示提交,但要求他们设置或创建分支名称new-feature

因此:

。。。似乎如果任何分支A正在跟踪远程分支B,则任何推送都将转到分支B。。。

至少在默认情况下,这是完全错误的。(不过,push.default有一个设置,确实实现了这一点。)

这里还有很多要知道的,特别是这个git rev-list --left-right --count在做什么,以及提交"打开"意味着什么——或者更准确地说,从可以访问——一个分支名称,但在这一点上,我已经没有时间和空间了。


1特别是,文件可以被跟踪未跟踪程跟踪名称,具有上游集的分支被称为正在跟踪其上游。动词(现在分词形式)变成形容词,在前两种情况下修饰名称文件

2有一些方法可以运行有限的git fetch不会更新所有远程跟踪名称。例如,使用git pull有时会做到这一点。我更喜欢将git fetch和其他命令分开,而不是使用git pull获取然后运行另一个Git命令。

3提取后,通常希望集成您提取的,因此起初,git fetch是一个后端"管道"命令,不适合用户运行,git pull运行git fetch,然后运行git merge。这是在remotes作为一个概念存在之前,因此也是在远程跟踪名称存在之前,所以用户没有任何理由运行git fetch

随着时间的推移,Git增加了远程和远程跟踪名称,现在pull(获取和合并)和fetch(不合并)之间的区别变得既有用又重要。但名称pull已经被用于表示提取和组合,因此发生了这起特殊的历史事故。

4也就是说,这是Git2.0及更高版本中的默认设置。您可以使用push.default进行更改,2.0以上的Git版本默认为matching

当使用matching时,你的Git会询问他们的Git关于他们的分支名称,匹配你的匹配分支名称,并默认从你的匹配名称推送到他们的匹配名称。

如果你将此设置更改为upstream,你的Git会要求他们的Git根据你的分支的上游设置来设置分支,这就是你在问题中所假设的。

现代默认设置是simple,它要求将上游设置为同名的分支,这样,如果您的上游设置为其一侧的不同名称,则您端的git push就会立即失败。然而,您可以通过键入git push originbranch来轻松覆盖这一点,这与git push originbranch:branch的含义相同:要求他们的Git使用与您在Git中使用的分支名称相同的分支名称。

最新更新