really close for simple powerups
[feisty_meow.git] / scripts / rev_control / version_control.sh
index 3e8ef4c430a36a706bf00437fb730e848a5d8d27..282dd4d922ff3bd9aa2708bbd34cca9760b679aa 100644 (file)
@@ -15,8 +15,11 @@ source "$FEISTY_MEOW_SCRIPTS/tty/terminal_titler.sh"
 export MAX_DEPTH=5
 
 # use our splitter tool for lengthy output if it's available.
-if [ ! -z "$(which splitter)" ]; then
+if [ ! -z "$(which splitter 2>/dev/null)" ]; then
   TO_SPLITTER="$(which splitter)"
+  # calculate the number of columsn in the terminal.
+  cols=$(get_maxcols)
+  TO_SPLITTER+=" --maxcol $(($cols - 1))"
 else
   TO_SPLITTER=cat
 fi
@@ -27,7 +30,7 @@ fi
 # is a DOS path, but we need it to be a DOS path for our GFFS testing, so that blows.
 # to get past this, TMP gets changed below to a hopefully generic and safe place.
 if [[ "$TMP" =~ .:.* ]]; then
-  echo "making weirdo temporary directory for PCDOS-style path."
+  log_feisty_meow_event "making weirdo temporary directory for PCDOS-style path."
   export TMP=/tmp/rev_control_$USER
 fi
 if [ ! -d "$TMP" ]; then
@@ -36,143 +39,165 @@ fi
 if [ ! -d "$TMP" ]; then
   echo "could not create the temporary directory TMP in: $TMP"
   echo "this script will not work properly without an existing TMP directory."
+  echo
+#hmmm: odd approach to solving the "sourced scripts shouldn't exit or they take down the
+#      original caller too" issue.
+  echo "hit ctrl-c to stay in this shell now, otherwise it may exit in 10 seconds..."
+  sleep 10
+  exit 1
 fi
 
 ##############
 
 # checks the directory provided into the revision control system repository it belongs to.
-function do_checkin()
+function do_revctrl_checkin()
 {
   local directory="$1"; shift
 
-  save_terminal_title
-
+#hmmm: another piece of reusable code, to process the directory for printing.
   # make a nice echoer since we want to use it inside conditions below.
   local nicedir="$directory"
   if [ $nicedir == "." ]; then
     nicedir=$(\pwd)
   fi
-  local blatt="echo checking in '$nicedir'..."
-
-  do_update "$directory"
-  test_or_die "repository update--this should be fixed before check-in."
+  local blatt_report="echo -ne \nchecking in '$nicedir'...  "
+  local tell_no_checkin="echo -ne \nskipping check-in due to presence of .no-checkin sentinel file: $nicedir"
 
   pushd "$directory" &>/dev/null
-  if [ -f ".no-checkin" ]; then
-    echo "skipping check-in due to presence of .no-checkin sentinel file."
-  elif [ -d "CVS" ]; then
+#hmmm: overly elaborate sections below here, but we do want precise handling for git case.
+  if [ -d "CVS" ]; then
     if test_writeable "CVS"; then
-      $blatt
-      cvs ci .
-      test_or_die "cvs checkin"
+      do_revctrl_simple_update "$directory"
+      exit_on_error "updating repository; this issue should be fixed before check-in."
+      if [ -f ".no-checkin" ]; then
+#        echo -ne "\nskipping check-in due to presence of .no-checkin sentinel file: $directory"
+        $tell_no_checkin
+      else
+        $blatt_report
+        cvs ci .
+        exit_on_error "cvs checkin"
+      fi
     fi
   elif [ -d ".svn" ]; then
     if test_writeable ".svn"; then
-      $blatt
-      svn ci .
-      test_or_die "svn checkin"
+      do_revctrl_simple_update "$directory"
+      exit_on_error "updating repository; this issue should be fixed before check-in."
+      if [ -f ".no-checkin" ]; then
+#        echo -ne "\nskipping check-in due to presence of .no-checkin sentinel file: $directory"
+        $tell_no_checkin
+      else
+        $blatt_report
+        svn ci .
+        exit_on_error "svn checkin"
+      fi
     fi
   elif [ -d ".git" ]; then
     if test_writeable ".git"; then
-      $blatt
-
-      # put all changed and new files in the commit.  not to everyone's liking.
-      git add --all .
-      test_or_die "git add all new files"
 
-      # see if there are any changes in the local repository.
-      if ! git diff-index --quiet HEAD --; then
-        # tell git about all the files and get a check-in comment.
-        git commit .
-        test_or_die "git commit"
+      # take steps to make sure the branch integrity is good and we're up to date against remote repos.
+      do_revctrl_careful_update "$(\pwd)"
+
+      if [ -f ".no-checkin" ]; then
+#        echo -ne "\nskipping check-in due to presence of .no-checkin sentinel file: $directory"
+        $tell_no_checkin
+      else
+        $blatt_report
+
+        # put all changed and new files in the commit.  not to everyone's liking.
+        git add --all . | $TO_SPLITTER
+        promote_pipe_return 0
+        exit_on_error "git add all new files"
+
+        # see if there are any changes in the local repository.
+        if ! git diff-index --quiet HEAD --; then
+          # tell git about all the files and get a check-in comment.
+#hmmm: begins to look like, you guessed it, a reusable bit that all commit actions could enjoy.
+          git commit .
+          retval=$?
+          continue_on_error "git commit"
+          if [ $retval -ne 0 ]; then
+            echo -e -n "Commit failed or was aborted:\nShould we continue with other check-ins? [y/N] "
+            local line
+            read line
+            if [[ "${line:0:1}" != "y" ]]; then
+              echo "Stopping check-in process due to missing commit and user request."
+              exit 1
+            fi
+          fi
+        fi
+
+        # we continue on to the push, even if there were no obvious changes this run, because
+        # there could already be committed changes that haven't been pushed yet.
+
+        # upload any changes to the upstream repo so others can see them.
+        git push --tags origin "$(my_branch_name)" 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
+        promote_pipe_return 0
+        exit_on_error "git push"
       fi
-
-      # a new set of steps we have to take to make sure the branch integrity is good.
-      do_careful_git_update "$(\pwd)"
-
-      # we continue on to the push, even if there were no changes this time, because
-      # there could already be committed changes that haven't been pushed yet.
-
-      # upload any changes to the upstream repo so others can see them.
-      git push origin "$(my_branch_name)" 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
-      test_or_die "git push"
-
     fi
   else
     # nothing there.  it's not an error though.
-    echo no repository in $directory
+    log_feisty_meow_event "no repository in $directory"
   fi
   popd &>/dev/null
 
-  restore_terminal_title
-
   return 0
 }
 
 # shows the local changes in a repository.
