~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2006-08-02 03:23:09 UTC
  • mto: This revision was merged to the branch mainline in revision 425.
  • Revision ID: aaron.bentley@utoronto.ca-20060802032309-6ad0139e61304b19
Etienne Goyer: remove unused shebangs, update packaging

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, PermissionDenied)
 
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)
45
 
    (True, ['foo'])
46
 
    >>> bzrlib.add.smart_add_branch(br, [br.base])
47
 
    1
48
 
    >>> is_clean(br)
 
49
    >>> is_clean(tree)
 
50
    (True, [u'foo'])
 
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", rev_id='commit-id')
 
56
    'commit-id'
 
57
    >>> is_clean(tree)
52
58
    (True, [])
53
 
    >>> rm_branch(br)
 
59
    >>> rm_tree(tree)
54
60
    """
55
61
    from bzrlib.diff import compare_trees
56
 
    old_tree = cur_branch.basis_tree()
57
 
    new_tree = cur_branch.working_tree()
 
62
    old_tree = cur_tree.basis_tree()
 
63
    new_tree = cur_tree
58
64
    non_source = []
59
65
    for path, file_class, kind, file_id, entry in new_tree.list_files():
60
66
        if file_class in ('?', 'I'):
61
67
            non_source.append(path)
62
68
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
63
 
    if len(delta.added) > 0 or len(delta.removed) > 0 or \
64
 
        len(delta.modified) > 0:
65
 
        return False, non_source
66
 
    return True, non_source 
67
 
 
68
 
def set_pull_data(br, location, rev_id):
69
 
    pull_file = file (br.controlfilename("x-pull-data"), "wb")
70
 
    pull_file.write("%s\n%s\n" % (location, rev_id))
71
 
 
72
 
def get_pull_data(br):
73
 
    """
74
 
    >>> br = temp_branch()
75
 
    >>> get_pull_data(br)
76
 
    (None, None)
77
 
    >>> set_pull_data(br, 'http://somewhere', '888-777')
78
 
    >>> get_pull_data(br)
79
 
    ('http://somewhere', '888-777')
80
 
    >>> rm_branch(br)
81
 
    """
82
 
    filename = br.controlfilename("x-pull-data")
83
 
    if not os.path.exists(filename):
84
 
        return (None, None)
85
 
    pull_file = file (filename, "rb")
86
 
    location, rev_id = [f.rstrip('\n') for f in pull_file]
87
 
    return location, rev_id
88
 
 
89
 
def set_push_data(br, location):
90
 
    push_file = file (br.controlfilename("x-push-data"), "wb")
91
 
    push_file.write("%s\n" % location)
92
 
 
93
 
def get_push_data(br):
94
 
    """
95
 
    >>> br = temp_branch()
96
 
    >>> get_push_data(br) is None
 
69
    return not delta.has_changed(), non_source
 
70
 
 
71
def set_push_data(tree, location):
 
72
    tree.branch.control_files.put_utf8("x-push-data", "%s\n" % location)
 
73
 
 
74
def get_push_data(tree):
 
75
    """
 
76
    >>> tree = temp_tree()
 
77
    >>> get_push_data(tree) is None
97
78
    True
98
 
    >>> set_push_data(br, 'http://somewhere')
99
 
    >>> get_push_data(br)
100
 
    'http://somewhere'
101
 
    >>> rm_branch(br)
 
79
    >>> set_push_data(tree, 'http://somewhere')
 
80
    >>> get_push_data(tree)
 
81
    u'http://somewhere'
 
82
    >>> rm_tree(tree)
102
83
    """
103
 
    filename = br.controlfilename("x-push-data")
104
 
    if not os.path.exists(filename):
 
84
    try:
 
85
        location = tree.branch.control_files.get_utf8('x-push-data').read()
 
86
    except NoSuchFile:
105
87
        return None
106
 
    push_file = file (filename, "rb")
107
 
    (location,) = [f.rstrip('\n') for f in push_file]
108
 
    return location
 
88
    return location.rstrip('\n')
109
89
 
