This page describes the git workflow for mson projects. We use a feature branch workflow with rebasing to keep history linear.
Core Principles
Create feature branches for all work, never commit directly to main. Keep feature branches up to date by rebasing, not merging. Write commit messages following Conventional Commits. Squash feature branches into a single commit when merging to main.
Branch Naming
Name your branches with a type prefix that matches Conventional Commits types.
The pattern type/scope-name helps organize branches, though scopes are optional for simple projects.
Examples:
docs/git-workflow- Adding git workflow documentationdocs/content-git-workflow- Same, with scope indicating content areafeat/code-copy-button- New featurefix/broken-links- Bug fixchore/update-deps- Maintenance work
Use lowercase with hyphens between words. The type prefix helps you remember what kind of commit message you’ll write.
Start a New Feature
Create a new branch from an up-to-date main branch.
# Make sure main is current
git switch main
git pull
# Create and switch to feature branch
git switch -c docs/git-workflow
The -c flag creates a new branch.
We use git switch instead of the older git checkout because it has a single purpose: switching branches.
The older git checkout did too many things (branches, files, detaching HEAD).
Now git switch handles branches and git restore handles files.
Stay Current with main
While you work on your feature branch, main will continue to evolve. You need to stay current with main to avoid conflicts later.
Always rebase to keep history linear. Never merge main into your feature branch.
Here’s the difference:
Before updating:
A---B---C main
\
D---E feature
After merging main (creates merge commit M):
A---B---C---F---G main
\ \
D---E-------M feature
After rebasing (replays your commits):
A---B---C---F---G main
\
D'---E' feature
Rebasing replays your commits (D and E) on top of the latest main. Your commits get new hashes (D’ and E’) because their parent commits changed. The history stays linear without merge commits cluttering the log.
Update your feature branch regularly:
# From your feature branch
git fetch origin
git rebase origin/main
If you’ve already pushed your feature branch, you’ll need to force push after rebasing:
git push --force-with-lease
Use --force-with-lease instead of --force.
It protects you from overwriting work if someone else pushed to your branch.
Never force push to main. The only exceptions are rare circumstances like cleaning up history on a brand new project or recovering from a corrupted history. These situations require team coordination.
Commit Your Work
Commit Message Format
Follow the Conventional Commits specification for all commit messages. Your first commit message on a feature branch is especially important because it becomes your merge request title.
The format is:
type(scope): subject line
Optional body paragraph explaining the why and what,
not the how. Keep the subject under 50 characters.
Wrap body lines at 72 characters.
If you used AI to generate code, commits, or MR descriptions,
include attribution.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Common types: feat, fix, docs, chore, refactor, test, style.
The scope is optional but helps organize commits in larger projects.
Scopes indicate which area changed: agents, theme, content, config.
Examples:
docs(content): add git workflow page
Explains feature branch workflow with rebasing
for mson group projects.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
feat(theme): add copy button to code blocks
Adds a copy-to-clipboard button for all code blocks.
Makes it easier for users to copy command examples.
AI-Assisted Work Attribution
When AI tools help with your work, include attribution for transparency. This applies to AI-generated code, commit messages, and merge request descriptions.
Add a Co-Authored-By line in the commit message footer:
docs(content): add git workflow page
Explains feature branch workflow with rebasing
for mson projects.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The robot emoji line indicates the commit message itself was AI-generated.
The Co-Authored-By credits AI assistance with the content.
Use both when appropriate, or just one depending on what was AI-assisted.
When to Commit
Commit whenever you reach a logical checkpoint in your work. Don’t worry about perfect commit messages for intermediate commits.
Since we squash when merging to main, only your first commit message matters for the final history. The first commit becomes the merge request title. Subsequent commits can be more casual like “wip” or “fix typo” since they’ll be squashed away.
You can plan your commits ahead if it helps you organize your work. Some developers like to commit frequently and clean up later with interactive rebase. Others prefer fewer, more polished commits. Choose what works for you.
Review Your Changes
Before committing, review what you’re about to commit.
# See which files changed
git status
# See the diff with staged and unstaged changes
git status -v
# See detailed diff
git diff
The git status -v command shows both the status and a diff in one command.
Stage and Commit
# Stage specific files
git add path/to/file.md
# Or stage all changes
git add .
# Commit with message
git commit -m "docs(content): add git workflow page"
For multi-line commit messages with attribution, use your editor:
git commit
This opens your configured editor where you can write a proper commit message with body text and attribution lines.
Push and Create Merge Request
Push your feature branch to the remote repository.
# First push (sets up tracking)
git push -u origin docs/git-workflow
# Subsequent pushes
git push
The -u flag sets up tracking so future pushes and pulls know where to go.
Create Draft Merge Request
Always create merge requests as drafts first. Drafts signal the work isn’t ready for review yet. They also let pipelines run without notifying reviewers.
Your first commit message becomes the merge request content:
- Commit subject line → MR title
- Commit body (excluding
Co-Authored-By) → MR description
For simple changes, the commit body works as the MR description. Complex changes may need more detailed descriptions with context, testing notes, or screenshots.
Create the draft MR using the GitLab CLI:
glab mr create --draft --title "docs(content): add git workflow page" --description "Explains feature branch workflow with rebasing for mson projects."
Or create it through the GitLab web interface after pushing.
When you’re ready for review, mark the MR as ready:
glab mr update --ready
This removes the draft status and notifies reviewers.
Squash and Merge
Once your merge request is approved, squash all commits into one and merge to main.
The squash commit includes a reference to the merge request in the subject line:
docs(content): add git workflow page (!42)
Explains feature branch workflow with rebasing
for mson projects.
GitLab adds the merge request reference to the subject. This happens manually or with a squash commit message template configured in project settings. The reference links every commit in main back to its review discussion.
Common Operations
Fixing a Commit Message
You have full freedom to rewrite history on your feature branch before opening an MR.
Use git commit --amend or git rebase -i to fix commits.
Once you open a draft MR, you can still rewrite history. Avoid rewriting after marking the MR ready or after reviewers start looking at it. Rewriting changes commit hashes and makes it hard to track what reviewers already saw.
If you need to fix something after review starts, just add a new commit.
Fix the most recent commit:
# Edit the commit message
git commit --amend
# Add forgotten files to the last commit
git add forgotten-file.md
git commit --amend --no-edit
After amending, force push:
git push --force-with-lease
Fix older commits:
Use interactive rebase to edit, reword, or squash commits.
# Rebase the last 3 commits
git rebase -i HEAD~3
This opens your editor with a list of commits.
Change pick to reword to edit a message, or squash to combine commits.
You can use the first letter as a shortcut: p for pick, r for reword, s for squash.
Save and close to proceed.
Discarding Changes
WARNING: These operations are destructive and delete files permanently.
Discard all uncommitted changes:
# Discard all changes and reset to HEAD
git reset --hard HEAD
This deletes all uncommitted work. You cannot recover discarded changes.
Discard changes to specific files:
# Discard changes to one file
git restore path/to/file.md
# Discard all unstaged changes
git restore .
Use git restore for safer, file-specific discards.
View History
# See recent commits
git log --oneline -10
# See commits on current branch not in main
git log main..HEAD --oneline
# See detailed commit info
git show abc123
The main..HEAD syntax shows commits unique to your branch.
This helps you see what will be included in your MR.
Stash Uncommitted Changes
If you need to switch branches but aren’t ready to commit:
# Stash your changes
git stash
# Switch branches and do other work
git switch other-branch
# Return and restore changes
git switch docs/git-workflow
git stash pop
Stashing temporarily saves uncommitted work.
git stash pop restores the changes and removes them from the stash.