签出旧提交,修改代码,然后将分离的HEAD推送到origin / master

时间:2018-04-23 09:51:26

标签: git

我已经检查了我的主分支上的先前提交(让我处于分离的HEAD状态)并修改了代码。现在我想推动这个 版本到origin / master,这样这个版本现在成为最新的提交。 在视觉上,这就是我所做的

这就是我的开始:

commits: A -> B -> C -> D (HEAD)

git checkoutC

commits: A -> B -> C (detached HEAD) -> D

修改了C处的代码,导致C'

commits: A -> B -> C' (detached HEAD) -> D

然后我add编辑了commit我的修改。现在我想在origin / master上进行C'最新的提交,以便它在D之前提交:

commits: A -> B -> C'-> D -> C' (HEAD)

如果C'之前的D恢复为C或保持为C',我并不特别在意。

我不清楚为了达到这个目的我需要发出什么命令,有人知道吗?

更新:我也不关心保留D。因此,也许可以删除D,这会使C'成为最新的?

更新:我最终git rebase提交C,从而删除D,然后重新添加修改以获取C',然后推送{{1} }}。我相信有更好的方法可以做到这一点......

2 个答案:

答案 0 :(得分:1)

嗯,你的提交图表并不完全准确(并且可以使用一点澄清)。进入这个的原因是,当我们达到这一点时,它将有助于理解解决方案。所以:

  

这就是我的开始:

commits: A -> B -> C -> D (HEAD)

现在,如果你要在提交之间绘制箭头,它们应该及时指向“向后”,因为git存储父指针而不是子指针。 (我只画线而不是箭头。)

我认为master分支(以及origin/master参考号)位于D。此外,由于您没有显示分离的HEAD状态,我假设您已master已签出。我通常会画出像

这样的东西
commits: A -- B -- C -- D <--(master)(origin/master)
                                 ^(HEAD)

显示引用指向的位置,并反映HEAD是指向master的符号引用。

  

我把结账给C:

所以现在我们已经

commits: A -- B -- C -- D <--(master)(origin/master)
                   ^(HEAD)

(这显示了以这种方式绘制HEAD的优势;只需移动它以使其指向提交,并且我们知道我们处于分离状态。)

  

在C处修改代码,导致C':

这就是上面的图表变得不准确的地方。这个新提交不会像您绘制的那样替换 C,而是C的新子项。所以这更像是

commits: A -- B -- C -- D <--(master)(origin/master)
                    \
                     C'
                     ^(HEAD)

(显示了绘制线而不是箭头的优点;更容易描绘不同的提交行)。但即使这样也不太正确,因为按照约定C'将意味着“一个新​​的/重写的提交应用与C的父级相同的更改适用于C的父级”;所以我们应该把它称之为别的。

commits: A -- B -- C -- D <--(master)(origin/master)
                    \
                     E
                     ^(HEAD)

现在我们可以解决你想要做的事情了。

现在,当你在更新中说你不关心保留D时,你可能没有考虑到保留它的所有理由。如果你说“我真的想把D扔出历史,这就是为什么......”这将是一回事,但如果它只是“我不关心这种或那种方式”,那么你应该考虑保留它。这就是原因:

删除D是历史记录重写。推送分支后,在该分支上执行历史记录重写可能会导致问题,尤其是当repo与其他用户共享时。请参阅“从上游rebase恢复”下的git rebase文档。

现在,如果你理解了这个问题 - 即如果你得到了它,你需要与拥有ref副本的任何其他人协调,并且如果没有这样做可能会导致你的重写被意外撤消 - 仍然想要丢弃D,那么你可以这样做:

重写方法

从原始问题停止的地方开始,您将master分支移动到新创建的提交。

git branch -f master
git checkout master

会给你

commits: A -- B -- C -- D <--(origin/master)
                    \
                     E <--(master)
                              ^(HEAD)

(事实上,在开始时将master重置为HEAD^更容易,而不是检查分离的HEAD状态;假设,这是,你知道你当时要重写一次。)

然后你可以推翻master的重写,但你必须“强制”推动。这是你将导致上游反弹的“红旗”

git push --force-with-lease

如果其他人向origin/master添加了更多提交,则会失败。这是因为完成重写会冒失去工作的风险,至少应采取额外措施来解决这个问题。如果您仍想覆盖该安全检查,可以说

git push -f

请注意,此方法或任何其他方法实际上都不会删除提交D。它会从D历史记录中删除master,这意味着它最终可能会被gc删除。

commits: A -- B -- C -- D
                    \
                     E <--(master)(origin/master)
                              ^(HEAD)

无重写方法

另一方面,如果你认为重写比它的价值更麻烦,你会做这样的事情:

再次找到原始问题所在的位置,您可能希望保留所做的更改,这样您就不必重新开始。

git branch temp
git checkout master

现在还原D

中所做的更改
git revert HEAD

产生

                          ~D <--(master)
                         /         ^(HEAD)
commits: A -- B -- C -- D <--(origin/master)
                    \         
                     E <--(temp)

TREE的内容(~D)将与C的内容相匹配,现在您可以说

git rebase master temp
git checkout master
git merge --ff-only temp
git branch -d temp

所以我们终于有了

                          ~D -- E' <--(master)
                         /               ^(HEAD)
commits: A -- B -- C -- D <--(origin/master)
                    \         
                     E

最初的E提交不再有意义; D~D)的撤销以及EE')的更改都在master上,可以正常推送。

答案 1 :(得分:0)

如果您只是想在C'处使用修改过的代码重写git历史记录,则可以创建一个新分支:

git checkout -b <name_of_new_branch>

然后强制将您的新分支推送到遥控器:

git push origin <your_branch_name> -f