aboutsummaryrefslogtreecommitdiffstats
path: root/libbe/git.py
blob: e15d77312b05ea90c0e16da8c106b91a638582a1 (plain) (blame)
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright (C) 2007 Chris Ball <chris@printf.net>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
import os
import tempfile

from rcs import invoke

def strip_git(filename):
    # Find the base path of the GIT tree, in order to strip that leading
    # path from arguments to git -- it doesn't like absolute paths.
    if os.path.isabs(filename):
        absRepoDir = os.path.abspath(git_repo_for_path('.'))
        absRepoSlashedDir = os.path.join(absRepoDir,"")
        assert filename.startswith(absRepoSlashedDir), \
            "file %s not in git repo %s" % (filename, absRepoSlashedDir)
        filename = filename[len(absRepoSlashedDir):]
    return filename

def invoke_client(*args, **kwargs):
    directory = kwargs['directory']
    expect = kwargs.get('expect', (0, 1))
    cl_args = ["git"]
    cl_args.extend(args)
    status,output,error = invoke(cl_args, expect, cwd=directory)
    return status, output

def add_id(filename, paranoid=False):
    filename = strip_git(filename)
    invoke_client("add", filename, directory=git_repo_for_path('.'))

def delete_id(filename):
    filename = strip_git(filename)
    invoke_client("rm", filename, directory=git_repo_for_path('.'))

def mkdir(path, paranoid=False):
    os.mkdir(path)

def set_file_contents(path, contents):
    add = not os.path.exists(path)
    file(path, "wb").write(contents)
    if add:
        add_id(path)

def detect(path):
    """Detect whether a directory is revision-controlled using GIT"""
    path = os.path.realpath(path)
    old_path = None
    while True:
        if os.path.exists(os.path.join(path, ".git")):
            return True
        if path == old_path:
            return False
        old_path = path
        path = os.path.dirname(path)

def precommit(directory):
    pass

def commit(directory, summary, body=None):
    if body is not None:
        summary += '\n' + body
    descriptor, filename = tempfile.mkstemp()
    try:
        temp_file = os.fdopen(descriptor, 'wb')
        temp_file.write(summary)
        temp_file.close()
        invoke_client('commit', '-a', '-F', filename, directory=directory)
    finally:
        os.unlink(filename)

def postcommit(directory):
    pass


# In order to diff the bug database, you need a way to check out arbitrary
# previous revisions and a mechanism for locating the bug_dir in the revision
# you've checked out.
#
# Copying the Mercurial implementation, this feature is implemented by four
# functions:
#
# git_dir_for_path : find '.git' for a git tree.
#
# export : check out a commit 'spec' from git-repo 'bug_dir' into a dir
#          'revision_dir'
#
# find_or_make_export : check out a commit 'spec' from git repo 'directory' to
#                       any location you please and return the path to the checkout
#
# path_in_reference : return a path to the bug_dir of the commit 'spec'

def git_repo_for_path(path):
    """Find the root of the deepest repository containing path."""
    # Assume that nothing funny is going on; in particular, that we aren't
    # dealing with a bare repo.
    dirname = os.path.dirname(git_dir_for_path(path))
    if dirname == '' : # os.path.dirname('filename') == ''
        dirname = '.'
    return dirname

def git_dir_for_path(path):
    """Find the git-dir of the deepest repo containing path."""
    return invoke_client("rev-parse", "--git-dir", directory=path)[1].rstrip()

def export(spec, bug_dir, revision_dir):
    """Check out commit 'spec' from the git repo containing bug_dir into
    'revision_dir'."""
    if not os.path.exists(revision_dir):
        os.makedirs(revision_dir)
    invoke_client("init", directory=revision_dir)
    invoke_client("pull", git_dir_for_path(bug_dir), directory=revision_dir)
    invoke_client("checkout", '-f', spec, directory=revision_dir)

def find_or_make_export(spec, directory):
    """Checkout 'spec' from the repo at 'directory' by hook or by crook and
    return the path to the working copy."""
    home = os.path.expanduser("~")
    revision_root = os.path.join(home, ".be_revs")
    if not os.path.exists(revision_root):
        os.mkdir(revision_root)
    revision_dir = os.path.join(revision_root, spec)
    if not os.path.exists(revision_dir):
        export(spec, directory, revision_dir)
    return revision_dir

def path_in_reference(bug_dir, spec):
    """Check out 'spec' and return the path to its bug_dir."""
    spec = spec or 'HEAD'
    spec = invoke_client('rev-parse', spec, directory=bug_dir)[1].rstrip()
    # This is a really hairy computation.
    # The theory is that we can't possibly be working out of a bare repo;
    # hence, we get the rel_bug_dir by chopping off dirname(git_dir_for_path(bug_dir))
    # + '/'.
    rel_bug_dir = strip_git(bug_dir)
    export_root = find_or_make_export(spec, directory=bug_dir)
    return os.path.join(export_root, rel_bug_dir)


name = "git"