Version Control with Git

Authors
Dr. Atle Rimehaug | Dr. Ole Bialas | Dr. Nicholas Del Grosso | Dr. Sangeetha Nandakumar

Writing code is often not a linear process, involving exploration, tinkering, and trial-and-error. Without the right tools, this can result in a messy codebase with multiple, slightly modified versions of the same code, making it hard to understand, maintain and reproduce the project. Version control systems provide a solution for this: They record snapshots of your project at different points in time, which allows you to see what changed and why and to revert to previous versions of your code if needed.

Git is a distributed version control system and it is by far the most popular one with an adoption rate of over 90% in the software industry. In this lesson, you’ll learn the basics of Git: how to track changes, view the project’s history and restore old versions.

The materials feature two different ways of using Git: the terminal and VS Code’s graphical user interface (GUI). It is recommended that you test out both methods because they each have their distinct advantages: The GUI provides a convenient interface that makes it easier to integrate Git into your workflow while the terminal allows you to use more advanced functionalities of Git that are not implemented in the GUI.

If you haven’t installed Git already, go to the website , download the installer for your operating system and follow the instructions (there are a lot of options in the installation - you can accept the defaults). Then run the following commands in your terminal (replacing the text in quotations with your actual name and email address) so Git can associate your commits with your identity.

git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

Section 1: Tracking Files with Git

Background

With Git, you create regular snapshots of your project files and save them in the version control system. This is a two-step process: first, you add files from your working directory to the staging area, then you commit all changes in the staging area to the repository where they are stored permanently.

The two-step process allows you to define exactly what goes into every snapshot. In a typical workflow, you stage any change you make to the files in the temporary staging area and once you are happy with the changes you have made, you commit them. The points where you stage and commit are up to you. Ideally, every snapshot reflects one unit of work, such as adding data, editing a script or creating a figure, and has a message that identifies the purpose of the commit. This gives you a history that is easy to read, understand, and undo if required.

Note that Git can only see your changes once they have been saved. In VS Code, if you have made a change to a file but haven’t saved it yet, there will be a white dot next to the filename in the tab in the editor (see red line in the screenshot below). For Git to detect the change, you need to save by pressing Ctrl + S (or Cmd + S on a Mac keyboard) or enable “Auto Save” via the “File” menu in the top left corner of VS Code.

Exercises

In this section, you are going to create your first git repository, add files to it and track their changes. Below is a table with the required commands that can be run in the terminal. There are also instructions for the same operations in the graphical interface of VS Code (other editors have similar interfaces). You can switch between both approaches as you like.

Code Description VS Code GUI
git init Initialize a new Git repository in the current directory Click Source Control (Ctrl + Shift + G) → Click “Initialize Repository”
git status Show the status of changes in the working directory Click Source Control (Ctrl + Shift + G) → See the file changes
git add file1.txt Stage file1.txt for commit Click ➕ (plus) icon next to the file in Source Control
git add . Stage all modified and new files Click ➕ (plus) icon next to each file OR Click “Stage All Changes”
git commit -m "<message>" Commit staged files with a message Click ✔ (checkmark) icon, enter commit message, and press Enter
git commit -am "<message>" Stage and commit all modified files in one step Click “Stage All Changes”, then ✔ (checkmark) icon

First, create a new, empty folder and open it in VS Code or do it in the terminal:

mkdir /tmp/new_repo # create directory
cd /tmp/new_repo # change working directory
/tmp/new_repo

Exercise: Initialize a new Git repository in this folder.

Solution
git init
Initialized empty Git repository in /tmp/new_repo/.git/

Exercise: Check the status of the repository - there should be nothing to commit yet.

Solution
git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Example: Create a new file called experiment_1.txt with any content and add it to the staging area.

# after creating experiment_1.txt:
git add experiment_1.txt

Exercise: Create experiment_2.txt and experiment_3.txt and stage both files.

Solution
# after creating experiment_2.txt and experiment_3.txt:
git add .

Exercise: Commit the staged changes and add a message like "Add data from experiments".

Solution
git commit -m "Add data from experiments"
[main (root-commit) d97c96c] Add data from experiments
 3 files changed, 3 insertions(+)
 create mode 100644 experiment_1.txt
 create mode 100644 experiment_2.txt
 create mode 100644 experiment_3.txt

