Skip to content

Commit b064366

Browse files
committed
Release v0.2.1 with sync and UX fixes
Prevent data loss in cross sync via stash/restore and tracked-only deletions. Add cross prune for cleanup of unused remotes and stale worktrees. Fix cross cd/wt intent, add fzf selection and clipboard workflow. Add tests for sync, prune, and cd/wt behavior; add git.just summary/squash helpers. fix(sync): add file deletion logic for upstream removed files - Add logic to detect and remove local files that were deleted upstream - Fixes Test 5: Sync with deleted upstream file - Now all 6 test scenarios in test/004_sync.sh pass feat(prune): implement cross prune command across all implementations Add 'cross prune' command to clean up unused remotes and worktrees: - Justfile.cross (lines 230-303): Interactive prune with confirmation - Go (src-go/main.go): Cobra command with full logic - Rust (src-rust/src/main.rs): Clap command with full logic - Test coverage (test/015_prune.sh): 3 comprehensive test scenarios Command behaviors: 1. 'cross prune' (no args): - Finds all remotes that have no active patches - Excludes 'origin' and 'git-cross' from cleanup - Asks for user confirmation before removal - Runs 'git worktree prune' to clean stale worktrees 2. 'cross prune <remote>' (with remote name): - Finds all patches using that remote - Removes each patch (worktree + local dir + metadata) - Removes the remote itself - Updates Crossfile and metadata.json other(git.just): summary and changelog utils - optional include git.just from Justfile
1 parent 786c2db commit b064366

File tree

16 files changed

+1804
-190
lines changed

16 files changed

+1804
-190
lines changed

AGENTS.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,30 @@ Testing is modular and targets each implementation:
5353
- **Rust**: `test/008_rust_cli.sh` verifies the Rust port.
5454
- **Go**: `test/009_go_cli.sh` verifies the Go implementation.
5555

56+
For known issues and planned enhancements, see [TODO.md](TODO.md).
57+
5658
## Agent Guidelines
5759

60+
### CRITICAL: Complete Implementation Requirement
61+
62+
**When implementing any feature or bug fix:**
63+
1. **ALL THREE implementations MUST be updated** - Justfile.cross, Go (src-go/), and Rust (src-rust/)
64+
2. **NO partial commits** - All implementations must land in the same commit or commit series
65+
3. **Test coverage required** - Each new feature/fix MUST have test coverage in test/XXX_*.sh
66+
4. **All implementations tested** - Tests must verify behavior across Just, Go, and Rust implementations
67+
5. **Command parity maintained** - All implementations must provide identical functionality and behavior
68+
69+
**Workflow:**
70+
- Implement in Justfile.cross first (reference implementation)
71+
- Port to Go (primary production implementation)
72+
- Port to Rust (experimental implementation)
73+
- Create/update test case (test/XXX_*.sh)
74+
- Verify all three implementations pass the same test
75+
- Document in TODO.md and commit message
76+
- Only then commit
77+
78+
### Other Guidelines
79+
5880
- **Consistency**: When adding features, ensure logic parity across `Justfile.cross`, Rust, and Go versions.
5981
- **Command Parity**: All implementations (Just, Go, Rust) **MUST** implement the same set of core commands to ensure a consistent user experience regardless of the implementation layer used.
6082
- **Tool Hygiene**: Installation and Git alias management MUST be handled through distribution (e.g., `Justfile`), keep binaries focused on functional command implementation.

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.2.1] - 2026-01-06
11+
12+
### Added
13+
- **`prune` command** - Clean up unused remotes and stale worktrees
14+
- `cross prune`: Interactive removal of remotes with no active patches
15+
- `cross prune <remote>`: Remove all patches for a specific remote
16+
- Excludes 'origin' and 'git-cross' from cleanup
17+
- Runs `git worktree prune` to clean stale worktrees
18+
- Implemented across all three implementations (Just, Go, Rust)
19+
- Full test coverage in `test/015_prune.sh`
20+
21+
### Fixed
22+
- **Sync command file deletion logic** - Only delete tracked files removed upstream
23+
- Previously would delete ALL files including user's untracked customizations
24+
- Now uses `git ls-files` to only check tracked files
25+
- Preserves untracked local files (config files, notes, etc.)
26+
- Fixes data loss risk for local customizations in patched directories
27+
- **Sync command data preservation** - Complete stash/restore workflow
28+
- Preserves uncommitted changes during sync operations
29+
- Handles untracked files properly with `--include-untracked`
30+
- Detects and removes files deleted upstream
31+
- Graceful conflict handling with user feedback
32+
33+
### Changed
34+
- **Agent guidelines** - Added critical implementation requirements in AGENTS.md
35+
- All three implementations must be updated together
36+
- No partial commits allowed
37+
- Test coverage required for all features
38+
- Command parity must be maintained
39+
40+
### Testing
41+
- Enhanced `test/004_sync.sh` with 6 comprehensive scenarios
42+
- Added `test/015_prune.sh` with 3 test scenarios
43+
- All tests pass for Just, Go, and Rust implementations
44+
1045
## [0.2.0] - 2025-12-01
1146

