[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
androllback2
, and current state is:
- Now, the developer would like to rollback to the commit
134cc79
, akahead^^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
git checkout rollback1
git log
If trying to look into forward commits, git will return fatal info
git checkout master
git show --oneline 6e865f8
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, adetached head
state. If no reference to that commit created, new commits based on the specified commit (implemented in thedetached 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 ingit log
, however, the reset history is noted down ingit reflog
locally.
Way1: git checkout
Manual
Workflow
- use
git checkout <commit>
to checkout to a detached HEAD state, which means simply thatHEAD
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 stategit branch <new_branch>
- create a new branch, but leave the HEAD detachedgit 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
- A:
- 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 untilgit checkout
to other branch/commits
- A: the
- Q: How does
git log
andgit 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.
- A: No.
Test1: git checkout head^^2
, then git branch <new_branch>
- Currently the project history is:
git checkout rollback1
git log
git reflog
git show --oneline head
- if rolling back to
head^^2
# check current head
git show --oneline head^^2
git checkout head^^2
# check head
git show --oneline head
# check log
git log
git reflog
#check Egit history
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.
# checkout other branches
git checkout master
git checkout rollback1
git checkout rollback2
Other branches are not affected at all.
The project history becomes ( with a new branch created, pointing to the commit 134cc79
:
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 isHEAD
before and after moving the commit?- A: After reverting, a new commit is created.
HEAD
moves a commit forward.
- A: After reverting, a new commit is created.
- Q: How does log change? How about
git reflog
?- A: a new commit created(
git log
).git reflog
also gets changed. One finding aboutgit reflog
is that: it is always the same viewed from different branches.
- A: a new commit created(
- 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
git log
git reflog
git show --oneline head
git show --oneline head^^2
git revert head^^2
git status
Meeting conflict when reverting back to head^^2
(there was conflict when merging the two commits)
The current version of test11
is:
Then resolve the conflict: ( if we use the file test11, current version, then)
git status
git add test11
git status
# the, `git revert --continue` to continue the revert
git revert --continue
git status
The revert is done now.
git log
git reflog
# a new revert commit is created and the head move 1 commit forward
git show --oneline head
# check status of other branches
git checkout master
git show --oneline head
git log
git reflog
git checkout rollback2
git show --oneline head
git log
git reflog
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>
andHEAD
, 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>
andHEAD
. If a file that is different between<commit>
andHEAD
has local changes, reset is aborted.
Test3
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
git reset --hard head~
The revert is done. Now let's check the change in log and head
pointer
git show --oneline head
git status
git branch
git log
git reflog
However, the reset can be tracked via git reflog
Also, checking status of other branches
git checkout master
git show --oneline head
git checkout rollback2
git show --oneline head
Summary:
git reset
does not affect other branches, only reset current checked-out branchgit reset
removed the commit fromgit log
, however, the revert info is logged ingit 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:
- 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:
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):
Step1: merge ( suppose no conflict)
git checkout rollback1
git merge rollback2
An auto-merge commit is created:
Step2: merge rollback2
forward, then make another commit after merging
# merge rollback2 forward
git checkout rollback2
git merge rollback1
# 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
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
When the revert is done, the project history becomes:
Tidy-up work
sync branch rollback1
and rollback2
by resetting rollback1
to head^
git checkout rollback1
git reset --hard head~
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
)
project status becomes:
# 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"
Now the two branches diverge, with conflicts:
Step1: checkout rollback1
, merge rollback2
(with rollback1
version file)
git checkout rollback1
git merge rollback2
Conflict here, need to be resolved. The conflict file test12-revert-common
now is:
then merge again:
git add test12-revert-common
git commit -m "resolve merge commit with rollback1 version"
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
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:
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~
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"
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)
after merging, the project status become:
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
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
(content of the conflicting file -- test12-revert-common
)
If we change the file when resolving conflicts to:
Then, then continue revert
git add test12-revert-common
git revert --continue
now the project status becomes:
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:
- now, checkout
rollback1
,revert
tohead^^2
(134cc79
)
The conflict file version is now:
- resolve the conflict, continue
revert
version of the file after resolving conflict:
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)
- now the revert conflict is resolved, the project status becomes:
- tidy-up work
reset the revert process, make project history clean again
git checkout rollback1
git reset --hard head~
The project status becomes:
Q4: straight-forward test case:
Step0: check start status
experimenting with file test12-revert1
. The start state of the file is:
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: )
After commit1, project status becomes:
After commit1, file list becomes: (no change)
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: )
After commit2, project status becomes:
After commit2, file list becomes: (no change)
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: )
After commit3, project status becomes:
After commit3, file list becomes: (no change)
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
:
The file test12-revert1
becomes:
Then resolve the revert conflict by modifying test12-revert1
to :
Then. issue the command to continue revert:
git add test12-revert1
git revert --continue
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 isx=1
- The version of
head
is:x=3
- When reverting the change made by
1
on tip ofhead
,head
will try to make changex=1
-->x=0
, but foundx=3
, conflict arising
Further tests (abstraction of tests above)
1. Revert merge commit, conflict when merge, (not sure about what the merge version look like)
2. Revert merge commit, conflict when merge, the merged version is different from any of its parents
3. Revert merge commit, conflict when merge, the merged version is the same as one of its parents
4. Revert merge commit, the merge commit having no 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
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
The whole file got compared here, though in both commits, the line z=1
are the same.