Make a change to experiment_1.txt and check the status - it should show you a modified file.

Solution
# after modifying experiment_1.txt:
git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   experiment_1.txt

no changes added to commit (use "git add" and/or "git commit -a")

Exercise: Add and commit the change (Hint: in the terminal, you can use commit -am to add and commit in one go.). Check the status to confirm there are no untracked changes.

Solution
#After modifying experiment_1.txt:
git commit -am "Add data to experiment 1"
git status
[main 53d0862] Add data to experiment 1
 1 file changed, 2 insertions(+)
On branch main
nothing to commit, working tree clean

Exercise: Make changes to experiment_2.txt and experiment_3.txt and save them in a single commit. Check the status whenever you need to.

Solution
#After modifying experiment_2.txt and experiment_3.txt:
git status
git add .
git status
git commit -m "New data for experiments 2 & 3"
git status
On branch main
nothing to commit, working tree clean
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   experiment_2.txt
	modified:   experiment_3.txt

[main 6f6bedb] New data for experiments 2 & 3
 2 files changed, 2 insertions(+)
On branch main
nothing to commit, working tree clean

Exercise: Delete experiment_3.txt, check the status and commit the change.

Solution
# after deleting experiment_3.txt:
git status
git commit -am "Delete experiment 3"
On branch main
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    experiment_3.txt

no changes added to commit (use "git add" and/or "git commit -a")
[main 471695c] Delete experiment 3
 1 file changed, 2 deletions(-)
 delete mode 100644 experiment_3.txt

Section 2: Inspecting the Commit History

Background

Commits are saved in order, creating a history of changes in the project. You can see past commits using git log or in the “Graph” panel of VS Code’s “Source Control” interface. Each entry contains a unique identifier (hash), a message as well as the time and author of the commit so you know who did what when.

The commit history also allows us to compare specific commits with git diff. The diff algorithm compares two files line-by-line to tell us exactly what has changed. We can select commits for git diff in two ways: either by using the commit hash or by the position of the commit relative to the most recent commit called HEAD (for example, HEAD~1 refers to the second most recent commit). Note that the commit hashes are unique for every repository so the ones you’ll see in the exercises and solutions in this notebook will not match the ones you’ll see on your machine.

Exercises

In this section, you’ll start by inspecting the commit history of your project. You’ll then use diff and compare specific commits to see what has changed. Some of these operations can be executed via the terminal and VS Code’s graphical interface while others are terminal only.

Code Description VS Code GUI
git log View the full commit history of the repository Expand the Graph in Source Control
git log --oneline View a compact version of the commit history N/A (terminal only)
git log -2 Display the last two commits N/A (terminal only)
git diff a1b2c3d Compare the working directory to the commit with the hash a1b2c3d Click on the file in Source Control and check the inline diff
git diff HEAD~1 Compare the working directory to the second most recent commit Click on the file in Source Control and check the inline diff
git diff HEAD~3 HEAD~2 Compare the third and second most recent commits N/A (terminal only)
git diff <commit1> <commit2> Compare two specific commits (the older commit goes first) N/A (terminal only)

Exercise: View the full commit history of the repository.

Solution
git log
commit 471695c1322b7286449b25dcab795852b3abbed1 (HEAD -> main)
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:53:18 2026 +0200

    Delete experiment 3

commit 6f6bedb37a946a29ec3624caf3c17ecaafb01763
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:51:18 2026 +0200

    New data for experiments 2 & 3

commit 53d0862ad4154a537807b877f9f8b50678886e77
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:51:11 2026 +0200

    Add data to experiment 1

commit d97c96cc4a8c23e93524fffe8b31435342dfa100
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:49:59 2026 +0200

    Add data from experiments

Exercise: Check your commit history, find the second most recent commit and copy its hash.

Solution
git log -2
commit 471695c1322b7286449b25dcab795852b3abbed1 (HEAD -> main)
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:53:18 2026 +0200

    Delete experiment 3

commit 6f6bedb37a946a29ec3624caf3c17ecaafb01763
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:51:18 2026 +0200

    New data for experiments 2 & 3

Exercise: Use the hash from the previous exercise to compare the commit to the working directory.

