Why Do Some Commits Get Fetched and Others Don’t? (Git & AOSP repo)

TL;DR

Which commits end up in your local repo is decided by four levers:

  • Refspecs — which refs (branches/tags) you asked for
  • Tag policy — all tags / no tags / only reachable tags
  • Server-side packing — clone-bundles & large packs may include extra objects you didn’t explicitly ask for
  • History shape & modes — reachability, shallow depth, partial clone

Change any of these and you will change which commits appear locally.


  1. Refspec: the primary switch

A refspec tells Git which refs to fetch and where to store them locally.

# .git/config (examples)
[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*   # all branches (common default)
    # (optional) fetch = +refs/tags/*:refs/tags/* # all tags, if present
  • +refs/heads/* → you requested every branch tip and enough history to reach them → many commits.
  • +refs/heads/foo:refs/remotes/origin/fooonly one branch.
  • git fetch origin tag vXonly that tag and the history needed to reach it.
  • repo sync -c narrows branches (heads) to the current branch, but not tags.

Inspect what you’re asking for:

git config --get-all remote.origin.fetch

  1. Tags: all / none / only reachable

There are three behaviors when fetching tags:

  • All tags
    git fetch --tags or git config remote.origin.tagOpt --tags
    → fetches every refs/tags/* and the commits they point to.
  • No tags
    git fetch --no-tags or git config remote.origin.tagOpt --no-tags
    → fetches no tags (you can still fetch a specific tag by name).
  • Default = auto-follow reachable tags
    If you fetch some history and a tag points into that history, Git brings that tag object too.
    This does not add extra commits; you already fetched the commits, the tag is just an extra pointer.

Check your current tag policy:

git config --get-all remote.origin.tagOpt || echo "auto-follow (default)"

  1. “I never asked for this” — why unrelated commits still show up

Two common reasons:

  1. Clone-bundle & pack reuse (AOSP/repo)

repo sync often downloads a clone bundle first (a large prebuilt pack), then does a normal fetch.
Servers also send big packs that reuse deltas across many refs. Both can include extra objects that you didn’t request explicitly (for compression/latency wins).

  • Those commits may exist in .git/objects without any local ref pointing at them.
  • That’s why this commit/object can be present even when no branch/tag contains it:
git cat-file -e <sha>^{commit} && echo present || echo absent
  • Skip it when you need tighter control:
repo sync --no-clone-bundle ...
  1. You fetched too many refs

If your refspec is the default +refs/heads/*, you literally fetched all branches.
Auto-follow then adds the tags reachable from those branches. Result: lots of unexpected tags/commits.


  1. Reachability, precisely

A commit X is included if any local ref (branch or tag) can reach X by following parent links.

Useful checks:

# Is X contained in Y's history?
git merge-base --is-ancestor X Y && echo YES || echo NO

# Who contains X?
git branch    --contains X
git branch -r --contains X
git tag       --contains X

# Count commits (different scopes)
git rev-list --count HEAD           # current branch
git rev-list --count --branches     # all local branches
git rev-list --count --remotes      # all remote-tracking branches
git rev-list --count --all          # everything reachable by any local ref

  1. Shallow & partial modes
  • Shallow clone (--depth=1)
    Only grabs the tip(s) you asked for with minimal ancestry → dramatically fewer commits.
    Trade-off: limited history operations (e.g., some merges/rebases).
  • Partial clone (--filter=blob:none)
    Fetches all commits/trees but defers blobs until first use.
    Commit count does not shrink, but bandwidth/IO often does.

  1. AOSP repo specifics
  • -c / --current-branch affects heads only, not tags.
  • Unless disabled, repo behaves close to:
    • fetch all heads (+refs/heads/*)
    • fetch all tags (+refs/tags/*)
    • optionally download a clone bundle
  • Therefore you can see commits unrelated to your target tag/branch because:
    • your refspec asked for them (all heads / all tags), or
    • the bundle/pack included them.

Dial it down:

# Smaller: avoid bundle + tags
repo sync -c --no-clone-bundle --no-tags platform/bionic

  1. Minimal, reproducible recipes
  1. Only the android-16.0.0_r2 snapshot (no history)
git init bionic-min && cd bionic-min
git remote add aosp https://android.googlesource.com/platform/bionic
git fetch --no-tags --depth=1 aosp tag android-16.0.0_r2
git checkout -q FETCH_HEAD
  1. Only android-16.0.0_r2 history (full), no extra tags
git init bionic-r2 && cd bionic-r2
git remote add aosp https://android.googlesource.com/platform/bionic
git fetch --no-tags aosp tag android-16.0.0_r2
git checkout -q FETCH_HEAD
  1. Only r2 history plus reachable tags (not all tags)
# Keep fetch scope narrow; do NOT use +refs/heads/*.
git init bionic-r2-tags && cd bionic-r2-tags
git remote add aosp https://android.googlesource.com/platform/bionic

# Step 1: get r2 history without tags
git fetch --no-tags aosp tag android-16.0.0_r2
git checkout -q FETCH_HEAD

# Step 2: auto-follow reachable tags
git fetch aosp   # no --tags / --no-tags → default auto-follow
  1. Restrict a repo to a single ref long-term
# Replace “all branches” with a single refspec
git config --unset-all remote.origin.fetch
git config --add remote.origin.fetch '+refs/tags/android-16.0.0_r2:refs/tags/android-16.0.0_r2'

# Optional: never auto-fetch tags
git config remote.origin.tagOpt --no-tags

  1. Why your experiments looked the way they did
  • Default (bundle + all heads and usually tags) had the most commits/objects.
  • --no-clone-bundle reduced commits/objects (no preloaded extras).
  • --no-clone-bundle --no-tags reduced further (no tag-only histories).
  • You still saw “mystery commits” with --no-tags because bundles/packs can include extra objects that no ref points to. They exist, but they’re not reachable from your branches/tags.

Bottom line

  • Refspecs define the scope; tag policy controls tag pointers; bundles/packs may add extra objects; history modes (shallow/partial) change what’s transferred.
  • If you want predictable, minimal results: narrow your refspecs, disable tags/bundle, and use shallow/partial when appropriate.
  • If you want correctness with some savings: restrict refspecs to what you actually need, keep default auto-follow to get only reachable tags, and avoid fetching all heads.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top