Juicy lil' bytes

Keep a Local Git Repository Synchronized With a Remote Git Repository

Here’s a shell script that will compare the SHA-1 commit ID’s between a local and remote Git repository, and then pull + fast forward merge any remote changes.

The Shell Script

This bash shell script is also available on my GitHub repository:

gitupdate.shLink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/bin/bash
# Scriptacular - gitupdate.sh
# Compare SHA-1 between local and remote repositories, git pull + FF merge in local if they differ
# Copyright 2013 Christopher Simpkins
# MIT License

# Default to working directory
LOCAL_REPO="."
# Default to git pull with FF merge in quiet mode
GIT_COMMAND="git pull --quiet"

# User messages
GU_ERROR_FETCH_FAIL="Unable to fetch the remote repository."
GU_ERROR_UPDATE_FAIL="Unable to update the local repository."
GU_ERROR_NO_GIT="This directory has not been initialized with Git."
GU_INFO_REPOS_EQUAL="The local repository is current. No update is needed."
GU_SUCCESS_REPORT="Update complete."


if [ $# -eq 1 ]; then
  LOCAL_REPO="$1"
  cd "$LOCAL_REPO"
fi

if [ -d ".git" ]; then
  # update remote tracking branch
  git remote update >&-
  if (( $? )); then
      echo $GU_ERROR_FETCH_FAIL >&2
      exit 1
  else
      LOCAL_SHA=$(git rev-parse --verify HEAD)
      REMOTE_SHA=$(git rev-parse --verify FETCH_HEAD)
      if [ $LOCAL_SHA = $REMOTE_SHA ]; then
          echo $GU_INFO_REPOS_EQUAL
          exit 0
      else
          $GIT_COMMAND
          if (( $? )); then
              echo $GU_ERROR_UPDATE_FAIL >&2
              exit 1
          else
              echo $GU_SUCCESS_REPORT
          fi
      fi
  fi
else
  echo $GU_ERROR_NO_GIT >&2
  exit 1
fi
exit 0

What it Does

The script confirms that the working directory (with the option to specify a different directory as an argument to the script) is initialized with git and then compares the SHA-1 ID’s between the local HEAD commit and the remote HEAD commit. If they have different commit ID’s, it performs a git pull based fast forward merge in the local repository. If the SHA-1 ID’s do not differ, the script aborts the pull, informs the user, and exits.

The magic is in the shell command substitution lines that retrieve the SHA-1 from the local HEAD commit and the remote HEAD commit:

1
2
3
4
5
6
LOCAL_SHA=$(git rev-parse --verify HEAD)
REMOTE_SHA=$(git rev-parse --verify FETCH_HEAD)
if [ $LOCAL_SHA = $REMOTE_SHA ]; then
  echo $GU_INFO_REPOS_EQUAL
  exit 0
else

The git rev-parse command is how you get this information and the bash idiom $(COMMAND) assigns the result of the command to local script variables.

You could hook this up to a cron task and keep your directory current on whatever schedule is appropriate based upon the frequency of changes in the remote repository. I am including it in a project to keep a user’s local set of scripts current with updates that I make to a remote script repository after a user initiated request for an update.

What it Doesn’t Do

I developed this to keep a static set of files in a local repository synchronized with a remote repository that receives updates. If the local repository is being used for development and there is overlap between changes in the remote and local repositories, git will cancel the merge and notify the user that they have changes that need to be committed (or stashed) before the merge will take place.