```markmap # Git Branching ## Why branching matters (and why Git is different) - Branching (general VCS concept) - Meaning: diverge from the main line of development - Goal: keep working without disturbing the main line - Traditional VCS branching (typical tradeoff) - Often “expensive” - May require creating a full new copy of the source directory - Large projects → branching can take a long time - Git branching “killer feature” - Incredibly lightweight model - Branch operations are (nearly) instantaneous - Switching branches is typically just as fast - Encourages frequent branching + merging (even multiple times per day) - Mastering branching can significantly change how you develop ## Branches in a Nutshell (how Git’s model works) - Why you must understand Git’s storage model - Branching is “just pointers” in Git, but that only makes sense once you know what commits are - Reference: earlier concept “What is Git?” (snapshots, SHA-1, objects) ### Git stores snapshots, not diffs/changesets - Git’s core model - Instead of storing a sequence of diffs, Git stores a sequence of snapshots - Each commit represents the state of the project at that point ### Commit objects: what a commit contains - Commit object includes - Pointer to the snapshot you committed (via a tree object) - Metadata - author name + email - commit message - Parent commit pointer(s) - 0 parents → initial commit - 1 parent → normal commit - 2+ parents → merge commit (merging 2+ branches) ### Example: first commit with 3 files (blobs + tree + commit) - Scenario - Working directory contains 3 files - You stage all and commit - Staging step (`git add …`) - Example: - `git add README test.rb LICENSE` - Git computes a checksum (SHA-1) for each file version - Git stores each file version as a **blob** object - The staging area (index) records the blob checksums for what’s staged - Commit step (`git commit …`) - Example: - `git commit -m "Initial commit"` - Git checksums each directory (here: project root) and stores a **tree** object - Tree object - Lists directory contents - Maps filenames → blob IDs - (And subdirectories → subtree IDs) - Git creates a **commit** object - Contains metadata + pointer to the root tree - Object count after this commit (in this example) - 3 blobs (file contents) - 1 tree (directory listing + blob references) - 1 commit (metadata + pointer to root tree) ### Commit history: parents create a graph - Each new commit typically - Points to a new snapshot (tree) - Points to its direct parent commit (previous tip) - Merge commits - Have multiple parents - Represent snapshots produced by merges ### What a branch is (Git definition) - Branch = lightweight movable pointer to a commit (the tip) - Default starting branch name - `master` (historical default name) - Moves forward automatically as you commit on it #### Note: “master” is not special - It’s identical to any other branch - It’s common because - `git init` creates it by default - many repos never rename it ### Creating a new branch (pointer creation only) - Command - `git branch testing` - Effect - Creates a new pointer named `testing` - Points to the same commit you’re currently on - Does **not** switch your working branch ### HEAD: how Git tracks “current branch” - HEAD in Git - Special pointer to the local branch you currently have checked out - Difference from other VCSs (conceptual) - In Git, HEAD is a pointer to the current local branch (not just “latest revision” in a repo) ### Seeing branch pointers in `git log` - Useful visualization option - `git log --oneline --decorate` - What `--decorate` shows - Labels like `HEAD -> master` - Other branch pointers (e.g., `testing`) attached to commits ### Switching branches (checkout) - Switch to an existing branch - `git checkout testing` - Effect - Moves HEAD to point to `testing` - Your next commit will advance `testing` (because HEAD points to it) ### Committing advances only the checked-out branch pointer - If you commit while on `testing` - `testing` pointer moves forward - `master` pointer stays behind (unchanged) ### Switching back updates pointers and your working directory - Switch back - `git checkout master` - Checkout does two major things - Moves HEAD to `master` - Resets working directory to match the snapshot at `master`’s tip - Result - Your future work on `master` diverges from the commit you left behind on `testing` #### Note: `git log` doesn’t show all branches by default - Default behavior - `git log` shows the history reachable from the currently checked-out branch - To see another branch’s history explicitly - `git log testing` - To see all branches - `git log --all` (often paired with `--graph` and `--decorate`) #### Note: switching branches changes working directory files - On branch switch, Git may - add files - remove files - modify files - Safety rule - If Git can’t switch cleanly (because it would overwrite conflicting uncommitted changes), it will block the checkout ### Divergent history and visualization - When both branches get new commits after diverging - History becomes a DAG with multiple “tips” - View divergence clearly - `git log --oneline --decorate --graph --all` ### Why Git branches are cheap - A branch is stored as a simple reference (a file) containing - the 40-character SHA-1 of the commit it points to - plus a newline → ~41 bytes written - Consequences - Create/delete branches instantly - Switching is fast because it’s mostly pointer movement + updating working directory snapshot - Contrast: older VCS branching - Often implemented by copying the entire project directory - Can take seconds/minutes depending on repo size - Merge support benefit - Git records parent pointers in commits - Merge-base detection for merges is typically automatic and easy ### Creating a branch and switching immediately - Common pattern - `git checkout -b ` - Git ≥ 2.23 alternative: `git switch` - switch to existing: `git switch ` - create + switch: `git switch -c ` (or `--create`) - return to previous branch: `git switch -` ## Basic Branching and Merging (realistic workflow) - Example workflow goal: develop features while handling urgent production fixes - High-level steps (website scenario) - Work on site - Create branch for a user story - Work on that branch - Urgent hotfix appears - switch to production branch - create hotfix branch - test hotfix - merge hotfix and deploy - return to user story branch ### Basic Branching example (issue branch + hotfix branch) - Starting assumption - You already have a few commits on `master` #### Create and work on a topic branch (issue #53) - Create + switch - `git checkout -b iss53` - Shorthand for - `git branch iss53` - `git checkout iss53` - Do work and commit - edit `index.html` - `git commit -a -m "Create new footer [issue 53]"` - Result - `iss53` advances (HEAD points to it) #### Interrupt with urgent hotfix (without mixing in feature work) - Key rule before switching branches - If working directory or staging area has uncommitted changes that would conflict, Git blocks switching - Best practice: keep a clean working state when switching - Mentioned workarounds (covered later): stashing, commit amending - Switch back to production/stable branch - `git checkout master` - What you gain - working directory restored to `master` snapshot (pre-issue work) - you can focus on hotfix cleanly #### Create and finish the hotfix - Create + switch to hotfix branch - `git checkout -b hotfix` - Fix and commit - edit `index.html` - `git commit -a -m "Fix broken email address"` #### Merge hotfix into master (fast-forward) - Merge steps - `git checkout master` - `git merge hotfix` - Why it’s a “fast-forward” merge - hotfix tip commit is directly ahead of master tip commit - No divergence to reconcile - Git simply moves the `master` pointer forward - Deployment outcome - master now points to a commit whose snapshot includes the hotfix - you can deploy production fix #### Delete completed hotfix branch - Delete (safe when merged) - `git branch -d hotfix` - Rationale - master already contains the hotfix work #### Return to feature branch (iss53) and continue - Switch back - `git checkout iss53` - Continue work and commit - `git commit -a -m "Finish the new footer [issue 53]"` - Important note: hotfix isn’t in `iss53` automatically - Options if needed - merge master into iss53: `git merge master` - or wait until iss53 is merged back into master ### Basic Merging (merge feature branch into master) - When issue #53 is done - `git checkout master` - `git merge iss53` - Why this merge differs from the hotfix merge - histories diverged earlier - master tip is not an ancestor of iss53 tip - Git performs a three-way merge - Inputs - snapshot at master tip - snapshot at iss53 tip - snapshot at their common ancestor - Output - a new merged snapshot - a new merge commit - “special” because it has more than one parent - Merge strategy note (as shown in output) - merge made by the `recursive` strategy (typical default for two heads) #### Clean up merged branch - Delete iss53 after merge - `git branch -d iss53` ### Basic Merge Conflicts (when Git cannot auto-merge) - When conflicts occur - both branches changed the same part of the same file differently - What `git merge` does on conflict - stops and reports conflict(s) - does NOT create the merge commit yet - requires manual resolution #### Identify unmerged paths - Use - `git status` - Status shows - you are in a merging state - list of “unmerged paths” - hints to: - fix conflicts - `git add` files to mark resolution - then `git commit` to conclude merge #### Conflict markers inserted into files - Git writes markers like - `<<<<<<<` (start of conflict block) - `=======` (separator) - `>>>>>>>` (end of block) - Meaning - Top section = HEAD version (current branch at merge time, e.g., master) - Bottom section = incoming branch version (e.g., iss53) #### Resolve and mark as resolved - Manual resolution workflow - edit file(s) - choose one side or combine them - remove all markers - Mark resolution - `git add ` for each conflicted file - staging indicates conflict resolved in Git #### Using a merge tool (optional) - Run - `git mergetool` - Behavior - opens a visual merge tool - helps walk through conflict resolution - If not configured - Git warns `merge.tool` isn’t configured - offers possible tool choices (platform dependent) - you can specify an alternative tool name #### Finalize the merge - Verify state - `git status` - typically indicates “all conflicts fixed” but merge still in progress - Conclude - `git commit` - Merge commit message details - default message mentions merged branch - often lists conflicts - note in message references merge metadata (e.g., `.git/MERGE_HEAD`) - you may edit message to explain how/why conflicts were resolved - Reference for deeper conflict handling - “Advanced Merging” (mentioned as later coverage) ## Branch Management (everyday utilities) - `git branch` does more than create/delete - provides multiple views and filters of branch state ### Listing branches - `git branch` - lists local branches - `*` shows current branch (HEAD points here) ### See last commit on each branch - `git branch -v` - shows branch tip commit SHA + message summary ### Filter by merge status - `git branch --merged` - branches already merged into current branch - usually safe to delete those (except the current `*` branch) - `git branch --no-merged` - branches not merged into current branch - deletion safety - `git branch -d ` fails if not fully merged - `git branch -D ` forces deletion (discarding unmerged work) #### Note: merge-status filters are relative to a base - Default base - current branch (if no argument given) - You can compare relative to a different branch without checking it out - `git branch --no-merged master` ## Changing a branch name (rename) - Safety warning - do not rename branches still used by other collaborators - do not rename default branches (master/main/etc.) without reading next section ### Rename locally - `git branch --move bad-branch-name corrected-branch-name` - Effect - preserves history - changes only your local ref name initially ### Publish the renamed branch and set upstream - `git push --set-upstream origin corrected-branch-name` - Effect - creates the new remote branch name - configures tracking ### Remove the old remote branch name - `git push origin --delete bad-branch-name` - Effect - fully replaces the bad remote name with the corrected one ### Verification - `git branch --all` - shows local branches and `remotes/origin/...` remote-tracking refs ## Changing the master branch name (e.g., `master` → `main`) - High-impact warning - renaming default branch can break - integrations/services - helper utilities - build/release scripts - any references in code, configs, docs - consult collaborators - search/update all references to the old name ### Local rename - `git branch --move master main` - Result - local `master` ref no longer exists - local `main` points to the same commit tip ### Push and set upstream - `git push --set-upstream origin main` - Result - remote now has `main` - remote may still have `master` - remote HEAD may still point to `origin/master` until host settings change ### Migration checklist (must update external references) - Dependent projects - update code/config referencing old branch - Test runner configs - update any branch-name assumptions - Build/release scripts - update target branch names - Repo host settings - default branch - merge rules / protections - other branch-name-based settings - Documentation - update old references - Pull requests - close/merge/retarget PRs aimed at old branch ### Delete old remote branch after transition - `git push origin --delete master` ## Branching Workflows (patterns enabled by lightweight branches) - Goal - choose a branching strategy that matches team/release needs - Key enabler - easy repeated three-way merges over time ### Long-Running Branches (progressive stability) - Concept - keep multiple always-open branches for different stability levels - merge “upwards” as code becomes stable - Common pattern - `master`: only stable/released (or release-candidate) code - `develop` / `next`: integration/testing branch; can be unstable - topic branches merged into develop/next for testing before master - How to think about “stability” - linear commit history view - stable branches are “behind” (older, tested commits) - bleeding-edge branches are “ahead” (newer, less proven commits) - “silo” view - commits graduate to more stable silos once fully tested - Multi-level stability in large projects - additional branches like `proposed` / `pu` (proposed updates) - idea: not everything is ready for `next` or `master` immediately - Note - not required, but often helpful for large/complex projects ### Topic Branches (short-lived branches) - Definition - branch created for a single feature/bugfix/experiment - typically merged and deleted after completion - Why Git makes this common - branch creation/merging is cheap → can do it many times a day - Benefits - clean context switching (work isolated by topic) - easier code review (topic’s commits grouped) - flexible integration timing (minutes, days, months later) - can merge in any order regardless of creation order - Example topology from the chapter - work on `master` - branch `iss91` (issue work) - branch `iss91v2` off `iss91` (alternate approach) - return to `master` and continue other work - branch `dumbidea` off `master` (experimental idea) - outcome - discard `iss91` if inferior - merge `iss91v2` and `dumbidea` if chosen - Reminder: local operations - branching/merging is local-only until you fetch/push/pull - Reference mention - more workflow discussion later in “Distributed Git” ## Remote Branches (remote references + remote-tracking branches) ### Remote references overview - Remote repos contain references (pointers) to - branches - tags - other refs - Ways to inspect - `git ls-remote ` (full list of remote refs) - `git remote show ` (focus on remote branches + info) ### Remote-tracking branches - Definition - local references that record the state of remote branches - you can’t move them yourself - Git updates them during network communication - Naming - `/` - Examples - `origin/master` - `origin/iss53` - Mental model - bookmarks showing where a remote branch was last time you connected ### Clone example (how origin/master appears) - When cloning from a server - Git names the remote `origin` by default - downloads data - creates `origin/master` (remote-tracking) - creates your local `master` starting at same commit as origin’s master #### Note: “origin” is not special - It’s just the default name created by `git clone` - You can rename the default remote at clone time - `git clone -o booyah ...` → remote-tracking branch becomes `booyah/master` ### Divergence between local and remote - If you commit locally and someone else pushes to the remote - histories diverge - `origin/master` does not move until you communicate ### Fetching updates remote-tracking branches - `git fetch origin` - contacts remote - downloads objects you don’t have - updates pointers like `origin/master` to newer commits ### Multiple remotes - Add another remote - `git remote add teamone ` - Fetch it - `git fetch teamone` - Possible outcome - if teamone has only a subset of commits you already have from origin: - fetch downloads no new objects - still updates `teamone/master` pointer to match teamone’s master tip ## Pushing (sharing branches) ### Why pushing is explicit - Local branches do not automatically sync to remotes - Benefit - you can keep private local branches - push only branches you intend to share/collaborate on ### Push a branch - Pattern - `git push ` - Example - `git push origin serverfix` - What Git expands it to (conceptual) - `refs/heads/serverfix:refs/heads/serverfix` - Push local branch to a different remote branch name - `git push origin serverfix:awesomebranch` ### Authentication convenience (HTTPS) - HTTPS push commonly prompts for username/password - To avoid typing credentials repeatedly - credential cache example: - `git config --global credential.helper cache` - reference mentioned: “Credential Storage” (for other options) ### After someone else fetches - Fetching a pushed branch - `git fetch origin` - Result - creates/updates a remote-tracking ref (e.g., `origin/serverfix`) - does NOT create a local editable branch automatically ### Using fetched remote-tracking branch work - Merge directly into current branch - `git merge origin/serverfix` - Create a local branch based on it (editable) and track it - `git checkout -b serverfix origin/serverfix` ## Tracking Branches (local branches that track upstream) ### Definitions - Tracking branch - local branch tied to a remote-tracking branch - Upstream branch - remote-tracking branch the local branch tracks ### Why tracking matters - On a tracking branch, `git pull` can automatically - fetch from the right remote - merge the right branch ### How tracking branches are created - Common creation form - `git checkout -b /` - Shorthand - `git checkout --track origin/serverfix` - Extra shortcut - `git checkout serverfix` - works if - local `serverfix` doesn’t exist, and - exactly one remote has `serverfix` - Different local name than remote branch - `git checkout -b sf origin/serverfix` - local `sf` tracks `origin/serverfix` ### Set or change upstream later - `git branch -u origin/serverfix` - also available as `--set-upstream-to` ### Upstream shorthand in commands - `@{upstream}` or `@{u}` - references the upstream branch of the current branch - Example - `git merge @{u}` instead of `git merge origin/master` (when master tracks origin/master) ### Inspect tracking status and ahead/behind - `git branch -vv` - shows local branches - indicates upstream tracking target - shows ahead/behind counts - Interpreting counts - ahead N → N local commits not pushed - behind N → N remote commits not merged locally - Cache caveat - ahead/behind shown is from last fetch; command doesn’t contact server - To refresh counts - `git fetch --all; git branch -vv` ## Pulling (fetch + merge convenience) - `git fetch` - downloads new data - does not modify working directory - leaves integration to you (merge/rebase) - `git pull` - in most cases = `fetch` immediately followed by `merge` - uses tracking (upstream) info to pick remote + branch - Guidance from the chapter - explicit `fetch` + `merge` is often clearer than the “magic” of `pull` ## Deleting Remote Branches - When a remote branch is no longer needed - merged into mainline/stable branch on the server - Delete remote branch pointer - `git push origin --delete serverfix` - Effect - removes the branch pointer on the server - server may keep underlying objects until garbage collection - accidental deletions can often be recovered before GC runs ## Rebasing (the other integration strategy) - Two main ways to integrate changes between branches - `merge` - `rebase` ### The Basic Rebase (replaying commits) - Starting situation - branches diverged; each has unique commits - Merge recap (already covered earlier) - three-way merge of: - tip snapshot A - tip snapshot B - common ancestor snapshot - creates a new snapshot + merge commit - Rebase concept - take the patch introduced by commits on one branch - reapply them on top of another branch’s tip - Example commands - `git checkout experiment` - `git rebase master` - Internal steps (conceptual) - find common ancestor between current branch and target branch - compute diffs for each commit on current branch since ancestor - save diffs temporarily - reset current branch to target tip - apply diffs sequentially (creating new commits with new SHAs) - After rebase - integrate by fast-forward merge - `git checkout master` - `git merge experiment` - Result comparison - final snapshot content is the same as with merge - history is different - rebase → linear-looking history - merge → preserves the true parallel shape - Common use case (contributing workflow) - rebase your work onto `origin/master` before submitting patches - maintainer can integrate via fast-forward / clean apply - Core conceptual distinction - rebase: replay changes in order introduced - merge: combine endpoints and record a merge ### More Interesting Rebases (rebasing a branch off another topic branch) - Scenario - topic branch `server` created from master; commits added - topic branch `client` created from `server`; commits added - later additional commits added to `server` - Goal - ship client changes now (merge into master) - delay server changes until tested - Use `--onto` - `git rebase --onto master server client` - Meaning - take commits on `client` that are not on `server` - replay them as if `client` started from `master` - Integrate client quickly - `git checkout master` - `git merge client` (fast-forward) - Integrate server later without manual checkout - `git rebase master server` - checks out `server` and replays onto master - `git checkout master` - `git merge server` (fast-forward) - Cleanup - delete topic branches once integrated - `git branch -d client` - `git branch -d server` ### The Perils of Rebasing (rewriting published history) - The one-line rule - Do not rebase commits that exist outside your repository and that people may have based work on - Why rebasing public commits is dangerous - rebase abandons existing commits and creates new ones - new commits have different SHAs - collaborators who based work on old SHAs must reconcile mismatched history - Example failure pattern (from the chapter) - you clone and do work - someone else pushes a merge to the central server - later they rebase their work and `push --force` (rewriting server history) - you fetch new commits - if you `git pull` normally, you may create a merge combining old + new lines - can lead to duplicate-looking commits (same message/author/date) with different IDs - pushing that back can reintroduce commits the other dev tried to eliminate - Social consequence emphasized - if you rewrite shared history, teammates will have to re-merge and untangle confusion ### Rebase When You Rebase (recovering after a force-push) - Problem after force-push - determine which commits are uniquely yours vs rewritten copies - Patch-id concept - besides commit SHA-1, Git can compute a checksum based on the patch content (“patch-id”) - How rebase helps - rebasing onto the updated target can let Git: - identify which commits are already represented (same patch) - replay only the unique commits - Example approach - `git rebase teamone/master` - What Git may compute during this recovery rebase (as described) - determine commits unique to your branch - exclude merge commits from replay - detect commits that were rewritten but represent the same patch in the target - apply remaining unique commits on top of the updated branch - Limitation noted - works best if rewritten commits are almost the same patch - otherwise Git may not detect duplication and may reapply a similar patch (possibly failing) - Convenience options - `git pull --rebase` instead of normal pull - or manual: `git fetch` then `git rebase /` - configure default: - `git config --global pull.rebase true` - Safety guideline recap - safe: rebase commits that never left your machine - generally ok: rebase pushed commits if nobody based work on them - risky: rebase publicly shared commits → coordinate + warn others to use `pull --rebase` ### Rebase vs. Merge (choosing based on what “history” means) - Two viewpoints on commit history - History as a factual record - commit history documents what actually happened - rewriting is “lying” about events - merge commits reflect real parallel work - History as a curated story - raw development includes missteps and dead ends - before mainline, rewrite history to tell a clearer story - tools mentioned: `rebase`, `filter-branch` - Conclusion - no universal best choice; depends on team/project - Practical “best of both worlds” guideline - rebase local changes before pushing (clean up) - never rebase anything you’ve pushed somewhere shared/public ## Summary (skills this chapter expects you to have now) - Branch creation and switching - create branches, move between them - understand HEAD as “current branch pointer” - Merging - fast-forward merges - three-way merges and merge commits (multiple parents) - resolve conflicts (markers, `status`, `add`, `mergetool`, final `commit`) - Branch management - list branches and identify current branch - inspect branch tips (`-v`) - find merged/unmerged branches (`--merged`, `--no-merged`) - delete safely (`-d`) or forcibly (`-D`) - rename branches (local + remote cleanup) - rename default branch (master/main) with ecosystem updates - Collaboration with remotes - remote-tracking branches, fetch/push/pull behaviors - create tracking branches and set upstream - delete remote branches - Rebasing - what rebase does and why it can make history linear - advanced rebase (`--onto`) - when rebasing is dangerous and how to mitigate with `pull --rebase` - Next topic preview (mentioned) - how to run your own Git repository-hosting server ```