Key Takeaways
- Choose Git workflows around release cadence, contributor access, review requirements, and integration frequency—not team size alone.
- Branches isolate unfinished work; automated checks and enforced review policies protect the shared branch.
- Short-lived feature branches can support trunk-based development. Gitflow adds separate development and release paths that need deliberate maintenance.
- Forks let contributors propose changes without write access to the canonical repository.
Git workflows define how your team develops, reviews, integrates, and releases changes. The right workflow fits your release model and access requirements while keeping integration manageable; adding more branches isn't automatically an improvement.
Compare centralized, feature-branch, Gitflow, and fork-based approaches, but don't treat them as mutually exclusive. For example, you can combine forks with short-lived feature branches and frequent integration.
What Is Git, and What Does a Workflow Add?
Git is distributed: developers create commits locally and exchange changes with other repositories. A shared central remote is a collaboration convention, not a Git requirement. A full clone gives you repository history locally, although shallow clones deliberately limit that history.
The basic operations do different jobs: fetch downloads remote changes, pull fetches and then integrates changes into the current branch, and push updates a remote repository. Merge and rebase are integration operations, not substitutes for sharing commits.
A branching strategy defines branch roles and integration paths. A workflow also defines permissions, review, checks, and release responsibilities. Pull requests and merge requests are hosting-platform collaboration features, not Git primitives: GitHub flow, GitLab merge requests, and Bitbucket pull requests provide those review processes.
Comparing Common Git Workflows
Centralized Workflow
In a centralized workflow, everyone collaborates through a shared remote branch, usually named main. Developers commit locally, integrate concurrent changes, and push back to that branch. Git itself remains distributed.
Suppose Developer A and Developer B start from the same commit. A pushes first. If B's push would replace A's history rather than extend it, Git normally rejects it as a non-fast-forward update. B must fetch, integrate A's work, resolve any conflicts, and test before pushing again.
This is a reasonable choice when direct pushes are acceptable and coordination is straightforward. If independent approval must happen before integration, use a review branch rather than relying on everyone remembering an informal rule.
Feature Branch Workflow
Create a branch for a focused feature, fix, or maintenance task, then propose its integration through review. The branch keeps unfinished commits separate from the target branch, but it doesn't prevent conflicts or guarantee that the eventual merge works.
Keep each branch small enough to review promptly. Prefer one behavior change over a mixture of formatting, dependency upgrades, and unrelated fixes. Git's own workflow guidance recommends small logical changes because they make review and later investigation easier.
Before merging, require the relevant build, tests, and review. For JavaScript projects, include linting checks; use focused unit tests to cover changed behavior. Configure enforcement rather than treating a green-looking pull request as sufficient.
Trunk-Based Development
Trunk-based development centers work on one shared integration branch, called trunk, main, or mainline. Developers integrate small batches at least daily; short-lived branches and quick reviews can fit this model. The important distinction is how long changes remain isolated, not whether branches exist.
A feature branch that lasts a few hours can support trunk-based development. A branch that holds an entire feature for weeks follows a different integration pattern. Prefer the former when frequent delivery matters, and split large work into independently safe increments.
Continuous integration means frequently combining work and checking that the shared code still works—not merely running a pipeline on isolated branches. Prioritize fast feedback and fixing a broken shared build.
Gitflow Workflow
Gitflow assigns explicit roles to branches. Using main for the production-history branch, which the original model calls master, its core paths are:
feature: branches from and merges intodevelop.develop: integrates work for the next release.release: branches fromdevelop; finishes inmainanddevelop, with the release tagged.hotfix: branches frommain; returns the fix tomainanddevelop. An active release branch also needs the fix.
These are branch roles, not separate repository forks. See the original Gitflow model for the integration rules.
Consider Gitflow when you need an explicit release-preparation phase while development continues. Don't choose it just because a project is complex: separate integration lines create more synchronization work, whereas frequent small integrations avoid large, delayed merge events.
Forking Workflow
A fork is a separate repository associated with the original project on the hosting platform. Contributors push to their own forks and propose changes to the canonical repository through a pull request or merge request. This is useful when contributors don't have direct write access to the official repository.
Canonical repository (upstream)
| fetch changes
v
Contributor's local clone
| push topic branch
v
Contributor's fork (origin)
| pull request / merge request
v
Canonical repository: review + checks + integrationWhen you clone your fork, origin normally points to that fork. Add a remote conventionally named upstream for the canonical repository. Run this Bash setup inside that clone, assuming upstream and the new branch don't already exist and the canonical branch is main:
# Run one command at a time; stop if a command fails.
read -r -p "Canonical repository Git URL: " UPSTREAM_URL
git remote add upstream "$UPSTREAM_URL"
git fetch upstream
git switch -c fix/contribution upstream/mainMake and commit your change, push the topic branch explicitly to origin, and open the contribution against the canonical repository. Fetch from upstream again when you need to incorporate newer project changes; fetching alone doesn't merge them into your branch.
Which Git Workflow Is Right for Your Team?
Use this table as a selection guide, not a ranking. Forking answers an access question; trunk-based development answers an integration-frequency question.
| Approach | Consider it when | Main tradeoff |
|---|---|---|
| Centralized | Direct shared-branch pushes are acceptable | Review-before-integration needs another mechanism |
| Feature branches | Each change needs focused review | Long branch lifetimes delay integration |
| Trunk-based | Frequent integration is a priority | Requires small changes and fast feedback |
| Gitflow | You need separate development and release-preparation paths | More integration paths to maintain |
| Fork-based | Contributors shouldn't receive canonical-repository write access | Contributors and maintainers coordinate across repositories |
The underlying branch and repository behaviors are described in Git's distributed-workflow guidance, Gitflow's original model, and DORA's integration guidance.
Before choosing, answer these questions:
- Release cadence: Do you prepare scheduled releases, or deliver whenever a change is ready?
- Deployment frequency: Can your checks and approvals keep up with the intended delivery rate?
- Review requirements: Who must approve, and how quickly can they respond?
- Branch lifetime: Can you integrate incremental work instead of waiting for a whole feature?
- Access: Who can push to the canonical repository?
- Supported versions: Do older release lines need fixes? Document which branches receive each fix and who owns the transfer.
My starting recommendation for a team delivering one current application is protected main, short-lived topic branches, and required review and checks. Add release branches or forks when a concrete requirement justifies them.
A Short-Lived Feature Branch, End to End
This example adds a documentation file in an existing clone. Assume a clean working tree, configured Git identity, an existing local main, push access to origin, and no existing file or branch with the names below. The team's policy permits merging main into topic branches and integrates pull requests with merge commits.
Create, Commit, Synchronize, and Push
# Run one command at a time; stop if a command fails.
git switch main
git pull --ff-only origin main
git switch -c docs/contribution-note
mkdir -p docs
printf '%s\n' 'Keep changes small and reviewable.' > docs/contribution-note.md
git add docs/contribution-note.md
git commit -m "docs: add contribution note"
git fetch origin
git merge --no-edit origin/main
# Run the repository's documented checks before pushing.
git push -u origin docs/contribution-noteswitch -c creates and switches to a branch. add stages the file, and commit -m records the staged change with a message.
pull --ff-only refuses to reconcile divergent local history. The later merge incorporates fetched main changes without rewriting existing commits; --no-edit accepts an automatically generated merge message. push -u publishes the branch and sets its upstream tracking relationship.
Review, Integrate, and Clean Up
- Open a pull request targeting
main. Explain the purpose and how you validated the change. - Address review feedback and failed checks. After synchronization or edits, rerun the relevant checks.
- Merge only after the required approvals and checks. Delete the remote topic branch through the host, then update your local checkout and remove its topic branch.
GitHub flow documents this branch, review, merge, and cleanup cycle. Branch protection can enforce approvals and status-check requirements.
git switch main
git pull --ff-only origin main
git branch -d docs/contribution-notebranch -d deletes a branch only when it is fully merged into its configured upstream, or into HEAD when no upstream is configured. If deletion is refused, inspect the history rather than immediately forcing it.
Synchronization, Conflicts, and Safe Recovery
Agree on Merge Versus Rebase
Merge preserves existing commits and joins development histories. Rebase reapplies commits on a new base, normally creating new commit identities, and can produce a linear history. Neither eliminates integration conflicts.
Don't rebase shared, published work that collaborators may have based changes on unless your team explicitly coordinates that rewrite. For the mechanics and tradeoffs, read git merge versus git rebase.
Keep Long-Lived Work Deliberate
If a branch must remain open, assign an owner and integration checkpoints. Synchronize before review and when upstream changes affect its behavior; don't save everything for release day. For maintained release lines, track fixes explicitly and avoid merging unrelated newer features into an older supported version.
Address conflicts promptly and talk to the people changing the same code. Resolve the intended behavior, not just the conflict markers, then test the combined result. Git can stop a merge for manual resolution; its merge tools can help inspect conflicting files.
Commit locally in logical checkpoints, push when you need collaboration or remote sharing, and integrate when the agreed gates are satisfied. These are separate decisions. For a bad change already shared, git revert records a new commit reversing an earlier change rather than removing that commit from history; reverting a merge requires additional care.
Enforce the Workflow, Including Agent Contributions
Protect integration branches with required reviews and checks, restrict bypass permissions, and decide whether branches must be current before merging. On GitHub, protected-branch settings support these controls; merely configuring a test job doesn't make it a merge requirement.
AI-Assisted Workflow
Give an AI coding agent one bounded task on its own branch. For parallel local work, a Git worktree provides a separate working directory attached to the same repository.
Require small, reviewable pull requests, CI as the integration gate, and human review before merge. Check generated code and tests yourself rather than trusting an agent's completion summary; GitHub's responsible-use guidance likewise calls for reviewing and testing agent-generated changes.
Common Mistakes
- Choosing by team size alone: Start with release, access, and review requirements.
- Calling branch pipelines continuous integration: Check whether work actually reaches the shared branch frequently.
- Using branches as a stability guarantee: Enforce checks and approvals at integration.
- Changing the workflow without a migration plan: Agree on the new target branch, finish or retarget open work, update automation, and pilot the change before expanding it.
If your workflow no longer fits, simplify one source of friction at a time. Measure branch age, review delays, and integration failures rather than counting branches as a proxy for success.
FAQ
What is the difference between a Git workflow and a branching strategy?
A branching strategy defines branch roles and integration paths. A Git workflow also covers contributor permissions, review, automated checks, and release responsibilities.
Is Gitflow still recommended?
Gitflow is an option when separate development and release-preparation paths meet a real requirement. It isn't a default recommendation for complex projects; consider short-lived branches and trunk-based development when frequent integration is the priority.
What is the difference between trunk-based development and feature branches?
Feature branches isolate a set of changes, while trunk-based development emphasizes integrating small batches into a shared branch at least daily. Short-lived feature branches can fit trunk-based development; long-lived feature branches do not follow that integration pattern.
How do you keep a feature branch up to date with the main branch?
Fetch the remote changes, then merge the remote-tracking main branch into your feature branch or rebase according to team policy. Resolve conflicts and rerun checks; don't rewrite shared history without coordinating with collaborators.
How do I handle merge conflicts effectively?
Address conflicts promptly and communicate with collaborators about overlapping changes. Inspect both changes, resolve the intended behavior, and test the integrated result before completing the merge or rebase.
Further Reading
- Pro Git: Distributed Workflows — shared repositories and contributor integration.
- GitHub flow — branch, review, merge, and cleanup.
- DORA: Trunk-based development — small batches and frequent integration.
- The original Gitflow model — branch roles and release mechanics.
- GitHub: Syncing a fork — incorporating canonical-repository changes.
- Pro Git: Rebasing — history rewriting and collaboration risks.
- GitHub: Protected branches — enforceable integration requirements.
- Git's workflow guidance — logical commits and maintained branch lines.
