设置: 我已将代码更改提交到本地和远程,但是都在错误的分支中。
我的解决方案:
问题:这是要走的路吗?如果我用google搜索,就会发现人们使用了还原功能,但我不明白为什么会如此,因为它看起来更复杂,更危险。为什么我应该使用还原功能?
答案 0 :(得分:1)
当您revert
进行某些提交时,您将创建否定目标提交的提交。结果与reset
相同,但您不必force push
,可以执行简单的push
,因为您添加到历史记录中不会将其删除。您可以revert
提交的另一个历史上没有的差异。在这种情况下,您不能使用reset
,因为它会导致失去目标提交后的所有提交。
您可能还会看到以下问题:What's the difference between Git Revert, Checkout and Reset?
答案 1 :(得分:1)
我建议您改用为基础,而不是3
:
git rebase --onto COMMIT_BEFORE_WRONG WRONG_COMMIT branch_with_wrong_commit
git push --force-with-lease
这“削减”了错误的提交。
添加-i
(对于 interactive )时,您可以检查正确的提交是否已移动。
相同的结果将带有:
git rebase -i COMMIT_BEFORE_WRONG
,然后在显示的“ todo”文件的第一行中将单词pick
更改为drop
。
答案 2 :(得分:1)
我[看到]人们使用还原,但我为什么不理解为什么它看起来更复杂,更危险。为什么我应该使用还原?
应该是一个很强的词。 (不如 shall 或 must 那么强,但至少相当强。:-))
...重置为第一个正确的提交,然后再次强制将其删除以删除远程分支上的错误提交
每当必须使用git push --force
或同等名称时,您就会以其他人可能期望的方式移动分支名称。 (在部分顺序中,力量比应该弱于:我会说 may
考虑:
$ git checkout <somebranch>
... work ...
$ git add <files> # or --all or . or whatever
$ git commit
git checkout
步骤的作用是将HEAD
附加到分支,并将分支名称指向的 tip commit 复制到您的Git索引中和工作树,以便您可以对其进行处理:
... <-F <-G <-H <--branch (HEAD)
名为 branch
的分支(全名是refs/heads/branch
的引用)存储某些提交H
的原始哈希ID。提交H
本身存储其父提交G
的原始哈希ID,该原始哈希ID存储某些提交F
的原始哈希ID,依此类推。因此,我们说名称指向尖端提交H
,它指向较早的提交G
,依此类推。
git add
步骤将更新索引/暂存区,以便可以进行提交,而git commit
步骤将创建一个新的提交。新提交将当前签出的提交H
的哈希ID作为其父哈希ID存储。 (当然,它将从当前索引/暂存区域创建的冻结快照存储为快照。)然后,作为最后一步,git commit
将新提交的哈希ID写入到附加了HEAD
的分支名称:
... <-F <-G <-H <-I <--branch (HEAD)
这是人们进行提交时分支增长的方式,一次提交一次。当您合并一系列提交时,无论是作为真正的合并,还是作为一种快进而不是全部合并的操作,分支也将同时获取新的提交,也许一次很多,也许以一种非线性的方式,但是重要的是新的提交总是导致回到现有的提交:
...--F--G--H--I---M <-- master (HEAD)
\ /
J--K--L <-- develop
将合并提交M
添加到master
会使H
的提交I
和master
reachable 生效,因为我们可以按照向后指向内部的提交箭头(在这里以线条显示,因为现在箭头太难绘制文本了)使用M
中的最上一行箭头。 (从M
到L
的向左箭头也使我们也可以从M
骑到K
或J
。{{ 3}}与波特兰的公交系统有一个很好的比喻,尽管任何都市火车系统都是相似的。)
但是假设我们改为这样做:
...--F--G--H--X <-- master (HEAD)
\
J--K <-- develop
然后意识到,哎呀,我们打算将提交X
放在develop
上。我们使用任何适合将复制 X
到新提交的方法,例如cherry-pick或git rebase --onto
(两者都做相同的工作)。然后,我们使用git checkout master; git reset --hard master~1
将X
推开,这样它就不再位于master
上:
X
/
...--F--G--H <-- master (HEAD)
\
J--K--L <-- develop
(这里L
是X
的副本,放在我们想要的位置。)这种分支名称运动会使提交X
悬空而无法找到它,至少,在 our 存储库中没有办法。但是,如果我们已经使用git push
将提交X
发送到其他地方,则某些 other Git会为其命名。实际上,我们也是如此:
X <-- origin/master
/
...--F--G--H <-- master (HEAD)
\
J--K--L <-- develop
我们的origin/master
是Git记住master
上的origin
的方式,仍然记得存在X
的提交。这意味着origin
的Git会记住X
在他们的master
上。
实际上,这是为什么,我们必须使用git push --force origin master
:告诉origin
上的Git它应该放弃 X
。如果我们在其他任何人(有权访问该Git的人)之前执行此操作,也将X
复制到其 Git存储库中,我们很好:没有人看到X
,因此移除X
不会伤害任何人。
如果别人 did 从另一个Git上抢了一份副本,问题就开始堆积起来。现在,第三个Git存储库仍具有提交X
,也许在它们的 master
中。也许他们在X
上方(他们的副本)建立了新的提交,而他们希望保留这些提交:
...--F--G--H--X--Y--Z <-- master (HEAD)
我们现在要告诉他们:哦,忘记X
,也将其从您的存储库中删除。这需要他们自己做git rebase --onto
或类似的事情,将其Y
和Z
复制到不再导致回到X
的新提交中。
简而言之,通过从我们的Git和X
的Git中删除origin
,我们为所有共享这些Git存储库的其他人增加了负担:他们也,必须全部删除其X
,并处理所有后果。
在某些项目中,每个人都同意这种情况可能发生–在任何时间到任何分支,或在任何时候到分支的某些特定子集。在这些项目中,重设分支机构和强制推动就可以了。在某些项目中,没有其他用户,或者您可以在任何人有机会发现错误之前进行强制推送;在这种情况下,也可以在其中进行复位和强制推动。当您开始为不准备这样做的人做大量工作时,就会出现问题。在这种情况下,只需提交 new 提交而只是撤消 X
中的工作,就可以为他们提供一种以<他们准备接受。