Version control · Git · Workflow
Version Control, Git and Branching Strategies: Which Workflow Fits Which Team
Every team that writes code needs to answer three questions: where does the history live, how do we work on things in parallel, and how does finished work reach users? Version control answers the first, Git is the tool most teams pick, and a branching strategy answers the other two. This article covers all three, with diagrams, and ends with the tools you can use to drive Git every day.
- A version control system (VCS) records every change to your files, who made it and why, so you can go back, compare and work in parallel.
- There are three generations: local, centralized (Subversion, Perforce) and distributed (Git, Mercurial). Git is the default today.
- Git stores snapshots, not diffs, and moves work through three areas: working directory, staging area, repository.
- Alternatives exist and some are better for special cases: Perforce or Unity Version Control for huge binary assets, Fossil for tiny self-contained projects, Jujutsu as a friendlier front end to Git.
- There is no single best branching strategy. Pick by how you release: GitHub Flow or trunk-based for continuous delivery, Git Flow or release branches for versioned software, fork and pull request for open source.
- Whatever you choose: short-lived branches, small pull requests, a protected main branch, and automated tests on every change.
- Tools: the command line is the foundation; add a GUI or your IDE for the visual parts, and a hosting service for sharing and review.
Contents
1. Version control overview
Without version control, a project folder fills up with report_final.docx, report_final_v2.docx and report_REALLY_final.docx. With two people editing, someone overwrites someone else. With a bug in production, nobody can say which change caused it.
A version control system fixes this by keeping a complete, searchable history of a project. Each saved state is a commit with an author, a time and a message. The system lets you:
- Go back. Restore any file, or the whole project, to how it was last Tuesday.
- Compare. See exactly which lines changed between two versions, and who changed them.
- Work in parallel. Two people (or one person with two ideas) change the same project at once, then merge the results.
- Find bugs. Ask which commit introduced a problem, and read why it was made.
- Release with confidence. Tag the exact code that shipped as version 2.4.
Analogy. Think of the edit history in a shared document, but for a whole folder of files, with the power to take a copy, try something risky in it, and then fold the good parts back in.
Three generations of version control
| Kind | How it works | Strength | Weakness |
|---|---|---|---|
| Local | Versions are stored in a database on your machine. | Simple; no server. | No team work; one disk failure loses everything. |
| Centralized | One server holds the history. You check out files, edit and commit back. | Easy to understand; fine-grained access control; file locking. | Needs the network to commit or view history; the server is a single point of failure. |
| Distributed | Every clone holds the full history. You commit locally, then share with push and pull. | Fast, offline, cheap branching, many backups. | More concepts to learn; large binary files and huge monorepos need extra care. |
Words you will meet in every VCS
| Term | Meaning |
|---|---|
| Repository | The project plus its whole history. |
| Commit | A saved state of the project, with a message. |
| Branch | A line of development that can move on independently. |
| Merge | Combining the changes of one branch into another. |
| Clone | A full copy of a repository on your machine. |
| Tag | A permanent name for one commit, such as v2.4.0. |
2. Git: how it works
Git (official site: git-scm.com) was written in 2005 by Linus Torvalds to manage the Linux kernel, after the tool the kernel team had been using became unavailable to them. It was designed to be fast, to handle thousands of parallel branches, and to need no central server. It is now the default choice, used by most open source projects and most companies.
Snapshots, not differences
Older systems stored each version as a list of changes to a file. Git thinks differently: every commit is a snapshot of the whole project at that moment (files that did not change are stored once and shared). A commit points to its parent, so the history is a chain, and a merge is a commit with two parents. Each commit has a unique ID, a hash such as 9fceb02. Because the ID is computed from the content and the parent, history cannot be changed silently.
The four places your work lives
- Working directory: the files you see and edit.
- Staging area (index): a draft of your next commit. You choose exactly what goes in, so one commit can hold one idea even if you changed five things.
- Local repository: the
.gitfolder with all commits. Committing is instant and needs no network. - Remote: a copy on a server (GitHub, GitLab and so on) that the team shares.
A branch is only a label
In Git a branch is a small pointer to a commit. Creating one costs almost nothing, which is why Git encourages branching for every task. HEAD says which branch you are on.
3. The commands you use every day
# start
git clone https://github.com/you/project.git
git switch -c feature/login # create and move to a new branch
# save work
git status # what changed?
git add src/Login.kt # stage one file (or: git add -p to pick pieces)
git commit -m "Add login form validation"
# share and update
git fetch # download new commits, change nothing
git pull --rebase # update your branch on top of the remote
git push -u origin feature/login # publish the branch
# combine
git switch main
git merge --no-ff feature/login # or merge through a pull request
# look around
git log --oneline --graph --all
git diff # unstaged changes
git blame src/Login.kt # who changed each line, and in which commit
Undoing things safely
| Situation | Command | Safe on shared history? |
|---|---|---|
| Discard edits in a file | git restore file | Yes (but the edits are gone) |
| Unstage a file | git restore --staged file | Yes |
| Fix the last commit message or add a forgotten file | git commit --amend | Only before you push |
| Undo a pushed commit | git revert <commit> (adds a new opposite commit) | Yes |
| Move the branch back and drop commits | git reset --hard <commit> | No, not after pushing |
| Set work aside for a moment | git stash and git stash pop | Yes (local only) |
| Copy one commit to another branch | git cherry-pick <commit> | Yes |
| Find the commit that broke something | git bisect | Yes (read only) |
| “I lost a commit!” | git reflog, then git switch -c rescue <id> | Yes |
Git rarely destroys committed work. git reflog remembers where your branches were for a few months, so most “disasters” are recoverable if you have committed.
Two files belong in every repository. A .gitignore lists what Git must not track (build output, node_modules, IDE settings, and above all secrets such as .env). A README.md tells a newcomer what the project is and how to run it. If a secret is ever committed, treat it as leaked and rotate it; deleting it in a later commit does not remove it from history.
4. Alternatives to Git
Git is the right default, but it is not the only option, and knowing the others helps you understand Git’s design and spot the cases where it is a poor fit.
| System | Type | Best known for | Consider it when |
|---|---|---|---|
| Subversion (SVN) | Centralized | Simple model, one revision number for the whole repo, path-based permissions. | You maintain an existing SVN project, or non-programmers need a simple “check out, edit, commit” flow. |
| Perforce Helix Core (P4) | Centralized | Huge repositories, large binary files, exclusive file locking, fine-grained control. | Game studios and hardware teams with gigabytes of art, models and builds. |
| Unity Version Control (formerly Plastic SCM) | Distributed and centralized | Handles big binaries, has a visual branch explorer and locking, friendly to artists. | Unity projects, mixed teams of artists and programmers. |
| Mercurial (hg) | Distributed | Similar power to Git with a more consistent, simpler command set. | You inherit a Mercurial repository. New projects rarely choose it, as most hosts now focus on Git. |
| Sapling | Distributed | Built at Meta for very large monorepos; stacked commits; can work with Git repositories. | You want to try stacked-commit workflows against a Git host. |
| Jujutsu (jj) | Distributed | Stores data in a Git repository, but treats your working copy as a commit and makes rewriting history and resolving conflicts gentler. | You like Git’s ecosystem but find its commands and the staging area awkward. Teammates can keep using plain Git. |
| Fossil | Distributed | One small program and one file per repository that also includes a wiki, bug tracker and web UI. Made for SQLite. | A small, self-contained project with no need for GitHub-style hosting. |
| Pijul | Distributed | A research-driven design based on patches rather than snapshots, with a different approach to merging. | You are curious about alternatives to the snapshot model. It has a small community. |
Older systems you may still meet in legacy code bases: CVS, Visual SourceSafe (retired) and TFVC, the centralized option in Azure DevOps (Microsoft recommends Git for new projects).
- Fast and offline: nearly every action is local.
- Cheap branches make the pull request workflow natural.
- A huge ecosystem: hosting, CI services, IDE support, tutorials and people who already know it.
- Free and open source, with no vendor tied to the data.
- Large binary files (video, textures, builds): every version is stored in history. Use Git LFS, or a tool like Perforce.
- Exclusive locks: two people cannot merge a binary scene file. Git LFS has locking, but it is not as smooth as Perforce.
- Gigantic monorepos: clones become slow. Partial clone, sparse checkout and tools like Scalar help.
- Learning curve: the commands are many and the names are not always intuitive.
5. Branching and merging basics
A branching strategy is a team agreement about which branches exist, what each is for, and how work flows between them. To compare strategies you first need the three ways to bring a branch back, and a few guard rails.
Three ways to integrate a branch
| Method | What happens | History looks like | Use it when |
|---|---|---|---|
| Merge commit | Adds a commit that joins two histories. | Branches and joins are visible. Nothing is rewritten. | You want a faithful record, or the branch is shared by several people. |
| Squash merge | All commits of the branch become one commit on the target. | One commit per pull request on main. Easy to read and to revert. | Branch commits are messy (“wip”, “fix typo”). The most common choice for small PRs. |
| Rebase | Replays your commits on top of the latest target, giving them new IDs. | A straight line, no merge commits. | Tidying your own branch before review. Never rebase commits others already pulled. |
Guard rails
- Protect the main branch. Require a pull request, at least one review and passing CI. Block force-pushes.
- Name branches so they explain themselves:
feature/login-form,fix/crash-on-rotate,release/2.4,hotfix/payment-timeout. Many teams add a ticket number. - Write good commit messages: a short summary line in the imperative (“Add retry to upload”), then a body that explains why.
- Tag releases with semantic versions:
v2.4.1.
6. Branching strategies
Three strategies cover most teams. Each is a different answer to one question: how long may work stay off the main line? The shorter the answer, the simpler the process and the easier the merges, but the more you rely on tests and feature flags.
A. GitHub Flow: one main branch, short feature branches
Everything on main is deployable. For each task you branch from main, commit, open a pull request, get review and passing tests, merge, and deploy.
- Web apps and services with one live version, deployed continuously.
- Small and mid-sized teams, and anyone new to branching.
- Supporting several released versions at once.
- Code that cannot reach users the moment it merges (app stores, scheduled releases) unless you add release tags or branches.
B. Git Flow: structured branches for versioned releases
Introduced by Vincent Driessen in 2010, Git Flow uses long-lived main (only released code, tagged) and develop (the next release being assembled). Short feature/* branches come off develop, release/* branches stabilise a version, and hotfix/* branches patch production directly.
- Software shipped as numbered versions: desktop apps, libraries, firmware, on-premise products.
- Teams that must maintain more than one released version, or run a formal QA phase.
- Continuous delivery: develop and main drift apart, and long feature branches make painful merges.
- Ceremony. For a web app that deploys daily it is usually too much; its author has said as much himself.
C. Trunk-based development: everyone integrates into one trunk
Developers commit to main (the trunk) at least daily, either directly or through very short-lived branches that last hours. Unfinished features are hidden behind feature flags so incomplete code can be merged and still stay off for users. When a release is needed you tag the trunk, or cut a short release/* branch that receives only bug fixes cherry-picked from the trunk.
- Teams with strong automated tests and CI who want to release often.
- Large organisations and monorepos: it avoids merge debt across many teams.
- It is the practice most associated with high-performing delivery teams in the DORA research.
- Teams without reliable tests or fast CI: a bad commit hurts everyone at once.
- Needs discipline with feature flags, which must be cleaned up later.
Other patterns worth knowing
- Fork and pull request. Contributors copy (fork) the project, work in their copy, and propose changes by pull request. The standard for open source, where you cannot give everyone write access.
- GitLab Flow. GitHub Flow plus environment branches (
main→staging→production) or release branches. A middle path when you cannot deploy straight from main. - Release branches per supported version (
release/1.x,release/2.x) with fixes cherry-picked back. This is how the Linux kernel, language runtimes and most libraries keep long-term support.
7. Choosing a strategy for your situation
Start from how your software reaches users, not from what is popular.
Situation by situation
| Situation | Recommended approach | Why, and what to watch |
|---|---|---|
| Solo project or learning | main plus a short branch per idea. Tag the versions you finish. | Branching is free and lets you throw away experiments. No ceremony needed. |
| Small team, web app, deploy daily | GitHub Flow with squash merge, CI required, auto-deploy on merge. | Keep PRs small (under a few hundred lines). Use feature flags for risky work. |
| Mature team with solid CI, many deploys a day | Trunk-based development with feature flags. | Needs fast tests, flag clean-up and a rule that a broken trunk is fixed first. |
| Mobile app (store review) | Trunk-based or GitHub Flow, plus a release/x.y branch cut for each store build. Fix bugs in main, cherry-pick to the release branch. | The live app is weeks old, so you must be able to patch exactly that version. Remote config and flags help. |
| Library or SDK with several supported versions | Trunk or develop for the next version, plus maintenance branches release/1.x, release/2.x. Tag every release. | Back-port security fixes with cherry-pick. Document which versions are supported. |
| Scheduled releases with a QA phase | Git Flow, or GitHub Flow with a release branch frozen for testing. | Keep the freeze short. Merge fixes back to main right away. |
| Production is on fire | Branch hotfix/* from the exact production tag, fix, test, release, then merge into main too. | Forgetting the merge back is how the same bug returns in the next release. |
| Open source | Fork and pull request; maintainers merge. Protect main; use labels and a CONTRIBUTING.md. | Ask contributors to rebase on the latest main. Use squash merge to keep history clean. |
| Large refactor or migration that takes weeks | Prefer branch by abstraction: hide the new path behind a flag and merge in small steps. If you must use a long branch, merge main into it every day. | The longer a branch lives, the more painful the final merge. Time-box it. |
| Several teams in one monorepo | Trunk-based, with code owners, path-based CI and sparse checkout. | Long-lived team branches turn into integration crises. |
| Regulated industry (audit, sign-off) | GitHub Flow or GitLab Flow with protected branches, required reviewers, signed commits and linked tickets. | The Git history becomes the audit trail. Never force-push to shared branches. |
| Several environments (dev, staging, production) | One build promoted through environments. Avoid separate code per environment. | Environment branches that drift apart cause “works on staging” bugs. Prefer config over code differences. |
If you are unsure, begin with GitHub Flow: protected main, short branches, small pull requests, squash merge, CI on every PR, tags for releases. Add a release/* branch only when you need to patch an old version, and move toward trunk-based development as your tests and feature flags mature.
8. Software for Git operations
Git itself is a command-line program that keeps data in a .git folder. Everything else is a layer on top of it. Pick one tool from each layer that you enjoy; they all work on the same repository, so you can mix them.
Clients
| Tool | Kind | Platforms | Good for |
|---|---|---|---|
| git (command line) | CLI, the reference | All | Everything. Needed for scripts, servers and fixing odd states. Learn it first. |
GitHub CLI (gh), GitLab CLI (glab) | CLI for the host | All | Create pull requests, review, check CI and manage issues without leaving the terminal. |
| VS Code Source Control | Editor built-in | All | Stage by line, inline diffs and conflict editor. Extensions such as GitLens add blame and history. |
| JetBrains IDEs (IntelliJ, Android Studio, PyCharm) | IDE built-in | All | Excellent diff and three-way merge, local history, shelve changes, per-line history. |
| Xcode, Visual Studio | IDE built-in | macOS, Windows | Everyday commit and branch work inside the platform IDE. |
| GitHub Desktop | GUI, free | macOS, Windows | The easiest start for beginners using GitHub. |
| Sourcetree | GUI, free | macOS, Windows | Visual branch graph; works with any host. |
| Fork | GUI, paid licence | macOS, Windows | Fast, clean interface, interactive rebase and conflict tools. |
| GitKraken | GUI, free tier and paid | All | Polished commit graph, integrations with hosts and issue trackers. |
| Tower | GUI, paid | macOS, Windows | Strong undo and guidance for less experienced users. |
| SmartGit | GUI, free for non-commercial use | All | Feature-rich, includes SVN support. |
| TortoiseGit, Git Extensions | GUI, free | Windows | Explorer integration for Windows users. |
| lazygit, gitui | Terminal UI, free | All | Keyboard-driven staging, rebase and branch work over SSH or in a terminal you already live in. |
Hosting services
| Service | Notes |
|---|---|
| GitHub | The largest community; pull requests, Actions CI, Pages, code search. Default for open source. |
| GitLab | All-in-one DevOps platform with built-in CI/CD and issue boards. Can be self-hosted. |
| Bitbucket | Atlassian product; tight integration with Jira and Pipelines. |
| Azure DevOps Repos | Part of Microsoft’s suite with boards and pipelines; common in Microsoft-centred companies. |
| Gitea, Forgejo | Lightweight, self-hosted, open source. Codeberg runs on Forgejo. |
| AWS CodeCommit | Closed to new customers in 2024, so choose another host for new work. |
Helpers worth adding
- Git LFS stores large files outside normal history and keeps lightweight pointers in Git.
- Hooks run scripts at key moments. Tools like pre-commit or Husky run linters and tests before a commit or push.
git worktreechecks out a second branch in another folder, handy for a hotfix while your feature is half done.- SSH keys or a credential manager (Git Credential Manager) so you do not type passwords. Sign commits with an SSH or GPG key if your team requires it.
- Diff and merge tools such as
delta, Meld, Beyond Compare or the IDE’s three-way merge make conflicts less scary. - CI services (GitHub Actions, GitLab CI, Jenkins, CircleCI) run your tests on every push, which every branching strategy above depends on.
Install git and learn the ten commands in section 3. Add the GUI or IDE integration you find most comfortable for diffs and conflicts, plus gh or glab if you live in the terminal. When a GUI does something unexpected, the command line is how you see what really happened.
9. Habits that work with any strategy
- Keep branches short-lived. Merge within days. The cost of merging grows faster than the age of the branch.
- Make small pull requests that do one thing. They get reviewed faster and are easier to revert.
- Pull or rebase often from main so conflicts show up while they are small.
- Automate checks: build, tests, lint and security scan on every PR. A branching strategy without CI is only a convention.
- Commit often, publish clean. Commit as you go locally, then tidy with an interactive rebase or squash before sharing.
- Never force-push a shared branch. If you must, use
--force-with-leaseon your own branch only. - Back up by pushing. Unpushed local commits exist on one disk only.
- Keep secrets and large generated files out of the repository.
10. Common mistakes
- Long-lived feature branches. Weeks of drift lead to a “merge hell” day. Merge in slices, use flags.
- Choosing Git Flow because it looks professional. If you deploy continuously, it adds work for no gain.
- Committing everything with
git add .without checkinggit status, and catching build files or secrets. - Vague commit messages (“fix”, “update”, “wip”) that make history useless for finding bugs.
- Rebasing or resetting shared history, which breaks teammates’ clones.
- Environment branches with different code, so what you tested is not what you ship.
- Forgetting to merge a hotfix back, so the bug returns in the next release.
- Putting big binaries in plain Git, bloating every clone forever.
- Resolving conflicts without understanding them. Read both sides, run the tests, and ask the author if unsure.
11. Takeaways and glossary
- Version control is the safety net and the collaboration tool of every software project.
- Git is distributed and snapshot-based: three local areas plus a remote, with branches as cheap labels.
- Alternatives win in special cases: huge binaries (Perforce, Unity Version Control), tiny self-hosted projects (Fossil), nicer ergonomics on Git (Jujutsu).
- Pick a branching strategy by how you release: GitHub Flow or trunk-based for continuous delivery, Git Flow or release branches for versioned software, fork and PR for open source.
- Short branches, small PRs, protected main and automated tests matter more than the name of the strategy.
- Learn the command line, then use GUIs and IDE tools for convenience.
| Term | In plain words |
|---|---|
| VCS | Software that records the history of changes to files. |
| Staging area | Where you assemble the next commit before saving it. |
| HEAD | The commit or branch you are currently on. |
| origin | The usual name of the remote you cloned from. |
| Pull request (PR) | A request to merge a branch, with review and checks. GitLab calls it a merge request. |
| Fast-forward | A merge where the target simply moves ahead because it has no new commits of its own. |
| Rebase | Replaying your commits on a new base, creating new commits. |
| Cherry-pick | Copying a single commit onto another branch. |
| Feature flag | A switch that turns unfinished code on or off without a new deploy. |
| Trunk | The main line of development that everyone integrates into. |
| Hotfix | An urgent fix to the code that is live in production. |
| Monorepo | One repository that holds many projects. |
Links and further reading
- Git quicksheet (companion one-page reference)
- Pro Git (free book) · Git reference · Learn Git Branching (interactive)
- GitHub Flow · A successful Git branching model (Git Flow) · trunkbaseddevelopment.com
- GitLab Flow · Semantic Versioning · Conventional Commits
- Subversion · Perforce Helix Core · Mercurial · Fossil · Jujutsu · Sapling · Pijul
- GitHub Desktop · Fork · GitKraken · Tower · lazygit · GitHub CLI · Git LFS