---
title: Git Gud
author: Floris van den Bosch
date: February 26, 2024
theme: dracula
slideNumber: \'c/t\'
---

# What is Git?

![](images/Git-Logo-White.png){width=25%}

> - **_version control system_** (VCS)
> - used on the **_command line_**
> - **_Distributed_** version control

---

## Version Control System (VCS)

manage changes made to collections of information

<br>

**Examples**

- editions in books
- revisions of technical standards
- software versions

---
  
## Local VCS

::::: columns
::: column

All version of your project are on your local PC

:::
::: column
![local VCS](images/local.png)
:::
:::::

---

## Centralized VCS
::::: columns
::: column

All versions are on a server

<br>

Easy to manage, but has a single point of failure

:::
::: column

![centralized VCS](images/centralized.png)
:::
:::::

---

## Distributed VCS
::::: columns
::: column
Everyone has a copy of the project.

<br>

This allows for more flexible setups.
:::
::: column

![distributed VCS](images/distributed.png){width=50%}
:::
:::::

# Why use Git?

---

## Improve project management
> 1. As lab journal for your scripts
> 2. Identify when bugs are introduced 
> 3. Share and collaborate on projects

---

## Git works offline
- Make and store changes offline
- Project history is stored offline
- Only need a connection when sharing

---

# Git basics

---

## New/Existing project

`git init`

This creates the `.git` subdirectory that stores all versions

We now have a git repository!

---

### example {.example}

We create a new game based on werewolves and quantum mechanics

 ```shell
$ mkdir quantumwerewolf
$ cd quantumwerewolf
$ git init
```

---

## Copy a project

 `git clone <project-url>` 
 
 The project is copied into the current directory

 ---

### example {.example}

We clone a game about werewolves and quantum mechanics

 ```shell
$ git clone https://github.com/ProodjePindakaas/Quantum-Werewolf.git
$ cd quantumwerewolf
```

---

## Making changes

---

### We found a bug!

```python
if answer.isdigit():  # <-- This is the problem
    i = int(answer) - 1
    if i in range(self.player_count):
        answer = self.players[i]
```

---

### Let's fix it

```python
if answer.isdecimal():  # Now it works!
    i = int(answer) - 1
    if i in range(self.player_count):
        answer = self.players[i]
```

---

### Staging the fix

We now have made a change, but git has not **_recorded_** it yet

We need to tell git which changes to record with

`git add <file>`

These changes are now **_staged_**

---

### Committing the fix

Now the file is ready to be **_committed_**

This will permanently add the changes to the file history

---

### Committing the fix

`git commit`

This will your text editor where you can describe the changes in this **_commit_** in detail.

Or short description with the `-m` option

`git commit -m "Fixed the bug"`

---

### Overview of file states

![Lifecycle of file statuses](images/lifecycle.png)

---

## Viewing changes

`git status`

View the current state of all files

```shell{.small}
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
 (use "git restore --staged <file>..." to unstage)
       modified:   staged_file.txt


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:   modified_file.txt

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

---

### File changes

See what you modified but have not staged with

```shell
$ git diff
```

Or see what you have staged but not committed

```shell
$ git diff --staged
```

---

#### Example

```diff
diff --git a/quantumwerewolf/cli.py b/quantumwerewolf/cli.py
index 6cfa32f..1a38216 100644
--- a/quantumwerewolf/cli.py
+++ b/quantumwerewolf/cli.py
@@ -70,7 +70,7 @@ class CliGame(Game):
    def ask_player(self, query, invalid_players=[]):
        answer = input(query + 'Name: ')

-        if answer.isdigit():
+        if answer.isdecimal():
            i = int(answer) - 1
            if i in range(self.player_count):
                answer = self.players[i]
```

---

### View history

`git log`

Get a summary of the commit history

```shell
$ git log
commit 5658af2ab70472d25bb47cf6a62468ec0b966ed3 (main)
Author: Floris van den Bosch <f.vdbosch98@gmail.com>
Date:   Wed Feb 28 11:06:31 2024 +0100

   Fixed the bug
