Git Workflow

Feature branch workflow with rebasing and conventional commits for the mson group

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 documentation
  • docs/content-git-workflow - Same, with scope indicating content area
  • feat/code-copy-button - New feature
  • fix/broken-links - Bug fix
  • chore/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.

Next Steps