~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2007-06-11 05:08:34 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20070611050834-wcbta2pfitcuopku
fix long-line detection

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Aaron Bentley
2
 
# <aaron.bentley@utoronto.ca>
 
1
# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron.bentley@utoronto.ca>
 
2
# Copyright (C) 2007 John Arbash Meinel
3
3
#
4
4
#    This program is free software; you can redistribute it and/or modify
5
5
#    it under the terms of the GNU General Public License as published by
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
 
import re
28
 
 
29
 
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, PermissionDenied)
 
31
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
32
 
 
33
def temp_tree():
30
34
    dirname = tempfile.mkdtemp("temp-branch")
31
 
    return bzrlib.branch.Branch.initialize(dirname)
32
 
 
33
 
def rm_branch(br):
34
 
    shutil.rmtree(br.base)
35
 
 
36
 
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):
37
41
    """
38
42
    Return true if no files are modifed or unknown
39
 
    >>> import bzrlib.add
40
 
    >>> br = temp_branch()
41
 
    >>> is_clean(br)
42
 
    (True, [])
43
 
    >>> fooname = os.path.join(br.base, "foo")
44
 
    >>> file(fooname, "wb").write("bar")
45
 
    >>> is_clean(br)
46
 
    (True, [u'foo'])
47
 
    >>> br.working_tree().add(["foo"])
48
 
    >>> is_clean(br)
49
 
    (False, [])
50
 
    >>> br.working_tree().commit("added file")
51
 
    >>> is_clean(br)
52
 
    (True, [])
53
 
    >>> rm_branch(br)
54
43
    """
55
 
    from bzrlib.diff import compare_trees
56
 
    old_tree = cur_branch.basis_tree()
57
 
    new_tree = cur_branch.working_tree()
 
44
    old_tree = cur_tree.basis_tree()
 
45
    new_tree = cur_tree
58
46
    non_source = []
59
 
    for path, file_class, kind, file_id, entry in new_tree.list_files():
60
 
        if file_class in ('?', 'I'):
61
 
            non_source.append(path)
62
 
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
 
47
    new_tree.lock_read()
 
48
    try:
 
49
        for path, file_class, kind, file_id, entry in new_tree.list_files():
 
50
            if file_class in ('?', 'I'):
 
51
                non_source.append(path)
 
52
        delta = new_tree.changes_from(old_tree, want_unchanged=False)
 
53
    finally:
 
54
        new_tree.unlock()
63
55
    return not delta.has_changed(), non_source
64
56
 
65
 
def set_push_data(br, location):
66
 
    push_file = file (br.control_files.controlfilename("x-push-data"), "wb")
67
 
    push_file.write("%s\n" % location)
 
57
def set_push_data(tree, location):
 
58
    tree.branch.control_files.put_utf8("x-push-data", "%s\n" % location)
68
59
 
69
 
def get_push_data(br):
 
60
def get_push_data(tree):
70
61
    """
71
 
    >>> br = temp_branch()
72
 
    >>> get_push_data(br) is None
 
62
    >>> tree = temp_tree()
 
63
    >>> get_push_data(tree) is None
73
64
    True
74
 
    >>> set_push_data(br, 'http://somewhere')
75
 
    >>> get_push_data(br)
76
 
    'http://somewhere'
77
 
    >>> rm_branch(br)
 
65
    >>> set_push_data(tree, 'http://somewhere')
 
66
    >>> get_push_data(tree)
 
67
    u'http://somewhere'
 
68
    >>> rm_tree(tree)
78
69
    """
79
 
    filename = br.control_files.controlfilename("x-push-data")
80
 
    if not os.path.exists(filename):
 
70
    try:
 
71
        location = tree.branch.control_files.get_utf8('x-push-data').read()
 
72
    except NoSuchFile:
81
73
        return None
82
 
    push_file = file (filename, "rb")
83
 
    (location,) = [f.rstrip('\n') for f in push_file]
84
 
    return location
 
74
    return location.rstrip('\n')
85
75
 
86
76
"""
87
77
>>> shell_escape('hello')
109
99
    def __init__(self, rsync_name):
110
100
        Exception.__init__(self, "%s not found." % rsync_name)
111
101
 
112
 
def rsync(source, target, ssh=False, excludes=(), silent=False, 
 
102
def rsync(source, target, ssh=False, excludes=(), silent=False,
113
103
          rsync_name="rsync"):
114
104
    """
 
105
    >>> new_dir = tempfile.mkdtemp()
 