-function do_diff
+function do_revctrl_diff
 {
   local directory="$1"; shift
 
-  save_terminal_title
-
   pushd "$directory" &>/dev/null
 
   # only update if we see a repository living there.
   if [ -d ".svn" ]; then
     svn diff .
-    test_or_die "subversion diff"
+    exit_on_error "subversion diff"
   elif [ -d ".git" ]; then
     git diff 
-    test_or_die "git diff"
+    exit_on_error "git diff"
   elif [ -d "CVS" ]; then
     cvs diff .
-    test_or_die "cvs diff"
+    exit_on_error "cvs diff"
   fi
 
   popd &>/dev/null
 
-  restore_terminal_title
-
   return 0
 }
 
 # reports any files that are not already known to the upstream repository.
-function do_report_new
+function do_revctrl_report_new
 {
   local directory="$1"; shift
 
-  save_terminal_title
-
   pushd "$directory" &>/dev/null
 
   # only update if we see a repository living there.
   if [ -f ".no-checkin" ]; then
-    echo "skipping reporting due to presence of .no-checkin sentinel file."
+    echo -ne "\nskipping reporting due to presence of .no-checkin sentinel file: $directory"
   elif [ -d ".svn" ]; then
     # this action so far only makes sense and is needed for svn.
     bash $FEISTY_MEOW_SCRIPTS/rev_control/svnapply.sh \? echo
-    test_or_die "svn diff"
+    exit_on_error "svn diff"
   elif [ -d ".git" ]; then
     git status -u
-    test_or_die "git status -u"
+    exit_on_error "git status -u"
   fi
 
   popd &>/dev/null
 
-  restore_terminal_title
-
   return 0
 }
 
