Git用分支覆盖master

时间:2019-02-26 13:32:15

标签: git

我克隆了一个存储库并创建了一个新分支。忘了结帐到新分支。是否进行了更改(删除了大多数文件),添加了,提交了然后将其推送到远程。现在我失去了主人。

试图返回到先前的状态:

git checkout master
git reset --hard Tony_branch
git push -f origin master

结果:

Total 0 (delta 0), reused 0 (delta 0)
remote: Processing changes: done
To ssh://<link> can't share the name obviously.
 ! [remote rejected] master -> master (non-fast forward)
error: failed to push some refs to

所有答案均建议使用“ git pull”。但是它将重新删除文件...

我该如何解决?

编辑:

我试图通过用我在更改前创建的分支覆盖旧的master来检索旧master。当然其他方法可能会更好。

4 个答案:

答案 0 :(得分:0)

我不知道您当前存储库的确切状态,因此此答案可能对您不起作用,至少不是开箱即用。相反,一旦您意识到按master是一个错误,我将采取应遵循的步骤。

首先,从本地master签出一个新分支:

# from master
git checkout -b Tony_branch_real

然后,将您所做的提交还原到master

# again, from master
git revert A^..B

这里A代表您对master的第一个意外提交,而B代表最后的意外提交。 git revert将向您的master分支添加一个 new 提交,从功能上撤消AB的所有(包括)提交范围。您的实际命令看起来像这样:

git revert dk93kg93^..k20gjei9

其中AB已被其SHA-1哈希替换。在您的git log分支上检查master,找到所需的SHA-1散列。

最后,您只需要定期将master推送到遥控器:

git push origin master

这应该行得通,因为您在本地所做的就是添加一个新的还原提交。

我们避免在master上执行诸如硬重置之类的事情的原因是,该分支可能是由其他开发人员共享的。硬重置会重写历史记录,因此对于公共共享分支,我们要避免这种情况。

答案 1 :(得分:0)

看来您的遥控器将不接受git push -f。因此,除非可以在远程(gitlab,github,bitbucket)上修复分支保护,否则无法撤消master分支。

但是,您可以通过各种方式恢复所做的文件。一种是还原主服务器的所有提交,直到您回到原始状态。

另一种方法是

git checkout master
git reset --hard origin/master
git reset Tony_branch #soft reset on purpose
git add ... # all changes
git commit -m 'Reverting master to state before bad push'
git push origin master

答案 2 :(得分:0)

基本上有两种方法可以解决这种错误。

一种方法是进行历史记录重写。这通常是犯错误的人想要的,因为它几乎完全消除了错误。但这并不总是可行的,有时甚至是不允许的。

其他选择是接受错误作为历史记录的一部分,并对历史记录也进行更正。

至少有两种方法可以完成这些操作。


历史记录重写

听起来您知道执行历史记录重写的合适方法,但是由于push -f步骤的缘故,它不起作用。如果您控制远程仓库,则可以更改权限/保护设置以允许强制推送到主仓库,但这不一定是个好主意,有两个原因:

首先,保护master分支在大多数情况下可能是一个好主意,即使在这种情况下也是如此。当然,如果这是唯一要考虑的问题,则可以取消保护它,进行重写,然后重新保护它。

第二个可能的问题是,如果任何其他克隆已经将错误的提交拉入master,则强行从master的历史记录中删除那些提交将“破坏”这些克隆,并且它们必须采取措施进行恢复。如果他们执行了错误的步骤,则可以轻松地重新引入已删除的提交,尤其是如果他们根据要撤消的提交进行任何新工作时,这尤其值得关注。请参阅“从上游基础恢复中”下的git rebase文档;即使您所做的并不是本质上的调整,也适用相同的情况。

因此,很可能您需要一种不重写历史记录的解决方案,这是我将在此期间重点关注的问题。


添加更正

您的存储库当前可能看起来像

... x <--(tony_branch)
     \
      A -- B -- C <--(master)(origin/master)

现在,您要将origin/master移至x;这将成为“最干净的”历史记录。 (而且,如果我们说实话,那将避免保留永久的滑倒记录。这并不是说我们有任何理由要保留错误记录;只是想摆脱这种错误。记录可能比记录更重要,而历史记录重写的成本也是如此。)

但是您已经读了很多,因为显然重写origin/master的历史记录是不可行的。因此,通常人们会建议使用git revert。这将导致类似

... x <--(tony_branch)
     \
      A -- B -- C -- ~CBA <--(master)(origin/master)

其中~CBA撤消对ABC所做的更改,而将内容(TREE对象)保留在master就像x一样。那是一种选择;这很简单,并且可以在以后的任何git命令中将其保持为直接状态。

另一种选择是将合并提交用于还原。这使得ABC看起来像被废弃的侧支。 “不利的一面”是更容易看到A .. C;可以忽略;“不利的一面”是有问题的合并会有点不自然-什么有时称为“邪恶合并”。如果您可能要重新确定这部分提交的内容,那么“邪恶合并”可能是一个问题-但是再说一次,因为我们去那里是因为master的历史重写不是一回事,也许这不是问题。没关系。无论如何,这最终看起来会更像

... x <--(tony_branch)
    |\
    | \---------- Y <--(master)(origin/master)
     \           /
      A -- B -- C 

为此,您可以说

git checkout master
git reset --hard tony_branch
git merge -s ours origin/master
git push

请注意,无论采用哪种方式,从AC的提交都将纳入master的历史记录中。这意味着不再可以通过将那些提交合并到master中来“重新应用”分支的更改-因为它们已经“存在”了。要解决此问题,您可以创建AC的提交副本,并将这些副本放在tony_branch上。这可以在更新master之前或之后完成(通过还原或合并);之前可能会更容易一些,但是我没有在其中包含说明,因为在这里解释为什么更加容易。

因此,您要做的是AC的“强制变基”。与流行的看法相反,rebase不会“替换”源提交,而是复制它们。强制操作只是意味着“即使我并没有真正更改基准或其他任何内容,也要进行复制”。第一步是检出{​​{1}}。如何执行操作取决于您采取了哪些其他步骤:

(A)将合并应用到C之后,您可以

master

,这会使您在git checkout master^2 处的合并提交的第二个父级处于分离的HEAD状态。或

(B)在应用还原后,您可以

master

(如果在单个提交中应用了还原;如果使用了单个还原提交,则类似于git checkout master^ ,但是使用了许多实际的还原提交来代替master~3)。或

(C)修复3之前,您可以简单地

master

但是请注意,您要做要以“分离的HEAD”状态结束,以避免git checkout --detach master 分支的错误移动;因此是master选项。

现在已经签出--detach,您可以

C

你会得到类似的东西

git rebase -f tony_branch
git branch -f tony_branch

答案 3 :(得分:0)

如果您对重置/还原不满意,我将执行以下步骤,而不是进行重置/还原:

(假设您在更改之前创建的分支名称为“ good-master-branch”)

  1. 将git main分支从master切换到另一个(例如,“ good-master-branch”)

如果使用的是github或bitbucket之类的工具,则可以在GUI中进行设置。

  1. 删除您当前不良的master分支

首先,请确保您不在主分支git checkout good-master-branch

删除本地git branch -D master

删除远程git push origin --delete master

  1. 使用良好的代码创建一个新的master分支

git checkout good-master-branch

git checkout -b master

  1. 将新的“ master”分支设置为您的git main分支

现在,您有了一个新的干净的master分支!