jacobian / jellyroll

You keep personal data in all sorts of places on the internets. Jellyroll brings them together onto your own site.

This URL has Read+Write access

jellyroll / jellyroll / providers / gitscm.py
100644 87 lines (73 sloc) 2.787 kb
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import re
import time
import logging
import datetime
import shutil
import tempfile
from unipath import FSPath as Path
from django.db import transaction
from django.utils.encoding import smart_unicode
from jellyroll.models import Item, CodeRepository, CodeCommit
from jellyroll.providers import utils
 
try:
    import git
except ImportError:
    git = None
 
log = logging.getLogger("jellyroll.providers.gitscm")
 
#
# Public API
#
def enabled():
    ok = git is not None
    if not ok:
        log.warn("The GIT provider is not available because the GitPython module "
                 "isn't installed.")
    return ok
 
def update():
    for repository in CodeRepository.objects.filter(type="git"):
        _update_repository(repository)
        
#
# Private API
#
 
def _update_repository(repository):
    source_identifier = "%s:%s" % (__name__, repository.url)
    last_update_date = Item.objects.get_last_update_of_model(CodeCommit, source=source_identifier)
    log.info("Updating changes from %s since %s", repository.url, last_update_date)
 
    # Git chokes on the 1969-12-31 sentinal returned by
    # get_last_update_of_model, so fix that up.
    if last_update_date.date() == datetime.date(1969, 12, 31):
        last_update_date = datetime.datetime(1970, 1, 1)
 
    working_dir, repo = _create_local_repo(repository)
    commits = repo.commits_since(since=last_update_date.strftime("%Y-%m-%d"))
    log.debug("Handling %s commits", len(commits))
    for commit in reversed(commits):
        if commit.author.email == repository.username:
            _handle_revision(repository, commit)
            
    log.debug("Removing working dir %s.", working_dir)
    shutil.rmtree(working_dir)
 
def _create_local_repo(repository):
    working_dir = tempfile.mkdtemp()
    g = git.Git(working_dir)
 
    log.debug("Cloning %s into %s", repository.url, working_dir)
    res = g.clone(repository.url)
    
    # This is pretty nasty.
    m = re.match('^Initialized empty Git repository in (.*)', res)
    repo_location = Path(m.group(1).rstrip('/'))
    if repo_location.name == ".git":
        repo_location = repo_location.parent
    return working_dir, git.Repo(repo_location)
 
@transaction.commit_on_success
def _handle_revision(repository, commit):
    log.debug("Handling [%s] from %s", commit.id[:7], repository.url)
    ci, created = CodeCommit.objects.get_or_create(
        revision = commit.id,
        repository = repository,
        defaults = {"message": smart_unicode(commit.message)}
    )
    if created:
        timestamp = datetime.datetime.fromtimestamp(time.mktime(commit.committed_date))
        return Item.objects.create_or_update(
            instance = ci,
            timestamp = timestamp,
            source = "%s:%s" % (__name__, repository.url),
        )