From 17721a1cf2a877f2e9aaed4e544a49a5cad3a2d9 Mon Sep 17 00:00:00 2001 From: Rootiest Date: Mon, 14 Sep 2026 23:22:52 -0400 Subject: [PATCH] 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