1

I was trying to write a function in to a bash script so it could check to make sure the files that get rsynced as part of the script are from an up to date master copy from git. I found this question which seemed to cover this situation. Maybe I have misunderstood what this should do but it doesn't seem to work as I hoped.

I have noticed that if I make a commit on a separate branch, and then merge in to master, when I change bask to master and forget to pull (which I almost always forget to do) the script doesn't notice that I am behind the master and allows the rsync. Can anyone advise why this function doesn't work as I hoped?

startup_check() 
{
    # Check to make sure we are on the master branch
    CURRENTBRANCH=$(git status|awk 'NR==1{print $3}')

    if [ ! "$CURRENTBRANCH" == "master" ]; then
        echo -e "Not on master - cannot proceed, please change to master by using:\ngit checkout master"
        exit 1
    fi  

    # Check whether the current working branch is ahead, behind or diverged from remote master, and exit if we're not using the current remote master
    LOCAL=$(git rev-parse @)
    REMOTE=$(git rev-parse @{u})
    BASE=$(git merge-base @ @{u})

    if [ "$LOCAL" == "$REMOTE" ]; then
        echo "Working branch is up-to-date with remote master, proceeding...."
    elif [ "$LOCAL" == "$BASE" ]; then
        echo -e "Your working branch is behind the remote branch, you need to run:\ngit pull"
        exit 1
    elif [ "$REMOTE" == "$BASE" ]; then
        echo -e "Your working branch is ahead of the remote branch, you need to run:\ngit push origin master"
        exit 1
    else
        echo "Your working branch has diverged from the remote master, cannot continue"
        exit 1
    fi  
}

I'm using git-2.6.2 and bash-4.2

Community
  • 1
  • 1
Rumbles
  • 1,367
  • 3
  • 16
  • 40
  • You already answered - you forgot to pull – Andrew C Jan 07 '16 at 15:01
  • 1
    `git rev-parse` is a local operation. If you don't fetch to update your remote refs then you won't see the remote ref change. You might want to look at the git bash completion script to see how it checks for ahead/behind/diverged/etc. since I believe its checks are more robust. – Etan Reisner Jan 07 '16 at 15:37
  • Thanks @EtanReisner that helped, I managed to spend some time on it last week and I thin k I have solved my problem, I'll add an answer shortly. – Rumbles Apr 19 '16 at 13:40

2 Answers2

1

From the comment on my OP I had a look at the code in the git-bash-completion code. Now looking back I think the original code may have worked if I just add a a git fetch early in the method.

However, I have written the following to do what I wanted, I've tested that it works if you're not on master and if there are remote changes that aren't reflected locally, and I believe the other parts will work:

