Archive

Posts Tagged ‘git’

A git hook to lock master and more

April 30, 2014 Leave a comment

Here is the hooks/update git script I’m using when I’m working with people not aware of how to use VCS software. It:

  • Check commit messages
  • Possibly lock master.
  • Check that nobody push -f on master.
#! /usr/bin/env python
import sys, os
from subprocess import *

lock_master = True

def revlist(old, new):
    r = Popen(["git", "rev-list", old+".."+new], stdout=PIPE).communicate()[0]
    return r.strip().split('\n')

def mergebase(old, new):
    return Popen(["git", "merge-base", old, new], stdout=PIPE).communicate()[0].strip()

def granted(user):
    return not user or "obiwan" == user

refname=sys.argv[1]
oldrev=sys.argv[2]
newrev=sys.argv[3]
user = os.environ.get('REMOTE_USER')
if not user:
    user = os.environ.get('USER')

if lock_master:
    if refname == "refs/heads/master" and not granted(user):
        print "Changing master not allowed."
        quit(1)
else:
    if mergebase(oldrev, newrev) != oldrev:
        if granted(user):
            print "Non fast forward push allowed."
            quit(0)
        else:
            print "Non fast forward push not allowed."
            quit(1)

for rev in revlist(oldrev, newrev):
    commit=Popen(["git", "cat-file", "commit", rev], stdout=PIPE).communicate()[0]
    commit_lines = commit.split("\n")
    for i in xrange(len(commit_lines)):
        line = commit_lines[i]
        if line.startswith("committer "):
            commiter = " ".join(line.split(" ")[1:-2])
            title_line = i+2
    title = commit_lines[title_line].strip()
    if "Signed-off-by" in title or len(title) == 0 or title.startswith("-"):
        print "Invalid commit comment:"
        print title
        print "The first line must be the commit title."
        quit(1)
Advertisements
Tags:

Changing git committers and authors with python

June 14, 2012 Leave a comment

Here is a python script I’m using to fix author and committer names in a git repository.

#! /usr/bin/python
import os, sys
from subprocess import *

user_map = {
"cartman": ("Eric Cartman", "eric.cartman@southpark.net")
"kenny": ("Kenny McCormick", "kenny.mccormick@southpark.net")
}

def setenv(var):
    user = user_map.get(os.environ["GIT_%s_NAME" % var])
    if user:
        os.environ["GIT_%s_NAME" % var] = user[0]
        os.environ["GIT_%s_EMAIL" % var] = user[1]

setenv("AUTHOR")
setenv("COMMITTER")

cmd = ['git', 'commit-tree']
cmd.extend(sys.argv[1:])
call(cmd)

Here is how to use this script:

git filter-branch --commit-filter '/path/to/authors.py "$@"' --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
Tags: ,

From git-svn to plain git

May 30, 2011 Leave a comment

Here is my script to prepare a git-svn repository to be pushed on a plain git repository. It create local branches from svn branches and local tags from svn tags.

#! /usr/bin/python
from subprocess import *
output = Popen(["git", "branch", "-rl"], stdout=PIPE).communicate()[0]
for b in output.split("\n"):
    b=b.strip()
    if b != "trunk" and len(b)>0:
        ref="remotes/"+b
        parent=Popen(["git", "log", "-n1", "--pretty=%P", ref], stdout=PIPE).communicate()[0]
        parent=parent.split()[-1]
        if b.startswith("tags/"):
            tag=b[len("tags/"):]
            message=Popen(["git", "log", "-n1", "--pretty=%B", ref], stdout=PIPE).communicate()[0]
            message=message.strip()
            call(["git", "tag", "-m", message, tag, parent])
        else:
            call(["git", "branch", b, parent])
Tags:

Mercurial vs Git performance

April 11, 2011 Leave a comment

I’ve just converted Netbeans/main Mercurial repository to Git using fast-export:

git clone http://repo.or.cz/r/fast-export.git
mkdir netbeans-git
cd netbeans-git
git init
~/fast-export/hg-fast-export.sh -r ~/netbeans-hg

The conversion lasts more than 24h. Using Python cProfile I’ve found that all measurable time was spent into the patch extraction of Mercurial. I did not investigate more but here are 2 hypothesis:

  • Mercurial is not designed to work on large source tree.
  • fast-export is not using Mercurial in the most efficient way.

