From a09d3db800eaff842f4b4ce5ba62a6d0d37849ed Mon Sep 17 00:00:00 2001 From: Rootiest Date: Wed, 16 Sep 2026 19:36:34 -0400 Subject: [PATCH] fix(mkrep): make origin linking idempotent Both link paths -- --remote and the --server/$GIT_SERVER link-existing branch -- called `git remote add origin` bare. That fails with "remote origin already exists" whenever the target is already linked, which is the normal case for `mkrep .` against an existing checkout and for any rerun against the same target. The failure took the whole call down with exit 1. Route both through a new _mkrep_add_origin helper: add when there is no origin, accept and report when origin already points at the requested URL, and refuse when it points elsewhere. A different URL is a different repo, so repointing stays the caller's explicit decision rather than a silent rewrite of a checkout mkrep may have been aimed at by mistake. --- functions/_mkrep_add_origin.fish | 53 ++++++++++++++++++++++++++++++++ functions/mkrep.fish | 18 ++++++----- tests/test-mkrep.fish | 24 +++++++++++++++ 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 functions/_mkrep_add_origin.fish diff --git a/functions/_mkrep_add_origin.fish b/functions/_mkrep_add_origin.fish new file mode 100644 index 0000000..90c34fc --- /dev/null +++ b/functions/_mkrep_add_origin.fish @@ -0,0 +1,53 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# DEPENDENCIES +# _mkrep_say, __fish_palette, git +# +# SYNOPSIS +# _mkrep_add_origin +# +# DESCRIPTION +# Links origin to , idempotently, for mkrep's --remote and --server +# link-existing flows. Both can run against a directory that is already a +# repo -- `mkrep .` on an existing checkout, or a second mkrep against the +# same target -- where a bare `git remote add origin` fails with "remote +# origin already exists" and takes the whole call down with it. +# +# No origin yet: adds it. An origin already pointing at : reports that +# and succeeds, since the requested end state already holds. An origin +# pointing somewhere else: refuses, naming both URLs. Repointing is not +# silently assumed -- an origin the caller did not ask about is more likely +# a checkout mkrep was aimed at by mistake than one it should rewrite, so +# the fix belongs in the caller's hands (git remote set-url origin ). +# +# ARGUMENTS +# silent 1 to suppress the success/already-linked notes (mkrep's -s) +# url Remote URL origin should point at +# +# EXIT STATUS +# 0 origin now points at (added, or already did) +# 1 git remote add failed, or origin points somewhere else +function _mkrep_add_origin --argument-names silent url + __fish_palette + + set -l existing (git remote get-url origin 2>/dev/null) + + if test -z "$existing" + git remote add origin $url + or begin + echo "$c_err""✘$c_reset Failed to add remote $c_arg$url$c_reset" >&2 + return 1 + end + _mkrep_say $silent "$c_ok""✔$c_reset Linked remote $c_arg$url$c_reset" + return 0 + end + + if test "$existing" = "$url" + _mkrep_say $silent "$c_warn""→$c_reset origin already points at $c_arg$url$c_reset" + return 0 + end + + echo "$c_err""✘$c_reset origin already points at $c_arg$existing$c_reset, not $c_arg$url$c_reset — run $c_cmd""git remote set-url origin $url$c_reset to repoint it" >&2 + return 1 +end diff --git a/functions/mkrep.fish b/functions/mkrep.fish index faceafa..f57e5e1 100644 --- a/functions/mkrep.fish +++ b/functions/mkrep.fish @@ -6,7 +6,8 @@ # # DEPENDENCIES # _fish_mkdir_p, __fish_palette, _mkrep_say, _mkrep_verbose, -# _mkrep_default_remote_cmd, _mkrep_remote_url, _mkrep_repo_exists, git +# _mkrep_add_origin, _mkrep_default_remote_cmd, _mkrep_remote_url, +# _mkrep_repo_exists, git # # SYNOPSIS # mkrep [--cd | --no-cd] [--mkdir | --no-mkdir] [--git | --no-git] @@ -28,7 +29,12 @@ # empty slot because clean just emptied it. # # --remote links an already-existing remote (git remote add origin -# ) -- it does not create anything. --new-remote creates one first +# ) -- it does not create anything. Linking is idempotent: an origin +# already pointing at that URL is reported and accepted, so rerunning +# mkrep against the same target, or pointing it at a checkout that is +# already linked, succeeds instead of failing on "remote origin already +# exists". An origin pointing somewhere else is an error, not a silent +# repoint. --new-remote creates one first # by running a shell command template in the new repo directory, then # nothing further is needed since the template itself does the linking # (e.g. gh repo create {name} --source=. --remote=origin --push). @@ -333,13 +339,11 @@ function mkrep --description 'Create a directory, cd into it, and git init it' if set -q _flag_remote _mkrep_verbose $silent $verbose "$c_dim""Running: git remote add origin $_flag_remote$c_reset" - git remote add origin $_flag_remote + _mkrep_add_origin $silent $_flag_remote or begin - echo "$c_err""✘$c_reset Failed to add remote $c_arg$_flag_remote$c_reset" >&2 cd $orig_pwd return 1 end - _mkrep_say $silent "$c_ok""✔$c_reset Linked remote $c_arg$_flag_remote$c_reset" end if set -q _flag_new_remote @@ -384,13 +388,11 @@ function mkrep --description 'Create a directory, cd into it, and git init it' if _mkrep_repo_exists $srv_type $USER $name set -l url (_mkrep_remote_url $srv_type $USER $name $srv_url) _mkrep_say $silent "$c_warn""→$c_reset $c_arg$USER/$name$c_reset already exists on $srv_type; linking instead of creating" - git remote add origin $url + _mkrep_add_origin $silent $url or begin - echo "$c_err""✘$c_reset Failed to add remote $c_arg$url$c_reset" >&2 cd $orig_pwd return 1 end - _mkrep_say $silent "$c_ok""✔$c_reset Linked remote $c_arg$url$c_reset" else # Creating a repository on a live forge is the only outward-facing # thing mkrep does, and an exported $GIT_SERVER alone is enough to diff --git a/tests/test-mkrep.fish b/tests/test-mkrep.fish index 4fd3b44..ae6e401 100644 --- a/tests/test-mkrep.fish +++ b/tests/test-mkrep.fish @@ -163,6 +163,30 @@ check "--remote links origin to the given url" https://example.invalid/me/repo.g cd $start rm -rf $base +# `mkrep .` against an existing checkout, and a plain rerun against the same +# target, both reach `git remote add origin` on a repo that already has one. +# A bare add fails there with "remote origin already exists" and takes the +# whole call down, so linking has to accept the end state it already wanted. +section "mkrep: --remote is idempotent" + +set -l base (_mkrep_sandbox) +set -l target $base/repo +mkrep --remote https://example.invalid/me/repo.git $target >/dev/null +mkrep --remote https://example.invalid/me/repo.git $target >/dev/null +check "relinking the same url exits 0" 0 $status +check "relinking leaves one origin" 1 (count (git -C $target remote)) +set -l url (git -C $target remote get-url origin) +check "relinking leaves origin untouched" https://example.invalid/me/repo.git $url + +# A different url is a different repo. Repointing a checkout the caller did +# not ask about is more likely a mistargeted mkrep than an intended rewrite. +mkrep --remote https://example.invalid/me/other.git $target >/dev/null 2>/tmp/mkrep-test-err +check "relinking a different url exits 1" 1 $status +set -l url (git -C $target remote get-url origin) +check "a refused relink leaves origin alone" https://example.invalid/me/repo.git $url +cd $start +rm -rf $base + section "mkrep: --remote and --new-remote are exclusive" set -l base (_mkrep_sandbox) -- 2.54.0