Workflows are a set of rules / guidelines that prescribe how git should be used.
Workflows can apply across projects, teams or organizations.
Workflows can include:
- Commit message formatting rules
- Automated tooling (linters, tests)
- Branching strategy
- Collaboration strategy
- Release strategy
Git workflows vary greatly depending on:
- Number of developers
- Number of repositories
- Security concerns
- Infrastructure & Tooling
- Knowledge & culture
Branching strategies
Branching strategies focus on how developers synchronize changes in a structured way.
Trunk-based
Everyone directly contributes to the main branch
-
PRO: very simple and easy to just start working.
-
CON: Can lead to frequent conflicts when developers makes changes to the same parts of the project
Feature branches
New features are developed on their own branch.
-
PRO: keeps development isolated, conflicts only happen after a big merge
-
CON: stacking feature branches can lead to complex merge trains
GOOD PRACTICE: update feature branches often by merging main into them
Git flow
A branching strategy meant to provide structure and clarity in complex projects.
Gitflow prescribes the following branches:
- main - Production-ready code
- develop - Integration branch
- feature/{name} - New features
- release/{number} - Preparing releases
- hotfix/{ticket} - Emergency fixes
Choosing a branching strategy
For beginners and small teams, feature branches are often the sweet spot - more organized than trunk-based but simpler than Gitflow.
For large projects with complex versioning and release cycles, something like Gitflow can be very valuable in providing a clear structure.
Pull vs merge requests
The 2 terms are functionally identical - a request for a second person to review and approve the changes before merging.
The pedantic difference is in where the code is hosted:
- Pull request - implies another remote repository (often called a
fork
) - Merge request - implies just a branch in the same repository
This is related to git’s lack of fine grained permissions within a repository.
Pull/merge requests vs direct merge
Pull/merge requests are not required when working on a shared repository.
You can merge into main locally and push the resulting commits manually.
There is no point in using PRs, when working alone on a personal project.
However pull requests provide an explicit mechanism for review, discussion and approval.
Most git hosting providers have additional tools and controls to make these processes easier and mandatory.
High level setups
Branching is one part of a full setup.
Another other major factor is the number of repository copies and who has access to contribute.
Personal hosted repository
The simplest workflow (aside from just using git locally). Perfect for personal projects.
-
Branching strategy can be as simple as
push to main
(trunk-based). -
Repository can be hosted on any of the popular providers (e.g GitHub).
-
Hosted repo is accessible from anywhere in the world.
Private git server
Self-hosted git on a physical server or VPS. Perfect for small projects and homelab setups.
-
Allows greater flexibility and privacy.
-
Requires more technical knowledge and support.
Shared repository
This is probably the most common setup - a team working on a shared repository.
Branching strategy can vary, but usually relies on
feature branches
and pull requests
.
Integration manager
A more complex fork-based setup, more popular among large open-source projects.
-
Developers maintain their own forks
-
An integration manager pulls and merges changes from them
-
Only the integration manager pushes to the
blessed repository
-
The integration manager schedules when things get merged
benevolent dictator
An even more complex setup, only suitable for truly big projects - such as the Linux Kernel.
It is similar to the integration manager setup but with an intermediate layer of lieutenants
that collate changes from multiple contributors.
-
The dictator only pulls from the lieutenants
-
Only the dictator pushed to the blessed repository
-
Everyone pulls from the blessed repository as the source of truth for the project
Git is flexible
These workflow examples are NOT set in stone.
Teams are encouraged to iterate and establish a workflow that best fits their goals, environment and work-style.
Centralized vs decentralized
- Centralized setups are more applicable within a single company where all developers are trusted. Mono-repos can be seen as an extension of this setup.
- Decentralized setups are more useful within open source, where the main owner of the projects wants to maintain control, while allowing anyone in the world to contribute.
Local Merging Workflow Example
# Start with a up-to-date main branch
git checkout main
git pull origin main
# Create feature branch
git checkout -b feature-contact-form
# Do work and commit
echo "<form>Contact Form</form>" > contact.html
git add contact.html
git commit -m "feat: add contact form"
# Push feature branch to remote
git push -u origin feature-contact-form
# When feature is complete, merge back
git checkout main
git pull origin main
git merge feature-contact-form
# push the merged feature to the remote
git push origin main
Merge Request Example
Instead of merging directly, most teams use Pull / Merge Requests for code review:
# 1. Create feature branch
git checkout -b feature-user-authentication
# do work ...
# commit and push
git commit -m "feat: added google login option"
git push -u origin feature-user-authentication
# 2. Create Pull Request through
# GitHub's web interface
During the review team members can request changes, suggest improvements or spot bugs.
# 3. Other team member reviews the code
# 4. Once approved, merge through web interface
# 5. Pull the merged changes locally
git checkout main
git pull origin main
# delete the merged branch locally
git branch -d feature-user-authentication
Forking Workflow Example
Same as with the Merge request, but the feature branch is hosted in a separate remote repo:
# 1. Fork repository on GitHub and clone your fork
git clone github/yourusername/forked-project.git
# 2. Create feature branch and work
git checkout -b fix-bug
# 3. Push to YOUR fork
git push origin fix-bug
# 4. Create Pull Request
# from your fork to the original
Best Practices
-
Have a well structured
README
file -
Setup a
.gitignore
based on your language and tooling -
Fetch
main
often and merge it into your feature branch (to avoid big conflicts) -
Make sure to NOT commit and push secrets
Pull request etiquette
-
When contributing to a project, always respect the project’s guidelines! (usually found in a
CONTRIBUTING.md
file) -
Do NOT make trivial changes that nobody asked for.
-
Do NOT demand from project owners to prioritize your contributions.
-
Be polite and respectful in discussions.
Create your first Pull request
-
Fork and clone the repository for the class slides
-
Create a branch called
{your-github-handle}
-
Add your Github handle to the
students.md
file -
Commit and push to your fork
-
Open a Pull request from your fork to the main repository
Fuller workflow example
# 1. Setup
mkdir team-project
cd team-project
git init
echo "# Team Project" > README.md
git add README.md
git commit -m "initial project setup"
# 2. Create remote repository on GitHub and connect
git remote add origin https://github.com/yourusername/team-project.git
git push -u origin main
# 3. Create feature branch
git checkout -b feature-user-profile
echo "<div>User Profile Component</div>" > profile.html
echo "body { margin: 0; }" > styles.css
git add .
git commit -m "feat: add user profile component and basic styles"
# 4. Continue development
echo "<script>console.log('Profile loaded');</script>" >> profile.html
git add profile.html
git commit -m "feat: add profile loading feedback"
# 5. Push feature branch
git push -u origin feature-user-profile
# 6. Simulate main branch changes (like from another developer)
git checkout main
echo "console.log('App initialized');" > app.js
git add app.js
git commit -m "feat: add app initialization"
git push origin main
# 7. Update feature branch with latest main
git checkout feature-user-profile
git merge main
# 8. Complete feature and merge
git checkout main
git pull origin main # Get any other changes
git merge feature-user-profile
git push origin main
# 9. Clean up
git branch -d feature-user-profile
git push origin --delete feature-user-profile