1247
### Added

Justfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import? "git.just"
2+
13
# Delegate all invocations to the full recipe set
24
[no-cd]
35
@cross *ARGS:
@@ -27,4 +29,4 @@
2729

2830
#_idea:
2931
# REPO_DIR=$(git rev-parse --show-toplevel) \
30-
# just --justfile "{{source_dir()}}/Justfile.cross" {{ARGS}}
32+
# just --justfile "{{source_dir()}}/Justfile.cross" {{ARGS}}

Justfile.cross

Lines changed: 266 additions & 44 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![CI](https://github.com/epcim/git-cross/workflows/CI/badge.svg)](https://github.com/epcim/git-cross/actions/workflows/ci.yml)
44
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5-
[![Version](https://img.shields.io/badge/version-0.2.0-blue.svg)](https://github.com/epcim/git-cross/blob/main/CHANGELOG.md)
5+
[![Version](https://img.shields.io/badge/version-0.2.1-blue.svg)](https://github.com/epcim/git-cross/blob/main/CHANGELOG.md)
66

77
**Git's CRISPR.** Minimalist approach for mixing "parts" of git repositories using `git worktree` + `rsync`.
88

TODO.md

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# TODO - git-cross
22

3+
## Summary
4+
5+
**Status:** v0.2.1 released with prune command and sync fixes
6+
**Critical Issues:** 0 (all P0 issues resolved)
7+
**Pending Enhancements:** 2 (single-file patch, fzf improvements)
8+
39
## Core Implementation Status
410

511
- [x] Go Implementation (Primary) - Feature parity achieved.
@@ -31,16 +37,140 @@
3137

3238
## Future Enhancements / Backlog
3339

34-
- [x] `cross list` comand shall either print all cross remote repositories (REMOTE (alias), GIT URL) in separate table above the table with patches. Or directly inline with each patch.
35-
- [x] Implement `cross remove` patch, to remove local_pathch patch and it's worktree. Finally clean up the Metadata an Crossfile. Once physically removed, `git worktree prune` will clenaup git itself.
36-
- [ ] Implement `cross prune [remote name]` to remove git remote repo registration from "cross use" command and ask user whether either remove all git remotes without active cross patches (like after: cross remove), then `git worktree prune` to remove all worktrees. optional argument (an remote repo alias/name would enforce either removal of all it's patches altogther with worktrees and remotes)
37-
- [x] Re-implement `wt` (worktree) command in Go and Rust with full test coverage (align logic with Justfile).
38-
- [ ] Refactor `cross cd` to target local patched folder and output path (no subshell), supporting fzf.
39-
- [ ] Review and propose implementation (tool and test) to be able patch even single file. If not easily possible without major refactoring, then evaluate new command "patch-file".
40-
- [ ] Improve interactive `fzf` selection in native implementations.
40+
### P1: High Priority
41+
- [x] **Implement `cross prune [remote name]`** - Remove git remote registration from "cross use" command and ask user whether to remove all git remotes without active cross patches (like after: cross remove), then `git worktree prune` to remove all worktrees. Optional argument (a remote repo alias/name) would enforce removal of all its patches together with worktrees and remotes.
42+
- **Effort:** 3-4 hours (completed 2025-01-06)
43+
- **Files:** `src-go/main.go`, `src-rust/src/main.rs`, `Justfile.cross`, `test/015_prune.sh`
44+
- **Implementation:**
45+
- ✅ Justfile.cross (lines 230-303): Full interactive prune with confirmation
46+
- ✅ Go (src-go/main.go): Cobra command with same logic
47+
- ✅ Rust (src-rust/src/main.rs): Clap command with same logic
48+
- ✅ Test coverage (test/015_prune.sh): 3 test scenarios
49+
- **Behavior:**
50+
- `cross prune`: Finds unused remotes, asks for confirmation, removes them, prunes stale worktrees
51+
- `cross prune <remote>`: Removes all patches for that remote, then removes the remote itself
52+
- **Status:** COMPLETE - Ready for v0.2.1 release
53+
54+
### P2: Medium Priority
55+
- [x] **Fix `cross cd` and `cross wt` commands** - Correct behavior for navigation
56+
- **Issue:** Commands needed proper separation of concerns and clipboard functionality
57+
- **Design Intent:**
58+
- `cross wt [path]` → Navigate to WORKTREE (`.git/cross/worktrees/`) - for git history operations
59+
- `cross cd [path]` → Navigate to LOCAL_PATH (patched directory) - for editing files
60+
- With explicit path: Open subshell in target directory
61+
- Without path (fzf): Copy selected path to clipboard for flexibility
62+
- **Implemented Behavior:**
63+
- **With path argument:** Opens subshell in target directory (cd → local_path, wt → worktree)
64+
- **Without path argument:** Uses fzf to select patch, then copies path to clipboard
65+
- **Rationale:** User with explicit path wants immediate access; user selecting needs flexibility
66+
- **Clipboard Support:**
67+
- macOS: Uses `pbcopy`
68+
- Linux: Uses `xclip` or `xsel` (with fallback)
69+
- Shows success message: "Path copied to clipboard: <path>"
70+
- **Implementation:**
71+
- ✅ Justfile.cross (lines 754-865): Simplified `_open_shell` with path-based logic
72+
- ✅ Go (src-go/main.go lines 681-818): Removed `--copy` flag, path-based behavior
73+
- ✅ Rust (src-rust/src/main.rs lines 424-525, 905-910): Added clipboard support, path-based logic
74+
- ✅ Test coverage (test/017_cd_wt_fix.sh): Basic functionality and error handling
75+
- **Effort:** 3 hours (completed 2025-01-06)
76+
- **Files:** `Justfile.cross`, `src-go/main.go`, `src-rust/src/main.rs`, `test/common.sh`, `test/017_cd_wt_fix.sh`
77+
- **Impact:** HIGH - Fixes command behavior, adds clipboard workflow for flexibility
78+
- **Status:** COMPLETE - All three implementations updated with feature parity
79+
80+
- [ ] **Single file patch capability** - Review and propose implementation (tool and test) to be able to patch even single file. If not easily possible without major refactoring, evaluate new command "patch-file".
81+
- **Effort:** 4-6 hours (includes research)
82+
83+
- [ ] **Improve interactive `fzf` selection** in native implementations - Better UI, preview panes, multi-select for batch operations.
84+
- **Effort:** 3-5 hours
85+
86+
### P3: Low Priority (UX Improvements)
87+
88+
- [ ] **Context-aware `cross diff` command** - Smart diff behavior based on current working directory
89+
- **Issue:** Currently `cross diff` shows diffs for ALL patches regardless of PWD
90+
- **Desired Behavior:**
91+
- When executed inside a patched local_path: Show diff only for that specific patch
92+
- When executed outside any patch (anywhere in repo): Show diffs for all patches
93+
- When given explicit path argument: Show diff for that specific patch with informative header
94+
- **Effort:** 4-6 hours (includes complexity analysis and implementation)
95+
- **Files:** `Justfile.cross`, `src-go/main.go`, `src-rust/src/main.rs`, `test/016_diff_context.sh`
96+
- **Complexity Analysis Required:**
97+
- **Current Implementation:**
98+
- Justfile: Uses `_resolve_context2` to resolve patch from path/PWD (lines 578-592)
99+
- Go: Iterates all patches, filters by explicit path arg only (lines 880-911)
100+
- Rust: Same as Go - no PWD detection (lines 1194-1214)
101+
- **Required Changes:**
102+
- **Low complexity** for Justfile (already has PWD resolution via `_resolve_context2`)
103+
- **Medium complexity** for Go/Rust (need to add PWD detection logic)
104+
- Need to add: Get PWD → Check if inside patch → Filter patches accordingly
105+
- **Implementation Strategy:**
106+
1. Detect current working directory relative to repo root
107+
2. Check if CWD is within any patch's local_path
108+
3. Filter patches based on context:
109+
- If inside patch + no explicit arg → show only that patch
110+
- If outside patches + no explicit arg → show all patches
111+
- If explicit arg provided → show only that patch (current behavior)
112+
4. Add informative header: "Diff for patch: {local_path}" when contextual
113+
- **Impact Assessment:**
114+
- **User Experience:** HIGH - More intuitive, reduces noise
115+
- **Breaking Changes:** NONE - Backwards compatible (explicit args work same)
116+
- **Code Complexity:** LOW to MEDIUM
117+
- Justfile: ~10-15 lines (reuse existing `_resolve_context2`)
118+
- Go: ~20-30 lines (add `getCurrentPath()` helper)
119+
- Rust: ~20-30 lines (add `get_current_path()` helper)
120+
- **Testing:** MEDIUM - Need scenarios for:
121+
1. Diff from inside patch (should show only that patch)
122+
2. Diff from outside patches (should show all)
123+
3. Diff with explicit arg (should show specified patch)
124+
4. Diff from nested subdirectory within patch (should resolve parent patch)
125+
- **Edge Cases:**
126+
- CWD inside nested subdirectory of patch (needs parent resolution)
127+
- Multiple patches in nested directories (resolve closest parent)
128+
- Symlinked directories (should follow symlinks)
129+
- **Priority Rationale:** Low priority - UX improvement, not a bug
130+
- **Status:** Documented for future implementation
131+
132+
### Completed Enhancements
41133

42134
## Known Issues (To FIX)
43135

136+
### ✅ P0: Sync Command Data Loss (FIXED)
137+
138+
- [x] **Issue:** The `cross sync` command in Go (and Rust) did not preserve local uncommitted changes. When users modified files in patched directory and ran sync, changes were lost/reverted.
139+
140+
**Fix Applied (2025-01-06):**
141+
- ✅ Go implementation (`src-go/main.go`): Added complete stash/restore workflow
142+
- ✅ Rust implementation (`src-rust/src/main.rs`): Added complete stash/restore workflow
143+
- ✅ Justfile implementation (`Justfile.cross`): Added explicit stash/restore workflow with file deletion detection
144+
- ✅ Test coverage enhanced (`test/004_sync.sh`): 6 comprehensive test scenarios
145+
- ✅ Added cleanup logic between tests to handle conflicted worktree states
146+
- ✅ Added file deletion detection: removes local files that were deleted upstream
147+
148+
**Workflow Now:**
149+
```
150+
1. Detect uncommitted changes (including untracked files) in local_path
151+
2. Rsync git-tracked files WITH current uncommitted content: local_path → worktree
152+
3. Stash uncommitted changes in local_path (with --include-untracked)
153+
4. Commit changes in worktree
154+
5. Check worktree state (recover from detached HEAD, abort in-progress operations)
155+
6. Pull --rebase from upstream
156+
7. Handle conflicts (exit if detected)
157+
8. Detect and remove local files that were deleted upstream
158+
9. Rsync worktree → local_path
159+
10. Restore stashed changes
160+
11. Detect and report merge conflicts
161+
```
162+
163+
**Test Scenarios Covered:**
164+
1. ✅ Basic sync with no local changes
165+
2. ✅ Sync with uncommitted local changes (preserves them)
166+
3. ✅ Sync with committed local changes
167+
4. ✅ Sync with conflicting changes (graceful failure)
168+
5. ✅ Sync with deleted upstream file (removes locally)
169+
6. ✅ Sync with new upstream file (adds locally)
170+
171+
**Testing:** Run `just cross-test 004` to validate all scenarios
172+
**Impact:** Data loss risk eliminated, file synchronization complete
173+
**Status:** FIXED - Ready for v0.2.1 release
44174
- [x] Updates to Crossfile can create duplicit lines (especially if user add spaces between remote_spec and local_spec.) Ideally we shall only check whether the local/path is already specified, and if yes then avoid update and avoid patch (as path exist.)
45175
- [x] Extend the tests, start using <https://github.com/runtipi/runtipi-appstore/> and sub-path apps/ for "patches". Document this in test-case design.
46176
- [x] Looks like the worktree created dont have any more "sparse checkout". Extend the validation, ie: that no other top-level files present in checkouts (assuming sub-path is used on remote repo)

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.2.1

git.just

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
# coding agent
3+
CA_CMD := "opencode run"
4+
CA_AGENT := "--agent GitSummary"
5+
CA_MODEL := "--model openai/gpt-5.2"
6+
7+
# git squash and commit with ai assisted commit message against @{upstream}... only your changes
8+
git-squash I="":
9+
#!/usr/bin/env fish
10+
set msg "$(begin echo "$I"; git log @{u}...HEAD; git diff @{u}...HEAD; end | {{CA_CMD}} "Generate: Commit message" {{CA_AGENT}} {{CA_MODEL}} )" \
11+
&& git reset --soft @{u} && git commit -m "$msg" && git log -1
12+
13+
# print summary of changes in git repository
14+
git-summary D="3":
15+
#!/usr/bin/env fish
16+
begin; git log "@{$D day ago}" 2>/dev/null; git diff "@{$D day ago}" 2>/dev/null; end | {{CA_CMD}} "Generate: Changelog summary" {{CA_AGENT}} {{CA_MODEL}} | tee /dev/stderr | pbcopy
17+

0 commit comments

Comments
 (0)