yet another tweak to fix pipes
[feisty_meow.git] / scripts / rev_control / version_control.sh
1 #!/bin/bash
2
3 # these are helper functions for doing localized revision control.
4 # this script should be sourced into other scripts that use it.
5
6 source "$FEISTY_MEOW_SCRIPTS/core/launch_feisty_meow.sh"
7 source "$FEISTY_MEOW_SCRIPTS/tty/terminal_titler.sh"
8
9 #hmmm: we need to dump all the outputs in this script into splitter
10
11 ##############
12
13 # the maximum depth that the recursive functions will try to go below the starting directory.
14 export MAX_DEPTH=5
15
16 # use our splitter tool for lengthy output if it's available.
17 if [ ! -z "$(which splitter)" ]; then
18   TO_SPLITTER="$(which splitter)"
19 else
20   TO_SPLITTER=cat
21 fi
22
23 ##############
24
25 # one unpleasantry to take care of first; cygwin barfs aggressively if the TMP directory
26 # is a DOS path, but we need it to be a DOS path for our GFFS testing, so that blows.
27 # to get past this, TMP gets changed below to a hopefully generic and safe place.
28 if [[ "$TMP" =~ .:.* ]]; then
29   echo "making weirdo temporary directory for PCDOS-style path."
30   export TMP=/tmp/rev_control_$USER
31 fi
32 if [ ! -d "$TMP" ]; then
33   mkdir -p $TMP
34 fi
35 if [ ! -d "$TMP" ]; then
36   echo "could not create the temporary directory TMP in: $TMP"
37   echo "this script will not work properly without an existing TMP directory."
38 fi
39 #hmmm: re-address the above code, since it doesn't make a lot of sense to me right now...
40
41
42 ##############
43
44 # checks the directory provided into the revision control system repository it belongs to.
45 function do_checkin()
46 {
47   local directory="$1"; shift
48
49   save_terminal_title
50
51   # make a nice echoer since we want to use it inside conditions below.
52   local nicedir="$directory"
53   if [ $nicedir == "." ]; then
54     nicedir=$(\pwd)
55   fi
56   local blatt="echo checking in '$nicedir'..."
57
58   local retval=0  # normally successful.
59
60   do_update "$directory"
61   retval=$?
62   test_or_die "repository update failed; this should be fixed before check-in."
63
64   pushd "$directory" &>/dev/null
65   if [ -f ".no-checkin" ]; then
66     echo "skipping check-in due to presence of .no-checkin sentinel file."
67   elif [ -d "CVS" ]; then
68     if test_writeable "CVS"; then
69       $blatt
70       cvs ci .
71       retval=$?
72     fi
73   elif [ -d ".svn" ]; then
74     if test_writeable ".svn"; then
75       $blatt
76       svn ci .
77       retval=$?
78     fi
79   elif [ -d ".git" ]; then
80     if test_writeable ".git"; then
81       $blatt
82       # snag all new files.  not to everyone's liking.
83       git add --all .
84       retval=$?
85
86       # see if there are any changes in the local repository.
87       if ! git diff-index --quiet HEAD --; then
88         # tell git about all the files and get a check-in comment.
89         git commit .
90         retval+=$?
91       fi
92       # catch if the diff-index failed somehow.
93       retval+=$?
94
95       local myself="$(my_branch_name)"
96       local parent="$(parent_branch_name)"
97
98       # upload any changes to the upstream repo so others can see them.
99       if [ "$myself" != "$parent" ]; then
100         git push origin "$(myself)" 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
101         retval+=${PIPESTATUS[0]}
102       else
103         # this branch is the same as the parent, so just push.
104         git push 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
105         retval+=${PIPESTATUS[0]}
106       fi
107
108     fi
109   else
110     # nothing there.  it's not an error though.
111     echo no repository in $directory
112     retval=0
113   fi
114   popd &>/dev/null
115
116   restore_terminal_title
117
118   return $retval
119 }
120
121 # shows the local changes in a repository.
122 function do_diff
123 {
124   local directory="$1"; shift
125
126   save_terminal_title
127
128   pushd "$directory" &>/dev/null
129   local retval=0  # normally successful.
130
131   # only update if we see a repository living there.
132   if [ -d ".svn" ]; then
133     svn diff .
134     retval+=$?
135   elif [ -d ".git" ]; then
136     git diff 
137     retval+=$?
138   elif [ -d "CVS" ]; then
139     cvs diff .
140     retval+=$?
141   fi
142
143   popd &>/dev/null
144
145   restore_terminal_title
146
147   return $retval
148 }
149
150 # reports any files that are not already known to the upstream repository.
151 function do_report_new
152 {
153   local directory="$1"; shift
154
155   save_terminal_title
156
157   pushd "$directory" &>/dev/null
158   local retval=0  # normally successful.
159
160   # only update if we see a repository living there.
161   if [ -f ".no-checkin" ]; then
162     echo "skipping reporting due to presence of .no-checkin sentinel file."
163   elif [ -d ".svn" ]; then
164     # this action so far only makes sense and is needed for svn.
165     bash $FEISTY_MEOW_SCRIPTS/rev_control/svnapply.sh \? echo
166     retval=$?
167   elif [ -d ".git" ]; then
168     git status -u
169     retval=$?
170   fi
171
172   popd &>/dev/null
173
174   restore_terminal_title
175
176   return $retval
177 }
178
179 # checks in all the folders in a specified list.
180 function checkin_list()
181 {
182   # make the list of directories unique.
183   local list="$(uniquify $*)"
184
185   save_terminal_title
186
187   # turn repo list back into an array.
188   eval "repository_list=( ${REPOSITORY_LIST[*]} )"
189
190   local outer inner
191
192   for outer in "${repository_list[@]}"; do
193     # check the repository first, since it might be an absolute path.
194     if [[ $outer =~ /.* ]]; then
195       # yep, this path is absolute.  just handle it directly.
196       if [ ! -d "$outer" ]; then continue; fi
197       do_checkin $outer
198       test_or_die "running check-in on: $outer"
199       sep 28
200     else
201       for inner in $list; do
202         # add in the directory component to see if we can find the folder.
203         local path="$inner/$outer"
204         if [ ! -d "$path" ]; then continue; fi
205         do_checkin $path
206         test_or_die "running check-in on: $path"
207         sep 28
208       done
209     fi
210   done
211
212   restore_terminal_title
213 }
214
215 # takes out the first few carriage returns that are in the input.
216 function squash_first_few_crs()
217 {
218   i=0
219   while read input_text; do
220     i=$((i+1))
221     if [ $i -le 5 ]; then
222       echo -n "$input_text  "
223     else
224       echo $input_text
225     fi
226   done
227   if [ $i -le 3 ]; then
228     # if we're still squashing eols, make sure we don't leave them hanging.
229     echo
230   fi
231 }
232
233 # a helpful method that reports the git branch for the current directory's
234 # git repository.
235 function my_branch_name()
236 {
237   echo "$(git branch | grep \* | cut -d ' ' -f2)"
238 }
239
240 # this reports the upstream branch for the current repo.
241 function parent_branch_name()
242 {
243   echo "$(git branch -vv | grep \* | cut -d ' ' -f2)"
244 }
245
246 # gets the latest versions of the assets from the upstream repository.
247 function do_update()
248 {
249   directory="$1"; shift
250
251   save_terminal_title
252
253   # make a nice echoer since we want to use it inside conditions below.
254   local nicedir="$directory"
255   if [ $nicedir == "." ]; then
256     nicedir=$(\pwd)
257   fi
258   local blatt="echo retrieving '$nicedir'..."
259
260   local retval=0  # plan on success for now.
261   pushd "$directory" &>/dev/null
262   if [ -d "CVS" ]; then
263     if test_writeable "CVS"; then
264       $blatt
265       cvs update . | squash_first_few_crs
266       retval=${PIPESTATUS[0]}
267     fi
268   elif [ -d ".svn" ]; then
269     if test_writeable ".svn"; then
270       $blatt
271       svn update . | squash_first_few_crs
272       retval=${PIPESTATUS[0]}
273     fi
274   elif [ -d ".git" ]; then
275     if test_writeable ".git"; then
276       $blatt
277       retval=0
278
279       # from very helpful page:
280       # https://stackoverflow.com/questions/10312521/how-to-fetch-all-git-branches
281       git branch -r | grep -v '\->' |
282           while read remote; do
283             git branch --track "${remote#origin/}" "$remote"
284             # ensure we notice a failure when adding tracking.
285             retval+=$?
286           done
287       retval+=${PIPESTATUS[0]}$?
288
289       git fetch --all 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
290       retval+=${PIPESTATUS[0]}
291
292       git pull --all 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
293       retval+=${PIPESTATUS[0]}
294     fi
295   else
296     # this is not an error necessarily; we'll just pretend they planned this.
297     echo no repository in $directory
298   fi
299   popd &>/dev/null
300
301   restore_terminal_title
302
303   return $retval
304 }
305
306 # gets all the updates for a list of folders under revision control.
307 function checkout_list()
308 {
309   local list="$(uniquify $*)"
310
311   save_terminal_title
312
313   # turn repo list back into an array.
314   eval "repository_list=( ${REPOSITORY_LIST[*]} )"
315
316   local outer inner
317
318   for outer in "${repository_list[@]}"; do
319     # check the repository first, since it might be an absolute path.
320     if [[ $outer =~ /.* ]]; then
321       # yep, this path is absolute.  just handle it directly.
322       if [ ! -d "$outer" ]; then continue; fi
323       do_update $outer
324       test_or_die "running update on: $path"
325       sep 28
326     else
327       for inner in $list; do
328         # add in the directory component to see if we can find the folder.
329         local path="$inner/$outer"
330         if [ ! -d "$path" ]; then continue; fi
331         do_update $path
332         test_or_die "running update on: $path"
333         sep 28
334       done
335     fi
336   done
337
338   restore_terminal_title
339 }
340
341 # provides a list of absolute paths of revision control directories
342 # that are located under the directory passed as the first parameter.
343 function generate_rev_ctrl_filelist()
344 {
345   local dir="$1"; shift
346   pushd "$dir" &>/dev/null
347   local dirhere="$( \cd "$(\dirname "$dir")" && /bin/pwd )"
348   local tempfile=$(mktemp /tmp/zz_checkins.XXXXXX)
349   echo >$tempfile
350   local additional_filter
351   find $dirhere -follow -maxdepth $MAX_DEPTH -type d -iname ".svn" -exec echo {}/.. ';' >>$tempfile 2>/dev/null
352   find $dirhere -follow -maxdepth $MAX_DEPTH -type d -iname ".git" -exec echo {}/.. ';' >>$tempfile 2>/dev/null
353   # CVS is not well behaved like git and (now) svn, and we seldom use it anymore.
354   popd &>/dev/null
355
356   # see if they've warned us not to try checking in within vendor hierarchies.
357   if [ ! -z "NO_CHECKIN_VENDOR" ]; then
358     sed -i -e '/.*\/vendor\/.*/d' "$tempfile"
359   fi
360
361   local sortfile=$(mktemp /tmp/zz_checkin_sort.XXXXXX)
362   sort <"$tempfile" >"$sortfile"
363   \rm "$tempfile"
364   echo "$sortfile"
365 }
366
367 # iterates across a list of directories contained in a file (first parameter).
368 # on each directory name, it performs the action (second parameter) provided.
369 function perform_revctrl_action_on_file()
370 {
371   local tempfile="$1"; shift
372   local action="$1"; shift
373
374   save_terminal_title
375
376   while read -u 3 dirname; do
377     if [ -z "$dirname" ]; then continue; fi
378     pushd "$dirname" &>/dev/null
379     echo "[$(pwd)]"
380     $action .
381     test_or_die "performing action $action on: $(pwd)"
382     sep 28
383     popd &>/dev/null
384   done 3<"$tempfile"
385
386   restore_terminal_title
387
388   rm $tempfile
389 }
390
391