xgem-cli 2.0.0-alpha.7 → 2.0.0-alpha.8
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/README.md +2 -1
- package/bin/xgem +2 -1
- package/bin/xgem.js +2 -1
- package/lib/git.sh +81 -2
- package/lib/version.sh +1 -1
- package/lib-win/git.js +61 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ xgem doctor ios [path] Report on iOS build readiness: CocoaPods vs SPM, d
|
|
|
49
49
|
targets per config, and whether they're reconciled
|
|
50
50
|
xgem git cmt "message" Auto-stage, commit, rebase-pull, and push
|
|
51
51
|
xgem git init Setup local repo, attach remote tracker shortcuts
|
|
52
|
+
xgem git branch Pick, create, or switch branches; remembers your choice
|
|
52
53
|
xgem git rm-remote Drop a configured remote
|
|
53
54
|
xgem git rm-branch Safely drop local and/or remote branch
|
|
54
55
|
xgem --version Print xgem's version
|
|
@@ -70,7 +71,7 @@ lib/
|
|
|
70
71
|
doctor.sh environment + feature detection ("xgem doctor")
|
|
71
72
|
ios.sh the SwiftPM-aware iOS build engine
|
|
72
73
|
flutter.sh flutter command group (build/hard-clean/build-runner), delegates iOS builds to ios.sh
|
|
73
|
-
git.sh git cmt/init/rm-remote/rm-branch
|
|
74
|
+
git.sh git cmt/init/branch/rm-remote/rm-branch
|
|
74
75
|
scaffold.sh generic clean/build/dev handling for the other, simpler frameworks
|
|
75
76
|
templates/ the actual clean/build/dev script content xgem scaffolds into your project's .xgem-automate/
|
|
76
77
|
```
|
package/bin/xgem
CHANGED
|
@@ -70,6 +70,7 @@ print_usage() {
|
|
|
70
70
|
echo " xgem doctor [ios] - Report on your environment / iOS SwiftPM build readiness"
|
|
71
71
|
echo " xgem git cmt \"message\" - Auto-stage, commit, rebase-pull, and push"
|
|
72
72
|
echo " xgem git init - Setup local repo, attach remote tracker shortcuts"
|
|
73
|
+
echo " xgem git branch - Pick, create, or switch branches; remembers your choice"
|
|
73
74
|
echo " xgem git rm-remote - Drop specified target remote tracing rules"
|
|
74
75
|
echo " xgem git rm-branch - Safely drop local and remote workspace branch states"
|
|
75
76
|
echo " xgem --version - Print xgem's version"
|
|
@@ -182,7 +183,7 @@ case "${1:-}" in
|
|
|
182
183
|
;;
|
|
183
184
|
|
|
184
185
|
git)
|
|
185
|
-
[ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|rm-remote|rm-branch>"
|
|
186
|
+
[ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>"
|
|
186
187
|
cmd_git "$2" "${3:-}"
|
|
187
188
|
exit 0
|
|
188
189
|
;;
|
package/bin/xgem.js
CHANGED
|
@@ -45,6 +45,7 @@ function printUsage() {
|
|
|
45
45
|
console.log(' xgem doctor [ios] - Report on your environment / iOS build availability');
|
|
46
46
|
console.log(' xgem git cmt "message" - Auto-stage, commit, rebase-pull, and push');
|
|
47
47
|
console.log(' xgem git init - Setup local repo, attach remote tracker shortcuts');
|
|
48
|
+
console.log(' xgem git branch - Pick, create, or switch branches; remembers your choice');
|
|
48
49
|
console.log(' xgem git rm-remote - Drop specified target remote tracing rules');
|
|
49
50
|
console.log(' xgem git rm-branch - Safely drop local and remote workspace branch states');
|
|
50
51
|
console.log(' xgem --version - Print xgem\'s version');
|
|
@@ -187,7 +188,7 @@ async function main() {
|
|
|
187
188
|
case 'run': return cmdRun(a2, a3);
|
|
188
189
|
case 'terminate': return cmdTerminate();
|
|
189
190
|
case 'git':
|
|
190
|
-
if (!a2) die('Usage: xgem git <cmt|init|rm-remote|rm-branch>');
|
|
191
|
+
if (!a2) die('Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>');
|
|
191
192
|
return cmdGit(a2, a3);
|
|
192
193
|
case 'doctor': return cmdDoctor(a2);
|
|
193
194
|
case '--version':
|
package/lib/git.sh
CHANGED
|
@@ -46,6 +46,29 @@ cmd_git_cmt() {
|
|
|
46
46
|
return 0
|
|
47
47
|
fi
|
|
48
48
|
|
|
49
|
+
# A pull needs something to pull FROM. If this branch has never been
|
|
50
|
+
# pushed before, there's no remote ref to pull, and `git pull --rebase`
|
|
51
|
+
# fails with "couldn't find remote ref <branch>" — which is not a
|
|
52
|
+
# connection problem or a conflict, just a brand-new branch. Check for
|
|
53
|
+
# that specific case first so it gets its own accurate message instead
|
|
54
|
+
# of being lumped in with real connectivity failures.
|
|
55
|
+
git ls-remote --exit-code --heads "$remote_name" "$current_branch" >/dev/null 2>&1
|
|
56
|
+
local ls_remote_status=$?
|
|
57
|
+
|
|
58
|
+
if [ "$ls_remote_status" -eq 2 ]; then
|
|
59
|
+
log_info "'$current_branch' doesn't exist on '$remote_name' yet — pushing to create it..."
|
|
60
|
+
if git push -u "$remote_name" "$current_branch"; then
|
|
61
|
+
log_success "Git workflow complete! Branch created and pushed."
|
|
62
|
+
else
|
|
63
|
+
die "Push operation failed."
|
|
64
|
+
fi
|
|
65
|
+
return 0
|
|
66
|
+
elif [ "$ls_remote_status" -ne 0 ]; then
|
|
67
|
+
log_error "Could not reach '$remote_name' to check for '$current_branch' — this looks like a real connection problem."
|
|
68
|
+
log_warn "Your commit is safe locally. Re-run 'xgem git cmt' once connectivity is restored, or push manually: git push $remote_name $current_branch"
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
49
72
|
local ahead_count
|
|
50
73
|
ahead_count=$(git rev-list --count "$remote_name/$current_branch..$current_branch" 2>/dev/null)
|
|
51
74
|
if [ "$ahead_count" = "0" ]; then
|
|
@@ -163,13 +186,69 @@ cmd_git_rm_branch() {
|
|
|
163
186
|
fi
|
|
164
187
|
}
|
|
165
188
|
|
|
166
|
-
#
|
|
189
|
+
# cmd_git_branch: lists local branches, lets the user pick one (or create a
|
|
190
|
+
# new one), checks it out, and remembers it as this repo's default so
|
|
191
|
+
# there's a quick way back to it later. `xgem git cmt` always operates on
|
|
192
|
+
# whatever's actually checked out (that's the only thing that's correct for
|
|
193
|
+
# a commit), so this command's job is the checkout + remembering, not
|
|
194
|
+
# changing how cmt picks its branch.
|
|
195
|
+
cmd_git_branch() {
|
|
196
|
+
local -a branches=()
|
|
197
|
+
while IFS= read -r line; do
|
|
198
|
+
[ -n "$line" ] && branches+=("$line")
|
|
199
|
+
done < <(git branch --format='%(refname:short)' 2>/dev/null)
|
|
200
|
+
|
|
201
|
+
[ ${#branches[@]} -gt 0 ] || die "No local branches found."
|
|
202
|
+
|
|
203
|
+
local current_branch
|
|
204
|
+
current_branch=$(git branch --show-current 2>/dev/null)
|
|
205
|
+
|
|
206
|
+
echo "Available branches:"
|
|
207
|
+
local i=1 b
|
|
208
|
+
for b in "${branches[@]}"; do
|
|
209
|
+
if [ "$b" = "$current_branch" ]; then
|
|
210
|
+
echo "$i) $b (current)"
|
|
211
|
+
else
|
|
212
|
+
echo "$i) $b"
|
|
213
|
+
fi
|
|
214
|
+
i=$((i + 1))
|
|
215
|
+
done
|
|
216
|
+
local create_option=$i
|
|
217
|
+
echo "$create_option) Create new branch"
|
|
218
|
+
|
|
219
|
+
local choice
|
|
220
|
+
read -r -p "Select [1-$create_option]: " choice
|
|
221
|
+
|
|
222
|
+
if [ "$choice" = "$create_option" ]; then
|
|
223
|
+
local new_branch
|
|
224
|
+
read -r -p "Enter new branch name: " new_branch
|
|
225
|
+
[ -n "$new_branch" ] || die "Branch name cannot be empty."
|
|
226
|
+
git checkout -b "$new_branch" || die "Could not create branch '$new_branch'."
|
|
227
|
+
git config --local xgem.default-branch "$new_branch"
|
|
228
|
+
log_success "Created and switched to '$new_branch', set as default for this repo."
|
|
229
|
+
return 0
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt ${#branches[@]} ]; then
|
|
233
|
+
die "Invalid selection."
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
local selected="${branches[$((choice - 1))]}"
|
|
237
|
+
if [ "$selected" != "$current_branch" ]; then
|
|
238
|
+
git checkout "$selected" || die "Could not switch to branch '$selected'."
|
|
239
|
+
fi
|
|
240
|
+
git config --local xgem.default-branch "$selected"
|
|
241
|
+
log_success "Switched to '$selected' and set as default for this repo."
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# xgem git <cmt|init|rm-remote|rm-branch|branch> ...
|
|
167
245
|
cmd_git() {
|
|
168
246
|
case "$1" in
|
|
169
247
|
cmt) cmd_git_cmt "$2" ;;
|
|
170
248
|
init) cmd_git_init ;;
|
|
171
249
|
rm-remote) cmd_git_rm_remote ;;
|
|
172
250
|
rm-branch) cmd_git_rm_branch ;;
|
|
173
|
-
|
|
251
|
+
branch) cmd_git_branch ;;
|
|
252
|
+
*) die "Unknown git subcommand '$1'. Usage: xgem git <cmt|init|rm-remote|rm-branch|branch>" ;;
|
|
174
253
|
esac
|
|
175
254
|
}
|
package/lib/version.sh
CHANGED
package/lib-win/git.js
CHANGED
|
@@ -60,6 +60,26 @@ async function cmdCmt(commitMsg) {
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// A pull needs something to pull FROM. If this branch has never been
|
|
64
|
+
// pushed before, there's no remote ref, and `git pull --rebase` fails
|
|
65
|
+
// with "couldn't find remote ref <branch>" — not a connection problem
|
|
66
|
+
// or a conflict, just a brand-new branch. Check for that case first.
|
|
67
|
+
const lsRemoteStatus = spawnSync('git', ['ls-remote', '--exit-code', '--heads', remoteName, currentBranch]).status;
|
|
68
|
+
|
|
69
|
+
if (lsRemoteStatus === 2) {
|
|
70
|
+
logInfo(`'${currentBranch}' doesn't exist on '${remoteName}' yet — pushing to create it...`);
|
|
71
|
+
if (git(['push', '-u', remoteName, currentBranch]).status === 0) {
|
|
72
|
+
logSuccess('Git workflow complete! Branch created and pushed.');
|
|
73
|
+
} else {
|
|
74
|
+
die('Push operation failed.');
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
} else if (lsRemoteStatus !== 0) {
|
|
78
|
+
logError(`Could not reach '${remoteName}' to check for '${currentBranch}' — this looks like a real connection problem.`);
|
|
79
|
+
logWarn(`Your commit is safe locally. Re-run 'xgem git cmt' once connectivity is restored, or push manually: git push ${remoteName} ${currentBranch}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
const aheadCount = gitCapture(['rev-list', '--count', `${remoteName}/${currentBranch}..${currentBranch}`]);
|
|
64
84
|
if (aheadCount === '0') {
|
|
65
85
|
logSuccess(`Already up to date with '${remoteName}/${currentBranch}' — nothing to push.`);
|
|
@@ -168,13 +188,53 @@ async function cmdRmBranch() {
|
|
|
168
188
|
}
|
|
169
189
|
}
|
|
170
190
|
|
|
191
|
+
// Lists local branches, lets the user pick one (or create a new one),
|
|
192
|
+
// checks it out, and remembers it as this repo's default. cmt always
|
|
193
|
+
// operates on whatever's actually checked out — this command's job is the
|
|
194
|
+
// checkout + remembering, not changing how cmt picks its branch.
|
|
195
|
+
async function cmdBranch() {
|
|
196
|
+
const branches = gitCapture(['branch', '--format=%(refname:short)']).split(/\r?\n/).filter(Boolean);
|
|
197
|
+
if (branches.length === 0) die('No local branches found.');
|
|
198
|
+
|
|
199
|
+
const currentBranch = gitCapture(['branch', '--show-current']);
|
|
200
|
+
|
|
201
|
+
console.log('Available branches:');
|
|
202
|
+
branches.forEach((b, i) => {
|
|
203
|
+
console.log(`${i + 1}) ${b}${b === currentBranch ? ' (current)' : ''}`);
|
|
204
|
+
});
|
|
205
|
+
const createOption = branches.length + 1;
|
|
206
|
+
console.log(`${createOption}) Create new branch`);
|
|
207
|
+
|
|
208
|
+
const choice = await prompt(`Select [1-${createOption}]`);
|
|
209
|
+
|
|
210
|
+
if (choice === String(createOption)) {
|
|
211
|
+
const newBranch = await prompt('Enter new branch name');
|
|
212
|
+
if (!newBranch) die('Branch name cannot be empty.');
|
|
213
|
+
if (git(['checkout', '-b', newBranch]).status !== 0) die(`Could not create branch '${newBranch}'.`);
|
|
214
|
+
git(['config', '--local', 'xgem.default-branch', newBranch]);
|
|
215
|
+
logSuccess(`Created and switched to '${newBranch}', set as default for this repo.`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const idx = parseInt(choice, 10) - 1;
|
|
220
|
+
if (Number.isNaN(idx) || idx < 0 || idx >= branches.length) die('Invalid selection.');
|
|
221
|
+
|
|
222
|
+
const selected = branches[idx];
|
|
223
|
+
if (selected !== currentBranch) {
|
|
224
|
+
if (git(['checkout', selected]).status !== 0) die(`Could not switch to branch '${selected}'.`);
|
|
225
|
+
}
|
|
226
|
+
git(['config', '--local', 'xgem.default-branch', selected]);
|
|
227
|
+
logSuccess(`Switched to '${selected}' and set as default for this repo.`);
|
|
228
|
+
}
|
|
229
|
+
|
|
171
230
|
async function cmdGit(sub, arg) {
|
|
172
231
|
switch (sub) {
|
|
173
232
|
case 'cmt': return cmdCmt(arg);
|
|
174
233
|
case 'init': return cmdInit();
|
|
234
|
+
case 'branch': return cmdBranch();
|
|
175
235
|
case 'rm-remote': return cmdRmRemote();
|
|
176
236
|
case 'rm-branch': return cmdRmBranch();
|
|
177
|
-
default: die(`Unknown git subcommand '${sub}'. Usage: xgem git <cmt|init|rm-remote|rm-branch>`);
|
|
237
|
+
default: die(`Unknown git subcommand '${sub}'. Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>`);
|
|
178
238
|
}
|
|
179
239
|
}
|
|
180
240
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xgem-cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.8",
|
|
4
4
|
"description": "Framework-aware automation CLI: scaffolds and runs clean/build/dev scripts per project type, an environment doctor, a SwiftPM-aware iOS build engine, and a git workflow helper. Runs natively on macOS, Linux, and Windows.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"xgem": "bin/xgem.js"
|