是否可以从以前的提交(几天前)修改代码(C#、java 等)并对其进行代码更改。再次提交。我通常使用 Visual Studio Team Explorer 提交代码。但是使用 git 是否可能(使用 Amend、rebase 命令)?另外,提交 # 是否更改?
2 回答
听起来你想使用git checkout --detach <commit>.
git checkout [--分离] | 提交 |
准备在 之上工作,方法是在其上分离 HEAD(参见“DETACHED HEAD”部分),并更新工作树中的索引和文件。保留对工作树中文件的本地修改,因此生成的工作树将是提交中记录的状态加上本地修改。
当参数是分支名称时,--detach 选项可用于在分支的尖端分离 HEAD(git checkout 将在不分离 HEAD 的情况下检查该分支)。
省略在当前分支的尖端分离 HEAD。
我建议在新分支中执行此操作,并在合并或重新定位到 master 之前在那里测试您的更改。
您问题的这一部分是真正的关键:
...另外,提交 # 是否更改?
答案是肯定的。提交哈希 ID 特定于那个提交。 所有提交都被冻结。你不能改变它们!您可以做的是进行新的和改进的替换提交。例如,就是git commit --amend这样。它也是这样git rebase做的,除了rebase它会自动为许多提交执行。
因此,要在命令行 Git 中执行此操作,您首先git checkout要提交您不喜欢的特定提交:
...--F--G--H--I--J <-- branch-X
^
|
mistake here
你可以:
git checkout <hash-of-H>
将其作为分离的 HEAD放入您的索引和工作树中。(这也让你有机会检查提交,看看它是否真的是你想要开始返工的地方。)然后你可以创建一个新的分支名称并切换到该新分支:
git checkout -b rework
或者,如果您愿意,可以将它们组合成一个命令:
git checkout -b rework <hash-of-H>
结果是:
I--J <-- branch-X
/
...--F--G--H <-- rework (HEAD)
我已将名称附加HEAD到分支名称rework,以显示您所在的分支。(如果您使用两步过程首先验证要返工的提交,您将不再处于“分离 HEAD”模式。)
您现在可以在工作树中进行一些更改git add,然后运行git commit --amend。该--amend选项告诉您的 Git 将当前提交推开,创建一个新提交,其父级是当前提交的父级。让我们调用新的 commit K,并像这样绘制它:
H--I--J <-- branch-X
/
...--F--G--K <-- rework (HEAD)
如您所见, commitH仍然存在,并且仍在 branch 上branch。rework但是,分支名称现在标识了新提交K,并且新提交K的父级是 commit G,而不是通常的情况:--amend使 GitG用作父级,而不是H用作父级。
您现在可以重新复制提交I以及J新的和改进I'的J'提交。使用git rebase,您可以以自动方式执行此操作 - 但使用 执行此操作可能更有指导性,至少一次,git cherry-pick因为这实际上是git rebase真正的工作方式。
所以,从这里,到樱桃采摘I和J:
git cherry-pick <hash-of-I>
这将告诉 Git 比较Ivs H,看看有什么I作用,然后尝试对您的提交进行相同的更改,K并自动进行一个新的提交,该提交也重新使用来自的提交消息I:
H--I--J <-- branch-X
/
...--F--G--K--I' <-- rework (HEAD)
如果一切顺利,您现在可以git cherry-pick提交J(通过其哈希 ID,或使用branch-X标识 commit 的名称,J如图所示)。如果一切顺利,您现在拥有:
H--I--J <-- branch-X
/
...--F--G--K--I'-J' <-- rework (HEAD)
现在您所要做的就是强制您的 Git 移动名称 branch以识别 commit J':
git branch -f branch-X rework
这导致:
H--I--J [abandoned]
/
...--F--G--K--I'-J' <-- branch-X, rework (HEAD)
您现在可以返回branch-X: git checkout branch-Xwill 附加HEAD到 name branch-X,而无需更改任何其他内容。然后您可以删除该名称rework,因为我们不再需要它。
请注意,所有三个提交现在都“重新编号”。实际上,它们只是三个新提交:三个原始提交仍然存在,永远冻结。它们现在只是不可见,或者至少不再通过名称找到branch-X。最终,这三个提交将完全消失,只要无法找到它们。
(Git 尝试给你至少 30 天的时间来改变主意并让他们回来。为此,你必须找到提交的哈希 ID J...这并不难,真的,使用git reflog. 在 30 天之后但是,在一天的时间段内,Git会使记住 ' 哈希 ID 的 reflog 条目过期J,这就是提交将真正、真正消失的时间,只要没有其他方法可以找到它。)
自动化这个
您可以使用自动化整个过程git rebase,尤其是git rebase -i. 我鼓励你先用手做几次,艰难的方式,几次,以很好地了解它是如何工作的以及出了什么问题。但要做到这一点git rebase -i,你会运行:
git rebase -i <hash-of-G>
G使用 commit 的哈希值而不是的哈希值的原因H是,git rebase真正的作用是:
- 列出它应该复制的提交,就像通过
git cherry-pick:这些是当前分支上的提交,但在您列出的提交之后。所以这是H,I和J。 - 然后,检查您要求的特定提交(
G在这种情况下),实际上,git cherry-pick在第一步中列出的每个提交上运行。
当你使用 时git rebase -i,Git 会在两个步骤之间暂停——通过运行你的编辑器——写下命令的指令表pick。这些pick命令代表要完成的每个樱桃挑选操作。1 您现在可以编辑此说明表,将pick命令更改为其他命令,交换pick命令或任何您喜欢的内容。(说明书上有说明,以注释的形式告诉你如何编辑它。)你做任何改变——比如改变pick——edit写出更新的工作表并退出你的编辑器,Git 开始遵循指示。
如果您使用该edit指令,Git 将运行樱桃挑选,然后在樱桃挑选成功完成后停止。然后,您可以使用git commit --amendon 将当前(精选副本)提交推开。
这里棘手的部分是,无论您如何处理说明,某些挑选樱桃的操作都可能失败。如果他们确实失败了,他们不会被拒绝。相反,它们停在中间。您处于冲突git merge操作的中间,因为对于 Git,cherry-pick 只是一种奇怪的合并形式。 您必须知道如何解决冲突的合并。 这意味着你应该在学习如何工作git merge 之前学习如何工作git cherry-pick,并且你应该git cherry-pick在跑步之前学习如何工作git rebase。
完成中断的 rebase 驱动的cherry-pick(通常)非常容易:您解决冲突,git add正确的文件,然后运行git rebase --continue. 请注意,您可能会在您打算提交的提交上发生冲突edit:在这种情况下,停止发生在提交新提交之前。您必须完成有冲突的合并并运行git rebase --continue才能进行提交。(我忘记了 Git 是否(1)做出提交,(2)停止让你修改它,但我怀疑它确实如此。)
1好像by cherry -pick在这里也通常变成实际上by cherry-pick。所有形式的git rebase都会尝试做一些作弊以使事情进展得更快和/或更顺畅和/或更好,但交互式 rebase 作弊最少,因为你可以在每一步停下来看看。