Update docs, changelog, examples, and add ADRs for v1.2
- Add v1.1.0 and v1.2.0 changelog entries - Add exclude field to config reference and example config - Add ADRs documenting all major design decisions - Fix step numbering in reverse_sync() - Fix action.yml to copy VERSION file - Add dist/ and .env to .gitignore - Use refs/tags/ format for Nix flake tag refs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
53
docs/adr/009-tree-comparison-guard.md
Normal file
53
docs/adr/009-tree-comparison-guard.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# ADR-009: Tree Comparison as Sync Skip Guard
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-02
|
||||
|
||||
## Context
|
||||
|
||||
Both forward and reverse sync need to detect "nothing to do" quickly. The primary mechanism is SHA comparison against stored state (last-synced SHA). However, this misses cases where:
|
||||
|
||||
- State is reset or lost
|
||||
- Reconciliation merges change SHAs without changing content
|
||||
- Multiple sync runs overlap
|
||||
|
||||
Additionally, reverse sync originally relied on `git log <base>..HEAD` to find new commits. After a reconciliation merge, the `..` range can leak old subrepo history through the merge's second parent, creating false positives.
|
||||
|
||||
## Decision
|
||||
|
||||
Add tree-level comparison as an early skip guard in both forward and reverse sync. Compare the git tree objects (which represent directory content, not commit history) to determine if there's actually any content difference.
|
||||
|
||||
### Forward sync
|
||||
|
||||
```bash
|
||||
mono_tree=$(git rev-parse 'HEAD^{tree}')
|
||||
subrepo_tree=$(git rev-parse "subrepo/${branch}^{tree}")
|
||||
[ "$mono_tree" = "$subrepo_tree" ] && echo "skip"
|
||||
```
|
||||
|
||||
### Reverse sync
|
||||
|
||||
```bash
|
||||
subrepo_tree=$(git rev-parse "HEAD^{tree}")
|
||||
josh_tree=$(git rev-parse "mono-filtered/${branch}^{tree}")
|
||||
[ "$subrepo_tree" = "$josh_tree" ] && echo "skip"
|
||||
```
|
||||
|
||||
Tree comparison happens **before** commit log analysis. If trees are identical, there is definitionally nothing to sync, regardless of what the commit history looks like.
|
||||
|
||||
### Combined with `--ancestry-path`
|
||||
|
||||
For reverse sync, even when trees differ, `git log --ancestry-path` restricts the commit range to the direct lineage between the two endpoints. This prevents old history from leaking through reconciliation merge parents.
|
||||
|
||||
## Consequences
|
||||
|
||||
**Positive:**
|
||||
- Eliminates false positives from reconciliation merges (trees are identical after reconciliation)
|
||||
- Fast — tree SHA comparison is O(1), no content traversal
|
||||
- Correct by definition — if trees match, content is identical
|
||||
- Defense in depth — works even when state tracking has gaps
|
||||
|
||||
**Negative:**
|
||||
- Tree comparison alone doesn't tell you *which* commits are new (still need `git log` for PR descriptions)
|
||||
- Adds an extra `git rev-parse` call per sync direction (negligible cost)
|
||||
- Cannot detect file-mode-only changes if josh normalizes modes (theoretical edge case)
|
||||
Reference in New Issue
Block a user