~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2005-10-27 05:19:16 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20051027051916-0c47bc829ce54fdc
Got command completion working

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