startup_check() 
{
  # Need to do a git fecth first to download remote changes for us to compare against
  git fetch
  # Most of this code was taken from the __git_ps1_changes method of https://github.com/markgandolfo/git-bash-completion
  local branch_ref
  branch_ref="$(git symbolic-ref -q HEAD 2>/dev/null)";
  if [ -n "$branch_ref" ]; then
    local branch_origin
    branch_origin="$(git for-each-ref --format='%(upstream:short)' $branch_ref)";
    if [ -n "$branch_origin" ]; then
      local branch
      branch=${branch_ref##refs/heads/};

      if [ "$branch" != "master" ]; then
        echo "Not working on the master - cannot proceed"
        exit 1
      fi

      local unpush
      unpush=$(git rev-list $branch_origin..$branch --count);
      local unpull
      unpull=$(git rev-list $branch..$branch_origin --count);
      local staged
      staged=$(git diff --staged --name-status | wc -l);
      local uncommits
      uncommits=$(git status -s -uall --porcelain);

      if [[ $unpush -gt 0 ]]; then
        echo "There are changes which have not been pushed - cannot proceed. The following commits need to be pushed:"
        local unpushed
        unpushed=$(git rev-list $branch_origin..$branch);
        for commit in $unpushed; do
          git --no-pager log --pretty=format:"%H - %an, %ar : %s" -n 1 $commit
        done
        exit 1
      fi

      if [[ $unpull -gt 0 ]]; then
        echo "There are changes which have not been pulled - cannot proceed. The following commits have been added to master since your last pull:"
        local unpulled
        unpulled=$(git rev-list $branch..$branch_origin);
        for commit in $unpulled; do
          git --no-pager log --pretty=format:"%H - %an, %ar : %s" -n 1 $commit
        done
        exit 1
      fi

      if [[ $staged -gt 0 ]]; then
        local staging
        staging=$(git diff --staged --name-status);
        echo "There are changes which are staged but have been commited - cannot proceed"
        echo $staging
        exit 1
      fi

      local unstaged
      unstaged=$(echo "$uncommits" | grep -c "^ [A-Z]");
      if [[ $unstaged -gt 0 ]]; then
        echo "There are unstaged changes - cannot proceed"
        echo $(echo "$uncommits" | grep "^ [A-Z]")
        exit 1
      fi

      local untracked
      untracked=$(echo "$uncommits" | grep -c "^??");
      if [[ $untracked -gt 0 ]]; then
        echo "There are untracked changes - cannot proceed"
        echo $(echo "$uncommits" | grep "^??")
        exit 1
      fi
    fi
  else
    echo "Working folder isn't a git folder"
    exit 1
  fi
}
Rumbles
  • 1,367
  • 3
  • 16
  • 40
  • 1
    There's no reason to `echo $(git ...)`. It just drops runs of spaces and exposes you to word-splitting and shell globbing. So don't do it. You could also save a little bit of work by not running the `unpushed`/`unpulled`/`staging` commands until you know you need to (e.g. the counts were greater than zero). If you put this into http://www.shellcheck.net/ you'll get a few other suggestions as well. – Etan Reisner Apr 19 '16 at 14:25
  • Thanks for your feedback again @EtanReisner I'll have a look in to that when I have time (so probably in around 6 months!) – Rumbles Apr 19 '16 at 14:28
  • Had some time to tinker, turns out you do need to echo the git log line as otherwise it will open each one up in an editor which you need to quit out of for each line... – Rumbles Apr 19 '16 at 15:11
  • Ah, no, just use `PAGER= git ...` or `git --no-pager log` to turn the paging off. – Etan Reisner Apr 19 '16 at 15:16
0

You first of all need to pull of course

Fixed script

The scrip contain spaces which cause the script to behavior differently on different unix flavors.

try this fixed one.

#!/bin/sh

# Check to make sure we are on the master branch
CURRENTBRANCH=$(git status|awk 'NR==1{print $3}');

if [ ! "$CURRENTBRANCH"=="master" ]; then
    echo -e "Not on master - cannot proceed, please change to master by using:\ngit checkout master"
    exit 1
fi

# Check whether the current working branch is ahead, behind or diverged from remote master, and exit if we're not using the current remote master
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse @{u})
BASE=$(git merge-base @ @{u})

if [ "$LOCAL"=="$REMOTE" ]; then
    echo "Working branch is up-to-date with remote master, proceeding...."
elif [ "$LOCAL"=="$BASE" ]; then
    echo -e "Your working branch is behind the remote branch, you need to run:\ngit pull"
    exit 1
elif [ "$REMOTE"=="$BASE" ]; then
    echo -e "Your working branch is ahead of the remote branch, you need to run:\ngit push origin master"
    exit 1
else
    echo "Your working branch has diverged from the remote master, cannot continue"
    exit 1
fi
Community
  • 1
  • 1
CodeWizard
  • 128,036
  • 21
  • 144
  • 167
  • d'oh, so close, yet so far! Thanks for the response, I will try it now! – Rumbles Jan 07 '16 at 15:23
  • That's weird, using your corrected master check it allowed me to run the script when I am on my branch instead of master. Replacing the spaces it catches that I am not on master and stops me from syncing. – Rumbles Jan 07 '16 at 15:29
  • 1
    Spaces around `==` in `[` are **vital** you **cannot** remove them (so the tests here are **entirely** broken). Also `==` in `[` is a bash-ism. POSIX requires `=` for equality in `[` tests. – Etan Reisner Jan 07 '16 at 15:33