A Git branch lets work continue from a chosen commit without moving another branch. Teams use branches to isolate features, fixes, experiments, and release work until those changes are ready to integrate.

Read Git Fundamentals and Git Remotes first if HEAD, commits, or remote-tracking branches are unfamiliar.

What a Branch Is

A branch is a lightweight, movable name that points to a commit. It is not another copy of the repository.

C1 ← C2 ← C3 ← C4
          ▲      ▲
          │      │
         main    feature/search-filters

Both branches initially point to the same commit. A new commit made while feature/search-filters is checked out moves that branch forward; main remains where it was.

HEAD normally points to the currently checked-out branch:

HEAD → feature/search-filters → C4

The working tree contains the files from the checked-out commit plus any local edits. Uncommitted edits belong to the working tree, not to a branch.

Note: A repository’s primary branch may be named main, master, or something else. These notes use main explicitly rather than assuming a default.

Inspect Branches

# List local branches
git branch
 
# Show local and remote-tracking branches
git branch --all
 
# Print only the current branch name
git branch --show-current
 
# Display the commit graph and branch pointers
git log --graph --oneline --decorate --all

In ordinary git branch output, * marks the current branch:

* main
  feature/search-filters
  fix/login-timeout

The asterisk does not indicate that main is more important. It only shows which branch is checked out in the current working tree.

Create and Switch Branches

Create a branch from the current commit and switch to it:

git switch -c feature/search-filters

This combines two operations:

git branch feature/search-filters
git switch feature/search-filters

Create a branch from another starting point:

git switch -c fix/login-timeout origin/main

The new branch starts at the commit identified by origin/main.

Switch between existing branches:

git switch main
git switch feature/search-filters
 
# Return to the previously checked-out branch
git switch -

git checkout -b <branch> is an older, still-supported way to create and switch branches. git switch -c <branch> is clearer because git checkout also performs unrelated file and commit operations.

Git may refuse to switch if doing so would overwrite local changes. Inspect git status, then commit coherent work or stash unfinished work instead of forcing the switch.

Scenario: Pause a Feature for an Urgent Fix

Suppose a report-export feature is in progress on feature/report-export when production checkout requests begin timing out.

flowchart LR
    C0["C0"] --> C1["C1"]
    C1 --> H1["H1<br/>Payment timeout fix"]
    C1 --> F1["F1<br/>Report export"] --> F2["F2"]
    C1 --> M1["M1<br/>fix merged"]
    H1 --> M1
    M1 --> M2["M2<br/>feature merged"]
    F2 --> M2

main follows C0 → C1 → M1 → M2. The fix and feature work begin from separate commits and later merge into the main line.

1. Preserve the unfinished feature work

Check the current state:

git status

Commit the work if it forms a useful checkpoint:

git add src/reports/export.ts
git commit -m "Add initial report export"

If it is not ready for a commit, stash tracked and untracked changes:

git stash push -u -m "Report export in progress"

See Temporarily Store Unfinished Work for stash behavior and limitations.

2. Create the fix from current production history

git switch main
git pull --ff-only
git switch -c fix/payment-timeout

This assumes the local main branch tracks the shared main branch covered in Git Remotes. Updating it first prevents the fix from starting at an unnecessarily old commit.

3. Make, inspect, and publish the fix

git status
git add config/payment-service.yaml
git diff --staged
git commit -m "Handle payment service timeout"
git push -u origin fix/payment-timeout

-u records origin/fix/payment-timeout as the local branch’s upstream. Later pushes from this branch can normally use git push without repeating both names.

The usual team workflow is then:

  1. Open a pull request from fix/payment-timeout to main.
  2. Run automated tests and review the change.
  3. Merge using the repository’s configured merge policy.
  4. Deploy and validate the fix through the team’s release process.

Git records and integrates the application change. It does not deploy or validate the live service by itself.

4. Resume the feature

After the fix is merged remotely:

git switch main
git pull --ff-only
 
git switch feature/report-export
git merge main

If the feature work was stashed, apply it after updating the feature branch:

git stash pop

The merge brings the production fix into the feature branch. Some teams use git rebase origin/main instead. Follow one agreed workflow: rebasing rewrites the feature commits and needs extra care if other developers already use that branch.

Merge Direction

git merge <source> integrates the source branch into the currently checked-out branch.

To merge fix/payment-timeout into main locally:

git switch main
git merge fix/payment-timeout

Read the second command as:

merge fix/payment-timeout into the current branch, main

This local merge-and-push workflow works only when project policy allows direct updates to main:

git push origin main

Many shared repositories protect main and require a pull request instead.

Fast-Forward and Three-Way Merges

Git chooses the merge result from the commit graph.

SituationResult
Current branch has not advanced since the other branch divergedGit can move the current branch pointer forward without a merge commit
Both branches contain new commitsGit normally performs a three-way merge and creates a commit with two parents
Both sides changed the same area incompatiblyGit pauses for conflict resolution

Fast-forward

Before:

C1 ← C2             main
      \
       C3 ← C4      feature

After merging feature into main, main can simply move to C4:

C1 ← C2 ← C3 ← C4


          main, feature

Three-way merge

When both branches advanced, Git compares the two tips and their common ancestor:

C1 ── C2 ── M1 ─────┐
       └── F1 ───────┴── M2 ← main


          feature

The merge commit has two parents. Repository settings may instead require squash merging, rebasing, or a merge commit even when a fast-forward is possible.

