From f5c0ec77c36f0b2d79c9453776036dbc71abf692 Mon Sep 17 00:00:00 2001 From: Human Date: Tue, 23 Jun 2026 16:01:23 -0700 Subject: [PATCH 1/2] %25: Fixed branch naming bug. Previously was adding an arbitrary to the end of the branch and riectory name --- CHANGELOG.md | 5 +++++ worktree-functions.sh | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bbe4be..25f79c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html): --- +## [1.0.2] — 2026-06-23 +### Fixed +- `_wrktr_sanitize_branch_name`: Fixed branch naming bug. Previously was adding an arbitrary `%` to the end of the branch and riectory name + + ## [1.0.1] — 2026-06-05 ### Fixed diff --git a/worktree-functions.sh b/worktree-functions.sh index af58108..3de5d4e 100644 --- a/worktree-functions.sh +++ b/worktree-functions.sh @@ -917,7 +917,10 @@ function wrktr_git() { # ----------------------------------------------------------------------------- function _wrktr_sanitize_branch_name() { local branch="$1" - branch="${branch//%/%25}" + + if ! git check-ref-format --branch "$branch" >/dev/null 2>&1; then + return 1 + fi echo "${branch//\//%2F}" } From 9451cf11543c4bc505c58d5c66ce04fdd463498e Mon Sep 17 00:00:00 2001 From: Human Date: Tue, 23 Jun 2026 16:20:18 -0700 Subject: [PATCH 2/2] update to branch naming function --- tests/wrktr.bats | 87 ++++++++++++++++++++++++++++++++++++++++--- worktree-functions.sh | 17 ++++++++- 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/tests/wrktr.bats b/tests/wrktr.bats index b0855a6..f063ef7 100644 --- a/tests/wrktr.bats +++ b/tests/wrktr.bats @@ -66,6 +66,21 @@ teardown() { [ "$result" = "main" ] } +@test "_wrktr_sanitize_branch_name: plain name with dash is unchanged" { + result="$(_wrktr_sanitize_branch_name "fix-crash-100")" + [ "$result" = "fix-crash-100" ] +} + +@test "_wrktr_sanitize_branch_name: plain name with underscore is unchanged" { + result="$(_wrktr_sanitize_branch_name "fix_crash_100")" + [ "$result" = "fix_crash_100" ] +} + +@test "_wrktr_sanitize_branch_name: plain name with dot is unchanged" { + result="$(_wrktr_sanitize_branch_name "release.2026")" + [ "$result" = "release.2026" ] +} + @test "_wrktr_sanitize_branch_name: single slash encoded as %2F" { result="$(_wrktr_sanitize_branch_name "feature/login")" [ "$result" = "feature%2Flogin" ] @@ -76,16 +91,76 @@ teardown() { [ "$result" = "a%2Fb%2Fc" ] } -@test "_wrktr_sanitize_branch_name: percent encoded before slash to avoid double-encoding" { - result="$(_wrktr_sanitize_branch_name "fix/crash-100%")" - [ "$result" = "fix%2Fcrash-100%25" ] -} - -@test "_wrktr_sanitize_branch_name: existing percent-sign is escaped first" { +@test "_wrktr_sanitize_branch_name: existing percent-sign is escaped before slash encoding" { result="$(_wrktr_sanitize_branch_name "already%2Fencoded")" [ "$result" = "already%252Fencoded" ] } +@test "_wrktr_sanitize_branch_name: internal percent-sign is escaped" { + result="$(_wrktr_sanitize_branch_name "fix/100%repro")" + [ "$result" = "fix%2F100%25repro" ] +} + +@test "_wrktr_sanitize_branch_name: trailing percent is rejected" { + run _wrktr_sanitize_branch_name "fix/crash-100%" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: trailing dash is rejected" { + run _wrktr_sanitize_branch_name "fix/crash-100-" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: trailing underscore is rejected" { + run _wrktr_sanitize_branch_name "fix/crash-100_" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: trailing dot is rejected by git" { + run _wrktr_sanitize_branch_name "fix/crash-100." + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: space is rejected" { + run _wrktr_sanitize_branch_name "feature/bad name" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: exclamation mark is rejected" { + run _wrktr_sanitize_branch_name "feature/bad!" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: at sign is rejected by git" { + run _wrktr_sanitize_branch_name "@" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: double dot is rejected by git" { + run _wrktr_sanitize_branch_name "feature/foo..bar" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: lock suffix is rejected by git" { + run _wrktr_sanitize_branch_name "feature/foo.lock" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + +@test "_wrktr_sanitize_branch_name: empty branch is rejected" { + run _wrktr_sanitize_branch_name "" + [ "$status" -eq 1 ] + [ "$output" = "" ] +} + # =========================================================================== # _wrktr_assert_subpath # =========================================================================== diff --git a/worktree-functions.sh b/worktree-functions.sh index 3de5d4e..3f99e34 100644 --- a/worktree-functions.sh +++ b/worktree-functions.sh @@ -921,7 +921,22 @@ function _wrktr_sanitize_branch_name() { if ! git check-ref-format --branch "$branch" >/dev/null 2>&1; then return 1 fi - echo "${branch//\//%2F}" + + # Require the original branch name to end in a letter or number. + # This prevents trailing %, -, _, ., etc. from becoming acceptable after encoding. + if [[ ! "$branch" =~ [A-Za-z0-9]$ ]]; then + return 1 + fi + + branch="${branch//%/%25}" + branch="${branch//\//%2F}" + + # Allow only safe output characters and known encodings. + if [[ ! "$branch" =~ ^([A-Za-z0-9._-]|%2F|%25)+$ ]]; then + return 1 + fi + + printf '%s\n' "$branch" } # -----------------------------------------------------------------------------