~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2005-10-03 16:55:41 UTC
  • Revision ID: abentley@panoramicfeedback.com-20051003165541-407522f389c7b955
Fixed last_revision calls

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)
50
 
    (True, [u'foo'])
51
 
    >>> bzrlib.add.smart_add_tree(tree, [tree.basedir])
52
 
    ([u'foo'], {})
53
 
    >>> is_clean(tree)
 
44
    >>> is_clean(br)
 
45
    (True, ['foo'])
 
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
    added foo
 
52
    >>> is_clean(br)
58
53
    (True, [])
59
 
    >>> rm_tree(tree)
 
54
    >>> rm_branch(br)
60
55
    """
61
56
    from bzrlib.diff import compare_trees
62
 
    old_tree = cur_tree.basis_tree()
63
 
    new_tree = cur_tree
 
57
    old_tree = cur_branch.basis_tree()
 
58
    new_tree = cur_branch.working_tree()
64
59
    non_source = []
65
 
    for path, file_class, kind, file_id, entry in new_tree.list_files():
 
60
    for path, file_class, kind, file_id in new_tree.list_files():
66
61
        if file_class in ('?', 'I'):
67
62
            non_source.append(path)
68
63
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
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
 
64
    if len(delta.added) > 0 or len(delta.removed) > 0 or \
 
65
        len(delta.modified) > 0:
 
66
        return False, non_source
 
67
    return True, non_source 
 
68
 
 
69
def set_pull_data(br, location, rev_id):
 
70
    pull_file = file (br.controlfilename("x-pull-data"), "wb")
 
71
    pull_file.write("%s\n%s\n" % (location, rev_id))
 
72
 
 
73
def get_pull_data(br):
 
74
    """
 
75
    >>> br = temp_branch()
 
76
    >>> get_pull_data(br)
 
77
    (None, None)
 
78
    >>> set_pull_data(br, 'http://somewhere', '888-777')
 
79
    >>> get_pull_data(br)
 
80
    ('http://somewhere', '888-777')
 
81
    >>> rm_branch(br)
 
82
    """
 
83
    filename = br.controlfilename("x-pull-data")
 
84
    if not os.path.exists(filename):
 
85
        return (None, None)
 
86
    pull_file = file (filename, "rb")
 
87
    location, rev_id = [f.rstrip('\n') for f in pull_file]
 
88
    return location, rev_id
 
89
 
 
90
def set_push_data(br, location):
 
91
    push_file = file (br.controlfilename("x-push-data"), "wb")
 
92
    push_file.write("%s\n" % location)
 
93
 
 
94
def get_push_data(br):
 
95
    """
 
96
    >>> br = temp_branch()
 
97
    >>> get_push_data(br) is None
78
98
    True
79
 
    >>> set_push_data(tree, 'http://somewhere')
80
 
    >>> get_push_data(tree)
81
 
    u'http://somewhere'
82
 
    >>> rm_tree(tree)
 
99
    >>> set_push_data(br, 'http://somewhere')
 
100
    >>> get_push_data(br)
 
101
    'http://somewhere'
 
102
    >>> rm_branch(br)
83
103
    """
84
 
    try:
85
 
        location = tree.branch.control_files.get_utf8('x-push-data').read()
86
 
    except NoSuchFile:
 
104
    filename = br.controlfilename("x-push-data")
 
105
    if not os.path.exists(filename):
87
106
        return None
88
 
    return location.rstrip('\n')
 
107
    push_file = file (filename, "rb")
 
108
    (location,) = [f.rstrip('\n') for f in push_file]
 
109
    return location
89
110
 
90
111
"""
91
112
>>> shell_escape('hello')
116
137
def rsync(source, target, ssh=False, excludes=(), silent=False, 
117
138
          rsync_name="rsync"):
118
139
    """
119
 
    >>> new_dir = tempfile.mkdtemp()
120
 
    >>> old_dir = os.getcwd()
121
 
    >>> os.chdir(new_dir)
122
140
    >>> rsync("a", "b", silent=True)
123
141
    Traceback (most recent call last):
124
 
    RsyncNoFile: No such file...
125
 
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
 
142
    RsyncNoFile: No such file a
 
143
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
126
144
    Traceback (most recent call last):
127
 
    RsyncNoFile: No such file...
128
 
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
 
145
    RsyncNoFile: No such file a
 
146
    >>> rsync("a", "b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
129
147
    Traceback (most recent call last):
130
148
    NoRsync: rsyncc not found.
131
 
    >>> os.chdir(old_dir)
132
 
    >>> os.rmdir(new_dir)
133
149
    """
134
150
    cmd = [rsync_name, "-av", "--delete"]
135
151
    if ssh:
190
206
        raise RsyncUnknownStatus(proc.returncode)
191
207
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
192
208
 
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')
 
209
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data', 
 
210
              '.bzr/x-pull', '.bzr/pull', '.bzr/stat-cache',
 
211
              '.bzr/x-rsync-data')
197
212
 
198
213
 
199
214
def read_revision_history(fname):
212
227
    tempdir = tempfile.mkdtemp('push')
213
228
    try:
214
229
        history_fname = os.path.join(tempdir, 'revision-history')
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)
 
230
        cmd = rsync(location+'.bzr/revision-history', history_fname,
 
231
                    silent=True)
221
232
        history = read_revision_history(history_fname)
222
233
    finally:
223
234
        shutil.rmtree(tempdir)
240
251
    except RsyncNoFile:
241
252
        return True
242
253
 
243
 
def rspush(tree, location=None, overwrite=False, working_tree=True):
244
 
    push_location = get_push_data(tree)
 
254
def push(cur_branch, location=None, overwrite=False):
 
255
    push_location = get_push_data(cur_branch)
245
256
    if location is not None:
246
257
        if not location.endswith('/'):
247
258
            location += '/'
248
259
        push_location = location
249
260
    
250
261
    if push_location is None:
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)
 
262
        print "No push location saved.  Please specify one on the command line."
 
263
        sys.exit(1)
 
264
 
 
265
    clean, non_source = is_clean(cur_branch)
 
266
    if not clean:
 
267
        print """Error: This tree has uncommitted changes or unknown (?) files.
 
268
Use "bzr status" to list them."""
 
269
        sys.exit(1)
 
270
    non_source.extend(exclusions)
271
271
    if not overwrite:
272
272
        try:
273
 
            if not history_subset(push_location, tree.branch):
 
273
            if not history_subset(push_location, cur_branch):
274
274
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
275
275
                                                    " newer version of remote"
276
276
                                                    " branch.")
284
284
                " specified location.  Please ensure that"
285
285
                ' "%s" is of the form "machine:/path".' % push_location)
286
286
    print "Pushing to %s" % 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
 
 
 
287
    rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
 
288
 
 
289
    set_push_data(cur_branch, push_location)
373
290
 
374
291
def run_tests():
375
292
    import doctest