From 1a07e94002a5d25c3706fa83ffb26ee4a361b44f Mon Sep 17 00:00:00 2001 From: Rootiest Date: Mon, 14 Sep 2026 21:06:28 -0400 Subject: [PATCH 1/3] feat(git): add --server/--check-existing to mkrep for gitea/gitlab/github mkrep can now pick a git host from --server, $GIT_SERVER, and $GITEA_URL/$GITEA_HOST/$GITLAB_URL/$GITLAB_HOST (base URL only -- type must come from --server/$GIT_SERVER, so setting those URL vars for an unrelated tool can't silently turn a plain mkrep call into a remote-creating one). Before creating anything it checks via gh/glab/tea whether the repo already exists and links instead of recreating it; --check-existing runs just that check and reports without touching remotes. --- functions/_mkrep_default_remote_cmd.fish | 32 +++++ functions/_mkrep_remote_url.fish | 36 +++++ functions/_mkrep_repo_exists.fish | 39 +++++ functions/mkrep.fish | 175 ++++++++++++++++++++--- tests/test-mkrep.fish | 167 +++++++++++++++++++++ 5 files changed, 430 insertions(+), 19 deletions(-) create mode 100644 functions/_mkrep_default_remote_cmd.fish create mode 100644 functions/_mkrep_remote_url.fish create mode 100644 functions/_mkrep_repo_exists.fish diff --git a/functions/_mkrep_default_remote_cmd.fish b/functions/_mkrep_default_remote_cmd.fish new file mode 100644 index 0000000..0ebb5f3 --- /dev/null +++ b/functions/_mkrep_default_remote_cmd.fish @@ -0,0 +1,32 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# _mkrep_default_remote_cmd +# +# DESCRIPTION +# Prints the default remote-create command template for , used by +# mkrep's --server flow when $MKREP_REMOTE_CMD is unset. Same {name}, +# {user}, {server} placeholders as --new-remote. +# +# ARGUMENTS +# type gitea, gitlab, or github +# +# RETURNS +# The template string, on stdout +# +# EXIT STATUS +# 0 known type +# 1 unrecognized type +function _mkrep_default_remote_cmd --argument-names type + switch $type + case github + echo 'gh repo create {name} --private --source=. --remote=origin --push' + case gitlab + echo 'glab repo create {name} --private --skipGitInit && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD' + case gitea + echo 'tea repos create --name {name} --private && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD' + case '*' + return 1 + end +end diff --git a/functions/_mkrep_remote_url.fish b/functions/_mkrep_remote_url.fish new file mode 100644 index 0000000..c06e466 --- /dev/null +++ b/functions/_mkrep_remote_url.fish @@ -0,0 +1,36 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# _mkrep_remote_url +# +# DESCRIPTION +# Builds the clone URL for a repo on , for mkrep's --server flow: +# linking to a repo that already exists, and reporting the URL of one it +# just created. is the resolved server base (from --server/$GIT_SERVER +# plus $GITEA_URL/$GITEA_HOST/$GITLAB_URL/$GITLAB_HOST) and is unused for +# github, which always resolves to github.com. +# +# ARGUMENTS +# type gitea, gitlab, or github +# user Account/namespace the repo lives under +# name Repo name +# url Server base URL (required for gitea/gitlab) +# +# RETURNS +# The clone URL, on stdout +# +# EXIT STATUS +# 0 URL built +# 1 unrecognized type, or gitea/gitlab given with no url +function _mkrep_remote_url --argument-names type user name url + switch $type + case github + echo "https://github.com/$user/$name.git" + case gitlab gitea + test -n "$url"; or return 1 + echo "$url/$user/$name.git" + case '*' + return 1 + end +end diff --git a/functions/_mkrep_repo_exists.fish b/functions/_mkrep_repo_exists.fish new file mode 100644 index 0000000..e70b841 --- /dev/null +++ b/functions/_mkrep_repo_exists.fish @@ -0,0 +1,39 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# DEPENDENCIES +# gh, glab, tea +# +# SYNOPSIS +# _mkrep_repo_exists +# +# DESCRIPTION +# Checks whether / already exists on the given server, for +# mkrep's --server and --check-existing flows. Each host CLI is already +# authenticated (gh/glab/tea login), so this shells out to its own +# repo-lookup command rather than querying an API directly. +# +# ARGUMENTS +# type gitea, gitlab, or github +# user Account/namespace to check under +# name Repo name +# +# EXIT STATUS +# 0 repository exists +# 1 repository does not exist, the check tool is missing, or type is +# invalid +function _mkrep_repo_exists --argument-names type user name + switch $type + case github + type -q gh; or return 1 + gh repo view "$user/$name" >/dev/null 2>&1 + case gitlab + type -q glab; or return 1 + glab repo view "$user/$name" >/dev/null 2>&1 + case gitea + type -q tea; or return 1 + tea repos "$user/$name" >/dev/null 2>&1 + case '*' + return 1 + end +end diff --git a/functions/mkrep.fish b/functions/mkrep.fish index 1450d9c..06b0b74 100644 --- a/functions/mkrep.fish +++ b/functions/mkrep.fish @@ -5,14 +5,15 @@ # 04-git-and-version-control # # DEPENDENCIES -# _fish_mkdir_p, __fish_palette, _mkrep_say, _mkrep_verbose, git +# _fish_mkdir_p, __fish_palette, _mkrep_say, _mkrep_verbose, +# _mkrep_default_remote_cmd, _mkrep_remote_url, _mkrep_repo_exists, git # # SYNOPSIS # mkrep [--cd | --no-cd] [--mkdir | --no-mkdir] [--git | --no-git] # [-c | --clean | --no-clean] [--strict] [-v | --verbose] # [-s | --silent] [--template ] [--branch ] -# [--remote ] [--new-remote []] [--name ] -# [-h | --help] +# [--remote ] [--new-remote []] [--server ] +# [--check-existing] [--name ] [-h | --help] # # DESCRIPTION # Creates a directory, cds into it, and git-inits it -- mkcd plus a git @@ -31,11 +32,33 @@ # 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). -# Two placeholders are substituted in the template: {name} (--name, or -# the target directory's basename) and {user} ($USER). Pass a command -# after --new-remote to use it for this call only; with no value it -# falls back to $MKREP_REMOTE_CMD. --remote and --new-remote are -# mutually exclusive, and either requires --git. +# Three placeholders are substituted in a template: {name} (--name, or +# the target directory's basename), {user} ($USER), and {server} (the +# resolved server base URL, gitea/gitlab only). Pass a command after +# --new-remote to use it for this call only; with no value it falls +# back to $MKREP_REMOTE_CMD. --remote and --new-remote are mutually +# exclusive, and either requires --git. +# +# --server (gitea, gitlab, or github) picks a host without an +# explicit --remote/--new-remote: resolve its base URL from +# $GITEA_URL/$GITEA_HOST (gitea) or $GITLAB_URL/$GITLAB_HOST (gitlab), +# preferring the _URL form when both are set, then run +# $MKREP_REMOTE_CMD or that type's built-in default template. With no +# --server, --remote, or --new-remote, $GIT_SERVER picks the type the +# same way (invalid values are rejected the same as an invalid +# --server) -- $GITEA_URL/$GITEA_HOST/$GITLAB_URL/$GITLAB_HOST only ever +# supply the base URL, never the type on their own, so setting one for +# an unrelated tool (an API token helper, say) can't turn a plain mkrep +# call into a remote-creating one. This auto-detect path is silent (no +# error) under --no-git; --server itself still requires --git, and +# --server together with --remote or --new-remote is an error. Either +# way, before creating anything mkrep checks whether +# / already exists on that host: if so, it links to the +# existing repo instead of creating one; if not, it creates the repo +# and reports the new remote's URL. --check-existing runs just that +# check and reports the result without creating or linking anything; it +# requires a resolved server and is mutually exclusive with --remote +# and --new-remote. # # ARGUMENTS # Directory to create and enter @@ -55,8 +78,11 @@ # --new-remote [] # Create + link a remote by running (or # $MKREP_REMOTE_CMD) in the new repo directory -# --name {name} substitution when --new-remote (default: 's -# basename) +# --server Auto-create/link a remote on gitea, gitlab, or github +# --check-existing Report whether the repo exists on the resolved +# server; creates or links nothing +# --name {name} substitution for --new-remote/--server +# (default: 's basename) # -h, --help Show this help message # # EXIT STATUS @@ -69,21 +95,27 @@ # mkrep --remote git@git.example.com:me/foo.git ~/projects/foo # set -Ux MKREP_REMOTE_CMD 'gh repo create {name} --private --source=. --remote=origin --push' # mkrep --new-remote ~/projects/foo +# set -gx GITEA_URL https://git.example.com +# set -gx GIT_SERVER gitea +# mkrep ~/projects/foo +# mkrep --server gitlab --check-existing ~/projects/foo # # Starting points for $MKREP_REMOTE_CMD, one per host CLI -- each assumes # that tool is already installed and authenticated, creates a private # repo under the caller's own account, and pushes the initial commit. -# gh and glab support a one-shot --source/--remote/--push; tea's create -# does not, so it is chained with the git commands that do the linking -# (adjust the host in the URL to your Gitea instance): +# These are also mkrep's built-in defaults for --server/$GIT_SERVER when +# $MKREP_REMOTE_CMD is unset. gh supports a one-shot +# --source/--remote/--push; glab and tea's create commands do not, so +# they are chained with the git commands that do the linking: # GitHub (gh): gh repo create {name} --private --source=. --remote=origin --push -# GitLab (glab): glab repo create {name} --private --source=. --remote=origin --push -# Gitea (tea): tea repo create --name {name} --private && git remote add origin https://YOUR-GITEA-HOST/{user}/{name}.git && git push -u origin HEAD +# GitLab (glab): glab repo create {name} --private --skipGitInit && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD +# Gitea (tea): tea repos create --name {name} --private && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD function mkrep --description 'Create a directory, cd into it, and git init it' __fish_palette argparse h/help cd no-cd mkdir no-mkdir git no-git c/clean no-clean strict \ - v/verbose s/silent template= branch= remote= new-remote=? name= \ + v/verbose s/silent template= branch= remote= new-remote=? server= \ + check-existing name= \ -- $argv or return 1 @@ -105,7 +137,9 @@ function mkrep --description 'Create a directory, cd into it, and git init it' echo " $c_flag--branch$c_reset $c_arg$c_reset git init -b " echo " $c_flag--remote$c_reset $c_arg$c_reset Link an existing remote" echo " $c_flag--new-remote$c_reset $c_arg$c_reset (optional) Create + link a remote" - echo " $c_flag--name$c_reset $c_arg$c_reset {name} substitution for --new-remote" + echo " $c_flag--server$c_reset $c_arg$c_reset Auto-create/link a remote (gitea, gitlab, github)" + echo " $c_flag--check-existing$c_reset Report whether the repo exists; creates nothing" + echo " $c_flag--name$c_reset $c_arg$c_reset {name} substitution for --new-remote/--server" echo " $c_flag-h$c_reset, $c_flag--help$c_reset Show this help message" echo echo "$c_head""Examples:$c_reset" @@ -125,13 +159,63 @@ function mkrep --description 'Create a directory, cd into it, and git init it' echo "$c_err""✘$c_reset --remote and --new-remote are mutually exclusive" >&2 return 1 end + if set -q _flag_server; and set -q _flag_remote + echo "$c_err""✘$c_reset --server and --remote are mutually exclusive" >&2 + return 1 + end + if set -q _flag_server; and set -q _flag_new_remote + echo "$c_err""✘$c_reset --server and --new-remote are mutually exclusive" >&2 + return 1 + end + if set -q _flag_check_existing; and set -q _flag_remote + echo "$c_err""✘$c_reset --check-existing and --remote are mutually exclusive" >&2 + return 1 + end + if set -q _flag_check_existing; and set -q _flag_new_remote + echo "$c_err""✘$c_reset --check-existing and --new-remote are mutually exclusive" >&2 + return 1 + end if set -q _flag_no_git - if set -q _flag_remote; or set -q _flag_new_remote - echo "$c_err""✘$c_reset --remote/--new-remote require --git" >&2 + if set -q _flag_remote; or set -q _flag_new_remote; or set -q _flag_server + echo "$c_err""✘$c_reset --remote/--new-remote/--server require --git" >&2 return 1 end end + set -l srv_type '' + set -l srv_url '' + if set -q _flag_server + set srv_type $_flag_server + else if test -n "$GIT_SERVER" + set srv_type $GIT_SERVER + end + if test -n "$srv_type" + switch $srv_type + case gitea + if test -n "$GITEA_URL" + set srv_url $GITEA_URL + else if test -n "$GITEA_HOST" + set srv_url $GITEA_HOST + end + case gitlab + if test -n "$GITLAB_URL" + set srv_url $GITLAB_URL + else if test -n "$GITLAB_HOST" + set srv_url $GITLAB_HOST + end + case github + # gh defaults to github.com; no base url needed + case '*' + echo "$c_err""✘$c_reset Unrecognized server type $c_arg$srv_type$c_reset (expected gitea, gitlab, or github)" >&2 + return 1 + end + end + + if set -q _flag_check_existing; and test -z "$srv_type" + echo "$c_err""✘$c_reset --check-existing needs a resolved server (--server, \$GIT_SERVER, or \$GITEA_URL/\$GITEA_HOST/\$GITLAB_URL/\$GITLAB_HOST)" >&2 + return 1 + end + set -l do_cd 1 set -q _flag_no_cd; and set do_cd 0 set -l do_mkdir 1 @@ -258,6 +342,59 @@ function mkrep --description 'Create a directory, cd into it, and git init it' _mkrep_say $silent "$c_ok""✔$c_reset Ran remote-create command" end + if not set -q _flag_remote; and not set -q _flag_new_remote + set -l name $_flag_name + test -z "$name"; and set name (path basename (path resolve $dir)) + + if set -q _flag_check_existing + if _mkrep_repo_exists $srv_type $USER $name + _mkrep_say $silent "$c_warn""→$c_reset $c_arg$USER/$name$c_reset already exists on $srv_type" + else + _mkrep_say $silent "$c_ok""✔$c_reset $c_arg$USER/$name$c_reset does not exist on $srv_type" + end + else if test -n "$srv_type"; and test $do_git -eq 1 + 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 + 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 + set -l cmd $MKREP_REMOTE_CMD + test -z "$cmd"; and set cmd (_mkrep_default_remote_cmd $srv_type) + + if string match -q '*{server}*' -- $cmd + if test -z "$srv_url" + echo "$c_err""✘$c_reset No base URL resolved for $srv_type (set \$GITEA_URL/\$GITEA_HOST or \$GITLAB_URL/\$GITLAB_HOST)" >&2 + cd $orig_pwd + return 1 + end + set cmd (string replace -a '{server}' $srv_url -- $cmd) + end + set cmd (string replace -a '{name}' $name -- $cmd) + set cmd (string replace -a '{user}' $USER -- $cmd) + + _mkrep_verbose $silent $verbose "$c_dim""Running: $cmd$c_reset" + if test $silent -eq 1 + eval $cmd >/dev/null 2>&1 + else + eval $cmd + end + or begin + echo "$c_err""✘$c_reset Remote-create command failed" >&2 + cd $orig_pwd + return 1 + end + set -l url (_mkrep_remote_url $srv_type $USER $name $srv_url) + _mkrep_say $silent "$c_ok""✔$c_reset Created new remote $c_arg$url$c_reset on $srv_type" + end + end + end + if test $do_cd -eq 0 cd $orig_pwd else diff --git a/tests/test-mkrep.fish b/tests/test-mkrep.fish index c48303e..d863688 100644 --- a/tests/test-mkrep.fish +++ b/tests/test-mkrep.fish @@ -184,6 +184,173 @@ check "env fallback template ran" repo (cat $target/created.txt) cd $start rm -rf $base +section "mkrep: --server conflicts with --remote/--new-remote" + +set -l base (_mkrep_sandbox) +set -l target $base/repo +mkrep --server gitea --remote https://example.invalid/x.git $target >/dev/null 2>/tmp/mkrep-test-err +check "--server + --remote exits 1" 1 $status +mkrep --server gitea --new-remote $target >/dev/null 2>/tmp/mkrep-test-err +check "--server + --new-remote exits 1" 1 $status +check "conflicting server flags create nothing" false (test -d $target; and echo true; or echo false) +rm -f /tmp/mkrep-test-err +rm -rf $base + +section "mkrep: --check-existing conflicts with --remote/--new-remote" + +set -l base (_mkrep_sandbox) +set -l target $base/repo +mkrep --check-existing --remote https://example.invalid/x.git $target >/dev/null 2>/tmp/mkrep-test-err +check "--check-existing + --remote exits 1" 1 $status +rm -f /tmp/mkrep-test-err +rm -rf $base + +section "mkrep: --check-existing needs a resolved server" + +begin + # Shadow to empty rather than erase: these are real exported vars in + # this dev environment (GITEA_URL et al.), and mkrep treats an empty + # value the same as unset, so this gives a clean slate without + # touching the actual global. + set -lx GIT_SERVER '' + set -lx GITEA_URL '' + set -lx GITEA_HOST '' + set -lx GITLAB_URL '' + set -lx GITLAB_HOST '' + set -l base (_mkrep_sandbox) + set -l target $base/repo + mkrep --check-existing $target >/dev/null 2>/tmp/mkrep-test-err + check "--check-existing with no server exits 1" 1 $status + check "unresolved --check-existing creates nothing" false (test -d $target; and echo true; or echo false) + rm -f /tmp/mkrep-test-err + rm -rf $base +end + +section "mkrep: --server requires --git" + +set -l base (_mkrep_sandbox) +set -l target $base/repo +mkrep --no-git --server gitea $target >/dev/null 2>/tmp/mkrep-test-err +check "--no-git + --server exits 1" 1 $status +rm -f /tmp/mkrep-test-err +rm -rf $base + +section "mkrep: --server rejects an unknown type" + +set -l base (_mkrep_sandbox) +set -l target $base/repo +mkrep --server bitbucket $target >/dev/null 2>/tmp/mkrep-test-err +check "unknown --server type exits 1" 1 $status +rm -f /tmp/mkrep-test-err +rm -rf $base + +# The rest of this section stub gh/glab/tea so no real network/CLI auth is +# needed: a fake binary on $PATH decides whether the "repo" is reported as +# already existing, and $MKREP_REMOTE_CMD (which --server honors exactly +# like --new-remote) stands in for the real create command. +set -g stub_bin (mktemp -d) + +function _mkrep_stub_tool --argument-names name exit_code + printf '#!/bin/sh\nexit %s\n' $exit_code >$stub_bin/$name + chmod +x $stub_bin/$name +end + +section "mkrep: --server auto-creates when the repo does not exist" + +begin + _mkrep_stub_tool tea 1 + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx PATH $stub_bin $PATH + set -lx GITEA_URL https://gitea.example.invalid + set -lx MKREP_REMOTE_CMD 'echo {server}/{user}/{name} >created.txt' + mkrep --server gitea $target >/dev/null + check "--server (repo absent) exits 0" 0 $status + check "--server ran the create template" "https://gitea.example.invalid/$USER/repo" (cat $target/created.txt) + cd $start + rm -rf $base +end + +section "mkrep: --server links instead of creating when the repo exists" + +begin + _mkrep_stub_tool tea 0 + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx PATH $stub_bin $PATH + set -lx GITEA_URL https://gitea.example.invalid + set -lx MKREP_REMOTE_CMD 'echo should-not-run >created.txt' + mkrep --server gitea $target >/dev/null + check "--server (repo present) exits 0" 0 $status + check "--server did not run the create template" false (test -f $target/created.txt; and echo true; or echo false) + set -l url (git -C $target remote get-url origin) + check "--server linked the existing repo's url" "https://gitea.example.invalid/$USER/repo.git" $url + cd $start + rm -rf $base +end + +section "mkrep: \$GITEA_URL alone (no \$GIT_SERVER) does not trigger anything" + +begin + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx GITEA_URL https://gitea.example.invalid + mkrep $target >/dev/null + check "bare \$GITEA_URL exits 0" 0 $status + check "bare \$GITEA_URL creates no remote" 0 (count (git -C $target remote)) + cd $start + rm -rf $base +end + +section "mkrep: \$GIT_SERVER + \$GITEA_URL together trigger the auto-create flow" + +begin + _mkrep_stub_tool tea 1 + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx PATH $stub_bin $PATH + set -lx GIT_SERVER gitea + set -lx GITEA_URL https://gitea.example.invalid + set -lx MKREP_REMOTE_CMD 'echo {name} >created.txt' + mkrep $target >/dev/null + check "\$GIT_SERVER + \$GITEA_URL exits 0" 0 $status + check "\$GIT_SERVER picked gitea" repo (cat $target/created.txt) + cd $start + rm -rf $base +end + +section "mkrep: an explicit --remote overrides \$GIT_SERVER/\$GITEA_URL" + +begin + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx GIT_SERVER gitea + set -lx GITEA_URL https://gitea.example.invalid + mkrep --remote https://example.invalid/me/repo.git $target >/dev/null + check "--remote over env exits 0" 0 $status + set -l url (git -C $target remote get-url origin) + check "--remote wins over \$GIT_SERVER/\$GITEA_URL" https://example.invalid/me/repo.git $url + cd $start + rm -rf $base +end + +section "mkrep: --check-existing reports without creating anything" + +begin + _mkrep_stub_tool tea 0 + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx PATH $stub_bin $PATH + set -l out (mkrep --server gitea --check-existing $target) + check "--check-existing exits 0" 0 $status + check "--check-existing creates the directory (mkrep's other defaults still run)" true (test -d $target; and echo true; or echo false) + check "--check-existing adds no remote" 0 (count (git -C $target remote)) + cd $start + rm -rf $base +end + +rm -rf $stub_bin + section "mkrep: --help" set -l base (_mkrep_sandbox) -- 2.54.0 From 0a3b332d794c24ebdcb80d9f70b628cc8f3c329b Mon Sep 17 00:00:00 2001 From: Rootiest Date: Mon, 14 Sep 2026 23:11:12 -0400 Subject: [PATCH 2/3] fix(git): prepend https:// to *_HOST vars in mkrep server detection $GITEA_HOST/$GITLAB_HOST are bare hostnames (git.example.com); only $GITEA_URL/$GITLAB_URL are expected to carry a scheme. mkrep was using _HOST values as-is, producing a schemeless clone URL when only the _HOST var was set. --- functions/mkrep.fish | 8 +++++--- tests/test-mkrep.fish | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/functions/mkrep.fish b/functions/mkrep.fish index 06b0b74..b9f638a 100644 --- a/functions/mkrep.fish +++ b/functions/mkrep.fish @@ -42,7 +42,9 @@ # --server (gitea, gitlab, or github) picks a host without an # explicit --remote/--new-remote: resolve its base URL from # $GITEA_URL/$GITEA_HOST (gitea) or $GITLAB_URL/$GITLAB_HOST (gitlab), -# preferring the _URL form when both are set, then run +# preferring the _URL form when both are set. _URL is used as-is and +# must include its scheme (https://git.example.com); _HOST is bare +# (git.example.com) and gets https:// prepended. Then run # $MKREP_REMOTE_CMD or that type's built-in default template. With no # --server, --remote, or --new-remote, $GIT_SERVER picks the type the # same way (invalid values are rejected the same as an invalid @@ -195,13 +197,13 @@ function mkrep --description 'Create a directory, cd into it, and git init it' if test -n "$GITEA_URL" set srv_url $GITEA_URL else if test -n "$GITEA_HOST" - set srv_url $GITEA_HOST + set srv_url "https://$GITEA_HOST" end case gitlab if test -n "$GITLAB_URL" set srv_url $GITLAB_URL else if test -n "$GITLAB_HOST" - set srv_url $GITLAB_HOST + set srv_url "https://$GITLAB_HOST" end case github # gh defaults to github.com; no base url needed diff --git a/tests/test-mkrep.fish b/tests/test-mkrep.fish index d863688..6b9a570 100644 --- a/tests/test-mkrep.fish +++ b/tests/test-mkrep.fish @@ -289,6 +289,38 @@ begin rm -rf $base end +section "mkrep: \$GITEA_HOST gets https:// prepended, \$GITEA_URL wins over it" + +begin + _mkrep_stub_tool tea 1 + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx PATH $stub_bin $PATH + set -lx GITEA_URL '' + set -lx GITEA_HOST gitea.example.invalid + set -lx MKREP_REMOTE_CMD 'echo {server} >created.txt' + mkrep --server gitea $target >/dev/null + check "bare \$GITEA_HOST exits 0" 0 $status + check "bare \$GITEA_HOST gets https:// prepended" https://gitea.example.invalid (cat $target/created.txt) + cd $start + rm -rf $base +end + +begin + _mkrep_stub_tool tea 1 + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx PATH $stub_bin $PATH + set -lx GITEA_HOST wrong.example.invalid + set -lx GITEA_URL https://right.example.invalid + set -lx MKREP_REMOTE_CMD 'echo {server} >created.txt' + mkrep --server gitea $target >/dev/null + check "\$GITEA_URL wins over \$GITEA_HOST exits 0" 0 $status + check "\$GITEA_URL wins over \$GITEA_HOST, used as-is" https://right.example.invalid (cat $target/created.txt) + cd $start + rm -rf $base +end + section "mkrep: \$GITEA_URL alone (no \$GIT_SERVER) does not trigger anything" begin -- 2.54.0 From 17721a1cf2a877f2e9aaed4e544a49a5cad3a2d9 Mon Sep 17 00:00:00 2001 From: Rootiest Date: Mon, 14 Sep 2026 23:22:52 -0400 Subject: [PATCH 3/3] fix(git): don't push an unborn HEAD from mkrep's default remote templates mkrep only ever runs git init, never a commit, so a freshly created repo has no HEAD yet. The gitea/gitlab default templates chained `git push -u origin HEAD` unconditionally after linking the remote, which fails immediately regardless of the remote ("src refspec HEAD does not match any") -- reproduced by a real user hitting it on the first mkrep --server call. Guard the push on HEAD actually resolving to a commit; skipping it is the correct outcome (nothing to push yet), and a real push failure once a commit exists still propagates. --- functions/_mkrep_default_remote_cmd.fish | 11 ++++++++--- functions/mkrep.fish | 17 ++++++++++------- tests/test-mkrep.fish | 22 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/functions/_mkrep_default_remote_cmd.fish b/functions/_mkrep_default_remote_cmd.fish index 0ebb5f3..a39bcdf 100644 --- a/functions/_mkrep_default_remote_cmd.fish +++ b/functions/_mkrep_default_remote_cmd.fish @@ -7,7 +7,12 @@ # DESCRIPTION # Prints the default remote-create command template for , used by # mkrep's --server flow when $MKREP_REMOTE_CMD is unset. Same {name}, -# {user}, {server} placeholders as --new-remote. +# {user}, {server} placeholders as --new-remote. gitlab/gitea guard +# their final push on HEAD actually resolving to a commit -- mkrep only +# ever runs `git init`, so a fresh call has no commit yet, and +# `git push -u origin HEAD` errors on an unborn HEAD regardless of the +# remote. Skipping the push there is silent success, not a swallowed +# failure: once a commit exists, a real push failure still propagates. # # ARGUMENTS # type gitea, gitlab, or github @@ -23,9 +28,9 @@ function _mkrep_default_remote_cmd --argument-names type case github echo 'gh repo create {name} --private --source=. --remote=origin --push' case gitlab - echo 'glab repo create {name} --private --skipGitInit && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD' + echo 'glab repo create {name} --private --skipGitInit && git remote add origin {server}/{user}/{name}.git && if git rev-parse --verify -q HEAD >/dev/null 2>&1; git push -u origin HEAD; end' case gitea - echo 'tea repos create --name {name} --private && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD' + echo 'tea repos create --name {name} --private && git remote add origin {server}/{user}/{name}.git && if git rev-parse --verify -q HEAD >/dev/null 2>&1; git push -u origin HEAD; end' case '*' return 1 end diff --git a/functions/mkrep.fish b/functions/mkrep.fish index b9f638a..be8bfa9 100644 --- a/functions/mkrep.fish +++ b/functions/mkrep.fish @@ -104,14 +104,17 @@ # # Starting points for $MKREP_REMOTE_CMD, one per host CLI -- each assumes # that tool is already installed and authenticated, creates a private -# repo under the caller's own account, and pushes the initial commit. -# These are also mkrep's built-in defaults for --server/$GIT_SERVER when -# $MKREP_REMOTE_CMD is unset. gh supports a one-shot -# --source/--remote/--push; glab and tea's create commands do not, so -# they are chained with the git commands that do the linking: +# repo under the caller's own account, and pushes it if there is +# already a commit to push (mkrep itself only runs git init, so a +# freshly created repo has none yet -- pushing an unborn HEAD is a +# guaranteed error regardless of the remote, so the push is skipped +# rather than attempted). These are also mkrep's built-in defaults for +# --server/$GIT_SERVER when $MKREP_REMOTE_CMD is unset. gh supports a +# one-shot --source/--remote/--push; glab and tea's create commands do +# not, so they are chained with the git commands that do the linking: # GitHub (gh): gh repo create {name} --private --source=. --remote=origin --push -# GitLab (glab): glab repo create {name} --private --skipGitInit && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD -# Gitea (tea): tea repos create --name {name} --private && git remote add origin {server}/{user}/{name}.git && git push -u origin HEAD +# GitLab (glab): glab repo create {name} --private --skipGitInit && git remote add origin {server}/{user}/{name}.git && if git rev-parse --verify -q HEAD >/dev/null 2>&1; git push -u origin HEAD; end +# Gitea (tea): tea repos create --name {name} --private && git remote add origin {server}/{user}/{name}.git && if git rev-parse --verify -q HEAD >/dev/null 2>&1; git push -u origin HEAD; end function mkrep --description 'Create a directory, cd into it, and git init it' __fish_palette diff --git a/tests/test-mkrep.fish b/tests/test-mkrep.fish index 6b9a570..0e08179 100644 --- a/tests/test-mkrep.fish +++ b/tests/test-mkrep.fish @@ -255,6 +255,28 @@ function _mkrep_stub_tool --argument-names name exit_code chmod +x $stub_bin/$name end +section "mkrep: --server's default gitea template survives an empty (commit-less) repo" + +begin + # Regression: mkrep only ever runs `git init`, so a freshly created + # repo has no commits yet. The default template's final push must not + # error on that unborn HEAD once `tea repos create` and `git remote + # add` (both real, local-only) have already succeeded. + printf '#!/bin/sh\ncase "$2" in\n create) exit 0 ;;\n *) exit 1 ;;\nesac\n' >$stub_bin/tea + chmod +x $stub_bin/tea + set -l base (_mkrep_sandbox) + set -l target $base/repo + set -lx PATH $stub_bin $PATH + set -lx GITEA_URL https://gitea.example.invalid + set -lx MKREP_REMOTE_CMD '' + mkrep --server gitea $target >/dev/null + check "default template exits 0 with no commits yet" 0 $status + set -l url (git -C $target remote get-url origin) + check "default template still linked the new remote" "https://gitea.example.invalid/$USER/repo.git" $url + cd $start + rm -rf $base +end + section "mkrep: --server auto-creates when the repo does not exist" begin -- 2.54.0