1- #! /bin/sh
1+ #! /bin/bash
22#
33# Execute Git commands on multiple directories and subdirectories
4- # > git iterate [-q|--quiet] DIR1 DIR2 ... -- GIT_COMMAND ...
5- # > git iterate [-q|--quiet] DIR1 DIR2 ... foreach SHELL_COMMAND ...
4+ # > git iterate [-v|-V] DIR1 DIR2 ... -- GIT_COMMAND ...
65#
76# Parameters:
8- # -q: Remove all non- error output
9- # --quiet: Remove announcement of the directories in which there are operations
7+ # -v: Tell in standard error output in which directory the Git command is operating
8+ # -V: Tell in standard output in which directory the Git command is operating
109# DIR*: list of super-directories, Git commands will be executed on each valid sub-directory
1110# GIT_COMMAND and next parameters: Git commands to be executed on each Git directory
12- #
13- # License: WTFPL 2.0 - Seb35
1411
15- # Recursively search all Git directories
16- search_git_folders () {
17- git --git-dir=" $1 " branch > /dev/null 2>&1
18- if [ $? = 0 ]
12+ IFS=$' \n '
13+ export GIT_PROGRESS_DELAY=-1
14+
15+ function search_git_folders() {
16+ if [ ! -d " $1 " ]
17+ then
18+ return
19+ elif [ ! -f " $1 /.git" -a ! -d " $1 /.git" -a ! -d " $1 /refs" ]
1920 then
20- echo " $1 "
21+ for folder in $( find " $1 " -mindepth 1 -maxdepth 1 -type d)
22+ do
23+ search_git_folders " $folder "
24+ done
2125 else
22- git --git-dir=" $1 /.git " branch > /dev/null 2>&1
26+ git --git-dir=" $1 " branch > /dev/null 2>&1
2327 if [ $? = 0 ]
2428 then
2529 echo " $1 "
2630 else
27- for folder in ` find " $1 " -mindepth 1 -maxdepth 1 -type d| sort`
28- do
29- search_git_folders " $folder "
30- done
31+ git --git-dir=" $1 /.git" branch > /dev/null 2>&1
32+ if [ $? = 0 ]
33+ then
34+ echo " $1 "
35+ else
36+ for folder in $( find " $1 " -mindepth 1 -maxdepth 1 -type d)
37+ do
38+ search_git_folders " $folder "
39+ done
40+ fi
3141 fi
3242 fi
3343}
3444
35- # Catch "verbose" option
36- quiet=" false"
37- if [ " $1 " = " -q" ]
38- then
39- quiet=" true"
40- shift
41- elif [ " $1 " = " --quiet" ]
42- then
43- quiet=" moderate"
44- shift
45- fi
46-
4745# Get folders
4846folders=" "
4947foreach=" false"
48+ options=" "
49+ quiet=" false"
50+ display_only=" false"
51+ clean=" "
52+ behind=" "
53+ ahead=" "
54+ detached=" "
5055for folder in " $@ "
5156do
57+ if [ -z " $options " ]; then
58+ if [ " $folder " = " -q" ]; then
59+ shift
60+ quiet=" moderate"
61+ continue
62+ elif [ " $folder " = " -qq" -o " $folder " = " --quiet" ]; then
63+ shift
64+ quiet=" true"
65+ continue
66+ elif [ " $folder " = " --to-be-cleaned" ]; then
67+ shift
68+ clean=" -3"
69+ continue
70+ elif [ " $folder " = " --very-unclean" ]; then
71+ shift
72+ clean=" -2"
73+ continue
74+ elif [ " $folder " = " --unclean" ]; then
75+ shift
76+ clean=" -1"
77+ continue
78+ elif [ " $folder " = " --almost-clean" ]; then
79+ shift
80+ clean=" 1"
81+ continue
82+ elif [ " $folder " = " --quite-clean" ]; then
83+ shift
84+ clean=" 2"
85+ continue
86+ elif [ " $folder " = " --clean" ]; then
87+ shift
88+ clean=" 3"
89+ continue
90+ elif [ " $folder " = " --very-clean" ]; then
91+ shift
92+ clean=" 4"
93+ continue
94+ elif [ " $folder " = " --very-very-clean" ]; then
95+ shift
96+ clean=" 5"
97+ continue
98+ elif [ " $folder " = " --late" -o " $folder " = " --behind" ]; then
99+ shift
100+ behind=" 1"
101+ continue
102+ elif [ " $folder " = " --not-late" -o " $folder " = " --not-behind" ]; then
103+ shift
104+ behind=" -1"
105+ continue
106+ elif [ " $folder " = " --ahead" ]; then
107+ shift
108+ ahead=" 1"
109+ continue
110+ elif [ " $folder " = " --not-ahead" ]; then
111+ shift
112+ ahead=" -1"
113+ continue
114+ elif [ " $folder " = " --late-or-ahead" -o " $folder " = " --behind-or-ahead" ]; then
115+ shift
116+ behind=" 2"
117+ ahead=" 2"
118+ continue
119+ elif [ " $folder " = " --detached" ]; then
120+ shift
121+ detached=" true"
122+ continue
123+ elif [ " $folder " = " --not-detached" ]; then
124+ shift
125+ detached=" false"
126+ continue
127+ fi
128+ fi
129+ options=" 1"
52130 shift
53131 if [ " $folder " = " --" ]
54132 then
58136 foreach=" true"
59137 break
60138 fi
61- folders=" $folders
62- $folder "
139+ folders=" $folders " $' \n ' " $folder "
63140done
64141
65142# If no folder is given, use local directory (recursively)
@@ -68,21 +145,162 @@ then
68145 folders=*
69146fi
70147
148+ # echo quiet="$quiet"
149+ # echo foreach="$foreach"
150+ # echo clean="$clean"
151+ # echo folders="$folders"
152+ # echo $behind
153+ # echo $ahead
154+
155+ if [ " $# " = 0 ]; then
156+ display_only=" true"
157+ fi
158+
71159# Iterate over each folder
72160first=" true"
73161for folder in $folders
74162do
75- folders=` search_git_folders " $folder " `
163+ folders=$( search_git_folders " $folder " )
76164 for git_folder in $folders
77165 do
78166 cd " $git_folder "
167+ # First check if the Git repo is eligible regarding its status and remote-tracking status
168+ if [ " $clean " = " -3" ]; then
169+ status1=$( git status --short)
170+ status2=$( git stash list)
171+ status3=$( LANG=" en" git branch --list -vv)
172+ status4=$( echo " $status3 " | grep " ^*" | grep -F -e " behind " -e " ahead " )
173+ status5=$( echo " $status3 " | grep " ^* (HEAD detached at " )
174+ # Display if ( $status1 !== "" || $status2 !== "" || $status3 === "" || $status4 !== "" || $status5 !== "" )
175+ if [ -z " $status1 " -a -z " $status2 " -a -n " $status3 " -a -z " $status4 " -a -z " $status5 " ]; then
176+ cd " $OLDPWD "
177+ continue
178+ fi
179+ elif [ " $clean " = " -2" ]; then
180+ status=$( git status --short| grep -v " ^??" )
181+ if [ -z " $status " ]; then
182+ cd " $OLDPWD "
183+ continue
184+ fi
185+ elif [ " $clean " = " -1" ]; then
186+ status=$( git status --short)
187+ if [ -z " $status " ]; then
188+ cd " $OLDPWD "
189+ continue
190+ fi
191+ elif [ " $clean " = " 1" ]; then
192+ status1=$( git status --short)
193+ status2=$( echo " $status1 " | grep -v " ^??" )
194+ status3=$( git stash list)
195+ # Display if ( ( status1 !== "" && status2 === "" ) || ( status2 === "" && status3 !== "" ) )
196+ # Display if status2 === "" && ( status1 !== "" || status3 !== "" )
197+ # Do not display if ( ( status1 === "" || status2 !== "" ) && status3 === "" )
198+ # Do not display if status2 !== "" || ( status1 === "" && status3 === "" )
199+ # if [ \( -z "$status1" -o -n "$status2" \) -a -z "$status3" ]; then
200+ if [ -n " $status2 " -o \( -z " $status1 " -a -z " $status3 " \) ]; then
201+ cd " $OLDPWD "
202+ continue
203+ fi
204+ elif [ " $clean " = " 2" ]; then
205+ status=$( git status --short| grep -v " ^??" )
206+ if [ -n " $status " ]; then
207+ cd " $OLDPWD "
208+ continue
209+ fi
210+ elif [ " $clean " = " 3" ]; then
211+ status=$( git status --short)
212+ if [ -n " $status " ]; then
213+ cd " $OLDPWD "
214+ continue
215+ fi
216+ elif [ " $clean " = " 4" ]; then
217+ status=$( git status --ignored --short)
218+ if [ -n " $status " ]; then
219+ cd " $OLDPWD "
220+ continue
221+ fi
222+ elif [ " $clean " = " 5" ]; then
223+ status1=$( git status --ignored --short)
224+ status2=$( git stash list)
225+ if [ -n " $status1 " -o -n " $status2 " ]; then
226+ cd " $OLDPWD "
227+ continue
228+ fi
229+ fi
230+ behind_found=" 0"
231+ if [ " $behind " = " 1" -o " $behind " = " 2" ]; then
232+ # TODO could be improved: there are false positive if the commit message contains " behind "
233+ status1=$( LANG=" en" git branch --list -vv)
234+ status2=$( echo " $status1 " | grep " ^*" | grep -F " behind " )
235+ # Display if ( status1 !== "" && status2 !== "" )
236+ if [ -z " $status1 " -o -z " $status2 " ]; then
237+ if [ " $behind " != " 2" -o " $ahead " != " 2" ]; then
238+ cd " $OLDPWD "
239+ continue
240+ fi
241+ else
242+ behind_found=" 1"
243+ fi
244+ elif [ " $behind " = " -1" ]; then
245+ # TODO could be improved: there are false positive if the commit message contains " behind "
246+ status1=$( LANG=" en" git branch --list -vv)
247+ status2=$( echo " $status1 " | grep " ^*" | grep -F " behind " )
248+ # Display if ( status1 !== "" && status2 === "" )
249+ if [ -z " $status1 " -o -n " $status2 " ]; then
250+ cd " $OLDPWD "
251+ continue
252+ fi
253+ fi
254+ if [ " $ahead " = " 1" -o " $ahead " = " 2" ]; then
255+ # TODO could be improved: there are false positive if the commit message contains " ahead "
256+ status1=$( LANG=" en" git branch --list -vv)
257+ status2=$( echo " $status1 " | grep " ^*" | grep -F " ahead " )
258+ # Display if ( status1 !== "" && status2 !== "" )
259+ if [ -z " $status1 " -o -z " $status2 " ]; then
260+ if [ " $behind " != " 2" -o " $ahead " != " 2" -o " $behind_found " != " 1" ]; then
261+ cd " $OLDPWD "
262+ continue
263+ fi
264+ fi
265+ elif [ " $ahead " = " -1" ]; then
266+ # TODO could be improved: there are false positive if the commit message contains " ahead "
267+ status1=$( LANG=" en" git branch --list -vv)
268+ status2=$( echo " $status1 " | grep " ^*" | grep -F " ahead " )
269+ # Display if ( status1 !== "" && status2 === "" )
270+ if [ -z " $status1 " -o -n " $status2 " ]; then
271+ cd " $OLDPWD "
272+ continue
273+ fi
274+ fi
275+ if [ " $detached " = " true" ]; then
276+ status1=$( LANG=" en" git branch --list -vv)
277+ status2=$( echo " $status1 " | grep " ^* (HEAD detached at " )
278+ # Display if ( status1 !== "" && status2 !== "" )
279+ if [ -z " $status1 " -o -z " $status2 " ]; then
280+ cd " $OLDPWD "
281+ continue
282+ fi
283+ elif [ " $detached " = " false" ]; then
284+ status1=$( LANG=" en" git branch --list -vv)
285+ status2=$( echo " $status1 " | grep " ^* (HEAD detached at " )
286+ # Display if ( status1 !== "" && status2 === "" )
287+ if [ -z " $status1 " -o -n " $status2 " ]; then
288+ cd " $OLDPWD "
289+ continue
290+ fi
291+ fi
292+ if [ " $display_only " = " true" ]; then
293+ echo " $folder "
294+ cd " $OLDPWD "
295+ continue
296+ fi
79297 if [ " $quiet " = " true" ]
80298 then
81299 if [ " $foreach " = " false" ]
82300 then
83- errors=` git $@ 2>&1 `
301+ errors=$( git $@ 2>&1 )
84302 else
85- errors=` $ @ 2>&1 `
303+ errors=$( $ @ 2>&1 )
86304 fi
87305 if [ " $? " != 0 ]
88306 then
91309 echo >&2
92310 fi
93311 first=" false"
94- echo " \033[1m $git_folder \033[0m " >&2
312+ echo $git_folder >&2
95313 echo " $errors " >&2
96314 echo " Error $? in $git_folder " >&2
97315 fi
98316 else
99317 if [ " $quiet " != " moderate" ]
100318 then
101- echo " \033[1m$git_folder \033[0m"
319+ echo -e " \033[1m$git_folder \033[0m"
102320 fi
103321 if [ " $foreach " = " false" ]
104322 then
0 commit comments