~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2005-11-10 20:15:27 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20051110201527-8caa84ae529a1871
Added Daniel Silverstone to credits

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)
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")
56
 
    >>> is_clean(tree)
 
50
    >>> br.commit("added file")
 
51
    >>> is_clean(br)
57
52
    (True, [])
58
 
    >>> rm_tree(tree)
 
53
    >>> rm_branch(br)
59
54
    """
60
55
    from bzrlib.diff import compare_trees
61
 
    old_tree = cur_tree.basis_tree()
62
 
    new_tree = cur_tree
 
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'):
67
62
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
68
63
    return not delta.has_changed(), non_source
69
64
 
70
 
def set_push_data(tree, location):
71
 
    push_file = file (tree.branch.control_files.controlfilename("x-push-data"), "wb")
 
65
def set_pull_data(br, location, rev_id):
 
66
    pull_file = file (br.controlfilename("x-pull-data"), "wb")
 
67
    pull_file.write("%s\n%s\n" % (location, rev_id))
 
68
 
 
69
def get_pull_data(br):
 
70
    """
 
71
    >>> br = temp_branch()
 
72
    >>> get_pull_data(br)
 
73
    (None, None)
 
74
    >>> set_pull_data(br, 'http://somewhere', '888-777')
 
75
    >>> get_pull_data(br)
 
76
    ('http://somewhere', '888-777')
 
77
    >>> rm_branch(br)
 
78
    """
 
79
    filename = br.controlfilename("x-pull-data")
 
80
    if not os.path.exists(filename):
 
81
        return (None, None)
 
82
    pull_file = file (filename, "rb")
 
83
    location, rev_id = [f.rstrip('\n') for f in pull_file]
 
84
    return location, rev_id
 
85
 
 
86
def set_push_data(br, location):
 
87
    push_file = file (br.controlfilename("x-push-data"), "wb")
72
88
    push_file.write("%s\n" % location)
73
89
 
74
 
def get_push_data(tree):
 
90
def get_push_data(br):
75
91
    """
76
 
    >>> tree = temp_tree()
77
 
    >>> get_push_data(tree) is None
 
92
    >>> br = temp_branch()
 
93
    >>> get_push_data(br) is None
78
94
    True
79
 
    >>> set_push_data(tree, 'http://somewhere')
80
 
    >>> get_push_data(tree)
 
95
    >>> set_push_data(br, 'http://somewhere')
 
96
    >>> get_push_data(br)
81
97
    'http://somewhere'
82
 
    >>> rm_tree(tree)
 
98
    >>> rm_branch(br)
83
99
    """
84
 
    filename = tree.branch.control_files.controlfilename("x-push-data")
 
100
    filename = br.controlfilename("x-push-data")
85
101
    if not os.path.exists(filename):
86
102
        return None
87
103
    push_file = file (filename, "rb")
117
133
def rsync(source, target, ssh=False, excludes=(), silent=False, 
118
134
          rsync_name="rsync"):
119
135
    """
120
 
    >>> new_dir = tempfile.mkdtemp()
121
 
    >>> old_dir = os.getcwd()
122
 
    >>> os.chdir(new_dir)
123
136
    >>> rsync("a", "b", silent=True)
124
137
    Traceback (most recent call last):
125
 
    RsyncNoFile: No such file...
126
 
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
 
138
    RsyncNoFile: No such file a
 
139
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
127
140
    Traceback (most recent call last):
128
 
    RsyncNoFile: No such file...
129
 
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
 
141
    RsyncNoFile: No such file a
 
142
    >>> rsync("a", "b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
130
143
    Traceback (most recent call last):
131
144
    NoRsync: rsyncc not found.
132
 
    >>> os.chdir(old_dir)
133
 
    >>> os.rmdir(new_dir)
134
145
    """
135
146
    cmd = [rsync_name, "-av", "--delete"]
136
147
    if ssh:
191
202
        raise RsyncUnknownStatus(proc.returncode)
192
203
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
193
204
 
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')
 
205
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data', 
 
206
              '.bzr/x-pull', '.bzr/pull', '.bzr/stat-cache',
 
207
              '.bzr/x-rsync-data')
198
208
 
199
209
 
200
210
def read_revision_history(fname):
213
223
    tempdir = tempfile.mkdtemp('push')
214
224
    try:
215
225
        history_fname = os.path.join(tempdir, 'revision-history')
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)
 
226
        cmd = rsync(location+'.bzr/revision-history', history_fname,
 
227
                    silent=True)
222
228
        history = read_revision_history(history_fname)
223
229
    finally:
224
230
        shutil.rmtree(tempdir)
241
247
    except RsyncNoFile:
242
248
        return True
243
249
 
244
 
def push(tree, location=None, overwrite=False, working_tree=True):
245
 
    push_location = get_push_data(tree)
 
250
def push(cur_branch, location=None, overwrite=False):
 
251
    push_location = get_push_data(cur_branch)
246
252
    if location is not None:
247
253
        if not location.endswith('/'):
248
254
            location += '/'
249
255
        push_location = location
250
256
    
251
257
    if push_location is None:
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
 
258
        raise bzrlib.errors.MustUseDecorated
256
259
 
257
260
    if push_location.find('://') != -1:
258
261
        raise bzrlib.errors.MustUseDecorated
260
263
    if push_location.find(':') == -1:
261
264
        raise bzrlib.errors.MustUseDecorated
262
265
 
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)
 
266
    clean, non_source = is_clean(cur_branch)
 
267
    if not clean:
 
268
        print """Error: This tree has uncommitted changes or unknown (?) files.
 
269
Use "bzr status" to list them."""
 
270
        sys.exit(1)
 
271
    non_source.extend(exclusions)
277
272
    if not overwrite:
278
273
        try:
279
 
            if not history_subset(push_location, tree.branch):
 
274
            if not history_subset(push_location, cur_branch):
280
275
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
281
276
                                                    " newer version of remote"
282
277
                                                    " branch.")
290
285
                " specified location.  Please ensure that"
291
286
                ' "%s" is of the form "machine:/path".' % push_location)
292
287
    print "Pushing to %s" % 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
 
    for line in lines:
312
 
        if line.startswith(apache_dir):
313
 
            url = line[len(apache_dir):].split('"')[0]
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
 
def iter_bzrdirs(t, lister=None):
342
 
    if lister is None:
343
 
        def lister(t):
344
 
            return t.list_dir('.')
345
 
    try:
346
 
        bzrdir = bzrdir_from_transport(t)
347
 
        yield bzrdir
348
 
    except (NotBranchError, UnsupportedFormatError, TransportError):
349
 
        pass
350
 
    try:
351
 
        for directory in lister(t):
352
 
            if directory == ".bzr":
353
 
                continue
354
 
            subt = t.clone(directory)
355
 
            for bzrdir in iter_bzrdirs(subt):
356
 
                yield bzrdir
357
 
    except NoSuchFile:
358
 
        pass
359
 
    except TransportError, e:
360
 
        pass
361
 
 
362
 
    
363
 
def bzrdir_from_transport(t):
364
 
    """Open a bzrdir from a transport (not a location)"""
365
 
    format = BzrDirFormat.find_format(t)
366
 
    BzrDir._check_supported(format, False)
367
 
    return format.open(t)
368
 
 
 
288
    rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
 
289
 
 
290
    set_push_data(cur_branch, push_location)
369
291
 
370
292
def run_tests():
371
293
    import doctest