Add Bzr._vcs_exists() anyway :p
[be.git] / libbe / storage / vcs / bzr.py
1 # Copyright (C) 2005-2010 Aaron Bentley and Panometrics, Inc.
2 #                         Ben Finney <benf@cybersource.com.au>
3 #                         Gianluca Montecchi <gian@grys.it>
4 #                         Marien Zwart <marienz@gentoo.org>
5 #                         W. Trevor King <wking@drexel.edu>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along
18 # with this program; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 """
22 Bazaar (bzr) backend.
23 """
24
25 try:
26     import bzrlib
27     import bzrlib.branch
28     import bzrlib.builtins
29     import bzrlib.config
30     import bzrlib.errors
31     import bzrlib.option
32 except ImportError:
33     bzrlib = None
34 import os
35 import os.path
36 import re
37 import shutil
38 import StringIO
39 import sys
40
41 import libbe
42 import base
43
44 if libbe.TESTING == True:
45     import doctest
46     import unittest
47
48
49 def new():
50     return Bzr()
51
52 class Bzr(base.VCS):
53     name = 'bzr'
54     client = None # bzrlib module
55
56     def __init__(self, *args, **kwargs):
57         base.VCS.__init__(self, *args, **kwargs)
58         self.versioned = True
59
60     def _vcs_version(self):
61         if bzrlib == None:
62             return None
63         return bzrlib.__version__
64
65     def _vcs_get_user_id(self):
66         # excerpted from bzrlib.builtins.cmd_whoami.run()
67         try:
68             c = bzrlib.branch.Branch.open_containing(self.repo)[0].get_config()
69         except errors.NotBranchError:
70             c = bzrlib.config.GlobalConfig()
71         return c.username()
72
73     def _vcs_detect(self, path):
74         if self._u_search_parent_directories(path, '.bzr') != None :
75             return True
76         return False
77
78     def _vcs_root(self, path):
79         """Find the root of the deepest repository containing path."""
80         cmd = bzrlib.builtins.cmd_root()
81         cmd.outf = StringIO.StringIO()
82         cmd.run(filename=path)
83         return cmd.outf.getvalue().rstrip('\n')
84
85     def _vcs_init(self, path):
86         cmd = bzrlib.builtins.cmd_init()
87         cmd.outf = StringIO.StringIO()
88         cmd.run(location=path)
89
90     def _vcs_destroy(self):
91         vcs_dir = os.path.join(self.repo, '.bzr')
92         if os.path.exists(vcs_dir):
93             shutil.rmtree(vcs_dir)
94
95     def _vcs_add(self, path):
96         path = os.path.join(self.repo, path)
97         cmd = bzrlib.builtins.cmd_add()
98         cmd.outf = StringIO.StringIO()
99         cmd.run(file_list=[path], file_ids_from=self.repo)
100
101     def _vcs_exists(self, path, revision=None):
102         manifest = self._vcs_listdir(
103             self.repo, revision=revision, recursive=True)
104         if path in manifest:
105             return True
106         return False
107
108     def _vcs_remove(self, path):
109         # --force to also remove unversioned files.
110         path = os.path.join(self.repo, path)
111         cmd = bzrlib.builtins.cmd_remove()
112         cmd.outf = StringIO.StringIO()
113         cmd.run(file_list=[path], file_deletion_strategy='force')
114
115     def _vcs_update(self, path):
116         pass
117
118     def _parse_revision_string(self, revision=None):
119         if revision == None:
120             return revision
121         rev_opt = bzrlib.option.Option.OPTIONS['revision']
122         try:
123             rev_spec = rev_opt.type(revision)
124         except bzrlib.errors.NoSuchRevisionSpec:
125             raise base.InvalidRevision(revision)
126         return rev_spec
127
128     def _vcs_get_file_contents(self, path, revision=None):
129         if revision == None:
130             return base.VCS._vcs_get_file_contents(self, path, revision)
131         path = os.path.join(self.repo, path)
132         revision = self._parse_revision_string(revision)
133         cmd = bzrlib.builtins.cmd_cat()
134         cmd.outf = StringIO.StringIO()
135         try:
136             cmd.run(filename=path, revision=revision)
137         except bzrlib.errors.BzrCommandError, e:
138             if 'not present in revision' in str(e):
139                 raise base.InvalidPath(path, root=self.repo, revision=revision)
140             raise
141         return cmd.outf.getvalue()
142
143     def _vcs_path(self, id, revision):
144         manifest = self._vcs_listdir(
145             self.repo, revision=revision, recursive=True)
146         return self._u_find_id_from_manifest(id, manifest, revision=revision)
147
148     def _vcs_isdir(self, path, revision):
149         try:
150             self._vcs_listdir(path, revision)
151         except AttributeError, e:
152             if 'children' in str(e):
153                 return False
154             raise
155         return True
156
157     def _vcs_listdir(self, path, revision, recursive=False):
158         path = os.path.join(self.repo, path)
159         revision = self._parse_revision_string(revision)
160         cmd = bzrlib.builtins.cmd_ls()
161         cmd.outf = StringIO.StringIO()
162         try:
163             cmd.run(revision=revision, path=path, recursive=recursive)
164         except bzrlib.errors.BzrCommandError, e:
165             if 'not present in revision' in str(e):
166                 raise base.InvalidPath(path, root=self.repo, revision=revision)
167             raise
168         children = cmd.outf.getvalue().rstrip('\n').splitlines()
169         children = [self._u_rel_path(c, path) for c in children]
170         return children
171
172     def _vcs_commit(self, commitfile, allow_empty=False):
173         cmd = bzrlib.builtins.cmd_commit()
174         cmd.outf = StringIO.StringIO()
175         cwd = os.getcwd()
176         os.chdir(self.repo)
177         try:
178             cmd.run(file=commitfile, unchanged=allow_empty)
179         except bzrlib.errors.BzrCommandError, e:
180             strings = ['no changes to commit.', # bzr 1.3.1
181                        'No changes to commit.'] # bzr 1.15.1
182             if self._u_any_in_string(strings, str(e)) == True:
183                 raise base.EmptyCommit()
184             raise
185         finally:
186             os.chdir(cwd)
187         return self._vcs_revision_id(-1)
188
189     def _vcs_revision_id(self, index):
190         cmd = bzrlib.builtins.cmd_revno()
191         cmd.outf = StringIO.StringIO()
192         cmd.run(location=self.repo)
193         current_revision = int(cmd.outf.getvalue())
194         if index > current_revision or index < -current_revision:
195             return None
196         if index >= 0:
197             return str(index) # bzr commit 0 is the empty tree.
198         return str(current_revision+index+1)
199
200     def _diff(self, revision):
201         revision = self._parse_revision_string(revision)
202         cmd = bzrlib.builtins.cmd_diff()
203         cmd.outf = StringIO.StringIO()
204         # for some reason, cmd_diff uses sys.stdout not self.outf for output.
205         stdout = sys.stdout
206         sys.stdout = cmd.outf
207         try:
208             status = cmd.run(revision=revision, file_list=[self.repo])
209         finally:
210             sys.stdout = stdout
211         assert status in [0,1], "Invalid status %d" % status
212         return cmd.outf.getvalue()
213
214     def _parse_diff(self, diff_text):
215         """
216         Example diff text:
217                 
218         === modified file 'dir/changed'
219         --- dir/changed 2010-01-16 01:54:53 +0000
220         +++ dir/changed 2010-01-16 01:54:54 +0000
221         @@ -1,3 +1,3 @@
222          hi
223         -there
224         +everyone and
225          joe
226         
227         === removed file 'dir/deleted'
228         --- dir/deleted 2010-01-16 01:54:53 +0000
229         +++ dir/deleted 1970-01-01 00:00:00 +0000
230         @@ -1,3 +0,0 @@
231         -in
232         -the
233         -beginning
234         
235         === removed file 'dir/moved'
236         --- dir/moved   2010-01-16 01:54:53 +0000
237         +++ dir/moved   1970-01-01 00:00:00 +0000
238         @@ -1,4 +0,0 @@
239         -the
240         -ants
241         -go
242         -marching
243         
244         === added file 'dir/moved2'
245         --- dir/moved2  1970-01-01 00:00:00 +0000
246         +++ dir/moved2  2010-01-16 01:54:34 +0000
247         @@ -0,0 +1,4 @@
248         +the
249         +ants
250         +go
251         +marching
252         
253         === added file 'dir/new'
254         --- dir/new     1970-01-01 00:00:00 +0000
255         +++ dir/new     2010-01-16 01:54:54 +0000
256         @@ -0,0 +1,2 @@
257         +hello
258         +world
259         
260         """
261         new = []
262         modified = []
263         removed = []
264         for line in diff_text.splitlines():
265             if not line.startswith('=== '):
266                 continue
267             fields = line.split()
268             action = fields[1]
269             file = fields[-1].strip("'")
270             if action == 'added':
271                 new.append(file)
272             elif action == 'modified':
273                 modified.append(file)
274             elif action == 'removed':
275                 removed.append(file)
276         return (new,modified,removed)
277
278     def _vcs_changed(self, revision):
279         return self._parse_diff(self._diff(revision))
280
281 \f
282 if libbe.TESTING == True:
283     base.make_vcs_testcase_subclasses(Bzr, sys.modules[__name__])
284
285     unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
286     suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()])