dorawyy/git-merge-conflicts-test

[testcase notes] #12: rollback ways comparison - `git checkout`, `git revert`, `git reset`

Opened this issue · 3 comments

Based on the scenario of #11 , let's continue the test cases about rolling back, in this issue

Abstract:

As we know, here are several ways to rollback the project to a history version, which are:

  • git checkout
  • git reset
  • git revert

In this issue, we are comparing above ways of rolling back.

Scenario:

  • Developer1 worked on two branches- rollback1 and rollback2, and current state is:

wechatimg255

  • Now, the developer would like to rollback to the commit 134cc79, aka head^^2.
  • The developer tried different ways, and let's see what would happen when the developer applies different command.

Results:

  • Observation1: git log are different in un-merged branches
# compare log of master branch and rollback1 branch
git checkout master
git log

screen shot 2017-10-15 at 2 46 39 pm

git checkout rollback1 
git log

screen shot 2017-10-15 at 2 47 22 pm

If trying to look into forward commits, git will return fatal info

git checkout master
git show --oneline 6e865f8

screen shot 2017-10-15 at 2 49 05 pm

YES, checked-out branch affects what commit the developer can rollback

  • Observation2:git checkout, git revert, git reset only affect current checked-out branch. They have no effect on other branches.
  • Observation3: Comparisons of git checkout, git revert, git reset:
    • git checkout moves the pointer back to the specified commit, a detached head state. If no reference to that commit created, new commits based on the specified commit (implemented in the detached head state) will get lost;
    • git revert creates a new commit to revert the specified commit, on the checked-out branch. revert conflict might show up here, required the developer to resolve the conflict to continue reverting.
    • git reset removes the specified commit from the checked-out branch. Those commits are no longer visible in git log, however, the reset history is noted down in git reflog locally.

Way1: git checkout

Manual

Workflow

  • use git checkout <commit> to checkout to a detached HEAD state, which means simply that HEAD refers to a specific commit, as opposed to referring to a named branch.
  • if no reference to the detailed HEAD state is created, the state will end up being recycled and lost.
  • to create a reference to the state, there are three ways:
    • git checkout -b <new_branch> - create a new branch, move HEAD there, no longer in detached state
    • git branch <new_branch> - create a new branch, but leave the HEAD detached
    • git tag <tag_name> - leave the HEAD detached

Questions to solve:

  • Q: Does what branch the developer is checking out affect what state he/she can rollback?
    • A: yes. it does affect. We cannot rollback to commits not visible on this branch yet.
  • Q: Where will head be after the developer rolls back?
    • A: head moves back to the commit we are checking out
  • Q: Where will other branches be - git checkout <other_branches>?
    • A: other branches are not affected.
  • Q: Where will head be after creating references?
    • A: the head does not change until git checkout to other branch/commits
  • Q: How does git log and git reflog change in the whole process?
    • A:
  • Q: Would forward commits get lost?
    • A: No. git checkout won't affect other branches and done commits.

Test1: git checkout head^^2, then git branch <new_branch>

  • Currently the project history is:
git checkout rollback1
git log
git reflog

screen shot 2017-10-15 at 2 57 34 pm

screen shot 2017-10-15 at 2 58 52 pm

screen shot 2017-10-15 at 2 59 35 pm

git show --oneline head

screen shot 2017-10-15 at 3 00 19 pm

  • if rolling back to head^^2
# check current head
git show --oneline head^^2

screen shot 2017-10-15 at 3 01 21 pm

git checkout head^^2

screen shot 2017-10-15 at 3 02 25 pm

# check head
git show --oneline head

screen shot 2017-10-15 at 3 04 07 pm

# check log
git log
git reflog

screen shot 2017-10-15 at 3 05 02 pm
screen shot 2017-10-15 at 3 05 31 pm

#check Egit history

screen shot 2017-10-15 at 3 09 03 pm

git branch 

It show currently, it is in the detached HEAD state

# Create ref(a new branch) to current state; ( here in this example, the developer just use the hash of the commit as branch name)
git branch 134cc79 
# to show current branch status
git branch

After creating the new branch, the state has not changed yet.

git checkout 134cc79

Now, it is no longer in the detached state.
screen shot 2017-10-15 at 3 14 33 pm

# checkout other branches
git checkout master
git checkout rollback1
git checkout rollback2

Other branches are not affected at all.
screen shot 2017-10-15 at 3 15 19 pm

