~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2005-09-13 22:49:40 UTC
  • mto: (147.1.21)
  • mto: This revision was merged to the branch mainline in revision 154.
  • Revision ID: abentley@panoramicfeedback.com-20050913224940-c6db07f39dd303be
Updated docs, obsoleted old executibles

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