List of exercises

Summary

Basics:

Branching and merging:

Conflict resolution:

Inspecting history:

Using the Git staging area:

Undoing and recovering:

Interrupted work:

Full list

This is a list of all exercises and solutions in this lesson, mainly as a reference for helpers and instructors. This list is automatically generated from all of the other pages in the lesson. Any single teaching event will probably cover only a subset of these, depending on their interests.

Motivation

In motivation.md:

Basics

In basics.md:

Basic-1: Record changes

Add 1/2 onion to ingredients.txt and also the instruction to “enjoy!” to instructions.txt.

After modifying the files, do not stage the changes yet (do not git add yet).

When you are done editing the files, try git diff:

$ git diff

You will see (can you identify in there the two added lines?):

diff --git a/ingredients.txt b/ingredients.txt
index 4422a31..ba8854f 100644
--- a/ingredients.txt
+++ b/ingredients.txt
@@ -2,3 +2,4 @@
 * 1 chili
 * 1 lime
 * 2 tsp salt
+* 1/2 onion
diff --git a/instructions.txt b/instructions.txt
index 7811273..2b11074 100644
--- a/instructions.txt
+++ b/instructions.txt
@@ -4,3 +4,4 @@
 * squeeze lime
 * add salt
 * and mix well
+* enjoy!

Now first stage and commit each change separately (what happens when we leave out the -m flag?):

$ git add ingredients.txt
$ git commit -m "add half an onion"
$ git add instructions.txt
$ git commit                   # <-- we have left out -m "..."

When you leave out the -m flag, Git should open an editor where you can edit your commit message. This message will be associated and stored with the changes you made. This message is your chance to explain what you’ve done and convince others (and your future self) that the changes you made were justified. Write a message and save and close the file.

When you are done committing the changes, experiment with these commands:

$ git log
$ git log --stat
$ git log --oneline

In basics.md:

(optional) Basic-2: Comparing and showing commits

  1. Have a look at specific commits with git show HASH.

  2. Inspect differences between commit hashes with git diff HASH1 HASH2.

In basics.md:

(optional) Basic-3: Visual diff tools

This exercise is only relevant for the command line. In the browser, the preview is already side-by-side and “visual”.

  • Make further modifications and experiment with git difftool (requires installing one of the visual diff tools):

On Windows or Linux:

$ git difftool --tool=meld HASH

On macOS:

$ git difftool --tool=opendiff HASH
Git difftool using meld

Git difftool using meld.

You probably want to use the same visual diff tool every time and you can configure Git for that:

$ git config --global diff.tool meld

In basics.md:

(optional) Basic-4: Browser and command line

You have noticed that it is possible to work either in the command line or in the browser. It could help to deepen the understanding trying to do the above steps in both.

  • If you have managed to do the above in the command line, try now in the browser.

  • If you got stuck in the command line and move to the browser, try now to trouble-shoot the command line Git.

In basics.md:

Basic-5: Test your understanding

Which command(s) below would save the changes of myfile.txt to an existing local Git repository?

  1. $ git commit -m "my recent changes"
    
  2. $ git init myfile.txt
    $ git commit -m "my recent changes"
    
  3. $ git add myfile.txt
    $ git commit -m "my recent changes"
    
  4. $ git commit -m myfile.txt "my recent changes"
    

Branching and merging

In branches.md:

Branch-1: Create and commit to branches

In this exercise, you will create another new branch and few more commits. We will use this in the next section, to practice merging. The goal of the exercise is to end up with 3 branches.

  • Change to the branch main.

  • Create another branch called less-salt.

    • Note! makes sure you are on main branch when you create the less-salt branch.

    • A safer way would be to explicitly mention to create from the main branch as shown below:

      $ git branch less-salt main
      
  • Switch to the less-salt branch.

  • On the less-salt branch reduce the amount of salt.

  • Commit your changes to the less-salt branch.

Use the same commands as we used above.

We now have three branches (in this case HEAD points to less-salt):

$ git branch

  experiment
* less-salt
  main

$ git graph

* bf28166 (HEAD -> less-salt) reduce amount of salt
| * bcb8b78 (experiment) maybe little bit less cilantro
| * f6ec7b7 let us try with some cilantro
|/
* e7cf023 (main) don't forget to enjoy
* 79161b6 add half an onion
* a3394e3 adding README
* 3696246 adding instructions
* f146d25 adding ingredients

