删除特定提交

发布于 2024-09-03 15:45:24 字数 1146 浏览 12 评论 0原文

我正在和一个朋友一起做一个项目,他编辑了一堆不应该编辑的文件。不知何故,我将他的工作合并到我的工作中,无论是当我拉出它时,还是当我试图挑选出我想要的特定文件时。我已经寻找和玩了很长时间,试图弄清楚如何删除包含对这些文件的编辑的提交,这似乎是在恢复和变基之间进行折腾,并且没有简单的示例,并且文档假设我知道的比我实际知道的多。

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

给定以下场景,如何删除提交 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.

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.

So here is a simplified version of the question:

Given the following scenario, how do I remove commit 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(-)

The expected result is

$ cat myfile
line 1
line 3

Here is an example of how I have been trying to revert

$ 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

天气好吗我好吗 2024-09-10 15:45:24

有四种方法可以做到这一点(将“commid-id”替换为您提交的哈希值):

  • 干净的方式,恢复但保留在日志中恢复:

     git revert --strategy 解析提交 ID
    

    注意:如果要删除的提交是合并提交,则需要附加 -m 1 (感谢 @Powertoaster 的提示!)。

  • 严厉的方式,仅删除最后一次提交:

     git reset --soft "HEAD^"
    

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

  • 交互式变基(这将显示最近 5 次提交的日志,并删除您不需要的行,或重新排序,或将多个提交压缩为一个,或执行您想要的任何其他操作,这是一个非常通用的工具) :

    <前><代码> git rebase -i HEAD~5

    如果犯了错误:

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

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

     gitcherry-pick commit-id
    
  • 另一种替代方案:

     git revert --no-commit
    
  • 作为最后的手段,如果您需要完全自由的历史编辑(例如,因为 git 不允许您编辑您想要的内容),您可以使用此 非常快开源应用程序:reposurgeon

注意:当然,所有这些更改都是在本地完成的,您应该随后使用 git push 将更改应用到远程。如果您的存储库不想删除提交(“不允许快进”,当您想删除已推送的提交时会发生这种情况),您可以使用 git push --force 强制推动更改。

注意2:如果在一个分支上工作并且您需要强制推送,您绝对应该避免 git push --force 因为这可能会覆盖其他分支(如果您对它们进行了更改,即使您当前签出在另一个分支上)。为了确保避免这种情况,在强制推送时始终指定远程分支git push --force origin your_branch

