删除特定提交

时间:2010-05-30 10:57:29

标签: git commit revert cherry-pick

我和一位朋友在一个项目上合作,他编辑了一堆本不应该编辑过的文件。不知怎的,我将他的作品合并到了我的作品中,无论是我拉它还是当我试图选择我想要的特定文件时。我一直在寻找和玩很长一段时间,试图找出如何删除包含这些文件编辑的提交,它似乎是revert和rebase之间的折腾,并没有简单的例子,并且docs假设我比我知道的更多。

所以这是问题的简化版本:

鉴于以下情况,如何删除提交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.

12 个答案:

答案 0 :(得分:228)

有四种方法:

  • 清理方式,恢复但保留日志恢复:

    git revert --strategy resolve <commit>
    
  • 苛刻的方式,完全删除最后一次提交:

    git reset --soft "HEAD^"
    

注意:避免git reset --hard,因为它也会丢弃自上次提交以来文件中的所有更改。如果--soft不起作用,请尝试--mixed--keep

  • Rebase(显示最近5次提交的日志并删除您不想要的行,或重新排序,或将多个提交压缩成一个,或者做任何您想做的事情,这是一个非常多才多艺工具):

    git rebase -i HEAD~5
    

如果犯了错误:

git rebase --abort
  • 快速rebase:仅使用其ID删除特定提交:

    git rebase --onto commit-id^ commit-id
    
  • 替代方案:您也可以尝试:

    git cherry-pick commit-id
    
  • 另一种选择:

    git revert --no-commit
    
  • 作为最后的手段,如果您需要完全自由编辑历史记录(例如,因为git不允许您编辑您想要的内容),您可以使用此very fast开源代码申请:reposurgeon

注意:当然,所有这些更改都是在本地完成的,您应该git push之后将更改应用到远程。如果您的repo不想删除提交(&#34;不允许快进&#34;,当您想删除已经推送的提交时会发生这种情况),您可以使用{{1}强制推动变革。

注意2:如果在分支上工作而你需要强制推送,你绝对应该避免使用git push -f,因为这可能会覆盖其他分支(如果你对其进行了更改,即使你当前的结账在另一个分支上) )。当您强制推送时,始终指定远程分支git push --force

答案 1 :(得分:75)

非常简单的方法

git rebase -i HEAD~x

(x =提交次数)

执行记事本文件后,除了提交enter image description here

之外,还会打开'put'

就是你的完成...... 只需同步git仪表板,更改将被推送到远程。如果您提交的提交已经在遥控器上,则必须强制进行更新。由于--force被视为harmful,因此请使用**shop** **product** **location** **time** **count_products** store1 ,A,B,C X 8.30 pm 3 store1 ,G,F Y 8.41 pm 2 store1 ,C,D,T,R Z 9.02 pm 4

答案 2 :(得分:56)

Git在计算要还原的差异时使用的算法需要

  1. 以后的提交不会修改被还原的行。
  2. 历史后期没有任何其他“相邻”提交。
  3. “相邻”的定义基于上下文差异的默认行数,即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中可用)称为Revert Strategy。你可以像这样调用git:

      

    git revert - 策略解析&lt; commit&gt;

    它应该做得更好,弄清楚你的意思。我不知道可用策略列表是什么,也不知道任何策略的定义。

答案 3 :(得分:36)

您的选择是在

之间
  1. 保留错误并引入修复和
  2. 删除错误并更改历史记录。
  3. 您应该选择(1)其他人是否接收到错误更改,以及(2)如果错误仅限于私人未推送分支。

    Git revert是一个自动化的工具(1),它创建了一个新的提交,撤消了之前的一些提交。您将在项目历史记录中看到错误和删除,但从存储库中提取的人员在更新时不会遇到问题。它在您的示例中不是以自动方式工作,因此您需要编辑'myfile'(删除第2行),执行git add myfilegit commit来处理冲突。然后,您将在历史记录中结束四次提交,其中提交4将恢复提交2。

    如果没有人关心您的历史记录发生变化,您可以重写它并删除提交2(选项2)。这样做的简单方法是使用git rebase -i 8230fa3。这将使您进入编辑器,您可以选择不通过删除提交来包含错误提交(并在其他提交消息旁边保留“选择”。请阅读consequences of doing this

答案 4 :(得分:20)

Approch 1

首先获取需要还原的提交哈希值(例如:1406cd61)。简单的修复将在命令下面,

$ git revert 1406cd61

如果您在1406cd61提交后提交了与1406cd61文件相关的更多更改,则上述简单命令将不起作用。然后你必须做以下步骤,这是樱桃采摘。

Approch 2

请遵循以下步骤,因为我们使用--force,您需要通过git repo拥有管理员权限才能执行此操作。

第1步:在您要删除的提交之前找到提交git log

第2步:结帐提交git checkout <commit hash>

第3步:使用您当前的结帐提交git checkout -b <new branch>

创建一个新分支

第4步:现在您需要在删除提交后添加提交git cherry-pick <commit hash>

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

第6步:所有提交都已添加到新分支并提交。检查一切是否处于正确状态并按预期工作。仔细检查所有提交的内容:git status

第7步:切换到已损坏的分支git checkout <broken branch>

第8步:现在,在您要移除的git reset --hard <commit hash>

之前,在已损坏的分支上执行硬重置

第9步:将固定分支合并到此分支git merge <branch name>

步骤10:将合并的更改推回原点。警告:这将覆盖远程仓库! git push --force origin <branch name>

您可以在不创建新分支的情况下执行此过程,方法是替换步骤2&amp; 3与步骤8然后不执行步骤7&amp; 9。

答案 5 :(得分:17)

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

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-rebase-todo

  1. 通过删除行
  2. 删除您不想要的提交
  3. 保存并退出
  4. 如果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.
    

    如果您将主题分支推送到远程,则可能需要强制推送,因为提交历史记录已更改。如果其他人在同一个分支机构工作,请给他们一个抬头。

答案 6 :(得分:7)

从这里的其他答案中,我对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将不存在)。假设我们要删除第3次提交 - 我们可以在myrepo_git目录中发出此命令:

