Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git revert back to certain commit without changing history and creating a new commit like git revert

Is there a way to revert to a certain commit without changing the remote history, basically undoing all the changes from that commit in a new commit just like git revert?

For eg - I have 3 commits commit A -> B -> C, my head is currently at C I want to now create another commit D which will have the same code as A so that I can push that commit to the remote branch without changing history.

like image 961
Manthan Jamdagni Avatar asked Mar 20 '18 15:03

Manthan Jamdagni


People also ask

How do you revert back to a certain commit in git?

Steps to revert a Git commitLocate the ID of the commit to revert with the git log or reflog command. Issue the git revert command and provide the commit ID of interest. Supply a meaningful Git commit message to describe why the revert was needed.

Does revert create a new commit?

The git revert command is a forward-moving undo operation that offers a safe method of undoing changes. Instead of deleting or orphaning commits in the commit history, a revert will create a new commit that inverses the changes specified. Git revert is a safer alternative to git reset in regards to losing work.


2 Answers

Be careful with the word "revert"

When people say "I want to revert" in Git they sometimes mean what git revert does, which is more of a back out operation, and sometimes mean what you do, which is to restore the source base from an earlier version.

To illustrate, suppose we have a commit that has just one file, README, and three commits:

A <-B <-C   <-- master (HEAD)

The version of README in revision A says "I am a README file", and is just one line long.

The version of README in revision B says "I am a README file." as before, but has a second line added, "This file is five lines long."

The version of README in revision C is corrected in that its second line says "This file is two lines long."

Git's git revert can undo a change, so that, right now, running git revert <hash-of-B> will attempt to remove the added line. This will fail since the line doesn't match up any more (and we can run git revert --abort to give up). Similarly, running git revert <hash-of-C> will attempt to undo the correction. This will succeed, effectively reverting to revision B!

This question, Undo a particular commit in Git that's been pushed to remote repos, is all about the backing-out kind of reverting. While that sometimes results in the reverting-to kind of reverting, it's not the same. What you want, according to your question, is more: "make me a new commit D that has the same source code as commit A". You want to revert to version A.

Git does not have a user command to revert to, but it's easy

This question, How to revert Git repository to a previous commit?, is full of answers talking about using git reset --hard, which does the job—but does it by lopping off history. The accepted answer, though, includes one of the keys, specifically this:

git checkout 0d1d7fc32 .

This command tells Git to extract, from the given commit 0d1d7fc32, all the files that are in that snapshot and in the current directory (.). If your current directory is the top of the work-tree, that will extract the files from all directories, since . includes, recursively, sub-directory files.

The one problem with this is that, yes, it extracts all the files, but it doesn't remove (from the index and work-tree) any files that you have that you don't want. To illustrate, let's go back to our three-commit repository and add a fourth commit:

$ echo new file > newfile
$ git add newfile
$ git commit -m 'add new file'

Now we have four commits:

A <-B <-C <-D   <-- master (HEAD)

where commit D has the correct two-line README, and the new file newfile.

If we do:

$ git checkout <hash-of-A> -- .

we'll overwrite the index and work-tree version of README with the version from commit A. We'll be back to the one-line README. But we will still have, in our index and work-tree, the file newfile.

To fix that, instead of just checkout out all files from the commit, we should start by removing all files that are in the index:

$ git rm -r -- .

Then it's safe to re-fill the index and work-tree from commit A:

$ git checkout <hash> -- .

(I try to use the -- here automatically, in case the path name I want resembles an option or branch name or some such; it makes this work even if I just want to check out the file or directory named -f, for instance).

Once you have done these two steps, it's safe to git commit the result.

Minor: a shortcut

Since Git actually just makes commits from the index, all you have to do is copy the desired commit into the index. The git read-tree command does this. You can have it update the work-tree at the same time, so:

$ git read-tree -u <hash>

suffices instead of remove-and-checkout. (You must still make a new commit as usual.)

like image 116
torek Avatar answered Oct 02 '22 04:10

torek


  1. git reset --hard <Commit A>
  2. git reset --soft <Commit C>
  3. git add . && git commit -m "Commit D"

Now it's at D and previous commits A, B and C have all been kept

like image 34
dzcpy Avatar answered Oct 02 '22 06:10

dzcpy