6.102
6.102 — Software Construction
Spring 2023

Disaster Recovery with Git

Objectives

  • Understand how to view and retrieve old versions from Git using Git commands
  • Practice using Git to recover old versions of code

Why do commits look like diffs?

We’ve defined a commit as a snapshot of our entire project, but if you ask Git, it doesn’t seem to see things that way:

$ git show 1255f4e
commit 1255f4e4a5836501c022deb337fda3f8800b02e4
Author: Max Goldman <maxg@mit.edu>
Date:   Mon Sep 14 14:58:40 2015 -0400

    Change the greeting

diff --git a/hello.txt b/hello.txt
index c1106ab..3462165 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1 @@
-Hello, version control!
+Hello again, version control!

Git is assuming that most of our project does not change in any given commit, so showing only the differences will be more useful. Almost all the time, that’s true. But we can also use Git to show us the entire state of the repository at a particular commit, not just the differences.

We can ask Git to show us what was in a particular file at a particular commit by typing a colon : after the commit ID and specifying a path to that file:

$ git show 3e62e60:hello.scm
(display "Hello, version control!")

We can also use this command to show the contents of a particular folder at a given commit by specifying a path to that folder. Notably, we can show the state of the entire repo at a given commit by typing nothing after the colon:

$ git show 3e62e60:
tree 3e62e60:

hello.rb
hello.scm
hello.txt

As the empty filepath corresponds to the root directory in the repo and thus shows all the files in the repo.

Using git show is one of the simplest ways you can use Git to recover from a disaster: ask Git to git show you the contents of a now-broken file at some earlier version when the file was OK.

Disaster recovery

As recommended in Version Control with Git, a GUI program can make it easier to navigate your repo and find the data you need to recover from a mistake. The built-in version control UI in VS Code is also useful for this (for example, the TIMELINE view in the Explorer pane allows you to quickly see the history of a file), but we continue to recommend the command line for running Git commands.

In 6.102, you can use GitHub’s web interface to review commits that were pushed to github.mit.edu.

  • On the summary page for a repo you see the files and folders in the root of the project on the current main branch.

  • Click commits to see the commit history.

  • Click on a commit message or commit ID to see the changes introduced by that commit.

  • Then click the Browse Files button on the far right to browse the entire project at that old version.

  • While you browse, notice the Tree: …commit ID… button on the left, which reminds you that you are looking at an old version of your repo. Normally, when you are browsing the latest version of your repo, this button says Branch: main instead.

git clone

Rather than having to recover from a catastrophic loss of work, it would be much better to prevent catastrophe. If you add, commit, and push your work regularly, that work is safe in the remote repo, and you can retrieve it with git clone into a fresh folder.

git revert

If you want to undo an entire commit, find its ID and use git revert. For example, to revert the addition of a Ruby greeting program in the hello-git repo:

Hover or tap on each step:

  1. Use git lol to see the current commit graph.
  2. Use ls to see the current files.
  3. Revert the “Greeting in Ruby” commit…
  4. … and Git asks for a commit message, providing a reasonable default.
  5. Save the commit message, and Git adds a new commit to the project history.
    This new commit undoes the change made by the old commit.
    It does not remove the old commit from the history of the project.
  6. The new commit is now the HEAD, and ls no longer shows hello.rb.
$ git lol
* b0b54b3 (HEAD, origin/main, origin/HEAD, main) Greeting in Java
*   3e62e60 Merge
|\  
| * 6400936 Greeting in Scheme
* | 82e049e Greeting in Ruby
|/  
* 1255f4e Change the greeting
* 41c4b8f Initial commit






To emphasize, git revert doesn’t rewind the whole repo back to an old version: it reverses the effect of an old commit by creating a new commit at the HEAD.

git show and git checkout

Most of the time when we make a mistake, we don’t want to revert an entire commit. For example, if the commit improved three different functions, and only one of those improvements was misguided, we’d like to undo just that one.

Often, the easiest way to find the working code you need is to ask Git to show you an old version. Hover or tap on each step in these examples:

Show the diff for a commit

  1. git showrevision will give you the diff for a commit.
    Removed lines of text are in red and prefixed with a minus sign -; added/modified lines are in green and prefixed with a plus +.
  2. If you git show a commit with a diff that doesn’t fit on one screen, it will be displayed one screenful at a time (originally using the more command, but now more likely its successor less). You’ll know this is happening if you see a : prompt at the bottom of the terminal…
  3. … which means you can press space to page down the diff, or use the arrow keys to go line-by-line. (You can press h for more help with using less.) Press q to quit less and get back to the command prompt.
$ git show 1255f4e
commit 1255f4e4a5836501c022deb337fda3f8800b02e4
Author: Max Goldman <maxg@mit.edu>
Date:   Mon Sep 14 14:58:40 2015 -0400

    Change the greeting

diff --git a/hello.txt b/hello.txt
index c1106ab..3462165 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1 @@
-Hello, version control!
+Hello again, version control!


Show an old version of a file

git showrevision:path/to/file will output an old version of a file.

$ git show 1255f4e:hello.txt
Hello again, version control!
$ git show 41c4b8f:hello.txt
Hello, version control!

When you find an old version that looks good, copy-and-paste is the simplest recovery strategy.

Replace a file with an old version

  1. git checkoutrevision--path/to/file will replace a file in your working directory with an old version. Both the -- (with a space before and after it) and the path in that command are important, don’t forget them!
  2. This command does not make a commit yet, so our history has not changed…
  3. … but it does stage the changes for commit!
  4. We can look at the staged changes: we’re reverting hello.txt back to its old contents by removing “again”.
$ git checkout 41c4b8f -- hello.txt









Unlike git revert, the revision in this commit is the specific revision we want: the file will look as it did including the changes in that revision, and none afterwards.

You do not want to use the command git checkoutrevision without a --path/to/file. If you do, your HEAD will now point to that old commit, and you will no longer be on branch main. This is called detached HEAD, and nobody wants that.

If a command warns you that your HEAD is detached, seek assistance before you continue to avoid losing work. If you have been working in a detached HEAD situation without realizing it, the work you did can almost always be recovered, but it has to be done carefully. Get help!

Practice with GitStream

GitStream will not work with multiple exercise pages open at the same time.

Don’t open exercises in multiple tabs. If an exercise doesn’t work, please close all open GitStream pages and try again.

If you encounter a problem, please ask for help.

Note that GitStream doesn’t keep track of whether you’ve already done this exercise. To see which GitStream exercises you’ve already done, look at Omnivore.