Here is a graphical representation of what we have created:

../_images/git-branch-2.svg
  • Now switch to main.

  • In a new commit, improve the README.md file (we added the word “Guacamole”):

    # Guacamole recipe
    
    This is an exercise repository.
    

Now you should have this situation:

$ git graph

* b4af65b (HEAD -> main) improve the documentation
| * bf28166 (less-salt) reduce amount of salt
|/
| * bcb8b78 (experiment) maybe little bit less cilantro
| * f6ec7b7 let us try with some cilantro
|/
* e7cf023 don't forget to enjoy
* 79161b6 add half an onion
* a3394e3 adding README
* 3696246 adding instructions
* f146d25 adding ingredients
../_images/git-branch-3.svg

And for comparison this is how it looks on GitHub.

In branches.md:

Branch-2: Merge branches

Merge experiment and less-salt back into main following the lesson below until the point where we start deleting branches.

In branches.md:

(optional) Branch-3: Perform a fast-forward merge

  1. Create a new branch from main and switch to it.

  2. Create a couple of commits on the new branch (for instance edit README.md):

    ../_images/git-pre-ff.svg
  3. Now switch to main.

  4. Merge the new branch to main.

  5. Examine the result with git graph.

  6. Have you expected the result? Discuss what you see.

In branches.md:

(optional) Branch-4: Rebase a branch (instead of merge)

As an alternative to merging branches, one can also rebase branches. Rebasing means that the new commits are replayed on top of another branch (instead of creating an explicit merge commit). Note that rebasing changes history and should not be done on public commits!

  1. Create a new branch, and make a couple of commits on it.

  2. Switch back to main, and make a couple of commits on it.

  3. Inspect the situation with git graph.

  4. Now rebase the new branch on top of main by first switching to the new branch, and then git rebase main.

  5. Inspect again the situation with git graph. Notice that the commit hashes have changed - think about why!

In branches.md:

Branch-5: Test your understanding

Which of the following combos (one or more) creates a new branch and makes a commit to it?

  1. $ git branch new-branch
    $ git add file.txt
    $ git commit
    
  2. $ git add file.txt
    $ git branch new-branch
    $ git switch new-branch
    $ git commit
    
  3. $ git switch --create new-branch
    $ git add file.txt
    $ git commit
    
  4. $ git switch new-branch
    $ git add file.txt
    $ git commit
    

Conflict resolution

In conflicts.md:

Conflict-1: Create another conflict and resolve

In this exercise, we repeat almost exactly what we did above with a different ingredient.

  1. Create two branches before making any modifications.

  2. Again modify some ingredient on both branches.

  3. Merge one, merge the other and observe a conflict, resolve the conflict and commit the merge.

  4. What happens if you apply the same modification on both branches?

  5. If you create a branch like-avocados, commit a change, then from this branch create another banch dislike-avocados, commit again, and try to merge both branches into main you will not see a conflict. Can you explain, why it is different this time?

In conflicts.md:

(optional) Conflict-2: Resolve a conflict when rebasing a branch

  1. Create two branches where you anticipate a conflict.

  2. Try to merge them and observe that indeed they conflict.

  3. Abort the merge with git merge --abort.

  4. What do you expect will happen if you rebase one branch on top of the other? Do you anticipate a conflict? Try it out.

In conflicts.md:

(optional) Conflict-3: Resolve a conflict using mergetool

  • Again create a conflict (for instance disagree on the number of avocados).

  • Stop at this stage:

    Auto-merging ingredients.txt
    CONFLICT (content): Merge conflict in ingredients.txt
    Automatic merge failed; fix conflicts and then commit the result.
    
  • Instead of resolving the conflict manually, use a visual tool (requires installing one of the visual diff tools):

    $ git mergetool
    
    Conflict resolution using mergetool
  • Your current branch is left, the branch you merge is right, result is in the middle.

  • After you are done, close and commit, git add is not needed when using git mergetool.

If you have not instructed Git to avoid creating backups when using mergetool, then to be on the safe side there will be additional temporary files created. To remove those you can do a git clean after the merging.

To view what will be removed:

$ git clean -n

To remove:

$ git clean -f

To configure Git to avoid creating backups at all:

$ git config --global mergetool.keepBackup false

Inspecting history

In archaeology.md:

History-1: Explore basic archaeology commands

Let us explore the value of these commands in an exercise. Future exercises do not depend on this, so it is OK if you do not complete it fully.

