更改远程分支的本地引用

6
我有两个仓库绑定到我的工作目录。
git remote show origin
* remote origin
  Fetch URL: ssh://project.git/
  Push  URL: ssh://project.git/
  HEAD branch: master
  Remote branch:
    master tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

git remote show develop
* remote develop
  Fetch URL: ssh://projecttest.git/
  Push  URL: ssh://projecttest.git/
  HEAD branch: master
  Remote branch:
    master new (next fetch will store in remotes/develop)
  Local ref configured for 'git push':
    master pushes to master (local out of date)

据我所知,本地分支是通过以下方式引用远程分支的:
  • 远程 origin/master 指向本地分支 master
  • 远程 develop/master 指向本地分支 master
我想要实现的是更改 develop/master 远程分支的引用。我希望它指向本地分支 trunk
  • 远程 develop/master 应该指向本地分支 trunk
如何实现这一点?

1
没有所谓的“指向”。你可以有本地分支跟踪远程分支。从某种意义上说,本地分支就像是指向远程分支,但这并不恰当,因为指向意味着它们是相同的,但它们并不相同,而且你知道它们可能不同步。因此,在这里使用跟踪是合适的,而且是从本地到远程,而不是相反。 - janos
1
好的,我的答案对你来说不起作用,我也不确定原因,所以我将删除它,以防在未来混淆其他用户。 - joews
1个回答

16

在回答之前,我想更改这里的术语,因为您使用某些词汇的方式与大多数git文档使用的词汇和方式不匹配。

首先,让我们谈谈git存储库中提交生成的图表。

每次在存储库中创建提交时,必须指定以下项目。其中许多是隐含的;实际上,git commitgit merge(它们是两个主要的提交创建者)将为您查找所有这些内容:

  • 一个父提交或一组父提交
  • 作者和“作者日期”(通常与提交者相同)
  • 提交者和“提交日期”(通常与上面的作者相同)
  • “树”(通常从索引/暂存区获取),以及
  • 提交消息(通过-m参数或git commit启动编辑器等方式)

Git将所有这些内容合并并生成一个新的唯一SHA-1值 - 那些40个字符的难以处理的字符串,如9c4ea50db79d3ce6fe3abccf20f1af27abae45b2 - 就是提交(它通过SHA-1 ID将此提交对象存储在仓库中)。如果提交是普通提交,则具有一个父提交,git commit通过HEAD(git将其存储在.git目录中)查找该父提交,然后在git commit开始创建新提交之前。如果它是“根提交”,例如在新存储库中的第一次提交,则没有父提交。如果它是“合并提交”(由git merge创建),则具有至少两个父ID(这些来自git存储在.git目录中的文件)。存储在任何给定提交中的父项都是原始的SHA-1 ID。

因此,给定任何一个提交SHA-1 ID,您可以从存储库中读取提交并获取其父提交。阅读这些内容会获取它们的父项。继续直到达到根提交,您就可以绘制出可能包含分支点和合并点的图表:

o-o-o-o-o-o-o
   \     /
    o-o-o

这是一个包含十个提交“节点”(o)的提交图,其中一些节点分支出去,然后再合并回来。我用 - \/ 字符之间画了线来连接这些节点。

当我们使用 Git 时,我们通常称这些线为“分支”,这是一个合理的名称,但“分支”还有另一个含义。

接下来,让我们这样定义“分支”(或“本地分支”),这就是 Git 的真正工作方式:“分支”是一个名称,比如 masterdevelop,它存储一个 SHA-1 ID,并且具有另外一个特殊属性。这个特殊属性是,当我们新建一个提交时,存储的 ID 会发生改变,这样分支名称始终标识着分支的“末端”。

请记住,每个提交都有它的父ID,但没有任何子ID。当您创建新的提交时,新的提交“通过这些存储的ID”指向旧的提交,但旧的提交永远不会改变。只给出父ID很难找到子ID,但只给出子ID很容易找到父ID。因此,只要分支名称“指向”分支的“末端”,我们就可以轻松地找到“分支的其余部分”。所以 Git 简单地保证本地分支名称始终标识着分支的末端。

(很不幸,“分支”既意味着“存储分支末端ID的本地名称”,也意味着“从末端开始向后工作形成的提交链”,在什么情况下使用哪一个几乎总是很明显,但只是“几乎”。)

现在让我们定义“远程”:远程是你在本地 Git 仓库中创建的一个名称,用于记住关于另一个 Git 仓库的某些事情,通常是另一台机器上属于其他人的仓库。远程有一个url(或者有时有几个:例如remote.origin.url和可选的remote.origin.pushurl),这就是 Git 将从另一个仓库获取和推送的方式。

