重命名本地和远程Git仓库的主分支

884

我有一个跟踪远程分支origin/master的本地分支master

我想把它们都重命名为master-old,包括本地和远程。这可以实现吗?

对于那些跟踪origin/master(并总是通过git pull更新他们的本地master分支)的其他用户,如果我重命名了远程分支,会发生什么?
他们的git pull是否仍然能够正常工作,还是会抛出找不到origin/master的错误?

接下来,我想创建一个新的master分支(包括本地和远程)。在我这样做之后,其他用户如果执行git pull会发生什么?

我想这样做会带来很多麻烦。有没有一种简单的方法来实现我的目标?或者我应该让master保持原样并创建一个新的分支master-new,然后在那里继续工作?


2
被接受的答案中给出的方法适用于任何名称的分支,但是由于 Git 中 master 分支的默认特殊角色,所以注意事项不适用。 - kynan
3
@kynan:我想我不太明白。在主分支上适用哪些注意事项,而其他分支上则不适用?如果有一个名为xy的分支,并且其他人已经跟踪该分支,那会有什么不同吗? 我认为我不理解。主分支有哪些需要注意的地方,在其他分支上却不需要呢?如果有一个名为xy的分支,而其他人已经追踪了该分支,那会有什么不同呢? - Albert
4
注意,通常情况下无法删除远程主分支。但这不适用于Aristotle的回答,因此您可能希望将其标记为被接受的答案。您是正确的,任何git push -f都会影响从任何远程跟踪分支进行pull的能力。 - kynan
1
你可以创建一个名为master-old的新分支,它指向之前master分支所在的同一提交。然后,通过使用“ours”策略进行合并,你可以将master分支覆盖为你的新更改。当远程不允许非快速转发更改时,进行合并操作是可行的。这也意味着其他用户将不会有强制更新。 - dnozay
1
@kynan 只有当 master 是唯一存在的分支时,它才是特殊的。一旦你拥有超过一个分支,所有分支就处于平等地位。 - jub0bs
显示剩余2条评论
17个回答

649

最接近重命名的方法是在远程上删除后,再重新创建。例如:

git branch -m master master-old
git push remote :master         # Delete master
git push remote master-old      # Create master-old on remote

git checkout -b master some-ref # Create a new local master
git push remote master          # Create master on remote

然而,这种方法有很多限制。首先,没有现有的检出将知道重命名 - Git不会尝试跟踪分支重命名。如果新的master还不存在,则git pull会报错。如果已经创建了新的master,则pull将尝试合并mastermaster-old。因此,除非您得到之前检出存储库的每个人的合作,否则通常不是一个好主意。

注意:较新版本的Git默认情况下不允许您远程删除主分支。您可以通过在remote存储库上设置receive.denyDeleteCurrent配置值为warnignore来覆盖此设置。否则,如果您准备立即创建新的主分支,请跳过git push remote:master步骤,并在git push remote master步骤中传递--force。请注意,如果您无法更改远程存储库的配置,则无法完全删除主分支!

这个限制仅适用于当前分支(通常为master分支);任何其他分支都可以按上述方法删除和重新创建。


3
分支只是一个(名称,哈希值)对-没有更多或更少。分支上有reflog,但这从未向远程客户端公开。 - bdonlan
147
在从远程删除主分支之前,我希望在远程创建一个主分支备份。我只是有点多疑。 - Adam Dymitruk
6
以下是亚里士多德的回答,可以让你不必删除主文件就能完成这个操作,因此我认为这更可取。 - Clay Bridges
13
如果您使用new-branch-nameold-branch-name代替master/master-old,那么这将是一个通用的问题,这样做会更加清晰和安全。 - Jaider
2
如果被删除的分支(这里是主分支)没有被其他分支引用,Git 可能会垃圾回收该“分支”上的所有提交。一些 Git porcelain 命令会触发垃圾回收。因此,请先创建新名称(指向相同的提交),然后再删除旧名称。 - Robert Siemer
显示剩余9条评论

270

假设您当前在master分支上:

git push origin master:master-old        # 1
git branch master-old origin/master-old  # 2
git reset --hard $new_master_commit      # 3
git push -f origin                       # 4
  1. 首先,在本地仓库的master提交记录的基础上,在origin仓库中创建一个名为master-old的分支。
  2. 为这个新的origin/master-old分支创建一个新的本地分支(它会被正确设置为跟踪分支)。
  3. 现在将本地的master指向你想要指向的任何提交记录。
  4. 最后,强制修改origin仓库中的master以反映你的新的本地master

(如果您以其他方式进行操作,则需要再添加至少一步以确保master-old正确设置为跟踪origin/master-old。在撰写本文时发布的其他解决方法中都没有包含这一步骤。)


