question: so now we fixed if a branch is 100% included in the default branch (or another branch) but what if a branch is branched of the 10th commit (from HEAD) of main, how would the code handle this · Entire
Question: If we would push to a backend that supports sha256, could we do the translation on the fly?
Short Answer
Technically yes, but not in the streaming-relay sense that git-sync is built around. It becomes a full materialize-translate-repack path, and it breaks signatures.
Why It Isn't Cheap
Object contents embed hashes. Trees list entry SHA-1s, commits list parent/tree SHA-1s, tags list target SHA-1s. To produce SHA-256 objects you must decode every object, rewrite internal references, and rehash. That kills the relay optimization in
bootstrap/sync— you can't just forward the source packfile bytes (internal/relay is the current fast path; seedocs/bootstrap.md).You need a persistent bidirectional mapping. For incremental runs, target tips are SHA-256 but the source's
havenegotiation expects SHA-1. git-sync would have to carry (or re-derive) a SHA-1↔SHA-256 map across runs, or re-translate everything each time. Git core handles this viaextensions.compatObjectFormat/ the "transition plan" loose-object mapping — doing it outside git means reimplementing that table.Signed commits and signed tags break. The signature covers the object bytes. Rewriting references changes the bytes, so every GPG/SSH signature becomes invalid. There's no fix-up; this is inherent to content-addressed storage.
Memory/cost profile changes. Current
--materialized-max-objectsguardrail bounds the fallback in-memory path. A translating push is strictly a materialized path plus a rewrite pass and a mapping table, making it more expensive than today's worst case.Receive-pack on a SHA-256 target speaks SHA-256 wire format. That's a protocol switch (the push path is still v1-style here; wire-level object IDs, ref advertisement, capabilities like
object-format=sha256all change). So the push side needs a second code path, not a flag flip.
Practical Take
It would be a separate --translate-to sha256 mode that replaces relay with decode-rewrite-rehash-repack, persists a hash map alongside managed refs, and documents that signatures don't survive. Worth it if the goal is a one-shot migration to a SHA-256 backend; not worth grafting onto the incremental relay path.
If you want, I can sketch where this would slot into the engine (plan → materialized translate stage → new push codec) without writing code yet.
Different question: If the target supports SHA256, could it translate on receive? Is that a thing?
Answer
Yes — this is actually a feature in git core, called the hash function transition / interop mode, using extensions.compatObjectFormat.
How It Works Server-Side
- The target repo is created with
objectFormat = sha256andcompatObjectFormat = sha1(or vice versa). Git then stores a bidirectional hash map for every object it receives. - On
receive-pack, if the client pushes SHA-1 wire format (negotiated via theobject-formatcapability), the server translates incoming objects into its primary format and records the mapping. Trees/commits are rewritten so internal references point at the primary-format hashes. - On
upload-pack/ref advertisement, the server can serve either format to a client depending on whichobject-format=the client negotiates. SHA-1 clients see SHA-1 hashes; SHA-256 clients see SHA-256. - Signed commits/tags: the transition plan handles this by signing in a canonical form and carrying the signature + original bytes; interop is designed so signatures still verify under the hash they were made in.
The Upshot for git-sync
- If the target server implements compat mode, git-sync needs no translation code at all. You just push SHA-1 as today; the server does the work. Git-sync's relay path remains intact, because the wire bytes can flow through unchanged until they hit the server.
- You need to negotiate
object-format=sha1in the push capabilities, and confirm the server advertisesobject-format. Currently, git-sync'sreceive-packpath doesn't inspect that; it would want to, to fail loudly if the target is SHA-256-only with no compat. - For incremental reruns, target ref advertisements return in whatever format the client requested, keeping the negotiation intact.
Current Reality Check
- Git core has had this since ~2.42, marked experimental.
- GitLab has had some SHA-256 repo support; GitHub historically has not shipped SHA-256 repos.
- Gitea/Forgejo have varying support.
- Self-hosted git daemons are the most likely places you'd encounter a SHA-256 target with compat mode.
Conclusion: "Translate on receive" is the right architectural layer for this problem; it's a server capability question, not a git-sync feature request. The wise approach for git-sync is to detect and surface it (probe object-format capabilities; refuse a SHA-256-only push target with a clear error) rather than try to implement translation itself.
Regarding sync work later
Answer
Yes, later incremental sync works, as long as two conditions hold on the target:
- Compat mode is permanent, not bootstrap-only.
- git-sync always negotiates
object-format=sha1with the target.
Given those, the incremental flow stays identical to today:
- Probe target → get SHA-1 tips (via compat map).
- Advertise those as
haveto source upload-pack. - Source returns a thin SHA-1 pack of only new objects.
- Relay to target; server translates and extends the map.
- Fast-forward checks,
--prune,--force, tag immutability — all operate on SHA-1 hashes on both sides, so the existing logic is untouched.
Edge Cases
- Third-party pushes to the target in SHA-256 between syncs.
- Ref rewrites on target.
- Tag retargeting.
One thing that does not work: if compat mode was never enabled and the target is SHA-256-only, the first sync fails at capability negotiation. A sensible git-sync behavior would be to detect object-format=sha256 without object-format=sha1 in the receive capabilities on the first probe and error out with a clear message.
Tool Support for Branches
Answer
Yes, with one nuance: it's two invocations, not one config. The tool is unidirectional per run by design — source → target with explicit scope, so you orchestrate the split externally:
# main: source is truth
git-sync sync --branch main A B
# feature branches: target-as-source, source-as-target (roles swap)
git-sync sync --branch feature/login,feature/billing B A
Flags and Precautions
- Ownership discipline is on you. Nothing in the tool prevents you from running both directions over overlapping branch sets and getting a ping-pong effect.
The sharp edges to know about:
- No wildcard branches.
replicateis directional too. Ifmainshould be strictly source-authoritative, usereplicatefor that run.- Auth is per-run.
Final Note
The tool supports explicit branch lists today; a glob flag for "all feature/*" becomes a sensible addition.
Optimization Discussion
Suggestion
Finding the default branch first could offer an optimization. Ordering helps, but utilizing the trunk's reachable set as a cutoff for every other branch's graph walk yields significant performance improvements.
Impact Expected:
- Reduces fetch times across branches by focusing only on new commits, leveraging trunk's commit graph and ancestry.
- Improve overall runtime of the
planphase by narrowing down necessary data fetching.
Implementation Changes
You'd need to tweak planBatches to allow trunk-first ordering and implement logic to utilize trunk’s reachable set for subsequent branches.
Summary of Changes
- Implemented trunk-first:
orderTrunkFirstenriches planning phase, reducing per-branch latency. - Added cutoffs: Drives efficiency by shortening unnecessary fetches into skip conditions.