Skip to content

Commit 96442b0

Browse files
committed
strategic update
1 parent 8858afc commit 96442b0

28 files changed

+913
-116
lines changed

Crossfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
# This is an example Crossfile, see more under `./examples`
33

44
# define upstream repos
5-
use git-cross https://github.com/epcim/git-cross.git
5+
cross use git-cross https://github.com/epcim/git-cross.git
66

77
# patch their paths to local
8-
patch git-cross:. vendor/git-cross
9-
sync
8+
cross patch git-cross:. vendor/git-cross
9+
cross sync
1010

11-
# git remote -v | grep fetch | column -t
12-
# git worktree list
11+
# cross exec git remote -v | grep fetch | column -t
12+
# cross exec git worktree list
1313

Justfile

Lines changed: 100 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export PATH := env_var('HOME') + "/bin:" + HOMEBREW_PREFIX + "/bin:" + env_var('
66
@cross +ARGS:
77
just {{ARGS}}
88

9+
# Auto-setup environment on first use
910
# Auto-setup environment on first use
1011
setup:
1112
#!/usr/bin/env fish
@@ -16,6 +17,24 @@ setup:
1617
end
1718
end
1819

20+
# Show this help message
21+
help:
22+
@echo "git-cross - Vendor parts of git repos into your project"
23+
@echo ""
24+
@echo "Usage:"
25+
@echo " just <command> [options]"
26+
@echo " ./cross <command> [options] (wrapper script)"
27+
@echo ""
28+
@echo "Available commands:"
29+
@just --list --unsorted
30+
@echo ""
31+
@echo "Quick start:"
32+
@echo " 1. Add upstream repo: just use <name> <url>"
33+
@echo " 2. Patch directory: just patch <name>:<path> <local_path>"
34+
@echo " 3. Sync from upstream: just sync"
35+
@echo " "
36+
@echo "For more details, see README.md"
37+
1938
# Check for required dependencies
2039
check-deps:
2140
#!/usr/bin/env fish
@@ -51,13 +70,18 @@ _resolve_context arg_rspec arg_lpath invocation_dir:
5170
# Check Crossfile for matching local path
5271
if test -f Crossfile
5372
while read -l line
54-
# Parse: patch <remote>:<path> <local> ...
73+
# Parse: [cross] patch <remote>:<path> <local> ...
5574
set parts (string split " " $line)
56-
if test "$parts[1]" = "patch"
57-
set p_local $parts[3]
75+
set cmd_idx 1
76+
if test "$parts[1]" = "cross"
77+
set cmd_idx 2
78+
end
79+
80+
if test "$parts[$cmd_idx]" = "patch"
81+
set p_local $parts[(math $cmd_idx + 2)] # Local path is 2 positions after command
5882
# Check if CWD is inside the patch local path
5983
if string match -q "$p_local*" "$rel_cwd"
60-
set rspec $parts[2]
84+
set rspec $parts[(math $cmd_idx + 1)] # Remote spec is 1 position after command
6185
set lpath $p_local
6286
echo "$rspec|$lpath"
6387
exit 0
@@ -82,117 +106,121 @@ update_crossfile cmd:
82106
echo "{{cmd}}" >> $CROSSFILE
83107
end
84108

109+
# Execute arbitrary shell command
110+
exec +CMD:
111+
{{CMD}}
112+
85113
# Add a remote repository
86-
use name url: check-deps (update_crossfile "use " + name + " " + url)
114+
use name url: check-deps (update_crossfile "cross use " + name + " " + url)
87115
echo here\
88116
@git remote show | grep -q "^{{name}}$" \
89117
|| { git remote add {{name}} {{url}} && echo "Added remote: {{name}} -> {{url}}"; }
90118

91119
# Patch a directory from a remote into a local path
92-
patch remote_spec local_path branch="master": check-deps (update_crossfile "patch " + remote_spec + " " + local_path + (if branch != "master" { " " + branch } else { "" }))
120+
patch remote_spec local_path branch="master": check-deps (update_crossfile "cross patch " + remote_spec + " " + local_path + (if branch != "master" { " " + branch } else { "" }))
93121
#!/usr/bin/env fish
94122
set parts (string split -m1 : {{remote_spec}})
95123
set remote $parts[1]
96124
set remote_path $parts[2]
97-
98125
test -n "$remote" -a -n "$remote_path" || begin
99126
echo "Error: Invalid format. Use remote:path"
100127
exit 1
101128
end
102-
103129
git remote show | grep -q "^$remote\$" || begin
104130
echo "Error: Remote '$remote' not found. Run: just use $remote <url>"
105131
exit 1
106132
end
107-
108133
set hash (echo $remote_path | md5sum | cut -d' ' -f1)
109134
set wt ".git/cross/worktrees/$remote"_"$hash"
110-
111135
echo "Setting up hidden worktree at $wt..."
112-
113136
if not test -d $wt
114137
mkdir -p (dirname $wt)
115-
116138
echo "Fetching $remote {{branch}}..."
117139
git fetch $remote {{branch}}
118-
119140
git rev-parse --verify "$remote/{{branch}}" >/dev/null || begin
120141
echo "Error: $remote/{{branch}} does not exist"
121142
exit 1
122143
end
123-
124144
git worktree add --no-checkout -B "cross/$remote/{{branch}}/$hash" $wt "$remote/{{branch}}" >/dev/null 2>&1
125-
126145
set sparse (git -C $wt rev-parse --git-path info/sparse-checkout)
127146
mkdir -p (dirname $sparse)
128147
echo "/$remote_path/" > $sparse
129-
130148
git -C $wt config core.sparseCheckout true
131149
git -C $wt read-tree -mu HEAD >/dev/null 2>&1
132150
end
133-
134151
echo "Syncing files to {{local_path}}..."
135152
mkdir -p {{local_path}}
136153
rsync -av --delete --exclude .git $wt/$remote_path/ {{local_path}}/
137-
138154
echo "Done. {{local_path}} now contains files from $remote:$remote_path"
139155

156+
# Internal: sync local paths from Crossfile
157+
_sync_from_crossfile:
158+
#!/usr/bin/env fish
159+
# Check if Crossfile exists
160+
if not test -f Crossfile
161+
echo "Note: No Crossfile found. Only worktrees were updated."
162+
exit 0
163+
end
164+
echo "Syncing changes to local paths..."
165+
while read -l line
166+
set parts (string split " " $line)
167+
# Determine if line starts with 'cross' prefix
168+
set cmd_idx 1
169+
if test "$parts[1]" = "cross"
170+
set cmd_idx 2
171+
end
172+
# Handle 'patch' command: sync worktree to local path
173+
if test "$parts[$cmd_idx]" = "patch"
174+
set rspec $parts[(math $cmd_idx + 1)]
175+
set lpath $parts[(math $cmd_idx + 2)]
176+
set rparts (string split -m1 : $rspec)
177+
set remote $rparts[1]
178+
set rpath $rparts[2]
179+
set hash (echo $rpath | md5sum | cut -d' ' -f1)
180+
set wt ".git/cross/worktrees/$remote"_"$hash"
181+
# Sync only if worktree exists
182+
if test -d $wt
183+
echo " $lpath ← $rspec"
184+
mkdir -p $lpath
185+
rsync -a --delete --exclude .git $wt/$rpath/ $lpath/ >/dev/null 2>&1
186+
end
187+
# Handle 'exec' command: run post-hooks or custom commands
188+
else if test "$parts[$cmd_idx]" = "exec"
189+
set cmd_str (string join " " $parts[(math $cmd_idx + 1)..-1])
190+
echo " exec: $cmd_str"
191+
eval $cmd_str
192+
end
193+
end < Crossfile
194+
echo "✅ Sync complete"
195+
140196
# Sync all patches from upstream
141197
sync: check-deps
142198
#!/usr/bin/env fish
143199
test -d .git/cross/worktrees || begin
144200
echo "No worktrees found."
145201
exit 0
146202
end
147-
148203
# First, update all worktrees from upstream
149204
for wt in .git/cross/worktrees/*
150205
test -d $wt || continue
151206
echo "Updating $wt..."
152-
153207
# Stash local changes if any
154208
set dirty (git -C $wt status --porcelain)
155209
if test -n "$dirty"
156210
echo "Stashing local changes in $wt..."
157211
git -C $wt stash
158212
end
159-
160213
set branch (git -C $wt rev-parse --abbrev-ref HEAD)
161214
set upstream (git -C $wt rev-parse --abbrev-ref --symbolic-full-name '@{upstream}')
162215
git -C $wt pull --rebase >/dev/null 2>&1
163-
164216
# Pop stash if we stashed
165217
if test -n "$dirty"
166218
echo "Popping stash in $wt..."
167219
git -C $wt stash pop
168220
end
169221
end
170-
171222
# Then, sync changes to local paths using Crossfile
172-
if test -f Crossfile
173-
echo "Syncing changes to local paths..."
174-
while read -l line
175-
set parts (string split " " $line)
176-
if test "$parts[1]" = "patch"
177-
set rspec $parts[2]
178-
set lpath $parts[3]
179-
set rparts (string split -m1 : $rspec)
180-
set remote $rparts[1]
181-
set rpath $rparts[2]
182-
183-
set hash (echo $rpath | md5sum | cut -d' ' -f1)
184-
set wt ".git/cross/worktrees/$remote"_"$hash"
185-
186-
if test -d $wt
187-
echo " $lpath ← $rspec"
188-
rsync -a --delete --exclude .git $wt/$rpath/ $lpath/ >/dev/null 2>&1
189-
end
190-
end
191-
end < Crossfile
192-
echo "✅ Sync complete"
193-
else
194-
echo "Note: No Crossfile found. Only worktrees were updated."
195-
end
223+
just --quiet _sync_from_crossfile
196224

197225
# Diff local vs upstream
198226
diff arg_rspec="" arg_lpath="": check-deps
@@ -289,9 +317,14 @@ list: check-deps
289317

290318
while read -l line
291319
set parts (string split " " $line)
292-
if test "$parts[1]" = "patch"
293-
set remote_spec $parts[2]
294-
set local_path $parts[3]
320+
set cmd_idx 1
321+
if test "$parts[1]" = "cross"
322+
set cmd_idx 2
323+
end
324+
325+
if test "$parts[$cmd_idx]" = "patch"
326+
set remote_spec $parts[(math $cmd_idx + 1)] # Remote spec
327+
set local_path $parts[(math $cmd_idx + 2)] # Local path
295328
set rparts (string split -m1 : $remote_spec)
296329
printf "%-20s %-30s %-20s\n" $rparts[1] $rparts[2] $local_path
297330
end
@@ -310,9 +343,14 @@ status: check-deps
310343

311344
while read -l line
312345
set parts (string split " " $line)
313-
if test "$parts[1]" = "patch"
314-
set remote_spec $parts[2]
315-
set local_path $parts[3]
346+
set cmd_idx 1
347+
if test "$parts[1]" = "cross"
348+
set cmd_idx 2
349+
end
350+
351+
if test "$parts[$cmd_idx]" = "patch"
352+
set remote_spec $parts[(math $cmd_idx + 1)] # Remote spec
353+
set local_path $parts[(math $cmd_idx + 2)] # Local path
316354
set rparts (string split -m1 : $remote_spec)
317355
set remote $rparts[1]
318356
set rpath $rparts[2]
@@ -370,8 +408,15 @@ replay: check-deps
370408
continue
371409
end
372410

373-
echo "▶ just $line"
374-
just $line
411+
# If line starts with "cross", execute it directly via just
412+
if string match -q "cross *" $line
413+
echo "▶ just $line"
414+
just $line
415+
else
416+
# Backward compatibility for old format (implicit "cross")
417+
echo "▶ just cross $line"
418+
just cross $line
419+
end
375420
end < Crossfile
376421

377422
echo "✅ Crossfile replay complete"

README.md

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ This allows you to "vendor" files from other repositories directly into your sou
4040

4141
```bash
4242
cd $YOUR_REPO
43+
just cross help # Show all commands
4344
just cross use demo https://github.com/example/demo.git
4445
just cross patch demo:docs vendor/docs
45-
just cross [sync|diff|push|list|status|...]
46+
just cross sync # Update from upstream
47+
just cross exec just posthook # Run post-hooks
4648
```
4749

4850
## Installation
@@ -198,6 +200,60 @@ vendor/docs Modified Synced No
198200
vendor/ansible/roles Clean 2 behind No
199201
```
200202

203+
---
204+
205+
#### `exec` - Run Custom Commands
206+
207+
Execute arbitrary shell commands or scripts from your `Crossfile`. Perfect for post-hooks, cleanup tasks, or custom automation:
208+
209+
```bash
210+
just cross exec <command>
211+
```
212+
213+
**Example:**
214+
215+
```bash
216+
# Simple command
217+
just cross exec echo "Setup complete"
218+
219+
# Run a recipe from your own Justfile
220+
just cross exec just posthook
221+
222+
# Chain multiple commands
223+
just cross exec "npm install && npm run build"
224+
```
225+
226+
**Multiline commands in Crossfile:**
227+
228+
For complex commands, you can use shell escaping or line continuation:
229+
230+
```
231+
# Crossfile
232+
cross use demo https://github.com/example/demo.git
233+
cross patch demo:src vendor/src
234+
235+
# Simple exec
236+
cross exec echo "Patching complete"
237+
238+
# Multiline via shell continuation
239+
cross exec sh -c 'echo "Step 1" && echo "Step 2" && echo "Step 3"'
240+
241+
# Call your own Justfile recipes
242+
cross exec just build
243+
cross exec just deploy
244+
```
245+
246+
**Common use cases:**
247+
248+
- **Post-patch hooks**: Run build scripts after vendoring dependencies
249+
- **Cleanup tasks**: Remove unwanted files or directories
250+
- **Custom automation**: Trigger CI/CD pipelines, notifications, etc.
251+
- **Integration**: Call your project's own `just` recipes or shell scripts
252+
253+
> **Note**: The `exec` command runs in your project's root directory with access to your full shell environment. Commands are executed via `eval` in `fish`.
254+
255+
---
256+
201257
**Status indicators:**
202258

203259
- **DIFF**: `Clean` | `Modified` | `Missing WT`

TODO.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Known issues:
22

3-
- re-test `replay` of default Crossfile and add additional test cases
4-
- patch/replay fails if "vendor" folder dont exist, `patch` function shall create required directories
5-
- Crossfile commands shall start with "cross"
6-
- Feature post hook. How to implement some clenaup per "patch". Alt1: Crossfile could be understood as "shell" file. Alt2: we could define post_hooks in Crossfile per patch, and innter would be shell cmds to executed.
3+
- [x] re-test `replay` of default Crossfile and add additional test cases
4+
- [x] patch/replay fails if "vendor" folder dont exist, `patch` function shall create required directories
5+
- [x] Crossfile commands shall start with "cross" (or plugin name). Future: "cross" could be the name of the tracking remote/plugin. `replay` should support 3rd party actions (e.g., `just <plugin> <cmd>`).
6+
- Crossfile commands shall start with "cross" (or plugin name). Future: "cross" could be the name of the tracking remote/plugin. `replay` should support 3rd party actions (e.g., `just <plugin> <cmd>`).
7+
- Crossfile commands shall start with "cross" (or plugin name). Future: "cross" could be the name of the tracking remote/plugin. `replay` should support 3rd party actions (e.g., `just <plugin> <cmd>`).
8+
- [x] Feature post hook: Implemented via `cross exec <cmd>`. User can add `cross exec just posthook` to Crossfile to trigger their own Justfile recipes.

examples/Crossfile-001

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# upstream
2-
use khue https://github.com/khuedoan/homelab
2+
cross use khue https://github.com/khuedoan/homelab
33

44
# cross patches
55
# ex: origin:path/remote/dir [local/path/to/dir] [branch]
6-
patch khue:/metal deploy/metal
6+
cross patch khue:/metal deploy/metal
77

0 commit comments

Comments
 (0)