Schlagworte: git

18.05.15

Git pre-push hook to check for bad log pattern

I've just written a pre-push hook for Git that will check for certain patterns in the log messages that would get pushed.

This is meant to prevent you from accidentally pushing anything that starts with "fixup!" or "stash!" to certain branches (master, etc). These log messages are meant to be used with the autostash feature, which would setup your file for `git rebase --interactive` accordingly.

The code can be found in my dotfiles: ~df/config/git/template/hooks/pre-push.

I am using this through my Git template directory, which gets used with `git init` (and `git init` in an existing repo will also install missing hooks).

Here's the current version:

Code:

#!/bin/sh
#
# Git pre-push hook to check for "fixup!" and "squash!" in commit messages,
# which are not meant to get pushed to public/non-PR branches.
#
# It will also prevent fast-forward pushes, because git-log fails for them.
#
# You can use `--no-verify` with git-push to skip this hook.
#
# Args:
# 1: name of the remote
# 2: location of the remote
# stdin: <local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF
 
remote=$1
 
# Which remote heads/branches to check?
# (a regular expression for "grep -E").
CHECK_REMOTE_HEADS='(master|develop|production|staging)'
 
# The log pattern to check.
# (a regular expression for "git grep")
BAD_LOG_PATTERN='^(fixup!|squash!)'
 
# Uncomment for debugging.
# (the ":" is there so the function can be "empty")
debug() {
  :
  # echo "debug: $@"
}
debug "$0: $1 $2 ($#)"
 
# Return status of the script.
ret=0
 
