Git Branches with Different Commit Histories
To keep a long-running dev branch with a detailed commit history while periodically merging changes into a main branch with a cleaner, simplified history, using git merge --squash is a viable approach, but whether it's the best choice depends on your goals and workflow.
Goals and Assumptions
- Long-running
devbranch: You wantdevto retain its full, detailed commit history for development, debugging, and collaboration. - Clean
mainbranch: You wantmainto have a simplified history, ideally with fewer commits (e.g., one commit per feature or release). - Merging from
devtomain: You plan to periodically integrate changes fromdevintomainwithout bringing the fulldevhistory.
Using git merge --squash
The --squash option in git merge combines all changes from the source branch (dev) into a single commit on the target branch (main), discarding the individual commit history from dev. This aligns with your goal of keeping a clean main branch.
Workflow with --squash
-
Work on
dev:-
Make commits on
devas usual, building up a detailed history.These commands demonstrate typical development work with detailed commit messages on the dev branch:
git checkout dev
git commit -m "Add feature X"
git commit -m "Fix bug in feature X"
git commit -m "Refactor feature X"
-
-
Merge to
mainwith--squash:-
When ready to integrate changes into
main, use--squashto collapse alldevchanges since the last merge into a single commit.This sequence shows how to squash multiple dev commits into a single main commit:
git checkout main
git merge --squash dev
git commit -m "Add feature X with all changes" -
This creates a single commit on
maincontaining all changes fromdevsince the branches diverged, without includingdev's commit history.
-
-
Continue Development:
-
After squashing,
mainanddevhave the same content but different histories. To keepdevup-to-date withmain(especially ifmainreceives other changes), rebase or mergemainback intodev:This command keeps dev synchronized with main using rebase to maintain a linear history:
git checkout dev
git rebase main-
Alternatively, merge
mainintodevto avoid rewriting history:This approach preserves the commit history without rewriting, which is safer for shared branches:
git checkout dev
git merge main
-
-
Pros of Using --squash
- Clean
mainHistory: Each merge results in a single, meaningful commit onmain, making the history easier to read for releases or audits. - Preserves
devHistory: The detailed commit history indevremains intact, useful for debugging or tracking incremental changes. - Simple Workflow: Straightforward for solo developers or small teams, as it avoids complex rebasing.
- No History Pollution:
maindoesn't inherit the granular, potentially messy commits fromdev.
Cons of Using --squash
- Loss of Granular History in
main: If you need to trace specific changes inmain(e.g., for debugging), the squashed commit lacks the detailed context preserved indev. - Manual Commit Messages: You must write a new commit message for each squashed commit, which can be tedious if merging frequently.
- Potential for Conflicts: If
maindiverges significantly (e.g., due to hotfixes or other merges), rebasing or mergingmainback intodevmay lead to conflicts. - Collaboration Challenges: If
devis shared with a team, squashing can make it harder to track changes when reviewingmain, as the commit history is condensed.
Alternatives to --squash
If --squash doesn't fully meet your needs, consider these alternatives for merging dev into main while maintaining different histories:
1. Regular Merge with Curated dev Commits
-
Approach: Clean up
dev's history before merging intomainusinggit rebase -ito squash or rewrite commits into a more concise form, then perform a regular merge.This workflow allows you to curate your commit history before merging:
git checkout dev
git rebase -i <commit-before-dev-diverged>
# Squash or edit commits in the interactive rebase interface
git checkout main
git merge dev -
Pros:
- Allows you to curate
dev's history before merging, retaining some meaningful commits inmain. - Maintains a direct connection between
devandmaincommits, making it easier to trace changes.
- Allows you to curate
-
Cons:
- Rewrites
dev's history, which can disrupt collaboration ifdevis shared. - Requires manual effort to organize commits during rebase.
- Rewrites
2. Feature Branches with Squashing
- Approach: Instead of squashing the entire
devbranch, create feature branches offdevfor specific tasks. Squash these feature branches when merging intomain, keepingdevas the long-running integration branch.git checkout -b feature-x dev
git commit -m "Work on feature X"
git checkout main
git merge --squash feature-x
git commit -m "Add feature X"
git checkout dev
git merge feature-x # Optionally merge feature back into dev - Pros:
- Keeps
dev's history intact while allowing granular squashing per feature. - Scales better for teams, as feature branches isolate changes.
- Keeps
- Cons:
- Adds overhead of managing multiple feature branches.
- Requires discipline to keep
devand feature branches in sync.
3. Cherry-Pick for Curated History
- Approach: Selectively cherry-pick specific commits from
devintomainto build a curated history.git checkout main
git cherry-pick <commit-hash> - Pros:
- Gives precise control over which commits appear in
main. - Avoids rewriting
dev's history.
- Gives precise control over which commits appear in
- Cons:
- Time-consuming for large numbers of commits.
- Risk of missing important changes if not carefully managed.
4. Gitflow-Inspired Workflow
- Approach: Use a release branch to stage changes from
devformain. Squash or rebase the release branch before merging intomain.git checkout -b release-x.x dev
git rebase -i <commit-before-dev-diverged>
git checkout main
git merge release-x.x
git checkout dev
git merge main - Pros:
- Provides a clear separation between development, staging, and production.
- Allows testing of squashed changes before merging into
main.
- Cons:
- More complex, with additional branches to manage.
- Still requires rebasing or squashing for a clean
mainhistory.
Recommendation
For your use case—a long-running dev branch with periodic merges into a clean main—using git merge --squash is a good choice if:
- You prioritize a very clean
mainhistory with one commit per major update or feature. - You're comfortable writing summary commit messages for squashed commits.
- Your team is small or you're working solo, minimizing collaboration issues.
However, consider these adjustments to optimize the workflow:
- Use Feature Branches: For larger features, work on short-lived feature branches off
dev, squash them when merging intodevormain, and keepdevas an integration branch with detailed history. This balances granularity indevand cleanliness inmain. - Automate Commit Messages: When squashing, use tools like
git log --onelineto summarizedev's changes for themaincommit message. - Regularly Sync
devwithmain: After each squash merge, mergemainback intodev(git checkout dev; git merge main) to avoid conflicts and keepdevup-to-date. Avoid rebasingdevif it's shared with others, as it rewrites history. - Tag Releases: After merging into
main, tag the commit (e.g.,git tag v1.0) to mark releases, making it easier to track milestones.
Example Workflow
- Work on
dev:git checkout dev
git commit -m "Add feature X"
git commit -m "Fix bug in feature X" - Squash-merge into
main:git checkout main
git merge --squash dev
git commit -m "Add feature X with all changes"
git tag v1.0 - Sync
devwithmain:git checkout dev
git merge main - Continue development on
dev.
Additional Considerations
- Collaboration: If
devis shared, communicate with your team about squash merges to avoid confusion, as squashed commits inmainwon't directly referencedev's commits. - Backup: Before squashing, create a backup branch (
git branch dev-backup dev) to preserve history in case of mistakes. - Conflict Management: If conflicts arise when merging
mainintodev, resolve them carefully and test thoroughly. - Tools: Use
git log --graph --oneline --allto visualize the diverging histories ofdevandmain.
Resources
For further details, refer to these resources:
- Pro Git Book: Chapter on Rebasing and Merging (https://git-scm.com/book/en/v2/Git-Branching-Rebasing).
- Atlassian Git Tutorial: Merging vs. Rebasing, including
--squash(https://www.atlassian.com/git/tutorials/merging-vs-rebasing). - Learn Git Branching: Interactive squash and merge exercises (https://learngitbranching.js.org/).
If you anticipate frequent merges or complex features, consider experimenting with feature branches or a release branch to reduce the risk of conflicts and improve traceability.