Solution
git diff a1b2c3d
diff --git a/experiment_3.txt b/experiment_3.txt
deleted file mode 100644
index 6e1ddff..0000000
--- a/experiment_3.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This is the 3rd experiment
-There are 4 conditions

Exercise: Compare the same commit to the working directory using HEAD~1.

Solution
git diff HEAD~1 # alternatively, use the commit hash
diff --git a/experiment_3.txt b/experiment_3.txt
deleted file mode 100644
index 6e1ddff..0000000
--- a/experiment_3.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This is the 3rd experiment
-There are 4 conditions

Exercise: Compare the differences between the third-most-recent commit and the working directory using HEAD or the hash.

Solution
git diff HEAD~2 # alternatively, use the commit hash
diff --git a/experiment_2.txt b/experiment_2.txt
index bfa0eae..72b7631 100644
--- a/experiment_2.txt
+++ b/experiment_2.txt
@@ -1 +1,2 @@
 This is the 2nd experiment
+There are 2 conditions
diff --git a/experiment_3.txt b/experiment_3.txt
deleted file mode 100644
index ea480b1..0000000
--- a/experiment_3.txt
+++ /dev/null
@@ -1 +0,0 @@
-This is the 3rd experiment

Exercise: Compare two specific commits.

Solution
git diff HEAD~3 HEAD~2 # alternatively, use the commit hashes
diff --git a/experiment_1.txt b/experiment_1.txt
index e9d916c..f6bbf50 100644
--- a/experiment_1.txt
+++ b/experiment_1.txt
@@ -1 +1,3 @@
 This is the 1st experiment
+There are 3 conditions
+aaa

Section 3: Reverting and Undoing Changes

Background

Because Git keeps the full history of all files, it allows you to go back to a previous version of your work if needed. You can run git checkout <commit> to set your repository to the exact state of that commit. This puts you in a “detached HEAD” state - this may sound scary but it simply means Git is no longer remembering new commits as part of your normal project history. This means that the detached HEAD state is useful for inspecting and experimenting with old commits but not for committing new work. To return to the latest commit on your default branch, use git checkout main or git checkout master, depending on the branch name in your repository.

You can also restore older commits permanently by using git reset. There are different reset modes: --soft and --hard. With a soft reset, you are keeping all modifications that happened after the reset point as staged changes in your working directory. With a hard reset, you immediately lose all modifications after the reset point which makes the operation dangerous. Resets can also be problematic in shared repositories because rewriting the git history can cause problems for your collaborators.

Another way of restoring older commits is git revert. Instead of removing changes, revert creates a new commit that undoes the changes made by the given commit. This is safer because it keeps the commit history intact. The downside is that reverts may cause merge conflicts. If you are trying to undo a line that has been modified again in later commits, Git does not know what to do. Should it undo the requested commit as well as the later one or keep the later one and ignore the revert? In these cases, Git will warn you about a merge conflict which means that you have to select manually what to keep and what to drop.

Exercises

In this section, you’ll start by using git checkout to inspect older versions of your repository and then use git reset and git revert to restore those older versions. Because most of these operations are not exposed by VS Code’s graphical interface, you’ll have to use the terminal. Here are the relevant commands:

Command Description
git checkout HEAD~1 “Rewind one step”: Update the working directory to match the second latest commit.
git checkout <commit> “Jump to a specific commit”: Update the working directory to match the given <commit>.
git checkout main Return to the latest commit on the main branch. Use master instead if that is your default branch name.
git reset --soft <commit> Rewind the history to commit hash <commit>, keeping all changes after in the staging area (dangerous in shared repos).
git reset --hard <commit> Rewind the history to commit hash <commit>, dumping all commits made afterward (dangerous in shared repos)
git revert <commit> Make a new commit to undo the given <commit> (great in shared repos).

Exercise: Check the last entry in your commit history, then use git checkout HEAD~1 to update the working directory to the second latest commit and check the commit history again.

Solution
git log -3 --oneline
git checkout HEAD~1
git log -3 --oneline
471695c (HEAD -> main) Delete experiment 3
6f6bedb New data for experiments 2 & 3
53d0862 Add data to experiment 1
Note: switching to 'HEAD~1'.

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 6f6bedb New data for experiments 2 & 3
6f6bedb (HEAD) New data for experiments 2 & 3
53d0862 Add data to experiment 1
d97c96c Add data from experiments

