Skip to content

Git Practice

  • main branch: C1 (line 1) -> C2 (line 2) -> C3 (bug line). HEAD is C3.
  • feat branch: Diverges at C2. Contains F1 -> F2 -> F3 -> F4 -> F5. HEAD is F5.
#!/bin/bash
# 1. Initialize clean environment
REPO_DIR="git-lab"
rm -rf $REPO_DIR
mkdir $REPO_DIR
cd $REPO_DIR
git init
# 2. Build Main Branch (C1, C2)
git checkout -b main
echo "line 1" > a.txt
git add a.txt
git commit -m "C1: Initial commit with line 1"
echo "line 2" >> a.txt
git add a.txt
git commit -m "C2: Appended line 2"
# 3. Diverge Feature Branch at C2
git branch feat
# 4. Complete Main Branch (C3 with Bug)
echo "bug line" >> a.txt
git add a.txt
git commit -m "C3: Appended bug line"
# 5. Build Feature Branch (F1 - F5)
git checkout feat
for i in {1..5}; do
echo "feature line $i" >> a.txt
git add a.txt
git commit -m "F$i: Appended feature line $i"
done
# 6. Display the final Directed Acyclic Graph (DAG)
echo -e "\n--- Final Repository State ---"
git log --all --decorate --oneline --graph

Target: Remove C3 from main. Logic: reset moves the HEAD pointer backward. The variants determine what happens to the filesystem and staging area.

  • Soft: Moves HEAD to C2. The staging area (index) and working directory keep the C3 changes (“bug line”).
    • Command: git reset --soft HEAD~1
    • Use: Squashing commits or fixing a typo in the previous commit message.
  • Mixed (Default): Moves HEAD to C2. Clears the staging area. The working directory keeps the C3 changes.
    • Command: git reset --mixed HEAD~1
    • Use: Uncommitting files to split them into smaller, granular commits.
  • Hard: Moves HEAD to C2. Forcibly overwrites the staging area and working directory to match C2. “Bug line” is permanently deleted.
    • Command: git reset --hard HEAD~1
    • Use: Complete abandonment of recent work.

Target: Remove C3 from main without rewriting history. Logic: Calculates the mathematical inverse diff of C3 (deleting “bug line”) and applies it as a brand new commit (C4).

  • Command: git revert C3
  • Result Graph: C1 -> C2 -> C3 -> C4 (Undo C3).
  • Use: Reversing changes on a public/shared branch where reset would corrupt collaborator histories.

Target: Update feat to include C3 while keeping a linear history. Logic: Detaches F1 through F5, moves the base of feat from C2 to C3, and sequentially replays the F commits on top.

  • Command: git checkout feat; git rebase main
  • Result Graph: C1 -> C2 -> C3 -> F1' -> F2' -> F3' -> F4' -> F5'. (The primes indicate new commit hashes).
  • Use: Maintaining a clean, single-line project history without cluttering it with merge commits.

Target: Integrate feat into main. Logic: Combines the diverging histories. Since feat and main diverged, Git creates a new “Merge Commit” (M1) on main that possesses two parent pointers (C3 and F5).

  • Command: git checkout main; git merge feat
  • Result Graph: main now points to M1.
  • Use: Standard integration of feature branches where preserving the exact chronological branching history is desired.

Target: Bring only F3 into main, ignoring F1, F2, F4, F5. Logic: Copies the exact filesystem diff introduced by F3 and applies it as a new commit on main.

  • Command: git checkout main; git cherry-pick <hash-of-F3>
  • Use: Porting a specific bug fix or isolated feature from an experimental branch to production.

Target: Update your local main with changes from the remote server. Logic: pull is syntactic sugar for fetch + merge. It downloads missing objects from the remote and instantly executes a merge into your active local branch.

  • Command: git pull origin main
  • Use: Daily synchronization. (Note: git pull --rebase is often preferred to prevent unnecessary merge commits).

Target: You are fixing C3 on main (changes uncommitted), but urgently need to view feat. Logic: Shelves uncommitted modifications (tracked files) onto a local stack, returning the working directory to a clean C3 state.

  • Command:
    1. git stash (Working directory is clean).
    2. git checkout feat (Do work, view files).
    3. git checkout main
    4. git stash pop (Reapplies the shelved changes and drops them from the stack).

Target: Mark C2 permanently as version 1.0. Logic: Branches move when new commits are made; tags are static pointers to a specific SHA-1 hash. Annotated tags (-a) contain author data and dates.

  • Command:
    1. git tag -a v1.0 <hash-of-C2> -m "Stable version"
    2. git push origin v1.0 (Tags must be pushed explicitly; git push ignores them).

Merge vs Squash Merge