16
这个回答比“the answer”更好,我同意,但对于只是想重命名一个分支(而不是显式地改名为master)的人来说,第三步并没有太多意义。 - knocte
无论您是在master分支还是其他分支上,对答案都没有任何影响。但问题的标题不太准确,它询问的任务比仅仅重命名一个分支要复杂得多。 - Aristotle Pagaltzis
3
这对我来说是可行的解决方案。我试图用另一个分支替换主分支。我使用git log -1 origin/what_i_want_as_new_master 命令获取了 $new_master_commit 以供第三步使用。在推送后(第四步),其他开发人员将收到信息“您的分支比 master 提前了 295 次提交。” 为了解决这个问题,我发送了一封电子邮件告诉他们每个人都运行以下命令:git pull; git checkout some_random_branch; git branch -D master; git pull; git checkout master; 基本上,他们需要删除本地 master 并拉取新版本,否则他们的本地代码就错了。 - nairbv
你可以更容易地完成这个任务:假设他们已经在master分支上,那么他们只需要执行git fetch && git reset --hard origin/master命令,就可以强制将本地的master分支与origin上的一致。我已经记录了这个过程,以及当你在master分支上有本地提交需要保留时的更复杂情况,详见https://dev59.com/D2855IYBdhLWcg3w6IzV。 - Aristotle Pagaltzis
请确保远程配置文件中含有 "denyNonFastforwards = false",否则您将收到以下错误提示:"remote: error: denying non-fast-forward refs/heads/master (you should pull first)"。 - gjcamann

180

我认为从Git v1.7开始,这种情况略有改变。现在很容易更新您本地分支的追踪参考到新的远程分支。

git branch -m old_branch new_branch         # Rename branch locally    
git push origin :old_branch                 # Delete the old branch    
git push --set-upstream origin new_branch   # Push the new branch, set local branch to track the new remote

14
一种替代--set-upstream的方法如下:在本地将分支重命名并在远程库中删除后,只需执行以下操作:git push -u --all - lucifurious
6
由于 Git 不允许删除远程主分支,因此这种方法无法在主分支上运行。 - Alexandre Neto
6
@AlexandreNeto 在这种情况下,您可以先执行第三行,然后将默认分支设置为 new_branch,最后再使用第二行删除远程的 master 分支。 - Tristan Jahier
4
惊人简单的步骤。这是对问题最好的回答。 - siddhusingh
21
删除远程分支的命令是 git push origin --delete old_branch,这个命令更易读一些。 - ThomasW
显示剩余5条评论

42

有许多方法可以重命名分支,但我将专注于更大的问题:"如何允许客户快进而不必在本地处理他们的分支"

首先简要介绍一下: renaming master branch and allowing clients to fast-forward

这实际上是很容易做到的,但不要滥用它。整个想法依赖于合并提交;因为它们允许快速前进,并将一个分支的历史链接到另一个分支。

重命名分支:

# rename the branch "master" to "master-old"
# this works even if you are on branch "master"
git branch -m master master-old

创建新的“主”分支:

# create master from new starting point
git branch master <new-master-start-point>

创建合并提交以形成父子历史记录:

# now we've got to fix the new branch...
git checkout master

# ... by doing a merge commit that obsoletes
# "master-old" hence the "ours" strategy.
git merge -s ours master-old

完成了!

git push origin master

这起作用是因为创建一个merge提交允许将分支“快进”到新修订版。

使用明智的合并提交消息:

renamed branch "master" to "master-old" and use commit ba2f9cc as new "master"
-- this is done by doing a merge commit with "ours" strategy which obsoletes
   the branch.

these are the steps I did:

git branch -m master master-old
git branch master ba2f9cc
git checkout master
git merge -s ours master-old

5
谢谢!git merge -s ours master-old 是其他答案所忽略的关键部分。此外,“易于操作”并不意味着“易于理解或发现”,这似乎是 Git 中许多情况的情况,但我离题了。 - Martin Vidner
3
谢谢你!我很喜欢这篇文章中没有提到删除并且对于那些使用上游克隆的人来说转换“无缝”的事实。 - Piotrek
1
如果主分支(master)有你不想要的更改(master-new),这个代码还能正常工作吗? - Ethan Fischer
@EthanFischer 是的。合并使用“ours”策略,基本上创建了一个空合并。但是这个空合并足以欺骗git认为它是快进推送。 - Double_A

38
git checkout -b new-branch-name
git push remote-name new-branch-name :old-branch-name

在删除 old-branch-name 前,你可能需要手动切换到 new-branch-name


