/
sweepwip
executable file
·180 lines (153 loc) · 6.05 KB
/
sweepwip
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env bash
# TODOS:
# check branches for wip?
DEFAULT_PROJECT_DIR=${SWEEPWIP_DIR:-${HOME}/Projects}
PROJECTS_DIR=${DEFAULT_PROJECT_DIR}
usage() {
[[ -n ${1} ]] && echo -e "** ${1}\n"
cat <<EOU
Scans directories for '.git' and outputs the state of the local git repository.
Usage $(basename $0) [ -h|--help ] [ -d DIR_TO_SCAN ] [ -abflMPRu ]
DIR_TO_SCAN Directory where find projects managed by git, currently it
defaults to "${DEFAULT_PROJECT_DIR}", you can change it by
setting the environment variable SWEEPWIP_DIR or using
the option -d.
Uppercase options like -M will supress behavior that is eneable by default,
lowercase options do the oposite.
-h|--help This message
-a Shows log messages of commits ahead
-b Shows log messages of commits behind
-d DIR Directory to scan for repositories, it superseeds SWEEPWIP_DIR
environment variable.
-f Perform 'git fetch'
-l List details of local changes
-M Supress local changes check, this will supress -l
-P Supress local push/pull differences
-R Supress check for remotes
-u Try fast-forward changes (git merge --ff-only)
-k Same as providing -a -b -f -l
-K Same as providing -f -a -b -u -d .
EOU
exit
}
# be compatible with GNU help style.
[[ ${1} == "--help" ]] && usage
# try to be backward compatible with old straight directory
# argument "sweepwip directory/", emit message, re-invoke
# with "-d" and quit the wrong invocation.
[[ -d "${1}" ]] \
&& (tput setaf 1; echo -e "*** Deprecated usage, please use -d. See --help"; tput sgr0;) \
&& (${0} -d ${@}) \
&& exit
# except 'h' here and 'p' in check_project, these
# options need to be the same
while getopts “hd:fMPRluabkK” OPTION; do
case $OPTION in
h) usage;;
d) PROJECTS_DIR=${OPTARG};;
K) PROJECTS_DIR=".";;
\?) usage;;
esac
done
check_project() {
# Options...
LIST_COMMITS_AHEAD=
LIST_COMMITS_BEHIND=
PERFORM_GIT_FETCH=
LIST_LOCAL_CHANGES=
FAST_FORWARD_CHANGES=
LOCAL_CHANGES=true
CHANGES_NOT_PUSHED=true
REMOTE_NOT_DEFINED=true
function _flip_option() { [[ ${1} == 'true' ]] && echo '' || echo 'true'; }
while getopts “p:d:fMPRluabkK” OPTION; do
case $OPTION in
d) PROJECTS_DIR=${OPTARG};;
p) PROJECT=${OPTARG};;
f) PERFORM_GIT_FETCH=$(_flip_option ${PERFORM_GIT_FETCH});;
M) LOCAL_CHANGES=$(_flip_option ${LOCAL_CHANGES});;
l) LIST_LOCAL_CHANGES=$(_flip_option ${LIST_LOCAL_CHANGES});;
P) CHANGES_NOT_PUSHED=$(_flip_option ${CHANGES_NOT_PUSHED});;
R) REMOTE_NOT_DEFINED=$(_flip_option ${REMOTE_NOT_DEFINED});;
u) FAST_FORWARD_CHANGES=$(_flip_option ${FAST_FORWARD_CHANGES});;
a) LIST_COMMITS_AHEAD=$(_flip_option ${LIST_COMMITS_AHEAD});;
b) LIST_COMMITS_BEHIND=$(_flip_option ${LIST_COMMITS_BEHIND});;
k)
LIST_COMMITS_AHEAD=$(_flip_option ${LIST_COMMITS_AHEAD})
LIST_COMMITS_BEHIND=$(_flip_option ${LIST_COMMITS_BEHIND})
PERFORM_GIT_FETCH=$(_flip_option ${PERFORM_GIT_FETCH})
LIST_LOCAL_CHANGES=$(_flip_option ${LIST_LOCAL_CHANGES})
;;
K)
PERFORM_GIT_FETCH=$(_flip_option ${PERFORM_GIT_FETCH})
LIST_COMMITS_AHEAD=$(_flip_option ${LIST_COMMITS_AHEAD})
LIST_COMMITS_BEHIND=$(_flip_option ${LIST_COMMITS_BEHIND})
PERFORM_GIT_FETCH=$(_flip_option ${PERFORM_GIT_FETCH})
;;
esac
done
function _colored() { tput setaf ${1}; echo -e ${2}; tput sgr0; }
function in_red() { _colored 1 "${1}"; }
function in_green() { _colored 2 "${1}"; }
function in_yellow() { _colored 3 "${1}"; }
function in_cyan() { _colored 6 "${1}"; }
[[ ! -d ${PROJECTS_DIR} ]] && in_red "error parsing options, use --help" && exit 1
local search_dir=${PROJECTS_DIR} # the root from where we start our search
local project_dir=${PROJECT%.git} # the dir found with .git striped
function proj_name() {
local name=${project_dir#${search_dir}}
# restore name if search_dir is the same as the project_dir
[[ ${name} == "/" ]] && name=${search_dir}
name=${name#/} # remove leading '/'
name=${name%/} # remove trailing '/'
echo $(basename "${name}")
}
function dirty_git_status() {
local status=$(git status -s)
if [[ ${status} != "" ]]; then
in_yellow "LOCAL CHANGES NOT COMMITED"
while read line; do
[[ -n ${LIST_LOCAL_CHANGES} ]] && echo -e "\t${line}"
done <<<"${status}"
fi
}
function git_changes_not_pushed() {
local status=$(git status -sb)
local main_branch=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)
local re='ahead ([0-9]+)'
if [[ ${status} =~ ${re} ]]; then
in_red "COMMITS AHEAD: ${BASH_REMATCH[1]}"
[[ -n ${LIST_COMMITS_AHEAD} ]] && \
git log --format=oneline --abbrev-commit origin/${main_branch}..${main_branch} \
| while read line; do echo -e "\t${line}"; done
fi
re='behind ([0-9]+)'
if [[ ${status} =~ ${re} ]]; then
in_cyan "COMMITS BEHIND: ${BASH_REMATCH[1]}"
[[ -n ${LIST_COMMITS_BEHIND} ]] && \
git log --format=oneline --abbrev-commit ${main_branch}..origin/${main_branch} \
| while read line; do echo -e "\t${line}"; done
fi
}
function git_remote_defined() {
if [[ $(git remote) == "" ]]; then
in_red "NO REMOTES DEFINED"
fi
}
function git_merge_ff_only() {
in_cyan "RUNNING: git merge --ff-only"
git merge --ff-only
}
# main execution
local project_name=$(proj_name)
in_green ">>> ${project_name} <<<"
(cd "${project_dir}"
[[ -n ${PERFORM_GIT_FETCH} ]] && git fetch -q
[[ -n ${CHANGES_NOT_PUSHED} ]] && git_changes_not_pushed
[[ -n ${FAST_FORWARD_CHANGES} ]] && git_merge_ff_only
[[ -n ${LOCAL_CHANGES} ]] && dirty_git_status
[[ -n ${REMOTE_NOT_DEFINED} ]] && git_remote_defined
)
}
export -f check_project # make this available for find
find -L "${PROJECTS_DIR}" -type d -name '.git' -exec bash -c "check_project $* -d '${PROJECTS_DIR}' -p '{}' " \;