The project history becomes ( with a new branch created, pointing to the commit 134cc79:
screen shot 2017-10-15 at 3 18 04 pm

Test1 Summary: when git checkout <commit>, just move head pointer back. All commits in the repo as well as other branches are not affected.

Way2: git revert

Manual

git revert

  • create a new commit of the commit the developer would like to roll back to
  • with the -n option, only reverting working tree and index back to the commit we would like to go back, no new commit created.

Questions

  • Q: Would git revert affect other branches?
    • A: No. Revert is branch-level. It creates a new commit on current branch, not affecting other branches.
  • Q: Are there any differences between reverting to a merge commit / normal commit?
    • A: Yes. When revering a merge commit, conflict might appear and requires the developer to resolve the conflict before continuing the revert.
  • Q: What about HEAD ? Where is HEAD before and after moving the commit?
    • A: After reverting, a new commit is created. HEAD moves a commit forward.
  • Q: How does log change? How about git reflog?
    • A: a new commit created(git log). git reflog also gets changed. One finding about git reflog is that: it is always the same viewed from different branches.
  • Q: How does working tree / index change?
    • A: Empty before reverting. When the revert conflict show up, the conflicting file is in working dir for developers to modify. After adding into index, the developer is able to continue the revert.

Test2

# check out rollback1
git checkout rollback1
git branch
# status of working tree/index
git status

screen shot 2017-10-15 at 7 34 24 pm

git log

screen shot 2017-10-15 at 7 35 46 pm

git reflog

screen shot 2017-10-15 at 7 36 16 pm

git show --oneline head
git show --oneline head^^2

screen shot 2017-10-15 at 7 37 16 pm

git revert head^^2
git status

Meeting conflict when reverting back to head^^2 (there was conflict when merging the two commits)
screen shot 2017-10-15 at 7 39 34 pm

The current version of test11 is:
31593105-7631cea8-b1e1-11e7-97d4-1994bb21de58

Then resolve the conflict: ( if we use the file test11, current version, then)
screen shot 2017-10-15 at 7 45 44 pm

git status
git add test11
git status

screen shot 2017-10-15 at 7 47 46 pm

# the, `git revert --continue` to continue the revert
git revert --continue
git status 

screen shot 2017-10-15 at 7 51 40 pm

The revert is done now.

git log

screen shot 2017-10-15 at 7 54 05 pm

git reflog

screen shot 2017-10-15 at 7 54 43 pm

# a new revert commit is created and the head move 1 commit forward
git show --oneline head

screen shot 2017-10-15 at 7 55 54 pm

screen shot 2017-10-15 at 7 56 59 pm

# check status of other branches
git checkout master
git show --oneline head
git log
git reflog

screen shot 2017-10-15 at 8 11 27 pm

screen shot 2017-10-15 at 8 12 33 pm

screen shot 2017-10-15 at 8 11 48 pm

screen shot 2017-10-15 at 8 11 57 pm

git checkout rollback2
git show --oneline head
git log
git reflog

screen shot 2017-10-15 at 8 13 44 pm

screen shot 2017-10-15 at 8 13 52 pm

screen shot 2017-10-15 at 8 14 37 pm

screen shot 2017-10-15 at 8 15 21 pm

Conclusion: other branches are not affected by reverting.


Fix current git tree, syncing branches rollback1 and rollback2 for next test.

git checkout rollback2
git merge rollback2

Way3: git reset

Manual

git reset [<mode>] [<commit>]

  • can erase history commits -- undo commits
  • This form resets the current branch head to and possibly updates the index (resetting it to the tree of <commit>) and the working tree depending on <mode>. If <mode> is omitted, defaults to --mixed. The <mode> must be one of the following:
    • --soft: Does not touch the index file or the working tree at all (but resets the head to <commit>, just like all modes do). This leaves all your changed files "Changes to be committed", as git status would put it.
    • --mixed: Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated. This is the default action.
    • --hard: Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.
    • --merge: Resets the index and updates the files in the working tree that are different between <commit> and HEAD, but keeps those which are different between the index and working tree (i.e. which have changes which have not been added). If a file that is different between <commit> and the index has unstaged changes, reset is aborted.
    • --keep: Resets index entries and updates files in the working tree that are different between <commit> and HEAD. If a file that is different between <commit> and HEAD has local changes, reset is aborted.

Test3

Current project history is:
screen shot 2017-10-15 at 8 39 50 pm

If we checkout rollback1, reset the commit we did in test2:

git checkout rollback1
git show --oneline head
git status
git branch
git log
git reflog

screen shot 2017-10-15 at 8 40 40 pm

screen shot 2017-10-15 at 8 41 19 pm

screen shot 2017-10-15 at 8 41 41 pm

git reset --hard head~

screen shot 2017-10-15 at 8 42 51 pm

The revert is done. Now let's check the change in log and head pointer

git show --oneline head

screen shot 2017-10-15 at 8 43 28 pm

The `head` pointer moved 1 commit back, same as `rollback2` branch now.
git status

screen shot 2017-10-15 at 8 44 05 pm

Working tree and index empty.
git branch

screen shot 2017-10-15 at 8 44 43 pm

git log

screen shot 2017-10-15 at 8 45 01 pm

When `--hard` resetting, the previous latest commit got removed directly from `git log`
git reflog

However, the reset can be tracked via git reflog
screen shot 2017-10-15 at 8 46 03 pm
screen shot 2017-10-15 at 8 46 47 pm

Also, checking status of other branches

git checkout master
git show --oneline head

screen shot 2017-10-15 at 8 47 15 pm

screen shot 2017-10-15 at 8 47 30 pm

git checkout rollback2
git show --oneline head

screen shot 2017-10-15 at 8 48 03 pm

screen shot 2017-10-15 at 8 48 18 pm

Summary:

  • git reset does not affect other branches, only reset current checked-out branch
  • git reset removed the commit from git log, however, the revert info is logged in git reflog

git revert conflict scenario Test

In the testcase above

  • there was a merge conflict during the merge commit
  • when resolving the merge conflict, the file with conflict was modified to something different from the two branches
  • when reverting, conflict

Main questions:

  • Q1: what if there was no conflict in the merge commit, when reverting, are there still any revert conflict?
    • A: yes, revert always have a revert commit.
  • Q2: when resolving the merge conflict, what if using the version of one of the two branches, still revert conflict?
    • A:
  • Q3: do developers get the chance to input conflict-resolving information while revert commit?
    • A: yes, they can input info, here is how the default look like:

screen shot 2017-10-23 at 9 37 53 am

  • Q4: if no conflict arise when revert, do developers still get a chance to input info for the revert commit ?
    • A: yes, always got a chance to attach customized info --> which means no difference between normal revert or revert with conflicts.

Q1: what if no merge conflict, still revert conflict?

Prep:

rollback1 and rollback2 branches both have commits, no conflict;

Previous project status:

screen shot 2017-10-23 at 11 06 48 am

Committed on both rollback1 and rollback2:

# rollback1 : test12-revert1
git checkout rollback1
touch test12-revert1
vim test12-revert1
git add  test12-revert1
git commit -m "rollback1: created file test12-revert1"


# rollback2: test12-revert2
git checkout rollback2
touch test12-revert2
vim test12-revert2
git add  test12-revert2
git commit -m "rollback2: created file test12-revert2"

Project status becomes (diverge with no conflict):
screen shot 2017-10-23 at 11 20 58 am

Step1: merge ( suppose no conflict)

git checkout rollback1
git merge rollback2

An auto-merge commit is created:
screen shot 2017-10-23 at 11 22 33 am

The project status becomes:
screen shot 2017-10-23 at 11 23 03 am

Step2: merge rollback2 forward, then make another commit after merging

# merge rollback2 forward
git checkout rollback2
git merge rollback1

screen shot 2017-10-23 at 11 26 07 am

# make a new commit
git checkout rollback1
touch test12-revert-common
git add test12-revert-common
git commit -m "rollback1: create file test12-revert-common after merging"

# sync the two branches
git checkout rollback2
git merge rollback1

screen shot 2017-10-23 at 11 28 31 am

Step3: revert rollback1 from head to head^^2 (suppose no conflict)

(suppose a revert commit is created, however, no conflict during revert)

git checkout rollback1
git show --oneline head    ##456a9fa (current head)
git show --oneline head^^2   ## 22ae3cb (on rollback2)
git revert head^^2

An auto-commit window pop up, asking the developer to fill in revert commit message
screen shot 2017-10-23 at 11 32 01 am
screen shot 2017-10-23 at 11 32 08 am

When the revert is done, the project history becomes:
screen shot 2017-10-23 at 11 33 13 am

Tidy-up work

sync branch rollback1 and rollback2 by resetting rollback1 to head^

git checkout rollback1
git reset --hard head~

Project status becomes:
screen shot 2017-10-23 at 11 37 49 am


Q2: what if using the version of file from one of the two branches?

both commits on rollback1 and rollback2, conflict when merging, take the version of rollback1 when resolving the conflict

Prep: conflict commits on rollback1 and rollback2

# commit on rollback1 branch
git checkout rollback1
vim test12-revert-common
# edit line1 
git add test12-revert-common
git commit -m "rollback1: edit line 1 of file test12-revert-common"

(edit on rollback1)
screen shot 2017-10-23 at 11 41 10 am
project status becomes:
screen shot 2017-10-23 at 11 41 49 am

# commit on rollback2 branch
git checkout rollback2
vim test12-revert-common
# edit line1  (same as rollback1)

git add test12-revert-common
git commit -m "rollback2: edit line 1 of file test12-revert-common"

(edit on rollback2)
screen shot 2017-10-23 at 11 42 46 am

screen shot 2017-10-23 at 11 43 23 am

Now the two branches diverge, with conflicts:
screen shot 2017-10-23 at 11 43 41 am

Step1: checkout rollback1, merge rollback2 (with rollback1 version file)

git checkout rollback1
git merge rollback2

screen shot 2017-10-23 at 11 45 49 am

Conflict here, need to be resolved. The conflict file test12-revert-common now is:
screen shot 2017-10-23 at 11 46 38 am

After resolution:
screen shot 2017-10-23 at 11 47 23 am

then merge again:

git add test12-revert-common
git commit -m "resolve merge commit with rollback1 version"

The project status becomes:
screen shot 2017-10-23 at 11 48 48 am

Step2: create another commit after merging, sync the two branches

git checkout rollback1
touch test12-revert-after-merge
git add test12-revert-after-merge
git commit -m "rollback1: commit after merge"

# sync the two branches
git checkout rollback2
git merge rollback1

screen shot 2017-10-23 at 11 50 49 am

Step3: checkout rollback1, revert from head to head^^ (rollback1 version), same as merge version

git checkout rollback1 
git show --oneline head  ### 3f20762, current head
git show --oneline head^^ ### 593f5de, commit on rollback1 (same as the merge version)

# revert
git revert head^^

An auto-merge commit is issued, no revert conflict:
screen shot 2017-10-23 at 11 53 36 am

Then, project status becomes:
screen shot 2017-10-23 at 11 54 55 am

Step4: tidy-up work, reset back, test what would happen if we choose another branch version when merging

git checkout rollback1
git reset --hard head~

screen shot 2017-10-23 at 11 57 23 am

Step5: try rollback2 branch version when merging, then reverting to head^^ agin

git checkout rollback2
vim test12-revert-common

# edit line1  (same as rollback1)

git add test12-revert-common
git commit -m "rollback2: edit line 1 of file test12-revert-common"

(the edit on rollback2 is:)
screen shot 2017-10-23 at 12 03 20 pm

screen shot 2017-10-23 at 12 03 45 pm

git checkout rollback1
git merge rollback2

# fix with rollback2 version 
git add test12-revert-common
git commit -m "resolve merge conflict: fix with rollback2 version"

(resolve merge conflict with rollback2 version)
screen shot 2017-10-23 at 12 05 38 pm
after merging, the project status become:
screen shot 2017-10-23 at 4 14 18 pm

Create another commit after merging

git checkout rollback1
touch test12-revert-after-merge
git add test12-revert-after-merge
git commit -m "rollback1: commit after merge"

# sync the two branches
git checkout rollback2
git merge rollback1

The project status becomes:
screen shot 2017-10-23 at 4 21 59 pm

Step6: checkout rollback1, reverting to head^^ ( same version as rollback2, different from )

git checkout rollback1
git show --oneline head  ### 07e4598
git show --oneline head^^  ### 593f5de (rollback1 commit)

# revert
git revert head^^
## revert conflict

(revert conflict arise)
screen shot 2017-10-23 at 5 50 44 pm

(current git status )
screen shot 2017-10-23 at 5 54 04 pm

(content of the conflicting file -- test12-revert-common )
1

If we change the file when resolving conflicts to:
screen shot 2017-10-23 at 8 59 41 pm

Then, then continue revert

git add test12-revert-common
git revert --continue

A revert commit is created:
screen shot 2017-10-23 at 9 04 57 pm

now the project status becomes:
screen shot 2017-10-23 at 9 05 45 pm


Q3: when revert conflict resolved and commit, any chance to input information?

This is tested by replaying the Test2 above of git revert

  • Before test, the project status is:

screen shot 2017-10-23 at 9 26 09 am

  • now, checkout rollback1, revert to head^^2 (134cc79)

screen shot 2017-10-23 at 9 28 38 am

The conflict file version is now:
screen shot 2017-10-23 at 9 33 44 am

  • resolve the conflict, continue revert

version of the file after resolving conflict:
screen shot 2017-10-23 at 9 36 36 am

Then, continue the reverting:
screen shot 2017-10-23 at 9 38 55 am

When issuing the command git revert --continue, a revert commit will be created, and ask developers to input message to attach (see next step)

  • a revert commit will be created, developers can input conflict-resolving information
    (here is the default info)

screen shot 2017-10-23 at 9 37 53 am

  • now the revert conflict is resolved, the project status becomes:

screen shot 2017-10-23 at 9 40 39 am

  • tidy-up work

reset the revert process, make project history clean again

git checkout rollback1
git reset --hard head~

screen shot 2017-10-23 at 9 42 52 am

The project status becomes:

screen shot 2017-10-23 at 9 43 14 am


Q4: straight-forward test case:

Step0: check start status

experimenting with file test12-revert1. The start state of the file is:
screen shot 2017-10-23 at 10 59 40 pm

project status:
screen shot 2017-10-23 at 11 01 34 pm

file list on rollback1:
screen shot 2017-10-23 at 11 01 52 pm

Step1: commit fileX lineX, on rollback1

git checkout rollback1
vim test12-revert1

git add test12-revert1
git commit -m "rollback1: 'x=commit1' in file test12-revert1"

(edit in the file test12-revert1 is highlight with blue: )
screen shot 2017-10-23 at 10 57 47 pm

After commit1, project status becomes:
screen shot 2017-10-23 at 11 03 15 pm

After commit1, file list becomes: (no change)
screen shot 2017-10-23 at 11 03 33 pm

Step2: another commit of fileX lineX, on rollback1 (same branch, same line of code as step1)

git checkout rollback1
vim test12-revert1

git add test12-revert1
git commit -m "rollback1: 'x=commit2' in file test12-revert1"

(edit in the file test12-revert1 is highlight with blue: )
screen shot 2017-10-23 at 11 05 09 pm

After commit2, project status becomes:
screen shot 2017-10-23 at 11 05 35 pm

After commit2, file list becomes: (no change)
screen shot 2017-10-23 at 11 05 56 pm

Step3: 3rd commit of fileX lineX, on rollback1 (same branch, same line of code as step1 and step2)

git checkout rollback1
vim test12-revert1

git add test12-revert1
git commit -m "rollback1: 'x=commit3' in file test12-revert1"

(edit in the file test12-revert1 is highlight with blue: )
screen shot 2017-10-23 at 11 07 30 pm

After commit3, project status becomes:
screen shot 2017-10-23 at 11 07 58 pm

After commit3, file list becomes: (no change)
screen shot 2017-10-23 at 11 08 33 pm

Step4: git revert head^^ ( at commit3, revert commit1)

git checkout rollback1
git show --oneline head  ## 2bb579d, commit3
git show --oneline head^^ ## 9cda35f, commit1

git revert head^^

Revert conflict arise in file test12-revert1:
screen shot 2017-10-23 at 11 11 32 pm

The file test12-revert1 becomes:
screen shot 2017-10-23 at 11 12 17 pm

Then resolve the revert conflict by modifying test12-revert1 to :
screen shot 2017-10-23 at 11 15 25 pm

Then. issue the command to continue revert:

git add test12-revert1
git revert --continue

The automatically-generated commit message is:
screen shot 2017-10-23 at 11 18 09 pm

Revert conflict resolved by now, project status becomes:
screen shot 2017-10-23 at 11 18 53 pm

git revert Scenario scratches

Basic scenario

As long as the head and the commit you want to revert, having different versions of the change part, there will be revert conflict;

For example:

  • The change from 0-->1 is: x=0 --> x=1, and the current state is x=1
  • The version of head is: x=3
  • When reverting the change made by 1 on tip of head, head will try to make change x=1 --> x=0, but found x=3, conflict arising

git_revert_basic_conflict

Further tests (abstraction of tests above)

1. Revert merge commit, conflict when merge, (not sure about what the merge version look like)

revert_merge_commit_with_conflict

2. Revert merge commit, conflict when merge, the merged version is different from any of its parents

revert_merge_commit_version_different_from_any_of_the_parents

3. Revert merge commit, conflict when merge, the merged version is the same as one of its parents

revert_merge_commit_with_conflict2

4. Revert merge commit, the merge commit having no conflict

revert_merge_commit_without_conflict

git revert

1.Revert a merge commit, failure, due to no parent commit specified

The revert just failed and got aborted, as no parent of the merge commit is specified, the revert does not know which parent commit to compare with

screen shot 2017-11-15 at 2 13 16 pm

Solution:

Specifying -m, which stands for mainline-parent-commit to use when reverting

2. Revert a merge commit with -m specified

#revert to the first parent of the merge commit, which is commit2 in this case;
git revert head^ -1 

Revert conflict arise:
screen shot 2017-11-15 at 2 45 13 pm

The whole file got compared here, though in both commits, the line z=1 are the same.