Exercise - Poem Part 2

Poem
Erlkönig – Johann Wolfgang von Goethe

Team 1

Wer reitet so spät durch Nacht und Wind?
Es ist der Vater mit seinem Kind;
Er hat den Knaben wohn in dem Arm,
Er faβt ihn sicher, er hält ihn warm.

Mein Sohn, was birgst du so bang dein Gesicht?
“Siehst, Vater, du den Erlkönig nicht?
Den Erlenkönig mit Kron und Schweif?”
Mein Sohn, es ist ein Nebelstreif.

Team 2

‘Du liebes Kind, komm, geh mit mir!
Gar schöne Spiele spiel ich mit dir
Manch bunte Blumen sind an dem Strand.
Meine Mutter hat manch gülden Gewand.’

“Mein Vater, mein Vater, und hörest du nicht,
Was Erlenkönig mir leise verspricht?”
Sei ruhig, bleibe ruhig, mein Kind;
In dürren Blättern säuselt der Wind.

Team 3

‘Willst, feiner Knabe, du mit mir gehn?
Meine Töchter solln dich warten schön;
Meine Töchter führen den nächtlichen Reihn,
Und wiegen und tanzen und singen dich ein.’

“Mein Vater, mein Vater, und siehst du nicht dort
Erlkönigs Töchter am düstern Ort?”
Mein Sohn, mein Sohn, ich seh es genau;
Es scheinen die alten Weiden so grau.

Team 4

‘Ich liebe dich, mich reizt deine schöne Gestalt;
Und bist du nicht willig, so brauch ich Gewalt.’
“Mein Vater, mein Vater, jetzt fasst er mich an!
Erlkönig hat mir ein Leids getan!”

Dem Vater grauset’s, er reitet geschwind,
Er hält in Armen das ächzende Kind,
Erreicht den Hof mit Mühe und Not;
In seinen Armen das Kind war tot.

Good luck with the exercises!


Advanced git features - The journey of the Erlkönig poem

The teams have now added their verses, but a challenge remains - Ensuring all changes are properly structured, reviewed, and safely integrated into the final poem. Just as the Erlkönig lurks in the shadows hidden, dangers in Git can disrupt progress. Let’s navigate them carefully!


Reset, Tag and Revert

After all teams have contributed their verses, the poem seems complete. However, a new requirement emerges:

For compatibility reasons, all umlauts ‘ä, ö, ü’ must be replaced with ‘ae, oe, ue’.

Unfortunately, some commits already contain these characters, and they have been pushed to the remote repository. The team must now carefully correct the text without losing history and keep the repository clean. Here’s how to handle the situation:

Step 1: Undo the last commit locally

To solve the problem, we’ll ensure that only one person per team makes the necessary change to the umlauts. Everyone should be done with Part 1 of the poem and the Teams verses should’ve been added to the remote feature branch.

First, the person responsible for fixing the umlauts will reset the last commit, while keeping the changes staged. We first fetch all the changes made by our teammates and then reset the latest commit and modify that commit.

git fetch

git pull

git reset --soft HEAD~1

When executing git status you will see that you have the changes of the latest commit locally and can modify them further.

$ git status
On branch feature-teamX
Your branch is behind 'origin/feature-teamX' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
 modified:   erlkoenig.txt

Step 2: Manually edit the file

The person fixing the umlauts will now open the file and manually replace all umlauts with their correct forms using your default editor ’nano’.

nano erlkoenig.txt

Replace:

  • ä → ae
  • ö → oe
  • ü → ue

After making the changes, close the file with Ctrl+X and save it with typing ‘y’ and pressing enter.

Step 3: Recommit the corrected version

Now that the umlauts have been corrected, the person will stage and commit the changes:

git add erlkoenig.txt

git commit -m "<your-name> standardized umlauts for compatibility"

Then, you can push the changes to the remote repository:

git push origin <branch_name_of_your_team>

