Overview
Teaching: 15 min
Exercises: 25 minQuestionsObjectives
- How can I undo things?
- Learn to undo changes safely
- See when undone changes are permanently deleted and when they can be retrieved
Clear your workspace
- If you have unstaged changes from earlier sections, remove them with
git checkout <filename>
.- We will see in more detail below how
git checkout
works.
We start with the following state:
Let’s make a new commit on a master
branch (e.g. modify a README.md file):
$ git log --oneline
e1d7745 (HEAD -> master) testing a new idea
5861578 Merge branch 'less-salt'
0996fff Merge branch 'experiment'
372c868 add README.md file
721d9c6 reduce amount of salt
a85267e maybe little bit less cilantro
c79bfc1 let us try with some cilantro
901f422 enjoy your dish!
7adfe4b add half an onion
49baa1f adding ingredients and instructions
We realize that this commit (e1d7745
) was a mistake and we wish to undo it
A safe way to undo the commit is to revert the commit with git revert
:
$ git revert e1d7745
This creates a new commit that does the opposite of the reverted commit. The old commit remains in the history:
$ git log --oneline
646c9e1 (HEAD -> master) Revert "testing a new idea"
e1d7745 testing a new idea
5861578 Merge branch 'less-salt'
0996fff Merge branch 'experiment'
372c868 add README.md file
721d9c6 reduce amount of salt
a85267e maybe little bit less cilantro
c79bfc1 let us try with some cilantro
901f422 enjoy your dish!
7adfe4b add half an onion
49baa1f adding ingredients and instructions
You can revert any commit, no matter how old it is. It doesn’t affect other commits you have done since then - but if they touch the same code, you may get a conflict (which we’ll learn about later).
Exercise: Revert a commit
- Create a commit.
- Revert the commit with
git revert
.- Inspect the history with
git log --oneline
.- Now try
git show
on both the reverted and the newly created commit.
Sometimes we commit but realize we forgot something. We can amend to the last commit:
$ git commit --amend
This can also be used to modify the last commit message.
Note that this will change the commit hash. This command modifies the history. This means that we never use this command on commits that we have shared with others.
Exercise: Modify a previous commit
- Make an incomplete change to the recipe or a typo in your change,
git add
andgit commit
the incomplete/unsatisfactory change.- Inspect the unsatisfactory but committed change with
git show
.- Now complete/fix the change but instead of creating a new commit, add to the previous commit with
git commit --amend
.- Inspect the modified commit with
git show
.
git checkout
git checkout <hash>
checks out a commit <hash>
and puts it in your work treeExercise: create a new branch from an old commit
Let’s inspect our current state:
$ git log --oneline d0a525a (HEAD -> master) write better instructions 646c9e1 Revert "testing a new idea" e1d7745 testing a new idea 5861578 Merge branch 'less-salt' 0996fff Merge branch 'experiment' 372c868 add README.md file 721d9c6 reduce amount of salt a85267e maybe little bit less cilantro c79bfc1 let us try with some cilantro 901f422 enjoy your dish! 7adfe4b add half an onion 49baa1f adding ingredients and instructions
We want to continue to work on adapting the amount of cilantro and we will create a branch from commit
a85267e
:$ git checkout -b experiment_with_cilantro a85267e
- Inspect the result with
git branch
andgit graph
. What is the content of your current working directory?This is often used to inspect old versions of the project (we will go back to that in archeology class).
git reset
git reset --hard <hash>
:
<hash>
and permanently throws away their changes!<hash>
)git reset --hard @{u}
is a command to make the local branch identical to upstreamgit reset --soft <hash>
:
<hash>
but keeps their modifications as staged changesExercise
- Let’s create few new commits on
experiment_with_cilantro
branch$ git log --oneline f85500e (HEAD -> experiment_with_cilantro) prepare todo list 25f8f0a modify recipe c7375d4 increase the amount of cilantro a85267e maybe little bit less cilantro c79bfc1 let us try with some cilantro 901f422 enjoy your dish! 7adfe4b add half an onion 49baa1f adding ingredients and instructions
- Realize you do not want these commits and wish to go back to the original state of that branch. This can be done with
$ git reset --hard a85267e
- What is now the output of
git graph
orgit log
?
After we have experimented, let us get our repositories to a similar state.
$ git checkout master
$ git log --oneline
d0a525a (HEAD -> master) write better instructions
646c9e1 Revert "testing a new idea"
e1d7745 testing a new idea
5861578 Merge branch 'less-salt'
0996fff Merge branch 'experiment'
372c868 add README.md file
721d9c6 reduce amount of salt
a85267e maybe little bit less cilantro
c79bfc1 let us try with some cilantro
901f422 enjoy your dish!
7adfe4b add half an onion
49baa1f adding ingredients and instructions
$ git reset --hard 901f422
HEAD is now at 901f422 we should not forget to enjoy
$ git log --oneline
901f422 (HEAD -> master) enjoy your dish!
7adfe4b add half an onion
49baa1f adding ingredients and instructions
undo unstaged changes:
git checkout -- <filename>
: any local changes you made to that file/repository are gonegit checkout -b new-experiment && git add . && git commit -m "testing the new idea"
: commiting local changes to a new branchgit stash save "testing a new idea"
: stash local changesundo staged but uncommited changes (you did git add
but didn’t do git commit
):
git reset HEAD
Comment: Different meanings of “checkout”
Depending on the context
git checkout
can do very different actions:1) Switch to a branch:
$ git checkout <branchname>
2) Bring the working tree to a specific state (commit), we will discuss that later:
$ git checkout <hash>
3) Set a file/path to a specific state (throws away all unstaged/uncommitted changes):
$ git checkout <path/file>
This is unfortunate from the user’s point of view but the way Git is implemented it makes sense. Picture
git checkout
as an operation that brings the working tree to a specific state. The state can be a commit or a branch (pointing to a commit).In latest Git (2.23) this is much nicer:
$ git switch <branchname> # switch to a different branch $ git restore <path/file> # discard changes in working directory
git revert <hash>
:
git checkout
:
git checkout
can be used in different contexts - see the comment abovegit reset --hard <hash>
:
<hash>
and resets your working directory to the state as in <hash>
(so it looks like later commits were never made)before undoing things consider taking a backup of your current working directory and .git