a3f91e6335d844e1cf4acc44a97a6999d213b573
[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   do_update "$directory"
59   test_or_die "repository update--this should be fixed before check-in."
60
61   pushd "$directory" &>/dev/null
62   if [ -f ".no-checkin" ]; then
63     echo "skipping check-in due to presence of .no-checkin sentinel file."
64   elif [ -d "CVS" ]; then
65     if test_writeable "CVS"; then
66       $blatt
67       cvs ci .
68       test_or_die "cvs checkin"
69     fi
70   elif [ -d ".svn" ]; then
71     if test_writeable ".svn"; then
72       $blatt
73       svn ci .
74       test_or_die "svn checkin"
75     fi
76   elif [ -d ".git" ]; then
77     if test_writeable ".git"; then
78       $blatt
79       # snag all new files.  not to everyone's liking.
80       git add --all .
81       test_or_die "git add all new files"
82
83       # see if there are any changes in the local repository.
84       if ! git diff-index --quiet HEAD --; then
85         # tell git about all the files and get a check-in comment.
86         git commit .
87         test_or_die "git commit"
88       fi
89       # catch if the diff-index failed somehow.
90       test_or_die "git diff-index"
91
92       local myself="$(my_branch_name)"
93       local parent="$(parent_branch_name)"
94
95       # upload any changes to the upstream repo so others can see them.
96       if [ "$myself" != "$parent" ]; then
97         git push origin "$(myself)" 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
98         test_or_die "git push to origin: $myself"
99       else
100         # this branch is the same as the parent, so just push.
101         git push 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
102         test_or_die "normal git push"
103       fi
104
105     fi
106   else
107     # nothing there.  it's not an error though.
108     echo no repository in $directory
109   fi
110   popd &>/dev/null
111
112   restore_terminal_title
113
114   true;
115 }
116
117 # shows the local changes in a repository.
118 function do_diff
119 {
120   local directory="$1"; shift
121
122   save_terminal_title
123
124   pushd "$directory" &>/dev/null
125
126   # only update if we see a repository living there.
127   if [ -d ".svn" ]; then
128     svn diff .
129     test_or_die "subversion diff"
130   elif [ -d ".git" ]; then
131     git diff 
132     test_or_die "git diff"
133   elif [ -d "CVS" ]; then
134     cvs diff .
135     test_or_die "cvs diff"
136   fi
137
138   popd &>/dev/null
139
140   restore_terminal_title
141
142   true;
143 }
144
145 # reports any files that are not already known to the upstream repository.
146 function do_report_new
147 {
148   local directory="$1"; shift
149
150   save_terminal_title
151
152   pushd "$directory" &>/dev/null
153
154   # only update if we see a repository living there.
155   if [ -f ".no-checkin" ]; then
156     echo "skipping reporting due to presence of .no-checkin sentinel file."
157   elif [ -d ".svn" ]; then
158     # this action so far only makes sense and is needed for svn.
159     bash $FEISTY_MEOW_SCRIPTS/rev_control/svnapply.sh \? echo
160     test_or_die "svn diff"
161   elif [ -d ".git" ]; then
162     git status -u
163     test_or_die "git status -u"
164   fi
165
166   popd &>/dev/null
167
168   restore_terminal_title
169
170   true
171 }
172
173 # checks in all the folders in a specified list.
174 function checkin_list()
175 {
176   # make the list of directories unique.
177   local list="$(uniquify $*)"
178
179   save_terminal_title
180
181   # turn repo list back into an array.
182   eval "repository_list=( ${REPOSITORY_LIST[*]} )"
183
184   local outer inner
185
186   for outer in "${repository_list[@]}"; do
187     # check the repository first, since it might be an absolute path.
188     if [[ $outer =~ /.* ]]; then
189       # yep, this path is absolute.  just handle it directly.
190       if [ ! -d "$outer" ]; then continue; fi
191       do_checkin $outer
192       test_or_die "running check-in (absolute) on path: $outer"
193       sep 28
194     else
195       for inner in $list; do
196         # add in the directory component to see if we can find the folder.
197         local path="$inner/$outer"
198         if [ ! -d "$path" ]; then continue; fi
199         do_checkin $path
200         test_or_die "running check-in (relative) on path: $path"
201         sep 28
202       done
203     fi
204   done
205
206   restore_terminal_title
207 }
208
209 # takes out the first few carriage returns that are in the input.
210 function squash_first_few_crs()
211 {
212   i=0
213   while read input_text; do
214     i=$((i+1))
215     if [ $i -le 5 ]; then
216       echo -n "$input_text  "
217     else
218       echo $input_text
219     fi
220   done
221   if [ $i -le 3 ]; then
222     # if we're still squashing eols, make sure we don't leave them hanging.
223     echo
224   fi
225 }
226
227 # a helpful method that reports the git branch for the current directory's
228 # git repository.
229 function my_branch_name()
230 {
231   echo "$(git branch | grep \* | cut -d ' ' -f2)"
232 }
233
234 # this reports the upstream branch for the current repo.
235 function parent_branch_name()
236 {
237   echo "$(git branch -vv | grep \* | cut -d ' ' -f2)"
238 }
239
240 # gets the latest versions of the assets from the upstream repository.
241 function do_update()
242 {
243   directory="$1"; shift
244
245   save_terminal_title
246
247   # make a nice echoer since we want to use it inside conditions below.
248   local nicedir="$directory"
249   if [ $nicedir == "." ]; then
250     nicedir=$(\pwd)
251   fi
252   local blatt="echo retrieving '$nicedir'..."
253
254   pushd "$directory" &>/dev/null
255   if [ -d "CVS" ]; then
256     if test_writeable "CVS"; then
257       $blatt
258       cvs update . | $TO_SPLITTER
259       test_or_die "cvs update"
260     fi
261   elif [ -d ".svn" ]; then
262     if test_writeable ".svn"; then
263       $blatt
264       svn update . | $TO_SPLITTER
265       test_or_die "svn update"
266     fi
267   elif [ -d ".git" ]; then
268     if test_writeable ".git"; then
269       $blatt
270
271       # from very helpful page:
272       # https://stackoverflow.com/questions/10312521/how-to-fetch-all-git-branches
273
274       for remote in $( git branch -r | grep -v -- '->' ); do
275         git branch --track ${remote#origin/} $remote 2>/dev/null
276 #hmmm: ignoring errors from these, since they are continual.
277 #hmmm: if we could find a way to not try to track with a local branch when there's already one present, that would be swell.  it's probably simple.
278       done
279
280       git fetch --all 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
281       test_or_die "git fetch"
282
283       git pull --all 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
284       test_or_die "git pull"
285     fi
286   else
287     # this is not an error necessarily; we'll just pretend they planned this.
288     echo no repository in $directory
289   fi
290   popd &>/dev/null
291
292   restore_terminal_title
293
294   true
295 }
296
297 # gets all the updates for a list of folders under revision control.
298 function checkout_list()
299 {
300   local list="$(uniquify $*)"
301
302   save_terminal_title
303
304   # turn repo list back into an array.
305   eval "repository_list=( ${REPOSITORY_LIST[*]} )"
306
307   local outer inner
308
309   for outer in "${repository_list[@]}"; do
310     # check the repository first, since it might be an absolute path.
311     if [[ $outer =~ /.* ]]; then
312       # yep, this path is absolute.  just handle it directly.
313       if [ ! -d "$outer" ]; then continue; fi
314       do_update $outer
315       test_or_die "running update on: $path"
316       sep 28
317     else
318       for inner in $list; do
319         # add in the directory component to see if we can find the folder.
320         local path="$inner/$outer"
321         if [ ! -d "$path" ]; then continue; fi
322         do_update $path
323         test_or_die "running update on: $path"
324         sep 28
325       done
326     fi
327   done
328
329   restore_terminal_title
330 }
331
332 # provides a list of absolute paths of revision control directories
333 # that are located under the directory passed as the first parameter.
334 function generate_rev_ctrl_filelist()
335 {
336   local dir="$1"; shift
337   pushd "$dir" &>/dev/null
338   local dirhere="$( \cd "$(\dirname "$dir")" && /bin/pwd )"
339   local tempfile=$(mktemp /tmp/zz_checkins.XXXXXX)
340   echo >$tempfile
341   local additional_filter
342   find $dirhere -follow -maxdepth $MAX_DEPTH -type d -iname ".svn" -exec echo {}/.. ';' >>$tempfile 2>/dev/null
343   find $dirhere -follow -maxdepth $MAX_DEPTH -type d -iname ".git" -exec echo {}/.. ';' >>$tempfile 2>/dev/null
344   # CVS is not well behaved like git and (now) svn, and we seldom use it anymore.
345   popd &>/dev/null
346
347   # see if they've warned us not to try checking in within vendor hierarchies.
348   if [ ! -z "NO_CHECKIN_VENDOR" ]; then
349     sed -i -e '/.*\/vendor\/.*/d' "$tempfile"
350   fi
351
352   local sortfile=$(mktemp /tmp/zz_checkin_sort.XXXXXX)
353   sort <"$tempfile" >"$sortfile"
354   \rm "$tempfile"
355   echo "$sortfile"
356 }
357
358 # iterates across a list of directories contained in a file (first parameter).
359 # on each directory name, it performs the action (second parameter) provided.
360 function perform_revctrl_action_on_file()
361 {
362   local tempfile="$1"; shift
363   local action="$1"; shift
364
365   save_terminal_title
366
367   while read -u 3 dirname; do
368     if [ -z "$dirname" ]; then continue; fi
369     pushd "$dirname" &>/dev/null
370     echo "[$(pwd)]"
371     $action .
372     test_or_die "performing action $action on: $(pwd)"
373     sep 28
374     popd &>/dev/null
375   done 3<"$tempfile"
376
377   restore_terminal_title
378
379   rm $tempfile
380 }
381