Exercise 2 - Non-linear workflow with branches

In this exercise, you will learn how to work with branches. You will create branches, merge them and resolve a conflict.

Create a branch

We want to take some further notes from the workshop. However, we will now work on different branches and start with the chapter on how to inspect the history. First, create a new branch inspect-history

git branch inspect-history

Let's list the branches we have now

git branch
Output
  inspect-history
* main

Fine, but we are still on main. Switch to inspect-history and list branches again

git switch inspect-history
git branch
Output
* inspect-history
  main

The switch command is relatively new in Git. On older versions, you have to use checkout instead. The command checkout can do different things, depending on the context and the arguments. To reduce ambiguities, in newer versions of Git switch can be used instead of checkout to switch between branches, and restore --staged to un-stage changes.

We now have two branches and are currently on inspect-history. When using git log, you will see that both branches point to the same commit.

git log
Output
commit 1860049c5afd2ffed4661e8e36745aec3da57183 (HEAD -> inspect-history, main)
Author: ... <...@ufz.de>
Date:   Wed Jan 27 20:27:02 2021 +0100

    added linear workflow

commit d8d9072990da891df3624358b7a8fc473afb57f8
Author: ... <...@ufz.de>
Date:   Wed Jan 27 20:25:35 2021 +0100

    initial commit

BTW, HEAD shows where you currently are.

There is also a shortcut that creates a branch and switches to it immediately

git switch -c inspect-history

Make changes on a branch

Create a new file inspecting.md

echo.> inspecting.md

Fill it with some content, e.g.

# Inspecting

## Inspect the history

```
git log
```

## Inspecting commits

```
git show
git show 3cda487
git show README.md
```

Save your changes.

Link it in README.md and save again.

# Git Workshop

Content

* [Linear workflow](linear-workflow.md)
* [Inspecting](inspecting.md)
* ...

Now, commit as usual

git status
git diff
git add README.md inspecting.md
git status
git commit -m "added page on inspecting the history"

Let's view the history again

git log

For a more compact view, use

git log --oneline
Output
eba0f8f (HEAD -> inspect-history) added page on inspecting the history
1860049 (main) added linear workflow
d8d9072 initial commit

You will see that branch inspect-history is now one commit ahead of main. I.e. it moved along with our changes, while main did not.

Switching between branches

With Git, it is possible to work on multiple branches in parallel. Using switch, we can simply switch between branches. Observe the content of your project folder. There are now three Markdown files.

Switch to main and see the folder content again

git switch main

Now, the file inspecting.md isn't there anymore. When we switched to main, Git replaced the files in our working copy with the version referenced by main. Switch to branch inspect-history again to see that the working copy changes again and inspecting.md is back again.

git switch inspect-history

Another branch

We now make some parallel edits on an additional branch. First, switch to main again, as we want to start from there rather than from inspect-history

git switch main

Create another branch amending-commits where we will take notes on that aspect

git switch -c amending-commits

Create a new file amending.md

echo.> amending.md

Fill it the following content (and save...)

# Amending commits

```
git status
git diff
git add README.md
git commit --amend
```

Link the file in README.md

# Git Workshop

Content

* [Linear workflow](linear-workflow.md)
* [Amending commits](amending.md)
* ...

You may notice that the link to inspecting.md has gone. This is because we went back to branch main, which does not contain that change.

Commit as usual

git status
git diff
git add README.md amending.md
git status
git commit -m "added page on amending commits"

Let's inspect again what we have, with option --oneline for a more compact view

git log --oneline
Output
0639ed4 (HEAD -> amending-commits) added page on amending commits
1860049 (main) added linear workflow
d8d9072 initial commit

You may be surprised where branch inspect-history has gone. This is because by default, Git only shows what is part of the history of the current state, which inspect-history is not. To see the entire history as a (directed acyclic) graph, add options --all and --graph

git log --graph --oneline --all
Output
* 0639ed4 (HEAD -> amending-commits) added page on amending commits
| * eba0f8f (inspect-history) added page on inspecting the history
|/
* 1860049 (main) added linear workflow
* d8d9072 initial commit

You should see the entire graph now, and that we have diverged from main in two different directions.

Merge

When the work on a branch is completed, it can be merged back into main. To merge, you need to be on the branch you want to merge the other branch into. So we switch to main

git switch main

Now, merge amending-commits into the current branch main

git merge amending-commits
Output
Updating 1860049..0639ed4
Fast-forward
 README.md   | 2 +-
 amending.md | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)
 create mode 100644 amending.md

Inspect the graph again

