0

I have a doubt regarding "git revert"

There is no internet connection. I have made 4 commits by the time the internet connection arrives. Then the internet connection arrives. I have to pull before I push. So I pull and it causes Auto merge to happen(without any conflicts). This merge is created as another (5th commit) Finally I push all the "FIVE" commits on gitlab.

If my manager does "git revert <5th commit SHA>, will all changes from my first commit to the 5th commit go away? If yes, then why? If no, then how many commits will not be there on gitlab when someone else pulls ?

Note- everything is happening on the Main branch. No secondary branches are created here.

  • It's a little bit hard understand what you mean. Could you edit and explain yourself in a better way? – Víctor López Sep 07 '17 at 14:32
  • Scenario!! There are 3 developers collaborating on a project. We make changes to code by doing `git add` then `git commit`. One of us doesn't push the changes to the Gitlab repository. He keeps on doing changes to code and carrying out the above to processes in total 4 times Let's say the commits look like this in the commit history !! 5ede3f (4th commit) 6ef3df (3rd commit) 7rg4fs (2nd commit) 4rgd5r (1st commit) Before I can push I have to pull the changes made by another developer on Gitlab. Automerging those, I have to create a 5th commit before I can push!! – Jatin Sharma Sep 07 '17 at 14:53
  • `git revert` does not *remove* commits. `git revert` adds a *new* commit, whose effect is "back out changes made by some previous commit". Imagine a pile of pages where, by default, you look only at the top page. `git revert` adds another page on top of the pile; what's written on the *new* page is what was on the previous page, with things crossed out and inserted based on what change the reverter said to revert. All the old pages are still in the pile! – torek Sep 07 '17 at 14:53
  • My manager doesn't want to retain the previous changes. He does "git revert <5th commit>" or some other command using the 5th commit id. Will it cancel all the changes which were pushed (1st to 4th commit)? That is my question. Please tell me if still doesn't make sense. I'll give a screenshot of commits to the project in that case. – Jatin Sharma Sep 07 '17 at 14:56
  • @torek What should one do if one wants to disregard the commits made from one particular commit to the latest one?? – Jatin Sharma Sep 07 '17 at 14:58
  • Reverting a merge commit is a bit tricky: running `git revert ` when `` identifies a merge commit just gives you an error message. You *can* revert a merge (by adding more arguments to the `git revert` command), but it's very important that anyone who does this understand *exactly what it means.* Start with [this answer](https://stackoverflow.com/a/6217372/1256452) to a related question. – torek Sep 07 '17 at 15:03
  • Thank you @torek. I'll have a look at the the link you mentioned !! – Jatin Sharma Sep 07 '17 at 15:24

1 Answers1

1

There are some things you (or your manager) need to understand about revert.

First, it doesn't delete commits. It creates a new commit that undoes changes from one or more old commits.

Second, if you want to revert a merge commit, you need to also specify which of the merge's parents you want to revert to. This matters because your "5th" commit is a merge.

Lastly, if you do revert a merge, it will be hard to re-introduce the undone changes later. The documentation states that you are telling git that you will never want the reverted side of the merge.

So what you have after your push is

x --- x --- x --- A --- B --- C --- D --- M
             \                          /
               o --- o --- o --- o --- O

The xs are commits you (and the remote) had before you started work.

The os and O are commits that appeared on the remote while you were "off line".

A through D are your commits.

M is the merge created by git when you pulled. This merge considers D to be parent 1, and O to be parent 2.

Now if you say

git revert -m 1 <SHA-of-M> 

this says "revert M to D". That would effectively undo all the o commits as well as O. On the other hand,

git revert -m 2 <SHA-of-M>

this says "revert M to O". That would effectively undo A, B, C, and D.

But again, this does not delete commits. It also does not remove commits from branch history. The result would be

x --- x --- x --- A --- B --- C --- D --- M --- W
             \                          /
               o --- o --- o --- o --- O

where the TREE (content) at W matches either the TREE at D or the TREE at O (depending on which -m option you specified).

And to reiterate: not only are all the commits from one side of the merge effectively undone, but it isn't so easy to re-do them because git considers them "accounted for" by M.

If you (or your manager) are trying to remove "unnecessary commits" from history, then you have to do a history rewrite. There are several ways to do that; revert isn't one of them. Any history rewrite affecting commits that have been pushed has costs associated with it. The view that merge commits are "ugly" or "unnecessary" is a matter of opinion; I question whether the cost is justified in most circumstances. But editorializing aside:

One procedure that would work (assuming your local master is still at M):

1) Create a temporary branch and move it to O

git checkout master
git checkout -b o_master
git reset --hard HEAD^2

2) Move master to D

git checkout master
git reset --hard HEAD^

3) Use rebase to create a linear history

git rebase o_master master
git branch -d o_master

You may have to resolve some conflicts during the rebase; see the git rebase docs.

This gives the appearance of moving the commits around so you have

x --- x --- x -- o --- o --- o --- o --- O --- A' --- B' --- C' --- D'

You have some new commits (A' through D'); each is a replacement for one of your original commits (A through D). The code states at A', B', and C' are untested and may not compile or may malfunction, which could affect future debugging efforts; so you should probably test them. The code state at D' should be equivalent to what you had at M before (so presumably it is tested), but this is not 100% guaranteed (for various reasons, but especially if there were conflicts to resolve) so it's best to validate (either test D' as well, or at least diff D' against the original M).

Next you would do a "force push".

git push -f

At this point, you have rewritten the remote history. While you still have not deleted any commits, you have removed some old commits from the master ref. This means that all of your coworkers must update, and any work they've done (based on M) must be rebased to D'. See "recovering from upstream rebase" in the git rebase docs for more details about this issue.

Technically all of the old commits still exist, but they aren't visible by default because you've taken them out of your refs' history. If you need them physically deleted, that's yet another multi-step procedure.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
  • Thank you for your Detailed answer!! It taught me some new things. I have another doubt. Let's say I use `git revert -m 1 `. This, according to your example undo's the changes in 'o'. You also mentioned that the branch history (and if my understanding is correct, the Github / lab server will still have all the files upto Merged part). Does this mean that the local changes will be undone for one of the developers? If yes, Then which person will lose the changes? The one who committed uptil "D" or the one with "O"? – Jatin Sharma Sep 07 '17 at 23:16
  • We haven't been using "rebase" till now. Might have to go through it. – Jatin Sharma Sep 07 '17 at 23:16
  • Regarding questions in first comment: I think again the thing to understand is, undoing *changes* (revert) is different from undoing *commits*. When you revert, you create a new commit, so the changes are "undone" in your local. You push, so that updates the remote with the new commit. Another dev pulls, they get that commit and the changes are "undone" for them. It's just like how any other change propagates; whoever has the commit that "undoes" the changes, sees the changes undone. – Mark Adelsberger Sep 08 '17 at 13:55
  • One additional note: I revised my comments about rebase; I realized that my stated assumption that the ending content is unchanged is ideally true, but may not always hold. – Mark Adelsberger Sep 08 '17 at 14:02
  • I see!! So the new commit after the `git revert` command when pulled by another dev causes the "reverts" cause by that command to be replicated on the local copy of that dev. Thanks @Mark, I understood ! – Jatin Sharma Sep 08 '17 at 23:49