-# checks in all the folders in a specified list.
+# checks in all the folders in the specified list.
 function checkin_list()
 {
   # make the list of directories unique.
   local list="$(uniquify $*)"
 
-  save_terminal_title
-
   # turn repo list back into an array.
   eval "repository_list=( ${REPOSITORY_LIST[*]} )"
 
@@ -183,56 +208,27 @@ function checkin_list()
     if [[ $outer =~ /.* ]]; then
       # yep, this path is absolute.  just handle it directly.
       if [ ! -d "$outer" ]; then continue; fi
-      do_checkin $outer
-      test_or_die "running check-in (absolute) on path: $outer"
-      sep 28
+      do_revctrl_checkin "$outer"
+      exit_on_error "running check-in (absolute) on path: $outer"
     else
       for inner in $list; do
         # add in the directory component to see if we can find the folder.
         local path="$inner/$outer"
         if [ ! -d "$path" ]; then continue; fi
-        do_checkin $path
-        test_or_die "running check-in (relative) on path: $path"
-        sep 28
+        do_revctrl_checkin "$path"
+        exit_on_error "running check-in (relative) on path: $path"
       done
     fi
   done
-
-  restore_terminal_title
 }
 
-# takes out the first few carriage returns that are in the input.
-function squash_first_few_crs()
-{
-  i=0
-  while read input_text; do
-    i=$((i+1))
-    if [ $i -le 5 ]; then
-      echo -n "$input_text  "
-    else
-      echo $input_text
-    fi
-  done
-  if [ $i -le 3 ]; then
-    # if we're still squashing eols, make sure we don't leave them hanging.
-    echo
-  fi
-}
-
-#hmmm: the below are git specific and should be named that way.
+#hmmm: below functions are git specific and should be named that way.
 
 function all_branch_names()
 {
   echo "$(git branch -vv | cut -d ' ' -f2)"
 }
 
-# a helpful method that reports the git branch for the current directory's
-# git repository.
-function my_branch_name()
-{
-  echo "$(git branch -vv | grep '\*' | cut -d ' ' -f2)"
-}
-
 #this had a -> in it at one point for not matching, didn't it?
 # this reports the upstream branch for the current repo.
 ##function parent_branch_name()
@@ -240,6 +236,13 @@ function my_branch_name()
   ##echo "$(git branch -vv | grep \* | cut -d ' ' -f2)"
 ##}
 
+# a helpful method that reports the git branch for the current directory's
+# git repository.
+function my_branch_name()
+{
+  echo "$(git branch -vv | grep '\*' | cut -d ' ' -f2)"
+}
+
 # reports the status of the branch by echoing one of these values:
 #   okay: up to date and everything is good.
 #   needs_pull: this branch needs to be pulled from origins.
@@ -250,122 +253,160 @@ function check_branch_state()
 {
   local branch="$1"; shift
 
+  if [ -z "$branch" ]; then
+    echo "No branch was passed to check branch state."
+    return 1
+  fi
+
   local to_return=120  # unknown issue.
 
   local local_branch=$(git rev-parse @)
   local remote_branch=$(git rev-parse "$branch")
   local merge_base=$(git merge-base @ "$branch")
 
+  local to_echo=
   if [ "$local_branch" == "$remote_branch" ]; then
-    echo "okay"
+    to_echo="okay"
   elif [ "$local_branch" == "$merge_base" ]; then
-    echo "needs_pull"
+    to_echo="needs_pull"
   elif [ "$remote_branch" == "$merge_base" ]; then
-    echo "needs_push"
+    to_echo="needs_push"
   else
-    echo "diverged"
+    to_echo="diverged"
   fi
 
+  echo -n "$to_echo"
+
   return $to_return
 }
 
+# only shows the branch state if it's not okay.
+# note that this is not the same as a conditional branch (ha ha).
+function show_branch_conditionally()
+{
+  local this_branch="$1"; shift
+
+  local state=$(check_branch_state "$this_branch")
+  if [ "$state" != "okay" ]; then
+    echo "=> branch '$this_branch' state is not clean: $state"
+  fi
+}
+
 # the git update process just gets more and more complex when you bring in
 # branches, so we've moved this here to avoid having a ton of code in the
 # other methods.
