我有一个Git仓库,看起来像这样:
A <- B <- C <- D <- HEAD
我希望将分支的头指向A,也就是说,我想让B、C、D和HEAD消失,并且我想让head成为A的同义词。
听起来我可以尝试revert(不适用,因为我已经在中间推送了更改),或者回滚。但是如何回滚多个提交?我一次一个还是一起回滚?顺序重要吗?
我有一个Git仓库,看起来像这样:
A <- B <- C <- D <- HEAD
我希望将分支的头指向A,也就是说,我想让B、C、D和HEAD消失,并且我想让head成为A的同义词。
听起来我可以尝试revert(不适用,因为我已经在中间推送了更改),或者回滚。但是如何回滚多个提交?我一次一个还是一起回滚?顺序重要吗?
扩展我在评论中写的内容
一般规则是,不应更改已经发布的历史记录,因为可能有人根据它们的工作。如果您重写(更改)历史记录,将会出现合并其更改和更新问题。
因此,解决方案是创建一个新提交,该提交撤消要摆脱的更改。您可以使用git revert命令来执行此操作。
您有以下情况:
A <-- B <-- C <-- D <-- master <-- HEAD
(箭头指向指针的方向:提交的“父”引用,在分支头(分支ref)的顶部提交,在HEAD引用的分支名称中)。
您需要创建以下内容:
A <-- B <-- C <-- D <-- [(BCD)-1] <-- master <-- HEAD
其中[(BCD)^-1]
表示撤消提交B、C、D中的更改的提交。数学告诉我们(BCD)-1 = D-1 C-1 B-1,因此您可以使用以下命令获取所需情况:
$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message for all of them"
对所有内容都有效,除了合并提交。
另一种解决方案是使用git checkout命令检出提交A的内容并提交这个状态。 这种方法也适用于合并提交。但是新增的文件将不会被删除。如果您有任何本地更改,请先使用git stash
命令进行保存:
$ git checkout -f A -- . # checkout that revision over the top of local files
$ git commit -a
那么你会得到以下情况:
A <-- B <-- C <-- D <-- A' <-- master <-- HEAD
提交 A' 的内容与提交 A 相同,但是它是一个不同的提交(包括提交信息、父提交和提交日期)。
Jeff Ferland提出的替代解决方案,由Charles Bailey进行修改,基于同样的思路,但使用了git reset。这里稍作修改,这种方法可以适用于所有情况:
$ git reset --hard A
$ git reset --soft D # (or ORIG_HEAD or @{1} [previous location of HEAD]), all of which are D
$ git commit
git checkout -f A -- .
不会删除这些文件,您需要手动删除。我现在采用了这种策略,谢谢Jakub。 - omagit checkout foo
可以指代切换分支 foo
或者从索引中检出文件 foo
。为消除歧义,需要使用 --
,例如 git checkout -- foo
总是与文件相关。 - Jakub Narębskigit revert --no-commit D C B
。 - welldan97git revert
命令还不支持同时撤销多个提交记录;这是一个比较新的更新。 - Jakub Narębski我发现有用的简洁方法
git revert --no-commit HEAD~3..
git commit -m "your message regarding reverting the multiple commits"
这个命令可以撤销最近的3次提交并只创建一个提交。
同时不会重写历史,因此不需要强制推送。
..
用于创建一个范围。意思是 HEAD~3..
和 HEAD~3..HEAD
是相同的。
git commit
命令实际上会执行提交操作。 - x1a4HEAD~3..
和 HEAD~3..HEAD
是相同的。 - Toine H要实现这个目标,您只需要使用 revert 命令,并指定您想要撤消的提交范围。
根据您的示例,您可以执行以下操作(假设您在 'master' 分支上):
git revert master~3..master
或者git revert B...D
或者 git revert D C B
这将在您的本地创建一个新的提交,该提交会撤销B、C和D所引入的更改(即它会撤消这些提交所做的更改):
A <- B <- C <- D <- BCD' <- HEAD
git revert --no-commit HEAD~2..
是一个更加惯用的方法。如果你在主分支上,不需要再指定主分支。 --no-commit
选项允许Git尝试一次性还原所有提交,而不是在历史记录中留下多个“还原提交…”消息(假设这正是您想要的)。 - kubimaster~3
。 - user456814--no-commit
(这样你就可以为每个还原操作获得单独的提交记录),然后在交互式变基中将它们全部压缩在一起。合并后的提交消息将包含所有的 SHA,您可以使用您喜欢的提交消息编辑器来安排它们的顺序。 - Radon Rosborough--no-commit
参数,它会一个一个地执行,你需要为每个提交不断地输入 git revert --continue
...我本来希望 Git 有一个友好的提交选项,比如“全部撤销并在单个提交中列出所有哈希值”,但看起来并没有 :| - rogerdpackgit reset --hard a
git reset --mixed d
git commit
这将一次性为它们所有的版本进行还原。请提供一个良好的提交信息。
HEAD
看起来像A
,那么他可能希望索引匹配,因此git reset --soft D
可能更合适。 - CB Baileygit reset --mixed D
?具体来说,为什么要使用 reset
命令?是因为如果不将 HEAD 重置到 D,那么 HEAD 将指向 A,导致 B、C 和 D 变成“悬空”状态并被垃圾回收,这不是用户想要的结果吗?但是为什么要加上 --mixed
参数呢?你已经回答过了,“--soft
重置不会移动索引...”,所以通过移动索引,这意味着索引将包含 D 的更改,而工作目录将包含 A 的更改——这样 git status
或 git diff
(比较索引 [D] 和工作目录 [A])就会显示出实质内容;也就是说用户正在从 D 回到 A。 - Nate Anderson与Jakub的回答类似,这让您可以轻松选择连续的提交来还原。
# Revert all commits from and including B to HEAD, inclusively
git revert --no-commit B^..HEAD
git commit -m 'message'
B^..HEAD
,否则 B 将被排除在外。 - tessusgit revert --no-commit B^..HEAD
或 git revert --no-commit A..HEAD
。 - Yoho我很沮丧,因为这个问题不能简单地回答。每一个其他的问题都是关于如何正确恢复并保留历史记录的。这个问题说:“我希望分支的头指向A,也就是说,我想让B、C、D和HEAD消失,并且我希望头部与A同义。”
git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f
我在阅读Jakub的帖子时学到了很多,但是公司里有个人(可以直接推送到我们的“测试”分支而不需要发起拉取请求)提交了五个糟糕的提交,试图修复他五个提交之前的错误。不仅如此,还有一两个拉取请求被接受,而这些现在都是错的。所以算了吧,我找到了最后一个好的提交(abc1234),然后只运行了基本脚本:
git checkout testing
git reset --hard abc1234
git push -f
我告诉了这个代码库中另外五个人,他们最好记录下过去几个小时的更改,并从最新的测试中清除/重新分支。故事结束。
git push --force-with-lease
命令,它会在且仅在没有其他人在提交范围内或之后对该分支进行过提交时,才重写历史记录。如果其他人已经使用了这个分支,则其历史记录不应被重写,而应该只是可见地还原提交。 - frandroid首先确保您的工作副本未被修改。
然后:
git diff --binary HEAD commit_sha_you_want_to_revert_to | git apply
然后就提交吧。不要忘记记录回滚的原因。
错误:没有完整的索引行,无法将二进制补丁应用于“some/image.png” 错误:some/image.png:补丁不适用
- GabLeRouxgit diff --binary HEAD commit_sha_you_want_to_revert_to | git apply
该命令用于二进制文件的更改恢复。 - weinerkgit revert A..Z
时,您会得到 error: commit X is a merge but no -m option was given.
的错误提示。 - Juliusz Gonerarestore
命令你也可以使用 restore
命令:
A <- B <- C <- D <- HEAD
假设你想让HEAD
看起来与A
完全相同。确保你已经拉取了最新的master
,然后创建一个新的分支。
git switch -c feature/flux-capacitor # synonymous with checkout -b
git restore --source A .
git add .
git commit
git push
restore
命令将一切(.
)更改为在--source
提交时的状态。 然后,您将其提交到本地分支并将其推送到源。 然后,您可以针对它打开PR。
这样做的好处是不会更改其他人可能已经基于工作的历史记录。 它还为未来的用户留下了有用的历史记录。
文档:git restore
这是对Jakub的答案中提供的解决方案之一的扩展。
我遇到了一个情况,需要回滚的提交比较复杂,其中几个提交是合并提交,并且我需要避免重写历史记录。我无法使用一系列git revert
命令,因为最终会出现反转更改之间出现冲突的情况。我最终采用了以下步骤。
首先,在将HEAD保留在分支尖端的同时,检出目标提交的内容:
git checkout -f <target-commit> -- .
(这个 -- 确保 <target-commit>
被解释为一次提交而不是一个文件;. 指的是当前目录。)
然后,确定在回滚的提交中添加了哪些文件,因此需要将其删除:
git diff --name-status --cached <target-commit>
新增的文件应该在行首显示"A"字母,且没有其他差异。现在,如果有任何需要删除的文件,请将这些文件标记为待删除状态:
git rm <filespec>[ <filespec> ...]
最后,提交还原:
git commit -m 'revert to <target-commit>'
如果需要的话,请确保我们回到所需的状态:git diff <target-commit> <current-commit>
不应该有任何差异。
git
HEAD 吗? - Suamere在共享仓库中(人们使用并且您想保留历史记录)撤销一组提交的简单方法是使用git revert
与git rev-list
结合使用。后者将为您提供一个提交列表,前者将执行还原操作。
有两种方法可以实现这一点。如果您想要在单个提交中撤销多个提交,请使用:
for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-commit $i; done
for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done
o---o---o---o---o---o--->
fff eee ddd ccc bbb aaa
for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done
<first-commit-sha> ^ .. <last-commit-sha>
是包含范围。此外,非常好的答案。在 Git Bash 中它对我起作用了。 - P Dgit checkout <first commit>
然后git archive
,然后手动创建一个解压归档版本的提交... - GACy20
git push -f HEAD~4:master
(假设远程分支是master)。是的,您可以像这样推送任何提交。 - u0b34a0f6aegit revert
命令撤销这些更改。请注意,这不会删除错误代码,而是创建一个新的提交来撤销它们。 - Jakub Narębskigit revert
将在history
中保留介入的工作。使用git checkout
和git reset --hard
来清除历史记录:请参见答案https://dev59.com/8HM_5IYBdhLWcg3wPAjU#38317763。 - WestCoastProjects