~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2006-11-23 18:06:43 UTC
  • Revision ID: abentley@panoramicfeedback.com-20061123180643-6ry3xqozdbn4cfhy
Update docs

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