-function do_careful_git_update()
+function do_revctrl_careful_update()
 {
   local directory="$1"; shift
   pushd "$directory" &>/dev/null
-  test_or_die "changing to directory: $directory"
+  exit_on_error "changing to directory: $directory"
 
   if [ ! -d ".git" ]; then
-    # we ignore if they're jumping into a non-useful folder, but also tell them.
-    echo "Directory is not a git repository: $directory"
-    return 0
+    # not a git project, so just boil this down to a getem action.
+    popd &>/dev/null
+    log_feisty_meow_event "skipping careful part and doing simple update on non-git repository: $directory"
+    do_revctrl_simple_update "$directory"
+    return $?
   fi
 
-  # first update all our remote branches to their current state from the repos.
-  git remote update
-  test_or_die "git remote update"
+#hmmm: another piece of reusable code, to process the directory for printing.
+  # make a nice echoer since we want to use it inside conditions below.
+  local nicedir="$directory"
+  if [ $nicedir == "." ]; then
+    nicedir=$(\pwd)
+  fi
+  local blatt_report="echo -e \ncarefully retrieving '$nicedir'..."
+  $blatt_report
 
   local this_branch="$(my_branch_name)"
-#appears to be useless; reports no changes when we need to know about remote changes that do exist:
-#hmmm: trying it out again now that things are better elsewhere.  let's see what it says.
-  state=$(check_branch_state "$this_branch")
-  echo "=> branch '$this_branch' state is: $state"
+
+  show_branch_conditionally "$this_branch"
+
+  # first update all our remote branches to their current state from the repos.
+  git remote update | $TO_SPLITTER
+  promote_pipe_return 0
+  exit_on_error "git remote update"
+
+  show_branch_conditionally "$this_branch"
 
   # this code is now doing what i have to do when i repair the repo.  and it seems to be good so far.
+  # note that we allow the local branch to be merged with its remote counterpart; otherwise we would
+  # miss changes that happened elsewhere which should be seen in our local copy.
   local branch_list=$(all_branch_names)
   local bran
   for bran in $branch_list; do
-#    echo "synchronizing remote branch: $bran"
-    git checkout "$bran"
-    test_or_die "git switching checkout to remote branch: $bran"
+    log_feisty_meow_event "synchronizing remote branch: $bran"
+    git checkout "$bran" | $TO_SPLITTER
+    promote_pipe_return 0
+    exit_on_error "git switching checkout to remote branch: $bran"
 
-    state=$(check_branch_state "$bran")
-    echo "=> branch '$bran' state is: $state"
+    show_branch_conditionally "$this_branch"
 
     remote_branch_info=$(git ls-remote --heads origin $bran 2>/dev/null)
     if [ ! -z "$remote_branch_info" ]; then
       # we are pretty sure the remote branch does exist.
-      git pull --no-ff origin "$bran"
+      git pull --tags origin "$bran" | $TO_SPLITTER
+      promote_pipe_return 0
     fi
-    test_or_die "git pull of remote branch: $bran"
+    exit_on_error "git pull of remote branch: $bran"
   done
   # now switch back to our branch.
-  git checkout "$this_branch"
-  test_or_die "git checking out our current branch: $this_branch"
+  git checkout "$this_branch" | $TO_SPLITTER
+  promote_pipe_return 0
+  exit_on_error "git checking out our current branch: $this_branch"
 
   # now pull down any changes in our own origin in the repo, to stay in synch
   # with any changes from others.
-  git pull --no-ff --all
-  test_or_die "git pulling all upstream"
+  git pull --tags --all | $TO_SPLITTER
+#is the above really important when we did this branch already in the loop?
+#it does an --all, but is that effective or different?  should we be doing that in above loop?
+  promote_pipe_return 0
+  exit_on_error "git pulling all upstream"
 
   popd &>/dev/null
 }
 
 # gets the latest versions of the assets from the upstream repository.
-function do_update()
+function do_revctrl_simple_update()
 {
   directory="$1"; shift
 
-  save_terminal_title
-
+#hmmm: another piece of reusable code, to process the directory for printing.
   # make a nice echoer since we want to use it inside conditions below.
   local nicedir="$directory"
   if [ $nicedir == "." ]; then
     nicedir=$(\pwd)
   fi
-  local blatt="echo retrieving '$nicedir'..."
+  local blatt_report="echo -e \nretrieving '$nicedir'..."
 
   pushd "$directory" &>/dev/null
   if [ -d "CVS" ]; then
     if test_writeable "CVS"; then
-      $blatt
+      $blatt_report
       cvs update . | $TO_SPLITTER
-      test_or_die "cvs update"
+      promote_pipe_return 0
+      exit_on_error "cvs update"
     fi
   elif [ -d ".svn" ]; then
     if test_writeable ".svn"; then
-      $blatt
+      $blatt_report
       svn update . | $TO_SPLITTER
-      test_or_die "svn update"
+      promote_pipe_return 0
+      exit_on_error "svn update"
     fi
   elif [ -d ".git" ]; then
     if test_writeable ".git"; then
-      $blatt
-      git pull --no-ff 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
-      if [ ${PIPESTATUS[0]} -ne 0 ]; then false; fi
-      test_or_die "git pull of origin without fast forwards"
+      $blatt_report
+      git pull --tags 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
+      promote_pipe_return 0
+      exit_on_error "git pull of origin"
     fi
   else
     # this is not an error necessarily; we'll just pretend they planned this.
-    echo no repository in $directory
+    log_feisty_meow_event "no repository in $directory"
   fi
   popd &>/dev/null
 
-  restore_terminal_title
-
   return 0
 }
 
