110
99
def __init__(self, rsync_name):
111
100
Exception.__init__(self, "%s not found." % rsync_name)
114
102
def rsync(source, target, ssh=False, excludes=(), silent=False,
115
103
rsync_name="rsync"):
105
>>> new_dir = tempfile.mkdtemp()
106
>>> old_dir = os.getcwd()
107
>>> os.chdir(new_dir)
108
>>> rsync("a", "b", silent=True)
109
Traceback (most recent call last):
110
RsyncNoFile: No such file...
111
>>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
112
Traceback (most recent call last):
113
RsyncNoFile: No such file...
114
>>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
115
Traceback (most recent call last):
116
NoRsync: rsyncc not found.
117
>>> os.chdir(old_dir)
118
>>> os.rmdir(new_dir)
116
120
cmd = [rsync_name, "-av", "--delete"]
118
122
cmd.extend(('-e', 'ssh'))
182
186
return [l.rstrip('\r\n') for l in
183
187
codecs.open(fname, 'rb', 'utf-8').readlines()]
186
def read_revision_info(path):
187
"""Parse a last_revision file to determine revision_info"""
188
line = open(path, 'rb').readlines()[0].strip('\n')
189
revno, revision_id = line.split(' ', 1)
191
return revno, revision_id
194
189
class RsyncNoFile(Exception):
195
190
def __init__(self, path):
196
191
Exception.__init__(self, "No such file %s" % path)
199
194
def __init__(self):
200
195
Exception.__init__(self, "Error in rsync protocol data stream.")
203
class NotStandalone(BzrError):
205
_fmt = '%(location)s is not a standalone tree.'
208
def __init__(self, location):
209
BzrError.__init__(self, location=location)
212
def get_revision_history(location, _rsync):
197
def get_revision_history(location):
213
198
tempdir = tempfile.mkdtemp('push')
218
200
history_fname = os.path.join(tempdir, 'revision-history')
220
cmd = my_rsync(location+'.bzr/revision-history', history_fname,
202
cmd = rsync(location+'.bzr/revision-history', history_fname,
222
204
except RsyncNoFile:
223
205
cmd = rsync(location+'.bzr/branch/revision-history', history_fname,
227
209
shutil.rmtree(tempdir)
231
def get_revision_info(location, _rsync):
232
"""Get the revsision_info for an rsync-able branch"""
233
tempdir = tempfile.mkdtemp('push')
238
info_fname = os.path.join(tempdir, 'last-revision')
239
cmd = rsync(location+'.bzr/branch/last-revision', info_fname,
241
return read_revision_info(info_fname)
243
shutil.rmtree(tempdir)
246
def history_subset(location, branch, _rsync=None):
212
def history_subset(location, branch):
213
remote_history = get_revision_history(location)
247
214
local_history = branch.revision_history()
249
remote_history = get_revision_history(location, _rsync)
251
revno, revision_id = get_revision_info(location, _rsync)
252
if revision_id == _mod_revision.NULL_REVISION:
254
return bool(revision_id.decode('utf-8') in local_history)
256
if len(remote_history) > len(local_history):
215
if len(remote_history) > len(local_history):
217
for local, remote in zip(remote_history, local_history):
258
for local, remote in zip(remote_history, local_history):
264
222
def empty_or_absent(location):
268
226
except RsyncNoFile:
271
def rspush(tree, location=None, overwrite=False, working_tree=True,
278
if (tree.bzrdir.root_transport.base !=
279
tree.branch.bzrdir.root_transport.base):
280
raise NotStandalone(tree.bzrdir.root_transport.base)
281
if (tree.branch.get_bound_location() is not None):
282
raise NotStandalone(tree.bzrdir.root_transport.base)
283
if (tree.branch.repository.is_shared()):
284
raise NotStandalone(tree.bzrdir.root_transport.base)
285
push_location = get_push_data(tree)
286
if location is not None:
287
if not location.endswith('/'):
289
push_location = location
291
if push_location is None:
292
raise BzrCommandError("No rspush location known or specified.")
294
if (push_location.find('::') != -1):
299
if (push_location.find('://') != -1 or
300
push_location.find(':') == -1):
301
raise BzrCommandError("Invalid rsync path %r." % push_location)
304
clean, non_source = is_clean(tree)
306
raise bzrlib.errors.BzrCommandError(
307
'This tree has uncommitted changes or unknown'
308
' (?) files. Use "bzr status" to list them.')
310
final_exclusions = non_source[:]
313
final_exclusions = []
314
for path, status, kind, file_id, entry in wt.list_files():
315
final_exclusions.append(path)
317
final_exclusions.extend(exclusions)
320
if not history_subset(push_location, tree.branch,
322
raise bzrlib.errors.BzrCommandError(
323
"Local branch is not a newer version of remote"
326
if not empty_or_absent(push_location):
327
raise bzrlib.errors.BzrCommandError(
328
"Remote location is not a bzr branch (or empty"
330
except RsyncStreamIO:
331
raise bzrlib.errors.BzrCommandError("Rsync could not use the"
332
" specified location. Please ensure that"
333
' "%s" is of the form "machine:/path".' % push_location)
334
trace.note("Pushing to %s", push_location)
335
my_rsync(tree.basedir+'/', push_location, ssh=usessh,
336
excludes=final_exclusions)
338
set_push_data(tree, push_location)
229
def rspush(tree, location=None, overwrite=False, working_tree=True):
230
push_location = get_push_data(tree)
231
if location is not None:
232
if not location.endswith('/'):
234
push_location = location
236
if push_location is None:
237
raise BzrCommandError("No rspush location known or specified.")
239
if (push_location.find('::') != -1):
244
if (push_location.find('://') != -1 or
245
push_location.find(':') == -1):
246
raise BzrCommandError("Invalid rsync path %r." % push_location)
249
clean, non_source = is_clean(tree)
251
print """Error: This tree has uncommitted changes or unknown (?) files.
252
Use "bzr status" to list them."""
254
final_exclusions = non_source[:]
257
final_exclusions = []
258
for path, status, kind, file_id, entry in wt.list_files():
259
final_exclusions.append(path)
261
final_exclusions.extend(exclusions)
264
if not history_subset(push_location, tree.branch):
265
raise bzrlib.errors.BzrCommandError("Local branch is not a"
266
" newer version of remote"
269
if not empty_or_absent(push_location):
270
raise bzrlib.errors.BzrCommandError("Remote location is not a"
271
" bzr branch (or empty"
273
except RsyncStreamIO:
274
raise bzrlib.errors.BzrCommandError("Rsync could not use the"
275
" specified location. Please ensure that"
276
' "%s" is of the form "machine:/path".' % push_location)
277
print "Pushing to %s" % push_location
278
rsync(tree.basedir+'/', push_location, ssh=usessh,
279
excludes=final_exclusions)
281
set_push_data(tree, push_location)
343
284
def short_committer(committer):
370
306
yield url.rstrip('/')
373
def list_branches(t):
374
def is_inside(branch):
375
return bool(branch.base.startswith(t.base))
377
if t.base.startswith('http://'):
378
def evaluate(bzrdir):
309
def iter_branches(t, lister=None):
310
"""Iterate through all the branches under a transport"""
311
for bzrdir in iter_bzrdirs(t, lister):
313
branch = bzrdir.open_branch()
314
if branch.bzrdir is bzrdir:
316
except (NotBranchError, UnsupportedFormatError):
320
def iter_branch_tree(t, lister=None):
321
for bzrdir in iter_bzrdirs(t, lister):
323
wt = bzrdir.open_workingtree()
325
except NoWorkingTree, UnsupportedFormatError:
380
327
branch = bzrdir.open_branch()
381
if is_inside(branch):
385
except NotBranchError:
387
return [b for b in BzrDir.find_bzrdirs(t, list_current=apache_ls,
388
evaluate=evaluate) if b is not None]
389
elif not t.listable():
390
raise BzrCommandError("Can't list this type of location.")
391
return [b for b in BzrDir.find_branches(t) if is_inside(b)]
394
def evaluate_branch_tree(bzrdir):
396
tree, branch = bzrdir._get_tree_branch()
397
except NotBranchError:
400
return True, (branch, tree)
403
def iter_branch_tree(t, lister=None):
404
return (x for x in BzrDir.find_bzrdirs(t, evaluate=evaluate_branch_tree,
405
list_current=lister) if x is not None)
408
def open_from_url(location):
409
location = urlutils.normalize_url(location)
410
dirname, basename = urlutils.split(location)
411
if location.endswith('/') and not basename.endswith('/'):
413
return get_transport(dirname).get(basename)
328
if branch.bzrdir is bzrdir:
330
except (NotBranchError, UnsupportedFormatError):
334
def iter_bzrdirs(t, lister=None):
337
return t.list_dir('.')
339
bzrdir = bzrdir_from_transport(t)
341
except (NotBranchError, UnsupportedFormatError, TransportError,
345
for directory in lister(t):
346
if directory == ".bzr":
349
subt = t.clone(directory)
350
except UnicodeDecodeError:
352
for bzrdir in iter_bzrdirs(subt, lister):
354
except (NoSuchFile, PermissionDenied, TransportError):
358
def bzrdir_from_transport(t):
359
"""Open a bzrdir from a transport (not a location)"""
360
format = BzrDirFormat.find_format(t)
361
BzrDir._check_supported(format, False)
362
return format.open(t)