Files
mapas-mentales/mindmap/Git Branching.md

798 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
```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 Gits model works)
- Why you must understand Gits 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
- Gits 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 whats 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
- Its identical to any other branch
- Its 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 youre 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` doesnt show all branches by default
- Default behavior
- `git log` shows the history reachable from the currently checked-out branch
- To see another branchs 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 cant 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 its 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 <newbranch>`
- Git ≥ 2.23 alternative: `git switch`
- switch to existing: `git switch <branch>`
- create + switch: `git switch -c <newbranch>` (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 its 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 isnt 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 <file>` 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` isnt 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 <branch>` fails if not fully merged
- `git branch -D <branch>` 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 (topics 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 <remote>` (full list of remote refs)
- `git remote show <remote>` (focus on remote branches + info)
### Remote-tracking branches
- Definition
- local references that record the state of remote branches
- you cant move them yourself
- Git updates them during network communication
- Naming
- `<remote>/<branch>`
- 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 origins master
#### Note: “origin” is not special
- Its 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 dont have
- updates pointers like `origin/master` to newer commits
### Multiple remotes
- Add another remote
- `git remote add teamone <url>`
- 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 teamones 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 <remote> <branch>`
- 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 <branch> <remote>/<branch>`
- Shorthand
- `git checkout --track origin/serverfix`
- Extra shortcut
- `git checkout serverfix`
- works if
- local `serverfix` doesnt 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 doesnt 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 branchs 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 <remote>/<branch>`
- 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 youve 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
```