28
from bzrlib.errors import (BzrCommandError, NotBranchError, NoSuchFile,
29
UnsupportedFormatError, TransportError,
30
NoWorkingTree, PermissionDenied)
31
from bzrlib.bzrdir import BzrDir, BzrDirFormat
27
from bzrlib import revision as _mod_revision, trace, urlutils
28
from bzrlib.errors import (
34
from bzrlib.bzrdir import BzrDir
35
from bzrlib.transport import get_transport
34
38
dirname = tempfile.mkdtemp("temp-branch")
99
104
def __init__(self, rsync_name):
100
105
Exception.__init__(self, "%s not found." % rsync_name)
102
def rsync(source, target, ssh=False, excludes=(), silent=False,
108
def rsync(source, target, ssh=False, excludes=(), silent=False,
103
109
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)
120
110
cmd = [rsync_name, "-av", "--delete"]
122
112
cmd.extend(('-e', 'ssh'))
176
166
raise RsyncUnknownStatus(proc.returncode)
177
167
return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
179
exclusions = ('.bzr/x-push-data', '.bzr/branch/x-push/data', '.bzr/parent',
169
exclusions = ('.bzr/x-push-data', '.bzr/branch/x-push/data', '.bzr/parent',
180
170
'.bzr/branch/parent', '.bzr/x-pull-data', '.bzr/x-pull',
181
171
'.bzr/pull', '.bzr/stat-cache', '.bzr/x-rsync-data',
182
172
'.bzr/basis-inventory', '.bzr/inventory.backup.weave')
186
176
return [l.rstrip('\r\n') for l in
187
177
codecs.open(fname, 'rb', 'utf-8').readlines()]
180
def read_revision_info(path):
181
"""Parse a last_revision file to determine revision_info"""
182
line = open(path, 'rb').readlines()[0].strip('\n')
183
revno, revision_id = line.split(' ', 1)
185
return revno, revision_id
189
188
class RsyncNoFile(Exception):
190
189
def __init__(self, path):
191
190
Exception.__init__(self, "No such file %s" % path)
194
193
def __init__(self):
195
194
Exception.__init__(self, "Error in rsync protocol data stream.")
197
def get_revision_history(location):
197
class NotStandalone(BzrError):
199
_fmt = '%(location)s is not a standalone tree.'
202
def __init__(self, location):
203
BzrError.__init__(self, location=location)
206
def get_revision_history(location, _rsync):
198
207
tempdir = tempfile.mkdtemp('push')
200
212
history_fname = os.path.join(tempdir, 'revision-history')
202
cmd = rsync(location+'.bzr/revision-history', history_fname,
214
cmd = my_rsync(location+'.bzr/revision-history', history_fname,
204
216
except RsyncNoFile:
205
217
cmd = rsync(location+'.bzr/branch/revision-history', history_fname,
209
221
shutil.rmtree(tempdir)
212
def history_subset(location, branch):
213
remote_history = get_revision_history(location)
225
def get_revision_info(location, _rsync):
226
"""Get the revsision_info for an rsync-able branch"""
227
tempdir = tempfile.mkdtemp('push')
232
info_fname = os.path.join(tempdir, 'last-revision')
233
cmd = rsync(location+'.bzr/branch/last-revision', info_fname,
235
return read_revision_info(info_fname)
237
shutil.rmtree(tempdir)
240
def history_subset(location, branch, _rsync=None):
214
241
local_history = branch.revision_history()
215
if len(remote_history) > len(local_history):
217
for local, remote in zip(remote_history, local_history):
243
remote_history = get_revision_history(location, _rsync)
245
revno, revision_id = get_revision_info(location, _rsync)
246
if revision_id == _mod_revision.NULL_REVISION:
248
return bool(revision_id.decode('utf-8') in local_history)
250
if len(remote_history) > len(local_history):
252
for local, remote in zip(remote_history, local_history):
222
258
def empty_or_absent(location):
226
262
except RsyncNoFile:
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)
265
def rspush(tree, location=None, overwrite=False, working_tree=True,
272
if (tree.bzrdir.root_transport.base !=
273
tree.branch.bzrdir.root_transport.base):
274
raise NotStandalone(tree.bzrdir.root_transport.base)
275
if (tree.branch.get_bound_location() is not None):
276
raise NotStandalone(tree.bzrdir.root_transport.base)
277
if (tree.branch.repository.is_shared()):
278
raise NotStandalone(tree.bzrdir.root_transport.base)
279
push_location = get_push_data(tree)
280
if location is not None:
281
if not location.endswith('/'):
283
push_location = location
285
if push_location is None:
286
raise BzrCommandError("No rspush location known or specified.")
288
if (push_location.find('::') != -1):
293
if (push_location.find('://') != -1 or
294
push_location.find(':') == -1):
295
raise BzrCommandError("Invalid rsync path %r." % push_location)
298
clean, non_source = is_clean(tree)
300
raise BzrCommandError(
301
'This tree has uncommitted changes or unknown'
302
' (?) files. Use "bzr status" to list them.')
304
final_exclusions = non_source[:]
307
final_exclusions = []
308
for path, status, kind, file_id, entry in wt.list_files():
309
final_exclusions.append(path)
311
final_exclusions.extend(exclusions)
314
if not history_subset(push_location, tree.branch,
316
raise BzrCommandError(
317
"Local branch is not a newer version of remote"
320
if not empty_or_absent(push_location):
321
raise BzrCommandError(
322
"Remote location is not a bzr branch (or empty"
324
except RsyncStreamIO:
325
raise BzrCommandError("Rsync could not use the"
326
" specified location. Please ensure that"
327
' "%s" is of the form "machine:/path".' % push_location)
328
trace.note("Pushing to %s", push_location)
329
my_rsync(tree.basedir+'/', push_location, ssh=usessh,
330
excludes=final_exclusions)
332
set_push_data(tree, push_location)
284
337
def short_committer(committer):
306
364
yield url.rstrip('/')
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):
367
def list_branches(t):
368
def is_inside(branch):
369
return bool(branch.base.startswith(t.base))
371
if t.base.startswith('http://'):
372
def evaluate(bzrdir):
374
branch = bzrdir.open_branch()
375
if is_inside(branch):
379
except NotBranchError:
381
return [b for b in BzrDir.find_bzrdirs(t, list_current=apache_ls,
382
evaluate=evaluate) if b is not None]
383
elif not t.listable():
384
raise BzrCommandError("Can't list this type of location.")
385
return [b for b in BzrDir.find_branches(t) if is_inside(b)]
388
def evaluate_branch_tree(bzrdir):
390
tree, branch = bzrdir._get_tree_branch()
391
except NotBranchError:
394
return True, (branch, tree)
320
397
def iter_branch_tree(t, lister=None):
321
for bzrdir in iter_bzrdirs(t, lister):
323
wt = bzrdir.open_workingtree()
325
except NoWorkingTree, UnsupportedFormatError:
327
branch = bzrdir.open_branch()
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)
398
return (x for x in BzrDir.find_bzrdirs(t, evaluate=evaluate_branch_tree,
399
list_current=lister) if x is not None)
402
def open_from_url(location):
403
location = urlutils.normalize_url(location)
404
dirname, basename = urlutils.split(location)
405
if location.endswith('/') and not basename.endswith('/'):
407
return get_transport(dirname).get(basename)