test-force-select.sh
#!/bin/bash ## The hand-editing journey: import and verify narrow to named branches, ## and import -f replays over an existing ref, keeping the old tip in the ## reflog. Without -f, an existing ref is still refused. set -euo pipefail cd "$(dirname "$0")" source ./lib.sh work=$(mktemp -d) trap 'rm -rf "$work"' EXIT make_corpus "$work/corpus" source "$work/corpus/FACTS" "$PATCHETTE" export --subject="$work/corpus/dev" --patches="$work/patches" "$base" topic/fancy docs-polish >/dev/null fancy=$'topic\u00a6fancy' # --- selection --- git clone -q "$work/corpus/upstream" "$work/subset" "$PATCHETTE" import --subject="$work/subset" --patches="$work/patches" docs-polish >/dev/null EXPECT equal "a named-subset import creates only the named refs" -- \ "$(git -C "$work/subset" for-each-ref --format='%(refname)' refs/heads/docs-polish refs/heads/topic/fancy)" "refs/heads/docs-polish" EXPECT refusal "a selection naming no branch folder is an error" "no branch folder for 'no-such-branch'" -- \ "$PATCHETTE" import --subject="$work/subset" --patches="$work/patches" no-such-branch # Narrowed verify reads only the named folders: tamper the other one and # watch selection dodge it while an unnarrowed verify trips on it. sed -i 's/> Polish the readme/> Polish the readme harder/' "$work/patches/docs-polish/polish-the-readme.patch" EXPECT success "a narrowed verify reads only the named folders" -- \ "$PATCHETTE" verify --subject="$work/corpus/dev" --patches="$work/patches" topic/fancy EXPECT refusal "while an unnarrowed verify still sees the rest" "MISMATCH" -- \ "$PATCHETTE" verify --subject="$work/corpus/dev" --patches="$work/patches" # Narrowing narrows validation too: a folder that fails validation (here, an # orphan patch file) blocks only the commands that include it. cp -r "$work/patches" "$work/strayed" cp "$work/strayed/docs-polish/polish-the-readme.patch" "$work/strayed/docs-polish/stray.patch" EXPECT success "a narrowed verify isn't blocked by another folder's validation failure" -- \ "$PATCHETTE" verify --subject="$work/corpus/dev" --patches="$work/strayed" topic/fancy EXPECT refusal "while an unnarrowed verify refuses it" "orphan" -- \ "$PATCHETTE" verify --subject="$work/corpus/dev" --patches="$work/strayed" # --- import -f --- # The docs-polish patch set is now hand-edited; its replay tip differs from # the branch the earlier import created. EXPECT refusal "an existing ref is refused without -f, which gets mentioned" "-f updates it instead" -- \ "$PATCHETTE" import --subject="$work/subset" --patches="$work/patches" docs-polish EXPECT equal "the refused ref is untouched" -- \ "$(git -C "$work/subset" rev-parse refs/heads/docs-polish)" "$tip_docs_polish" "$PATCHETTE" import -f --subject="$work/subset" --patches="$work/patches" docs-polish >/dev/null newtip=$(git -C "$work/subset" rev-parse refs/heads/docs-polish) EXPECT success "import -f updates the ref to the edited replay" -- \ test "$newtip" != "$tip_docs_polish" EXPECT success "the previous tip remains findable in the reflog" -- \ grep -q "$tip_docs_polish" <(git -C "$work/subset" log -g --format=%H refs/heads/docs-polish) # -f still never moves a branch some checkout has checked out (git branch -f # refuses the same): that checkout's index and files would be left behind, # reading as a staged revert. The main checkout and linked worktrees alike. git clone -q "$work/corpus/upstream" "$work/busy" git -C "$work/busy" switch -qc docs-polish EXPECT refusal "import -f refuses a branch checked out in the main checkout" "checked out in" -- \ "$PATCHETTE" import -f --subject="$work/busy" --patches="$work/patches" docs-polish EXPECT equal "and leaves it where it was" -- \ "$(git -C "$work/busy" rev-parse refs/heads/docs-polish)" "$base" git -C "$work/busy" branch -q topic/fancy main git -C "$work/busy" worktree add -q "$work/busy-wt" topic/fancy EXPECT refusal "import -f refuses a branch checked out in a linked worktree" "checked out in" -- \ "$PATCHETTE" import -f --subject="$work/busy" --patches="$work/patches" topic/fancy EXPECT equal "and leaves it where it was" -- \ "$(git -C "$work/busy" rev-parse refs/heads/topic/fancy)" "$base" # A bare repo has no reflogs by default (core.logAllRefUpdates is off), so # this leg is what proves import creates the reflog trail itself rather than # inheriting it from non-bare defaults. git clone -q --bare "$work/corpus/upstream" "$work/bare.git" "$PATCHETTE" import --subject="$work/bare.git" --patches="$work/patches" topic/fancy >/dev/null sed -i 's/> Teach gizmo to hum/> Teach gizmo to really hum/' "$work/patches/$fancy/teach-gizmo-to-hum.patch" "$PATCHETTE" import -f --subject="$work/bare.git" --patches="$work/patches" topic/fancy >/dev/null EXPECT success "even in a bare repo, the previous tip remains in the reflog" -- \ grep -q "$tip_topic_fancy" <(git -C "$work/bare.git" log -g --format=%H refs/heads/topic/fancy) # Forcing a ref to the tip it already has changes nothing, and says so, # rather than claiming an update with the same tip as "previous". EXPECT success "a no-op import -f reports the ref as unchanged" -- \ bash -c '"$@" | grep -q "unchanged"' -- \ "$PATCHETTE" import -f --subject="$work/bare.git" --patches="$work/patches" topic/fancy exit "$RESULT"
tests/test-force-select.sh · 5.1 KiB · 28b2b86895