~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2005-09-23 00:49:22 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20050923004922-ef0340d45b2e3cfe
tweaked Meinel's changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
17
import bzrlib
18
 
import bzrlib.errors
19
18
import os
20
19
import os.path
21
20
import sys
22
21
import tempfile
23
22
import shutil
24
 
import errno
25
23
from subprocess import Popen, PIPE
26
 
import codecs
27
24
 
28
25
def temp_branch():
29
26
    dirname = tempfile.mkdtemp("temp-branch")
42
39
    >>> fooname = os.path.join(br.base, "foo")
43
40
    >>> file(fooname, "wb").write("bar")
44
41
    >>> is_clean(br)
45
 
    (True, [u'foo'])
46
 
    >>> br.working_tree().add(["foo"])
 
42
    (True, ['foo'])
 
43
    >>> bzrlib.add.smart_add_branch(br, [br.base])
 
44
    1
47
45
    >>> is_clean(br)
48
46
    (False, [])
49
 
    >>> br.working_tree().commit("added file")
 
47
    >>> br.commit("added file")
 
48
    added foo
50
49
    >>> is_clean(br)
51
50
    (True, [])
52
51
    >>> rm_branch(br)
55
54
    old_tree = cur_branch.basis_tree()
56
55
    new_tree = cur_branch.working_tree()
57
56
    non_source = []
58
 
    for path, file_class, kind, file_id, entry in new_tree.list_files():
 
57
    for path, file_class, kind, file_id in new_tree.list_files():
59
58
        if file_class in ('?', 'I'):
60
59
            non_source.append(path)
61
60
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
62
 
    return not delta.has_changed(), non_source
 
61
    if len(delta.added) > 0 or len(delta.removed) > 0 or \
 
62
        len(delta.modified) > 0:
 
63
        return False, non_source
 
64
    return True, non_source 
63
65
 
64
66
def set_pull_data(br, location, rev_id):
65
67
    pull_file = file (br.controlfilename("x-pull-data"), "wb")
121
123
    arg_str = " ".join([shell_escape(a) for a in args])
122
124
    return os.system(arg_str)
123
125
 
124
 
class RsyncUnknownStatus(Exception):
125
 
    def __init__(self, status):
126
 
        Exception.__init__(self, "Unknown status: %d" % status)
127
 
 
128
 
class NoRsync(Exception):
129
 
    def __init__(self, rsync_name):
130
 
        Exception.__init__(self, "%s not found." % rsync_name)
131
 
 
132
 
def rsync(source, target, ssh=False, excludes=(), silent=False, 
133
 
          rsync_name="rsync"):
134
 
    """
135
 
    >>> rsync("a", "b", silent=True)
136
 
    Traceback (most recent call last):
137
 
    RsyncNoFile: No such file a
138
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True)
139
 
    Traceback (most recent call last):
140
 
    RsyncNoFile: No such file a
141
 
    >>> rsync("a", "b", excludes=("*.py",), silent=True, rsync_name="rsyncc")
142
 
    Traceback (most recent call last):
143
 
    NoRsync: rsyncc not found.
144
 
    """
145
 
    cmd = [rsync_name, "-av", "--delete"]
 
126
def rsync(source, target, ssh=False, excludes=()):
 
127
    """
 
128
    >>> real_system = os.system
 
129
    >>> os.system = sys.stdout.write
 
130
    >>> rsync("a", "b")
 
131
    ['rsync', '-av', '--delete', 'a', 'b']
 
132
    >>> rsync("a", "b", excludes=("*.py",))
 
133
    ['rsync', '-av', '--delete', '--exclude-from', '-', 'a', 'b']
 
134
    >>> os.system = real_system
 
135
    """
 
136
    cmd = ["rsync", "-av", "--delete"]
146
137
    if ssh:
147
138
        cmd.extend(('-e', 'ssh'))
148
139
    if len(excludes) > 0:
149
140
        cmd.extend(('--exclude-from', '-'))
150
141
    cmd.extend((source, target))
151
 
    if silent:
152
 
        stderr = PIPE
153
 
        stdout = PIPE
154
 
    else:
155
 
        stderr = None
156
 
        stdout = None
157
 
    try:
158
 
        proc = Popen(cmd, stdin=PIPE, stderr=stderr, stdout=stdout)
159
 
    except OSError, e:
160
 
        if e.errno == errno.ENOENT:
161
 
            raise NoRsync(rsync_name)
162
 
            
 
142
    proc = Popen(cmd, stdin=PIPE)
163
143
    proc.stdin.write('\n'.join(excludes)+'\n')
164
144
    proc.stdin.close()
165
 
    if silent:
166
 
        proc.stderr.read()
167
 
        proc.stderr.close()
168
 
        proc.stdout.read()
169
 
        proc.stdout.close()
170
145
    proc.wait()