当然,另一个仓库有(本地)分支——分支名称指向最新提交的代码,而我们经常需要知道另一个仓库的本地分支(名称和最新提交的代码),或者更准确地说,上次我们检查时这些分支里的内容是什么(它们会过期,所以我们不时刷新它们)。这就是“远程分支”的概念。因此,我们可以通过以下示例来粗略定义“远程分支”:远程分支remotes/origin/master是本地分支master在远程origin上上次我们连接并询问“你的master里有什么?”时的副本。

更准确地说:每个远程分支remotes/R/B 是本地分支B在远程R上上次我们检查并保存的副本。

现在我们只需要另一个定义,即“跟踪分支”。跟踪分支是一种本地分支,我们已经告诉git有一个对应的远程分支,并且在该远程分支上有一个对应的分支名称。我们告诉git这两件事情的底层方法是使用git config配置两个字符串。例如,假设我们希望本地分支greyhound跟踪名为hare的远程racetrack上的分支。要在最低级别上执行此操作,我们可以这样做:

git config branch.greyhound.remote racetrack
git config branch.greyhound.merge hare

(为了使事情更加复杂 - 这似乎是一个历史性的偶然事件 - 实际跟踪是通过远程fetch配置映射的。但是remote.racetrack.fetch几乎总是设置为读取+refs/heads/*:refs/remotes/racetrack/* 。在很大程度上,我们可以假设refs/remotes/部分是不变的。)因此,再次,在最底层,当git想要查看greyhound是否已经追上hare时,它实际上是查看remotes/racetrack/harehare部分来自branch.greyhound.merge行,而racetrack部分来自branch.greyhound.remote

如果您省略remotes/部分,git将提供该部分。 因此,您通常可以只写origin/master,而不是remotes/origin/master。(如果您创建了歧义,例如如果您创建了一个名为origin的本地分支,则会有一些例外情况。但是只要您不这样做,就可以省略remotes/,人们和git通常都这样做。)

这里最后一个侧面说明,因为我有其他定义。特殊引用HEAD通常是“符号引用”。符号引用包含另一个引用的名称,通常是本地分支名称。这是git知道将本地分支调整到新的分支尖端的方法:如果您“在主分支上”,HEAD包含ref:refs/heads/master,因此git commit添加一个新提交并更新master 。如果HEAD包含一个原始SHA-1值,则git commit像往常一样添加新的提交,然后更改HEAD以具有新的SHA-1值,但没有本地分支名称指向新的提交。这就是“脱离HEAD”状态。


哇!现在回到手头的问题。您即将遇到的困扰目前正在被修复的git中。

您想要或需要:

  1. 在名为origin的远程,必须有一个带有master分支的git仓库
  2. 在名为develop的远程,必须有一个带有master分支的git仓库
  3. 在本地仓库中,您想要将master设置为跟踪origin/master的分支
  4. 在本地仓库中,您想要将trunk设置为跟踪develop/master的分支
  5. 当您使用git push origin命令时,您希望将master推送到origin的master,并且可能根本不推送trunk
  6. 当您使用git push develop命令时,您希望将trunk推送到develop的master,并且可能根本不推送master
  7. 当您使用git push命令(没有其他参数)时,您想要... 好吧,我不太确定您想要什么,但我会在下面猜测。

步骤1至4只需进行一次。如果master分支尚未在一个或两个仓库中创建,您可以执行以下操作:

git push -u origin master:master
git push -u develop trunk:master

(如果你的git push版本足够新,拥有-u),你可以一次完成所有操作。或者,如果你有这个选项,你可以使用--set-upstream,或者如果喜欢,完全手动跟踪使用git config。但无论如何,如果需要,请在远程仓库上创建分支,然后将它们捡起(作为本地仓库中的“远程分支”)并设置本地分支以“跟踪”远程分支。

接下来是剩余步骤,这些步骤可能会有点混乱。

在最近的git版本中(至少1.7开始),有一个配置设置push.default,它影响当你省略refspec时git push的工作方式,即步骤5到7中的push命令。你需要将其设置为upstream。这不是默认设置,所以必须运行:

git config push.default upstream

进行设置。

如果你运行裸的git push(如#7),git使用相同的跟踪分支信息来选择远程。所以,现在你已经改变了push.default,如果你在分支master上并运行git push,你实际上运行的是git push origin master:master,如果你在分支trunk上并运行git push,你实际上运行的是git push develop trunk:master

然而,如果你只是让push.default upstream设置完成它的工作,并在master上运行git push develop,那么你没有告诉它推送名为trunk的本地分支。所以,要使5和6起作用,你需要两个不同的git配置项。

如果你运行git push remote(没有refspec参数),git push查找remote.remote.push,如果设置了,就使用它。因此,你可以设置remote.origin.push master:masterremote.develop.push trunk:master。现在git push develop有一个设置,不需要使用push.default了。


谢谢!这是我见过的最好的答案! - pepuch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接