A merge conflict will occur, fix it with the commands described in the previous chapter Exercise Poem Part 1 . Use ‘Your-name resolve conflict of standardized umlauts’ as commit message.

Step 4: All team members apply the changes using Cherry-Pick

@All team members: Now that one person has made the necessary corrections, the rest of the team will update their local branch to include these changes. Each team member will fetch the latest changes made by the person who fixed the umlauts using ‘git cherry-pick’.

Fetch the latest changes from the remote:

git fetch

Find the commit hash of the ‘Standardized umlauts’ commit. You can do this by looking at the commit history:

git log --oneline

Hint: If you’ve configured an alias for the ‘git log –oneline’ command as described in the basics chapter, you could use your alias ‘git logo’ instead.

Output
$ git log --oneline
4f3756b (HEAD -> feature-teamX, origin/feature-teamX) Your-name resolve conflict of standardized umlauts
9086e44 Your-name standardized umlauts for compatibility
e0364ac Your-name resolved conflict
7e2da2d Your-name adds verses
4d2a06b Tim adds verses
4589ffe Initial text file
bed37fd (main) Your-name initial commit

Cherry-pick the commit that standardized the umlauts into your local branch:

git cherry-pick <commit-hash>

Output

Output without conflicts:

$ git cherry-pick 9086e44
On branch feature-team1
Your branch is up to date with 'origin/feature-team1'.

