@humanu/orchestra 0.5.77 → 0.5.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/{gw.js → orchestra-cli.js} +1 -1
- package/bin/orchestra.js +2 -2
- package/install.js +41 -42
- package/package.json +2 -3
- package/resources/api/git.sh +4 -444
- package/resources/api/tmux.sh +4 -2804
- package/resources/prebuilt/linux-x64/{gw-env-copy → env-copy} +0 -0
- package/resources/prebuilt/linux-x64/orchestra +0 -0
- package/resources/prebuilt/macos-arm64/{gw-env-copy → env-copy} +0 -0
- package/resources/prebuilt/macos-arm64/orchestra +0 -0
- package/resources/prebuilt/macos-intel/{gw-env-copy → env-copy} +0 -0
- package/resources/prebuilt/macos-intel/orchestra +0 -0
- package/resources/scripts/{gw.sh → orchestra-cli.sh} +14 -14
- package/resources/scripts/orchestra-local.sh +6 -6
- package/resources/scripts/{gwr.sh → orchestra.sh} +11 -55
- package/resources/scripts/{shell/bridge → server/services}/ai.sh +4 -4
- package/resources/scripts/{gw-bridge.sh → server/services/dispatch.sh} +62 -59
- package/resources/scripts/server/services/git/api.sh +447 -0
- package/resources/scripts/{shell/git/bridge_check_branch.sh → server/services/git/check_branch_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_create_worktree.sh → server/services/git/create_worktree_api.sh} +3 -3
- package/resources/scripts/{shell/git/bridge_create_worktree_from_existing.sh → server/services/git/create_worktree_from_existing_api.sh} +2 -2
- package/resources/scripts/{shell/git/bridge_create_worktree_from_remote.sh → server/services/git/create_worktree_from_remote_api.sh} +2 -2
- package/resources/scripts/{shell/git/bridge_delete_branch_only.sh → server/services/git/delete_branch_only_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_delete_worktree.sh → server/services/git/delete_worktree_api.sh} +2 -2
- package/resources/scripts/{shell/git/bridge_delete_worktree_only.sh → server/services/git/delete_worktree_only_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_enhanced_git_status.sh → server/services/git/enhanced_git_status_api.sh} +3 -3
- package/resources/scripts/{shell/git/bridge_git_status.sh → server/services/git/git_status_api.sh} +2 -2
- package/resources/scripts/{shell/git/bridge_list_worktrees.sh → server/services/git/list_worktrees_api.sh} +2 -2
- package/resources/scripts/server/services/git/merge_api.sh +12 -0
- package/resources/scripts/{shell/git/bridge_merge_from_primary.sh → server/services/git/merge_from_primary_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_merge_into_primary.sh → server/services/git/merge_into_primary_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_primary_branch.sh → server/services/git/primary_branch_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_rebase_from_primary.sh → server/services/git/rebase_from_primary_api.sh} +1 -1
- package/resources/scripts/server/services/git/repo_api.sh +12 -0
- package/resources/scripts/{shell/git/bridge_repo_info.sh → server/services/git/repo_info_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_squash_into_primary.sh → server/services/git/squash_into_primary_api.sh} +1 -1
- package/resources/scripts/{shell/git/bridge_switch_worktree.sh → server/services/git/switch_worktree_api.sh} +2 -2
- package/resources/scripts/server/services/git/worktree_api.sh +17 -0
- package/resources/scripts/{shell/bridge/utils.sh → server/services/json.sh} +23 -23
- package/resources/scripts/{shell/bridge → server/services/session}/tmux.sh +14 -14
- package/resources/scripts/server/session/tmux_api.sh +2806 -0
- package/resources/scripts/services.sh +6 -0
- package/resources/scripts/shell/AGENTS.md +63 -74
- package/resources/scripts/shell/build/dependencies.sh +33 -0
- package/resources/scripts/shell/build/install.sh +7 -0
- package/resources/scripts/shell/build/load.sh +10 -0
- package/resources/scripts/shell/build/logging.sh +6 -0
- package/resources/scripts/shell/build/rust.sh +18 -0
- package/resources/scripts/shell/build/services.sh +17 -0
- package/resources/scripts/shell/build/{build_usage.sh → usage.sh} +6 -6
- package/resources/scripts/shell/cli_load.sh +9 -0
- package/resources/scripts/shell/{gw_env_copy.sh → env_copy.sh} +11 -11
- package/resources/scripts/shell/env_copy_command.sh +2 -2
- package/resources/scripts/shell/git/checkout_worktree.sh +4 -4
- package/resources/scripts/shell/git/create_worktree.sh +2 -2
- package/resources/scripts/shell/git/delete_worktree.sh +1 -1
- package/resources/scripts/shell/git/merge.sh +1 -1
- package/resources/scripts/shell/git/repo.sh +1 -1
- package/resources/scripts/shell/git/worktree.sh +1 -1
- package/resources/scripts/shell/gwr/check-updates.sh +1 -1
- package/resources/scripts/shell/gwr_binary.sh +4 -4
- package/resources/scripts/shell/gwr_load.sh +1 -1
- package/resources/scripts/shell/gwr_services.sh +10 -0
- package/resources/scripts/shell/gwr_usage.sh +10 -10
- package/resources/scripts/shell/orchestra-command-hook.sh +15 -15
- package/resources/scripts/shell/orchestra-local.sh +6 -6
- package/resources/scripts/shell/tmux/new_session_command.sh +1 -1
- package/bin/gwr.js +0 -10
- package/resources/scripts/shell/build/build_bridge.sh +0 -17
- package/resources/scripts/shell/build/build_dependencies.sh +0 -33
- package/resources/scripts/shell/build/build_install.sh +0 -7
- package/resources/scripts/shell/build/build_load.sh +0 -10
- package/resources/scripts/shell/build/build_logging.sh +0 -6
- package/resources/scripts/shell/build/build_rust.sh +0 -18
- package/resources/scripts/shell/git/bridge_merge.sh +0 -12
- package/resources/scripts/shell/git/bridge_repo.sh +0 -12
- package/resources/scripts/shell/git/bridge_worktree.sh +0 -17
- package/resources/scripts/shell/gw_legacy_wrappers.sh +0 -7
- package/resources/scripts/shell/gw_load.sh +0 -10
- package/resources/scripts/shell/gwr_bridge.sh +0 -10
- /package/resources/scripts/shell/{gw_debug.sh → cli_debug.sh} +0 -0
- /package/resources/scripts/shell/{gw_err.sh → cli_err.sh} +0 -0
- /package/resources/scripts/shell/{gw_have_cmd.sh → cli_have_cmd.sh} +0 -0
- /package/resources/scripts/shell/{gw_info.sh → cli_info.sh} +0 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
###############################################################################
|
|
4
|
+
# git.sh – Git Worktree API Module
|
|
5
|
+
# ---------------------------------------------------------------------------
|
|
6
|
+
# This module provides a centralized API for all Git operations used by
|
|
7
|
+
# orchestra-cli.sh and services.sh. It abstracts Git worktree management
|
|
8
|
+
# and other Git operations into a clean, consistent interface.
|
|
9
|
+
###############################################################################
|
|
10
|
+
|
|
11
|
+
# --------------------------- Repository Information -------------------------
|
|
12
|
+
|
|
13
|
+
# Get the Git repository root directory
|
|
14
|
+
git_repo_root() {
|
|
15
|
+
git rev-parse --show-toplevel 2>/dev/null || true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Check if current directory is in a Git repository
|
|
19
|
+
git_is_repo() {
|
|
20
|
+
git rev-parse --git-dir >/dev/null 2>&1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# Get current absolute path (resolved)
|
|
24
|
+
git_current_path() {
|
|
25
|
+
pwd -P
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Get the primary repository root (shared across all worktrees)
|
|
29
|
+
git_shared_root() {
|
|
30
|
+
local common_dir
|
|
31
|
+
common_dir="$(git rev-parse --git-common-dir 2>/dev/null)" || return 1
|
|
32
|
+
if [[ -z "$common_dir" ]]; then
|
|
33
|
+
return 1
|
|
34
|
+
fi
|
|
35
|
+
if [[ "$common_dir" != /* ]]; then
|
|
36
|
+
common_dir="$(pwd -P)/$common_dir"
|
|
37
|
+
fi
|
|
38
|
+
dirname "$common_dir"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Convert branch name to slug (replace / with -)
|
|
42
|
+
git_branch_to_slug() {
|
|
43
|
+
echo "$1" | tr '/' '-'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# --------------------------- Branch Operations ------------------------------
|
|
47
|
+
|
|
48
|
+
# Check if a branch exists
|
|
49
|
+
git_branch_exists() {
|
|
50
|
+
local branch_name="$1"
|
|
51
|
+
git rev-parse --verify "$branch_name" >/dev/null 2>&1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Get current branch name (or empty if detached HEAD)
|
|
55
|
+
git_current_branch() {
|
|
56
|
+
git symbolic-ref --short HEAD 2>/dev/null || true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Get current commit SHA (short format)
|
|
60
|
+
git_current_commit_short() {
|
|
61
|
+
git rev-parse --short HEAD 2>/dev/null || true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Delete a local branch (force delete)
|
|
65
|
+
git_delete_branch() {
|
|
66
|
+
local branch_name="$1"
|
|
67
|
+
git branch -D "$branch_name" 2>/dev/null || true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# --------------------------- Worktree Operations ----------------------------
|
|
71
|
+
|
|
72
|
+
# Ensure ignore rules for worktrees directory exist in both .gitignore and .git/info/exclude
|
|
73
|
+
# Non-destructive and idempotent; creates files if needed
|
|
74
|
+
git_ensure_ignore_worktrees() {
|
|
75
|
+
local root; root="$(git_repo_root)"
|
|
76
|
+
[[ -z "$root" ]] && return 1
|
|
77
|
+
|
|
78
|
+
# .git/info/exclude (local-only)
|
|
79
|
+
local exclude_file="$root/.git/info/exclude"
|
|
80
|
+
mkdir -p "$root/.git/info" 2>/dev/null || true
|
|
81
|
+
if [[ -f "$exclude_file" ]]; then
|
|
82
|
+
if ! grep -q -E '^worktrees/(\s*)?$' "$exclude_file" 2>/dev/null; then
|
|
83
|
+
echo 'worktrees/' >> "$exclude_file"
|
|
84
|
+
fi
|
|
85
|
+
else
|
|
86
|
+
echo 'worktrees/' >> "$exclude_file"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Top-level .gitignore (shared)
|
|
90
|
+
local gi_file="$root/.gitignore"
|
|
91
|
+
if [[ -f "$gi_file" ]]; then
|
|
92
|
+
if ! grep -q -E '^worktrees/(\s*)?$' "$gi_file" 2>/dev/null; then
|
|
93
|
+
echo 'worktrees/' >> "$gi_file"
|
|
94
|
+
fi
|
|
95
|
+
else
|
|
96
|
+
echo 'worktrees/' > "$gi_file"
|
|
97
|
+
fi
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# List all worktrees: returns lines with "path\tbranch\tsha_short"
|
|
101
|
+
git_list_worktrees() {
|
|
102
|
+
local root; root="$(git_repo_root)"
|
|
103
|
+
[[ -z "$root" ]] && return 1
|
|
104
|
+
|
|
105
|
+
local current_path=""
|
|
106
|
+
local current_branch=""
|
|
107
|
+
local current_head=""
|
|
108
|
+
|
|
109
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
110
|
+
case "$line" in
|
|
111
|
+
worktree\ *)
|
|
112
|
+
current_path="${line#worktree }"
|
|
113
|
+
;;
|
|
114
|
+
HEAD\ *)
|
|
115
|
+
current_head="${line#HEAD }"
|
|
116
|
+
;;
|
|
117
|
+
branch\ *)
|
|
118
|
+
current_branch="${line#branch }"
|
|
119
|
+
if [[ "$current_branch" == refs/heads/* ]]; then
|
|
120
|
+
current_branch="${current_branch#refs/heads/}"
|
|
121
|
+
fi
|
|
122
|
+
;;
|
|
123
|
+
*)
|
|
124
|
+
;;
|
|
125
|
+
esac
|
|
126
|
+
|
|
127
|
+
if [[ -z "$line" ]]; then
|
|
128
|
+
if [[ -n "$current_path" ]]; then
|
|
129
|
+
printf '%s\t%s\t%s\n' "$current_path" "$current_branch" "${current_head:0:7}"
|
|
130
|
+
fi
|
|
131
|
+
current_path=""
|
|
132
|
+
current_branch=""
|
|
133
|
+
current_head=""
|
|
134
|
+
fi
|
|
135
|
+
done < <(cd "$root" && git worktree list --porcelain)
|
|
136
|
+
|
|
137
|
+
if [[ -n "$current_path" ]]; then
|
|
138
|
+
printf '%s\t%s\t%s\n' "$current_path" "$current_branch" "${current_head:0:7}"
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Get branch name for a given worktree path
|
|
143
|
+
git_worktree_path_to_branch() {
|
|
144
|
+
local path="$1"
|
|
145
|
+
local root; root="$(git_repo_root)"
|
|
146
|
+
[[ -z "$root" ]] && return 1
|
|
147
|
+
|
|
148
|
+
local current_path=""
|
|
149
|
+
local current_branch=""
|
|
150
|
+
|
|
151
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
152
|
+
case "$line" in
|
|
153
|
+
worktree\ *)
|
|
154
|
+
current_path="${line#worktree }"
|
|
155
|
+
;;
|
|
156
|
+
branch\ *)
|
|
157
|
+
current_branch="${line#branch }"
|
|
158
|
+
if [[ "$current_branch" == refs/heads/* ]]; then
|
|
159
|
+
current_branch="${current_branch#refs/heads/}"
|
|
160
|
+
fi
|
|
161
|
+
;;
|
|
162
|
+
*)
|
|
163
|
+
;;
|
|
164
|
+
esac
|
|
165
|
+
|
|
166
|
+
if [[ -z "$line" ]]; then
|
|
167
|
+
if [[ "$current_path" == "$path" ]]; then
|
|
168
|
+
printf '%s\n' "$current_branch"
|
|
169
|
+
return 0
|
|
170
|
+
fi
|
|
171
|
+
current_path=""
|
|
172
|
+
current_branch=""
|
|
173
|
+
fi
|
|
174
|
+
done < <(cd "$root" && git worktree list --porcelain)
|
|
175
|
+
|
|
176
|
+
return 1
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# Get worktree path for a given branch name
|
|
180
|
+
git_branch_to_worktree_path() {
|
|
181
|
+
local branch_name="$1"
|
|
182
|
+
local root; root="$(git_repo_root)"
|
|
183
|
+
[[ -z "$root" ]] && return 1
|
|
184
|
+
|
|
185
|
+
local current_path=""
|
|
186
|
+
local current_branch=""
|
|
187
|
+
|
|
188
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
189
|
+
case "$line" in
|
|
190
|
+
worktree\ *)
|
|
191
|
+
current_path="${line#worktree }"
|
|
192
|
+
;;
|
|
193
|
+
branch\ *)
|
|
194
|
+
current_branch="${line#branch }"
|
|
195
|
+
if [[ "$current_branch" == refs/heads/* ]]; then
|
|
196
|
+
current_branch="${current_branch#refs/heads/}"
|
|
197
|
+
fi
|
|
198
|
+
;;
|
|
199
|
+
*)
|
|
200
|
+
;;
|
|
201
|
+
esac
|
|
202
|
+
|
|
203
|
+
if [[ -z "$line" ]]; then
|
|
204
|
+
if [[ "$current_branch" == "$branch_name" ]]; then
|
|
205
|
+
printf '%s\n' "$current_path"
|
|
206
|
+
return 0
|
|
207
|
+
fi
|
|
208
|
+
current_path=""
|
|
209
|
+
current_branch=""
|
|
210
|
+
fi
|
|
211
|
+
done < <(cd "$root" && git worktree list --porcelain)
|
|
212
|
+
|
|
213
|
+
return 1
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# Create a new worktree with a new branch
|
|
217
|
+
git_create_worktree_with_branch() {
|
|
218
|
+
local branch_name="$1"
|
|
219
|
+
local worktree_path="$2"
|
|
220
|
+
local root; root="$(git_repo_root)"
|
|
221
|
+
[[ -z "$root" ]] && return 1
|
|
222
|
+
|
|
223
|
+
local relative_path="${worktree_path#$root/}"
|
|
224
|
+
(cd "$root" && git worktree add "$relative_path" -b "$branch_name" >&2)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Create a new worktree from existing branch
|
|
228
|
+
git_create_worktree_from_branch() {
|
|
229
|
+
local branch_name="$1"
|
|
230
|
+
local worktree_path="$2"
|
|
231
|
+
local root; root="$(git_repo_root)"
|
|
232
|
+
[[ -z "$root" ]] && return 1
|
|
233
|
+
|
|
234
|
+
local relative_path="${worktree_path#$root/}"
|
|
235
|
+
(cd "$root" && git worktree add "$relative_path" "$branch_name" >&2)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
git_create_worktree_for_existing_branch() {
|
|
239
|
+
local branch_name="$1"
|
|
240
|
+
local root; root="$(git_repo_root)"
|
|
241
|
+
[[ -z "$root" ]] && return 1
|
|
242
|
+
|
|
243
|
+
git_branch_exists "$branch_name" || { echo "Branch does not exist: $branch_name" >&2; return 1; }
|
|
244
|
+
|
|
245
|
+
git_ensure_ignore_worktrees >/dev/null 2>&1 || true
|
|
246
|
+
|
|
247
|
+
local worktree_path; worktree_path="$(git_build_worktree_path "$branch_name")"
|
|
248
|
+
mkdir -p "$(dirname "$worktree_path")"
|
|
249
|
+
|
|
250
|
+
git_create_worktree_from_branch "$branch_name" "$worktree_path" || return 1
|
|
251
|
+
|
|
252
|
+
echo "$worktree_path"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
git_create_worktree_from_remote_branch() {
|
|
256
|
+
local branch_name="$1"
|
|
257
|
+
local remote_name="${2:-origin}"
|
|
258
|
+
local root; root="$(git_repo_root)"
|
|
259
|
+
[[ -z "$root" ]] && return 1
|
|
260
|
+
|
|
261
|
+
git_ensure_ignore_worktrees >/dev/null 2>&1 || true
|
|
262
|
+
|
|
263
|
+
local worktree_path; worktree_path="$(git_build_worktree_path "$branch_name")"
|
|
264
|
+
mkdir -p "$(dirname "$worktree_path")"
|
|
265
|
+
|
|
266
|
+
local relative_path="${worktree_path#$root/}"
|
|
267
|
+
(cd "$root" && git worktree add "$relative_path" -b "$branch_name" "$remote_name/$branch_name" >&2) || return 1
|
|
268
|
+
|
|
269
|
+
echo "$worktree_path"
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# Remove a worktree (with force fallback)
|
|
273
|
+
git_remove_worktree() {
|
|
274
|
+
local worktree_path="$1"
|
|
275
|
+
local root; root="$(git_repo_root)"
|
|
276
|
+
[[ -z "$root" ]] && return 1
|
|
277
|
+
|
|
278
|
+
local relative_path="${worktree_path#$root/}"
|
|
279
|
+
(cd "$root" && {
|
|
280
|
+
git worktree remove "$relative_path" 2>/dev/null || \
|
|
281
|
+
git worktree remove --force "$relative_path" 2>/dev/null || true
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Get Git status for current directory
|
|
286
|
+
git_status() {
|
|
287
|
+
git status "$@"
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# Get short Git status for current directory
|
|
291
|
+
git_status_short() {
|
|
292
|
+
git status --short
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
# --------------------------- Worktree Path Utilities ------------------------
|
|
296
|
+
|
|
297
|
+
# Build standard worktree path for a branch
|
|
298
|
+
git_build_worktree_path() {
|
|
299
|
+
local branch_name="$1"
|
|
300
|
+
local root; root="$(git_repo_root)"
|
|
301
|
+
[[ -z "$root" ]] && return 1
|
|
302
|
+
|
|
303
|
+
local slug; slug="$(git_branch_to_slug "$branch_name")"
|
|
304
|
+
echo "$root/worktrees/$slug"
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# Check if a worktree exists for a branch
|
|
308
|
+
git_worktree_exists_for_branch() {
|
|
309
|
+
local branch_name="$1"
|
|
310
|
+
local path; path="$(git_branch_to_worktree_path "$branch_name")"
|
|
311
|
+
[[ -n "$path" ]]
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
# Get or create worktree path for a branch
|
|
315
|
+
git_ensure_worktree_for_branch() {
|
|
316
|
+
local branch_name="$1"
|
|
317
|
+
local root; root="$(git_repo_root)"
|
|
318
|
+
[[ -z "$root" ]] && return 1
|
|
319
|
+
|
|
320
|
+
# Check if worktree already exists
|
|
321
|
+
local existing_path; existing_path="$(git_branch_to_worktree_path "$branch_name")"
|
|
322
|
+
if [[ -n "$existing_path" ]]; then
|
|
323
|
+
echo "$existing_path"
|
|
324
|
+
return 0
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
# Create worktree if branch exists
|
|
328
|
+
if git_branch_exists "$branch_name"; then
|
|
329
|
+
# Ensure ignores before creating worktree
|
|
330
|
+
git_ensure_ignore_worktrees >/dev/null 2>&1 || true
|
|
331
|
+
local worktree_path; worktree_path="$(git_build_worktree_path "$branch_name")"
|
|
332
|
+
mkdir -p "$(dirname "$worktree_path")"
|
|
333
|
+
git_create_worktree_from_branch "$branch_name" "$worktree_path"
|
|
334
|
+
echo "$worktree_path"
|
|
335
|
+
return 0
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
return 1
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
# Create new branch and worktree
|
|
342
|
+
git_create_branch_and_worktree() {
|
|
343
|
+
local branch_name="$1"
|
|
344
|
+
local root; root="$(git_repo_root)"
|
|
345
|
+
[[ -z "$root" ]] && return 1
|
|
346
|
+
|
|
347
|
+
# Ensure ignores before creating worktree
|
|
348
|
+
git_ensure_ignore_worktrees >/dev/null 2>&1 || true
|
|
349
|
+
local worktree_path; worktree_path="$(git_build_worktree_path "$branch_name")"
|
|
350
|
+
mkdir -p "$(dirname "$worktree_path")"
|
|
351
|
+
git_create_worktree_with_branch "$branch_name" "$worktree_path"
|
|
352
|
+
echo "$worktree_path"
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
# --------------------------- Validation Functions ---------------------------
|
|
356
|
+
|
|
357
|
+
# Validate that we're in a Git repository
|
|
358
|
+
git_require_repo() {
|
|
359
|
+
git_is_repo || { echo "❌ Not a git repository or git command not found." >&2; return 1; }
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
# Get repository root with validation
|
|
363
|
+
git_require_repo_root() {
|
|
364
|
+
local root; root="$(git_repo_root)"
|
|
365
|
+
[[ -z "$root" ]] && { echo "❌ Not a git repository" >&2; return 1; }
|
|
366
|
+
echo "$root"
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
# --------------------------- Merge/Rebase Helpers ----------------------------
|
|
370
|
+
|
|
371
|
+
# Detect the primary branch name (default remote HEAD), fall back to common names
|
|
372
|
+
# Echoes the branch name on success; returns non-zero on failure
|
|
373
|
+
git_primary_branch() {
|
|
374
|
+
local head_ref branch
|
|
375
|
+
# Try origin/HEAD symbolic ref
|
|
376
|
+
head_ref=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || true)
|
|
377
|
+
if [[ -n "$head_ref" ]]; then
|
|
378
|
+
# refs/remotes/origin/main -> extract last component
|
|
379
|
+
branch="${head_ref##*/}"
|
|
380
|
+
[[ -n "$branch" ]] && { echo "$branch"; return 0; }
|
|
381
|
+
fi
|
|
382
|
+
# Try parsing `git remote show origin`
|
|
383
|
+
branch=$(git remote show origin 2>/dev/null | awk -F': ' '/HEAD branch/ {print $2; exit}')
|
|
384
|
+
if [[ -n "$branch" && "$branch" != "(unknown)" ]]; then
|
|
385
|
+
echo "$branch"; return 0
|
|
386
|
+
fi
|
|
387
|
+
# Fallback candidates
|
|
388
|
+
for cand in main master trunk; do
|
|
389
|
+
if git rev-parse --verify "$cand" >/dev/null 2>&1 || git rev-parse --verify "origin/$cand" >/dev/null 2>&1; then
|
|
390
|
+
echo "$cand"; return 0
|
|
391
|
+
fi
|
|
392
|
+
done
|
|
393
|
+
return 1
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
# Check if a worktree at path is clean (no changes)
|
|
397
|
+
# Usage: git_is_worktree_clean <path>
|
|
398
|
+
git_is_worktree_clean() {
|
|
399
|
+
local path="$1"
|
|
400
|
+
[[ -z "$path" ]] && return 1
|
|
401
|
+
local out
|
|
402
|
+
out=$(git -C "$path" status --porcelain 2>/dev/null || true)
|
|
403
|
+
[[ -z "$out" ]]
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
# Fetch from origin with prune for a given path
|
|
407
|
+
git_fetch_prune() {
|
|
408
|
+
local path="$1"
|
|
409
|
+
git -C "$path" fetch origin --prune 2>&1
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
# Fast-forward-only pull for the currently checked out branch at path
|
|
413
|
+
# Returns non-zero if not a fast-forward
|
|
414
|
+
# Usage: git_pull_ff_only <path>
|
|
415
|
+
git_pull_ff_only() {
|
|
416
|
+
local path="$1"
|
|
417
|
+
git -C "$path" pull --ff-only 2>&1
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
# Merge a ref into the currently checked out branch at target path
|
|
421
|
+
# Usage: git_merge_into <target_path> <ref>
|
|
422
|
+
git_merge_into() {
|
|
423
|
+
local target="$1"; local ref="$2"
|
|
424
|
+
git -C "$target" merge "$ref" 2>&1
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
# Rebase the current branch at target path onto upstream ref
|
|
428
|
+
# Usage: git_rebase_onto <target_path> <upstream_ref>
|
|
429
|
+
git_rebase_onto() {
|
|
430
|
+
local target="$1"; local upstream="$2"
|
|
431
|
+
git -C "$target" rebase "$upstream" 2>&1
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
# Squash-merge branch into the primary worktree path, then commit if changes staged
|
|
435
|
+
# Usage: git_squash_merge_into <primary_path> <branch>
|
|
436
|
+
git_squash_merge_into() {
|
|
437
|
+
local primary_path="$1"; local branch="$2"
|
|
438
|
+
git -C "$primary_path" merge --squash "$branch" 2>&1
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
# Ensure a primary worktree exists; echoes the path
|
|
442
|
+
# Usage: git_ensure_primary_worktree
|
|
443
|
+
git_ensure_primary_worktree() {
|
|
444
|
+
local primary
|
|
445
|
+
primary=$(git_primary_branch) || return 1
|
|
446
|
+
git_ensure_worktree_for_branch "$primary"
|
|
447
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# shellcheck shell=bash
|
|
4
4
|
|
|
5
5
|
# Create worktree
|
|
6
|
-
|
|
6
|
+
create_worktree_api() {
|
|
7
7
|
if [[ -z "${1:-}" ]]; then
|
|
8
8
|
json_error "Branch name required"
|
|
9
9
|
return 1
|
|
@@ -26,8 +26,8 @@ bridge_create_worktree() {
|
|
|
26
26
|
fi
|
|
27
27
|
|
|
28
28
|
local env_copy_bin
|
|
29
|
-
if ! env_copy_bin="$(
|
|
30
|
-
json_error "
|
|
29
|
+
if ! env_copy_bin="$(env_copy_bin)"; then
|
|
30
|
+
json_error "env-copy binary not found"
|
|
31
31
|
return 1
|
|
32
32
|
fi
|
|
33
33
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# shellcheck shell=bash
|
|
4
4
|
|
|
5
5
|
# Create worktree from existing branch
|
|
6
|
-
|
|
6
|
+
create_worktree_from_existing_api() {
|
|
7
7
|
if [[ -z "${1:-}" ]]; then
|
|
8
8
|
json_error "Branch name required"
|
|
9
9
|
return 1
|
|
@@ -29,4 +29,4 @@ bridge_create_worktree_from_existing() {
|
|
|
29
29
|
else
|
|
30
30
|
json_error "Not a git repository"
|
|
31
31
|
fi
|
|
32
|
-
}
|
|
32
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# shellcheck shell=bash
|
|
4
4
|
|
|
5
5
|
# Create worktree from remote branch
|
|
6
|
-
|
|
6
|
+
create_worktree_from_remote_api() {
|
|
7
7
|
if [[ -z "${1:-}" ]]; then
|
|
8
8
|
json_error "Branch name required"
|
|
9
9
|
return 1
|
|
@@ -25,4 +25,4 @@ bridge_create_worktree_from_remote() {
|
|
|
25
25
|
else
|
|
26
26
|
json_error "Not a git repository"
|
|
27
27
|
fi
|
|
28
|
-
}
|
|
28
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# shellcheck shell=bash
|
|
4
4
|
|
|
5
5
|
# Delete worktree and branch
|
|
6
|
-
|
|
6
|
+
delete_worktree_api() {
|
|
7
7
|
if [[ -z "${1:-}" ]]; then
|
|
8
8
|
json_error "Branch name required"
|
|
9
9
|
return 1
|
|
@@ -23,4 +23,4 @@ bridge_delete_worktree() {
|
|
|
23
23
|
else
|
|
24
24
|
json_error "Not a git repository"
|
|
25
25
|
fi
|
|
26
|
-
}
|
|
26
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# shellcheck shell=bash
|
|
4
4
|
|
|
5
5
|
# Get enhanced git status
|
|
6
|
-
|
|
6
|
+
enhanced_git_status_api() {
|
|
7
7
|
# Optional directory parameter - if provided, cd to that directory first
|
|
8
8
|
target_dir="${1:-}"
|
|
9
9
|
if [[ -n "$target_dir" ]]; then
|
|
@@ -48,7 +48,7 @@ bridge_enhanced_git_status() {
|
|
|
48
48
|
# Check for staged changes first
|
|
49
49
|
if [[ "$index_status" != " " && "$index_status" != "?" ]]; then
|
|
50
50
|
# Look up stats in bulk staged diff
|
|
51
|
-
stats="$(echo "$staged_stats" | grep -F " $filepath" | head -1)"
|
|
51
|
+
stats="$(echo "$staged_stats" | grep -F " $filepath" | head -1 || true)"
|
|
52
52
|
if [[ -n "$stats" && "$stats" != "- -"* ]]; then
|
|
53
53
|
added="$(echo "$stats" | cut -f1)"
|
|
54
54
|
deleted="$(echo "$stats" | cut -f2)"
|
|
@@ -61,7 +61,7 @@ bridge_enhanced_git_status() {
|
|
|
61
61
|
# Check for working directory changes if no staged stats or if workdir is also modified
|
|
62
62
|
if [[ "$workdir_status" != " " && "$workdir_status" != "?" ]] && [[ $added -eq 0 && $deleted -eq 0 ]]; then
|
|
63
63
|
# Look up stats in bulk unstaged diff
|
|
64
|
-
stats="$(echo "$unstaged_stats" | grep -F " $filepath" | head -1)"
|
|
64
|
+
stats="$(echo "$unstaged_stats" | grep -F " $filepath" | head -1 || true)"
|
|
65
65
|
if [[ -n "$stats" && "$stats" != "- -"* ]]; then
|
|
66
66
|
added="$(echo "$stats" | cut -f1)"
|
|
67
67
|
deleted="$(echo "$stats" | cut -f2)"
|
package/resources/scripts/{shell/git/bridge_git_status.sh → server/services/git/git_status_api.sh}
RENAMED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
# shellcheck shell=bash
|
|
4
4
|
|
|
5
5
|
# Get git status
|
|
6
|
-
|
|
6
|
+
git_status_api() {
|
|
7
7
|
if git_require_repo_root >/dev/null 2>&1; then
|
|
8
8
|
status_output="$(git status --porcelain 2>/dev/null || echo "")"
|
|
9
9
|
echo "\"$status_output\""
|
|
10
10
|
else
|
|
11
11
|
json_error "Not a git repository"
|
|
12
12
|
fi
|
|
13
|
-
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# shellcheck shell=bash
|
|
4
|
+
|
|
5
|
+
# Master file that sources all individual services functions for merge operations
|
|
6
|
+
# This maintains the same interface while using modular files
|
|
7
|
+
|
|
8
|
+
# Source individual merge functions
|
|
9
|
+
source "$(dirname "${BASH_SOURCE[0]}")/merge_from_primary_api.sh"
|
|
10
|
+
source "$(dirname "${BASH_SOURCE[0]}")/rebase_from_primary_api.sh"
|
|
11
|
+
source "$(dirname "${BASH_SOURCE[0]}")/merge_into_primary_api.sh"
|
|
12
|
+
source "$(dirname "${BASH_SOURCE[0]}")/squash_into_primary_api.sh"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# shellcheck shell=bash
|
|
4
|
+
|
|
5
|
+
# Master file that sources all individual services functions for repository operations
|
|
6
|
+
# This maintains the same interface while using modular files
|
|
7
|
+
|
|
8
|
+
# Source individual repo functions
|
|
9
|
+
source "$(dirname "${BASH_SOURCE[0]}")/repo_info_api.sh"
|
|
10
|
+
source "$(dirname "${BASH_SOURCE[0]}")/git_status_api.sh"
|
|
11
|
+
source "$(dirname "${BASH_SOURCE[0]}")/enhanced_git_status_api.sh"
|
|
12
|
+
source "$(dirname "${BASH_SOURCE[0]}")/primary_branch_api.sh"
|