Branching Strategy Is a Team Decision

Git supplies branch and merge mechanics; it does not require one branching model.

ApproachTypical shapeUseful when
Short-lived topic branchesBranch from main, review, merge, deleteTeams integrate small changes frequently
Trunk-based developmentVery short branches or direct, guarded changes to the main lineStrong automated testing and rapid integration are available
Gitflow-style branchesmain, long-lived develop, plus feature, release, and hotfix branchesA team deliberately manages scheduled releases through multiple long-lived stages

A permanent develop branch is therefore a workflow choice, not a requirement or default created by Git. Likewise, “never commit directly to main” is a common repository policy, not a limitation of the Git command-line tool.

Long-lived branches usually diverge further and require more integration work. Small changes, frequent integration, automated tests, and clear ownership reduce conflict risk.

Compare Work with git diff

The meaning of - and + in diff output depends on the two compared states:

  • - marks a line present on the left or earlier side but absent on the right side.
  • + marks a line present on the right or later side but absent on the left side.

Useful comparisons include:

CommandComparison
git diffWorking tree against the staging area
git diff --stagedStaging area against HEAD
git diff HEADWorking tree, including staged and unstaged changes, against HEAD
git diff main feature/metrics-dashboardSnapshot at main against the snapshot at the feature tip
git diff main...feature/metrics-dashboardChanges on the feature side since its merge base with main

For git diff, main..feature/metrics-dashboard compares the same two endpoint snapshots as main feature/metrics-dashboard. Three dots have different meaning: they start the comparison at the branches’ common ancestor.

Use a path after -- to restrict a comparison:

git diff main...feature/metrics-dashboard -- src/metrics/dashboard.ts

Handle a Merge Conflict

Git automatically combines non-overlapping changes. It may stop when both branches change the same lines differently, or when one branch deletes a path that the other modifies.

Start from a clean working tree before merging:

git status
git switch main
git merge fix/cache-expiry

A textual conflict may appear as:

<<<<<<< HEAD
cache_ttl_seconds: 300
=======
cache_ttl_seconds: 600
>>>>>>> fix/cache-expiry

In this merge:

  • HEAD is the current receiving branch.
  • fix/cache-expiry is the branch being merged.
  • ======= separates the two versions.

The correct result may use either side, combine both sides, or replace both. Edit the file so it contains the intended final configuration and remove all conflict markers.

Then inspect, test, and stage the resolution:

git status
git diff
 
git add config/cache.yaml
git diff --staged
git merge --continue

git add marks that path as resolved. git merge --continue completes the paused merge after all conflicts are resolved.

To abandon the merge and try to reconstruct the pre-merge state:

git merge --abort

git merge --abort is safest when the merge began with a clean working tree. It may be unable to reconstruct unrelated uncommitted changes exactly.

Warning: A file with no remaining conflict markers is not necessarily correct. Validate syntax, tests, and operational intent before completing the merge.

Delete a Finished Branch

After verifying that the work is integrated, delete the local branch:

git switch main
git branch -d fix/payment-timeout

-d refuses deletion when Git cannot confirm that the branch is merged into its upstream or the current branch. Force deletion bypasses that protection:

git branch -D fix/payment-timeout

Use -D only when the unmerged commits are intentionally disposable or recoverable elsewhere.

Note: After a squash merge, -d may refuse deletion because the original branch commits are not ancestors of main. Verify the merged result and repository history before deliberately using -D.

Deleting a local branch does not delete its remote counterpart:

git push origin --delete fix/payment-timeout

Deleting a branch removes its name, not the commits that are still reachable through another branch or tag.

Command Summary

CommandPurpose
git branchList local branches
git switch -c <branch>Create and switch to a branch
git switch <branch>Switch to an existing branch
git merge <branch>Merge a source branch into the current branch
git merge --continueComplete a merge after resolving conflicts
git merge --abortAbandon a merge in progress
git diff <a> <b>Compare two endpoint snapshots
git diff <a>...<b>Compare the merge base with the second endpoint
git branch -d <branch>Safely delete a merged local branch
git push origin --delete <branch>Delete a branch from the named remote

Mistakes to Avoid

  • Treating a branch as a complete copy of the repository
  • Assuming git branch <name> also switches to the new branch
  • Merging while unsure which branch is currently checked out
  • Starting urgent work from a stale local main
  • Forcing a branch switch when local work has not been preserved
  • Using git branch -D as the normal cleanup command
  • Treating a develop branch as a Git requirement
  • Removing conflict markers without validating the combined result
  • Assuming deletion of a local branch also deletes the remote branch
  • Assuming a successful Git merge means the change was deployed successfully

TL;DR

  • A branch is a movable name pointing to a commit, not a duplicate repository.
  • git switch -c <branch> creates and checks out a branch.
  • Preserve unfinished work before switching to an urgent fix.
  • git merge <branch> merges the named branch into the current branch.
  • A fast-forward moves a pointer; diverged histories normally require a three-way merge.
  • Branching models and protected-branch rules are team policies, not Git requirements.
  • Resolve conflicts by editing the intended result, staging it, testing it, and continuing the merge.
  • Prefer git branch -d over force deletion.

Resources

Continue with Undoing Changes for stashing work and choosing between restore, reset, and revert.

Note: Branch, merge, and diff behavior was checked against current Git documentation in July 2026.