I was working with a friend on a project, and he edited a bunch of files that shouldn't have been edited. Somehow I merged his work into mine, either when I pulled it, or when I tried to just pick the specific files out that I wanted. I've been looking and playing for a long time, trying to figure out how to remove the commits that contain the edits to those files, it seems to be a toss up between revert and rebase, and there are no straightforward examples, and the docs assume I know more than I do.

下面是这个问题的简化版本:

给定下面的场景,我如何删除提交2?

$ mkdir git_revert_test && cd git_revert_test

$ git init
Initialized empty Git repository in /Users/josh/deleteme/git_revert_test/.git/

$ echo "line 1" > myfile

$ git add -A

$ git commit -m "commit 1"
[master (root-commit) 8230fa3] commit 1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 myfile

$ echo "line 2" >> myfile

$ git commit -am "commit 2"
[master 342f9bb] commit 2
 1 files changed, 1 insertions(+), 0 deletions(-)

$ echo "line 3" >> myfile

$ git commit -am "commit 3"
[master 1bcb872] commit 3
 1 files changed, 1 insertions(+), 0 deletions(-)

预期的结果是

$ cat myfile
line 1
line 3

以下是我一直试图恢复的一个例子

$ git revert 342f9bb
Automatic revert failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>'
and commit the result.

你的选择是

保留错误并引入修复 删除错误并更改历史记录。

您应该选择(1)如果错误的更改已被其他人拾取,以及(2)如果错误仅限于一个私有的未推送分支。

Git revert是一个自动执行(1)的工具,它创建了一个新的提交,撤销了之前的一些提交。您将在项目历史中看到错误和删除,但是从您的存储库中提取的人在更新时不会遇到问题。在你的例子中,它不是以自动的方式工作的,所以你需要编辑'myfile'(删除第2行),git添加myfile和git提交来处理冲突。然后,历史记录中将有四个提交,提交4将还原提交2。

如果没有人关心你的历史改变,你可以重写它并删除提交2(选择2)。最简单的方法是使用git rebase -i 8230fa3。这将使您进入一个编辑器,您可以通过删除提交来选择不包括错误的提交(并将“pick”放在其他提交消息旁边)。仔细研究一下这样做的后果。


听起来好像坏提交在某个时候被合并提交了。你的合并提交被拉回来了吗?如果是,那么你将使用git revert;你必须咬紧牙关,克服矛盾。如果不是,那么您可以选择rebase或revert,但是您可以在合并提交之前这样做,然后重做合并。

There's not much help we can give you for the first case, really. After trying the revert, and finding that the automatic one failed, you have to examine the conflicts and fix them appropriately. This is exactly the same process as fixing merge conflicts; you can use git status to see where the conflicts are, edit the unmerged files, find the conflicted hunks, figure out how to resolve them, add the conflicted files, and finally commit. If you use git commit by itself (no -m <message>), the message that pops up in your editor should be the template message created by git revert; you can add a note about how you fixed the conflicts, then save and quit to commit.

对于第二种情况,在合并之前修复问题,有两个子情况,这取决于合并后您是否做了更多的工作。如果你还没有,你可以简单地git重置-硬头^取消合并,做还原,然后重新合并。但我猜你有。所以,你最终会这样做:

在合并之前创建一个临时分支,并签出它 进行还原(或使用git rebase -i <something before bad commit> <temporary branch>来删除bad commit) 重做合并 将你的后续工作重新基于:git Rebase—to <临时分支> <旧合并提交> <实际分支> 移除临时分支


Git在计算要恢复的差异时使用的算法需要这样做

任何以后的提交都不会修改被还原的行。 在以后的历史中没有任何其他“相邻”提交。

“相邻”的定义基于上下文差异的默认行数,即3。如果'myfile'是这样构造的:

$ cat >myfile <<EOF
line 1
junk
junk
junk
junk
line 2
junk
junk
junk
junk
line 3
EOF
$ git add myfile
$ git commit -m "initial check-in"
 1 files changed, 11 insertions(+), 0 deletions(-)
 create mode 100644 myfile
