
Git Branches and Merging: A Complete Practical Guide
Branches allow developers to separate one line of work from another. A new feature can evolve without immediately changing stable code, an urgent fix can be prepared independently, and experiments can be abandoned without damaging the main development line.
Git makes branching lightweight because a branch is not a complete duplicated project directory. It is a movable reference to a commit. As new commits are created, the current branch reference moves forward.
This guide explains the branch model first, then turns it into a complete practical workflow. You will create and switch branches, inspect divergence, perform fast-forward and three-way merges, resolve conflicts, abort unsafe integrations, rename and delete branches, and verify whether work has been merged before cleanup.
What You Will Learn
What a Git branch actually represents.
How branches, commits, and
HEADare connected.How to list, create, switch, rename, copy, and delete branches.
How to start a branch from another commit or branch.
How to compare branches before merging.
The difference between fast-forward and three-way merges.
How
--ff-only,--no-ff, and--no-commitaffect a merge.How to understand and resolve merge conflicts.
How to abort or continue a merge safely.
How to inspect merged and unmerged branches before deletion.
How local branches differ from remote-tracking branches.
Common branching mistakes and practical team conventions.
What Is a Git Branch?
A branch is a named reference to a commit. It identifies the current tip of one line of development.
Assume a repository has three commits:
A---B---C mainThe branch named main points to commit C.
When you create a branch named feature/search, Git creates another reference at the same commit:
A---B---C main
feature/searchNo files have been copied and no new commit has been created. Both names point to the same commit.
After switching to feature/search and creating a commit, only that branch moves:
A---B---C main
\
D feature/searchThe branch is therefore a movable label, not a folder backup.
Branches Are Lightweight References
In Git, creating a branch is inexpensive because Git mainly creates a new reference. The project snapshots and commits already exist in the object database.
This makes short-lived branches practical for:
Features.
Bug fixes.
Experiments.
Documentation updates.
Release preparation.
Emergency hotfixes.
Refactoring.
Lightweight does not mean careless. A repository can become difficult to understand when branches are poorly named, kept indefinitely, or merged without review.
What Is HEAD?
HEAD identifies the location currently checked out in the working tree.
In a normal branch workflow, HEAD points to a branch, and the branch points to a commit:
HEAD -> feature/search -> DWhen a new commit is created, the current branch moves to the new commit:
Before:
HEAD -> feature/search -> D
After commit E:
HEAD -> feature/search -> EThe other branches do not move automatically.
Create a Practice Repository
The examples use a small project named Task Notes.
macOS, Linux, or Git Bash
mkdir git-branches-demo
cd git-branches-demo
git init -b main
cat > app.txt <<'EOF'
Task Notes
Storage: local
EOF
cat > README.md <<'EOF'
# Task Notes
A small project for practicing Git branches and merges.
EOF
git add app.txt README.md
git commit -m "Create task notes project"
PowerShell
New-Item -ItemType Directory git-branches-demo
Set-Location git-branches-demo
git init -b main
@"
Task Notes
Storage: local
"@ | Set-Content app.txt
@"
# Task Notes
A small project for practicing Git branches and merges.
"@ | Set-Content README.md
git add app.txt, README.md
git commit -m "Create task notes project"
Verify the initial repository:
git status
git branch --show-current
git log --oneline --decorateList Local Branches
Run:
git branchThe current branch is marked with an asterisk:
* mainShow the latest commit on each branch:
git branch -vShow more relationship information, including upstream status when configured:
git branch -vvList local and remote-tracking branches:
git branch -aList only remote-tracking branches:
git branch -rIn this local practice repository, remote lists will remain empty until a remote is added and fetched.
Create a Branch Without Switching
Create a branch at the current commit:
git branch feature/searchList branches:
git branchExpected structure:
feature/search
* mainThe new branch exists, but HEAD still points to main.
Confirm the graph:
git log --oneline --decorate --allBoth branch names should decorate the same commit.
Switch to an Existing Branch
Use:
git switch feature/searchVerify:
git branch --show-current
git statusSwitching updates the working tree and index to match the selected branch, as long as doing so does not overwrite unsafe local changes.
The Older checkout Command
You will often see:
git checkout feature/searchgit checkout can switch branches, restore paths, and detach HEAD. Modern Git provides git switch for branch switching and git restore for file restoration, which makes intent clearer.
Existing projects and scripts may still use checkout, so developers should recognize both forms.
Create and Switch in One Command
Create a new branch and switch to it:
git switch -c feature/exportThe older equivalent is:
git checkout -b feature/exportThe -c option creates the branch only if it does not already exist.
The force-create form is:
git switch -C feature/export-C can reset an existing branch to a new starting point. Use it carefully because moving an existing branch reference may make commits unreachable from that branch.
Create a Branch from a Specific Starting Point
A new branch normally starts at the current HEAD. You can choose another starting point:
git switch -c hotfix/login mainCreate a branch from a tag:
git switch -c support/v1.0 v1.0.0Create a branch from a commit:
git switch -c investigation 7a93c2eCreate without switching:
git branch investigation 7a93c2eThe starting point determines the first commit reachable from the new branch tip.
Make the First Feature Commit
Switch to the search branch if necessary:
git switch feature/searchCreate a feature file.
macOS, Linux, or Git Bash
cat > search.txt <<'EOF'
Search tasks by keyword.
EOF
git add search.txt
git commit -m "Add keyword search description"
PowerShell
"Search tasks by keyword." | Set-Content search.txt
git add search.txt
git commit -m "Add keyword search description"
Inspect the graph:
git log --oneline --graph --decorate --allConceptually:
A main
\
B feature/searchThe feature branch moved forward. main remained at the original commit.
Switching with Uncommitted Changes
Git may allow a branch switch when local changes can be preserved safely. It refuses when switching would overwrite changes or create an unsafe state.
Before switching branches, run:
git statusThen choose one of these actions:
Commit a complete logical change.
Stash incomplete work when appropriate.
Discard unwanted changes deliberately.
Remain on the current branch until the work is organized.
Do not use force options merely to bypass a warning. Git may be protecting uncommitted work.
Switch Back to the Previous Branch
Use:
git switch -This switches to the previously checked-out branch or location.
The equivalent revision notation is often represented internally by:
@{-1}The shortcut is useful when moving repeatedly between a feature branch and its integration branch.
Detached HEAD
When you switch directly to a commit rather than a branch, HEAD can become detached:
git switch --detach HEAD~1In this state, HEAD points directly to a commit:
HEAD -> BNo branch name automatically moves when you create a new commit.
Detached mode is useful for:
Inspecting an old version.
Running tests against a historical commit.
Temporarily exploring another point in history.
If you create valuable commits while detached, preserve them by creating a branch:
git switch -c recovery/experimentTo return without preserving new detached commits:
git switch mainDetached HEAD is not inherently an error, but commits created there can become difficult to find after leaving unless a branch or tag preserves them.
Rename a Branch
Rename the current branch:
git branch -m feature/task-searchRename another local branch:
git branch -m old-name new-nameForce a rename when the target name already exists:
git branch -M new-nameUse force carefully. It can replace the destination branch reference.
A local rename does not automatically rename a branch on a remote hosting platform. Remote branch publication and cleanup require separate push operations, which will be covered in the remote collaboration articles.
Copy a Branch Reference
Git can copy a branch and its configuration:
git branch -c feature/task-search backup/task-searchForce-copy with:
git branch -C feature/task-search backup/task-searchCopying a branch name is less common than creating a normal branch at the same commit. It is useful when you intentionally want related branch configuration and reflog behavior copied as well.
Compare Branches Before Merging
Never merge only because a branch name sounds complete. Inspect its commits and content.
Show Commits Unique to the Feature Branch
git log --oneline main..feature/task-searchShow Commits Unique to Main
git log --oneline feature/task-search..mainShow Unique Commits on Both Sides
git log --left-right --graph --oneline main...feature/task-searchCompare Feature Changes Since Divergence
git diff --stat main...feature/task-search
git diff main...feature/task-searchShow the Common Ancestor
git merge-base main feature/task-searchThe merge base is a best common ancestor used to reason about changes made after the branches diverged.
What Does git merge Do?
git merge incorporates changes from another commit or branch into the current branch.
The direction is crucial.
To merge the search feature into main:
git switch main
git merge feature/task-searchThe current branch receives the other branch’s changes. Running the command while on the feature branch would attempt to merge main into the feature branch instead.
Before every merge, confirm:
git branch --show-current
git statusFast-Forward Merge
A fast-forward is possible when the current branch has no new commits after the feature branch diverged.
Before:
A---B main
\
C---D featureMore precisely, main points to B, and the feature branch is a direct descendant:
A---B---C---D feature
^
mainAfter:
A---B---C---D main, featureGit only moves the main reference forward. It does not need a merge commit because there is no divergent current-branch history to combine.
Perform the merge:
git switch main
git merge feature/task-searchThe result may report a fast-forward update.
Require Fast-Forward Only
Use:
git merge --ff-only feature/task-searchThis succeeds only when Git can update the current branch without creating a merge commit.
If the histories diverged, the command stops instead of choosing a merge strategy.
This is useful when a workflow requires linear integration and wants unexpected divergence to be handled explicitly.
Force a Merge Commit with --no-ff
Even when fast-forwarding is possible, you can request a merge commit:
git merge --no-ff feature/task-searchThe history becomes:
A---B-------M main
\ /
C---D featureThe merge commit preserves a visible integration boundary around the feature branch.
Benefits may include:
Making a completed feature visible as one grouped branch in history.
Providing a merge message with integration context.
Making a whole feature easier to identify for later reversion.
Costs may include:
Additional merge commits.
A more complex graph.
Noise when every tiny branch receives a merge commit.
The correct choice depends on the team’s history policy.
Three-Way Merge
A three-way merge is needed when both branches contain commits after their common ancestor.
Example:
C---D feature
/
A---B---E---F mainGit considers three important snapshots:
The merge base where the branches shared history.
The current branch tip.
The branch being merged.
If changes can be combined automatically, Git creates a merge result and normally records a merge commit:
C---D
/ \
A---B---E---F---M mainThe merge commit has two parents: the previous current-branch tip and the merged branch tip.
Create a Three-Way Merge Example
Starting from a clean main, create a reporting branch:
git switch -c feature/reportAdd a report file:
macOS, Linux, or Git Bash
cat > report.txt <<'EOF'
Report completed tasks.
EOF
git add report.txt
git commit -m "Add completed-task report description"
PowerShell
"Report completed tasks." | Set-Content report.txt
git add report.txt
git commit -m "Add completed-task report description"
Switch to main and make an independent change:
git switch mainmacOS, Linux, or Git Bash
cat >> README.md <<'EOF'
## Storage
Tasks are stored locally.
EOF
git add README.md
git commit -m "Document local task storage"
PowerShell
@"
## Storage
Tasks are stored locally.
"@ | Add-Content README.md
git add README.md
git commit -m "Document local task storage"
The branches now diverge. Merge the feature:
git merge feature/reportBecause the changes affect separate files, Git should combine them automatically and create a merge commit.
Inspect the result:
git log --oneline --graph --decorate --all
git show --stat --summary HEADInspect a Merge Before Committing
Use:
git merge --no-commit --no-ff feature/reportThis performs the merge but stops before creating the merge commit, allowing you to inspect and adjust the result.
Review:
git status
git diff --staged
git diff --checkRun tests, then commit:
git commitWhy include --no-ff? A fast-forward update does not create a merge commit, so --no-commit cannot stop before a merge commit that does not exist. Combining the options ensures the integration stops for review when a merge commit is intended.
Preview Whether a Merge Base Exists
Inspect the common ancestor:
git merge-base main feature/reportCheck whether one branch is already an ancestor of another:
git merge-base --is-ancestor feature/report mainThe command communicates through its exit status:
Success means the first revision is an ancestor of the second.
A nonzero status means it is not.
This is useful in scripts and release checks.
What Is a Merge Conflict?
A merge conflict occurs when Git cannot determine a safe combined result automatically.
Common causes include:
Both branches changed the same lines differently.
One branch deleted a file that the other modified.
Both branches added different files at the same path.
A file or directory was renamed in incompatible ways.
Binary files changed on both branches.
A conflict is not corruption. Git has preserved the competing information and asks a developer to decide the intended result.
Create a Controlled Merge Conflict
Start from a clean main. Create a branch:
git switch -c feature/cloud-storageChange app.txt to:
Task Notes
Storage: cloud
Stage and commit:
git add app.txt
git commit -m "Use cloud task storage"Return to main:
git switch mainChange the same line differently:
Task Notes
Storage: encrypted local database
Commit:
git add app.txt
git commit -m "Use encrypted local task storage"Now merge:
git merge feature/cloud-storageGit should stop and report a conflict in app.txt.
Read the Conflict State
Run:
git statusGit identifies unmerged paths and provides commands for continuing or aborting.
Short status may show:
UU app.txtUU means both sides modified the file and the conflict remains unresolved.
Other conflict codes can represent delete/modify, add/add, or other combinations.
Understand Conflict Markers
Open the conflicted file. A text conflict may look like:
Task Notes
<<<<<<< HEAD
Storage: encrypted local database
=======
Storage: cloud
>>>>>>> feature/cloud-storage
The sections mean:
<<<<<<< HEAD: Content from the current branch.=======: Separator.>>>>>>> feature/cloud-storage: Content from the branch being merged.
The final file must contain the actual intended content, not the conflict markers.
Resolve a Conflict Manually
Suppose the correct product decision is to support both modes. Edit the file to:
Task Notes
Storage: encrypted local database with optional cloud synchronization
Then mark the file resolved by staging it:
git add app.txtCheck for unresolved files:
git status
git diff --checkReview the combined result:
git diff --stagedComplete the merge:
git commitOr use:
git merge --continuegit merge --continue completes the merge after conflicts are resolved and staged. Git may open the editor for the merge message.
Use the Index Stages During a Conflict
During an unresolved merge, the index can store multiple stages for a conflicted path:
Stage 1: Merge-base version.
Stage 2: Current branch version.
Stage 3: Other branch version.
Inspect them:
git ls-files -uView the base version:
git show :1:app.txtView the current side:
git show :2:app.txtView the other side:
git show :3:app.txtThis can be more precise than reading conflict markers alone.
Ours and Theirs During a Normal Merge
During a normal merge:
Ours refers to the current branch checked out when the merge began.
Theirs refers to the branch or commit being merged.
Take the current side of one conflicted file:
git checkout --ours app.txt
git add app.txtTake the incoming side:
git checkout --theirs app.txt
git add app.txtModern restoration syntax can also select index stages in supported workflows:
git restore --ours app.txt
git restore --theirs app.txtDo not use these commands automatically. They replace the working file with one side and may discard valuable changes from the other. Review the result before staging.
Also note that the meaning of “ours” and “theirs” can become counterintuitive during rebasing because the operation’s perspective differs. Always verify the current operation and file content.
Use a Merge Tool
After configuring a supported visual or terminal merge tool, run:
git mergetoolA merge tool can display:
The common base.
The current branch version.
The incoming version.
The proposed result.
After the tool finishes, inspect the files and repository status. Some tools leave backup files that should be removed or ignored according to project policy.
Abort a Merge
When the merge should not continue:
git merge --abortGit attempts to reconstruct the state before the merge began.
Abort is safest when the working tree was clean before starting the merge. Pre-existing uncommitted changes can make restoration more complicated.
This is why a clean working tree is strongly recommended before integration.
Fallback Seen in Older Workflows
You may encounter:
git reset --mergeIt can be used in related recovery situations, but git merge --abort communicates the intention more clearly during an active merge.
Restart Conflict Resolution
If a conflict was edited incorrectly but the entire merge should not be aborted, restore the conflict presentation carefully.
One approach is to restore the unresolved stages for the file through the merge operation’s available index state, but behavior depends on what has already been staged or overwritten.
For beginners, the safest choices are usually:
Use version-control-aware editor actions before staging.
Inspect stages with
git show :1:,:2:, and:3:.Abort and restart the merge when the resolution process became unreliable.
Do not continue from a file whose origin you no longer understand.
Conflict Resolution Workflow
Run
git status.Identify every unmerged path.
Open each conflict and understand both intentions.
Consult tests, requirements, related commits, and teammates when necessary.
Edit the file into the correct combined state.
Remove all conflict markers.
Run
git add pathto mark the path resolved.Repeat until no unmerged paths remain.
Run tests and static checks.
Review the staged result.
Complete the merge with
git merge --continueorgit commit.Inspect the final graph and application behavior.
Inspect an In-Progress Merge
Useful commands include:
git status
git diff
git diff --staged
git diff --check
git ls-files -u
During conflicts:
git diffdisplays unresolved combined diff information.git diff --stagedshows resolved content already prepared for the merge commit.git ls-files -ulists unmerged index entries.git diff --checkhelps detect whitespace errors and remaining conflict markers in added lines.
Merge Messages
When a merge commit is created, Git proposes a message. You can supply one:
git merge --no-ff feature/report -m "Merge completed-task reporting"
Or allow the editor to open:
git merge --no-ff --edit feature/reportA useful merge message can explain:
Which feature or fix was integrated.
Why a non-obvious conflict was resolved in a particular way.
Important compatibility or deployment considerations.
Related issue or review references according to team conventions.
Do not use a merge message as a substitute for clear commits and review history.
Merge Strategy and Strategy Options
Git supports merge strategies selected with -s and strategy-specific options passed with -X.
For ordinary two-head merges, Git’s default strategy is designed for common branch integration and handles renames and many automatic combinations.
Examples you may encounter:
git merge -s ort feature/report
git merge -Xours feature/report
git merge -Xtheirs feature/report
The -Xours and -Xtheirs strategy options are not the same as discarding the entire other branch or current branch. They influence how conflicting hunks are favored during the merge while still incorporating non-conflicting changes.
Do not use strategy options to silence conflicts unless the policy is intentional and the result is tested.
The ours Strategy Is Different
This command:
git merge -s ours obsolete-branchcreates a merge result that uses the current tree while recording the other history as merged.
It is fundamentally different from -Xours. The ours strategy ignores the other tree’s content for the result. This is an advanced history operation and should not be used as a routine conflict shortcut.
Merge More Than One Branch
Git can perform an octopus merge with multiple heads in suitable non-conflicting cases:
git merge branch-a branch-b branch-cThis creates a merge with more than two parents when the strategy can resolve it automatically.
Octopus merges are generally used for combining several independent, already-tested topics. They are not suitable for complex conflict resolution because understanding a many-parent conflict becomes difficult.
Find Branches Already Merged
List local branches whose tips are reachable from HEAD:
git branch --mergedList branches not fully merged into HEAD:
git branch --no-mergedCheck relative to a specific branch:
git branch --merged main
git branch --no-merged mainThis is useful before branch cleanup.
A branch appearing under --merged main means its tip is reachable from main. It does not prove that the feature was integrated through a merge commit; fast-forwarding, rebasing, or equivalent changes can affect interpretation.
Find Branches Containing a Commit
List branches containing a selected commit:
git branch --contains <commit>List branches that do not contain it:
git branch --no-contains <commit>This helps answer questions such as:
Has the security fix reached the release branch?
Which maintenance branches contain this patch?
Can this temporary branch be removed without losing its tip?
Delete a Local Branch Safely
Delete a branch only when you are not currently on it:
git branch -d feature/reportThe lowercase -d performs a safety check and normally refuses to delete a branch that is not fully merged into an appropriate upstream or current history.
Force deletion:
git branch -D feature/report-D is equivalent to a forceful delete. It removes the branch reference even when commits may not be reachable elsewhere.
Before forcing:
git log --oneline main..feature/report
git branch --contains feature/report
git reflog show feature/report
If the work might still matter, create a backup tag or branch rather than deleting blindly.
Deleting a Branch Does Not Delete Commits Immediately
A branch name is a reference. Deleting it removes that reference, not necessarily the underlying commit objects immediately.
Commits remain available while reachable from another branch, tag, remote-tracking reference, or other reference.
Recently deleted local branch tips may also remain discoverable through reflogs for a limited period. However, unreachable objects can eventually be pruned.
Reflog is a recovery aid, not a permanent branch archive.
Local Branches and Remote-Tracking Branches
A local branch is a branch you can check out and commit on, such as:
main
feature/search
A remote-tracking branch records Git’s latest known state of a branch from a remote after fetching:
origin/main
origin/feature/search
You do not normally commit directly on origin/main. It moves when remote information is fetched.
Create a local branch based on a remote-tracking branch:
git switch -c feature/search --track origin/feature/searchIn many common cases, Git can infer the local name:
git switch feature/searchRemote publishing, upstream configuration, pushing, fetching, and deleting remote branches will be covered separately because they involve collaboration and server state.
What Is an Upstream Branch?
A local branch can be configured to track an upstream branch. This relationship helps commands report whether the local branch is ahead or behind and provides defaults for operations such as pull and push, depending on configuration.
Inspect tracking information:
git branch -vvSet an upstream for an existing local branch:
git branch --set-upstream-to=origin/feature/search feature/searchRemove it:
git branch --unset-upstream feature/searchAn upstream is not the same as a parent commit or merge base. It is branch configuration describing a related remote or local branch.
Merge vs Rebase at a High Level
Merging combines histories and can create a commit with multiple parents. Rebasing replays commits onto a new base and creates replacement commits.
Merge:
C---D
/ \
A---B---E---F---MRebase conceptually:
A---B---E---F---C'---D'Rebasing can produce a linear history, but it rewrites commit identifiers. Merging preserves the existing commits and records integration explicitly.
This article focuses on merging. Rebasing should be learned separately because conflict perspective, shared-history safety, and recovery differ.
Branch Naming Conventions
Useful names describe purpose:
feature/task-search
fix/login-timeout
hotfix/payment-callback
docs/local-setup
refactor/order-service
release/2.4
Avoid names such as:
test
new
final
branch2
john-work
changes
Good conventions are:
Short enough to type and read.
Specific enough to identify purpose.
Consistent across the team.
Compatible with repository and automation rules.
Free of private or sensitive information.
Some teams include ticket identifiers:
feature/APP-142-task-search
The exact style matters less than consistency and clarity.
Practical Feature Branch Workflow
1. Start from an Updated Integration Branch
git switch main
git statusWhen remotes are involved, fetch and update according to the team workflow before creating the branch.
2. Create the Feature Branch
git switch -c feature/task-search3. Make Focused Commits
git add -p
git diff --staged
git commit -m "Add task keyword matching"
4. Inspect the Feature
git log --oneline main..feature/task-search
git diff main...feature/task-search
5. Run Tests
# Use the project’s real test command
php vendor/bin/phpunit
6. Merge into the Target Branch
git switch main
git merge --no-ff feature/task-search
Use the merge mode required by the team. Some teams prefer fast-forward-only integration; others preserve merge commits.
7. Verify the Integrated Result
git status
git log --oneline --graph --decorate --all
Run the full relevant test suite on the merged result, not only on the feature branch.
8. Delete the Completed Local Branch
git branch -d feature/task-searchPractical Hotfix Workflow
Assume a feature branch is incomplete when an urgent production issue appears.
1. Preserve Incomplete Feature Work
Commit a coherent checkpoint or stash it according to team practice.
2. Switch to the Production Base
git switch main3. Create a Hotfix Branch
git switch -c hotfix/login-timeout4. Implement and Test the Fix
git add -p
git commit -m "Increase login provider timeout"
5. Integrate the Fix
git switch main
git merge --no-ff hotfix/login-timeout
6. Bring the Fix into Other Active Lines
Depending on the repository workflow, merge the updated base into the feature branch or apply the fix through another approved integration method.
git switch feature/large-change
git merge main
This prevents the active feature from reintroducing old behavior later.
Manual Run: Fast-Forward Merge
Initial History
A mainCreate a Branch
git switch -c feature/aCreate Two Commits
A---B---C feature/a
^
mainReturn to Main
git switch mainMerge
git merge feature/aResult
A---B---C main, feature/aNo merge commit is needed. The current branch pointer moves to C.
Manual Run: Three-Way Merge
Initial History
A---B mainFeature Work
C---D feature
/
A---B mainIndependent Main Work
C---D feature
/
A---B---E---F mainMerge While on Main
git switch main
git merge featureResult
C---D
/ \
A---B---E---F---M mainThe new merge commit M has the old main tip and the feature tip as parents.
Manual Run: Conflict and Resolution
Base File
Storage: localMain Changes It To
Storage: encrypted local databaseFeature Changes It To
Storage: cloudMerge Attempt
git switch main
git merge feature/cloud-storageConflict State
<<<<<<< HEAD
Storage: encrypted local database
=======
Storage: cloud
>>>>>>> feature/cloud-storage
Resolved Product Decision
Storage: encrypted local database with optional cloud synchronizationMark Resolved and Continue
git add app.txt
git diff --staged
git merge --continue
Verify
git status
git log --oneline --graph --decorate --all
Common Branching Mistakes
Creating a Branch from the Wrong Starting Point
Always inspect the current branch and recent history before creating a new branch.
Forgetting Which Branch Is Current
Run:
git branch --show-current
git status
before committing or merging.
Treating a Branch as a Folder Copy
A branch is a reference to history. Switching changes the working tree to match another history line.
Leaving Uncommitted Changes Before Merging
Pre-existing changes complicate conflict resolution and abort behavior.
Merging in the Wrong Direction
git merge other changes the current branch. Switch to the intended target first.
Resolving Conflicts by Keeping Only the Version That Compiles
A syntactically valid result can still violate requirements or silently remove behavior. Understand both changes and run tests.
Using ours or theirs Without Reviewing
Whole-file selection may discard valid work from one side.
Deleting an Unmerged Branch with -D
Force deletion can remove the easiest reference to unique commits.
Keeping Completed Branches Forever
Stale branches create noise and make it difficult to distinguish active work from historical leftovers.
Using --no-ff or --ff-only Without a Team Policy
Both choices shape history. Apply them intentionally and consistently.
Confusing a Local Branch with origin/branch
A remote-tracking branch records fetched remote state. It is not the normal local branch where you create commits.
Best Practices for Branches and Merges
Create branches from the correct, updated base.
Use descriptive and consistent branch names.
Keep one primary purpose per branch.
Create focused commits before integration.
Check the current branch before merging.
Start merges with a clean working tree.
Inspect unique commits and the three-dot diff before integration.
Run tests on both the feature and merged result.
Resolve conflicts based on intended behavior, not only text preference.
Review conflict resolutions before completing the merge.
Use force deletion only after proving unique work is no longer needed.
Delete completed short-lived branches to reduce clutter.
Document whether the team uses fast-forward, merge commits, squash merging, or rebasing.
Branch Command Reference
| Command | Purpose |
|---|---|
git branch | List local branches |
git branch -vv | Show branch tips and upstream relationships |
git branch name | Create a branch without switching |
git switch name | Switch to an existing branch |
git switch -c name | Create and switch to a branch |
git switch - | Return to the previous checkout location |
git switch --detach revision | Inspect a revision with detached HEAD |
git branch -m old new | Rename a local branch |
git branch -d name | Delete a safely merged local branch |
git branch -D name | Force-delete a local branch |
git branch --merged main | List branches merged into main |
git branch --no-merged main | List branches not merged into main |
git branch --contains commit | List branches containing a commit |
Merge Command Reference
| Command | Purpose |
|---|---|
git merge branch | Merge a branch into the current branch |
git merge --ff-only branch | Allow only a fast-forward update |
git merge --no-ff branch | Create a merge commit even when fast-forwarding is possible |
git merge --no-commit --no-ff branch | Prepare a merge commit and stop for inspection |
git merge --continue | Complete a merge after conflicts are resolved |
git merge --abort | Attempt to return to the pre-merge state |
git merge-base A B | Show a best common ancestor |
git mergetool | Open the configured merge-resolution tool |
git ls-files -u | List unmerged index entries |
git diff --check | Check for whitespace errors and conflict markers in changes |
Pre-Merge Checklist
The current branch is the intended merge target.
The working tree and index are clean.
The feature branch contains the intended commits.
The branch diff has been reviewed.
Relevant tests pass before integration.
The required merge mode is known: fast-forward, merge commit, or another approved method.
No unrelated local changes will interfere.
A recovery path exists for high-risk integration.
Post-Merge Checklist
No unresolved paths remain.
No conflict markers remain in tracked files.
The combined code passes tests and static checks.
The merge commit or fast-forward result is visible in history.
The application behaves correctly with both sets of changes.
The completed branch is safe to delete.
Other long-lived branches receive critical fixes when required.
Frequently Asked Questions
Does Creating a Branch Copy the Entire Project?
No. Git creates a lightweight reference to a commit. The working tree changes when you switch branches.
What Is the Difference Between git branch and git switch -c?
git branch name creates the branch without switching. git switch -c name creates it and checks it out.
Why Can Git Not Switch Branches?
Local changes may be overwritten by the target branch. Commit, stash, discard, or otherwise organize the changes safely before switching.
Which Branch Receives the Merge?
The currently checked-out branch receives the branch named in git merge.
What Is a Fast-Forward Merge?
It moves the current branch reference forward because the other branch is already a descendant and no divergent current-branch commits need combining.
Does --no-ff Always Create a Merge Commit?
It prevents fast-forwarding in a normal nontrivial branch merge and requests a merge commit. Other special cases and command conditions should be checked in the official documentation.
Why Did --no-commit Not Stop My Merge?
A fast-forward update creates no merge commit to pause before. Use --no-ff --no-commit when a reviewable merge commit is required.
Is a Merge Conflict a Git Error?
No. It means Git cannot safely choose the combined content automatically and requires a human decision.
Can I Abort a Conflict?
Yes, use git merge --abort. Restoration is most reliable when the working tree was clean before the merge.
Should I Always Choose Ours or Theirs?
No. Most real conflicts require a combined result. Selecting one whole side can discard necessary behavior.
Can I Delete a Branch After Merging?
Yes. Use git branch -d name after verifying the branch’s work is reachable from the intended target.
What Happens to Commits After Branch Deletion?
They remain while reachable from another reference. Unique unreachable commits may eventually be pruned, although local reflogs can provide temporary recovery opportunities.
What Is the Difference Between a Branch and an Upstream?
A branch is a movable reference to a commit. An upstream is configuration linking a local branch to another branch for status and default synchronization behavior.
Should I Merge or Rebase?
Both integrate lines of development differently. Merging preserves existing commits and can record integration with a merge commit. Rebasing creates replacement commits on a new base. Use the workflow defined by the team and avoid rewriting shared history casually.
Conclusion
Git branches are lightweight references that let development continue along independent lines. They make feature work, experiments, fixes, and releases easier to isolate without copying the project or immediately changing stable code.
Merging brings those lines together. A fast-forward moves a branch reference when no divergence exists. A three-way merge combines changes using the common ancestor and both branch tips. When Git cannot determine the correct combined content, it preserves the competing versions and requires a deliberate conflict resolution.
A safe integration workflow begins before the merge command: use a clean working tree, confirm the target branch, review unique commits and diffs, and run tests. During conflicts, understand both intentions, stage the correct combined result, and verify the final application rather than choosing one side automatically.
After integration, inspect the graph, confirm that the branch tip is reachable from the intended target, and remove completed short-lived branches. The result is not only working code but a history that future developers can understand.
The next article will focus on Git stash and tags: temporarily setting work aside, recovering stashed changes, creating release markers, comparing lightweight and annotated tags, and publishing tags safely.
Official References and Further Reading

