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
23
24
from subprocess import Popen, PIPE
28
from bzrlib.errors import (BzrCommandError, NotBranchError, NoSuchFile,
29
UnsupportedFormatError, TransportError,
30
NoWorkingTree, PermissionDenied)
31
from bzrlib.bzrdir import BzrDir, BzrDirFormat
34
28
dirname = tempfile.mkdtemp("temp-branch")
35
return BzrDir.create_standalone_workingtree(dirname)
38
shutil.rmtree(tree.basedir)
40
def is_clean(cur_tree):
29
return bzrlib.branch.Branch.initialize(dirname)
32
shutil.rmtree(br.base)
34
def is_clean(cur_branch):
42
36
Return true if no files are modifed or unknown
43
37
>>> import bzrlib.add
44
>>> tree = temp_tree()
38
>>> br = temp_branch()
47
>>> fooname = os.path.join(tree.basedir, "foo")
41
>>> fooname = os.path.join(br.base, "foo")
48
42
>>> file(fooname, "wb").write("bar")
51
>>> bzrlib.add.smart_add_tree(tree, [tree.basedir])
45
>>> bzrlib.add.smart_add_branch(br, [br.base])
55
>>> tree.commit("added file", rev_id='commit-id')
49
>>> br.commit("added file")
61
55
from bzrlib.diff import compare_trees
62
old_tree = cur_tree.basis_tree()
56
old_tree = cur_branch.basis_tree()
57
new_tree = cur_branch.working_tree()
65
for path, file_class, kind, file_id, entry in new_tree.list_files():
59
for path, file_class, kind, file_id in new_tree.list_files():
66
60
if file_class in ('?', 'I'):
67
61
non_source.append(path)
68
62
delta = compare_trees(old_tree, new_tree, want_unchanged=False)
69
return not delta.has_changed(), non_source
71
def set_push_data(tree, location):
72
tree.branch.control_files.put_utf8("x-push-data", "%s\n" % location)
74
def get_push_data(tree):
76
>>> tree = temp_tree()
77
>>> get_push_data(tree) is None
63
if len(delta.added) > 0 or len(delta.removed) > 0 or \
64
len(delta.modified) > 0:
65
return False, non_source
66
return True, non_source
68
def set_pull_data(br, location, rev_id):
69
pull_file = file (br.controlfilename("x-pull-data"), "wb")
70
pull_file.write("%s\n%s\n" % (location, rev_id))
72
def get_pull_data(br):
74
>>> br = temp_branch()
77
>>> set_pull_data(br, 'http://somewhere', '888-777')
79
('http://somewhere', '888-777')
82
filename = br.controlfilename("x-pull-data")
83
if not os.path.exists(filename):
85
pull_file = file (filename, "rb")
86
location, rev_id = [f.rstrip('\n') for f in pull_file]
87
return location, rev_id
89
def set_push_data(br, location):
90
push_file = file (br.controlfilename("x-push-data"), "wb")
91
push_file.write("%s\n" % location)
93
def get_push_data(br):
95
>>> br = temp_branch()
96
>>> get_push_data(br) is None
79
>>> set_push_data(tree, 'http://somewhere')
80
>>> get_push_data(tree)
98
>>> set_push_data(br, 'http://somewhere')
85
location = tree.branch.control_files.get_utf8('x-push-data').read()
103
filename = br.controlfilename("x-push-data")
104
if not os.path.exists(filename):
88
return location.rstrip('\n')
106
push_file = file (filename, "rb")
107
(location,) = [f.rstrip('\n') for f in push_file]
91
111
>>> shell_escape('hello')
109
129
def __init__(self, status):
110
130
Exception.__init__(self, "Unknown status: %d" % status)
112
class NoRsync(Exception):
113
def __init__(self, rsync_name):
114
Exception.__init__(self, "%s not found." % rsync_name)
116
def rsync(source, target, ssh=False, excludes=(), silent=False,
119
>>> new_dir = tempfile.mkdtemp()
120
>>> old_dir = os.getcwd()
121
>>> os.chdir(new_dir)
122
>>> rsync("a", "b", silent=True)
123
Traceback (most recent call last):
124
RsyncNoFile: No such file...
125
>>> rsync(new_dir + "/a", new_dir + "/b", excludes=("*.py",), silent=True)
126
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")
129
Traceback (most recent call last):
130
NoRsync: rsyncc not found.
131
>>> os.chdir(old_dir)
132
>>> os.rmdir(new_dir)
134
cmd = [rsync_name, "-av", "--delete"]
132
def rsync(source, target, ssh=False, excludes=(), silent=False):
134
>>> real_system = os.system
135
>>> os.system = sys.stdout.write
137
['rsync', '-av', '--delete', 'a', 'b']
138
>>> rsync("a", "b", excludes=("*.py",))
139
['rsync', '-av', '--delete', '--exclude-from', '-', 'a', 'b']
140
>>> os.system = real_system
142
cmd = ["rsync", "-av", "--delete"]
136
144
cmd.extend(('-e', 'ssh'))
137
145
if len(excludes) > 0:
182
183
proc.stderr.read()
183
184
proc.stderr.close()
185
if proc.returncode == 12:
186
raise RsyncStreamIO()
187
elif proc.returncode == 23:
186
if proc.returncode == 23:
188
187
raise RsyncNoFile(source)
189
188
elif proc.returncode != 0:
190
189
raise RsyncUnknownStatus(proc.returncode)
191
190
return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
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')
192
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data',
193
'.bzr/pull', '.bzr/stat-cache', '.bzr/x-rsync-data')
199
196
def read_revision_history(fname):
204
201
def __init__(self, path):
205
202
Exception.__init__(self, "No such file %s" % path)
207
class RsyncStreamIO(Exception):
209
Exception.__init__(self, "Error in rsync protocol data stream.")
211
204
def get_revision_history(location):
212
205
tempdir = tempfile.mkdtemp('push')
214
207
history_fname = os.path.join(tempdir, 'revision-history')
216
cmd = rsync(location+'.bzr/revision-history', history_fname,
219
cmd = rsync(location+'.bzr/branch/revision-history', history_fname,
208
cmd = rsync(location+'.bzr/revision-history', history_fname,
221
210
history = read_revision_history(history_fname)
223
212
shutil.rmtree(tempdir)
240
229
except RsyncNoFile:
243
def rspush(tree, location=None, overwrite=False, working_tree=True):
244
push_location = get_push_data(tree)
232
def push(cur_branch, location=None, overwrite=False):
233
push_location = get_push_data(cur_branch)
245
234
if location is not None:
246
235
if not location.endswith('/'):
248
237
push_location = location
250
239
if push_location is None:
251
raise BzrCommandError("No rspush location known or specified.")
253
if (push_location.find('://') != -1 or
254
push_location.find(':') == -1):
255
raise BzrCommandError("Invalid rsync path %r." % push_location)
258
clean, non_source = is_clean(tree)
260
print """Error: This tree has uncommitted changes or unknown (?) files.
261
Use "bzr status" to list them."""
263
final_exclusions = non_source[:]
266
final_exclusions = []
267
for path, status, kind, file_id, entry in wt.list_files():
268
final_exclusions.append(path)
270
final_exclusions.extend(exclusions)
240
print "No push location saved. Please specify one on the command line."
243
clean, non_source = is_clean(cur_branch)
245
print """Error: This tree has uncommitted changes or unknown (?) files.
246
Use "bzr status" to list them."""
248
non_source.extend(exclusions)
271
249
if not overwrite:
273
if not history_subset(push_location, tree.branch):
251
if not history_subset(push_location, cur_branch):
274
252
raise bzrlib.errors.BzrCommandError("Local branch is not a"
275
253
" newer version of remote"
279
257
raise bzrlib.errors.BzrCommandError("Remote location is not a"
280
258
" bzr branch (or empty"
282
except RsyncStreamIO:
283
raise bzrlib.errors.BzrCommandError("Rsync could not use the"
284
" specified location. Please ensure that"
285
' "%s" is of the form "machine:/path".' % push_location)
286
260
print "Pushing to %s" % push_location
287
rsync(tree.basedir+'/', push_location, ssh=True,
288
excludes=final_exclusions)
290
set_push_data(tree, push_location)
293
def short_committer(committer):
294
new_committer = re.sub('<.*>', '', committer).strip(' ')
295
if len(new_committer) < 2:
301
"""Screen-scrape Apache listings"""
302
apache_dir = '<img border="0" src="/icons/folder.gif" alt="[dir]">'\
305
expr = re.compile('<a[^>]*href="([^>]*)"[^>]*>', flags=re.I)
307
match = expr.search(line)
311
if url.startswith('http://') or url.startswith('/') or '../' in url:
315
yield url.rstrip('/')
318
def iter_branches(t, lister=None):
319
"""Iterate through all the branches under a transport"""
320
for bzrdir in iter_bzrdirs(t, lister):
322
branch = bzrdir.open_branch()
323
if branch.bzrdir is bzrdir:
325
except (NotBranchError, UnsupportedFormatError):
329
def iter_branch_tree(t, lister=None):
330
for bzrdir in iter_bzrdirs(t, lister):
332
wt = bzrdir.open_workingtree()
334
except NoWorkingTree, UnsupportedFormatError:
336
branch = bzrdir.open_branch()
337
if branch.bzrdir is bzrdir:
339
except (NotBranchError, UnsupportedFormatError):
343
def iter_bzrdirs(t, lister=None):
346
return t.list_dir('.')
348
bzrdir = bzrdir_from_transport(t)
350
except (NotBranchError, UnsupportedFormatError, TransportError,
354
for directory in lister(t):
355
if directory == ".bzr":
358
subt = t.clone(directory)
359
except UnicodeDecodeError:
361
for bzrdir in iter_bzrdirs(subt, lister):
363
except (NoSuchFile, PermissionDenied, TransportError):
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)
261
rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
263
set_push_data(cur_branch, push_location)