Reverting specific commits from git
Asked Answered
B

2

69

I have a git tree with a lot of commits and a lot of files. Now, I want to revert specific commits that touch a file only. To explain:

> git init
Initialized empty Git repository in /home/psankar/specific/.git/
> echo "File a" > a
> git add a ; git commit -m "File a"
[master (root-commit) 5267c21] File a
 1 file changed, 1 insertion(+)
 create mode 100644 a
> echo "File b" > b
> git add b; git commit -m "File b"
[master 7b560ae] File b
 1 file changed, 1 insertion(+)
 create mode 100644 b
> echo "File c" > c
> git add c; git commit -m "File c"
[master fd6c132] File c
 1 file changed, 1 insertion(+)
 create mode 100644 c
> echo "b and c modified" > b ; cp b c
> git commit -a -m "b and c modified"
[master 1d8b062] b and c modified
 2 files changed, 2 insertions(+), 2 deletions(-)
> echo "a modified" > a
> git commit -a -m "a modified"
[master 5b7e0cd] a modified
 1 file changed, 1 insertion(+), 1 deletion(-)
> echo "c modified" > c
> git commit -a -m "c modified"
[master b49eb8e] c modified
 1 file changed, 1 insertion(+), 1 deletion(-)
> git log --pretty=oneline c
> git log --pretty=oneline c | cat
b49eb8e03af331bddf90342af7d076f831282bc9 c modified
1d8b062748f23d5b75a77f120930af6610b8ff98 b and c modified
fd6c13282ae887598d39bcd894c050878c53ccf1 File c

Now I want to revert just the two commits b49eb8 and 1d8b06 without reverting the changes to a. IOW revert only the commits in a file (without reverting other intermediate commits (which may be thousands in number) in different files) How is this possible ?

Bename answered 2/12, 2013 at 9:17 Comment(2)
use git rebase -i <commithash>Rickety
git show --stat -p COMMITID will give you quick line count statistics and a patch showing the exact changes performed by that commit. Armed with that information you can decide whether you want to git revert COMMITID. Using -n will allow you to assess before commit or even to git revert --abortFotinas
G
124

You can use git revert with the --no-commit option. In your example:

$ git revert --no-commit b49eb8e 1d8b062
# Files that were modified in those 2 commits will be changed in your working directory
# If any of those 2 commits had changed the file 'a' then you could discard the revert for it:
$ git checkout a
$ git commit -a -m "Revert commits b49eb8e and 1d8b062"

--no-commit option will not do auto commit, it allows you to edit and add your own commit message

Note that with unlike git reset with git revert all the reverted commits will still be there in the commit history

Ganister answered 2/12, 2013 at 9:57 Comment(2)
+1 for the --no-commit switch and mentioning that the revert has to be done in reverse orderAccursed
I have tried the same in my machine before asking here and it did not work then. Now tried in a new git setup (v 1.8) where it works fine. Probably it is a bug in the old git that I had (1.6). Thanks.Bename
A
15

There are two cases here:

  1. When you have already pushed your git tree to somewhere and you don't want to change the history. In this case you will need a new commit expressing the changes you made in reverting the previous commits. You should use @mamapitufo's answer.

  2. If you have never pushed the branch that the changes are on, and you can change the history. In this case you can completely remove the unwanted commits. This will neaten the history and means that you don't push a wrong turning to your co-workers or the public.

In the second case you should do git rebase -i. Find a commit which comes before any of the history you want to change. This might be the hash of the commit, or the name of the branch or tag. For example, you could do

git rebase -i 23def8231

or if you started from the branch origin/dev_branch and did the work which includes the bits to remove on your branch called dev_branch, you could do

git rebase -i origin/dev_branch

Now, you will be sent into an editor window where you can see a list of all the commits which you are rebasing. This might be vim - if you don't usually edit in the terminal it could be set as the default. You will probably need a quick start guide to vim and an open mind if that's the case.

Now, the easiest thing to do is to remove commits. You do this by deleting the line, or adding a # which indicates a comment to the start of the line. (There are already some comments in the file, to explain things to you. Ignoring these or deleting them has no effect.)

When you are finished, save the file and exit the editor. The rebase happens like this: git goes back to the commit you named. It goes through the list you saved and replays each commit in that list. Then it makes the result of that process the new version of the branch you were originally on.

Important things to remember:

  • If you get lost or delete too many lines, you can cancel the rebase by deleting every commit line in the file, and saving. The rebase process will end with nothing changed at all.
  • It's possible to create conflicts. For example if you remove a commit which edits a file, and leave in a later commit which edits the same place. The later commit will now not apply correctly and you will have to edit by hand or in a merge tool to get the version you want.

You can also do lots of other manipulation in git rebase -i. For example changing the order of commits, squashing several together into one, adding extra changes between commits, or changing the messages. It is very useful. The classic use case is cleaning up your local branch before you push it back to somewhere where other people will look at your changes.

Avrilavrit answered 10/10, 2015 at 23:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.