Having such huge repositories in both Mercurial and Git is a good opportunity to measure how much Mercurial is slower than Git. For the following tests, I’m using Git 1.7.4.1 and Mercurial 1.7.5. I chose two commands:

  • status which shows how the tool scale with large source tree.
  • log which shows how the tool scale with a large number of commit.

The Netbeans/main repository repository contains more than 190000 commits and the current source tree contains 90519 files.
Read more…

Trac and git-http-backend

June 1, 2010 Leave a comment

Here is how to setup a git repository to be used with Trac and served only by HTTP.

Prerequisites

First install the required software. On Debian Squeeze just do:

apt-get install git trac-git gitweb

Trac configuration

Once the Trac project has been created modify the trac.ini file to enable the git plugin:

[git]
#comment/remove git_bin:
#git_bin = git
#or give the full path
#git_bin = /usr/bin/git

[components]
tracext.git.* = enabled

[trac]
repository_dir = /srv/scm/git/dagobah.git
repository_type = git

Git repository creation

As easy as:

mkdir /srv/scm/git/dagobah.git/
cd /srv/scm/git/dagobah.git/
git init --bare
git config http.getanyfile true
git config http.uploadpack true
git config http.receivepack true

Apache and git-http-backend

git-http-backend doesn’t have any builtin access control mecanism. It relies on Apache for that. Having different ACL for different git repository is probably possible with only Apache configuration but it’s beyond my field of expertise. So I wrote a wrapper around git-http-backend to manage ACLs and also, to dispatch requests between git-web and git-http-backend:

#!/usr/bin/python

GIT_PROJECT_ROOT='/srv/scm/git'
GIT_HTTP_BACKEND='/usr/lib/git-core/git-http-backend'
GIT_WEB='/usr/share/gitweb/index.cgi'
GITWEB_CONFIG='/etc/gitweb.conf'

import os
import subprocess
import csv
import re

#I find easier to change git-http-backend settings here than in the
#apache config file but both work
os.environ['GIT_PROJECT_ROOT']=GIT_PROJECT_ROOT
os.environ['GIT_HTTP_EXPORT_ALL']="1"
os.environ['GITWEB_CONFIG']=GITWEB_CONFIG

def print_env():
    """For debugging"""
    print "Content-type: text/html\n"
    e = os.environ
    for n in e:
       print n + " " + e[n] +"\n"

user = os.environ.get('REMOTE_USER')
script_name = os.environ.get('SCRIPT_NAME')
path_info = os.environ.get('REQUEST_URI')
path_info = path_info[ len(script_name): ]

is_gitweb = re.search(r"^/[^/]*/(HEAD|info/refs.*|objects/info/[^/]+|git-(upload|receive)-pack)$", path_info) == None

if is_gitweb:
    match = re.search(r"\?p=([^;]*)", path_info)
    if match :
        project_name = match.group(1)
    else:
        project_name = path_info.split("/")[1]
    is_writing = False
else:
    project_name = path_info.split("/")[1]
    is_writing = path_info.endswith("git-receive-pack")

valid_user = False
try:
    acl_file="/".join([GIT_PROJECT_ROOT,project_name,"acl.conf"])
    reader = csv.reader(open(acl_file, "rb"), delimiter=' ')
    for l in reader:
        if len(l)==2 and (user == l[0] or l[0]=='*') and ( (l[1] == 'r' and not is_writing) or l[1]=='rw'):
            valid_user = True
            break
except IOError:
    pass

if valid_user:
    if is_gitweb:
        subprocess.call([GIT_WEB])
    else:
        subprocess.call([GIT_HTTP_BACKEND])
else:
    print "Content-type: text/html"
    print "Status: 403 Forbidden\n"
    print "The user "+str(user)+" is not allowed to access to this page."

This script read ACLs from a file named &ltgit repository&gt/acl.conf. The format is simple and allows to specify read or read/write access:

$cat /srv/scm/git/dagobah.git/acl.conf
yoda rw
leia r
r2d2 rw
obiwan r

To use this wrapper just add this line to the Apache configuration:

ScriptAlias /git /var/www/cgi/git-http-backend-wrapper.cgi

Ensure that the directory containing the cgi file have the +ExecCGI option.

References

Tags: ,