Remove specific commit

486,970

Solution 1

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.

Solution 2

There are four ways of doing so:

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

    git revert --strategy resolve <commit>
    
  • 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.

  • Rebase (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
    
  • Alternatives: 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 -f 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). Prefer to always specify the remote branch when you force push: git push --force origin your_branch.

Solution 3

Here is an easy solution:

git rebase -i HEAD~x

(Note: x is the number of commits)

upon executing notepad file will open. Enter drop besides your commit.
If you don’t know Vim, just click on each word pick that you want to edit and then hit the "I" key (for insert mode). Once you’re done typing hit the "esc" key to exit insert mode.



enter image description here

and that's it, you are done... Just sync the git dashboard and the changes will be pushed to remote.

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.

Solution 4

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.

Solution 5

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.

Share:
486,970
CanadianGirl827x
Author by

CanadianGirl827x

Updated on July 10, 2022

Comments

  • CanadianGirl827x
    CanadianGirl827x almost 2 years

    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.
    
  • Cascabel
    Cascabel almost 14 years
    Rebase may be tricky, since it sounds like there's been a merge.
  • Samuel
    Samuel over 9 years
    git rebase -i 8230fa3, and delete the commit's line worked great for me with my local-only changes. Thanks!
  • Ilker Cat
    Ilker Cat about 8 years
    Based on suggestions of @gaborous: do a "git rebase -i HEAD~2". Now you have several options. In the vim you can see some commented lines: one of them tells you that you simply can delete a line (which should be the commit you want to get rid of) and this commit will be removed together with it's log in your history.
  • Swathin
    Swathin over 7 years
    git revert --strategy resolve <commit> .This command worked for me. thanks :)
  • Hal Eisen
    Hal Eisen about 7 years
    perl -p is useful for writing very short (one line) programs which loop and pass their input through to the output, similar to sed. perl -i is used for editing files in place. perl -e is how to submit the code to be evaluated.
  • lv10
    lv10 over 6 years
    git rebase -i HEAD~5 worked for me. Then I just removed the commit(s) that I didn't need and I was able to solve the problem in less than 30 seconds. Thank you.
  • Herpes Free Engineer
    Herpes Free Engineer over 5 years
    For cpp-mentality people: x (no. of commits) is inclusive. e.g. HEAD~4 includes the last 4 commits.
  • celerno
    celerno about 5 years
    perfect suggestion. just want to add, this also discard the changes from the dropped commit.
  • pfabri
    pfabri almost 5 years
    Could you please give an example of 'try another strategy'?
  • pfabri
    pfabri almost 5 years
    What an invaluable walk-through - the exact use-case I was struggling with, this helped me immensely.
  • Dennis
    Dennis almost 5 years
    @pfabri you could for example cherry-pick two ranges of commits where you leave out the bad commit. You could revert the bad commit. You could avoid using a git solution by even manually undoing the changes, or starting from a fresh branch without the bad commit and manually redoing the good changes. If the bad commit contains sensitive data, you'll need a more careful strategy: help.github.com/en/articles/…
  • Ustin
    Ustin over 4 years
    tried to revert 3 commits: git rebase -i HEAD-3 got error fatal: Needed a single revision invalid upstream 'HEAD-3'
  • JD-V
    JD-V over 4 years
    @Ustin it's ~3 not -3
  • bigbiggerpepe
    bigbiggerpepe about 4 years
    The first approach worked like a charm. It includes you a commit specifying that your desired commit has been reverted, which is really nice for tracking purposes.
  • Eduard
    Eduard about 4 years
    how to "enter drop"? I can't write anything
  • Eduard
    Eduard about 4 years
    Answer to my question above: If you don’t know Vim, just click on each word pick that you want to edit and then hit the <i> key (for insert mode). Once you’re done typing hit the <esc> key to exit insert mode.
  • Petra
    Petra about 4 years
    I also really like this solution, eventho it leaves a trace in the GitHub history. after writing drop on the commits i wanted to get rid of i used :w! and then :q to exit vim.
  • sagar suri
    sagar suri about 4 years
    This is an amazing solution.
  • rotarydial
    rotarydial almost 4 years
    This worked best for me, particularly where I wanted to erase changes to a specific file without deleting it entirely.
  • tankman175
    tankman175 about 3 years
    Nice of you to explain vim, but git should automatically use the systems default-editor
  • VimNing
    VimNing almost 3 years
    Both God and I bless you.
  • DrV
    DrV almost 3 years
    For what it's worth, the first command git revert ... helped me hold back commits from production branch (e.g. master) and then with combination of git cherry-pick was able to include back those reverted commits into my develop branch in order to not lose any development work while only deploying what needed to be deployed.
  • chendu
    chendu over 2 years
    getting fatal: Cannot do hard reset with paths. when i use the 1st command
  • Mansur Ul Hasan
    Mansur Ul Hasan over 2 years
    it looks like you resetting with paths instead of commit hash
  • Powertoaster
    Powertoaster over 2 years
    git revert --strategy resolve <commit> worked for me but I had to add a -m flag. Like this: git revert --strategy resolve <commit> -m 1 since my commit was a merge.
  • mtorreblanca
    mtorreblanca about 2 years
    just an exquisite answer
  • N Altun
    N Altun about 2 years
    This was the perfect answer for me. Cheers