~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2006-03-24 19:01:30 UTC
  • Revision ID: abentley@panoramicfeedback.com-20060324190130-2208c693486a8b33
Added apache index scraping to the branches command

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
#    You should have received a copy of the GNU General Public License
15
15
#    along with this program; if not, write to the Free Software
16
16
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 
import bzrlib
18
 
import bzrlib.errors
 
17
import codecs
 
18
import errno
19
19
import os
20
 
import os.path
21
 
import sys
 
20
import re
22
21
import tempfile
23
22
import shutil
24
 
import errno
25
23
from subprocess import Popen, PIPE
26
 
import codecs
27
 
 
28
 
def temp_branch():
 
24
import sys
 
25
 
 
26
import bzrlib
 
27
import bzrlib.errors
 
28
from bzrlib.errors import (BzrCommandError, NotBranchError, NoSuchFile,
 
29
                           UnsupportedFormatError, TransportError, 
 
30
                           NoWorkingTree)
 
31
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
32
 
 
33
def temp_tree():
29
34
    dirname = tempfile.mkdtemp("temp-branch")
30
 
    return bzrlib.branch.Branch.initialize(dirname)
31
 
 
32
 
def rm_branch(br):
33
 
    shutil.rmtree(br.base)
34
 
 
35
 
def is_clean(cur_branch):
 
35
    return BzrDir.create_standalone_workingtree(dirname)
 
36
 
 
37
def rm_tree(tree):
 
38
    shutil.rmtree(tree.basedir)
 
39
 
 
40
def is_clean(cur_tree):
36
41
    """
37
42
    Return true if no files are modifed or unknown
38
43
    >>> import bzrlib.add
39
 
    >>> br = temp_branch()
40
 
    >>> is_clean(br)
 
44
    >>> tree = temp_tree()
 
45
    >>> is_clean(tree)
41
46
    (True, [])
42
 
    >>> fooname = os.path.join(br.base, "foo")
 
47
    >>> fooname = os.path.join(tree.basedir, "foo")
43
48
    >>> file(fooname, "wb").write("bar")
44
 
    >>> is_clean(br)
 
49
    >>> is_clean(tree)
45
50
    (True, [u'foo'])
46
 
    >>> bzrlib.add.smart_add_branch(br, [br.base])
47
 
    1
48
 
    >>> is_clean(br)
 
51
    >>> bzrlib.add.smart_add_tree(tree, [tree.basedir])
 
52
    ([u'foo'], {})
 
53
    >>> is_clean(tree)
49
54
    (False, [])
50
 
    >>> br.commit("added file")
51
 
    >>> is_clean(br)
 
55
    >>> tree.commit("added file")
 
56
    >>> is_clean(tree)
52
57
    (True, [])
53
 
    >>> rm_branch(br)
 
58
    >>> rm_tree(tree)
54
59
    """
55
60
    from bzrlib.diff import compare_trees
56
 
    old_tree = cur_branch.basis_tree()
57
 
    new_tree = cur_branch.working_tree()
 
61
    old_tree = cur_tree.basis_tree()
 
62
    new_tree = cur_tree
58
63
    non_source = []
59
64
    for path, file_class, kind, file_id, entry in new_tree.list_files():
60
65
        if file_class in ('?', 'I'):
62
67
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
63
68
    return not delta.has_changed(), non_source
64
69
 
65
 
def set_pull_data(br, location, rev_id):
66
 
    pull_file = file (br.controlfilename("x-pull-data"), "wb")
67
 
    pull_file.write("%s\n%s\n" % (location, rev_id))
68
 
 
69
 
def get_pull_data(br):
70
 
    """
71
 
    >>> br = temp_branch()
72
 
    >>> get_pull_data(br)
73
 
    (None, None)
74
 
    >>> set_pull_data(br, 'http://somewhere', '888-777')
75
 
    >>> get_pull_data(br)
76
 
    ('http://somewhere', '888-777')
77
 
    >>> rm_branch(br)
78
 
    """
79
 
    filename = br.controlfilename("x-pull-data")
80
 
    if not os.path.exists(filename):
81
 
        return (None, None)
82
 
    pull_file = file (filename, "rb")
83
 
    location, rev_id = [f.rstrip('\n') for f in pull_file]
84
 
    return location, rev_id
85
 
 
86
 
def set_push_data(br, location):
87
 
    push_file = file (br.controlfilename("x-push-data"), "wb")
 
70
def set_push_data(tree, location):
 