110
90
"""
111
91
>>> shell_escape('hello')
136
116
def rsync(source, target, ssh=False, excludes=(), silent=False, 
137
117
          rsync_name="rsync"):
138
118
    """
 
119
    >>> new_dir = tempfile.mkdtemp()
 
120
    >>> old_dir = os.getcwd()
 
121
    >>> os.chdir(new_dir)
139
122
    >>> rsync("a", "b", silent=True)
140
123
    Traceback (most recent call last):
141
 
    RsyncNoFile: No such file a
142
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
 
124
    RsyncNoFile: No such file...
 
125
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
143
126
    Traceback (most recent call last):
144
 
    RsyncNoFile: No such file a
145
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
 
127
    RsyncNoFile: No such file...
 
128
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
146
129
    Traceback (most recent call last):
147
130
    NoRsync: rsyncc not found.
 
131
    >>> os.chdir(old_dir)
 
132
    >>> os.rmdir(new_dir)
148
133
    """
149
134
    cmd = [rsync_name, "-av", "--delete"]
150
135
    if ssh:
205
190
        raise RsyncUnknownStatus(proc.returncode)
206
191
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
207
192
 
208
 
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data', 
209
 
              '.bzr/x-pull', '.bzr/pull', '.bzr/stat-cache',
210
 
              '.bzr/x-rsync-data')
 
193
exclusions = ('.bzr/x-push-data', '.bzr/branch/x-push/data', '.bzr/parent', 
 
194
              '.bzr/branch/parent', '.bzr/x-pull-data', '.bzr/x-pull',
 
195
              '.bzr/pull', '.bzr/stat-cache', '.bzr/x-rsync-data',
 
196
              '.bzr/basis-inventory', '.bzr/inventory.backup.weave')
211
197
 
212
198
 
213
199
def read_revision_history(fname):
226
212
    tempdir = tempfile.mkdtemp('push')
227
213
    try:
228
214
        history_fname = os.path.join(tempdir, 'revision-history')
229
 
        cmd = rsync(location+'.bzr/revision-history', history_fname,
230
 
                    silent=True)
 
215
        try:
 
216
            cmd = rsync(location+'.bzr/revision-history', history_fname,
 
217
                        silent=True)
 
218
        except RsyncNoFile:
 
219
            cmd = rsync(location+'.bzr/branch/revision-history', history_fname,
 
220
                        silent=True)
231
221
        history = read_revision_history(history_fname)
232
222
    finally:
233
223
        shutil.rmtree(tempdir)
250
240
    except RsyncNoFile:
251
241
        return True
252
242
 
253
 
def push(cur_branch, location=None, overwrite=False):
254
 
    push_location = get_push_data(cur_branch)
 
243
def rspush(tree, location=None, overwrite=False, working_tree=True):
 
244
    push_location = get_push_data(tree)
255
245
    if location is not None:
256
246
        if not location.endswith('/'):
257
247
            location += '/'
258
248
        push_location = location
259
249
    
260
250
    if push_location is None:
261
 
        print "No push location saved.  Please specify one on the command line."
262
 
        sys.exit(1)
263
 
 
264
 
    clean, non_source = is_clean(cur_branch)
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
 
    non_source.extend(exclusions)
 
251
        raise BzrCommandError("No rspush location known or specified.")
 
252
 
 
253
    if (push_location.find('://') != -1 or
 
254
        push_location.find(':') == -1):
 
255
        raise BzrCommandError("Invalid rsync path %r." % push_location)
 
256
 
 
257
    if working_tree:
 
258
        clean, non_source = is_clean(tree)
 
259
        if not clean:
 
260
            print """Error: This tree has uncommitted changes or unknown (?) files.
 
