如果这是一个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/feature
或git 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——只有一个!
您使用的三种形式是:
分支名称:
master
、develop
、feature
或feature/one
等等。这些都是您的,您可以随意使用。(不过,你可能会希望你的名字与其他人的名字相匹配,至少在不同的时候是这样。)标记名:
v2.1
等等。当你的是你的时,你的Git会尝试与其他Git存储库共享:如果你的Gits发现他们的有v2.1
,而你没有,你的Git很可能会将他们的复制到你的。远程跟踪名称:
origin/master
、origin/feature
等等。Git调用这些的远程跟踪分支名称,有些人将其缩短为,但它们与分支名称不同。你的Git将这些从属于其他Git的分支名称。你让你的Git调用他们的Git,并从他们那里获得任何新提交。然后,你的Git会更新你的远程跟踪名称,使其与他们的名称相匹配。远程跟踪名称是通过将远程名称(在本例中为
origin
)粘贴在其分支的名称前面,并用斜线将它们隔开来构建的。这就是为什么origin/master
"跟踪"它们的master
,每次运行git fetch origin
时都会更新2
所有这些名称的工作原理相似。它们都有长形式:例如,refs/heads/master
是master
的全拼写,而refs/remotes/origin/master
是origin/master
的全拼写。这就是Git知道哪一个是哪种名称的方式。Git通常会缩短显示给您的名称,去掉分支名称的refs/heads/
部分,或远程跟踪名称的refs/remotes/
部分。
注意,git fetch
与git push
的相反方向一样接近。看起来这些应该是push
和pull
,但由于历史事故3,它是push
和fetch
。Pull只是指:运行fetch,然后运行第二个Git命令,默认为git merge
,以与当前分支的上游合并
获取和推送非常相似,但有两个关键区别:
Fetch获取东西。您告诉您的Git:调用您以名称
origin
(或您在此处使用的任何远程名称)存储其URL的Git。您的Git调用该URL上的服务器,该服务器必须作为Git存储库进行应答。然后这个服务器告诉你它的分支名称和它们的提交,你的Git检查你是否有这些提交。如果没有,你的Git会要求他们的Git提交这些提交,如果你没有,他们的父提交,如果需要,他们的父母提交,等等。他们会给你他们所有的提交,你没有,你需要Git来完成这些提交。获得提交后,
git fetch
通过重命名其分支来更新远程跟踪名称。推送发送内容。和以前一样,你让你的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中使用的分支名称相同的分支名称。