Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXXI · Fetch vs PullFetchVsPull

Configuring pull to rebase by default — pull.rebase and branch.<name>.rebase

Intermediate⏱ ~18 mingit

What you'll learn

  • Distinguish pull.rebase (repo-wide default) from branch.<name>.rebase (single-branch default) and explain the precedence rules
  • Configure pull.rebase true globally and override it per-branch with branch.<name>.rebase false
  • Predict the resulting behaviour of git pull for a given combination of global, repo, and branch-level settings
  • Identify the team implications of a global rebase default — what it standardises and what it breaks
  • Recommend a configuration strategy that encodes the team pull policy in the repo rather than in every engineer's .gitconfig

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

git pull is configurable: the team can decide in advance whether it should default to merge, rebase, or fast-forward only, and that decision can be encoded in Git’s configuration so every pull behaves the same way without the engineer remembering to pass a flag. The two configuration keys are pull.rebase (which sets the default for the entire repository or the entire machine) and branch.<name>.rebase (which sets the default for one specific branch). The combination of the two is what most production teams use to encode their pull policy.

pull.rebase: the repository-wide default

The pull.rebase configuration key accepts four values:

  • false (default) — git pull runs git fetch followed by git merge. A merge commit is produced on divergence.
  • truegit pull runs git fetch followed by git rebase. Local commits are replayed on top of the fetched upstream.
  • merges (or interactive) — git pull runs git fetch followed by git rebase --rebase-merges, which preserves merge nodes in the local history while replaying the linear chain.
  • only is a separate key (pull.ff = only) — see the previous lesson.
# Set pull.rebase at the repo level (this clone only)
git config pull.rebase true

# Set pull.rebase at the global level (every clone on this machine)
git config --global pull.rebase true

# Verify
git config --get pull.rebase
# true

# Effective setting for the current branch
git config --get-regexp '^pull\.rebase$'
# pull.rebase true

After git config pull.rebase true, every git pull in the affected repository defaults to rebase mode. The engineer still sees the fetch output, but the integration step is a rebase instead of a merge; if a local commit conflicts with the fetched upstream, the engineer resolves the conflict at the commit level and git rebase --continues.

branch.<name>.rebase: the per-branch override

branch.&lt;name&gt;.rebase configures a single branch to override the repository-wide pull.rebase. The value true forces rebase mode for that branch; false forces merge mode; if unset, the repo-level setting applies.

# Override repo default for one branch
git config branch.feature.iam-rotation.rebase true

# Override repo default for another branch in the opposite direction
git config branch.main.rebase false
# (forces merge mode on main, regardless of pull.rebase)

# Verify all branch-level rebase settings
git config --get-regexp '^branch\..*\.rebase$'
# branch.feature.iam-rotation.rebase true
# branch.main.rebase false

The branch name in the config key is the local branch name, not the remote-tracking name. A branch named release/v1.4.0 appears in the config as branch.release/v1.4.0.rebase (the dots in the version number are part of the branch name, not section separators).

flowchart LR
    A["git pull origin main"] --> B["read configuration"]
    B --> C{"branch.main.rebase\nset?"}
    C -->|yes| D["use branch.main.rebase"]
    C -->|no| E{"pull.rebase\nset at repo level?"}
    E -->|yes| F["use pull.rebase"]
    E -->|no| G{"pull.rebase\nset at global level?"}
    G -->|yes| H["use global pull.rebase"]
    G -->|no| I["default: merge mode"]
    D --> J["rebase or merge per branch"]
    F --> J
    H --> J
    I --> J

The precedence from most specific to least specific is: command-line flags (--rebase, --no-rebase, --ff-only) win over everything; then branch-level config wins over repo-level config; then repo-level config wins over global config; and finally the built-in default (merge mode) applies if nothing is set.

Encoding the team policy in the repo

A team that wants every engineer to pull the same way on the shared branches should encode the policy in the repository, not in every engineer’s ~/.gitconfig. The mechanism is the repository’s .git/config (set with git config from inside the clone) or, more durably, a config file shipped with the repository’s setup script.

# Repo-level policy for an infrastructure team
# Pull with rebase on feature branches (clean local history)
git config pull.rebase true

# Pull with merge on shared branches (preserve SHAs)
git config branch.main.rebase false
git config branch.master.rebase false
git config branch.release.rebase false
git config branch.production.rebase false

# Optional: ff-only on CI clones
git config pull.ff only