261
    Use "bzr status" to list them."""
 
262
            sys.exit(1)
 
263
        final_exclusions = non_source[:]
 
264
    else:
 
265
        wt = tree
 
266
        final_exclusions = []
 
267
        for path, status, kind, file_id, entry in wt.list_files():
 
268
            final_exclusions.append(path)
 
269
 
 
270
    final_exclusions.extend(exclusions)
270
271
    if not overwrite:
271
272
        try:
272
 
            if not history_subset(push_location, cur_branch):
 
273
            if not history_subset(push_location, tree.branch):
273
274
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
274
275
                                                    " newer version of remote"
275
276
                                                    " branch.")
283
284
                " specified location.  Please ensure that"
284
285
                ' "%s" is of the form "machine:/path".' % push_location)
285
286
    print "Pushing to %s" % push_location
286
 
    rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
287
 
 
288
 
    set_push_data(cur_branch, push_location)
 
287
    rsync(tree.basedir+'/', push_location, ssh=True, 
 
288
          excludes=final_exclusions)
 
289
 
 
290
    set_push_data(tree, push_location)
 
291
 
 
292
 
 
293
def short_committer(committer):
 
294
    new_committer = re.sub('<.*>', '', committer).strip(' ')
 
295
    if len(new_committer) < 2:
 
296
        return committer
 
297
    return new_committer
 
298
 
 
299
 
 
300
def apache_ls(t):
 
301
    """Screen-scrape Apache listings"""
 
302
    apache_dir = '<img border="0" src="/icons/folder.gif" alt="[dir]">'\
 
303
        ' <a href="'
 
304
    lines = t.get('.')
 
305
    expr = re.compile('<a[^>]*href="([^>]*)"[^>]*>', flags=re.I)
 
306
    for line in lines:
 
307
        match = expr.search(line)
 
308
        if match is None:
 
309
            continue
 
310
        url = match.group(1)
 
311
        if url.startswith('http://') or url.startswith('/') or '../' in url:
 
312
            continue
 
313
        if '?' in url:
 
314
            continue
 
315
        yield url.rstrip('/')
 
316
 
 
317
 
 
318
def iter_branches(t, lister=None):
 
319
    """Iterate through all the branches under a transport"""
 
320
    for bzrdir in iter_bzrdirs(t, lister):
 
321
        try:
 
322
            branch = bzrdir.open_branch()
 
323
            if branch.bzrdir is bzrdir:
 
324
                yield branch
 
325
        except (NotBranchError, UnsupportedFormatError):
 
326
            pass
 
327
 
 
328
 
 
329
def iter_branch_tree(t, lister=None):
 
330
    for bzrdir in iter_bzrdirs(t, lister):
 
331
        try:
 
332
            wt = bzrdir.open_workingtree()
 
333
            yield wt.branch, wt
 
334
        except NoWorkingTree, UnsupportedFormatError:
 
335
            try:
 
336
                branch = bzrdir.open_branch()
 
337
                if branch.bzrdir is bzrdir:
 
338
                    yield branch, None
 
339
            except (NotBranchError, UnsupportedFormatError):
 
340
                continue
 
341
 
 
342
 
 
343
def iter_bzrdirs(t, lister=None):
 
344
    if lister is None:
 
345
        def lister(t):
 
346
            return t.list_dir('.')
 
347
    try:
 
348
        bzrdir = bzrdir_from_transport(t)
 
349
        yield bzrdir
 
350
    except (NotBranchError, UnsupportedFormatError, TransportError,
 
351
            PermissionDenied):
 
352
        pass
 
353
    try:
 
354
        for directory in lister(t):
 
355
            if directory == ".bzr":
 
356
                continue
 
357
            try:
 
358
                subt = t.clone(directory)
 
359
            except UnicodeDecodeError:
 
360
                continue
 
361
            for bzrdir in iter_bzrdirs(subt, lister):
 
362
                yield bzrdir
 
363
    except (NoSuchFile, PermissionDenied, TransportError):
 
364
        pass
 
365
 
 
366
    
 
367
def bzrdir_from_transport(t):
 
368
    """Open a bzrdir from a transport (not a location)"""
 
369
    format = BzrDirFormat.find_format(t)
 
370
    BzrDir._check_supported(format, False)
 
371
    return format.open(t)
 
372
 
289
373
 
290
374
def run_tests():
291
375
    import doctest