Exercise: Use git checkout main (or git checkout master) to move the working directory back to the most recent commit on your default branch and check the commit history.

Solution
git checkout main # use master instead if that is your default branch
git log -3 --oneline
Previous HEAD position was 6f6bedb New data for experiments 2 & 3
Switched to branch 'main'
471695c (HEAD -> main) Delete experiment 3
6f6bedb New data for experiments 2 & 3
53d0862 Add data to experiment 1

Exercise: Check the commit history, pick a commit from the past and checkout to that commit. Explore the files to see how the repository looked at that time. When you are done, checkout to the most recent commit on your default branch again.

Solution
git checkout HEAD~3 # or use the commit hash
# after exploring the files:
git checkout main # use master instead if that is your default branch
Note: switching to 'HEAD~3'.

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 d97c96c Add data from experiments
Previous HEAD position was d97c96c Add data from experiments
Switched to branch 'main'

Exercise: Undo the latest commit using git revert HEAD. This may open the text editor VIM to edit your commit message. To exit VIM, press Esc, type :wq (w for write, q for quit) and hit Enter.

Solution
git revert HEAD
[main a0fc378] Revert "Delete experiment 3"
 Date: Thu Jun 4 12:54:20 2026 +0200
 1 file changed, 2 insertions(+)
 create mode 100644 experiment_3.txt

Exercise: Check the commit history to view the entry made by revert. Then run git revert HEAD again to undo the undoing of the previous commit.

Solution
git log -1
git revert HEAD
commit a0fc3786da341621aed4d46f1ccd1a76b541f581 (HEAD -> main)
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:54:20 2026 +0200

    Revert "Delete experiment 3"
    
    This reverts commit 471695c1322b7286449b25dcab795852b3abbed1.
[main 6c89aee] Reapply "Delete experiment 3"
 Date: Thu Jun 4 12:54:29 2026 +0200
 1 file changed, 2 deletions(-)
 delete mode 100644 experiment_3.txt

Exercise: Check the commit history to view the entry made by revert. Then run git revert HEAD again to undo the undoing of the previous commit and check the history again.

Solution
git log -1
git revert HEAD
git log -2
commit 6c89aee6255469f46d72ebeb6355ac9aa007553c (HEAD -> main)
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:54:29 2026 +0200

    Reapply "Delete experiment 3"
    
    This reverts commit a0fc3786da341621aed4d46f1ccd1a76b541f581.
[main 8c77a0b] Revert "Reapply "Delete experiment 3""
 Date: Thu Jun 4 12:54:32 2026 +0200
 1 file changed, 2 insertions(+)
 create mode 100644 experiment_3.txt
commit 8c77a0bff80f8b26110436978143a6277a08b5c0 (HEAD -> main)
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:54:32 2026 +0200

    Revert "Reapply "Delete experiment 3""
    
    This reverts commit 6c89aee6255469f46d72ebeb6355ac9aa007553c.

commit 6c89aee6255469f46d72ebeb6355ac9aa007553c
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:54:29 2026 +0200

    Reapply "Delete experiment 3"
    
    This reverts commit a0fc3786da341621aed4d46f1ccd1a76b541f581.

Exercise: Pick a commit from the past and use git reset --soft to reset the repository to that state. Then, check git status - you should see the modifications that happened after the reset point as staged changes. Commit the staged changes again.

Solution
git reset --soft HEAD~2
git status
git add .
git commit -m "re-commit after soft reset"
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   experiment_2.txt
	modified:   experiment_3.txt

[main 5aabcbe] re-commit after soft reset
 2 files changed, 2 insertions(+)

Exercise: Pick a commit from the past and use git reset --hard to reset the repository to that state. Then, check git status - you should not see any changes because the modifications after the reset point have been dropped.

Solution
git reset --hard HEAD~2
git status
HEAD is now at d97c96c Add data from experiments
On branch main
nothing to commit, working tree clean

Section 4: Ignoring Files

Background

