Cloning a git repository in an idempotent way
In config management, it is best to do setup in an idempotent way. After some basic research here's a way to clone a repository in an idempotent way. In this example we'll be cloning my home repository into ${HOME}/git/home.
First, check to see if the repository directory exists. Then change the working directory to it.
Next you want to check that we're actually in a repository. If not, then initialize an empty repository.
Next you want to check that your origin remote is set up. If it's not then it means this is still a first time clone and we need to add an origin remote.
Now fetch the latest changes. This won't affect the currently checked out HEAD. It will simply download the entire repository into the origin remote namespace refs/remotes/origin. This is akin to git clone to download your repository.
Check that you have a workspace checked out. That is, your HEAD has a reference checked out. If there is no HEAD reference checked out then check out the default branch on the remote repository. If HEAD already has something checked out then don't do anything.
Here's the full script.
We've just cloned a repository in an idempotent way. If the repository already exists then it doesn't try to do anything funny. In this way, you can break out the different steps and do different things if you like... Or you could just be lazy and do the following.
First, check to see if the repository directory exists. Then change the working directory to it.
Code:
#variables repo="https://github.com/samrocketman/home.git" local_repo_location="${HOME}/git/home" default_branch="master" #create the repository destination directory if [ ! -e "${local_repo_location}" ];then mkdir -p "${local_repo_location}" elif [ ! -d "${local_repo_location}" ];then echo "ERROR: ${local_repo_location} exists but is not a directory." exit 1 fi cd "${local_repo_location}"
Code:
#ensure it is a git repository if ! git rev-parse --is-inside-work-tree &> /dev/null;then echo "${PWD} is not a git repository. Running 'git init'." git init else echo "${PWD} is already a git repository." fi
Code:
#remember we set repo=https://github.com/samrocketman/home #ensure it has an origin remote configured if ! git config remote.origin.url &> /dev/null;then echo "No origin remote found. Running 'git remote add origin ${repo}'." git remote add origin ${repo} else echo "origin remote already exists." git remote -v fi
Code:
#fetch the latest changes echo "Downloading latest repository changes. Running 'git fetch'." git fetch
Code:
#checkout the default branch if no workspace checked out if ! git rev-parse --abbrev-ref HEAD &> /dev/null;then if [ ! -z "${default_branch}" ];then echo "HEAD not currently checked out. Running 'git checkout ${default_branch}'." git checkout ${default_branch} else echo "ERROR: Could not determine remote default branch." fi else echo "Workspace HEAD is already checked out." fi
Code:
#this turns an existing directory into a git repository instead of cloning #variables repo="https://github.com/samrocketman/home.git" local_repo_location="${HOME}/git/home" default_branch="master" #create the repository destination directory if [ ! -e "${local_repo_location}" ];then mkdir -p "${local_repo_location}" elif [ ! -d "${local_repo_location}" ];then echo "ERROR: ${local_repo_location} exists but is not a directory." exit 1 fi cd "${local_repo_location}" #ensure it is a git repository if ! git rev-parse --is-inside-work-tree &> /dev/null;then echo "${PWD} is not a git repository. Running 'git init'." git init else echo "${PWD} is already a git repository." fi #ensure it has an origin remote configured if ! git config remote.origin.url &> /dev/null;then echo "No origin remote found. Running 'git remote add origin ${repo}'." git remote add origin ${repo} else echo "origin remote already exists." git remote -v fi #fetch the latest changes echo "Downloading latest repository changes. Running 'git fetch'." git fetch #checkout the default branch if no workspace checked out if ! git rev-parse --abbrev-ref HEAD &> /dev/null;then #get the default branch of the origin remote default_branch="$(git ls-remote origin | awk '$2 == "HEAD" { head=$1; next}; $1 == head {sub(/refs\/heads\//, "",$2);print $2}')" if [ ! -z "${default_branch}" ];then echo "HEAD not currently checked out. Running 'git checkout ${default_branch}'." git checkout ${default_branch} else echo "ERROR: Could not determine remote default branch." fi else echo "Workspace HEAD is already checked out." fi
Code:
[ ! -e "${HOME}/git/home" ] && git clone https://github.com/samrocketman/home.git "${HOME}/git/home"
Total Comments 2
Comments
-
I was thinking of attempting parsing the default branch with the following.
Code:git ls-remote origin | awk '$2 == "HEAD" { head=$1; next}; $1 == head && $2 ~ /^refs\/heads/ {sub(/refs\/heads\//, "",$2);print $2}'
Posted 05-20-2015 at 02:19 AM by sag47 -
This blog post is useless due to the advent of the smart git protocol. Since sites using the "dumb protocol" are becomming rare this post has limited, if any, use.
Posted 12-07-2015 at 01:48 PM by sag47