-
-
Notifications
You must be signed in to change notification settings - Fork 15
/
gh-clean-branches
executable file
·124 lines (100 loc) · 4.11 KB
/
gh-clean-branches
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/bin/zsh
# safely delete local branches that have no remotes and no hanging changes
# will not delete branches with not committed changes
# run inside a git repo folder
DRY_RUN=false
while [[ "$#" -gt 0 ]]; do
case "$1" in
--dry-run )
DRY_RUN=true
shift
;;
--force)
FORCE_DELETE=true
shift
;;
--verbose)
VERBOSE=true
shift
;;
* )
printf "%s\n" "Usage: gh clean-branches [--dry-run] [--force] [--verbose]"
exit 1
;;
esac
done
red=$(printf '\e[1;31m')
green=$(printf '\e[1;32m')
yellow=$(printf '\e[1;33m')
blue=$(printf '\e[1;34m')
end=$(printf '\e[0m')
printf "%s\n" "${green}Sync branches${end}"
git fetch -p >/dev/null 2>&1 # hide response
upstream_name=$(git remote show)
home_branch=$(git branch --show-current)
# Pull all upstream
for upstream_name in $(git remote show); do
default_branch=$(LANG=en_GB git remote show ${upstream_name} | awk '/HEAD branch/ {print $NF}')
printf "%s\n" "${green}Checking out ${upstream_name}/${default_branch}${end}"
git checkout $default_branch -q
printf "%s\n" "${green}Pulling ${upstream_name}/${default_branch}${end}"
git pull ${upstream_name} ${default_branch} -q
if [[ $? -ne 0 ]]; then
printf "%s\n" "${red}Failed to pull, check for uncommitted changes.${end}"
exit 1
fi
# get al upstream branches (e.g. origin/my-branch)
upstream_branches_str=$(git for-each-ref --format='%(refname:short)' refs/remotes/${upstream_name})
# trim the upstream name (e.g. "origin/") from branch names
upstream_branches_str=${upstream_branches_str//${upstream_name}\// }
# accumulated branches from all upstream
remote_branches_str="${remote_branches_str}${upstream_branches_str}\n"
if [[ ${VERBOSE} == true ]]; then
printf "%s\n%s\n" "${blue}Remote branches on ${upstream_name}:${end}" "${upstream_branches_str}"
fi
done
unique_remote_branches_str=$(echo "${remote_branches_str}" | sort -u)
local_branches_str=$(git branch)
local_branches_str=${local_branches_str/\*?/ } # trim the "*"" on the current branch
local_branches_str=${local_branches_str/\ ?refs\/heads?/} # remove the "refs/heads" if exists
if [[ ${VERBOSE} == true ]]; then
printf "%s\n%s\n" "${blue}Local branches:${end}" "${local_branches_str}"
fi
setopt extended_glob
local_branches=("${(f)local_branches_str}") # split string by \n to array
local_branches=(${local_branches:#* ${default_branch}}) # filter out default_branch
local_branches=(${local_branches// ##}) # trim spaces
remote_branches=("${(f)unique_remote_branches_str}") # split string by \n to array
remote_branches=(${remote_branches:#* ${default_branch}}) # filter out default_branch
remote_branches=(${remote_branches// ##}) # trim spaces
missing_upstream_branches=(${local_branches:|remote_branches}) # local_branches minus remote_branches
branches_count=${#missing_upstream_branches[@]}
if [[ ${FORCE_DELETE} == true ]]; then
delete_flag='-D'
else
delete_flag='-d'
fi
if [[ ${branches_count} -eq 0 ]]; then
printf "%s\n" "${green}No local branches with missing upstream found${end}"
else
printf "%s\n" "${blue}Local branches with missing upstream:${end}"
for branch in "${missing_upstream_branches[@]}"; do
printf "%s\n" " ${branch}"
done
if [[ ${DRY_RUN} == false ]]; then
[[ ${FORCE_DELETE} == true ]] && printf "%s\n" "${yellow}Force delete is enabled${end}"
for branch in "${missing_upstream_branches[@]}"; do
printf "%s\n" "${green}Deleting branch:${end} ${branch}"
git branch ${delete_flag} "${branch}" -q
if [[ $? -ne 0 ]]; then
printf "%s\n" "❌ ${red}Could not delete${end} ${branch}"
printf "%s\n" "${yellow}Try using --force flag${end}"
fi
done
else
printf "%s\n" "${green}Dry run: not deleting branches${end}"
fi
fi
# Trying to checkout the home branch, if this branch was deleted, it will silently fail and stay on the default_branch
git checkout $home_branch >/dev/null 2>&1 # hide response
printf "\n%s\n" "${green}Done${end}"