$ perl -p -i -e 's/line 2/this is the second line/;' myfile
$ git commit -am "changed line 2 to second line"
[master d6cbb19] changed line 2
 1 files changed, 1 insertions(+), 1 deletions(-)
$ perl -p -i -e 's/line 3/this is the third line/;' myfile
$ git commit -am "changed line 3 to third line"
[master dd054fe] changed line 3
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git revert d6cbb19
Finished one revert.
[master 2db5c47] Revert "changed line 2"
 1 files changed, 1 insertions(+), 1 deletions(-)

然后一切都按预期工作。

第二个答案非常有趣。有一个尚未正式发布的特性(尽管在Git v1.7.2-rc2中可用)称为恢复策略。你可以像这样调用git:

Git revert—strategy resolve <commit>

它应该能更好地理解你的意思。我不知道可用的策略列表是什么,也不知道任何策略的定义。


有四种方法:

干净的方式,恢复,但保持日志恢复: Git revert—strategy resolve <commit> 严厉的方式,完全删除只有最后一个提交: git重置-软“头^”

注意:避免git重置——很难,因为它也会丢弃自上次提交以来文件中的所有更改。如果——软的不行,那就试试——混合的或者——保留的。

Rebase(显示最近5次提交的日志并删除你不想要的行,或重新排序,或将多个提交压缩在一个文件中,或做任何你想做的事情,这是一个非常通用的工具): git rebase -i HEAD~5

如果犯了错误:

git rebase --abort

快速rebase:只删除使用id的特定提交: Git rebase—to commit-id^ commit-id 替代方案:你还可以试试: Git选择commit-id 还有另一种选择: Git恢复——不提交 作为最后的手段,如果你需要完全自由的历史编辑(例如,因为git不允许你编辑你想要的内容),你可以使用这个非常快速的开源应用程序:reposurgeon。

注意:当然,所有这些更改都是在本地完成的,你应该在git push之后将更改应用到远程。如果你的repo不想删除提交(“不允许快进”,当你想删除已经提交的提交时就会发生),你可以使用git push -f强制推送更改。

注2:如果在一个分支上工作,你需要强制push,你应该绝对避免git push——force,因为这可能会覆盖其他分支(如果你对它们做了更改,即使你当前的签出是在另一个分支上)。当你强制push时,最好总是指定远程分支:git push——force origin your_branch。


你可以使用git rebase删除不需要的提交。 假设您将同事的topic分支的一些提交包含到您的topic分支中,但后来决定不需要这些提交。

git checkout -b tmp-branch my-topic-branch  # Use a temporary branch to be safe.
git rebase -i master  # Interactively rebase against master branch.

此时,文本编辑器将打开交互式rebase视图。例如

通过删除它们的行来删除您不想要的提交 保存并退出

如果改基不成功,删除临时分支并尝试其他策略。否则,请继续执行以下说明。

git checkout my-topic-branch
git reset --hard tmp-branch  # Overwrite your topic branch with the temp branch.
git branch -d tmp-branch  # Delete the temporary branch.

如果将主题分支推到远程,可能需要强制推,因为提交历史已经更改。如果其他人在同一个分支上工作,给他们提个醒。


所以你做了一些工作并推动了它,我们称之为提交A和b。你的同事也做了一些工作,提交C和d。你将你同事的工作合并到你的工作中(合并提交E),然后继续工作,也提交了(提交F),然后发现你的同事修改了一些他不应该修改的东西。

你的提交历史是这样的:

A -- B -- C -- D -- D' -- E -- F

你真的想摆脱C D D'既然你说你将同事的工作合并到你的工作中,这些提交已经“存在”了,所以使用git rebase等方法删除这些提交是不可以的。相信我,我试过了。

现在,我看到了两条出路:

