Git – Remove in between commit

Our team is doing several projects in PHP. We have wrongly committed a folder of One Project to another. Now, we want to remove that particular commit from the Project. If we remove the particular folder/commit from project then there is no issue for our project.

If we just remove the folder and issue the new commit at current position then folder is removed but it will remain in the history of Git. So, we want to remove it completely from refs, history and other things from Git.

  • Unable to add submodules in Git
  • How to list all Git tags?
  • How to resolve merge conflicts in Git?
  • Ignore one line in a specific file in Git
  • git merge command use
  • git extensions single file history only diffs
  • We can also create a separate branch but the commit refs of authors would be lost. We want to only remove that particular commit. We have no issue in rewriting history or re-basing it but don’t know how to do it.

    In the project we have done 136 commits and want to remove commit no.76th. The required information about SHA is as under

    5d39775b          //136th commit
    a4df5ee9          //135th commit
    6971cf35          //134th commit
    .....
    ....
    162f833c          //76th commit
    32603274          //75th commit
    .....
    ....
    9770059          //1st commit
    

  • UsernamePasswordCredentials contains a null username or password
  • programmatically detect current git branch checked out from C# code
  • SherlockActivity cannot be resolved to a type in android
  • Git and pbxproj
  • Github windows: Commit failed: Failed to create a new commit
  • Git setup for non-developers
  • 5 Solutions collect form web for “Git – Remove in between commit”

    In your master branch, you can interactively rebase:

    git rebase -i 162f833c^
    

    This will rebase on top on the commit before the offending commit.
    Now just remove the offending commit from the list, save, and exist the editor (default in *nix platforms is vi).

    This will rebase your branch on top the commit before the problematic one, without it, which seems to be what you’re trying to achieve.

    You can use interactive rebase.

    Since the commit you want to delete has the sha1 162f833c, then just do git rebase -i 162f833c^

    A text editor will open with a list of commits. Just delete the line corresponding to the commit you want to delete, save, and close.

    As a safety net, when I do this kind of stuff, I like to put a tag on my HEAD first, so that if something goes wrong, I can just checkout this tag and retrieve my initial state.

    I have tried all the provided methods i.e. rebase and cherry-pick. I here to provide full information about both the methods tried and things done by me, to help to understand things with better view

    Preparations

    To check which is best I have done these things:

    1. Fork the remote repo so that the original would be intact and things can be understood easily. So, don’t do any original repo until you are sure things done are correct or not.

    2. first taken the clean copy of Git Repo. Generally, whenever we stash or do other things that are also stored in Git as local Git database. So, that Git database doesn’t have any local or temporary things.

    3. I have calculated total number of bytes taken by folder .git. As the repo is clean the minimum bytes would be correct here. To go further deep, I have noted down both bytes taken and bites taken on disk. As both are different things and they are:

      bytes – 7,963,769 and size on disk – 8,028,160

    4. If you are using Windows and have installed any Anti virus then you have to disable the Active/Real time mode. As doing things Git need fast access of i/o files checking. What happens when file is created by Git, the active mode locks the new Created for checking virus and when Git tries to re-access that it fails due to locking by Antivirus. In Case of failure you have start your process again from 1.

    Method I – Rebase

    In re-basing method can be done by two ways. One is through –onto and other is with -i.

    -i Method

    I used the following command:

     git rebase -i 162f833c^
    

    and it had opened the vim Editor and I see the list of commits starting from 162f833c not from master. If the end the following lines are provided.

    # Rebase 3260327..5d39775 onto 3260327
    #
    # Commands:
    #  p, pick = use commit
    #  r, reword = use commit, but edit the commit message
    #  e, edit = use commit, but stop for amending
    #  s, squash = use commit, but meld into previous commit
    #  f, fixup = like "squash", but discard this commit's log message
    #  x, exec = run command (the rest of the line) using shell
    #
    # If you remove a line here THAT COMMIT WILL BE LOST.
    # However, if you remove everything, the rebase will be aborted.
    #
    

    I removed a line so that commit will be lost and saved the file and quit the Editor and when I quit it started re-basing as showing:

    Rebasing ( 1/64) ..(2/64)
    
    Successfully rebased and updated refs/heads/master.
    

    And then tried to check the log to see that commit is lost or not and in the log I couldn’t find the commit I want to delete. Means the successfully completed the command. The I tried to check the status as:

    # On branch master
    # Your branch and 'origin/master' have diverged,
    # and have 64 and 65 different commit(s) each, respectively.
    

    Since the commits is deleted but on the remote from which it is derived required to be pushed. So, pushed the commit with the force to override earlier code.

    git push -f
    

    After that I removed the local repo and re-fetched again the repo and the size it shows:

    bytes 6,831,765 bytes
    size on disk 6,897,664 bytes
    

    –onto Method

    After going through Many blogs and Manual, I found 1 blog here which really let me know how to use it. So the command I used is:

    git rebase --onto 3260327 79504a5~1
    

    and the out put is:

    First, rewinding head to replay your work on top of it...
    Applying: 77th Message
    Applying: 78th Message
    Applying: 79th Message
    ....
    Last commit
    

    And then tried to check the log to see that commit is lost or not and in the log I couldn’t find the commit I want to delete. Means the successfully completed the command. The I tried to check the status as:

    # On branch master
    # Your branch and 'origin/master' have diverged,
    # and have 64 and 65 different commit(s) each, respectively.
    

    So, with single command all the things are done and then I done a force push to check the bytes etc as earlier

    git push -f
    

    After that I removed the local repo and re-fetched again the repo and the size it shows:

    bytes - 6,831,389
    size on disk - 6,893,568    
    

    Rebase -i Vs –onto

    Thus, after doing rebase method. I really approve the –onto method for deleting the commit in between as it is single command and the bytes are also saved marginally higher then -i method. The real advantage is that we don’t have to do additional things in –onto method.

    Method II – Cherry-pick

    Cherry-pick really nice method but run through many commands and you have to take little cautions running the commands. First, I have to create a separate log file with

    git log --all --decorate --oneline --graph > 1
    

    Which showed me the log of the repo as we need to view it again and again. From this repo identify the commit you want to delete and copy the SHA Key the last good commit meaning thereby identify the commit up to which we didn’t want to change any thing. So, the next command would be

    git checkout 3260327 -b repair
    

    generated output:

    Switched to a new branch 'repair'
    

    So, up to last Good commit we have created a new branch. So, things up to last Good commit doesn’t change. Again I run the following command to view all the good commits:

    git log --decorate --oneline --graph > 2
    

    I have removed the world –all as, I want to view the commits of branch repair only. As it showed me Good Commits upto marked correctly. Now, next command is to be used with caution as it is includes the bad commit as starting point and end point the last commit done and it would be as:

    git cherry-pick 162f833..5d39775
    

    Output:

    [repair 9ed3f18] xxxxxx
    x files changed, xxx insertions(+), xx deletions(-)
    [repair 7f06d73] xxxxx
    xx files changed, xxx insertions(+), xx deletions(-)
    .....
    ...
    

    What this command does that it recommits all the commits leaving first commit provided above i.e (162f833) to last commit (5d39775) provided. So, the sha commits value would be changed accordingly as it is recommitting the commits one by one. Now it is the time to view the log as:

    git log --all --decorate --oneline --graph > 3
    

    output as:

    * f61a9a5 (HEAD, repair) xxxxxx
    * 25be3b9 xxxxx
    * 49be029 xxxxx
     .......
     .......
    | * 5d39775 (origin/master, origin/HEAD, master)
    | * a4df5ee xxxxx
    | * 6971cf3 xxxxxx
    | .......
    | .......
    | * 162f833 xxxx
    |/
    * 3260327 xxxxx
    ......
    * 9770059 xxxxx
    

    So, viewing graph let us know that it had re-commits all the commits except the bad commit. showing you all the old sha keys with new keys. If everything is all right we have to make repair branch as master and deleting the master branch as:

    git checkout master
    

    and output as:

    Switched to branch 'master'
    

    First checkout master branch so that we can over ride the changes on master branch. Then

    git reset --hard 3260327
    

    and output as:

    HEAD is now at 3260327 xxxxx
    

    It will discard the commits after the good commit and now we have to merge the repair branch with master:

    git merge repair
    

    and output as:

    Updating 3260327..40d290d
    Fast-forward
    

    Now, if you view the log it will not show you the bad commit and every thing is done. I done a force push to check the bytes etc as earlier

    git push -f
    

    After that I removed the local repo and re-fetched again the repo and the size it shows:

    bytes - 6,831,556
    size on disk - 6,897,664
    

    Ranking of commands

    Rebase –onto [First]

    bytes - 6,831,389
    size on disk - 6,893,568    
    

    Cherry Pick [second]

    bytes - 6,831,556
    size on disk - 6,897,664
    

    Rebase -i [third]

    bytes 6,831,765 bytes
    size on disk 6,897,664 bytes
    

    I would like to give preference to git rebase –onto command as things are done clean way with single command easily.

    You can also use git cherry-pick for this:

    $ g  # g is my alias for git log --all --decorate --oneline --graph --color
    * 618f8e5 [2013-12-14 15:13] (HEAD, master) me: good 6
    * e27d6d7 [2013-12-14 15:13] me: good 5
    * 533f6c3 [2013-12-14 15:13] me: good 4
    * 877585f [2013-12-14 15:13] me: bad
    * 00c06f3 [2013-12-14 15:12] me: good 3
    * e9f80a4 [2013-12-14 15:12] me: good 2
    * 3122ba7 [2013-12-14 15:12] me: good 1
    * 98da603 [2013-12-14 15:12] me: first
    $ git checkout 00c06f3 -b repair
    Switched to a new branch 'repair'
    $ git cherry-pick 877585f..618f8e5
    [repair b340e21] good 4
     1 file changed, 1 insertion(+)
    [repair 1d2e0d0] good 5
     1 file changed, 1 insertion(+)
    [repair 1ed1d19] good 6
     1 file changed, 1 insertion(+)
    $ g
    * 1ed1d19 [2013-12-14 15:13] (HEAD, repair) me: good 6
    * 1d2e0d0 [2013-12-14 15:13] me: good 5
    * b340e21 [2013-12-14 15:13] me: good 4
    | * 618f8e5 [2013-12-14 15:13] (master) me: good 6
    | * e27d6d7 [2013-12-14 15:13] me: good 5
    | * 533f6c3 [2013-12-14 15:13] me: good 4
    | * 877585f [2013-12-14 15:13] me: bad
    |/  
    * 00c06f3 [2013-12-14 15:12] me: good 3
    * e9f80a4 [2013-12-14 15:12] me: good 2
    * 3122ba7 [2013-12-14 15:12] me: good 1
    * 98da603 [2013-12-14 15:12] me: first
    $ git checkout master
    Switched to branch 'master'
    $ git reset --hard repair
    HEAD is now at 1ed1d19 good 6
    $ git branch -d repair
    Deleted branch repair (was 1ed1d19).
    $ g
    * 1ed1d19 [2013-12-14 15:13] (HEAD, master) me: good 6
    * 1d2e0d0 [2013-12-14 15:13] me: good 5
    * b340e21 [2013-12-14 15:13] me: good 4
    * 00c06f3 [2013-12-14 15:12] me: good 3
    * e9f80a4 [2013-12-14 15:12] me: good 2
    * 3122ba7 [2013-12-14 15:12] me: good 1
    * 98da603 [2013-12-14 15:12] me: first
    $ # Done :)
    

    git rebase is maybe better, so at least I am showing that there are usually multiple ways in git. After all, rebase is so powerful that “all meaningful operations in git can be expressed in terms of the rebase command” (Linus Torvalds).

    You can automatically remove a commit and rewrite history by (where ${ref_to_delete} is the commit you want to remove, and assumes master is the branch that it is on):

    git rebase --onto ${ref_to_delete}~ ${ref_to_delete} master
    

    If you use a remote repository, you’ll have to push with -f:

    git push -f
    
    Git Baby is a git and github fan, let's start git clone.