.bzr transition.
[be.git] / libbe / storage / vcs / git.py
1 # Copyright (C) 2008-2009 Ben Finney <benf@cybersource.com.au>
2 #                         Chris Ball <cjb@laptop.org>
3 #                         Gianluca Montecchi <gian@grys.it>
4 #                         W. Trevor King <wking@drexel.edu>
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with this program; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 """
21 Git backend.
22 """
23
24 import os
25 import os.path
26 import re
27 import shutil
28 import sys
29 import unittest
30
31 import libbe
32 import libbe.ui.util.user
33 import base
34 if libbe.TESTING == True:
35     import doctest
36
37
38 def new():
39     return Git()
40
41 class Git(base.VCS):
42     name='git'
43     client='git'
44
45     def __init__(self, *args, **kwargs):
46         base.VCS.__init__(self, *args, **kwargs)
47         self.versioned = True
48
49     def _vcs_version(self):
50         status,output,error = self._u_invoke_client('--version')
51         return output
52
53     def _vcs_get_user_id(self):
54         status,output,error = \
55             self._u_invoke_client('config', 'user.name', expect=(0,1))
56         if status == 0:
57             name = output.rstrip('\n')
58         else:
59             name = ''
60         status,output,error = \
61             self._u_invoke_client('config', 'user.email', expect=(0,1))
62         if status == 0:
63             email = output.rstrip('\n')
64         else:
65             email = ''
66         if name != '' or email != '': # got something!
67             # guess missing info, if necessary
68             if name == '':
69                 name = libbe.ui.util.user.get_fallback_username()
70             if email == '':
71                 email = libe.ui.util.user.get_fallback_email()
72             return libbe.ui.util.user.create_user_id(name, email)
73         return None # Git has no infomation
74
75     def _vcs_detect(self, path):
76         if self._u_search_parent_directories(path, '.git') != None :
77             return True
78         return False 
79
80     def _vcs_root(self, path):
81         """Find the root of the deepest repository containing path."""
82         # Assume that nothing funny is going on; in particular, that we aren't
83         # dealing with a bare repo.
84         if os.path.isdir(path) != True:
85             path = os.path.dirname(path)
86         status,output,error = self._u_invoke_client('rev-parse', '--git-dir',
87                                                     cwd=path)
88         gitdir = os.path.join(path, output.rstrip('\n'))
89         dirname = os.path.abspath(os.path.dirname(gitdir))
90         return dirname
91
92     def _vcs_init(self, path):
93         self._u_invoke_client('init', cwd=path)
94
95     def _vcs_destroy(self):
96         vcs_dir = os.path.join(self.repo, '.git')
97         if os.path.exists(vcs_dir):
98             shutil.rmtree(vcs_dir)
99
100     def _vcs_add(self, path):
101         if os.path.isdir(path):
102             return
103         self._u_invoke_client('add', path)
104
105     def _vcs_remove(self, path):
106         if not os.path.isdir(self._u_abspath(path)):
107             self._u_invoke_client('rm', '-f', path)
108
109     def _vcs_update(self, path):
110         self._vcs_add(path)
111
112     def _vcs_get_file_contents(self, path, revision=None):
113         if revision == None:
114             return base.VCS._vcs_get_file_contents(self, path, revision)
115         else:
116             arg = '%s:%s' % (revision,path)
117             status,output,error = self._u_invoke_client('show', arg)
118             return output
119
120     def _vcs_commit(self, commitfile, allow_empty=False):
121         args = ['commit', '--all', '--file', commitfile]
122         if allow_empty == True:
123             args.append('--allow-empty')
124             status,output,error = self._u_invoke_client(*args)
125         else:
126             kwargs = {'expect':(0,1)}
127             status,output,error = self._u_invoke_client(*args, **kwargs)
128             strings = ['nothing to commit',
129                        'nothing added to commit']
130             if self._u_any_in_string(strings, output) == True:
131                 raise base.EmptyCommit()
132         revision = None
133         revline = re.compile('(.*) (.*)[:\]] (.*)')
134         match = revline.search(output)
135         assert match != None, output+error
136         assert len(match.groups()) == 3
137         revision = match.groups()[1]
138         full_revision = self._vcs_revision_id(-1)
139         assert full_revision.startswith(revision), \
140             'Mismatched revisions:\n%s\n%s' % (revision, full_revision)
141         return full_revision
142
143     def _vcs_revision_id(self, index):
144         args = ['rev-list', '--first-parent', '--reverse', 'HEAD']
145         kwargs = {'expect':(0,128)}
146         status,output,error = self._u_invoke_client(*args, **kwargs)
147         if status == 128:
148             if error.startswith("fatal: ambiguous argument 'HEAD': unknown "):
149                 return None
150             raise base.CommandError(args, status, stderr=error)
151         commits = output.splitlines()
152         try:
153             return commits[index]
154         except IndexError:
155             return None
156
157 \f    
158 if libbe.TESTING == True:
159     base.make_vcs_testcase_subclasses(Git, sys.modules[__name__])
160
161     unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
162     suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()])