71
    push_file = file (tree.branch.control_files.controlfilename("x-push-data"), "wb")
88
72
    push_file.write("%s\n" % location)
89
73
 
90
 
def get_push_data(br):
 
74
def get_push_data(tree):
91
75
    """
92
 
    >>> br = temp_branch()
93
 
    >>> get_push_data(br) is None
 
76
    >>> tree = temp_tree()
 
77
    >>> get_push_data(tree) is None
94
78
    True
95
 
    >>> set_push_data(br, 'http://somewhere')
96
 
    >>> get_push_data(br)
 
79
    >>> set_push_data(tree, 'http://somewhere')
 
80
    >>> get_push_data(tree)
97
81
    'http://somewhere'
98
 
    >>> rm_branch(br)
 
82
    >>> rm_tree(tree)
99
83
    """
100
 
    filename = br.controlfilename("x-push-data")
 
84
    filename = tree.branch.control_files.controlfilename("x-push-data")
101
85
    if not os.path.exists(filename):
102
86
        return None
103
87
    push_file = file (filename, "rb")
133
117
def rsync(source, target, ssh=False, excludes=(), silent=False, 
134
118
          rsync_name="rsync"):
135
119
    """
 
120
    >>> new_dir = tempfile.mkdtemp()
 
121
    >>> old_dir = os.getcwd()
 
122
    >>> os.chdir(new_dir)
136
123
    >>> rsync("a", "b", silent=True)
137
124
    Traceback (most recent call last):
138
 
    RsyncNoFile: No such file a
139
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
 
125
    RsyncNoFile: No such file...
 
126
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
140
127
    Traceback (most recent call last):
141
 
    RsyncNoFile: No such file a
142
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
 
128
    RsyncNoFile: No such file...
 
129
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
143
130
    Traceback (most recent call last):
144
131
    NoRsync: rsyncc not found.
 
132
    >>> os.chdir(old_dir)
 
133
    >>> os.rmdir(new_dir)
145
134
    """
146
135
    cmd = [rsync_name, "-av", "--delete"]
147
136
    if ssh:
202
191
        raise RsyncUnknownStatus(proc.returncode)
203
192
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
204
193
 
205
 
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data', 
206
 
              '.bzr/x-pull', '.bzr/pull', '.bzr/stat-cache',
207
 
              '.bzr/x-rsync-data')
 
194
exclusions = ('.bzr/x-push-data', '.bzr/branch/x-push/data', '.bzr/parent', 
 
195
              '.bzr/branch/parent', '.bzr/x-pull-data', '.bzr/x-pull',
 
196
              '.bzr/pull', '.bzr/stat-cache', '.bzr/x-rsync-data',
 
197
              '.bzr/basis-inventory', '.bzr/inventory.backup.weave')
208
198
 
209
199
 
210
200
def read_revision_history(fname):
223
213
    tempdir = tempfile.mkdtemp('push')
224
214
    try:
225
215
        history_fname = os.path.join(tempdir, 'revision-history')
226
 
        cmd = rsync(location+'.bzr/revision-history', history_fname,
227
 
                    silent=True)
 
216
        try:
 
217
            cmd = rsync(location+'.bzr/revision-history', history_fname,
 
218
                        silent=True)
 
219
        except RsyncNoFile:
 
220
            cmd = rsync(location+'.bzr/branch/revision-history', history_fname,
 
221
                        silent=True)
228
222
        history = read_revision_history(history_fname)
229
223
    finally:
230
224
        shutil.rmtree(tempdir)
247
241
    except RsyncNoFile:
248
242
        return True
249
243
 
250
 
def push(cur_branch, location=None, overwrite=False):
251
 
    push_location = get_push_data(cur_branch)
 
244
def push(tree, location=None, overwrite=False, working_tree=True):
 
245
    push_location = get_push_data(tree)
252
246
    if location is not None:
253
247
        if not location.endswith('/'):
254
248
            location += '/'
255
249
        push_location = location
256
250
    
257
251
    if push_location is None:
258
 
        raise bzrlib.errors.MustUseDecorated
 
252
        if tree.branch.get_push_location() is None:
 
253
            raise BzrCommandError("No push location known or specified.")
 
254
        else:
 
255
            raise bzrlib.errors.MustUseDecorated
259
256
 
260
257
    if push_location.find('://') != -1:
261
258
        raise bzrlib.errors.MustUseDecorated
263
260
    if push_location.find(':') == -1:
264
261
        raise bzrlib.errors.MustUseDecorated
