From 8ae9c6ecda7177670c631df040a1c465107d6657 Mon Sep 17 00:00:00 2001 From: "RingOfStorms (Joshua Bell)" Date: Tue, 16 Sep 2025 14:52:07 -0500 Subject: [PATCH] more helpers for git and tmux flow --- common/general/shell/branch.func.sh | 84 ++++++++++++++++++++++++---- common/general/shell/branchd.func.sh | 10 ++++ common/general/shell/common.nix | 1 + common/general/shell/common.sh | 4 +- common/general/shell/tmux_helpers.sh | 34 +++++++++++ hosts/lio/flake.lock | 8 +-- 6 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 common/general/shell/tmux_helpers.sh diff --git a/common/general/shell/branch.func.sh b/common/general/shell/branch.func.sh index 3422ce4..03908fc 100644 --- a/common/general/shell/branch.func.sh +++ b/common/general/shell/branch.func.sh @@ -1,6 +1,48 @@ branch() { local branch_name=${1:-} + # helper: set tmux window name. If tmux is in auto mode, always rename. + # If tmux is manual but the current window name matches the previous branch, + # allow renaming from previous branch name to the new one. + _branch__maybe_set_tmux_name() { + if ! command -v tmux_window >/dev/null 2>&1; then + return 1 + fi + local new_name prev_branch tmux_status tmux_cur + new_name=${1:-} + prev_branch=${2:-} + tmux_status=$(tmux_window status 2>/dev/null || true) + if [ "$tmux_status" = "auto" ]; then + tmux_window rename "$new_name" 2>/dev/null || true + return 0 + fi + # tmux is manual. If the current tmux name matches the previous branch, + # we consider it safe to update it to the new branch name. + if [ -n "$prev_branch" ]; then + tmux_cur=$(tmux_window get 2>/dev/null || true) + if [ "$tmux_cur" = "$prev_branch" ]; then + tmux_window rename "$new_name" 2>/dev/null || true + fi + fi + } + + # helper: revert tmux to automatic rename only if current tmux name matches previous branch + _branch__revert_tmux_auto() { + if ! command -v tmux_window >/dev/null 2>&1; then + return 1 + fi + local prev_branch=${1:-} + if [ -z "$prev_branch" ]; then + tmux_window rename 2>/dev/null || true + return 0 + fi + local tmux_cur + tmux_cur=$(tmux_window get 2>/dev/null || true) + if [ "$tmux_cur" = "$prev_branch" ]; then + tmux_window rename 2>/dev/null || true + fi + } + # Determine repo root early so we can run branches inside it local common_dir if ! common_dir=$(git rev-parse --git-common-dir 2>/dev/null); then @@ -24,12 +66,11 @@ branch() { fi local branches_list_raw branches_list selection + # Gather local and remote branches with fallbacks to ensure locals appear + branches_list_raw="" if declare -f local_branches >/dev/null 2>&1; then - branches_list_raw=$(local_branches 2>/dev/null || true; remote_branches 2>/dev/null || true) - else - branches_list_raw=$(git -C "$repo_dir" branch --format='%(refname:short)' 2>/dev/null || true; git -C "$repo_dir" branch -r --format='%(refname:short)' 2>/dev/null | sed 's#^.*/##' || true) + branches_list_raw=$(cd "$repo_dir" && local_branches 2>/dev/null || true; cd "$repo_dir" && remote_branches 2>/dev/null || true) fi - branches_list=$(printf "%s " "$branches_list_raw" | awk '!seen[$0]++') if [ -z "$branches_list" ]; then @@ -37,13 +78,19 @@ branch() { return 1 fi - selection=$(printf "%s\n" "$branches_list" | fzf --height=40% --prompt="Select branch: ") - if [ -z "$selection" ]; then + fzf_out=$(printf "%s\n" "$branches_list" | fzf --height=40% --prompt="Select branch: " --print-query) + if [ -z "$fzf_out" ]; then echo "No branch selected." >&2 return 1 fi - - branch_name="$selection" + branch_query=$(printf "%s\n" "$fzf_out" | sed -n '1p') + branch_selection=$(printf "%s\n" "$fzf_out" | sed -n '2p') + if [ -n "$branch_selection" ]; then + branch_name="$branch_selection" + else + # user typed something in fzf but didn't select: use that as new branch name + branch_name=$(printf "%s" "$branch_query" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + fi fi local repo_base repo_hash default_branch @@ -52,6 +99,10 @@ branch() { default_branch=$(getdefault) + # capture current branch name as seen by tmux so we can decide safe renames later + local prev_branch + prev_branch=$(git -C "$PWD" rev-parse --abbrev-ref HEAD 2>/dev/null || true) + # Special-case: jump to the main working tree on the default branch if [ "$branch_name" = "default" ] || [ "$branch_name" = "master" ] || [ "$branch_name" = "$default_branch" ]; then if [ "$repo_dir" = "$PWD" ]; then @@ -59,7 +110,10 @@ branch() { return 0 fi echo "Switching to main working tree on branch '$default_branch'." + # capture current branch name as seen by tmux so we only revert if it matches + prev_branch=$(git -C "$PWD" rev-parse --abbrev-ref HEAD 2>/dev/null || true) cd "$repo_dir" || return 1 + _branch__revert_tmux_auto "$prev_branch" || true return 0 fi @@ -69,6 +123,7 @@ branch() { if [ -n "$existing" ]; then echo "Opening existing worktree for branch '$branch_name' at '$existing'." cd "$existing" || return 1 + _branch__maybe_set_tmux_name "$branch_name" "$prev_branch" || true return 0 fi @@ -91,6 +146,7 @@ branch() { if [ -d "$wt_path" ]; then echo "Opening existing worktree at '$wt_path'." cd "$wt_path" || return 1 + _branch__maybe_set_tmux_name "$branch_name" "$prev_branch" || true return 0 fi @@ -120,13 +176,16 @@ branch() { # Try to add or update worktree from the resolved ref. Use a fallback path if needed. if [ "$local_exists" -eq 1 ]; then - if git -C "$repo_dir" worktree add "$wt_path" "$branch_name" 2>/dev/null; then + if git -C "$repo_dir" worktree add "$wt_path" "$branch_name" 2>/dev/null; then cd "$wt_path" || return 1 + _branch__maybe_set_tmux_name "$branch_name" "$prev_branch" || true return 0 fi + else if git -C "$repo_dir" worktree add -b "$branch_name" "$wt_path" "$branch_from" 2>/dev/null; then cd "$wt_path" || return 1 + _branch__maybe_set_tmux_name "$branch_name" "$prev_branch" || true return 0 fi fi @@ -135,9 +194,10 @@ branch() { local start_sha if start_sha=$(git -C "$repo_dir" rev-parse --verify "$branch_from" 2>/dev/null); then if git -C "$repo_dir" branch "$branch_name" "$start_sha" 2>/dev/null; then - if git -C "$repo_dir" worktree add "$wt_path" "$branch_name" 2>/dev/null; then - cd "$wt_path" || return 1 - return 0 + if git -C "$repo_dir" worktree add "$wt_path" "$branch_name" 2>/dev/null; then + cd "$wt_path" || return 1 + _branch__maybe_set_tmux_name "$branch_name" "$prev_branch" || true + return 0 else git -C "$repo_dir" branch -D "$branch_name" 2>/dev/null || true rmdir "$wt_path" 2>/dev/null || true diff --git a/common/general/shell/branchd.func.sh b/common/general/shell/branchd.func.sh index 98ed5f5..053d16f 100644 --- a/common/general/shell/branchd.func.sh +++ b/common/general/shell/branchd.func.sh @@ -79,12 +79,22 @@ branchdel() { if git -C "$repo_dir" worktree remove "$target_wt" 2>/dev/null; then rm -rf -- "$target_wt" 2>/dev/null || true echo "Removed worktree: $target_wt" + # delete local branch if it exists + if git -C "$repo_dir" show-ref --verify --quiet "refs/heads/$branch"; then + git -C "$repo_dir" branch -D "$branch" 2>/dev/null || true + echo "Deleted local branch: $branch" + fi return 0 fi # try with --force as a fallback if git -C "$repo_dir" worktree remove --force "$target_wt" 2>/dev/null; then rm -rf -- "$target_wt" 2>/dev/null || true echo "Removed worktree (forced): $target_wt" + # delete local branch if it exists + if git -C "$repo_dir" show-ref --verify --quiet "refs/heads/$branch"; then + git -C "$repo_dir" branch -D "$branch" 2>/dev/null || true + echo "Deleted local branch: $branch" + fi return 0 fi diff --git a/common/general/shell/common.nix b/common/general/shell/common.nix index 16f2026..a2e1eeb 100644 --- a/common/general/shell/common.nix +++ b/common/general/shell/common.nix @@ -60,6 +60,7 @@ with lib; environment.shellInit = lib.concatStringsSep "\n\n" [ (builtins.readFile ./common.sh) + (builtins.readFile ./tmux_helpers.sh) (builtins.readFile ./branch.func.sh) (builtins.readFile ./branchd.func.sh) (builtins.readFile ./link_ignored.func.sh) diff --git a/common/general/shell/common.sh b/common/general/shell/common.sh index 8f88702..3c23918 100644 --- a/common/general/shell/common.sh +++ b/common/general/shell/common.sh @@ -103,11 +103,11 @@ forcepush () { } remote_branches () { - git branch -a | grep 'remotes' | grep -v -E '.*(HEAD|${DEFAULT})' | cut -d'/' -f 3- + git for-each-ref --format='%(refname:short)' refs/remotes 2>/dev/null | sed 's#^[^/]*/##' | grep -v '^HEAD$' || true } local_branches () { - git branch -a | grep -v 'remotes' | grep -v -E '.*(HEAD|${DEFAULT})' | grep -v '^*' | cut -d' ' -f 3- + git for-each-ref --format='%(refname:short)' refs/heads 2>/dev/null || true } prunel () { diff --git a/common/general/shell/tmux_helpers.sh b/common/general/shell/tmux_helpers.sh new file mode 100644 index 0000000..b644b4b --- /dev/null +++ b/common/general/shell/tmux_helpers.sh @@ -0,0 +1,34 @@ +tmux_window () { + cmd=${1:-} + case "${cmd}" in + rename) + if [ -z "${2:-}" ]; then + tmux setw automatic-rename + else + tmux rename-window "$2" + fi + ;; + get) + printf '%s' "$(tmux display-message -p '#W')" + ;; + status) + out="$(tmux show-window-options automatic-rename 2>/dev/null || true)" + if printf '%s' "$out" | grep -q 'automatic-rename on'; then + printf 'auto' + elif printf '%s' "$out" | grep -q 'automatic-rename off'; then + printf 'manual' + else + # If tmux returns nothing (option not set), default to auto + if [ -z "$out" ]; then + printf 'auto' + else + return 1 + fi + fi + ;; + *) + printf 'Usage: tmux_window {rename [NAME]|get|status}\n' >&2 + return 2 + ;; + esac +} diff --git a/hosts/lio/flake.lock b/hosts/lio/flake.lock index 919cf27..547d122 100644 --- a/hosts/lio/flake.lock +++ b/hosts/lio/flake.lock @@ -1206,11 +1206,11 @@ "rust-overlay": "rust-overlay_2" }, "locked": { - "lastModified": 1757954114, - "narHash": "sha256-1Y6+uiLicKPARiiIjYY32asP9oKBW9z1LEBRYOR6ajI=", + "lastModified": 1758041510, + "narHash": "sha256-vcK6ZwAWNfjdDFYKLVrWk+azva58AiDpm8nMfIniFWA=", "ref": "refs/heads/master", - "rev": "f1487458ea787031e00230b604e652464fd44139", - "revCount": 303, + "rev": "b3dbdf3f7360747987bf38bcdd9baf01b4906929", + "revCount": 304, "type": "git", "url": "https://git.joshuabell.xyz/ringofstorms/nvim" },