if you haven't pushed E and F to your coworker or anyone else (typically your "origin" server) yet, you could still remove those from the history for the time being. This is your work that you want to save. This can be done with a git reset D' (replace D' with the actual commit hash that you can obtain from a git log At this point, commits E and F are gone and the changes are uncommitted changes in your local workspace again. At this point I would move them to a branch or turn them into a patch and save it for later. Now, revert your coworker's work, either automatically with a git revert or manually. When you've done that, replay your work on top of that. You may have merge conflicts, but at least they'll be in the code you wrote, instead of your coworker's code. If you've already pushed the work you did after your coworker's commits, you can still try and get a "reverse patch" either manually or using git revert, but since your work is "in the way", so to speak you'll probably get more merge conflicts and more confusing ones. Looks like that's what you ended up in...


从这里的其他答案,我有点困惑如何git rebase -i可以用来删除一个提交,所以我希望它可以在这里记下我的测试用例(非常类似于OP)。

下面是一个bash脚本,你可以粘贴在/tmp文件夹中创建一个测试存储库:

set -x

rm -rf /tmp/myrepo*
cd /tmp

mkdir myrepo_git
cd myrepo_git
git init
git config user.name me
git config user.email me@myself.com

mkdir folder
echo aaaa >> folder/file.txt
git add folder/file.txt
git commit -m "1st git commit"

echo bbbb >> folder/file.txt
git add folder/file.txt
git commit -m "2nd git commit"

echo cccc >> folder/file.txt
git add folder/file.txt
git commit -m "3rd git commit"

echo dddd >> folder/file.txt
git add folder/file.txt
git commit -m "4th git commit"

echo eeee >> folder/file.txt
git add folder/file.txt
git commit -m "5th git commit"

现在,我们有了一个包含以下内容的file.txt文件:

aaaa
bbbb
cccc
dddd
eeee

此时,HEAD是第5次提交,HEAD~1是第4次提交,HEAD~4是第1次提交(因此HEAD~5不存在)。假设我们想要删除第三次提交——我们可以在myrepo_git目录下发出这个命令:

git rebase -i HEAD~4

(注意git rebase -i HEAD~5的结果是“fatal:需要一次修订;上游HEAD~5”无效。)一个文本编辑器(参见@Dennis回答中的截图)将打开以下内容:

pick 5978582 2nd git commit
pick 448c212 3rd git commit
pick b50213c 4th git commit
pick a9c8fa1 5th git commit

# Rebase b916e7f..a9c8fa1 onto b916e7f
# ...

所以我们得到了所有的提交,因为(但不包括)我们所请求的HEAD~4。删除行pick 448c212第三次git提交并保存文件;你会从git rebase中得到这样的响应:

error: could not apply b50213c... 4th git commit

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To check out the original branch and stop rebasing run "git rebase --abort".
Could not apply b50213c... 4th git commit

此时,在文本编辑器中打开myrepo_git/folder/file.txt;你会看到它被修改了:

aaaa
bbbb
<<<<<<< HEAD
=======
cccc
dddd
>>>>>>> b50213c... 4th git commit

基本上,git看到当HEAD第二次提交时,有aaaa + bbbb的内容;然后它有一个补丁添加的cccc+dddd,它不知道如何附加到现有的内容。

所以这里git不能为你决定——你必须做出决定:通过删除第三次提交,你要么保留它所带来的更改(这里是cccc行)——要么不保留。如果你不这样做,只需删除额外的行-包括cccc -在文件夹/file.txt使用文本编辑器,所以它看起来像这样:

aaaa
bbbb
dddd

... 然后保存文件夹/file.txt。现在你可以在myrepo_git目录下执行以下命令:

$ nano folder/file.txt  # text editor - edit, save
$ git rebase --continue
folder/file.txt: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add

啊-所以为了标记我们已经解决了冲突,我们必须在git rebase——continue之前添加文件夹/file.txt:

$ git add folder/file.txt
$ git rebase --continue

这里再次打开文本编辑器,显示第4行git commit -这里我们有机会更改提交消息(在这种情况下可以有意义地更改为第4(并删除第3)提交或类似)。假设你不想退出文本编辑器而不保存;一旦你这样做了,你会得到:

$ git rebase --continue
[detached HEAD b8275fc] 4th git commit
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master.

在这一点上,现在你有一个这样的历史(你也可以用say gitk检查。或其他工具)的/file.txt文件夹的内容(显然,原始提交的时间戳没有改变):

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   dddd
                |  +eeee

如果之前,我们决定保留cccc行(我们删除的第三次git提交的内容),我们会有:

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +cccc
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   cccc
                |   dddd
                |  +eeee

好吧,这是我希望我能找到的那种阅读,开始研究git rebase如何在删除提交/修订方面工作;所以希望这也能帮助到其他人…


这里有一个简单的解决方法:

git rebase -i HEAD~x

其中x是提交的数量。

在提交之前输入drop:

就这样,完成了。如果您删除的提交已经在远程上,则必须强制push。因为——force被认为是有害的,所以使用git push——force-with-lease。


方法1

首先获取需要恢复的提交散列(例如:1406cd61)。简单的修复将在命令之下,

$ git revert 1406cd61

如果你在1406cd61文件提交后提交了更多与1406cd61文件相关的更改,上述简单命令将无法工作。然后你要做下面的步骤,也就是摘樱桃。

方法2

请遵循下面的操作顺序,因为我们正在使用——force,您需要对git回购有管理权限才能做到这一点。

步骤1:找到要删除git日志的提交前的提交

步骤2:签出提交git Checkout <commit hash>

步骤3:使用当前的checkout commit git checkout -b <new branch>创建一个新的分支

第4步:现在你需要在移除commit git cherry-pick <commit hash>之后添加commit

步骤5:现在对所有想要保留的提交重复步骤4。

步骤6:将所有提交添加到新分支并提交之后。检查所有东西是否处于正确的状态并按预期工作。再次检查已提交的所有内容:git状态

步骤7:切换到你的broken branch git checkout <broken branch>

第8步:现在执行一个硬重置,在你想要删除git重置之前的提交分支上——hard <commit hash>

步骤9:将固定的分支合并到这个分支git Merge <分支名称>

步骤10:将合并的更改推回原点。警告:这将覆盖远程回购!Git push——force origin <分支名称>

您可以在不创建新分支的情况下执行该过程,将步骤2和步骤3替换为步骤8,然后不执行步骤7和步骤9。


Git恢复——策略解析 如果commit是一个merge: 使用git revert——strategy resolve -m


我能想到一个很简单的方法

git重置——hard HEAD <你的提交ID>

然后重置远程分支

Git push origin -f


我有一个简单的解决方案,使用补丁恢复您所有的更改。

检查当前的主要分支机构(例如开发)

git checkout develop

在历史日志中查找你的commit-id,只签出你对一个新分支的更改:

git log
git checkout -b your-branch <your-commit-id>

在你的分支中查找,找到你想要恢复到的前一个状态:

git checkout -b prev-status <previous-commit-id>

创建一个补丁,可以恢复您的所有更改:

git diff your-branch..prev-status > reverts.patch
# the comparing order of branches is important

检查当前的头分支,并应用恢复补丁

git checkout origin develop
git apply reverts.patch
git add *
git commit -m "revert all my changes"

这里有一个快速的例子,如何用egit做到这一点:

我想删除我添加文件的提交 右键点击要删除的文件之前的提交,选择“interactive rebase” 在rebase视图中选择commit three并单击上面的跳过图标。在胡佛这个图标,它告诉你提交将被删除。 点击start rebase。 进入git staging,点击push。 在弹出窗口,出现点击力量和前进 在此之后,您可以看到提交已被删除。


最近遇到了类似的问题,最后这样做,感觉更简单,可以在少数情况下工作。

我的情况:

% git log --oneline

4ad59d6 commit 3
f244533 commit 2
c5b4688 commit 1

我们要做的是创建commit 4,并将“commit 2”中的更改还原。这就是我们所做的:

获取commit 2中的更改: % git显示> ~/patches/commit.2.patch 恢复更改: % git应用-R ~/patches/commit.2.patch 创建新的提交: % git commit -Am“提交4:恢复提交2中的更改”

附注:这适用于我们可以轻松应用revert的情况-如果相同的代码行在一定时间内被修改了,这将不起作用。


最简单的方式, 只需查找日志并获得提交的计数,并检查出您想要删除的commit-msg。

将x替换为count,我将显示所有的commit-msg,只需删除你想删除的commit-msg。

# git 变基 -i HEAD ~ x