There are four ways of doing so (replace "commid-id" with your commit's hash):

  • Clean way, reverting but keep in log the revert:

      git revert --strategy resolve commit-id
    

    Note: if the commit to remove is a merge commit, you will need to append -m 1 (thanks to @Powertoaster for the tip!).

  • Harsh way, remove altogether only the last commit:

      git reset --soft "HEAD^"
    

    Note: Avoid git reset --hard as it will also discard all changes in files since the last commit. If --soft does not work, rather try --mixed or --keep.

  • Interactive rebase (this will show the log of the last 5 commits and delete the lines you don't want, or reorder, or squash multiple commits in one, or do anything else you want, this is a very versatile tool):

      git rebase -i HEAD~5
    

    And if a mistake is made:

      git rebase --abort
    
  • Quick rebase: remove only a specific commit using its id:

      git rebase --onto commit-id^ commit-id
    
  • Alternative: you could also try:

      git cherry-pick commit-id
    
  • Yet another alternative:

      git revert --no-commit
    
  • As a last resort, if you need full freedom of history editing (eg, because git don't allow you to edit what you want to), you can use this very fast open source application: reposurgeon.

Note: of course, all these changes are done locally, you should git push afterwards to apply the changes to the remote. And in case your repo doesn't want to remove the commit ("no fast-forward allowed", which happens when you want to remove a commit you already pushed), you can use git push --force to force push the changes.

Note2: if working on a branch and you need to force push, you should absolutely avoid git push --force because this may overwrite other branches (if you have made changes in them, even if your current checkout is on another branch). To ensure to avoid that, always specify the remote branch when you force push: git push --force origin your_branch.

不寐倦长更 2024-09-10 15:45:24

这是一个简单的解决方案:

git rebase -i HEAD~x

其中 x 是提交次数。

在提交之前输入 drop

在此处输入图像描述

就这样,您就完成了。如果您删除的提交已经在远程上,您将必须强制推送。由于 --force 被认为有害,请使用 git push --force-with-lease

Here is an easy solution:

git rebase -i HEAD~x

Where x is the number of commits.

Enter drop before your commit:

enter image description here

And that's it, you are done. If the commit you drop was already on the remote, you will have to force push. Since --force is considered harmful, use git push --force-with-lease.

童话 2024-09-10 15:45:24

Git 在计算要恢复的差异时使用的算法要求

  1. 正在恢复的行不会被任何后续提交修改。
  2. 历史上后来没有任何其他“相邻”提交。

“相邻”的定义基于上下文差异的默认行数,即 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 --策略解析

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

The algorithm that Git uses when calculating diffs to be reverted requires that

  1. The lines being reverted are not modified by any later commits.
  2. There not be any other "adjacent" commits later in history.

The definition of "adjacent" is based on the default number of lines from a context diff, which is 3. So if 'myfile' was constructed like this:

$ 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(-)

Then it all works as expected.

The second answer was very interesting. There is a feature that has not yet been officially released (though it is available in Git v1.7.2-rc2) called Revert Strategy. You can invoke git like this:

git revert --strategy resolve <commit>

and it should do a better job of figuring out what you meant. I do not know what the list of available strategies is, nor do I know the definition of any strategy.

病女 2024-09-10 15:45:24

方法 1

首先获取您需要恢复的提交哈希(例如:1406cd61)。简单的修复将在下面的命令中进行,

$ git revert 1406cd61

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

方法 2

请按照以下操作顺序进行操作,由于我们使用 --force,您需要对 git 存储库具有管理员权限才能执行此操作。

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

第 2 步: 签出该提交 git checkout

第 3 步: 使用当前签出提交创建一个新分支 git checkout -b

第 4 步: > 现在您需要在删除的提交之后添加该提交 gitcherry-pick

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

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

第 7 步: 切换到损坏的分支 git checkout

第 8 步:现在对损坏的分支执行硬重置,将其重置为您要删除的分支之前的提交 git reset --hard

第 9 步: 将您的固定分支合并到此分支中 git merge <分支名称>

第 10 步: 将合并的更改推回原点。警告:这将覆盖远程存储库! git push --force origin <分支名称>

您可以通过替换步骤 2 和 来完成此过程,而无需创建新分支。 3与步骤8然后不执行步骤7和3 9.

Approach 1

First get the commit hash(ex:1406cd61) that you need to revert. simple fix will be below command,

$ git revert 1406cd61

if you have commit more changes related to 1406cd61 files after 1406cd61 commit, Above simple command will not work. Then you have to do the below steps, which is cherry picking.

Approach 2

Please follow below sequence of actions, Since we are using --force you need to have admin rights over the git repo to do this.

Step 1: Find the commit before the commit you want to remove git log

Step 2: Checkout that commit git checkout <commit hash>

Step 3: Make a new branch using your current checkout commit git checkout -b <new branch>

Step 4: Now you need to add the commit after the removed commit git cherry-pick <commit hash>

Step 5: Now repeat Step 4 for all other commits you want to keep.

Step 6: Once all commits have been added to your new branch and have been committed. Check that everything is in the correct state and working as intended. Double check everything has been committed: git status

Step 7: Switch to your broken branch git checkout <broken branch>

Step 8: Now perform a hard reset on the broken branch to the commit prior to the one your want to remove git reset --hard <commit hash>

Step 9: Merge your fixed branch into this branch git merge <branch name>

Step 10: Push the merged changes back to origin. WARNING: This will overwrite the remote repo! git push --force origin <branch name>

You can do the process without creating a new branch by replacing Step 2 & 3 with Step 8 then not carry out Step 7 & 9.

超可爱的懒熊 2024-09-10 15:45:24

您的选择是

  1. 保留错误并引入修复,还是
  2. 删除错误并更改历史记录。

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

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

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

Your choice is between

  1. keeping the error and introducing a fix and
  2. removing the error and changing the history.

You should choose (1) if the erroneous change has been picked up by anybody else and (2) if the error is limited to a private un-pushed branch.

Git revert is an automated tool to do (1), it creates a new commit undoing some previous commit. You'll see the error and removal in the project history but people who pull from your repository won't run into problems when they update. It's not working in an automated manner in your example so you need to edit 'myfile' (to remove line 2), do git add myfile and git commit to deal with the conflict. You will then end up with four commits in your history, with commit 4 reverting commit 2.

If nobody cares that your history changes, you can rewrite it and remove commit 2 (choice 2). The easy way to do this is to use git rebase -i 8230fa3. This will drop you into an editor and you can choose not to include the erroneous commit by removing the commit (and keeping "pick" next to the other commit messages. Do read up on the consequences of doing this.

木落 2024-09-10 15:45:24

您可以使用 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.

此时,您的文本编辑器将打开交互式变基视图。例如

git-rebase-todo

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

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

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.

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

You can remove unwanted commits with git rebase.
Say you included some commits from a coworker's topic branch into your topic branch, but later decide you don't want those commits.

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

At this point your text editor will open the interactive rebase view. For example

git-rebase-todo

  1. Remove the commits you don't want by deleting their lines
  2. Save and quit

If the rebase wasn't successful, delete the temporary branch and try another strategy. Otherwise continue with the following instructions.

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.

If you're pushing your topic branch to a remote, you may need to force push since the commit history has changed. If others are working on the same branch, give them a heads up.

鱼窥荷 2024-09-10 15:45:24

从这里的其他答案中,我对如何使用 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 [email protected]

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 结果为“致命:需要单个修订;无效的上游 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 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 无法为您做出决定 - 必须由您做出决定:通过删除第三次提交,您要么保留它引入的更改(此处为line cccc) - 或者你不这样做。如果不这样做,只需使用文本编辑器删除 folder/file.txt 中的多余行 - 包括 cccc - 即可,如下所示:

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

啊 - 所以为了标记我们已经解决了冲突,我们必须 git add folder/file.txt,在执行 git rebase --continue 之前:

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

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

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

此时,你已经有了这样的 文件夹内容的历史记录(你也可以使用 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 是如何在删除提交/修订方面工作的;所以希望它也能帮助其他人......

From other answers here, I was kind of confused with how git rebase -i could be used to remove a commit, so I hope it's OK to jot down my test case here (very similar to the OP).

Here is a bash script that you can paste in to create a test repository in the /tmp folder:

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 [email protected]

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"

At this point, we have a file.txt with these contents:

aaaa
bbbb
cccc
dddd
eeee

At this point, HEAD is at the 5th commit, HEAD~1 would be the 4th - and HEAD~4 would be the 1st commit (so HEAD~5 wouldn't exist). Let's say we want to remove the 3rd commit - we can issue this command in the myrepo_git directory:

git rebase -i HEAD~4

(Note that git rebase -i HEAD~5 results with "fatal: Needed a single revision; invalid upstream HEAD~5".) A text editor (see screenshot in @Dennis' answer) will open with these contents:

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
# ...

So we get all commits since (but not including) our requested HEAD~4. Delete the line pick 448c212 3rd git commit and save the file; you'll get this response from 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

At this point open myrepo_git/folder/file.txt in a text editor; you'll see it has been modified:

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

Basically, git sees that when HEAD got to 2nd commit, there was content of aaaa + bbbb; and then it has a patch of added cccc+dddd which it doesn't know how to append to the existing content.

So here git cannot decide for you - it is you who has to make a decision: by removing the 3rd commit, you either keep the changes introduced by it (here, the line cccc) -- or you don't. If you don't, simply remove the extra lines - including the cccc - in folder/file.txt using a text editor, so it looks like this:

aaaa
bbbb
dddd

... and then save folder/file.txt. Now you can issue the following commands in myrepo_git directory:

$ 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

Ah - so in order to mark that we've solved the conflict, we must git add the folder/file.txt, before doing git rebase --continue:

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

Here a text editor opens again, showing the line 4th git commit - here we have a chance to change the commit message (which in this case could be meaningfully changed to 4th (and removed 3rd) commit or similar). Let's say you don't want to - so just exit the text editor without saving; once you do that, you'll get:

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

At this point, now you have a history like this (which you could also inspect with say gitk . or other tools) of the contents of folder/file.txt (with, apparently, unchanged timestamps of the original commits):

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

And if previously, we decided to keep the line cccc (the contents of the 3rd git commit that we removed), we would have had:

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

Well, this was the kind of reading I hoped I'd have found, to start grokking how git rebase works in terms of deleting commits/revisions; so hope it might help others too...

别想她 2024-09-10 15:45:24

所以听起来这个错误的提交在某个时候被合并到了合并提交中。您的合并提交已经被拉取了吗?如果是,那么您将需要使用 git revert ;你必须咬紧牙关,解决冲突。如果不是,那么您可以想象变基或恢复,但您可以在合并提交之前执行此操作,然后重做合并。

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

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

  • 在合并之前创建一个临时分支,然后检查它
  • 是否进行恢复(或使用 git rebase -i <在错误提交之前的东西> <临时分支> ; 以删除错误的提交)
  • 重做合并
  • 变基您后续的工作:git rebase --onto; <旧的合并提交> <真实分支>
  • 删除临时分支

So it sounds like the bad commit was incorporated in a merge commit at some point. Has your merge commit been pulled yet? If yes, then you'll want to use git revert; you'll have to grit your teeth and work through the conflicts. If no, then you could conceivably either rebase or revert, but you can do so before the merge commit, then redo the merge.

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.

For the second case, fixing the problem before your merge, there are two subcases, depending on whether you've done more work since the merge. If you haven't, you can simply git reset --hard HEAD^ to knock off the merge, do the revert, then redo the merge. But I'm guessing you have. So, you'll end up doing something like this:

  • create a temporary branch just before the merge, and check it out
  • do the revert (or use git rebase -i <something before the bad commit> <temporary branch> to remove the bad commit)
  • redo the merge
  • rebase your subsequent work back on: git rebase --onto <temporary branch> <old merge commit> <real branch>
  • remove the temporary branch
≈。彩虹 2024-09-10 15:45:24

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

  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"

I have a simple solution using a patch to revert all your changes.

  1. Checkout current head branch (e.g. develop)
git checkout develop
  1. Lookup your commit-id in the history log, and check out only your changes into a new branch:
git log
git checkout -b your-branch <your-commit-id>
  1. Look up in your branch, and find the previous status you want to revert to:
git checkout -b prev-status <previous-commit-id>
  1. Create a patch that can revert all your changes:
git diff your-branch..prev-status > reverts.patch
# the comparing order of branches is important
  1. checkout current head branch and apply the reverting patch
git checkout origin develop
git apply reverts.patch
git add *
git commit -m "revert all my changes"
天赋异禀 2024-09-10 15:45:24

最近遇到了类似的问题,最终这样做了,感觉更简单,适用于少数情况。

我的场景:

% git log --oneline

4ad59d6 commit 3
f244533 commit 2
c5b4688 commit 1

我们想要做的是创建提交 4,并恢复“提交 2”中的更改。这就是我们所做的:

  1. 获取提交 2 中的更改:

    % git show > ~/patches/commit.2.patch

  2. 恢复更改:

    % git apply -R ~/patches/commit.2.patch

  3. 创建新提交:

    % git commit -Am“提交 4:恢复提交 2 中的更改”

PS:这适用于我们可以轻松应用恢复的情况 - 如果同一行代码随着时间的推移而被修改,这将不起作用。

Recently encountered a similar issue and ended up doing this which felt simpler which can work for few cases.

My scenario:

% git log --oneline

4ad59d6 commit 3
f244533 commit 2
c5b4688 commit 1

What we wanted to do wes to create commit 4 with changes in "commit 2" reverted. This is what we did:

  1. Get the changes in commit 2:

    % git show > ~/patches/commit.2.patch

  2. Revert the changes:

    % git apply -R ~/patches/commit.2.patch

  3. Create new commit:

    % git commit -Am "commit 4 : reverts changes in commit 2"

P.S: This works for cases where we can easily apply revert - if the same lines of code has be amended in course of time, this would not work.

久夏青 2024-09-10 15:45:24

因此,您做了一些工作并推送了它,我们称它们为提交 A 和 B。您的同事也做了一些工作,提交了 C 和 D。您将同事的工作合并到您的工作中(合并提交 E),然后继续工作,提交了该工作,也(提交F),并发现你的同事改变了一些他不应该改变的东西。

所以你的提交历史看起来像这样:

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

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

现在,我看到了两种解决方法:

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

    来完成

    git 重置 D'
    

    (将 D' 替换为您可以从 git log 获取的实际提交哈希值

    此时,提交 E 和 F 已消失,并且更改再次成为本地工作区中未提交的更改。此时,我会将它们移动到分支或将它们变成补丁并保存以供以后使用。现在,可以使用 git revert 自动或手动恢复同事的工作。完成后,在此基础上重播您的工作。您可能存在合并冲突,但至少它们会出现在您编写的代码中,而不是您同事的代码中。

  • 完成后,重播您的工作, 在同事提交后,您已经推送了您所做的工作,您仍然可以尝试手动或使用 git revert 获取“反向补丁”,但由于您的工作“妨碍”,所以说起来,您可能会遇到更多合并冲突和更令人困惑的冲突,看起来这就是您最终遇到的情况...

So you did some work and pushed it, lets call them commits A and B. Your coworker did some work as well, commits C And D. You merged your coworkers work into yours (merge commit E), then continued working, committed that, too (commit F), and discovered that your coworker changed some things he shouldn't have.

So your commit history looks like this:

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

You really want to get rid of C, D, and D'. Since you say you merged your coworkers work into yours, these commits already "out there", so removing the commits using e.g. git rebase is a no-no. Believe me, I've tried.

Now, I see two ways out:

  • 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...

虐人心 2024-09-10 15:45:24

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

git revert --strategy resolve
if commit is a merge:
use git revert --strategy resolve -m 1

非要怀念 2024-09-10 15:45:24

以下是如何使用 egit 执行此操作的快速示例:

  1. 我想删除添加文件 3 的提交 3
    输入图片此处描述
  2. 右键单击​​要删除的提交之前的提交,然后选择“交互式变基”
    输入图片此处描述
  3. 在 rebase 视图中选择提交 3,然后单击上面的“跳过”图标。将鼠标悬停在该图标上时,它会告诉您该提交将被删除。
    输入图片此处描述
  4. 单击开始变基。
    输入图片此处描述
  5. 进入 git staging 并单击“推送”。
    输入图片此处描述
  6. 在出现的弹出窗口中单击“强制并继续”
    输入图片此处描述
  7. 之后您可以看到该提交已被删除。
    输入图片此处描述

Here is a quick example of how to do this with egit:

  1. I want to delete the commit 3 where I added the file 3
    enter image description here
  2. Right click on the commit before the one to be deleted and choose "interactive rebase"
    enter image description here
  3. In the rebase view choose the commit three and click on the icon skip above. On hoover over this icon, it tells you that the commit will be deleted.
    enter image description here
  4. Click on start rebase.
    enter image description here
  5. Go in git staging and click on push.
    enter image description here
  6. In the popup which appears click on force and go forward
    enter image description here
  7. After this you can see that the commit was deleted.
    enter image description here
樱花坊 2024-09-10 15:45:24

最简单的方法,
只需查找日志并计算提交的计数,然后查看要删除的提交消息。

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

# git rebase -i HEAD~x

Simplest way,
Just look for the log and have a count which commits and check out the commit-msg which you want to drop.

Replace x with the count, I will show all the commit-msg, just delete the commit-msg which you want to remove.

# git rebase -i HEAD~x

榕城若虚 2024-09-10 15:45:24

我不擅长 GIT CLI。我需要从分支中删除一些提交。我是用 TortoiseGit 做的。以下是步骤。首先,右键单击文件资源管理器中的 Tortoisegit 并打开Rebase...窗口:

在此处输入图像描述

  1. 分支下拉列表中,选择分支。
  2. 上游下拉列表中,选择主分支。
  3. 选择强制变基
  4. 在主窗口中,单击要删除的提交。检查第二个窗口中的文件以确保。然后,右键单击提交并选择跳过
  5. 单击按钮开始变基

I am bad at GIT CLI. I needed to remove a couple of commits from a branch. I did it using TortoiseGit. Here are the steps. First, right click Tortoisegit in the File Explorer and open the Rebase... window:

enter image description here

  1. In the Branch dropdown, select the branch.
  2. In the Upstream dropdown, select the main branch.
  3. Select Force Rebase.
  4. In the main window, click on a commit that is to be removed. Inspect the files in the second window to make sure. Then, right click on the commit and select Skip.
  5. Click on the button Start Rebase.
网白 2024-09-10 15:45:24

如果您碰巧在分支上签出,则只需通过针对主分支的交互式变基即可解决此问题。我个人更喜欢这种方法,而不是计算提交次数以获得正确的提交次数的方法(例如 git rebase -i HEAD~4 )。也就是说,当有能力的时候。

git rebase -i main

当出现提示时,只需将 pick 更改为 drop 即可删除要删除的提交。按 ctrl + k + x 保存并关闭(假设 Joe 自己的编辑器)。

If you happen to be checked out on a branch this can be solved simply by interactive rebasing against the main branch. I personally prefer this method over the one where you count the number of commits to get to the right one (e.g. git rebase -i HEAD~4). That is, when able to.

git rebase -i main

When prompted just change up pick with drop for the commit(s) to remove. Hit ctrl + k + x to save and close (assuming Joe's Own Editor).

帅哥哥的热头脑 2024-09-10 15:45:24

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

git reset --hard HEAD

然后重置远程分支

git push origin -f

i would see a very simple way

git reset --hard HEAD <YOUR COMMIT ID>

and then reset remote branch

git push origin -f

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文