Skip to content

Move the gnarly git aliases into bin scripts - #47

Merged
slifty merged 10 commits into
mainfrom
noissue-git-improvements
Sep 2, 2026
Merged

slifty merged 10 commits into
mainfrom
noissue-git-improvements

Conversation

@slifty

@slifty slifty commented Sep 2, 2026 •

Copy link
Copy Markdown
Owner

The [alias] block had grown several aliases that were single, very long
lines 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.

alias now
up bin/git-up
cleanup bin/git-cleanup
brebase bin/git-brebase
edit bin/git-edit
sync bin/git-sync

plog, cremit, fpush and track are already readable one-liners and are
left alone.

The aliases point at ~/.dotfiles/bin/... rather than relying on bin/ being
on $PATH, so they keep working from a minimal environment such as a GUI git
client.

Behaviour changes

Each extraction was diffed against the old alias by running both against
identical throwaway repos. Three deliberate differences:

  • git edit raised its stash flag before taking the stash, so aborting in
    that window could git stash drop an unrelated stash. Our copy had drifted
    from the source gist,
    which has these the other way round; restored to match.
  • git edit now exits non-zero when it aborts. It previously returned the
    status 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 sed ending a pipeline rather than the
    symbolic-ref feeding it. Against an upstream with no default branch it got
    as far as checking out a branch named sync/<timestamp>- and failing to merge
    <remote>/. The intended error now actually comes out.

git cleanup matches protected branch names as substrings, so a merged branch
named devops is spared along with dev. That is preserved as-is rather than
tightened, since the command deletes branches and the current behaviour errs
towards keeping them. It is commented in the script.

Also fixed

  • system/install.sh was the one install script with no shebang, no set -e
    and no shared functions, and its bare ln failed on every re-bootstrap once
    $HOME/bin existed. Linking is conditional now, and the apps it opens are
    guarded so a missing cask warns rather than halting under set -e.
  • An indented code block inside a list item in claude/config/CLAUDE.md never
    reached a prettier fixed point, gaining a couple of spaces per pass, so
    npm run format dirtied the file and npm run lint failed on it regardless.
    Fencing the block settles it. This was failing CI on main already.

5a78435 predates this work and rides along on the branch.


🤖 Generated with Claude Code

Session: d019263c-0e35-4452-b1c2-f685d76d5c18

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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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, and git sync into standalone bin/ scripts and updated aliases to call them.
  • Updated system/install.sh to follow the repo’s standard install-script pattern (shebang, set -e, shared functions.sh), make linking idempotent, and guard open calls.
  • Fixed markdown code-block formatting in claude/config/CLAUDE.md and 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.

Comment thread system/install.sh
Comment on lines +6 to +11
if [ -L "$HOME/bin" ]; then
info 'bin is already linked'
else
info 'Linking bin'
ln -ns ~/.dotfiles/bin "$HOME/bin"
fi

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@slifty
slifty force-pushed the noissue-git-improvements branch from d8ea7de to 8ebe8af Compare September 2, 2026 18:09
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
@slifty

slifty commented Sep 2, 2026

Copy link
Copy Markdown
Owner Author

On the other half of the review — the bin/git-cleanup xargs concern doesn't reproduce on macOS.

BSD xargs does not run the command on empty input (GNU's -r behaviour is the default here), and although grep -Ev exits 1 when it filters everything out, the pipeline's status is xargs's, since pipefail is not set. Measured:

$ printf '' | xargs -n 1 echo RAN     # prints nothing, exit 0
$ git branch --merged | grep -Ev '...' ; echo $?    # 1
$ git branch --merged | grep -Ev '...' | xargs -n 1 git branch -d ; echo $?    # 0

Running git cleanup in a repo with nothing to delete exits 0 under set -e. No change made.

🤖 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
@slifty
slifty merged commit 1ec8a8d into main Sep 2, 2026
1 check passed
@slifty
slifty deleted the noissue-git-improvements branch September 2, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants