Skip to content

Chapter 10: Git Version Control

Source slides: V08_Git.pdf. Exercise: E08_Git.pdf. The exercise has three tasks: setup & first commit, branching & merging, GitLab collaboration. The lecture references https://learngitbranching.js.org/ for branch demos.


1. Chapter Overview

In any HPC/CFD project you write code (solvers, scripts, gnuplot, sbatch templates) that evolves over months. Without version control you cannot answer "what changed in commit X", "who introduced this bug", "give me yesterday's working version". Git is the de-facto standard: distributed (every developer has a full copy), fast, and free.

Why it matters in HPC/CFD: clusters require reproducibility — published results must be recoverable from a specific commit hash. CFD groups push to GitLab (often the university's, e.g. git.rz.tu-bs.de for TU Braunschweig). Branching enables parallel feature work; merging integrates it.

What the examiner asks (very common):

  • "Difference between git add, git commit, git push."
  • "What is the staging area?"
  • "How do you create a branch and merge it?"
  • "What's a remote? origin?"
  • "Explain git log, git status, git diff."
  • "Explain GitFlow naming convention."
  • "Resolve a merge conflict."

What you must master for top grade:

  • The three-zone model (working dir / staging / local repo) + remote repo.
  • The 4 file states: untracked / unmodified / modified / staged.
  • Top-15 commands.
  • Branch workflow.
  • HTTPS vs SSH remote authentication.
  • Personal-access tokens for GitLab.

2. Basics from Zero

A Git repository is just a hidden folder .git/ inside your project that records snapshots of your files. You explicitly stage the changes you want to record (git add), then commit them (git commit). Each commit gets a unique 40-char SHA-1 hash that forever identifies that exact tree.

Three big advantages:

  1. History. git log shows every change with author/date/message.
  2. Branching. Create parallel timelines, merge them.
  3. Collaboration. Push your local repo to a remote (GitHub, GitLab, Bitbucket); other people pull from it.

The flow:

WORKING        STAGING        LOCAL          REMOTE
  DIR          AREA           REPO           REPO
   │            │              │              │
   │  add ──▶   │              │              │
   │            │  commit ──▶  │              │
   │            │              │  push ──▶    │
   │            │              │  ◀── pull    │

বাংলায়: Git-এর মূল ছন্দ: ফাইল এডিট করো → git add দিয়ে staging area-তে তোলো → git commit দিয়ে লোকাল history-তে snapshot নাও → git push দিয়ে সার্ভারে পাঠাও। মনে রাখো, commit শুধু তোমার মেশিনে থাকে — push না করা পর্যন্ত টিমের কেউ সেটা দেখতে পায় না। এই চারটা zone আর তিনটা verb-এর ছবিটা পরীক্ষায় আঁকতে পারলে অর্ধেক নম্বর পকেটে।

Real-life analogy. Git = a time-machine + photo album for code. You take photos (commit); each photo has a label and a date. You can flip back, branch into alternate timelines, and share albums with collaborators.

Real-life HPC use. A CFD researcher tracks solver.cpp, case_template/in.dat, plot_residuals.gp. Every paper figure is reproducible because a git tag v1.0 marks the commit used to generate it.

What if you misunderstand? You forget to git add after editing a file → commit records nothing. Or you think commit already shares with team — it doesn't, you must push. Or you delete a branch before merging → work lost.

বাংলায়: তিনটা ক্লাসিক ভুল মুখস্থ রাখো: (১) এডিটের পর add ভুলে গেলে commit-এ নতুন পরিবর্তন ঢোকেই না; (২) commit করেই ভাবা "টিম পেয়ে গেছে" — আসলে push না করা পর্যন্ত সব লোকাল; (৩) merge করার আগে branch delete করলে কাজ হারানোর ঝুঁকি। এই তিনটা trap পরীক্ষার ছোট প্রশ্নে ঘুরেফিরে আসে, আর viva-তেও।


3. Hard English Made Easy

Hard Term Simple English বাংলা Example
Repository (repo) Folder tracked by git গিট-নিয়ন্ত্রিত ফোল্ডার git init
Working directory Files visible on disk কাজের ফোল্ডার ls
Staging area / index Pre-commit zone কমিটের আগের জায়গা git add
Commit Saved snapshot সংরক্ষিত স্ন্যাপশট git commit -m
Hash 40-char SHA-1 ID কমিটের স্বতন্ত্র চিহ্ন d29882c…
Branch Independent timeline আলাদা সময়রেখা git branch x
Merge Combine two branches শাখা একত্রিত করা git merge x
Conflict Both branches changed same lines একই লাইনে দুই পরিবর্তন conflict markers <<<<
Remote Server-hosted copy সার্ভারে কপি origin
Origin Default remote name ডিফল্ট রিমোট git push origin main
HEAD Pointer to current commit বর্তমান অবস্থান git reset HEAD~1
Pull Fetch + merge ফেচ ও মার্জ git pull
Push Send local commits to remote সার্ভারে পাঠানো git push
Clone Copy a remote into a new local রিমোটের লোকাল কপি git clone url
Tag Named commit (e.g. release) কমিটের নামকরণ git tag v1.0
Diff Show line-level changes পরিবর্তন প্রদর্শন git diff
Tracked / Untracked In repo / not ট্র্যাকড / আনট্র্যাকড new file = untracked
Modified / Staged Changed but not staged / staged পরিবর্তিত / স্টেজড git status
GitLab / GitHub Hosting platforms হোস্টিং প্ল্যাটফর্ম git.rz.tu-bs.de
Personal Access Token (PAT) Token in lieu of password পাসওয়ার্ডের পরিবর্তে টোকেন for GitLab HTTPS

4. Deep Theory Explanation

4.1 Three zones + four file states

Untracked → Unmodified → Modified → Staged
              ▲                          │
              │                          ▼
              └────── commit ────────────┘
  • Untracked — file is new, git ignores it until you git add.
  • Unmodified — tracked, not touched since last commit.
  • Modified — tracked, edited but not staged.
  • Staged — added to the index, ready for commit.

বাংলায়: Git-এ একটা ফাইল চারটা অবস্থার যেকোনো একটায় থাকে: untracked (git এখনো চেনে না), unmodified (শেষ commit-এর পর অপরিবর্তিত), modified (বদলেছ কিন্তু stage করোনি), staged (পরের commit-এর জন্য প্রস্তুত)। git add ফাইলকে staged করে, git commit সেটাকে history-তে ঢুকিয়ে আবার unmodified বানায়। কোন ফাইল কোন অবস্থায় আছে সন্দেহ হলেই git status — এটাই তোমার কম্পাস।

4.2 Configuration

git --version
git config --global user.name  "Debwashis"
git config --global user.email "debwa.web@gmail.com"
git config --global core.editor vim         # or nano
git config --global merge.tool vimdiff
git config --global init.defaultBranch main
git config --list                # show all
cat ~/.gitconfig                 # the file

Three scopes: --system, --global, --local (per-repo).

4.3 Repository setup

git init                  # initialize a new repo here
git clone <url>           # copy a remote
git status                # state report
git log --oneline         # commits one per line
git log --graph --all     # ASCII tree of branches

4.4 The basic working cycle

echo "# My Project" > README.md
git status                # shows README.md untracked
git add README.md         # stage
git status                # shows it staged
git commit -m "Initial commit with README.md"
git log                   # see the commit

Useful flags:

Flag Meaning
git add . stage everything in dir
git add -p interactive hunks
git commit -am "msg" add tracked + commit
git commit --amend edit last commit
git rm file unstage + delete
git mv a b rename tracked file

4.5 Inspecting

git status               # what's changed
git diff                 # unstaged diff
git diff --staged        # staged diff
git diff HEAD            # all vs last commit
git log --oneline -n 10  # last 10 lines
git log -p file          # patch-form history of file
git blame file           # who changed each line
git show <hash>          # full commit

4.6 Undoing

Goal Command
Unstage git reset HEAD file
Restore working file from last commit git restore file (or git checkout -- file)
Undo last commit but keep changes git reset --soft HEAD~1
Undo last commit and changes git reset --hard HEAD~1
Revert (new commit reversing) git revert <hash>

বাংলায়: reset history-কে পিছনে টেনে নেয় — branch pointer সরে যায়, পুরনো commit-গুলো আপাতদৃষ্টিতে হারায়; আর revert একটা নতুন commit বানায় যা আগের পরিবর্তন উল্টে দেয়, history অক্ষত থাকে। যে branch অন্যরাও ব্যবহার করে সেখানে reset কখনোই নয় — revert-ই নিরাপদ। আর --hard মানে working directory-ও মুছে যাবে; ভুল করে ফেললে git reflog দিয়ে হারানো commit ফেরত আনা যায় — এটা পরীক্ষার অন্যতম পছন্দের প্রশ্ন।

4.7 Branches

git branch                 # list local
git branch feature_1       # create
git checkout feature_1     # switch
git checkout -b feature_2  # create + switch (modern: git switch -c)
git branch -d feature_1    # delete after merge
git branch -D feature_1    # force delete

Merge:

git checkout main
git merge feature_1        # fast-forward or merge commit

Three-way merge happens when both diverged. Conflict markers:

<<<<<<< HEAD
my version
=======
their version
>>>>>>> feature_1

Resolve, git add, git commit.

বাংলায়: Branch কোনো ফোল্ডারের কপি নয় — এটা একটা commit-এর দিকে তাক করা হালকা pointer, তাই branch বানানো প্রায় ফ্রি আর তাৎক্ষণিক। merge-এর সময় দুই branch একই লাইনে ভিন্ন পরিবর্তন করলে git নিজে সিদ্ধান্ত নিতে পারে না — তখন conflict marker দিয়ে দুটো version-ই ফাইলে দেখায়; তুমি ফাইল ঠিক করে git add ও git commit করলে merge শেষ হয়। পরীক্ষায় conflict resolve-এর ধাপগুলো হুবহু লিখতে বলা হয়।

4.8 GitFlow naming (slide content)

Branch Purpose
main/master Production-stable
develop Integration of features
feature/x New feature
release/x.y Pre-release polish
hotfix/x Urgent production fix
bugfix/x Non-urgent fix on develop

Naming convention varies; the lecture mentions GitFlow vs GitHub Flow.

4.9 Remotes & GitLab

git remote add origin https://git.rz.tu-bs.de/user/proj.git
git remote -v                      # list
git fetch origin                   # download new commits
git pull origin main               # = fetch + merge
git push origin main               # send commits up
git push -u origin feature_1       # set upstream

For TU-BS GitLab the lecture's E08 Task 3 walks through:

  1. Create blank project in GitLab.
  2. Generate a Personal Access Token (api / read_repository / write_repository scopes).
  3. Clone with HTTPS (token instead of password) or with SSH (key registered in GitLab).
  4. Edit, stage, commit, push.

Remote-sync picture — fetch vs pull vs push:

      LAPTOP (local repo)                      SERVER (origin)
 ┌──────────────────────────────┐         ┌─────────────────────────┐
 │ main         ──► C3 (yours)  │         │ main ──► C4             │
 │ origin/main  ──► C2 (stale!) │         │ (teammate pushed C4)    │
 └───────────────┬──────────────┘         └────────────┬────────────┘
                 │                                     │
   git fetch ────┼─────────────────────────────────────┘
                 │  downloads C4, moves ONLY origin/main → C4
                 │  (your main and working dir untouched — always safe)
   git pull   =  fetch + merge origin/main into main
                 │  (or fetch + rebase with --rebase)
   git push ─────┼────────────────────────────────────►
                 │  uploads your commits, advances remote main;
                 ▼  REJECTED if the remote has commits you lack
            work continues

বাংলায়: fetch শুধু সার্ভার থেকে নতুন commit নামিয়ে origin/main pointer-টা এগিয়ে দেয় — তোমার নিজের branch বা working directory-তে হাতও দেয় না, তাই fetch সবসময় নিরাপদ। pull মানে fetch + merge: নামিয়ে সাথে সাথে মিশিয়েও দেয়। push তোমার commit সার্ভারে তোলে, কিন্তু সার্ভারে এমন commit থাকলে যা তোমার কাছে নেই, push প্রত্যাখ্যাত হয় — আগে pull করতে হয়। আর GitLab-এ এখন password চলে না — PAT বা SSH key লাগবেই।

4.10 Diagrams from V08

  • "Working dir → Staging → Local repo → Remote" — the four zones and the verbs that move between them (add, commit, push/pull).
  • "File states": untracked → modified → staged → unmodified.
  • "Branching tree": main with feature_1, feature_2 spurs and a merge commit.

4.11 Git as a DAG — the formal model

Git history is a directed acyclic graph \(G = (V, E)\): every commit is a node \(v \in V\), and every parent pointer is an edge \(v \to \mathrm{parent}(v)\). Edges point backwards in time (a merge commit has two outgoing parent edges). The graph is acyclic by construction: a commit's ID is a hash over its parents' IDs, so a cycle would require a commit to (indirectly) contain its own hash. On top of the DAG sit only pointers:

  • a branch is a movable pointer to one node (it is not a container of commits);
  • HEAD is a pointer to the current branch (or directly to a commit when "detached").

Define the ancestor set:

\[\mathrm{anc}(x) = \{\, y \in V : y \text{ is reachable from } x \text{ along parent edges} \,\} \cup \{x\}\]

The merge-base of two branches is their lowest common ancestor — the deepest node in \(\mathrm{anc}(A) \cap \mathrm{anc}(B)\); git computes it with git merge-base A B.

Fast-forward condition (state this formally in the exam). Merging feature into main is a fast-forward iff the target is an ancestor of the source:

\[\mathrm{main} \in \mathrm{anc}(\mathrm{feature}) \iff \mathrm{mb}(\mathrm{main}, \mathrm{feature}) = \mathrm{main}\]

In that case git merely slides the main pointer to the feature tip — no new commit. If both branches contain commits the other lacks (the merge-base equals neither tip), git performs a 3-way merge of merge-base, tip A and tip B, and creates a merge commit with two parents.

Worked example. Commits \(C_1 \leftarrow C_2\), main at \(C_2\); feature continues \(C_2 \leftarrow C_3 \leftarrow C_4\). Then \(\mathrm{anc}(\mathrm{feature}) = \{C_4, C_3, C_2, C_1\} \ni \mathrm{main}\) → fast-forward, main slides to \(C_4\). But if main had meanwhile gained \(C_5\) on top of \(C_2\), then \(\mathrm{mb} = C_2\), which is neither tip → 3-way merge with parents \(C_5\) and \(C_4\).

Commit DAG with branch pointers and HEAD, before/after the merge (arrows = "child points to parent"):

 BEFORE                                    AFTER  git merge feature

 C1 ◄─ C2 ◄─ C5   ◄── main ◄── HEAD        C1 ◄─ C2 ◄─ C5 ◄─── M  ◄── main ◄── HEAD
        ▲                                         ▲            │
        └─ C3 ◄─ C4  ◄── feature                  └─ C3 ◄─ C4 ◄┘
 (merge-base = C2)                                         └── feature (unchanged)
                                           M = merge commit with TWO parents (C5, C4)

Fast-forward vs 3-way merge, side by side:

 FAST-FORWARD possible                  │  3-WAY MERGE needed
 (main IS an ancestor of feature)       │  (branches diverged)
 C1 ◄─ C2 ◄─ C3 ◄─ C4                   │  C1 ◄─ C2 ◄─ C5          ◄── main
        ▲           ▲                   │         ▲
       main      feature                │         └─ C3 ◄─ C4      ◄── feature
                                        │  merge-base = C2 (neither tip)
 git merge feature:                     │  git merge feature:
 main pointer slides to C4              │  new commit M, parents C5 + C4
 ── NO new commit appears ──            │  ── merge commit DOES appear ──

বাংলায়: পুরো Git history আসলে একটা directed acyclic graph: প্রতিটা commit একটা node, আর প্রতিটা commit তার parent-এর দিকে edge রাখে (merge commit-এর দুটো parent)। Branch মানে স্রেফ একটা চলমান pointer, HEAD মানে "তুমি এখন কোথায় দাঁড়িয়ে"। Fast-forward তখনই হয় যখন target branch source-এর ancestor — তখন pointer-টা এগিয়ে যায়, নতুন commit লাগে না; দুই branch আলাদা পথে এগোলে 3-way merge হয় আর দুই parent-ওয়ালা merge commit তৈরি হয়। পরীক্ষায় "when does a merge commit appear" এলে এই শর্তটাই আনুষ্ঠানিকভাবে লিখবে।

4.12 Content addressing — why hashes ARE the history

Every object (file blob, directory tree, commit) is stored under the SHA-1 hash of its own content:

\[\mathrm{id}(o) = \mathrm{SHA1}(\text{type} \,\Vert\, \text{size} \,\Vert\, \text{content})\]

Consequences worth full marks:

  1. Identical content ⇒ identical ID. A file repeated in 1000 commits is stored exactly once (automatic deduplication).
  2. Any change ⇒ new ID. One flipped bit yields a completely different hash; and because a commit hashes its tree and its parents' IDs, editing anything deep in history changes every later commit ID — history is tamper-evident.
  3. Collision is a non-issue in practice. By the birthday bound, \(n\) objects collide with probability \(\approx n^2 / 2^{161}\); for a million objects that is \(\sim 10^{-37}\).

Worked example (same output on every machine on Earth — that is the point):

$ echo 'hello' | git hash-object --stdin
ce013625030ba8dba906f756967f9e9ca394464a

This is why citing a commit hash in a paper pins the exact code state used for the results.

বাংলায়: Git-এ কোনো serial number নেই — প্রতিটা object-এর নামই তার content-এর SHA-1 hash। একই content মানেই পৃথিবীর যেকোনো মেশিনে একই hash, আর এক bit বদলালেই সম্পূর্ণ নতুন hash। Commit-এর hash-এর ভেতরে parent-এর hash-ও ঢোকে, তাই পুরনো history চুপিচুপি বদলানো অসম্ভব — বদলালেই পরের সব hash পাল্টে যায়। এজন্যই paper-এ একটা commit hash দিলেই ফল reproducible — পরীক্ষায় "why SHA-1" প্রশ্নের তিনটা পয়েন্ট: deduplication, tamper-evidence, reproducibility।

4.13 The three trees — predicting git status

Git permanently compares three trees: HEAD (the last commit's snapshot), the index (staging area), and the working directory. Every git status line is just a pairwise difference between them:

Working dir vs index Index vs HEAD git status shows
same same "nothing to commit, working tree clean"
differs same red — "Changes not staged for commit"
same differs green — "Changes to be committed"
differs differs the file appears in BOTH red and green

The commands are tree-to-tree copy operations:

                       THE THREE TREES OF GIT

  ┌───────────────┐   git add    ┌───────────┐  git commit  ┌──────────┐
  │  WORKING DIR  │ ───────────► │   INDEX   │ ───────────► │   HEAD   │
  │ (files you    │              │ (staging  │              │ (last    │
  │  edit)        │              │  area)    │              │  commit) │
  └───────────────┘              └───────────┘              └──────────┘
         ▲                             ▲                         │
         │ git restore <file>          │ git restore --staged    │
         │ (index ─► working)          │ (HEAD ─► index)         │
         │                             └─────────────────────────┤
         │        git reset --hard   (HEAD ─► index AND working) │
         └───────────────────────────────────────────────────────┘

Worked example. You edit solver.cpp, run git add solver.cpp, then edit it again. Now: working ≠ index (second edit, red) and index ≠ HEAD (first edit, green) — git status lists the file twice. Committing now would record only the first edit; you must git add again to include the second.

বাংলায়: git status বুঝতে তিনটা গাছ মনে রাখো: working directory (যেখানে এডিট করো), index/staging (পরের commit-এর খসড়া), আর HEAD (শেষ commit)। লাল মানে working আর index আলাদা — add করা বাকি; সবুজ মানে index আর HEAD আলাদা — commit-এর অপেক্ষায়। add করার পর আবার এডিট করলে একই ফাইল লাল-সবুজ দুটো তালিকাতেই দেখা যায় — কারণ index-এ আছে পুরোনো সংস্করণ, working-এ নতুনটা। এই দৃশ্যটা ব্যাখ্যা করতে পারা মানেই three-trees মডেল সত্যিই বোঝা।


5. Command / Syntax / Code Breakdown

git init

Create .git/ in the current dir.

git clone <url>

Fresh local copy of a remote repo + sets its origin.

git status

What's changed; red = modified/untracked, green = staged.

git add <file> / git add .

Stage. Add -p for hunk-by-hunk.

git commit -m "msg"

Snapshot staged changes. -am adds tracked files + commits. Write imperative messages: "Add error bars", not "Added".

git log

History. --oneline --graph --decorate --all is the classic compact view.

git diff, git diff --staged

Show unstaged / staged changes.

git checkout / git switch

Change branch (switch for branches, restore for files in modern git).

git branch <name>, git branch -d, git branch -a

Manage branches.

git merge <branch>

Bring <branch> into the current one.

git pull, git push

Sync with remote.

git remote add origin <url>

Attach remote.

git tag v1.0

Mark a commit.

git stash, git stash pop

Park / restore dirty work-in-progress.


6. Mandatory Practical Examples

Example 6.1 — Setup, init, first commit (E08 Task 1)

# 1. install
git --version
sudo apt install git                # if missing

# 2. configure
git config --global user.name "Debwashis"
git config --global user.email "debwa.web@gmail.com"
git config --list

# 3. project
mkdir my_project && cd my_project
git init
echo "# My Project" > README.md
git add README.md
git commit -m "Initial commit with README.md"
git log

Expected output:

$ git log
commit d29882cf6d972626a8ac337e04a65969c7a6551b (HEAD -> main)
Author: Debwashis <debwa.web@gmail.com>
Date:   Fri May 8 12:00:00 2026 +0200

    Initial commit with README.md

Example 6.2 — Branching & merging (E08 Task 2)

# 1. branch
git checkout -b feature

# 2. edit + stage + commit
echo "## Feature" >> README.md
git add README.md
git commit -m "Added new feature description to README.md"

# 3. merge into master
git checkout master         # (or main)
git merge feature

# 4. delete the merged branch
git branch -d feature
git log --oneline --graph --all

Expected output:

*   3a4b5c6 (HEAD -> master) Merge branch 'feature'
|\
| * 1f2a3b4 (feature) Added new feature description to README.md
|/
* d29882c Initial commit with README.md

বাংলায়: এই ছোট্ট graph-টা পড়তে শেখো: তারকাচিহ্ন = commit, রেখা = parent-সম্পর্ক, |\ মানে দুই parent-ওয়ালা merge commit। পরীক্ষায় "এই log থেকে ইতিহাস ব্যাখ্যা করো" ধরনের প্রশ্নে প্রতিটা প্রতীক ধরে ধরে বলো।

Example 6.3 — Collaboration with TU-BS GitLab (E08 Task 3)

# 1. (UI) Create blank project on git.rz.tu-bs.de
# 2. (UI) Settings → Access Tokens → create PAT with api/read/write
TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"

# 3. clone (HTTPS with token)
git clone https://oauth2:${TOKEN}@git.rz.tu-bs.de/me/my-git-exercise.git
cd my-git-exercise

# 4. local edits
mkdir test_dir && touch test_dir/new_file.txt
echo "This file is for my git practice" >> README.md

# 5. stage, commit, push
git add test_dir README.md
git commit -m "my first commit to repository"
git push origin main

Or with SSH:

# Create key (Ch.4)
ssh-keygen -t ed25519 -f ~/.ssh/gitlab_key
cat ~/.ssh/gitlab_key.pub      # paste into GitLab > SSH keys
git clone git@git.rz.tu-bs.de:me/my-git-exercise.git

Example 6.4 — Conflict resolution

# Two contributors edited line 3 of README.md
git pull origin main           # ← conflict
# README.md now contains markers:
# <<<<<<< HEAD
# my new line
# =======
# teammate's new line
# >>>>>>> origin/main
# Edit to keep desired text, then:
git add README.md
git commit -m "Resolve conflict in README.md"
git push

বাংলায়: Conflict মানে git হাল ছেড়ে দিয়ে দুটো সংস্করণই মার্কারসহ ফাইলে রেখে দিয়েছে — সিদ্ধান্ত তোমার। নিয়ম: মার্কারের তিনটা লাইন (<<<<<<<, =======, >>>>>>>) মুছে কাঙ্ক্ষিত লেখাটুকু রাখো, তারপর add + commit। মার্কার রেখে commit করা = পরীক্ষায় শূন্য, বাস্তবে ভাঙা কোড।

Example 6.5 — Stash and undo

git stash             # park dirty edits
git pull              # update
git stash pop         # bring back
git revert <hash>     # add a commit that undoes <hash>
git reset --soft HEAD~1   # uncommit but keep staged
git reset --hard HEAD~1   # uncommit and discard

7. Real HPC/CFD Workflow

# 1. fresh feature
git switch -c feature/k-omega-sst

# 2. work iteratively
vim solver.cpp
git status; git diff
git add solver.cpp
git commit -m "Add k-omega SST turbulence model"

# 3. push
git push -u origin feature/k-omega-sst

# 4. open Merge Request on GitLab UI

# 5. after review, locally:
git switch main
git pull
git branch -d feature/k-omega-sst

For reproducibility of papers:

git tag -a v1.0-paper -m "Used in JCP paper"
git push --tags

Submitting a job on the cluster:

git clone git@git.rz.tu-bs.de:group/cfd-suite.git
cd cfd-suite
git checkout v1.0-paper
make
sbatch run.sbatch

8. Exercises and Solutions

E08 fully solved above (6.1–6.3).

Marking schemes

Task 1 (10 marks): 1 install/version; 2 config name + email; 1 verify config; 1 mkdir & cd; 1 git init; 1 README created; 1 git add; 1 git commit -m; 1 git log.

Task 2 (8 marks): 1 git checkout -b feature; 1 edit README; 1 add; 1 commit; 1 switch back; 1 merge; 1 branch -d; 1 log showing merge.

Task 3 (10 marks): 1 GitLab login; 1 create project + visibility; 1 README initialise; 2 PAT with proper scopes; 1 clone HTTPS or SSH; 1 local changes; 1 stage; 1 commit; 1 push & verify.

Common mistakes

  • Forgetting git add after editing → "nothing to commit" or stale content.
  • Committing on main directly when policy is feature-branch.
  • Merging without pulling first → push rejected.
  • git push without -u first time → "no upstream" warning.
  • PAT pasted without the oauth2: user prefix → 401.
  • Using HTTPS with the account password (GitLab requires PAT/SSH).

Harder versions

  • Use rebase instead of merge: git switch feature; git rebase main.
  • Signed commits: git commit -S -m "..." with a GPG key.
  • .gitignore for build outputs:
*.o
*.exe
build/
results/

9. Written Exam Focus

9.1 Short Answers

Q. Difference between commit and push. A. commit records a snapshot in your LOCAL repo only; push uploads local commits to the remote (origin). Many commits can precede a single push.

Q. What is the staging area? A. An intermediate buffer (the index) where you assemble the next commit. git add puts changes in; git commit writes them to history.

Q. Define HEAD. A. A symbolic pointer to the current branch's tip commit — the parent of your next commit.

Q. Difference between merge and rebase. A. merge joins branches preserving history (merge commit, two parents); rebase replays commits on a new base producing linear history. merge is safer; rebase is cleaner.

Q. What is origin? A. Conventional name of the default remote — usually where you cloned from.

9.2 Medium Answers

Q. (8 marks) Walk through a daily Git workflow — edit to push.

A. Edit (vim solver.cpp) → git statusgit add solver.cppgit diff --staged to verify → git commit -m "Add turbulence model"git pull --rebase origin main to integrate teammates without merge bubbles → git push origin main. For long features: branch first (git switch -c feature/x), push with -u, open a Merge Request.

Q. (5 marks) Compare git rm and plain rm + commit.

A. Plain rm deletes only from the working dir; the deletion still needs staging (git add -A). git rm file removes the file AND stages the deletion in one step.

9.3 Long Answer (12 marks)

Q. Discuss the architecture of Git, its zones, and its branching/merging model with a CFD example.

A.

Introduction. Git is a distributed VCS — every developer holds the entire history. State flows through four zones: working directory → staging area → local repository → remote repository.

Main concept. Each commit is a SHA-1-keyed snapshot referencing its parent(s) — a directed acyclic graph. Branches are mutable pointers into the DAG; merging creates a commit with two parents.

Step-by-step CFD example.

  1. git init creates .git/.
  2. Two researchers branch: feature/turbulence, feature/io.
  3. Each commits independently; both push to origin.
  4. The lead merges turbulence into main, then io — auto-merge if no overlap, conflict markers otherwise.
  5. CI builds, tags v1.0-paper.
  6. On the cluster: clone, git checkout v1.0-paper, build, sbatch.

Real HPC/CFD link. Reproducibility — every figure in the paper traces to a tagged commit; cluster jobs cite the hash.

Conclusion. Git's zone model + DAG gives individual safety and team collaboration with full history.

9.4 Output Prediction

$ git log --oneline
3a4b5c6 (HEAD -> main) Merge branch 'feature'
1f2a3b4 (feature)      Added feature description
d29882c                Initial commit with README.md
$ git status            # after editing README.md but no add
modified:   README.md
no changes added to commit

9.5 Comparison

fetch pull
Action download download + merge
Working dir safe may auto-merge
Use when inspect remote integrate
reset revert
History rewrites preserves
Public branch dangerous safe
New commit? no yes

9.6 Templates

Workflow: status → diff → add → commit → pull --rebase → push.

Feature-branch: switch -c → work → push -u → Merge Request → main pull → branch -d.

Conflict: pull → see markers → edit → add → commit → push.

9.7 Marking Scheme — "Setup + first commit" (8 marks)

  • 1 install/verify.
  • 2 config name+email.
  • 1 init.
  • 1 file create.
  • 1 add.
  • 1 commit -m.
  • 1 log to verify.

10. Very Hard Questions

Beginner

  1. Initialize a repo. → git init.
  2. Stage a file. → git add file.
  3. Commit. → git commit -m "msg".
  4. View history. → git log.
  5. Clone. → git clone url.

Intermediate

  1. Create branch & switch. → git switch -c x.
  2. Merge x into main. → git switch main; git merge x.
  3. Delete branch. → git branch -d x.
  4. Add remote. → git remote add origin url.
  5. First push. → git push -u origin main.

Hard

  1. Undo last commit, keep staged. → git reset --soft HEAD~1.
  2. Squash three commits. → git rebase -i HEAD~3, mark lower two s.
  3. Restore a deleted file from last commit. → git restore -- file.
  4. Apply one commit from another branch. → git cherry-pick <hash>.
  5. Commits touching solver.cpp. → git log -- solver.cpp.

Very Hard

  1. Fast-forward vs 3-way merge? → FF: target is an ancestor — pointer just moves, no merge commit. 3-way: histories diverged — new commit with two parents.
  2. Recover a deleted branch. → git reflog to find the tip → git branch x <hash>.
  3. Sketch git bisect. → git bisect start; git bisect bad; git bisect good <h>; … (≈log₂N steps, Ch 13 link).

Deep Integration

  1. Git + cluster reproducibility → tag, clone, checkout tag, build, sbatch (§7).
  2. Why is rebasing a shared branch dangerous? → rewrites public history; teammates' clones now diverge.

Coding/Command

  1. Stage all .cpp and commit. → git add '*.cpp'; git commit -m "...".
  2. Show changes to README in last 5 commits. → git log -p -5 README.md.

Debugging

  1. "Updates were rejected because the remote contains work…" → git pull --rebase then push.
  2. PAT 401 on push. → token expired or missing scope; regenerate with write_repository.

Long Written

  1. (250 words) Compare GitFlow and GitHub Flow for HPC team workflows.

11. Debugging and Mistake Analysis

Mistake Why wrong Correct Explanation
Editing on main directly bypasses review git switch -c feature/x branch first
Committing without add wrong/empty content git add first staging gate
push after rewriting history rejected --force-with-lease (private branches only) safe-force
commit -am with untracked files they're skipped git add them -a covers tracked only
HTTPS with account password rejected PAT or SSH auth policy
No .gitignore binaries committed add it + git rm --cached repo hygiene
pull on dirty tree conflicts in WIP stash; pull; stash pop park first
reset --hard casually lost work commit/stash first safety
Merging untested broken main MR with CI review

বাংলায়: Git-দুর্ঘটনার ৯০% উদ্ধারযোগ্য — মন্ত্রটা git reflog: HEAD যেখানে যেখানে ছিল সব লেখা থাকে, হারানো commit-ও সেখান থেকে ফেরে। একমাত্র সত্যিকারের ক্ষতি: কখনো commit-ই করা হয়নি এমন কাজের উপর reset --hard। শিক্ষা: ঘনঘন commit করো।


12. Mini Project for Mastery

Goal: Mirror E08 end-to-end on a CFD-flavoured repo.

git init cfd_demo && cd cfd_demo
echo "# CFD Demo" > README.md
printf '*.png\nbuild/\n*.o\n' > .gitignore
git add . && git commit -m "Initial scaffold"

git switch -c feature/solver
cat > solver.cpp <<'EOF'
#include <iostream>
int main(){ std::cout<<"solver\n"; return 0; }
EOF
git add solver.cpp && git commit -m "Add solver skeleton"

git switch main
git merge feature/solver
git branch -d feature/solver

git remote add origin git@git.rz.tu-bs.de:debwa/cfd_demo.git
git push -u origin main

git tag -a v0.1 -m "First clean version"
git push --tags

Connection to exam: every E08 task exercised; the tag-for-reproducibility is the bonus mark.


13. Final Chapter Cheat Sheet

Item Memorise
Setup git config --global user.name/.email
New repo git init
Clone git clone url
Stage git add file
Commit git commit -m "msg"
Status git status
Log git log --oneline --graph --all
Diff git diff / --staged
Branch git branch [-d]
Switch git switch [-c]
Merge git merge x
FF condition fast-forward iff target is ancestor of source
Rebase git rebase main
Pull git pull
Push git push -u origin main
Remote git remote add origin url
Tag git tag v1.0
Stash git stash / pop
Undo (soft) git reset --soft HEAD~1
Undo (hard) git reset --hard HEAD~1
Rescue git reflog
Trap committing without staging
Top phrase "Git tracks history as a DAG of commits; the staging area is the gate to that history."

14. Mock Exam — Four Levels

Level 1 — Basic (definitions & syntax)

Q1. Give the two config commands required before your first commit.

Solution: git config --global user.name "…" and git config --global user.email "…".

Q2. Which command shows unstaged changes, and which shows staged ones?

Solution: git diff (unstaged) and git diff --staged (staged).

Q3. Create branch fix/bc and switch to it — one command.

Solution: git switch -c fix/bc (or git checkout -b fix/bc).

Q4. What does git clone <url> set up besides copying files?

Solution: The full history (.git), the origin remote, and a checked-out default branch tracking it.

Q5. Command to list all branches including remote ones.

Solution: git branch -a.

Level 2 — Intuitive (predict / explain why)

Q1. You edit a.txt, run git add a.txt, then edit it AGAIN. What does git status show, and what does a commit now contain?

Solution: a.txt appears BOTH as "staged" and "modified" — the index holds the first edit; the commit would contain only that first edit (the second stays unstaged until another add).

Q2. Why does git log after git fetch origin not show the teammate's new commits on your main?

Solution: fetch only updates origin/main; your local main pointer is untouched. Look with git log origin/main or integrate via merge/pull.

Q3. Predict: branch feature was created from main and main has had NO commits since. What does git merge feature on main do?

Solution: A fast-forward — main's pointer simply moves to feature's tip; no merge commit (unless --no-ff).

Q4. Two commits have identical file contents but different messages. Same hash or different? Why?

Solution: Different — the commit hash covers the tree AND metadata: message, author, timestamp, parent. Identical content ⇒ identical tree hash only.

Q5. After git reset --hard HEAD~3, are the three commits gone forever?

Solution: Not immediately — they're unreachable but alive in the reflog (~90 days). git refloggit branch rescue <hash> recovers them.

Level 3 — Hard (exam level)

Q1. (8 marks) Resolve this conflict so BOTH changes survive (yours first), and give the full command sequence. File during merge:

<<<<<<< HEAD
nu = 1.5e-5
=======
nu = 1.6e-5   # updated viscosity
>>>>>>> feature/visc

Solution: Edit the file to:

nu = 1.5e-5
# alternative from feature/visc: nu = 1.6e-5 (updated viscosity)
(or whatever combined content is wanted — markers REMOVED), then:
git add in.dat
git commit            # default merge message
git push
বাংলা ইঙ্গিত: marker-মুক্ত ফাইল + add + commit — তিন ধাপ; "দুটোই রাখো" বললে কোনটা কার্যকর লাইন আর কোনটা মন্তব্য, সেটা স্পষ্ট করো।

Q2. (8 marks) You committed secrets.key two commits ago on a private branch. Remove it from history (last 3 commits) and prevent recurrence.

Solution:

git rebase -i HEAD~3        # mark the guilty commit 'edit'
git rm --cached secrets.key
echo "secrets.key" >> .gitignore
git add .gitignore
git commit --amend
git rebase --continue
Private branch ⇒ history rewrite is acceptable; on a shared branch you'd need to rotate the secret and use filter-repo + force-push coordination. বাংলা ইঙ্গিত: দুই অর্ধ: ইতিহাস থেকে মুছতে rebase/amend, ভবিষ্যৎ আটকাতে .gitignore — শুধু git rm করলে ইতিহাসে রয়েই যায়।

Q3. (10 marks) Yesterday main worked; today it crashes. There are 32 new commits. Give the exact bisect session (with a test command make test) and the expected number of steps.

Solution:

git bisect start
git bisect bad HEAD
git bisect good HEAD~32
git bisect run make test     # automated: builds+tests each midpoint
git bisect reset
\(\lceil \log_2 32 \rceil = 5\) steps. bisect run automates marking using the command's exit code. বাংলা ইঙ্গিত: git bisect run জানা থাকলে হাতে good/bad মার্ক করাও লাগে না — exit code-ই বিচারক; 32 commit → মাত্র 5 টেস্ট।

Q4. (8 marks) Draw (ASCII) the DAG after: commit A; branch f from A; commit B on main; commit C on f; merge f into main as M. Mark HEAD, main, f.

Solution:

A ──── B ──────── M   ◄── main, HEAD
 \               /
  └──── C ──────┘     ◄── f
M has two parents (B and C) — that's the 3-way merge; f still points at C until deleted. বাংলা ইঙ্গিত: DAG আঁকার প্রশ্নে দুই parent-এর তীরই আসল — M থেকে B আর C দুদিকে রেখা না টানলে নম্বর নেই।

Q5. (10 marks) Explain why git pull --rebase is recommended over plain git pull for everyday work on main, including the history difference (draw both).

Solution: Plain pull = fetch + merge → a merge commit every time you're behind:

…─A─B─────M    (your C merged with remote B via M)
      \  /
       C
pull --rebase = fetch + replay your local commits on top → linear:
…─A─B─C'       (your C rebased as C')
Same content, no noise commits; history reads chronologically. Caveat: your local (unpushed) commits get new hashes — fine, they were private. বাংলা ইঙ্গিত: "merge bubble" শব্দটা ব্যবহার করো — পরীক্ষক চেনে; আর rebase-এর শর্ত: শুধু এখনো push-না-করা commit-এ।

Level 4 — Beyond the lecture (transfer + coding)

Q1. CI thinking: write the 6-line .gitlab-ci.yml that builds your CMake project (Ch 12) and runs ctest on every push.

Solution:

build_test:
  image: gcc:12
  script:
    - cmake -B build -S .
    - cmake --build build -j
    - ctest --test-dir build --output-on-failure
Every push triggers the pipeline; a red pipeline blocks the Merge Request — that's how main stays green. বাংলা ইঙ্গিত: GitLab CI প্রশ্নে কাঠামো মনে রাখো: job-name → image → script-তালিকা; বাকিটা শুধু Ch 12-এর build কমান্ড।

Q2. Combine with bash (Ch 8): one-liner printing the 5 files changed most often across the whole history.

Solution:

git log --name-only --pretty=format: | grep -v '^$' | sort | uniq -c | sort -nr | head -5
--name-only lists files per commit; empty pretty format suppresses headers; the classic count-pipeline does the rest (Ch 7 link). বাংলা ইঙ্গিত: "কোন ফাইল সবচেয়ে বেশি বদলায়" = hotspot-বিশ্লেষণ; git-output + sort|uniq -c|sort -nr — দুই অধ্যায়ের বিয়ে।

Q3. A teammate force-pushed and your local main now says "have diverged". Recover WITHOUT losing your two local commits.

Solution:

git branch backup            # safety pointer to current state
git fetch origin
git reset --hard origin/main # adopt their rewritten history
git cherry-pick backup~1 backup   # replay your two commits
git branch -D backup
cherry-pick re-applies your work on the new base; the temporary branch guarantees nothing is lost even if a pick conflicts. বাংলা ইঙ্গিত: উদ্ধার-অভিযানের প্রথম কমান্ড সবসময় একটা backup branch — খরচ শূন্য, বিমা সম্পূর্ণ।

Q4. Reproducibility audit (CFD context): a reviewer asks "which exact code produced Fig. 3?". Describe the tag+hash discipline that answers this in one command, and the command.

Solution: Discipline: every paper figure's data dir contains a PROVENANCE.txt written by the run script: git describe --tags --dirty + git rev-parse HEAD at runtime; runs refuse to start if the tree is dirty. Answer command: git checkout $(cat results/fig3/PROVENANCE.txt | head -1) — the working tree is now byte-identical to what produced the figure. বাংলা ইঙ্গিত: --dirty flag-টাই গুরুত্বপূর্ণ — uncommitted বদল নিয়ে চালানো run চিরতরে অপুনরুৎপাদ্য; সেটা আটকানোই নিয়মের আত্মা।


End of Chapter 10.