Undoing and recovering#

Objectives

  • Learn to undo changes safely

  • See when undone changes are permanently deleted and when they can be retrieved

Instructor note

  • 10 min teaching/type-along

  • 15 min exercise

One of the main points of version control is that you can go back in time to recover. Unlike this xkcd comic implies: https://xkcd.com/1597/

In this episode we show a couple of commands that can be used to undo mistakes. We also list a couple of common mistakes and discuss how to recover from them. Some commands preserve the commit history and some modify commit history. Modifying history? Isn’t a “commit” permanent?

  • You can modify old commit history.

  • But if you have shared that history already, modifying it can make a huge mess.

Note

As long as you commit something once (or at least git add it), you can almost always go back to it, no matter what you do. But you may need to ask Stack Overflow or your local guru… until that guru becomes you.


Undoing your recent, uncommitted and unstaged changes (preserves history)#

You do some work, and want to undo your uncommitted and unstaged modifications. You can always do that with:

  • git restore .: (the dot means “here and in all folders below”)

Or, you can undo things selectively:

  • git restore -p (decide which portions of changes to undo) or git restore <path> (decide which path/file)

Note

In case the above does not work, your Git version might be older than from 2019. On older Git it is git checkout instead of git restore.

If you have staged changes, you have at least two options to undo the staging:

  • git restore --staged . followed by git status and git restore .

  • git reset --hard HEAD throws away everything that is not in last commit (HEAD)


Reverting commits (preserves history)#

  • Imagine we made a few commits.

  • We realize that the latest commit f960dd3 was a mistake and we wish to undo it:

$ git log --oneline

f960dd3 (HEAD -> master) not sure this is a good idea
dd4472c we should not forget to enjoy
2bb9bb4 add half an onion
2d79e7e adding ingredients and instructions

A safe way to undo the commit is to revert the commit with git revert:

$ git revert f960dd3

This creates a new commit that does the opposite of the reverted commit. The old commit remains in the history:

$ git log --oneline

d62ad3e (HEAD -> master) Revert "not sure this is a good idea"
f960dd3 not sure this is a good idea
dd4472c we should not forget to enjoy
2bb9bb4 add half an onion
2d79e7e 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#

Undoing-1: Revert a commit

  • Create a commit (commit A).

  • Revert the commit with git revert (commit B).

  • Inspect the history with git log --oneline.

  • Now try git show on both the reverted (commit A) and the newly created commit (commit B).

Adding to the previous commit (modifies history)#

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 avoid this command on commits that we have shared with others.

Exercise: Modify a previous commit#

Undoing-2: Modify a previous commit

  1. Make an incomplete change to the recipe or a typo in your change, git add and git commit the incomplete/unsatisfactory change.

  2. Inspect the unsatisfactory but committed change with git show. Remember or write down the commit hash.

  3. Now complete/fix the change but instead of creating a new commit, add the correction to the previous commit with git add, followed by git commit --amend. What changed?

Rewinding branches (modifies history)#

You can reset branch history to move your branch back to some point in the past.

  • git reset --hard <hash> will force a branch label to any other point. All other changes are lost (but it is possible to recover if you force reset by mistake).

  • Be careful if you do this - it can mess stuff up. Use git graph a lot before and after.

Exercise: Git reset#

Undoing-3: Destroy our experimentation in this episode

After we have experimented with reverts and amending, let us destroy all of that and get our repositories to a similar state.

  • First, we will look at our history (git log/git graph) and find the last commit <hash> before our tests.

  • Then, we will git reset --hard <hash> to that.

  • Then, git graph again to see what happened.

$ git log --oneline

d62ad3e (HEAD -> master) Revert "not sure this is a good idea"
f960dd3 not sure this is a good idea
dd4472c we should not forget to enjoy
2bb9bb4 add half an onion
2d79e7e adding ingredients and instructions

$ git reset --hard dd4472c

HEAD is now at dd4472c we should not forget to enjoy

$ git log --oneline

dd4472c (HEAD -> master) we should not forget to enjoy
2bb9bb4 add half an onion
2d79e7e adding ingredients and instructions

Recovering from committing to the wrong branch#

It is easy to forget to create a branch or to create it and forget to switch to it when committing changes.

Here we assume that we made a couple of commits but we realize they went to the wrong branch.

Solution 1 using git cherry-pick:

  1. Make sure that the correct branch exists and if not, create it. Make sure to create it from the commit hash where you wish you had created it from: git branch <branchname> <hash>

  2. Switch to the correct branch.

  3. git cherry-pick <hash> can be used to take a specific commit to the current branch. Cherry-pick all commits that should have gone to the correct branch, from oldest to most recent.

  4. Rewind the branch that accidentally got wrong commits with git reset --hard (see also above).

Solution 2 using git reset –hard (makes sense if the correct branch should contain all commits of the accidentally modified branch):

  1. Create the correct branch, pointing at the latest commit: git branch <branchname>.

  2. Check with git log or git graph that both branches point to the same, latest, commit.

  3. Rewind the branch that accidentally got wrong commits with git reset --hard (see also above).

Recovering from merging/pulling into the wrong branch#

git merge, git rebase, and git pull modify the current branch, never the other branch. But sometimes we run this command on the wrong branch.

  1. Check with git log the commit hash that you would like to rewind the wrongly modified branch to.

  2. Rewind the branch that accidentally got wrong commits with git reset --hard <hash> (see also above).

Recovering from conflict after git pull#

git pull can create a conflict since git pull always also includes a git merge (more about this in the collaborative Git lesson).

The recovery is same as described in Conflict resolution. Either resolve conflicts or abort the merge with git merge --abort.


Undoing-4: Test your understanding

  1. What happens if you accidentally remove a tracked file with git rm, is it gone forever?

  2. Is it OK to modify commits that nobody has seen yet?

  3. What situations would justify to modify the Git history and possibly remove commits?