Re: [PATCH v2 1/2] Documentation: describe connectivity checking
- From
Patrick Steinhardt <ps@pks.im>
- Date
- Oct 5, 2026, 07:59 UTC
- Message-ID
- <asNY7SfEohsOSf0J@pks.im>
- In-Reply-To
- <97c11449aeae924436ba22a00a2545254e988a58.1790600552.git.gitgitgadget@gmail.com>
On Mon, Sep 28, 2026 at 01:02:31PM +0000, Kristofer Karlsson via GitGitGadget wrote:
Show 19 quoted lines
> diff --git a/Documentation/technical/connectivity-check.adoc b/Documentation/technical/connectivity-check.adoc > new file mode 100644 > index 0000000000..d20bff6af6 > --- /dev/null > +++ b/Documentation/technical/connectivity-check.adoc > @@ -0,0 +1,109 @@ > +Connectivity checking > +===================== > + > +After receiving new objects via fetch, push (receive-pack), clone, > +or bundle, Git verifies that the new reference tips do not leave > +the repository in a state where reachable objects are missing. > +This verification is called the connectivity check. > + > +Connectivity invariant > +---------------------- > + > +A repository is connected when every object reachable from its > +references is available locally (with exceptions noted below).
Right. I think it would also be important to spell out the reverse of this, which is that nothing can be assumed about objects that aren't reachable by any reference. So even if an object already exists in the object database, it is not safe to assume that it is fully connected unless it is referenced.
> +The connectivity check maintains this invariant when references > +are updated. It trusts the existing connected state and verifies
Nit: it's basically already implicit, but I'd clarify that "existing connected state" is again just the connected state of objects reachable from reference tips. So maybe "It trusts that all objects reachable from references are already fully connected and verifies..."
Show 27 quoted lines
> +that the new reference tips do not introduce references to > +unavailable objects. Verification is permitted to stop when it > +reaches objects already reachable from trusted existing > +references, since their closure is already connected. These > +trusted references include local references and references from > +alternate object stores. > + > +Without this check, a truncated or corrupted transfer could leave > +a repository in a state where later history walks encounter > +missing objects. > + > +Exceptions > +~~~~~~~~~~ > + > +Gitlink entries (submodule references) are excluded from > +connectivity checking. Their target objects belong to a separate > +repository. > + > +In partial clones, objects promised by a promisor remote are > +accepted as connected without requiring local existence. The > +check excludes promisor objects from traversal so that it does > +not trigger on-demand fetches for them. > + > +Full connectivity check > +----------------------- > + > +`check_connected()` (see `connected.c`) normally performs the
I'm always a bit hesitant to directly refer to code in our docs. We should either make this documentation part of "connected.c" directly, or we should not refer to code. Otherwise, chances that this documentation grows stale is very high.
Show 48 quoted lines
> +connectivity check using a `rev-list` subprocess, feeding the
> +new reference tips via stdin. A normal invocation is roughly:
> +
> + git rev-list --objects --stdin --not --all --quiet
> + --alternate-refs [--exclude-promisor-objects]
> +
> +When promisor remotes are configured, `check_connected()` first
> +attempts a fast path based on promisor packfiles. If it falls
> +back to the `rev-list` check, `--exclude-promisor-objects` is
> +added so that the traversal does not trigger on-demand fetches.
> +
> +Consider the following graph after a fetch, where all reference
> +tips point directly to commits. For simplicity, only local
> +references appear on the already-connected side; alternate refs
> +play the same role. N3 is a merge commit:
> +
> + /-------------L2
> + /
> + C1---B1---C2---B2-----L1
> + \ \
> + N1 N3---T2
> + \ /
> + N2-----------T1
> +
> + L1, L2: local refs
> + T1, T2: incoming tips (new refs)
> + N1, N2, N3: incoming commits (N3 is a merge)
> + B1, B2: boundary commits (already connected)
> + C1, C2: already connected (but not boundary)
> +
> +The incoming set is the commits reachable from the incoming
> +tips but not from the already-connected side. Boundary commits
> +are the already-connected commits at the edge of that set. Here
> +B1 is an ancestor of B2, which happens when incoming branches
> +fork at different depths in the existing history.
> +
> +The check proceeds in three phases:
> +
> +1. Walk from the incoming tips (T1, T2) against the trusted
> + refs (L1, L2) to find the incoming set ({N1, N2, N3, T1, T2}).
> +
> +2. Walk the trees of the boundary commits (B1, B2) and mark
> + those objects uninteresting. These trees are already trusted
> + because their commits are on the already-connected side.
> +
> +3. Walk the trees of each incoming commit and verify that every
> + referenced object is connected, stopping at objects already
> + marked uninteresting in phase 2.I feel like these phases here basically just explain how revision walks work without adding any more details that are specifically relevant to the connectivity check.
Show 17 quoted lines
> +Deepening fetches > +~~~~~~~~~~~~~~~~~ > + > +For deepening fetches (where the shallow boundary moves), the > +full check omits `--not --all`. There is no existing-reference > +boundary at which the walk can stop. Instead, traversal follows > +the effective shallow boundary supplied for the deepened > +repository. The new content may be below the old shallow > +boundary even when the tips themselves have not changed. > + > +Non-commit tips > +~~~~~~~~~~~~~~~ > + > +When a new reference points to a non-commit object, such as a > +tag, tree, or blob, that object is not part of the commit walk. > +These non-commit tips are handled by the subsequent object > +traversal.
Huh, what subsequent object traversal? This part puzzles me a bit.
Patrick