如何弃用旧的Git主分支并使用分支作为新的主分支?

5
我们有一个项目,曾经需要回溯Git历史并从旧提交中开始一个新分支(我们此处称其为“new_branch”)。现在,所有的开发都在new_branch上进行,而旧的Master在分支创建之后就毫无用处了。因此,现在我们面临这样一种情况:
C1 = Commit 1, C2 = Commit 2, etc.

            C1                       
             |                       
            C2                       
             |\                      
            C3 \                     
             |  \                    
            ...  \                   
             |    \                  
Master -->  C17    \                         
                    \                
                   C18               
                    |                        
                   ...               
                    |                
                   C37 <-- new_branch

我现在想做的是放弃(撤销)从C3到C17这段时间Master分支上所做的所有提交。至少,我认为我想要这样做?我的意思是,C3到C17现在已经无用了,如果我能够撤销它们,那么我就可以将new_branch合并回Master,让Master重新有用。

所以我的问题是,这是否是实现我能够再次使用Master的正确方法?我应该使用哪个命令(我在Linux中使用Git命令行)来撤销C3到C17的提交?由于这是一项工作项目,所以非常重要,我必须做对。


删除 master,将 new_branch 重命名为 master。 - ΦXocę 웃 Пepeúpa ツ
你是说从C3到C17的所有提交都要被放弃吗? - msanford
1
是的,C3到C17发生在7周前,最后走投无路。我们早已经放手不管了。 - Enfors
3个回答

6
最简单的解决方案是将new_branch强制推送到源的master分支:
git checkout new_branch
git push -f master

然后以以下几种方式之一修复本地的master

  1. git reset --hard origin/master(在master分支上执行)
  2. git branch -D master(不在master分支上执行,在获取更新后再次拉取)

在这种情况下,没有必要用new_branch重新派生现有的master

这将会彻底删除现有的master并用new_branch的内容和历史记录替换它。请确保你的意图正确。


正如Romain在下面指出的那样,使用git branch -f可以省略本地修复步骤:

git branch -f master new_branch && git push -f origin master

由于它也在本地移动分支指针,因此需要注意。


与许多Git问题一样,有许多解决方法,其他答案可能同样可行。


1
这就是非常简单的无可否认的吸引力。非常感谢。除非有人给我提供更好的建议,否则我很可能会这样做。 - Enfors
2
@Enfors Pleasure。请耐心等待其他答案的出现,解决Git问题有多种方法,不同的工作流程具有不同的优势。如果您接受它,我会(非常)高兴地知道它对您起作用了! - msanford
2
非常好的回答。为了简洁起见(来自主分支),这里提供一个变体:git branch -f master new_branch && git push -f origin master。只是为了提供另一种实现相同解决方案的方式(但思路是相同的,因此不需要完整的答案)。 - Romain Valeri
2
@Endors 是的,这就是我倾向于这样操作的主要原因。一个命令在本地设置正确,一个命令反映在远程服务器上的操作。 - Romain Valeri
1
@msanford 我一开始也考虑了一下,但是没关系,我觉得你表达得很清楚。 - Romain Valeri
显示剩余3条评论

3
有两种常见的方法来实现这一点;每种方法都有缺点,所以取决于你的情况更重要的是什么。每种方法也有不同的变化,其中有较小的权衡...
第一种方法是重写master的历史记录。这意味着以一种方式移动master,从其历史记录中删除当前作为其历史记录一部分的某些提交。
无论如何进行历史重写,都会产生一定的成本-因此通常最好采用最简单满足您要求的方式。在这种情况下,可能是这样:
git checkout master
git reset --hard new_branch
git push --force-with-lease

这种方法与msanford建议的方法类似,但有两个优点。首先,它更加简洁(因为它首先更新本地分支并将其用作更新远程的设置)。其次,用--force-with-lease替换-f会提供一些额外的安全性,因为它确保远程没有新提交你不知道的内容。(在你的特定情况下,这可能不是什么问题;但养成这个好习惯真的很重要。)
对于在多个存储库中共享的分支进行历史重写的成本(即已推送,然后可能被其他存储库fetchpull)是,共享该分支的其他存储库将看到该分支以“意外”的方式移动,并且必须恢复。恢复过程在git rebase文档的“从上游rebase恢复”部分中有记录。在执行此操作时,与整个团队协调非常重要,因为如果任何团队成员在恢复时做错了事情,它可能会撤销历史重写。
好处是最终得到一个非常好的历史记录,根本不显示已删除的提交。(请注意,如果这些提交已经共享,则无法保证它们“永远消失”。如果它们包含敏感信息,则这是一个更大的问题,该信息基本上必须被视为已泄露 - 虽然你可以采取措施尝试防止进一步传播这样的信息。)
因此,如果你可以实际协调存储库的所有用户,那么这是一个很好的方法。但如果你不能,还有另一种方法。
第二种方法是撤消主分支上的更改 - 即将其还原回C2 - 然后合并new_branch。这符合分支通常移动的方式,因此不会带来重写的成本。但是,这意味着放弃的提交(及其反转)将永远保留在历史记录中。
git checkout master
git revert -n C3..HEAD
git commit
git merge new_branch

这让你获得了一个类似于"最初的回答"的历史记录。
C1 - C2 - C3 - ... - C17 - R -- M <--(master)
       \                       /
        C18 - ........... - C37 <--(new_branch)

Rmaster 恢复到 C2 时的状态,M 是在此基础上简单地合并了新的更改。

如果你从 revert 命令中省略 -n 选项,你可以跳过 commit 命令,但是你将得到一个独立的提交来撤消从 C3C17 的每个提交。也有一些技术可以将撤销变化嵌入到合并提交中,如果你真的认为这样做更好,但它会创建一个“恶意合并”——隐藏更改并可能混淆用户和某些未来操作(如 git rebase):

缺点是历史记录较混乱。(如果您真的需要历史记录来记录您到达目前状态的方式,则可以说它更准确;但它肯定更加复杂,并显示您可能不再需要的信息。如果撤销提交包含您不希望继续传播的敏感信息,则肯定不适用。)

但是,正如上面提到的,它遵循常规的分支移动模式,因此避免了历史重写的成本。

因此,再次强调,这取决于您情况下更重要的是什么。


0

所以如果你想删除 c3 - c17 ,你可以检出 c3,然后将你的新分支更改与其合并。然后如果你将更改推送回主分支,那么 c3 - c17 将被删除。


1
合并的意义在哪里?由于它位于分离的 HEAD 状态之上,因此不会自动合并到“master”中,推送将不得不被强制执行/将成为历史重写;这意味着您可以放弃合并,直接从新分支进行强制推送。 - Mark Adelsberger
但是你说你想要新分支的代码和c2。你必须以某种方式将它们合并,即合并?我的意思是,你可以重新定位或手动完成,但无论如何,这是你的项目。 - Sam Alexander
好的...首先,我不是OP,所以我没有说过这样的话。其次,C2已经在new_branch中了。 - Mark Adelsberger

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