You are currently cherry-picking commit 9086e44.
  (all conflicts fixed: run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

nothing to commit, working tree clean
The previous cherry-pick is now empty, possibly due to conflict resolution.
If you wish to commit it anyway, use:

    git commit --allow-empty

Otherwise, please use 'git cherry-pick --skip'

Output with conflicts:

$ git cherry-pick 9086e44
Auto-merging erlkoenig.txt
CONFLICT (content): Merge conflict in erlkoenig.txt
error: could not apply 9086e44... Your-name standardized umlauts for compatibility
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".

If there are no conflicts, you don’t need to do anything further.

If any conflicts arise during the cherry-pick, resolve them manually and continue:

nano erlkoenig.txt

git add erlkoenig.txt

git cherry-pick --continue

Output
$ git cherry-pick --continue
[Your-name standardized umlauts for compatibility 9086e44] <commit-message>
 Author: Your-name <Your-name>
 Date: Mon Mar 31 09:40:06 2025 +0200
 1 file changed, 1 deletion(-)

You now have the ‘Standardized umlauts’ commit from your teammate locally.

Optional Step 5: Using git stash to save work in progress

Imagine one of the team members is currently working on a new stanza but hasn’t committed it yet. They don’t want to lose their progress while applying the cherry-picked umlaut fix. This is where ‘git stash’ comes in handy:

git stash

This temporarily saves their changes and reverts the working directory to a clean state. Now they can safely apply the cherry-picked commit.

They can see their stashed work with:

git stash list

After applying the umlaut fix, they retrieve their stashed work with:

git stash apply

Now their stanza is back, and they can continue working without having lost any progress.

If they no longer need the stash after applying it they can use the following command to remove it from the stash list:

git stash pop

Step 6: Tagging the standardized version for future reference

After using cherry-pick and pushing the umlaut fix, it is useful to mark this version as a reference point. This way, if something goes wrong later, the team can easily return to this version.

One team member should create the tag with your team-number.

git tag -a v<team-number>-umlauts-fixed --message "Umlauts standardized"

To ensure this tag is available to everyone, push it to the remote repository:

git push origin v<team-number>-umlauts-fixed

Output
$ git push origin v1-umlauts-fixed
Username for 'https://git.gitlab.cloudtrainings.online':
Password for 'https://novatec1@git.gitlab.cloudtrainings.online':
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 161 bytes | 161.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
To https://git.gitlab.cloudtrainings.online/root/git-training.git
 * [new tag]         v1-umlauts-fixed -> v1-umlauts-fixed

Now, if at any point the team needs to check the standardized version, they can use:

git pull --tag

git tag

git checkout v<team-number>-umlauts-fixed

Output
$ git checkout v1-umlauts-fixed
Note: switching to 'v1-umlauts-fixed'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 4f3756b Your-name resolve conflict of standardized umlauts

Git enters a detached ‘HEAD’ state because the tag points to a specific commit rather than a branch. This means you’re not on any active branch, and any new commits you make will be floating without being attached to a branch.

If you want to make changes based on the tag, create a new branch with git switch -c <new-branch-name>.

Switch back to your teams branch:

git switch feature-teamX

Step 7: Reverting changes if a mistake was made

A few minutes later, some team members realize they can actually use the umlauts so they want to revert the change. Instead of manually fixing them again, they can revert the faulty commit and create a new commit that undoes the change:

Get the commit hash of the ‘standardized umlauts for compatibility’ commit:

git log --oneline

Now revert that commit:

git revert <commit-hash>

Your ’nano’ text editor will open with an edited commit message containing information about the reversion. Just close it with Ctrl+X. The unwanted change is now undone while preserving the commit history.

When you execute the git log --oneline command again you will now see that there is a new commit with your reversion, but the commit you reverted still exists.


Rebase and final merge - The Grand Finale

The teams have collaborated, resolved conflicts, and refined their verses. Now, it’s time to bring everything together into the main branch while maintaining a clean and structured commit history. Instead of merging directly, we will use interactive rebase and Merge Requests in GitLab to ensure a well-organized integration.


Cleaning up the commit history with interactive rebase

Before merging into ‘main’, each team should review and refine their commit history to remove unnecessary or redundant commits.

Step 1: Start an interactive rebase

Run the following command. Adjust the number <4> to include all commits of your teammates.

You can check the number of commits you want to include with git log --oneline.

git rebase --interactive HEAD~4

This opens a text editor where you will see a list of recent commits like this:

Output
pick  a1b2c3d  Added verse 1
pick  d4e5f6g  Fixed typo in verse
pick  h7i8j9k  Updated verse formatting
pick  l1m2n3o  Standardized umlauts

Step 2: Modify the commit history

Change the word pick to one of the following actions:

  • squash (s) → Combine the commit with the one above.
  • reword (r) → Edit the commit message.
  • drop (d) → Remove the commit.

For example, if the ‘Fixed typo in verse’ and ‘Updated verse formatting’ commits should be merged into the ‘Added verse 1’ commit you could do the following:

Solution
pick  a1b2c3d  Added verse 1
squash d4e5f6g  Fixed typo in verse
squash h7i8j9k  Updated verse formatting
pick  l1m2n3o  Standardized umlauts

This would add the two commits to the first commit ‘Added verse 1’.

Save and close the file. Git will then prompt you to edit the new combined commit message.

Please delete the old commit messages and enter a new one ‘Team X final commit’. Save and close the file.

Output
Team 1 final commit
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Author:    Your-Name
# Date:      Mon Mar 31 07:29:57 2025 +0200
#
# interactive rebase in progress; onto bed37fd
# Last commands done (3 commands done):
#    squash 7e2da2d Your-Name adds verses
#    squash 9086e44 Your-Name standardized umlauts for compatibility
# No commands remaining.
# You are currently rebasing branch 'feature-team1' on 'bed37fd'.
#
# Changes to be committed:
#       new file:   erlkoenig.txt
#

Step 3: Complete the rebase

Now force push your changes:

git push --force origin <branch_name_of_your_team>

This step ensures that the feature branch has a clean and structured commit history before merging into the ‘main’ branch.

No worries if the squash commit didn’t work out as expected, your instructor will discuss this together with you later. For now, just continue with the exercise.


Merge Request

Now one person of your team opens a Merge Request in GitLab:

Step 1: Opening a merge request in GitLab

  1. Go to GitLab → ‘Projects’ → ‘git-training’ → ‘Merge Requests’ → ‘New Merge Request’.

  2. Select the source branch (‘branch_name_of_your_team’) and target branch (‘main’) and click on ‘compare branches and continue’.

    NewMergeRequest NewMergeRequest

  3. Review the changes and provide a meaningful description like ‘Team X stanza’.

  4. Submit the Merge Request with ‘Create Merge Request’.

    CreateMergeRequest CreateMergeRequest

Under the ‘Commits’ section of your merge request you can see all the work you’ve done.

Step 2: Handling merge request issues

If the Merge Request has merge conflicts, GitLab will notify you. To fix them run the following locally:

git fetch

git rebase origin/main

Resolve any conflicts manually by opening the files with your nano editor nano erlkoenig.txt and save it with your changes. Continue the rebase:

git add <conflicted-files>

git rebase --continue

If the rebase was successful you will receive the following output:

Successfully rebased and updated refs/heads/<your-branch-name>.

You can now force push your changes:

git push --force origin <branch_name_of_your_team>

Once the conflicts are resolved, GitLab will automatically update the Merge Request.

Step 3: Merging the request

Once the Merge Request is approved and conflicts are resolved, merge it into ‘main’ via GitLab.

Click Merge in GitLab.

MergeBranch MergeBranch

BONUS Step 4: Rebase the feature branch onto the main branch

If your merge request doesn’t contain any merge conflicts you could now experience a rebase with the following.

Hint: This is only possible if other teams have merged their branches to the main branch beforehand.

git fetch

git rebase origin/main

Output

If the rebase was successful you will receive the following output:

$ git rebase origin/main
Successfully rebased and updated refs/heads/feature-team1.

If conflicts occur, Git will pause the rebase. Open the conflicting files and resolve the conflicts, then:

git add <conflicted-files>

git rebase --continue

You should now receive the success message from above.


Clean Code Practices – Python Scripts & .gitignore

As the teams continue polishing the poem, one member decides to write a small Python script to format the poem automatically only for test purposes. This script generates a new file — but: these helper files should not be part of version control!

Finally let’s learn how to properly use a .gitignore file in a Python-based project.

Step 1: Write the Python helper script

Create a file called format_poem.py by typing:

nano format_poem.py

Enter the following code:

# format_poem.py
# Helper script to format the Erlkönig poem

input_file = "erlkoenig.txt"
output_file = "erlkoenig_formatted.txt"

with open(input_file, "r", encoding="utf-8") as f:
    lines = f.readlines()

formatted = [line.strip().capitalize() + "\n" for line in lines if line.strip()]

with open(output_file, "w", encoding="utf-8") as f:
    f.writelines(formatted)

print("Poem has been formatted.")

Close and save it. Then use the following command to run the script:

python format_poem.py

A new file erlkoenig_formatted.txt will be created. It’s useful — but it should not be committed to the repository.

Step 2: Create a .gitignore file

To exclude these files from Git, create a .gitignore file:

nano .gitignore

Add the following lines:

# Ignore Python helper script
format_poem.py

# Ignore auto-generated output
erlkoenig_formatted.txt

# Ignore Python bytecode
__pycache__/
*.pyc

Save and close the file.

Step 3: Check if files are ignored

Use the following command to verify the status:

git status

You should not see format_poem.py or erlkoenig_formatted.txt listed anymore — that means Git is ignoring them successfully. Your output should look similar to this:

Output
On branch feature-teamX

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  .gitignore

nothing added to commit but untracked files present (use "git add" to track)

Conclusion

The poem The Erlkönig has now been successfully assembled, reflecting the collaboration and skill of the teams. By navigating conflicts, structuring commits, refining history, and practising clean code structure, participants have mastered essential git skills.

Review your commit history to admire the journey:

git log --oneline --graph


Celebrate the successful collaboration and the completion of the git training!