By default, Git tracks every file in the directory. However, some files and folders should not be tracked. For example, Git is not intended to be used for large files, so you should not let Git track your neural recordings. To prevent Git from tracking specific files, we can create a file called .gitignore and add the to-be-ignored files. .gitignore is simply a text file where you can either list individual files or patterns such as *.pdf or subject*, which mean “ignore every file/folder ending with .pdf or starting with subject”. There are also .gitignore generators that provide ready-made templates with files and patterns that are commonly ignored for a given language.

Importantly, adding a file to .gitignore that is already tracked by Git will not automatically make Git drop it. To do this, you need to call git rm --cached <file> which will remove it from Git’s index but keep it as a file in your working directory. While Git will ignore changes in this file going forward, it will not remove it from the commit history so it can still be restored later. Note that editing .gitignore and running git rm --cached are changes that need to be committed to be effective.

Exercises

In the following exercises you will create a .gitignore file, add files to ignore and observe how this affects Git’s behavior. Here are the commands you need to know:

Code Description VS Code GUI
git status Show the status of changes in the working directory Click Source Control (Ctrl + Shift + G) → See the file changes
git add f1.txt f2.txt Stage specific files for commit Click ➕ (plus) icon next to the files in Source Control
git add . Stage modifications of all files Click ➕ (plus) icon next Changes
git commit -m "Commit message" Commit staged files with a message Click ✔ (checkmark) icon, enter commit message, and press Enter
git commit -am "Commit message" Stage and commit all modified files in one step Click “Stage All Changes”, then ✔ (checkmark) icon
git rm --cached <file> Remove the file from Git’s index N/A (command line only)

Exercise: Create the .gitignore file in your folder and add experiment_4.txt to it. Then stage and commit the file.

Solution
# after creating .gitignore and adding experiment_4.txt:
git add .
git commit -m "add .gitignore"
[main 09d1502] add .gitignore
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore

Exercise: Create experiment_4.txt and check git status. You should not see any untracked changes because the file should be ignored.

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

Exercise: Add *.txt to .gitignore to ignore all text files and commit.

Solution
# after adding *.txt to .gitignore
git commit -am "ignore all text files"
[main e295f6c] ignore all text files
 1 file changed, 1 insertion(+), 1 deletion(-)

Example: Use git rm --cached to remove experiment_1.txt from Git’s index but keep it in your working directory and commit.

Solution
git rm --cached experiment_1.txt
git commit -am "rm experiment_1.txt"
rm 'experiment_1.txt'
[main fc5f115] rm experiment_1.txt
 1 file changed, 1 deletion(-)
 delete mode 100644 experiment_1.txt

Exercise: Use git rm --cached to remove experiment_2.txt from Git’s index but keep it in your working directory and commit.

Solution
git rm --cached experiment_2.txt
git commit -am "rm experiment_2.txt"
rm 'experiment_2.txt'
[main e4edd72] rm experiment_2.txt
 1 file changed, 1 deletion(-)
 delete mode 100644 experiment_2.txt

Exercise: Modify experiment_2.txt and check git status - it should not show any changes.

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

Section 5: Bonus: Working with Branches

Background

When experimenting with code, we usually want to avoid breaking the project. Git branches are great for this - they allow you to create a separate copy of your repository that can be modified without affecting anything else. When a repository is initialized, Git creates a single default branch called main (or master in older versions) but you can create additional branches using the git branch command. The working directory always shows the currently selected branch which is shown in the VS Code GUI next to the repository name or when you call git status. When you switch branches with git checkout your working directory will change to the new branch.

If you made commits to a new branch that you want to incorporate into your main branch you can merge the branches. This is a typical workflow for collaborative coding: everyone creates their own branch off the default branch, makes their commits and then (after review) merges them back.

Git creates a new commit for every merge that contains the entire commit history of the merged branch. A useful variant is git merge --squash which treats all commits in the merged branch as a single commit. This is nice when you experimented on a side branch, made a bunch of commits and want to avoid cluttering the git history with them.

Exercises

In this bonus section you are going to create new branches in your repository, edit those branches and then merge them into main or master. You can do this via the terminal or using the branch menu in VS Code’s graphical interface (see screenshot below).

If you are working in the terminal and merge a branch without adding a message with -m the terminal may open the editor Vim where you can edit the default message. To exit Vim press Esc, type :wq (write + quit) and hit enter.