171
 
    if proc.returncode == 12:
172
 
        raise RsyncStreamIO()
173
 
    elif proc.returncode == 23:
174
 
        raise RsyncNoFile(source)
175
 
    elif proc.returncode != 0:
176
 
        raise RsyncUnknownStatus(proc.returncode)
177
146
    return cmd
178
147
 
179
 
 
180
 
def rsync_ls(source, ssh=False, silent=True):
181
 
    cmd = ["rsync"]
182
 
    if ssh:
183
 
        cmd.extend(('-e', 'ssh'))
184
 
    cmd.append(source)
185
 
    if silent:
186
 
        stderr = PIPE
187
 
    else:
188
 
        stderr = None
189
 
    proc = Popen(cmd, stderr=stderr, stdout=PIPE)
190
 
    result = proc.stdout.read()
191
 
    proc.stdout.close()
192
 
    if silent:
193
 
        proc.stderr.read()
194
 
        proc.stderr.close()
195
 
    proc.wait()
196
 
    if proc.returncode == 12:
197
 
        raise RsyncStreamIO()
198
 
    elif proc.returncode == 23:
199
 
        raise RsyncNoFile(source)
200
 
    elif proc.returncode != 0:
201
 
        raise RsyncUnknownStatus(proc.returncode)
202
 
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
203
 
 
204
 
exclusions = ('.bzr/x-push-data', '.bzr/parent', '.bzr/x-pull-data', 
205
 
              '.bzr/x-pull', '.bzr/pull', '.bzr/stat-cache',
206
 
              '.bzr/x-rsync-data')
207
 
 
208
 
 
209
 
def read_revision_history(fname):
210
 
    return [l.rstrip('\r\n') for l in
211
 
            codecs.open(fname, 'rb', 'utf-8').readlines()]
212
 
 
213
 
class RsyncNoFile(Exception):
214
 
    def __init__(self, path):
215
 
        Exception.__init__(self, "No such file %s" % path)
216
 
 
217
 
class RsyncStreamIO(Exception):
218
 
    def __init__(self):
219
 
        Exception.__init__(self, "Error in rsync protocol data stream.")
220
 
 
221
 
def get_revision_history(location):
222
 
    tempdir = tempfile.mkdtemp('push')
223
 
    try:
224
 
        history_fname = os.path.join(tempdir, 'revision-history')
225
 
        cmd = rsync(location+'.bzr/revision-history', history_fname,
226
 
                    silent=True)
227
 
        history = read_revision_history(history_fname)
228
 
    finally:
229
 
        shutil.rmtree(tempdir)
230
 
    return history
231
 
 
232
 
def history_subset(location, branch):
233
 
    remote_history = get_revision_history(location)
234
 
    local_history = branch.revision_history()
235
 
    if len(remote_history) > len(local_history):
236
 
        return False
237
 
    for local, remote in zip(remote_history, local_history):
238
 
        if local != remote:
239
 
            return False 
240
 
    return True
241
 
 
242
 
def empty_or_absent(location):
243
 
    try:
244
 
        files = rsync_ls(location)
245
 
        return files == ['.']
246
 
    except RsyncNoFile:
247
 
        return True
248
 
 
249
 
def push(cur_branch, location=None, overwrite=False):
 
148
exclusions = ('.bzr/x-push-data', '.bzr/x-pull-data', '.bzr/stat-cache')
 
149
 
 
150
 
 
151
def push(cur_branch, location=None):
250
152
    push_location = get_push_data(cur_branch)
251
153
    if location is not None:
252
154
        if not location.endswith('/'):
254
156
        push_location = location
255
157
    
256
158
    if push_location is None:
257
 
        raise bzrlib.errors.MustUseDecorated
258
 
 
259
 
    if push_location.find('://') != -1:
260
 
        raise bzrlib.errors.MustUseDecorated
261
 
 
262
 
    if push_location.find(':') == -1:
263
 
        raise bzrlib.errors.MustUseDecorated
 
159
        print "No push location saved.  Please specify one on the command line."
 
160
        sys.exit(1)
264
161
 
265
162
    clean, non_source = is_clean(cur_branch)
266
163
    if not clean:
268
165
Use "bzr status" to list them."""
269
166
        sys.exit(1)
270
167
    non_source.extend(exclusions)
271
 
    if not overwrite:
272
 
        try:
273
 
            if not history_subset(push_location, cur_branch):
274
 
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
275
 
                                                    " newer version of remote"
276
 
                                                    " branch.")
277
 
        except RsyncNoFile:
278
 
            if not empty_or_absent(push_location):
279
 
                raise bzrlib.errors.BzrCommandError("Remote location is not a"
280
 
                                                    " bzr branch (or empty"
281
 
                                                    " directory)")
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)
 
168
 
286
169
    print "Pushing to %s" % push_location
287
170
    rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
288
171