106
    >>> old_dir = os.getcwd()
 
107
    >>> os.chdir(new_dir)
115
108
    >>> rsync("a", "b", silent=True)
116
109
    Traceback (most recent call last):
117
 
    RsyncNoFile: No such file a
118
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
 
110
    RsyncNoFile: No such file...
 
111
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
119
112
    Traceback (most recent call last):
120
 
    RsyncNoFile: No such file a
121
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
 
113
    RsyncNoFile: No such file...
 
114
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
122
115
    Traceback (most recent call last):
123
116
    NoRsync: rsyncc not found.
 
117
    >>> os.chdir(old_dir)
 
118
    >>> os.rmdir(new_dir)
124
119
    """
125
120
    cmd = [rsync_name, "-av", "--delete"]
126
121
    if ssh:
139
134
    except OSError, e:
140
135
        if e.errno == errno.ENOENT:
141
136
            raise NoRsync(rsync_name)
142
 
            
 
137
 
143
138
    proc.stdin.write('\n'.join(excludes)+'\n')
144
139
    proc.stdin.close()
145
140
    if silent:
181
176
        raise RsyncUnknownStatus(proc.returncode)
182
177
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
183
178
 
184
 
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data', 
185
 
              '.bzr/x-pull', '.bzr/pull', '.bzr/stat-cache',
186
 
              '.bzr/x-rsync-data')
 
179
exclusions = ('.bzr/x-push-data', '.bzr/branch/x-push/data', '.bzr/parent',
 
180
              '.bzr/branch/parent', '.bzr/x-pull-data', '.bzr/x-pull',
 
181
              '.bzr/pull', '.bzr/stat-cache', '.bzr/x-rsync-data',
 
182
              '.bzr/basis-inventory', '.bzr/inventory.backup.weave')
187
183
 
188
184
 
189
185
def read_revision_history(fname):
202
198
    tempdir = tempfile.mkdtemp('push')
203
199
    try:
204
200
        history_fname = os.path.join(tempdir, 'revision-history')
205
 
        cmd = rsync(location+'.bzr/revision-history', history_fname,
206
 
                    silent=True)
 
201
        try:
 
202
            cmd = rsync(location+'.bzr/revision-history', history_fname,
 
203
                        silent=True)
 
204
        except RsyncNoFile:
 
205
            cmd = rsync(location+'.bzr/branch/revision-history', history_fname,
 
206
                        silent=True)
207
207
        history = read_revision_history(history_fname)
208
208
    finally:
209
209
        shutil.rmtree(tempdir)
216
216
        return False
217
217
    for local, remote in zip(remote_history, local_history):
218
218
        if local != remote:
219
 
            return False 
 
219
            return False
220
220
    return True
221
221
 
222
222
def empty_or_absent(location):
226
226
    except RsyncNoFile:
227
227
        return True
228
228
 
229
 
def push(cur_branch, location=None, overwrite=False, working_tree=True):
230
 
    push_location = get_push_data(cur_branch)
 
229
def rspush(tree, location=None, overwrite=False, working_tree=True):
 
230
    push_location = get_push_data(tree)
231
231
    if location is not None:
232
232
        if not location.endswith('/'):
233
233
            location += '/'
234
234
        push_location = location
235
 
    
 
235
 
236
236
    if push_location is None:
237
 
        raise bzrlib.errors.MustUseDecorated
238
 
 
239
 
    if push_location.find('://') != -1:
240
 
        raise bzrlib.errors.MustUseDecorated
241
 
 
242
 
    if push_location.find(':') == -1:
243
 
        raise bzrlib.errors.MustUseDecorated
244
 
 
245
 
    clean, non_source = is_clean(cur_branch)
246
 
    if not clean:
247
 
        print """Error: This tree has uncommitted changes or unknown (?) files.
248
 
Use "bzr status" to list them."""
249
 
        sys.exit(1)
 
237
        raise BzrCommandError("No rspush location known or specified.")
 
238
 
 
239
    if (push_location.find('::') != -1):
 
240
        usessh=False
 
241
    else:
 
242
        usessh=True
 
243
 
 
244
    if (push_location.find('://') != -1 or
 
245
        push_location.find(':') == -1):
 
246
        raise BzrCommandError("Invalid rsync path %r." % push_location)
 
247
 
250
248
    if working_tree:
 
249
        clean, non_source = is_clean(tree)
 
250
        if not clean:
 
251
            print """Error: This tree has uncommitted changes or unknown (?) files.
 