Command Description VS Code GUI
git branch List the branches in the repo (active one has a star by it) Source Control -> click on the current branch
git branch <name> Make a new branch from the current commit, named <name> Branches -> Create Branch … (VS Code immediately switches to the new branch)
git branch -d <name> Delete the branch <name> and all the commits with it. Branches -> Delete Branch …
git branch -D <name> Delete the branch <name> and all the commits with it, even if they aren’t merged somewhere else already. Branches -> Delete Branch … and confirm
git checkout <name> Switch the working directory to the specified branch. Source Control -> click on the current branch -> select another one
git merge <name> Merge the commits from the branch <name> into the current branch. Branches -> Merge …
git merge --squash <name> Stage the combined changes from the branch <name> as one commit in the current branch. N/A (terminal only)

Exercise: Create a tracked Markdown file called notes.md that you can modify on different branches.

Solution
echo "# Project notes" > notes.md
git add notes.md
git commit -m "Add project notes"
[main a763c0b] Add project notes
 1 file changed, 1 insertion(+)
 create mode 100644 notes.md

Exercise: Create a new branch called feature1 and switch to that branch. Confirm you are on feature1 by checking git status or the Source Control panel in VS Code.

Solution
git branch feature1
git checkout feature1
git status
Switched to branch 'feature1'
On branch feature1
nothing to commit, working tree clean

Exercise: Make at least two commits to feature1.

Solution
# after modifying notes.md:
git commit -am "Update project notes"
git commit -am "Update project notes again"
[feature1 131f4e8] Update project notes
 1 file changed, 1 insertion(+)
[feature1 41624f9] Update project notes again
 1 file changed, 1 insertion(+)

Exercise: Switch back to main (might also be called master) and merge feature1. Check the commit history to confirm your default branch contains the commits from feature1.

Solution
git checkout main # use master instead if that is your default branch
git merge feature1
git log -3
Switched to branch 'main'
Merge made by the 'ort' strategy.
 notes.md | 2 ++
 1 file changed, 2 insertions(+)
commit bb8ee1aa10939798c6a62fd20f30f34cdf425113 (HEAD -> main)
Merge: a763c0b 41624f9
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:56:59 2026 +0200

    merge feature1 into default branch

commit 41624f9335d04f248edf6abffa185426650d4975 (feature1)
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:56:57 2026 +0200

    Update project notes again

commit 131f4e86859f12fc534f3cc47aa47bc2d4a761a7
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:56:57 2026 +0200

    Update project notes

Exercise: Delete the branch feature1.

Solution
git branch -d feature1
Deleted branch feature1 (was 41624f9).

Exercise: Switch to a new branch feature2 and make at least two commits to that branch.

Solution
git branch feature2
git checkout feature2
# after modifying notes.md:
git commit -am "Update notes on feature2"
# after another change:
git commit -am "Update notes on feature2 again"
Switched to branch 'feature2'
[feature2 dab5de7] Update notes on feature2
 1 file changed, 1 insertion(+)
[feature2 2fb43ec] Update notes on feature2 again
 1 file changed, 1 insertion(+)

Exercise: Switch back to main (or master) and merge feature2 with --squash. Then, check the commit history - you should only see a single commit for the merge.

Solution
git checkout main # use master instead if that is your default branch
git merge --squash feature2
git commit -m "squash merge feature2 into default branch"
git log -3
Switched to branch 'main'
Updating bb8ee1a..2fb43ec
Fast-forward
Squash commit -- not updating HEAD
 notes.md | 2 ++
 1 file changed, 2 insertions(+)
[main 4cfb798] squash merge feature2 into default branch
 1 file changed, 2 insertions(+)
commit 4cfb7980a0d70a461282dcf3bede8d8a93da416e (HEAD -> main)
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:57:19 2026 +0200

    squash merge feature2 into default branch

commit bb8ee1aa10939798c6a62fd20f30f34cdf425113
Merge: a763c0b 41624f9
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:56:59 2026 +0200

    merge feature1 into default branch

commit 41624f9335d04f248edf6abffa185426650d4975
Author: obi <ole.bialas@posteo.de>
Date:   Thu Jun 4 12:56:57 2026 +0200

    Update project notes again