这个解决方案的任何部分是否删除了本地旧分支名称,还是需要另外操作? - GreenAsJade
4
我认为最后必须运行git branch -d 旧分支名称来删除本地旧分支。 - Nabi K.A.Z.
您只需要一个命令即可推送更改:git push remote-name new-branch-name :old-branch-name - sigod
这种方式不会使你的git历史记录变得复杂吗?因为你是打开一个新分支而不是仅重命名当前分支。 - androidevil
1
@androider 不是。在Git中,分支是“简单引用”(http://git-scm.com/book/en/Git-Internals-Git-References)。 - sigod
显示剩余2条评论

12

我假设您仍然关心与您之前的问题相同的情况。也就是说,master-new 不会在其历史记录中包含 master-old。如果您将 master-new 称为 "master",那么您将有效地重写了历史记录。重要的不是您如何进入 master 不是以前的某个位置的后代状态,而是它处于该状态。

其他用户在 master 不存在时尝试拉取将只会失败(远程没有这样的 ref),一旦它再次出现在新位置,他们的拉取将必须尝试将他们的 master 与新的远程 master 合并,就像您在您的存储库中合并了 master-old 和 master-new 一样。考虑到您在这里想要做什么,合并将会有冲突。(如果它们被解决,并且结果被推回到存储库中,您将处于更糟糕的状态 - 历史记录的两个版本都在那里。)

简单回答您的问题:您应该接受有时历史记录中会出现错误。这是可以的,每个人都会遇到。在 git.git 存储库中有已撤消提交。重要的是,一旦我们发布历史记录,它就是每个人都可以信任的东西。

* 如果是这样,那么这将相当于将某些更改推送到 master 上,然后创建一个新分支来替换它。没有问题。


是的,这是同样的问题,只是有一个解决方法的想法。但即使我不重命名分支,我也很感兴趣是否可能。我认为像“master”这样的引用只是对特定提交的引用。我真的不想改变任何历史记录。我想我只是要将主引用指向另一个头。这也意味着,如果我以前使用过某个分支名称,我永远不能再次使用它了吗? - Albert
实际上,分支是引用 - 指向提交的指针。问题在于,我们期望分支的头以特定的方式演变(即始终快进)。从其他人的角度来看,在公共存储库中移动分支与重写分支历史记录相同。它不再指向包含所有内容的提交。 - Cascabel

10

当我尝试使用选定的答案时失败了。它会抛出一个错误:refusing to delete the current branch: refs/heads/master。我猜我会发布对我有用的内容:

git checkout master             # If not in master already

git branch placeholder          # Create placeholder branch
git checkout placeholder        # Check out to placeholder
git push remote placeholder     # Push placeholder to remote repository

git branch -d master            # Remove master in local repository
git push remote :master         # Remove master from remote repository.

关键是在将其推送到远程存储库之前检查占位符。其余的都很明显;删除主分支并将其推送到远程存储库应该可以工作。摘自此处


如果在远程端启用了此选项,当执行git push remote :master时,它将失败 - 您将在错误日志行中看到"remote: error:"作为前缀。 - rafalmag

2

在服务器上登录,进入Git目录并重命名裸仓库中的分支。

这样做不会出现重新上传相同分支时所涉及的所有问题。实际上,“客户端”将自动识别修改后的名称并更改其远程引用。

之后(或之前),您还可以修改分支的本地名称。


10
我忘记了登录 Github 服务器的凭证。有人有凭证吗? :-P - Daniel Fisher lennybacon

2

好的,在本地和远程重命名分支非常容易!...

如果你在分支上,可以轻松执行以下操作:

git branch -m <branch>

如果不在分支上,则需要执行以下操作:

git branch -m <your_old_branch> <your_new_branch>

然后像这样将删除推送到远程:

git push origin <your_old_branch>

现在你完成了。

如果在尝试推送时出现上游错误,只需执行以下操作:

git push --set-upstream origin <your_new_branch>

我还创建了下面的图像,以展示实际命令行中的步骤。只需按照步骤操作即可:

Enter image description here


你的删除推送建议没有奏效,我需要运行以下命令:git push origin --delete <your_old_branch>。 - super IT guy

1
这是我所知道的最简单和最易读的方法:

使用-m选项“移动”本地分支

git branch -m my_old_branch_name my_new_branch_name

将'moved'分支推送到远程,使用-u设置'upstream'

git push origin -u my_new_branch_name

设置'upstream'本质上是将您的本地分支与远程连接,这样像fetch、pull和push这样的操作就可以正常工作。

从远程删除旧分支

git push origin -D <old_name>
您的本地分支已经不存在了,因为在第一步中您已经将其“移动”了。

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