265
262
 
266
 
    clean, non_source = is_clean(cur_branch)
267
 
    if not clean:
268
 
        print """Error: This tree has uncommitted changes or unknown (?) files.
269
 
Use "bzr status" to list them."""
270
 
        sys.exit(1)
271
 
    non_source.extend(exclusions)
 
263
    if working_tree:
 
264
        clean, non_source = is_clean(tree)
 
265
        if not clean:
 
266
            print """Error: This tree has uncommitted changes or unknown (?) files.
 
267
    Use "bzr status" to list them."""
 
268
            sys.exit(1)
 
269
        final_exclusions = non_source[:]
 
270
    else:
 
271
        wt = tree
 
272
        final_exclusions = []
 
273
        for path, status, kind, file_id, entry in wt.list_files():
 
274
            final_exclusions.append(path)
 
275
 
 
276
    final_exclusions.extend(exclusions)
272
277
    if not overwrite:
273
278
        try:
274
 
            if not history_subset(push_location, cur_branch):
 
279
            if not history_subset(push_location, tree.branch):
275
280
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
276
281
                                                    " newer version of remote"
277
282
                                                    " branch.")
285
290
                " specified location.  Please ensure that"
286
291
                ' "%s" is of the form "machine:/path".' % push_location)
287
292
    print "Pushing to %s" % push_location
288
 
    rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
289
 
 
290
 
    set_push_data(cur_branch, push_location)
 
293
    rsync(tree.basedir+'/', push_location, ssh=True, 
 
294
          excludes=final_exclusions)
 
295
 
 
296
    set_push_data(tree, push_location)
 
297
 
 
298
 
 
299
def short_committer(committer):
 
300
    new_committer = re.sub('<.*>', '', committer).strip(' ')
 
301
    if len(new_committer) < 2:
 
302
        return committer
 
303
    return new_committer
 
304
 
 
305
 
 
306
def apache_ls(t):
 
307
    """Screen-scrape Apache listings"""
 
308
    apache_dir = '<IMG border="0" src="/icons/folder.gif" ALT="[DIR]">'\
 
309
        ' <A HREF="'
 
310
    lines = t.get('.')
 
311
    for line in lines:
 
312
        if line.startswith(apache_dir):
 
313
            url = line[len(apache_dir):].split('"')[0]
 
314
            yield url.rstrip('/')
 
315
 
 
316
 
 
317
def iter_branches(t, lister=None):
 
318
    """Iterate through all the branches under a transport"""
 
319
    for bzrdir in iter_bzrdirs(t, lister):
 
320
        try:
 
321
            branch = bzrdir.open_branch()
 
322
            if branch.bzrdir is bzrdir:
 
323
                yield branch
 
324
        except (NotBranchError, UnsupportedFormatError):
 
325
            pass
 
326
 
 
327
 
 
328
def iter_branch_tree(t, lister=None):
 
329
    for bzrdir in iter_bzrdirs(t, lister):
 
330
        try:
 
331
            wt = bzrdir.open_workingtree()
 
332
            yield wt.branch, wt
 
333
        except NoWorkingTree, UnsupportedFormatError:
 
334
            try:
 
335
                branch = bzrdir.open_branch()
 
336
                if branch.bzrdir is bzrdir:
 
337
                    yield branch, None
 
338
            except (NotBranchError, UnsupportedFormatError):
 
339
                continue
 
340
 
 
341
def iter_bzrdirs(t, lister=None):
 
342
    if lister is None:
 
343
        def lister(t):
 
344
            return t.list_dir('.')
 
345
    try:
 
346
        bzrdir = bzrdir_from_transport(t)
 
347
        yield bzrdir
 
348
    except (NotBranchError, UnsupportedFormatError, TransportError):
 
349
        pass
 
350
    try:
 
351
        for directory in lister(t):
 
352
            if directory == ".bzr":
 
353
                continue
 
354
            subt = t.clone(directory)
 
355
            for bzrdir in iter_bzrdirs(subt):
 
356
                yield bzrdir
 
357
    except NoSuchFile:
 
358
        pass
 
359
    except TransportError, e:
 
360
        pass
 
361
 
 
362
    
 
363
def bzrdir_from_transport(t):
 
364
    """Open a bzrdir from a transport (not a location)"""
 
365
    format = BzrDirFormat.find_format(t)
 
366
    BzrDir._check_supported(format, False)
 
367
    return format.open(t)
 
368
 
291
369
 
292
370
def run_tests():
293
371
    import doctest