Skip to content

Commit d7d0db8

Browse files
committed
refactor: extract _clean_merged from cmd_clean (#118)
Move the --merged workflow (105 lines, provider detection, branch iteration, PR status checking) into its own _clean_merged() function. cmd_clean now calls it conditionally.
1 parent 7c5857d commit d7d0db8

File tree

1 file changed

+111
-103
lines changed

1 file changed

+111
-103
lines changed

bin/gtr

Lines changed: 111 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,116 @@ cmd_list() {
10331033
}
10341034

10351035
# Clean command (remove prunable worktrees)
1036+
# Remove worktrees whose PRs/MRs are merged (handles squash merges)
1037+
# Usage: _clean_merged repo_root base_dir prefix yes_mode dry_run
1038+
_clean_merged() {
1039+
local repo_root="$1" base_dir="$2" prefix="$3" yes_mode="$4" dry_run="$5"
1040+
1041+
log_step "Checking for worktrees with merged PRs/MRs..."
1042+
1043+
# Detect hosting provider (GitHub, GitLab, etc.)
1044+
local provider
1045+
provider=$(detect_provider) || true
1046+
if [ -z "$provider" ]; then
1047+
local remote_url
1048+
remote_url=$(git remote get-url origin 2>/dev/null || true)
1049+
if [ -z "$remote_url" ]; then
1050+
log_error "No remote URL configured for 'origin'"
1051+
else
1052+
# Sanitize URL to avoid leaking embedded credentials (e.g., https://token@host/...)
1053+
local safe_url="${remote_url%%@*}"
1054+
if [ "$safe_url" != "$remote_url" ]; then
1055+
safe_url="<redacted>@${remote_url#*@}"
1056+
fi
1057+
log_error "Could not detect hosting provider from remote URL: $safe_url"
1058+
log_info "Set manually: git gtr config set gtr.provider github (or gitlab)"
1059+
fi
1060+
exit 1
1061+
fi
1062+
1063+
# Ensure provider CLI is available and authenticated
1064+
ensure_provider_cli "$provider" || exit 1
1065+
1066+
# Fetch latest from origin
1067+
log_step "Fetching from origin..."
1068+
git fetch origin --prune 2>/dev/null || log_warn "Could not fetch from origin"
1069+
1070+
local removed=0
1071+
local skipped=0
1072+
1073+
# Get main repo branch to exclude it
1074+
local main_branch
1075+
main_branch=$(current_branch "$repo_root")
1076+
1077+
# Iterate through worktree directories
1078+
for dir in "$base_dir/${prefix}"*; do
1079+
[ -d "$dir" ] || continue
1080+
1081+
local branch
1082+
branch=$(current_branch "$dir")
1083+
1084+
if [ -z "$branch" ] || [ "$branch" = "(detached)" ]; then
1085+
log_warn "Skipping $dir (detached HEAD)"
1086+
skipped=$((skipped + 1))
1087+
continue
1088+
fi
1089+
1090+
# Skip if same as main repo branch
1091+
if [ "$branch" = "$main_branch" ]; then
1092+
continue
1093+
fi
1094+
1095+
# Check if worktree has uncommitted changes
1096+
if ! git -C "$dir" diff --quiet 2>/dev/null || \
1097+
! git -C "$dir" diff --cached --quiet 2>/dev/null; then
1098+
log_warn "Skipping $branch (has uncommitted changes)"
1099+
skipped=$((skipped + 1))
1100+
continue
1101+
fi
1102+
1103+
# Check for untracked files
1104+
if [ -n "$(git -C "$dir" ls-files --others --exclude-standard 2>/dev/null)" ]; then
1105+
log_warn "Skipping $branch (has untracked files)"
1106+
skipped=$((skipped + 1))
1107+
continue
1108+
fi
1109+
1110+
# Check if branch has a merged PR/MR
1111+
if check_branch_merged "$provider" "$branch"; then
1112+
if [ "$dry_run" -eq 1 ]; then
1113+
log_info "[dry-run] Would remove: $branch ($dir)"
1114+
removed=$((removed + 1))
1115+
elif [ "$yes_mode" -eq 1 ] || prompt_yes_no "Remove worktree and delete branch '$branch'?"; then
1116+
log_step "Removing worktree: $branch"
1117+
local remove_output
1118+
if remove_output=$(git worktree remove "$dir" 2>&1); then
1119+
# Also delete the local branch
1120+
git branch -d "$branch" 2>/dev/null || git branch -D "$branch" 2>/dev/null || true
1121+
log_info "Removed: $branch"
1122+
removed=$((removed + 1))
1123+
else
1124+
if [ -n "$remove_output" ]; then
1125+
log_error "Failed to remove worktree: $remove_output"
1126+
else
1127+
log_error "Failed to remove worktree: $branch"
1128+
fi
1129+
fi
1130+
else
1131+
log_warn "Skipped: $branch (user declined)"
1132+
skipped=$((skipped + 1))
1133+
fi
1134+
fi
1135+
# Branches without merged PRs are silently skipped (this is the normal case)
1136+
done
1137+
1138+
echo ""
1139+
if [ "$dry_run" -eq 1 ]; then
1140+
log_info "Dry run complete. Would remove: $removed, Skipped: $skipped"
1141+
else
1142+
log_info "Merged cleanup complete. Removed: $removed, Skipped: $skipped"
1143+
fi
1144+
}
1145+
10361146
cmd_clean() {
10371147
local merged_mode=0
10381148
local yes_mode=0
@@ -1104,109 +1214,7 @@ EOF
11041214

11051215
# --merged mode: remove worktrees with merged PRs/MRs (handles squash merges)
11061216
if [ "$merged_mode" -eq 1 ]; then
1107-
log_step "Checking for worktrees with merged PRs/MRs..."
1108-
1109-
# Detect hosting provider (GitHub, GitLab, etc.)
1110-
local provider
1111-
provider=$(detect_provider) || true
1112-
if [ -z "$provider" ]; then
1113-
local remote_url
1114-
remote_url=$(git remote get-url origin 2>/dev/null || true)
1115-
if [ -z "$remote_url" ]; then
1116-
log_error "No remote URL configured for 'origin'"
1117-
else
1118-
# Sanitize URL to avoid leaking embedded credentials (e.g., https://token@host/...)
1119-
local safe_url="${remote_url%%@*}"
1120-
if [ "$safe_url" != "$remote_url" ]; then
1121-
safe_url="<redacted>@${remote_url#*@}"
1122-
fi
1123-
log_error "Could not detect hosting provider from remote URL: $safe_url"
1124-
log_info "Set manually: git gtr config set gtr.provider github (or gitlab)"
1125-
fi
1126-
exit 1
1127-
fi
1128-
1129-
# Ensure provider CLI is available and authenticated
1130-
ensure_provider_cli "$provider" || exit 1
1131-
1132-
# Fetch latest from origin
1133-
log_step "Fetching from origin..."
1134-
git fetch origin --prune 2>/dev/null || log_warn "Could not fetch from origin"
1135-
1136-
local removed=0
1137-
local skipped=0
1138-
1139-
# Get main repo branch to exclude it
1140-
local main_branch
1141-
main_branch=$(current_branch "$repo_root")
1142-
1143-
# Iterate through worktree directories
1144-
for dir in "$base_dir/${prefix}"*; do
1145-
[ -d "$dir" ] || continue
1146-
1147-
local branch
1148-
branch=$(current_branch "$dir")
1149-
1150-
if [ -z "$branch" ] || [ "$branch" = "(detached)" ]; then
1151-
log_warn "Skipping $dir (detached HEAD)"
1152-
skipped=$((skipped + 1))
1153-
continue
1154-
fi
1155-
1156-
# Skip if same as main repo branch
1157-
if [ "$branch" = "$main_branch" ]; then
1158-
continue
1159-
fi
1160-
1161-
# Check if worktree has uncommitted changes
1162-
if ! git -C "$dir" diff --quiet 2>/dev/null || \
1163-
! git -C "$dir" diff --cached --quiet 2>/dev/null; then
1164-
log_warn "Skipping $branch (has uncommitted changes)"
1165-
skipped=$((skipped + 1))
1166-
continue
1167-
fi
1168-
1169-
# Check for untracked files
1170-
if [ -n "$(git -C "$dir" ls-files --others --exclude-standard 2>/dev/null)" ]; then
1171-
log_warn "Skipping $branch (has untracked files)"
1172-
skipped=$((skipped + 1))
1173-
continue
1174-
fi
1175-
1176-
# Check if branch has a merged PR/MR
1177-
if check_branch_merged "$provider" "$branch"; then
1178-
if [ "$dry_run" -eq 1 ]; then
1179-
log_info "[dry-run] Would remove: $branch ($dir)"
1180-
removed=$((removed + 1))
1181-
elif [ "$yes_mode" -eq 1 ] || prompt_yes_no "Remove worktree and delete branch '$branch'?"; then
1182-
log_step "Removing worktree: $branch"
1183-
local remove_output
1184-
if remove_output=$(git worktree remove "$dir" 2>&1); then
1185-
# Also delete the local branch
1186-
git branch -d "$branch" 2>/dev/null || git branch -D "$branch" 2>/dev/null || true
1187-
log_info "Removed: $branch"
1188-
removed=$((removed + 1))
1189-
else
1190-
if [ -n "$remove_output" ]; then
1191-
log_error "Failed to remove worktree: $remove_output"
1192-
else
1193-
log_error "Failed to remove worktree: $branch"
1194-
fi
1195-
fi
1196-
else
1197-
log_warn "Skipped: $branch (user declined)"
1198-
skipped=$((skipped + 1))
1199-
fi
1200-
fi
1201-
# Branches without merged PRs are silently skipped (this is the normal case)
1202-
done
1203-
1204-
echo ""
1205-
if [ "$dry_run" -eq 1 ]; then
1206-
log_info "Dry run complete. Would remove: $removed, Skipped: $skipped"
1207-
else
1208-
log_info "Merged cleanup complete. Removed: $removed, Skipped: $skipped"
1209-
fi
1217+
_clean_merged "$repo_root" "$base_dir" "$prefix" "$yes_mode" "$dry_run"
12101218
fi
12111219
}
12121220

0 commit comments

Comments
 (0)