git rebase -i HEAD~4

请注意,git rebase -i HEAD~5会导致&#34;致命:需要单个修订;无效的上游HEAD~5&#34;。)文本编辑器(请参阅{{ 3}})将打开这些内容:

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 3rd git commit并保存文件;您将从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 - folder/file.txt,所以它看起来像这样:

aaaa
bbbb
dddd

...然后保存folder/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

啊 - 所以为了表明我们已经解决了冲突,我们必须<{em> git add folder/file.txt,然后再做git rebase --continue:< / p>

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

此处再次打开文本编辑器,显示行4th git commit - 这里我们有机会更改提交消息(在这种情况下可以有意义地更改为4th (and removed 3rd) commit或类似)。让我们说你不想 - 所以只需退出文本编辑器而不保存;一旦你这样做,你就会得到:

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

此时,您现在拥有gitk .内容的这样的历史记录(您也可以使用folder/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(我们删除的第3个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在删除提交/修订方面的工作原理;所以希望它也可以帮助别人......

答案 7 :(得分:2)

所以听起来好的提交在某些时候被合并到提交中。你的合并提交已被撤销了吗?如果是,那么您将要使用git revert;你必须抓紧牙关,解决冲突。如果不是,那么你可以设想rebase或revert,但你可以在合并提交之前,然后重做合并。

我们可以为第一个案例提供帮助,真的。在尝试恢复并发现自动恢复失败后,您必须检查冲突并适当地修复它们。这与修复合并冲突的过程完全相同;您可以使用git status查看冲突的位置,编辑未合并的文件,查找冲突的文件,找出解决方法,添加冲突的文件,最后提交。如果您单独使用git commit(无-m <message>),则编辑器中弹出的消息应为git revert创建的模板消息;您可以添加有关如何修复冲突的注释,然后保存并退出提交。

对于第二种情况,在合并之前解决问题,有两个子句,取决于你是否在合并后做了更多的工作。如果还没有,你可以简单地git reset --hard HEAD^敲除合并,进行恢复,然后重做合并。但我猜你有。所以,你最终会做这样的事情:

  • 在合并之前创建一个临时分支,并将其检出
  • 执行还原(或使用git rebase -i <something before the bad commit> <temporary branch>删除错误提交)
  • 重做合并
  • 重新开始您的后续工作:git rebase --onto <temporary branch> <old merge commit> <real branch>
  • 删除临时分支

答案 8 :(得分:1)

所以你做了一些工作并推动它,让我们称他们为A和B。你的同事做了一些工作,提交C和D.你把你的同事工作合并到你的(合并提交E),然后继续工作,也承诺了(承诺F),并发现你的同事改变了一些他不应该做的事情。

所以你的提交历史是这样的:

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

你真的想摆脱C,D和D'。既然你说你把你的同事工作合并到了你的工作中,那么这些提交已经“在那里”,所以删除提交的工具就像使用例如git rebase是禁忌。相信我,我已经尝试过了。

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

  • 如果您尚未将E和F推送给您的同事或其他任何人(通常是您的“原始”服务器),您仍然可以暂时删除历史记录中的那些。这是您要保存的工作。这可以用

    完成
    git reset D'
    

    (将D'替换为您可以从git log

    获取的实际提交哈希

    此时,提交E和F消失,更改是本地工作空间中未提交的更改。此时我会将它们移动到分支或将它们转换为补丁并保存以供日后使用。现在,使用git revert或手动自动恢复同事的工作。当你完成后,重温你的工作。您可能有合并冲突,但至少它们会在您编写的代码中,而不是您的同事的代码。

  • 如果您已经推动了在同事提交后所做的工作,您仍然可以手动或使用git revert尝试获得“反向补丁”,但由于您的工作是“在方式“,可以说你可能会得到更多的合并冲突和更令人困惑的冲突。看起来这就是你最终的结果......

答案 9 :(得分:0)

git revert-策略解决 如果提交是合并的: 使用git revert --strategy resolve -m 1

答案 10 :(得分:0)

我会看到一个非常简单的方法

git reset --hard HEAD <YOUR COMMIT ID>

然后重置远程分支

git push origin -f

答案 11 :(得分:0)

我有一个简单的解决方案,可以使用补丁来还原您的所有更改。

  1. 检查当前的主分支(例如开发)
git checkout develop
  1. 在历史日志中查找您的提交 ID,并仅将您的更改签出到新分支中:
git log
git checkout -b your-branch <your-commit-id>
  1. 在您的分支中查找,并找到您要恢复到的先前状态:
git checkout -b prev-status <previous-commit-id>
  1. 创建一个可以恢复所有更改的补丁:
git diff your-branch..prev-status > reverts.patch
# the comparing order of branches is important
  1. 检查当前的头分支并应用恢复补丁
git checkout origin develop
git apply reverts.patch
git add *
git commit -m "revert all my changes"