while read local_ref local_sha remote_ref remote_sha; do
  debug "Pushing: $local_ref ($local_sha) => $remote_ref ($remote_sha)"
 
  # Is this a new remote ref?
  if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then
    log_arg="$local_sha"
  else
    log_arg="${remote_sha}..${local_sha}"
  fi
 
  # Skip certain branches, but no other refs (tags).
  remote_head=${remote_ref#refs/heads/}
  if [ "$remote_ref" != "$remote_head" ]; then
    # This is a head/branch (no tag).
 
    # Allow any non-protected branch.
    if ! echo "$remote_head" | grep -qE "$CHECK_REMOTE_HEADS"; then
      debug "Skipping remote head: $remote_ref ($remote_head)"
      continue
    fi
  fi
 
  # Check for the bad log pattern.
  log_cmd="git log --grep $BAD_LOG_PATTERN --color=always $log_arg"
  log="$($log_cmd)"
  log_ret=$?
  if [ "$log_ret" != 0 ]; then
    echo "ERROR: git-log failed (non-fast-forward / forced push?)"
    echo "  cmd: $log_cmd"
    ret=1
  fi
  if [ -n "$log" ]; then
    echo "ERROR (pre-push): there are commits with a bad pattern ($BAD_LOG_PATTERN)!"
    echo "       local_ref: $local_ref => remote_ref: $remote_ref"
    debug "git-log argument: $log_arg"
    echo "$log" | sed 's/^/  /'
    ret=1
  fi
done
 
debug "ret=$ret"
exit $ret
By Daniel in development, Open Source, Snippets05/18/15 English (US) Email
Tags: ,

05.09.12

Deploying a website using Git via SSH remote

Git has a concept of "remotes" (tracked repositories), which allows to have
arbitrary alternate remote locations besides the typical "origin" remote, like "web".

The basic idea is to setup a user on the remote server ($SSH_DEPLOYUSER) which
is allowed to login via SSH (e.g. by adding your public SSH key to the deploy
user's ~/.ssh/authorized_keys file) and will be used to checkout what you want
to deploy.

To accomplish this you have to setup the Git working directory on the server and
add a "post-receive" hook, which will be invoked by Git after you have pushed
to the repository:

Code:

$ mkdir /path/to/repo-checkout
$ cd /path/to/repo-checkout
$ git init
# Create the post-receive file/hook (Ctrl-D to end the input to "cat"):
$ cat > .git/hooks/post-receive
#!/bin/sh
export GIT_DIR=$(pwd)
cd ..
git checkout -f
git submodule update --init --recursive
$ chmod +x .git/hooks/post-receive
$ git config --add receive.denyCurrentBranch ignore
$ chown $SSH_DEPLOYUSER -R .

On the local side you have to add a "remote" (named "web" in this case):

The final step is to initially push to it (which requires to specify the "refspec" once - following deployments can be done by just doing a "git push web"):

Code:

$ git remote add web ssh://$DEPLOYUSER@host.example.com/path/to/repo-checkout/.git
$ git push web +master:refs/heads/master

These instructions are based on the howto at toroid.org/ams/git-website-howto, but the main difference is that I am not using a "bare" repository here, which would not allow to use Git submodules; submodules require a "full" Git working directory and having a checkout of the repository requires the receive.denyCurrentBranch=ignore setting.

18.12.09

Convert etckeeper repository from Bazaar to Git

Quite a while ago I've installed etckeeper and changed the configuration to use Bazaar as its backend for myself (and sponsored/helped with a patch to change the default in Ubuntu).

However, already the first comment asked me why I would be using you Bazaar, if Git was that much faster (and required less space).

At that time I've thought, that Bazaar would catch up, and they (luckily) have done so in some areas, but Git is still a lot faster.

Therefore I've decided to change the repository from bzr to git. I've done so on my home machine and will do so on my dedicated server boxes in the next days, so it's a not only a recommendation but also documentation.

Howto convert a bzr repository to git (etckeeper)

Open a root shell, then you should export $GIT_DIR first:

export GIT_DIR=/etc/.git

The following will then convert /etc from a bzr to a git repository ("fast-export" is included in bzr-fastimport - you may have to install this first):

bzr fast-export --export-marks=$GIT_DIR/bzr.mark /etc | git fast-import --export-marks=$GIT_DIR/git.mark

After this (which will take a while depending on your history) you want to adjust the VCS setting in etckeeper.conf  (uncomment VCS=git and comment VCS=bzr):

sed -i -r -e s/'#\s*(VCS="git)"'/'\1'/ -e s/'VCS="bzr"'/'# \0'/ /etc/etckeeper/etckeeper.conf

I don't remember correctly, but the Git repository was not really setup correctly in the end - but the following fixed it:

etckeeper init

Benchmarks

I've done some benchmarking, comparing "$DVCS status" against each other. This will look if there are any files modified in the current tree, and it's what etckeeper does before and after upgrading (IIRC).
The following shows the commands with cold and warm caches (I've cleared the disk caches when changing to a new set).

What you can see is not only that "git status" is twice as fast with a cold cache (and even more with a warm one), but also that bzr takes even more time to print a single line of "It sure does!".

$ sudo time git st
0.01user 0.04system 0:05.61elapsed 1%CPU (0avgtext+0avgdata 0maxresident)k
29232inputs+688outputs (113major+1605minor)pagefaults 0swaps
$ sudo time git st
0.02user 0.02system 0:00.06elapsed 59%CPU (0avgtext+0avgdata 0maxresident)k
16inputs+688outputs (0major+1716minor)pagefaults 0swaps

$ sudo time bzr st
0.14user 0.06system 0:11.74elapsed 1%CPU (0avgtext+0avgdata 0maxresident)k
17840inputs+8outputs (36major+3750minor)pagefaults 0swaps
$ sudo time bzr st
0.16user 0.03system 0:00.32elapsed 62%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+3787minor)pagefaults 0swaps

$ sudo time bzr rocks
It sure does!
0.20user 0.05system 0:08.19elapsed 3%CPU (0avgtext+0avgdata 0maxresident)k
17392inputs+8outputs (34major+3422minor)pagefaults 0swaps
$ sudo time bzr rocks
It sure does!
0.16user 0.01system 0:00.20elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+3458minor)pagefaults 0swaps

I'll update this post when scripting for the update of my various OpenVZ containers, but the basic information should stand.

From now on, I will save 5+ seconds on any "aptitude safe-upgrade". Hopefully this multiplies somehow, so the time investment into this post pays back.. ;)

Seitenleiste