```
 
 ---

# Intermission

## `git init`

## `git clone` 

## `git add` 

## `git commit` 

## `git status` 

## `git diff` 

## `git log` 

---

# Using branches

---

## Linear History

Without branching our project history looks like this

![](images/basic-branching-1.png)

_Note: the default branch may also be called `main`_

---

## Creating a Branch

`git branch <branch-name>`

this creates a new **_branch_** with the name `<branch-name>`

Usually branches are created for a **_specific purpose_**

- fixing a bug
- a new feature

---

## Selecting a branch

We can **_check out_** the new branch

`git checkout <branch-name>`

Moves the **_HEAD_** to this branch and changes our local files accordingly.

---

### example

We create new branch for the bug

```shell
$ git branch iss53
$ git checkout iss53
```

![](images/basic-branching-2.png){width=80%}

---

### example

With `git status` we can see on which branch we are

```shell
$ git status
On branch iss53
```

---

### example

Now we make the changes as before and commit them with `git commit`

![](images/basic-branching-3.png){width=80%}

---

## Merging branches

When a branch is done and tested we want to **_merge_** it into the main branch

`git checkout <master-branch>`

`git merge <branch-to-merge>`

---

### example

When all is we and tested we merge the fix for the issue.

```shell
$ git checkout master
$ git merge iss53
Updating f42c576..3a0874c
Fast-forward
quantumwerewolf/cli.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
```

---

### example
![](images/basic-branching-3a.png){width=80%}

This we call a **_fast-forward_**, as the history is still linear

---

### diverging example

We are working on two branches

![](images/basic-branching-4.png){width=80%}

---

### diverging example

first we merge the hotfix into master

![](images/basic-branching-5.png){width=60%}

This is a simple **_fast-forward_**

---

### diverging example

The `hotfix` branch can now be deleted

![](images/basic-branching-6.png){width=80%}

---

### diverging example

Finally, when the `iss53` branch is done we merge this into the `master` branch

![](images/basic-merging-2.png){width=80%}

---

## Conflicts

If we alter the same line in the same file, we get a **_conflict_**

---

### example

When a conflict occurs git does not make a new commit

```shell
$ git merge iss53
Auto-merging example.txt
CONFLICT (content): Merge conflict in quantumwerewolf/cli.py
Automatic merge failed; fix conflicts and then commit the result.
```

---

### example

`git mergetool`

This detects the conflict and lets you fix them in your favorite **_mergetool_** (e.g. vimdiff)

After the manual merging we still need to **_commit_** the merge.

---

## Branching workflow

![](images/lr-branches-2.png){width=70%}

- **_master_**: stable branch used for production
- **_develop_**: semi-stable branch with finished features
- _topical_: separate branch for each feature/bug

---

## Rebasing

`git rebase <rebase-to-branch>`

An alternative to a merge (or fast-forward) is the **_rebase_**

It fast-forwards the branching point of the branch

_Best practice: avoid using rebases_

---

### example

We have a simple divergence

![](images/basic-rebase-1.png)

---

### example

We could do a regular merge

```shell
$ git checkout master
$ git merge experiment
```

![](images/basic-rebase-2.png)

---

### example

Instead, we rebase the `experiment` to the `master` branch

```shell
$ git checkout experiment
$ git rebase master
```

![](images/basic-rebase-3.png)

---

### example

Now we move the `master` branch forward again

```shell
$ git checkout master
$ git merge experiment
```

![](images/basic-rebase-4.png)

---

# Working with Remotes

---

## Remotes

A **_remote_** is a copy of the project's repository in another location

**Examples**

 - on Github/Gitlab
 - a different machine in a network
 - a different folder on the same machine

_Note: Using `git clone` automatically adds the remote **origin**_

---

## Showing Remotes

`git remote`

This shows a list of current remotes

`git remote -v` 

This shows the URLs for each remote

---

### example

```shell
$ git clone https://github.com/ProodjePindakaas/Quantum-Werewolf.git
$ cd quantumwerewolf
$ git remote
origin
$ git remote -v
origin  https://github.com/ProodjePindakaas/Quantum-Werewolf.git (fetch)
origin  https://github.com/ProodjePindakaas/Quantum-Werewolf.git (push)
```

---

## Pushing and Pulling

`git fetch <remote>`

Gets the history on your remote

`git push <remote> <branch>`

Sends and add your changes to the remote

`git pull <remote> <branch>`

Retrieves and merges changes from the remote

---

### example

Sending the bugfix to the remote (Github)

```shell
$ git push -u origin iss53
```
<br>

_Note: `-u` is needed as the `iss53` branch does not exist yet on the remote_

---

### example

Or get the fix from the remote

```shell
$ git pull origin iss53
```
<br>

_Note: this might be one situation to use **rebase** if you haven't pulled from the remote in a long while._

---

## Adding remotes

`git remote add <name> <url>`

Adds a new remote

---

### example

We want to run the game on a cluster

```shell
me@cluster $ git clone me@host:/path/to/repository
me@host    $ git remote add cluster me@cluster:/path/to/copy
```

Now we can push our program to the cluster!

---

## Hosting repositories

**self-hosting options**:

- bare repository with SHH/HTTPS
- Self hosted web interface: GitWeb or Gitlab
- Third party hosting: Github or Gitlab

---

# Tips and Tricks

---

## gitignore

Local files that you do not want to record:

- temporary/cache files
- Output files

Declare files/folders you want git to ignore in `.gitignore` files

_Hint: you can find premade `.gitignore` files in [github.com/github/gitignore](https://github.com/github/gitignore) for many programming languages_

---

## Tags

By default, git gives every version (commit) a hash to identify it

`git tag <version>`

Allows you to manually tag the current version (ex. `git tag v3.1.4`)

You can also tag a previous commit with its hash

`git tag <version> <hash>`

---

## stash

We all make mistakes

`git stash`

Temporarily store your file modifications

`git stash pop`

Applies the stored changes to your current branch

---

## git tree

The `git log` command shows a linear history by default

`git log --oneline --graph --color --all --decorate`

Shows the branches explicitly as 'timelines'

---

![](images/tree.png)

---

## Aliases

You can alias command and options you often use

`git config alias.<alias-name> <command>`

such as

`git config --global alias.tree 'log --oneline --graph --color --all --decorate'`

Now we can just run `git tree`!

---

## rerere

You may run into the same conflict numerous times

`git config --global rerere.enabled true`

Enabling ReReRe (Reuse Recorded Resolution) automatically applied your conflict resolutions to future occurences

---

# Further reading

Talk inspired by and with images from the book ProGit

(Chacon, S., & Straub, B. Pro Git (Version 2) [Computer software]. https://github.com/progit/progit2)

![](images/cover.png){width=20%}

https://git-scm.com/