Setting up Git repository with best practices & Installing git

This blog shows how to set up a git repository with best practices. It also shows how to build & install the latest version of Git on Linux (RHEL) from downloaded tarball (i.e. *.tar.gz file).

We had an older version of git in our Linux VM; It was missing a lot of useful features. Also, our organization’s yum repository did not contain the latest git. So we had to download the tarball and build git from the source. The process is not scary and worth every bit of your effort. Over time we identified some best practices for managing our git repository. What we identified is not new but they are not available under one roof.

The installation

  • Download the latest version of git from https://git-scm.com/downloads
  • Copy it to a folder on your Linux box
  • Go to the folder and execute the below command
$ gunzip git-2.28.0.tar.gz
$ tar xf git-2.28.0.tar
$ cd git-2.28.0
$ make configure
$ ./configure --prefix=/usr
$ make all
  • Next login as root and run below commands OR run below commands with sudo. I executed them by logging in as root.
  • Note we will be running below command from the folder where you executed “make all”
$ make install 
$ git --version 
git version 2.28.0

Note: When I installed git, it somehow changed the permission of the directory /usr/share/perl5 making it unavailable for regular users. It removed the read & execute permission of others. It can be easily fixed using the below command.

$ chmod -r 755 /usr/share/perl5 

Best Practices for Git repositories

  1. Don’t allow direct commits in the Master & Development branch
    • This blocks the backdoor for untested code to get into production
    • Implement a client-side git hook to enforce this rule
  2. Commits should only be allowed in feature branches that have branched out of the Development branch.
  3. Only through pull requests we should allow merging onto the Development branch or to any long-running branch.
  4. Enforce pull request review before every merge
    • This blocks unreviewed codes from getting into the Development branch.
    • Implement this as a branch rule through the settings page in GitHub
  5. Dismiss stale pull request approvals when new commits are pushed
    • Rule 2 & 3 can be enforced through settings in git (Settings > branches > Add Rule)
  6. Ensure that the branches are up to date before merging. In the initial days do it manually. There is a way to enforce it through GitHub but it needs status checks.
  7. Ensure tagging is happening on master every time a package is created and deployed in Test or Prod environment
    • This ensures that you have the option to revert back to any previously deployed version
  8. As we are doing deployment from the master branch, in a big team it makes sense to restrict the number of people who can merge into the master branch.
  9. Streamline feature branch naming convention
    • This ensures that every branch name gives some basic information about it. According to me, feature branch name should have a short developer name, name of the base branch (from which it is created) & a short feature name. So a feature branch name would look like <developerName>_<baseBranchName>_<FeatureName>
  10. You may have come across best practices with respect to comments. However at the start just follow these 3 rules. These rules can be enforced using git hooks.
    1. Make comments as detailed as possible. There is no prize for writing short comments.
    2. When writing detailed comments, ensure your 1st line is like a header and less than 60 char long and the second line should be the start of the paragraph describing the change. I normally keep an empty line between them.
    3. At times you may not have much to write and a short comment is enough, then ensure the 60 characters long policy.

Setting up Git hooks

Git hooks are ways to execute a script when a certain event happens or about to happen. These scripts can check specific conditions and block the event to complete successfully when conditions fail to pass.

By default git hooks are present in …/.git/hooks directory. Git gives some sample codes for us to view. Take a look at them, it will give some idea.

$ ls .git/hooks
applypatch-msg.sample         commit-msg.sample
fsmonitor-watchman.sample     post-update.sample
pre-applypatch.sample         pre-commit.sample
pre-merge-commit.sample       pre-push.sample
pre-rebase.sample             pre-receive.sample
prepare-commit-msg.sample     update.sample

Now let’s implement a new git hook that can stop users from directly committing code into “master” and “development” branch. Here we will use a pre-commit hook; before committing our code should check the branch being involved and if the branch name is either “master” or “development” we should throw an error.

#!/usr/ksh

BRANCH=$(git rev-parse --abbrev-ref HEAD)

if [[ $BRANCH == "master" || $BRANCH == "development" ]]
then
   echo "ERROR: Commits not allowed on branch, $BRANCH" >&2
   exit 1
fi

exit 0;

Now create a new file called pre-commit inside …/.git/hooks directory and copy the above code. It’s a small shell script that checks the branch name where the commit is happening. If the branch name matches the ones in the script then it exits with return code 1, else the exit code in 1. You may need to change the branch name as per your need.

Placing the file inside …/.git/hooks directory ensures that you are not allowed to commit into the master and the development branch. But how do we ensure the rule is enforced for everyone in the team?

Sharing Git hook with your team

The best solution is to keep the file in a common folder accessible to all in the team. On Linux create a folder called use /usr/.<appName>_gitHooks as the common folder for your team and store the pre-commit file in it.

So the folder name would be .sams_gitHooks in case your team’s name is SAMS. We ensure that the folder is hidden from casual browsers by adding a dot (.) at the start of the folder name. Having the folder team’s name in the folder name enables multiple teams to co-exist on the same VM or host.

Lastly we need to tell git to look into this common folder for hooks. We can do this using below command.

$ git config --global core.hooksPath /usr/.<appName>_gitHooks

To enforce this rule for all your developers you should add it in some setEnvironment script that everyone in the team calls every time they login or start working. Note that the script should be called from a location where git command can locate the working directory.

More usage of Git hook

You can use client-side Git hooks to do more advanced stuff. For example, how about doing some code review before a commit or displaying code comment percentage after every commit. There are many interesting things that can be done just by tapping into pre-commit and post-commit hook. I will let you come up with the possibilities.

Let me show you how we can extract the list of files getting changed using a pre-commit hook. Once we have the list, we can process these files from the list both at the pre-commit level or at the post-commit level. Pre-commit level processing should be used in case you want to block the commit, maybe due to the findings from your checkers. Post-commit level processing should be used to display additional comments or statistics about the files that got committed.

Below is the code you need to add in your pre-commit hook file to get the list of files being committed.

if git rev-parse --verify HEAD >/dev/null 2>&1
then
   against=HEAD
else
   # Initial commit; diff against an empty tree object
   against=$(git hash-object -t tree 2>/dev/null)
fi

MYID=$(whoami)
FILE_LIST="/tmp/${MYID}_commit.log"
> ${FILE_LIST}

CURR_DIR=$(pwd)
for FILE in $(git diff --cached --name-only --diff-filter=AM -z $against | xargs --null echo)
do
   echo ${CURR_DIR}/${FILE} >> ${FILE_LIST}
done

exit 0;

Once the list is ready, you can process it either in pre-commit or post-commit. You just have to loop over the file and select whichever file you need and run whatever check you want.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *