Repository navigation
Move the gnarly git aliases into bin scripts - #47
Conversation
This is not a vendor specific concept, so we wanna allow the tracking of it. If I'm on a project that doesn't want this (but my tooling does) then I can override that ignore in that project via local means.
There was a problem hiding this comment.
🟡 Changes recommended
The new bin/git-cleanup and updated system/install.sh have confirmed failure modes (macOS xargs empty-input behavior; existing non-symlink $HOME/bin) that can break normal usage under set -e.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR refactors several complex git aliases out of git/gitconfig.symlink into dedicated bin/git-* scripts to improve readability and maintainability, while also hardening an install script and fixing a markdown formatting issue that was breaking Prettier/CI.
Changes:
- Extracted
git up,git cleanup,git brebase,git edit, andgit syncinto standalonebin/scripts and updated aliases to call them. - Updated
system/install.shto follow the repo’s standard install-script pattern (shebang,set -e, sharedfunctions.sh), make linking idempotent, and guardopencalls. - Fixed markdown code-block formatting in
claude/config/CLAUDE.mdand adjusted the global gitignore list.
File summaries
| File | Description |
|---|---|
system/install.sh |
Adds standard script boilerplate and makes bin-linking + app-opening more robust/idempotent. |
git/gitignore.symlink |
Removes .java-version from the global ignore list. |
git/gitconfig.symlink |
Replaces long shell-escaped aliases with calls to ~/.dotfiles/bin/git-* scripts. |
claude/config/CLAUDE.md |
Switches to fenced code block to stabilize Prettier formatting. |
bin/git-up |
New script implementing the git up behavior with clearer control flow and messaging. |
bin/git-sync |
New script implementing the git sync behavior with corrected default-branch detection. |
bin/git-edit |
New script implementing the git edit workflow with explicit abort handling and non-zero exit on abort. |
bin/git-cleanup |
New script implementing the git cleanup branch-deletion workflow. |
bin/git-brebase |
New script implementing the git brebase interactive-rebase workflow. |
Review details
- Files reviewed: 4/9 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if [ -L "$HOME/bin" ]; then | ||
| info 'bin is already linked' | ||
| else | ||
| info 'Linking bin' | ||
| ln -ns ~/.dotfiles/bin "$HOME/bin" | ||
| fi |
There was a problem hiding this comment.
Good catch, fixed in b8711e3.
Worth noting the failure is quieter than described: ln -ns against an existing directory doesn't fail at all. It creates the link inside it as $HOME/bin/bin, exits 0, and set -e never fires — so bootstrap reports success while the dotfiles bin is not where path.zsh expects it. Only the plain-file case exits non-zero.
Existing paths now move to bin.backup before linking, following the pattern in maestral/install.sh, with a collision guard so an existing backup is an error rather than a second clobber. Verified across four states: no bin, already linked, real directory, real file.
🤖 Generated with Claude Code
Session: d019263c-0e35-4452-b1c2-f685d76d5c18
The inline alias was one long line of escaped shell, effectively unreadable. Moving the body to bin/git-up lets it use real control flow, comments and usage docs, leaving a one-line alias behind. The alias points at the absolute path rather than relying on bin/ being on $PATH, so it also works from a minimal environment. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
Same treatment as git-up: the branch scan and the grep pattern it built by string substitution are far easier to follow as a real script. Behaviour is unchanged, including the fact that protected names match as substrings, so a branch like "devops" is spared too. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
Carries the same base branch detection as cleanup, which was the bulk of the unreadable one-liner. The merge-base rebase it actually performs is one line once that is out of the way. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
Easily the worst offender of the aliases, and the one whose error handling most needed to be readable. The script also records the caveats and links back to the gist it came from. Two behaviour changes fall out of the rewrite. Our copy had drifted from the gist and raised the stash flag before taking the stash, so an abort in between could drop an unrelated stash; the flag now goes up only once the stash exists. An abort also exits non-zero now, rather than returning the status of its own cleanup. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
The "could not determine default branch" check could never fire: it tested the exit status of the sed at the end of a pipeline rather than the symbolic-ref feeding it. An upstream with no default branch got as far as checking out a branch named "sync/<timestamp>-" and failing on a merge of "<remote>/". The check now tests for an empty result, so the error it always meant to give actually comes out. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
It was the one install script with no shebang, no set -e and no shared functions, and its bare ln meant every re-bootstrap failed once $HOME/bin existed. Linking is now conditional, and the apps it opens are guarded so a missing cask warns instead of halting the script under set -e. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
The indented code block inside a list item never reached a fixed point: each run added a couple of spaces, so `npm run format` dirtied the file and `npm run lint` failed on it anyway. Fencing the block gives prettier explicit delimiters and settles it. CI on main was failing on this. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
d8ea7de to
8ebe8af
Compare
Guarding only on -L meant an existing real $HOME/bin fell through to the ln, which does not fail on a directory: it quietly creates the link inside it as $HOME/bin/bin, exits 0, and set -e never notices. A plain file did fail, aborting the script. Existing paths are now moved to bin.backup first, following the pattern in maestral/install.sh, and a backup that is already taken is an error rather than a second clobber. Claude-Session: d019263c-0e35-4452-b1c2-f685d76d5c18
|
On the other half of the review — the BSD Running 🤖 Generated with Claude Code Session: d019263c-0e35-4452-b1c2-f685d76d5c18 |
The attribution block set commit and pr to empty strings and sessionUrl to false, which suppresses attribution entirely and directly contradicts the AI attribution rules in CLAUDE.md. That mismatch is why the commits and PR on this branch went out unattributed and had to be rewritten. Dropping the two empty strings falls back to the standard Claude Code attribution, and sessionUrl is pinned true rather than left to default so a future change of default cannot silently switch it off again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uuja2EnY8Wg7UGoWPoyNf5
The
[alias]block had grown several aliases that were single, very longlines of escaped shell — effectively unreadable and unreviewable. Each one
moves into
bin/, where it gets real control flow, comments and usage docs,and leaves a one-line alias behind.
upbin/git-upcleanupbin/git-cleanupbrebasebin/git-brebaseeditbin/git-editsyncbin/git-syncplog,cremit,fpushandtrackare already readable one-liners and areleft alone.
The aliases point at
~/.dotfiles/bin/...rather than relying onbin/beingon
$PATH, so they keep working from a minimal environment such as a GUI gitclient.
Behaviour changes
Each extraction was diffed against the old alias by running both against
identical throwaway repos. Three deliberate differences:
git editraised its stash flag before taking the stash, so aborting inthat window could
git stash dropan unrelated stash. Our copy had driftedfrom the source gist,
which has these the other way round; restored to match.
git editnow exits non-zero when it aborts. It previously returned thestatus of its own cleanup, which was usually 0.
git sync's "could not determine default branch" check could never fire —it tested the exit status of the
sedending a pipeline rather than thesymbolic-reffeeding it. Against an upstream with no default branch it gotas far as checking out a branch named
sync/<timestamp>-and failing to merge<remote>/. The intended error now actually comes out.git cleanupmatches protected branch names as substrings, so a merged branchnamed
devopsis spared along withdev. That is preserved as-is rather thantightened, since the command deletes branches and the current behaviour errs
towards keeping them. It is commented in the script.
Also fixed
system/install.shwas the one install script with no shebang, noset -eand no shared functions, and its bare
lnfailed on every re-bootstrap once$HOME/binexisted. Linking is conditional now, and the apps it opens areguarded so a missing cask warns rather than halting under
set -e.claude/config/CLAUDE.mdneverreached a prettier fixed point, gaining a couple of spaces per pass, so
npm run formatdirtied the file andnpm run lintfailed on it regardless.Fencing the block settles it. This was failing CI on
mainalready.5a78435predates this work and rides along on the branch.🤖 Generated with Claude Code
Session: d019263c-0e35-4452-b1c2-f685d76d5c18