~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2006-04-20 23:22:19 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20060420232219-04cb6657e7e3aa73
Switch fix from ddaa

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")
 
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'):
61
66
            non_source.append(path)
62
67
    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")
 
68
    return not delta.has_changed(), non_source
 
69
 
 
70
def set_push_data(tree, location):
 
71
    push_file = file (tree.branch.control_files.controlfilename("x-push-data"), "wb")
91
72
    push_file.write("%s\n" % location)
92
73
 
93
 
def get_push_data(br):
 
74
def get_push_data(tree):
94
75
    """
95
 
    >>> br = temp_branch()
96
 
    >>> get_push_data(br) is None
 
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)
 
79
    >>> set_push_data(tree, 'http://somewhere')
 
80
    >>> get_push_data(tree)
100
81
    'http://somewhere'
101
 
    >>> rm_branch(br)
 
82
    >>> rm_tree(tree)
102
83
    """
103
 
    filename = br.controlfilename("x-push-data")
 
84
    filename = tree.branch.control_files.controlfilename("x-push-data")
104
85
    if not os.path.exists(filename):
105
86
        return None
106
87
    push_file = file (filename, "rb")
136
117
def rsync(source, target, ssh=False, excludes=(), silent=False, 
137
118
          rsync_name="rsync"):
138
119
    """
 
120
    >>> new_dir = tempfile.mkdtemp()
 
121
    >>> old_dir = os.getcwd()
 
122
    >>> os.chdir(new_dir)
139
123
    >>> rsync("a", "b", silent=True)
140
124
    Traceback (most recent call last):
141
 
    RsyncNoFile: No such file a
142
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
 
125
    RsyncNoFile: No such file...
 
126
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
143
127
    Traceback (most recent call last):
144
 
    RsyncNoFile: No such file a
145
 
    >>> 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")
146
130
    Traceback (most recent call last):
147
131
    NoRsync: rsyncc not found.
 
132
    >>> os.chdir(old_dir)
 
133
    >>> os.rmdir(new_dir)
148
134
    """
149
135
    cmd = [rsync_name, "-av", "--delete"]
150
136
    if ssh:
205
191
        raise RsyncUnknownStatus(proc.returncode)
206
192
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
207
193
 
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')
 
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')
211
198
 
212
199
 
213
200
def read_revision_history(fname):
226
213
    tempdir = tempfile.mkdtemp('push')
227
214
    try:
228
215
        history_fname = os.path.join(tempdir, 'revision-history')
229
 
        cmd = rsync(location+'.bzr/revision-history', history_fname,
230
 
                    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)
231
222
        history = read_revision_history(history_fname)
232
223
    finally:
233
224
        shutil.rmtree(tempdir)
250
241
    except RsyncNoFile:
251
242
        return True
252
243
 
253
 
def push(cur_branch, location=None, overwrite=False):
254
 
    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)
255
246
    if location is not None:
256
247
        if not location.endswith('/'):
257
248
            location += '/'
258
249
        push_location = location
259
250
    
260
251
    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)
 
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
 
256
 
 
257
    if push_location.find('://') != -1:
 
258
        raise bzrlib.errors.MustUseDecorated
 
259
 
 
260
    if push_location.find(':') == -1:
 
261
        raise bzrlib.errors.MustUseDecorated
 
262
 
 
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)
270
277
    if not overwrite:
271
278
        try:
272
 
            if not history_subset(push_location, cur_branch):
 
279
            if not history_subset(push_location, tree.branch):
273
280
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
274
281
                                                    " newer version of remote"
275
282
                                                    " branch.")
283
290
                " specified location.  Please ensure that"
284
291
                ' "%s" is of the form "machine:/path".' % push_location)
285
292
    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)
 
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
    expr = re.compile('<a[^>]*href="([^>]*)"[^>]*>', flags=re.I)
 
312
    for line in lines:
 
313
        match = expr.search(line)
 
314
        if match is None:
 
315
            continue
 
316
        url = match.group(1)
 
317
        if url.startswith('http://') or url.startswith('/') or '../' in url:
 
318
            continue
 
319
        if '?' in url:
 
320
            continue
 
321
        yield url.rstrip('/')
 
322
 
 
323
 
 
324
def iter_branches(t, lister=None):
 
325
    """Iterate through all the branches under a transport"""
 
326
    for bzrdir in iter_bzrdirs(t, lister):
 
327
        try:
 
328
            branch = bzrdir.open_branch()
 
329
            if branch.bzrdir is bzrdir:
 
330
                yield branch
 
331
        except (NotBranchError, UnsupportedFormatError):
 
332
            pass
 
333
 
 
334
 
 
335
def iter_branch_tree(t, lister=None):
 
336
    for bzrdir in iter_bzrdirs(t, lister):
 
337
        try:
 
338
            wt = bzrdir.open_workingtree()
 
339
            yield wt.branch, wt
 
340
        except NoWorkingTree, UnsupportedFormatError:
 
341
            try:
 
342
                branch = bzrdir.open_branch()
 
343
                if branch.bzrdir is bzrdir:
 
344
                    yield branch, None
 
345
            except (NotBranchError, UnsupportedFormatError):
 
346
                continue
 
347
 
 
348
 
 
349
def iter_bzrdirs(t, lister=None):
 
350
    if lister is None:
 
351
        def lister(t):
 
352
            return t.list_dir('.')
 
353
    try:
 
354
        bzrdir = bzrdir_from_transport(t)
 
355
        yield bzrdir
 
356
    except (NotBranchError, UnsupportedFormatError, TransportError,
 
357
            PermissionDenied):
 
358
        pass
 
359
    try:
 
360
        for directory in lister(t):
 
361
            if directory == ".bzr":
 
362
                continue
 
363
            try:
 
364
                subt = t.clone(directory)
 
365
            except UnicodeDecodeError:
 
366
                continue
 
367
            for bzrdir in iter_bzrdirs(subt, lister):
 
368
                yield bzrdir
 
369
    except (NoSuchFile, PermissionDenied, TransportError):
 
370
        pass
 
371
 
 
372
    
 
373
def bzrdir_from_transport(t):
 
374
    """Open a bzrdir from a transport (not a location)"""
 
375
    format = BzrDirFormat.find_format(t)
 
376
    BzrDir._check_supported(format, False)
 
377
    return format.open(t)
 
378
 
289
379
 
290
380
def run_tests():
291
381
    import doctest