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. Do not stage the changes 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 2607525..ec0abc6 100644
--- a/ingredients.txt
+++ b/ingredients.txt
@@ -1,3 +1,4 @@
 * 2 avocados
 * 1 lime
 * 2 tsp salt
+* 1/2 onion
diff --git a/instructions.txt b/instructions.txt
index 6a8b2af..f7dd63a 100644
--- a/instructions.txt
+++ b/instructions.txt
@@ -3,3 +3,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. Inspect differences between commit hashes with git diff <hash1> <hash2>.

  2. Have a look at specific commits with git show <hash>.

In basics.md:

(optional) Basic-3: Visual diff tools

  • 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: Renaming and removing files

  1. Create a new file, git add and git commit the file.

  2. Rename the file with git mv (you will need to git commit the rename).

  3. Use git log --oneline and git status.

  4. Remove the file with git rm (again you need to git commit the change).

  5. Inspect the history with git log --stat.

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.

  • Change to the branch master.

  • Create another branch called less-salt

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

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

$ git branch less-salt master
  • Where you 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
  master

$ git graph

* bf59be6 (HEAD -> less-salt) reduce amount of salt
| * 6feb49d (experiment) maybe little bit less cilantro
| * 7cf6d8c let us try with some cilantro
|/
* dd4472c (master) we should not forget to enjoy
* 2bb9bb4 add half an onion
* 2d79e7e adding ingredients and instructions

Here is a graphical representation of what we have created:

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

  • Add and commit the following README.md to master:

# Guacamole recipe

Used in teaching Git.

Now you should have this situation:

$ git graph

* 40fbb90 (HEAD -> master) draft a readme
| * bf59be6 (less-salt) reduce amount of salt
|/
| * 6feb49d (experiment) maybe little bit less cilantro
| * 7cf6d8c let us try with some cilantro
|/
* dd4472c we should not forget to enjoy
* 2bb9bb4 add half an onion
* 2d79e7e adding ingredients and instructions
../_images/git-branch-3.svg

And for comparison this is how it looks on GitHub.

In branches.md:

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

  1. Create a new branch from master 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 master.

  4. Merge the new branch to master.

  5. Examine the result with git graph.

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

In branches.md:

(optional) Branch-3: 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 master, and make a couple of commits on it.

  3. Inspect the situation with git graph.

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

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

In branches.md:

Branch-4: 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 checkout new-branch
    $ git commit
    
  3. $ git checkout -b new-branch
    $ git add file.txt
    $ git commit
    
  4. $ git checkout 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 master 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.

In-person workshops: We recommend that you do this exercise in groups of two and discuss with your neighbors.

Video workshops: We will group you in breakout rooms of ~4 persons where you can work and discuss together. A helper or instructor will pop in to help out. In the group one participant can share their screen and others in the group help out, discuss, and try to follow along. Please write down questions in the collaborative notes. After 15-20 minutes we will bring you back into the main room and discuss.

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 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 can you bring the code to the commit precisely 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!).

Video workshops: We will group you in breakout rooms of ~4 persons where you can work and discuss together. A helper or instructor will pop in to help out. Please write down questions in the collaborative notes. After 15-20 minutes we will bring you back into the main room and discuss.

Hints

Finding the first commit:

$ git log --oneline | tail -n 1

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

$ # create branch pointing to the parent of somehash
$ git checkout -b branchname somehash~1

$ # instead of a tilde you can also use this
$ git checkout -b 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

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

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, 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, 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?