~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2005-09-23 03:08:27 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20050923030827-bd5a4ebd3440daff
prevented accidental overwrites from push

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
18
19
import os
19
20
import os.path
20
21
import sys
21
22
import tempfile
22
23
import shutil
23
24
from subprocess import Popen, PIPE
 
25
import codecs
24
26
 
25
27
def temp_branch():
26
28
    dirname = tempfile.mkdtemp("temp-branch")
123
125
    arg_str = " ".join([shell_escape(a) for a in args])
124
126
    return os.system(arg_str)
125
127
 
126
 
def rsync(source, target, ssh=False, excludes=()):
 
128
class RsyncUnknownStatus(Exception):
 
129
    def __init__(self, status):
 
130
        Exception.__init__(self, "Unknown status: %d" % status)
 
131
 
 
132
def rsync(source, target, ssh=False, excludes=(), silent=False):
127
133
    """
128
134
    >>> real_system = os.system
129
135
    >>> os.system = sys.stdout.write
139
145
    if len(excludes) > 0:
140
146
        cmd.extend(('--exclude-from', '-'))
141
147
    cmd.extend((source, target))
142
 
    proc = Popen(cmd, stdin=PIPE)
 
148
    if silent:
 
149
        stderr = PIPE
 
150
        stdout = PIPE
 
151
    else:
 
152
        stderr = None
 
153
        stdout = None
 
154
    proc = Popen(cmd, stdin=PIPE, stderr=stderr, stdout=stdout)
143
155
    proc.stdin.write('\n'.join(excludes)+'\n')
144
156
    proc.stdin.close()
 
157
    if silent:
 
158
        proc.stderr.read()
 
159
        proc.stderr.close()
 
160
        proc.stdout.read()
 
161
        proc.stdout.close()
145
162
    proc.wait()
 
163
    if proc.returncode == 23:
 
164
        raise RsyncNoFile(source)
 
165
    elif proc.returncode != 0:
 
166
        raise RsyncUnknownStatus(proc.returncode)
146
167
    return cmd
147
168
 
 
169
 
 
170
def rsync_ls(source, ssh=False, silent=True):
 
171
    cmd = ["rsync"]
 
172
    if ssh:
 
173
        cmd.extend(('-e', 'ssh'))
 
174
    cmd.append(source)
 
175
    if silent:
 
176
        stderr = PIPE
 
177
    else:
 
178
        stderr = None
 
179
    proc = Popen(cmd, stderr=stderr, stdout=PIPE)
 
180
    result = proc.stdout.read()
 
181
    proc.stdout.close()
 
182
    if silent:
 
183
        proc.stderr.read()
 
184
        proc.stderr.close()
 
185
    proc.wait()
 
186
    if proc.returncode == 23:
 
187
        raise RsyncNoFile(source)
 
188
    elif proc.returncode != 0:
 
189
        raise RsyncUnknownStatus(proc.returncode)
 
190
    return [l.split(' ')[-1].rstrip('\n') for l in result.splitlines(True)]
 
191
 
148
192
exclusions = ('.bzr/x-push-data', '.bzr/x-pull-data', '.bzr/stat-cache')
149
193
 
150
194
 
151
 
def push(cur_branch, location=None):
 
195
def read_revision_history(fname):
 
196
    return [l.rstrip('\r\n') for l in
 
197
            codecs.open(fname, 'rb', 'utf-8').readlines()]
 
198
 
 
199
class RsyncNoFile(Exception):
 
200
    def __init__(self, path):
 
201
        Exception.__init__(self, "No such file %s" % path)
 
202
 
 
203
def get_revision_history(location):
 
204
    tempdir = tempfile.mkdtemp('push')
 
205
    try:
 
206
        history_fname = os.path.join(tempdir, 'revision-history')
 
207
        cmd = rsync(location+'.bzr/revision-history', history_fname,
 
208
                    silent=True)
 
209
        history = read_revision_history(history_fname)
 
210
    finally:
 
211
        shutil.rmtree(tempdir)
 
212
    return history
 
213
 
 
214
def history_subset(location, branch):
 
215
    remote_history = get_revision_history(location)
 
216
    local_history = branch.revision_history()
 
217
    if len(remote_history) > len(local_history):
 
218
        return False
 
219
    for local, remote in zip(remote_history, local_history):
 
220
        if local != remote:
 
221
            return False 
 
222
    return True
 
223
 
 
224
def empty_or_absent(location):
 
225
    try:
 
226
        files = rsync_ls(location)
 
227
        return files == ['.']
 
228
    except RsyncNoFile:
 
229
        return True
 
230
 
 
231
def push(cur_branch, location=None, overwrite=False):
152
232
    push_location = get_push_data(cur_branch)
153
233
    if location is not None:
154
234
        if not location.endswith('/'):
165
245
Use "bzr status" to list them."""
166
246
        sys.exit(1)
167
247
    non_source.extend(exclusions)
168
 
 
 
248
    if not overwrite:
 
249
        try:
 
250
            if not history_subset(push_location, cur_branch):
 
251
                raise bzrlib.errors.BzrCommandError("Local branch is not a"
 
252
                                                    " newer version of remote"
 
253
                                                    " branch.")
 
254
        except RsyncNoFile:
 
255
            if not empty_or_absent(push_location):
 
256
                raise bzrlib.errors.BzrCommandError("Remote location is not a"
 
257
                                                    " bzr branch (or empty"
 
258
                                                    " directory)")
169
259
    print "Pushing to %s" % push_location
170
260
    rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
171
261