Exercise steps:

  • Clone this repository: https://github.com/networkx/networkx.git. Then step into the new directory and create an exercise branch from the networkx-2.6.3 tag/release:

    $ git clone https://github.com/networkx/networkx.git
    $ cd networkx
    $ git switch --create exercise networkx-2.6.3
    

    On old Git versions which do not know the switch command (before 2.23), you need to use this instead:

    $ git checkout -b exercise networkx-2.6.3
    

Then using the above toolbox try to:

  1. Find the code line which contains "Logic error in degree_correlation".

  2. Find out when this line was last modified or added. Find the actual commit which modified that line.

  3. Inspect that commit with git show.

  4. Create a branch pointing to the past when that commit was created to be able to browse and use the code as it was back then.

  5. How would you bring the code to the version of the code right before that line was last modified?

In archaeology.md:

(optional) History-2: Use git bisect to find the bad commit

In this exercise, we use git bisect on an example repository. It is OK if you do not complete this exercise fully.

Begin by cloning https://github.com/coderefinery/git-bisect-exercise.

Motivation

The motivation for this exercise is to be able to do archaeology with Git on a source code where the bug is difficult to see visually. Finding the offending commit is often more than half the debugging.

Background

The script get_pi.py approximates pi using terms of the Nilakantha series. It should produce 3.14 but it does not. The script broke at some point and produces 3.57 using the last commit:

$ python get_pi.py

3.57

At some point within the 500 first commits, an error was introduced. The only thing we know is that the first commit worked correctly.

Your task

  • Clone this repository and use git bisect to find the commit which broke the computation (solution - spoiler alert!).

  • Once you have found the offending commit, also practice navigating to the last good commit.

  • Bonus exercise: Write a script that checks for a correct result and use git bisect run to find the offending commit automatically (solution - spoiler alert!).

Hints

Finding the first commit:

$ git log --oneline | tail -n 1

How to navigate to the parent of a commit with hash SOMEHASH:

$ git switch --create BRANCHNAME SOMEHASH~1

Instead of a tilde you can also use this:

$ git switch --create BRANCHNAME SOMEHASH^

Using the Git staging area

In staging-area.md:

Staging-1: Perform an interactive commit

One option to help us create nice logical commits is to stage interactively with git commit --patch:

  1. Make two changes in instructions.txt, at the top and bottom of the file. Make sure that they are separated by at least several unmodified lines.

  2. Run git commit --patch. Using the keystrokes above, commit one of the changes.

  3. Do it again for the other change.

  4. When you’re done, inspect the situation with git log, git status, git diff and git diff --staged.

  5. When would this be useful?

In staging-area.md:

Staging-2: Use the staging area to make a commit in two steps

  1. In your recipe example, make two different changes to ingredients.txt and instructions.txt which do not go together.

  2. Use git add to stage one of the changes.

  3. Use git status to see what’s going on, and use git diff and git diff --staged to see the changes.

  4. Feel some regret and unstage the staged change.

Undoing and recovering

In recovering.md:

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).

In recovering.md:

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?

In recovering.md:

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

d3fc63a (HEAD -> main) Revert "not sure this is a good idea"
e02efcd not sure this is a good idea
b4af65b improve the documentation
e7cf023 don't forget to enjoy
79161b6 add half an onion
a3394e3 adding README
3696246 adding instructions
f146d25 adding ingredients

$ git reset --hard b4af65b

HEAD is now at b4af65b improve the documentation

$ git log --oneline

b4af65b (HEAD -> main) improve the documentation
e7cf023 don't forget to enjoy
79161b6 add half an onion
a3394e3 adding README
3696246 adding instructions
f146d25 adding ingredients

In recovering.md:

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?

Interrupted work

In interrupted.md:

Interrupted-1: Stash some uncommitted work

  1. Make a change.

  2. Check status/diff, stash the change with git stash, check status/diff again.

  3. Make a separate, unrelated change which doesn’t touch the same lines. Commit this change.

  4. Pop off the stash you saved with git stash pop, and check status/diff.

  5. Optional: Do the same but stash twice. Also check git stash list. Can you pop the stashes in the opposite order?

  6. Advanced: What happens if stashes conflict with other changes? Make a change and stash it. Modify the same line or one right above or below. Pop the stash back. Resolve the conflict. Note there is no extra commit.

  7. Advanced: what does git graph show when you have something stashed?