252
    Use "bzr status" to list them."""
 
253
            sys.exit(1)
251
254
        final_exclusions = non_source[:]
252
255
    else:
253
 
        wt = cur_branch.working_tree()
 
256
        wt = tree
254
257
        final_exclusions = []
255
258
        for path, status, kind, file_id, entry in wt.list_files():
256
259
            final_exclusions.append(path)
258
261
    final_exclusions.extend(exclusions)
259
262
    if not overwrite:
260
263
        try:
261
 
            if not history_subset(push_location, cur_branch):
 
264
            if not history_subset(push_location, tree.branch):
262
265
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
263
266
                                                    " newer version of remote"
264
267
                                                    " branch.")
272
275
                " specified location.  Please ensure that"
273
276
                ' "%s" is of the form "machine:/path".' % push_location)
274
277
    print "Pushing to %s" % push_location
275
 
    rsync(cur_branch.base+'/', push_location, ssh=True, 
 
278
    rsync(tree.basedir+'/', push_location, ssh=usessh,
276
279
          excludes=final_exclusions)
277
280
 
278
 
    set_push_data(cur_branch, push_location)
 
281
    set_push_data(tree, push_location)
 
282
 
279
283
 
280
284
def short_committer(committer):
281
285
    new_committer = re.sub('<.*>', '', committer).strip(' ')
284
288
    return new_committer
285
289
 
286
290
 
 
291
def apache_ls(t):
 
292
    """Screen-scrape Apache listings"""
 
293
    apache_dir = '<img border="0" src="/icons/folder.gif" alt="[dir]">'\
 
294
        ' <a href="'
 
295
    lines = t.get('.')
 
296
    expr = re.compile('<a[^>]*href="([^>]*)"[^>]*>', flags=re.I)
 
297
    for line in lines:
 
298
        match = expr.search(line)
 
299
        if match is None:
 
300
            continue
 
301
        url = match.group(1)
 
302
        if url.startswith('http://') or url.startswith('/') or '../' in url:
 
303
            continue
 
304
        if '?' in url:
 
305
            continue
 
306
        yield url.rstrip('/')
 
307
 
 
308
 
 
309
def iter_branches(t, lister=None):
 
310
    """Iterate through all the branches under a transport"""
 
311
    for bzrdir in iter_bzrdirs(t, lister):
 
312
        try:
 
313
            branch = bzrdir.open_branch()
 
314
            if branch.bzrdir is bzrdir:
 
315
                yield branch
 
316
        except (NotBranchError, UnsupportedFormatError):
 
317
            pass
 
318
 
 
319
 
 
320
def iter_branch_tree(t, lister=None):
 
321
    for bzrdir in iter_bzrdirs(t, lister):
 
322
        try:
 
323
            wt = bzrdir.open_workingtree()
 
324
            yield wt.branch, wt
 
325
        except NoWorkingTree, UnsupportedFormatError:
 
326
            try:
 
327
                branch = bzrdir.open_branch()
 
328
                if branch.bzrdir is bzrdir:
 
329
                    yield branch, None
 
330
            except (NotBranchError, UnsupportedFormatError):
 
331
                continue
 
332
 
 
333
 
 
334
def iter_bzrdirs(t, lister=None):
 
335
    if lister is None:
 
336
        def lister(t):
 
337
            return t.list_dir('.')
 
338
    try:
 
339
        bzrdir = bzrdir_from_transport(t)
 
340
        yield bzrdir
 
341
    except (NotBranchError, UnsupportedFormatError, TransportError,
 
342
            PermissionDenied):
 
343
        pass
 
344
    try:
 
345
        for directory in lister(t):
 
346
            if directory == ".bzr":
 
347
                continue
 
348
            try:
 
349
                subt = t.clone(directory)
 
350
            except UnicodeDecodeError:
 
351
                continue
 
352
            for bzrdir in iter_bzrdirs(subt, lister):
 
353
                yield bzrdir
 
354
    except (NoSuchFile, PermissionDenied, TransportError):
 
355
        pass
 
356
 
 
357
 
 
358
def bzrdir_from_transport(t):
 
359
    """Open a bzrdir from a transport (not a location)"""
 
360
    format = BzrDirFormat.find_format(t)
 
361
    BzrDir._check_supported(format, False)
 
362
    return format.open(t)
 
363
 
 
364
 
287
365
def run_tests():
288
366
    import doctest
289
367
    result = doctest.testmod()