@@ -374,8 +415,6 @@ function checkout_list()
 {
   local list="$(uniquify $*)"
 
-  save_terminal_title
-
   # turn repo list back into an array.
   eval "repository_list=( ${REPOSITORY_LIST[*]} )"
 
@@ -386,22 +425,52 @@ function checkout_list()
     if [[ $outer =~ /.* ]]; then
       # yep, this path is absolute.  just handle it directly.
       if [ ! -d "$outer" ]; then continue; fi
-      do_update $outer
-      test_or_die "running update on: $path"
-      sep 28
+      do_revctrl_simple_update $outer
+      exit_on_error "running update on: $path"
     else
       for inner in $list; do
         # add in the directory component to see if we can find the folder.
         local path="$inner/$outer"
         if [ ! -d "$path" ]; then continue; fi
-        do_update $path
-        test_or_die "running update on: $path"
-        sep 28
+        do_revctrl_simple_update $path
+        exit_on_error "running update on: $path"
       done
     fi
   done
+}
+
+# does a careful update on all the folders in the specified list;
+# it looks in the REPOSITORY_LIST for those names and updates them.
+# this is just like checkout_list, but it's for the puffing up action
+# we need to do on git.
+function puff_out_list()
+{
+  # make the list of directories unique.
+  local list="$(uniquify $*)"
 
-  restore_terminal_title
+  # turn repo list back into an array.
+  eval "repository_list=( ${REPOSITORY_LIST[*]} )"
+
+  local outer inner
+
+#hmmm: once again, seeing some reusable code in this loop...
+  for outer in "${repository_list[@]}"; do
+    # check the repository first, since it might be an absolute path.
+    if [[ $outer =~ /.* ]]; then
+      # yep, this path is absolute.  just handle it directly.
+      if [ ! -d "$outer" ]; then continue; fi
+      do_revctrl_careful_update "$outer"
+      exit_on_error "running puff-out (absolute) on path: $outer"
+    else
+      for inner in $list; do
+        # add in the directory component to see if we can find the folder.
+        local path="$inner/$outer"
+        if [ ! -d "$path" ]; then continue; fi
+        do_revctrl_careful_update "$path"
+        exit_on_error "running puff-out (relative) on path: $path"
+      done
+    fi
+  done
 }
 
 # provides a list of absolute paths of revision control directories
@@ -426,8 +495,8 @@ function generate_rev_ctrl_filelist()
 
   local sortfile=$(mktemp /tmp/zz_checkin_sort.XXXXXX)
   sort <"$tempfile" >"$sortfile"
-  \rm "$tempfile"
   echo "$sortfile"
+  \rm "$tempfile"
 }
 
 # iterates across a list of directories contained in a file (first parameter).
@@ -437,8 +506,6 @@ function perform_revctrl_action_on_file()
   local tempfile="$1"; shift
   local action="$1"; shift
 
-  save_terminal_title
-
   local did_anything=
 
   while read -u 3 dirname; do
@@ -451,8 +518,7 @@ function perform_revctrl_action_on_file()
     echo "[$(pwd)]"
     # pass the current directory plus the remaining parameters from function invocation.
     $action . 
-    test_or_die "performing action $action on: $(pwd)"
-    sep 28
+    exit_on_error "performing action $action on: $(pwd)"
     popd &>/dev/null
   done 3<"$tempfile"
 
@@ -460,8 +526,6 @@ function perform_revctrl_action_on_file()
     echo "There was nothing to do the action '$action' on."
   fi
 
-  restore_terminal_title
-
-  rm $tempfile
+  rm "$tempfile"
 }