~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2007-01-15 15:19:20 UTC
  • Revision ID: abentley@panoramicfeedback.com-20070115151920-pb4eto22dzk34co2
Add --silent option to patch

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
 
import re
28
 
 
29
 
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():
30
34
    dirname = tempfile.mkdtemp("temp-branch")
31
 
    return bzrlib.branch.Branch.initialize(dirname)
32
 
 
33
 
def rm_branch(br):
34
 
    shutil.rmtree(br.base)
35
 
 
36
 
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):
37
41
    """
38
42
    Return true if no files are modifed or unknown
39
43
    >>> import bzrlib.add
40
 
    >>> br = temp_branch()
41
 
    >>> is_clean(br)
 
44
    >>> tree = temp_tree()
 
45
    >>> is_clean(tree)
42
46
    (True, [])
43
 
    >>> fooname = os.path.join(br.base, "foo")
 
47
    >>> fooname = os.path.join(tree.basedir, "foo")
44
48
    >>> file(fooname, "wb").write("bar")
45
 
    >>> is_clean(br)
 
49
    >>> is_clean(tree)
46
50
    (True, [u'foo'])
47
 
    >>> br.working_tree().add(["foo"])
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.working_tree().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)
 
67
    delta = new_tree.changes_from(old_tree, want_unchanged=False)
63
68
    return not delta.has_changed(), non_source
64
69
 
65
 
def set_push_data(br, location):
66
 
    push_file = file (br.control_files.controlfilename("x-push-data"), "wb")
67
 
    push_file.write("%s\n" % location)
 
70
def set_push_data(tree, location):
 
71
    tree.branch.control_files.put_utf8("x-push-data", "%s\n" % location)
68
72
 
69
 
def get_push_data(br):
 
73
def get_push_data(tree):
70
74
    """
71
 
    >>> br = temp_branch()
72
 
    >>> get_push_data(br) is None
 
75
    >>> tree = temp_tree()
 
76
    >>> get_push_data(tree) is None
73
77
    True
74
 
    >>> set_push_data(br, 'http://somewhere')
75
 
    >>> get_push_data(br)
76
 
    'http://somewhere'
77
 
    >>> rm_branch(br)
 
78
    >>> set_push_data(tree, 'http://somewhere')
 
79
    >>> get_push_data(tree)
 
80
    u'http://somewhere'
 
81
    >>> rm_tree(tree)
78
82
    """
79
 
    filename = br.control_files.controlfilename("x-push-data")
80
 
    if not os.path.exists(filename):
 
83
    try:
 
84
        location = tree.branch.control_files.get_utf8('x-push-data').read()
 
85
    except NoSuchFile:
81
86
        return None
82
 
    push_file = file (filename, "rb")
83
 
    (location,) = [f.rstrip('\n') for f in push_file]
84
 
    return location
 
87
    return location.rstrip('\n')
85
88
 
86
89
"""
87
90
>>> shell_escape('hello')
112
115
def rsync(source, target, ssh=False, excludes=(), silent=False, 
113
116
          rsync_name="rsync"):
114
117
    """
 
118
    >>> new_dir = tempfile.mkdtemp()
 
119
    >>> old_dir = os.getcwd()
 
120
    >>> os.chdir(new_dir)
115
121
    >>> rsync("a", "b", silent=True)
116
122
    Traceback (most recent call last):
117
 
    RsyncNoFile: No such file a
118
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
 
123
    RsyncNoFile: No such file...
 
124
    >>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
119
125
    Traceback (most recent call last):
120
 
    RsyncNoFile: No such file a
121
 
    >>> 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")
122
128
    Traceback (most recent call last):
123
129
    NoRsync: rsyncc not found.
 
130
    >>> os.chdir(old_dir)
 
131
    >>> os.rmdir(new_dir)
124
132
    """
125
133
    cmd = [rsync_name, "-av", "--delete"]
126
134
    if ssh:
181
189
        raise RsyncUnknownStatus(proc.returncode)
182
190
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
183
191
 
184
 
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data', 
185
 
              '.bzr/x-pull', '.bzr/pull', '.bzr/stat-cache',
186
 
              '.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')
187
196
 
188
197
 
189
198
def read_revision_history(fname):
202
211
    tempdir = tempfile.mkdtemp('push')
203
212
    try:
204
213
        history_fname = os.path.join(tempdir, 'revision-history')
205
 
        cmd = rsync(location+'.bzr/revision-history', history_fname,
206
 
                    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)
207
220
        history = read_revision_history(history_fname)
208
221
    finally:
209
222
        shutil.rmtree(tempdir)
226
239
    except RsyncNoFile:
227
240
        return True
228
241
 
229
 
def push(cur_branch, location=None, overwrite=False, working_tree=True):
230
 
    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)
231
244
    if location is not None:
232
245
        if not location.endswith('/'):
233
246
            location += '/'
234
247
        push_location = location
235
248
    
236
249
    if push_location is None:
237
 
        raise bzrlib.errors.MustUseDecorated
238
 
 
239
 
    if push_location.find('://') != -1:
240
 
        raise bzrlib.errors.MustUseDecorated
241
 
 
242
 
    if push_location.find(':') == -1:
243
 
        raise bzrlib.errors.MustUseDecorated
244
 
 
245
 
    clean, non_source = is_clean(cur_branch)
246
 
    if not clean:
247
 
        print """Error: This tree has uncommitted changes or unknown (?) files.
248
 
Use "bzr status" to list them."""
249
 
        sys.exit(1)
 
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
 
250
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)
251
267
        final_exclusions = non_source[:]
252
268
    else:
253
 
        wt = cur_branch.working_tree()
 
269
        wt = tree
254
270
        final_exclusions = []
255
271
        for path, status, kind, file_id, entry in wt.list_files():
256
272
            final_exclusions.append(path)
258
274
    final_exclusions.extend(exclusions)
259
275
    if not overwrite:
260
276
        try:
261
 
            if not history_subset(push_location, cur_branch):
 
277
            if not history_subset(push_location, tree.branch):
262
278
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
263
279
                                                    " newer version of remote"
264
280
                                                    " branch.")
272
288
                " specified location.  Please ensure that"
273
289
                ' "%s" is of the form "machine:/path".' % push_location)
274
290
    print "Pushing to %s" % push_location
275
 
    rsync(cur_branch.base+'/', push_location, ssh=True, 
 
291
    rsync(tree.basedir+'/', push_location, ssh=usessh, 
276
292
          excludes=final_exclusions)
277
293
 
278
 
    set_push_data(cur_branch, push_location)
 
294
    set_push_data(tree, push_location)
 
295
 
279
296
 
280
297
def short_committer(committer):
281
298
    new_committer = re.sub('<.*>', '', committer).strip(' ')
284
301
    return new_committer
285
302
 
286
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
 
 
377
 
287
378
def run_tests():
288
379
    import doctest
289
380
    result = doctest.testmod()