git log --graph --oneline --all
Output
* 0639ed4 (HEAD -> main, amending-commits) added page on amending commits
| * eba0f8f (inspect-history) added page on inspecting the history
|/
* 1860049 added linear workflow
* d8d9072 initial commit

Now, main and amending-commits point to the same commit, but somehow our branching topology has gone. This is because Git makes a so-called "fast-forward" merge in case there are no conflicts. We can prevent that with the option --no-ff. But first, we have to undo the merge.

Reset a branch

To bring main back to the state before the merge, we make a hard reset to the previous commit (use the correct hash, the 3rd counted from the top!)

git reset --hard 1860049
Output
HEAD is now at 1860049 added linear workflow

Inspect again to see that we have what we had before the merge

git log --graph --oneline --all
Output
* 0639ed4 (HEAD -> amending-commits) added page on amending commits
| * eba0f8f (inspect-history) added page on inspecting the history
|/
* 1860049 (main) added linear workflow
* d8d9072 initial commit

Merge with an extra commit

Now, we can merge without "fast-forward"

git merge amending-commits --no-ff
Output
Merge made by the 'recursive' strategy.
 README.md   | 2 +-
 amending.md | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)
 create mode 100644 amending.md

Inspect again to see that the branching topology is preserved now

git log --graph --oneline --all
Output
*   e25d3cc (HEAD -> main) Merge branch 'amending-commits'
|\
| * 0639ed4 (amending-commits) added page on amending commits
|/
| * eba0f8f (inspect-history) added page on inspecting the history
|/
* 1860049 added linear workflow
* d8d9072 initial commit

Further, a new commit was created, with an automatic message (Merge branch 'amending-commits')

Merge with conflict

We can now try to also merge branch inspect-history

git merge inspect-history --no-ff
Output
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

Git says that automatic merging failed due to a conflict in README.md, and that we should resolve the conflict and then commit. This is also a reminder to always read Git's output carefully.

To get some more insights, check the status

git status
Output
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:
        new file:   inspecting.md

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   README.md

Again, read carefully! We have unmerged paths. Further, is says we can resolve the conflicts and commit, or abort. merge is an example of a modal command, i.e. we have to commit or abort the merge before we can continue to use Git normally. For practice, we abort the merge and view the status:

git merge --abort
git status
Output
On branch main
nothing to commit, working tree clean

That was for practice. Now let's merge again and resolve the conflict.

git merge inspect-history --no-ff
Output
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

We get the same message as before: that automatic merging failed due to a conflict in README.md.

Resolve conflicts

Open file README.md. It has the following content

# Git Workshop

Content

* [Linear workflow](linear-workflow.md)
<<<<<<< HEAD
* [Amending commits](amending.md)
=======
* [Inspecting](inspecting.md)
>>>>>>> inspect-history
* ...

The content between <<<<<<< HEAD and >>>>>>> inspect-history is where the conflict is, with the first part on main (our current HEAD), and the second part on inspect-history. Resolve the conflict and make the file look like this

# Git Workshop

Content

* [Linear workflow](linear-workflow.md)
* [Inspecting](inspecting.md)
* [Amending commits](amending.md)

Now that the conflict is resolved we can stage as usual

git status
git diff
git add README.md
git status
Output
On branch main
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   README.md
        new file:   inspecting.md

Git tells us that all conflicts are fixed, but we are still merging. We do what it suggests and commit

git commit

Leave the message in the popup as is and close it.

Through the commit, we finished the merge and are now no longer in "merge mode". Confirm through checking the status

git status
Output
On branch main
nothing to commit, working tree clean

Inspect the graph again

git log --graph --oneline --all
Output
*   fc527c5 (HEAD -> main) Merge branch 'inspect-history'
|\
| * eba0f8f (inspect-history) added page on inspecting the history
* |   e25d3cc Merge branch 'amending-commits'
|\ \
| |/
|/|
| * 0639ed4 (amending-commits) added page on amending commits
|/
* 1860049 added linear workflow
* d8d9072 initial commit

We see two nicely merged branches.

To finish our work, we should delete the merged branches. They are only pointers and not needed anymore. The topology will remain untouched by deleting them

git branch --delete amending-commits
git branch --delete inspect-history

And, a final check that everything worked as expected

git log --graph --oneline --all
*   fc527c5 (HEAD -> main) Merge branch 'inspect-history'
|\
| * eba0f8f added page on inspecting the history
* |   e25d3cc Merge branch 'amending-commits'
|\ \
| |/
|/|
| * 0639ed4 added page on amending commits
|/
* 1860049 added linear workflow
* d8d9072 initial commit

Continue with Exercise 3