This configuration travels with the clone: a new engineer running git clone of the repository and then git pull on any branch gets the right behaviour automatically, because the per-branch overrides are in the repository’s .git/config that the clone creates.

A common pattern is to ship the configuration as a script in the repository’s bootstrap directory (e.g., scripts/setup-git-config.sh) that every new engineer runs once after cloning:

#!/usr/bin/env bash
# scripts/setup-git-config.sh
# Run once after cloning the infrastructure repository.

set -euo pipefail

# Pull with rebase by default
git config pull.rebase true

# Pull with merge on shared branches
for branch in main master release production; do
    git config "branch.${branch}.rebase" false
done

# Prune on every fetch
git config fetch.prune true

# Push only the current branch, never force
git config push.default current
git config --unset push.followTags 2>/dev/null || true

echo "git config set for this clone. Verify with 'git config --get-regexp'."

The script is idempotent: running it twice produces the same configuration. It can be re-run by any engineer who clones the repository to a new machine.

Verifying the effective configuration

Three commands to verify what pull will actually do on the current branch:

# Show every config setting that affects pull, in order of precedence
git config --list --show-origin | grep -E '(pull|branch)' | sort

# Effective setting for one branch
git config --get "branch.$(git symbolic-ref --short HEAD).rebase"
# false   (because main has a per-branch override)

# What pull will do (verbose output of an empty pull)
git pull --no-rebase --dry-run --verbose 2>&1 | head

The --show-origin flag in the first command tells you whether a setting came from ~/.gitconfig (global), the repo’s .git/config (local), or the branch-level section. That origin is what lets you diagnose a wrong-pull surprise: if branch.main.rebase = false is in the local config but the engineer is still seeing rebase mode, the per-branch override is being shadowed by a command-line flag or by a typo in the branch name.

Production discipline

  1. Encode pull policy in the repo, not in ~/.gitconfig. A new engineer cloning the repository should get the right policy for free.
  2. Override per-branch for shared branches. Repo-wide pull.rebase = true plus per-branch rebase = false on shared branches is safer than repo-wide rebase.
  3. Ship a setup script. A scripts/setup-git-config.sh in the repository is more discoverable than a wiki page that nobody reads.
  4. Verify the effective setting with --show-origin. When a pull behaves unexpectedly, the first diagnostic is to ask Git which config layer the setting came from.
  5. Audit the policy periodically. A team whose members have set conflicting global configs will have inconsistent pull behaviour even if the repo config is correct. The script fixes this; the wiki page does not.

Cross-course references

  • Git, CI/CD & GitOps - Part XXII (RebaseDeeper) covers rebase in detail; the configuration here is the prelude to those mechanics.
  • Terraform for Production Sysadmins - Part XVIII (TeamConfig) describes how a Terraform team encodes shared Git policies (including pull.rebase and branch.&lt;name&gt;.rebase) in a bootstrap script that runs on every engineer’s first clone.
  • Ansible for Production Sysadmins - Part XL (BranchPolicy) ships a similar setup script for Ansible playbooks, with per-branch overrides for the shared integration branch.

Quiz

Knowledge check · 4 questions

  1. Q1. In a repository with `pull.rebase = true` set at the repo level and `branch.main.rebase = false` set per-branch, what does `git pull` do on the `main` branch?

  2. Q2. Setting `pull.rebase = true` in `~/.gitconfig` (the global config) is not necessarily the safest way to make every pull on every clone default to rebase mode.

  3. Q3. Describe the precedence rules for pull configuration, from most specific to least specific, and explain why a team should encode pull policy in the repo rather than in ~/.gitconfig.

  4. Q4. A team has set `pull.rebase = true` globally on every engineer's machine. The team shares a `release/v2.0` branch. Two engineers report that their release branches have diverged and a force-push happened. Diagnose and recommend a fix.

    Two engineers working on `release/v2.0` both have `pull.rebase = true` set globally. Each has local commits ahead of origin/release-v2.0. Engineer A runs `git pull` (rebase mode, because of the global setting), rewrites their local commits on top of the fetched upstream, and pushes — producing a force-push because the SHA changed. Engineer B's clone, which has the original SHAs, now reports divergence. Engineer B rebases their own work onto the new SHAs and pushes — another force-push. The history of the release branch has been rewritten twice in one afternoon.

Passing score: 75%. Answers are checked in this browser.