##// END OF EJS Templates
i18n: avoid naive plural tricks
Martin Geisler -
r6952:3fffba1c default
parent child Browse files
Show More
@@ -1,1334 +1,1330 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 from i18n import _
9 from i18n import _
10 from node import hex, nullid, short
10 from node import hex, nullid, short
11 import base85, cmdutil, mdiff, util, revlog, diffhelpers, copies
11 import base85, cmdutil, mdiff, util, revlog, diffhelpers, copies
12 import cStringIO, email.Parser, os, popen2, re, errno
12 import cStringIO, email.Parser, os, popen2, re, errno
13 import sys, tempfile, zlib
13 import sys, tempfile, zlib
14
14
15 class PatchError(Exception):
15 class PatchError(Exception):
16 pass
16 pass
17
17
18 class NoHunks(PatchError):
18 class NoHunks(PatchError):
19 pass
19 pass
20
20
21 # helper functions
21 # helper functions
22
22
23 def copyfile(src, dst, basedir=None):
23 def copyfile(src, dst, basedir=None):
24 if not basedir:
24 if not basedir:
25 basedir = os.getcwd()
25 basedir = os.getcwd()
26
26
27 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
27 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
28 if os.path.exists(absdst):
28 if os.path.exists(absdst):
29 raise util.Abort(_("cannot create %s: destination already exists") %
29 raise util.Abort(_("cannot create %s: destination already exists") %
30 dst)
30 dst)
31
31
32 targetdir = os.path.dirname(absdst)
32 targetdir = os.path.dirname(absdst)
33 if not os.path.isdir(targetdir):
33 if not os.path.isdir(targetdir):
34 os.makedirs(targetdir)
34 os.makedirs(targetdir)
35
35
36 util.copyfile(abssrc, absdst)
36 util.copyfile(abssrc, absdst)
37
37
38 # public functions
38 # public functions
39
39
40 def extract(ui, fileobj):
40 def extract(ui, fileobj):
41 '''extract patch from data read from fileobj.
41 '''extract patch from data read from fileobj.
42
42
43 patch can be a normal patch or contained in an email message.
43 patch can be a normal patch or contained in an email message.
44
44
45 return tuple (filename, message, user, date, node, p1, p2).
45 return tuple (filename, message, user, date, node, p1, p2).
46 Any item in the returned tuple can be None. If filename is None,
46 Any item in the returned tuple can be None. If filename is None,
47 fileobj did not contain a patch. Caller must unlink filename when done.'''
47 fileobj did not contain a patch. Caller must unlink filename when done.'''
48
48
49 # attempt to detect the start of a patch
49 # attempt to detect the start of a patch
50 # (this heuristic is borrowed from quilt)
50 # (this heuristic is borrowed from quilt)
51 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
51 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
52 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
52 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
53 '(---|\*\*\*)[ \t])', re.MULTILINE)
53 '(---|\*\*\*)[ \t])', re.MULTILINE)
54
54
55 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
55 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
56 tmpfp = os.fdopen(fd, 'w')
56 tmpfp = os.fdopen(fd, 'w')
57 try:
57 try:
58 msg = email.Parser.Parser().parse(fileobj)
58 msg = email.Parser.Parser().parse(fileobj)
59
59
60 subject = msg['Subject']
60 subject = msg['Subject']
61 user = msg['From']
61 user = msg['From']
62 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
62 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
63 # should try to parse msg['Date']
63 # should try to parse msg['Date']
64 date = None
64 date = None
65 nodeid = None
65 nodeid = None
66 branch = None
66 branch = None
67 parents = []
67 parents = []
68
68
69 if subject:
69 if subject:
70 if subject.startswith('[PATCH'):
70 if subject.startswith('[PATCH'):
71 pend = subject.find(']')
71 pend = subject.find(']')
72 if pend >= 0:
72 if pend >= 0:
73 subject = subject[pend+1:].lstrip()
73 subject = subject[pend+1:].lstrip()
74 subject = subject.replace('\n\t', ' ')
74 subject = subject.replace('\n\t', ' ')
75 ui.debug('Subject: %s\n' % subject)
75 ui.debug('Subject: %s\n' % subject)
76 if user:
76 if user:
77 ui.debug('From: %s\n' % user)
77 ui.debug('From: %s\n' % user)
78 diffs_seen = 0
78 diffs_seen = 0
79 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
79 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
80 message = ''
80 message = ''
81 for part in msg.walk():
81 for part in msg.walk():
82 content_type = part.get_content_type()
82 content_type = part.get_content_type()
83 ui.debug('Content-Type: %s\n' % content_type)
83 ui.debug('Content-Type: %s\n' % content_type)
84 if content_type not in ok_types:
84 if content_type not in ok_types:
85 continue
85 continue
86 payload = part.get_payload(decode=True)
86 payload = part.get_payload(decode=True)
87 m = diffre.search(payload)
87 m = diffre.search(payload)
88 if m:
88 if m:
89 hgpatch = False
89 hgpatch = False
90 ignoretext = False
90 ignoretext = False
91
91
92 ui.debug(_('found patch at byte %d\n') % m.start(0))
92 ui.debug(_('found patch at byte %d\n') % m.start(0))
93 diffs_seen += 1
93 diffs_seen += 1
94 cfp = cStringIO.StringIO()
94 cfp = cStringIO.StringIO()
95 for line in payload[:m.start(0)].splitlines():
95 for line in payload[:m.start(0)].splitlines():
96 if line.startswith('# HG changeset patch'):
96 if line.startswith('# HG changeset patch'):
97 ui.debug(_('patch generated by hg export\n'))
97 ui.debug(_('patch generated by hg export\n'))
98 hgpatch = True
98 hgpatch = True
99 # drop earlier commit message content
99 # drop earlier commit message content
100 cfp.seek(0)
100 cfp.seek(0)
101 cfp.truncate()
101 cfp.truncate()
102 subject = None
102 subject = None
103 elif hgpatch:
103 elif hgpatch:
104 if line.startswith('# User '):
104 if line.startswith('# User '):
105 user = line[7:]
105 user = line[7:]
106 ui.debug('From: %s\n' % user)
106 ui.debug('From: %s\n' % user)
107 elif line.startswith("# Date "):
107 elif line.startswith("# Date "):
108 date = line[7:]
108 date = line[7:]
109 elif line.startswith("# Branch "):
109 elif line.startswith("# Branch "):
110 branch = line[9:]
110 branch = line[9:]
111 elif line.startswith("# Node ID "):
111 elif line.startswith("# Node ID "):
112 nodeid = line[10:]
112 nodeid = line[10:]
113 elif line.startswith("# Parent "):
113 elif line.startswith("# Parent "):
114 parents.append(line[10:])
114 parents.append(line[10:])
115 elif line == '---' and gitsendmail:
115 elif line == '---' and gitsendmail:
116 ignoretext = True
116 ignoretext = True
117 if not line.startswith('# ') and not ignoretext:
117 if not line.startswith('# ') and not ignoretext:
118 cfp.write(line)
118 cfp.write(line)
119 cfp.write('\n')
119 cfp.write('\n')
120 message = cfp.getvalue()
120 message = cfp.getvalue()
121 if tmpfp:
121 if tmpfp:
122 tmpfp.write(payload)
122 tmpfp.write(payload)
123 if not payload.endswith('\n'):
123 if not payload.endswith('\n'):
124 tmpfp.write('\n')
124 tmpfp.write('\n')
125 elif not diffs_seen and message and content_type == 'text/plain':
125 elif not diffs_seen and message and content_type == 'text/plain':
126 message += '\n' + payload
126 message += '\n' + payload
127 except:
127 except:
128 tmpfp.close()
128 tmpfp.close()
129 os.unlink(tmpname)
129 os.unlink(tmpname)
130 raise
130 raise
131
131
132 if subject and not message.startswith(subject):
132 if subject and not message.startswith(subject):
133 message = '%s\n%s' % (subject, message)
133 message = '%s\n%s' % (subject, message)
134 tmpfp.close()
134 tmpfp.close()
135 if not diffs_seen:
135 if not diffs_seen:
136 os.unlink(tmpname)
136 os.unlink(tmpname)
137 return None, message, user, date, branch, None, None, None
137 return None, message, user, date, branch, None, None, None
138 p1 = parents and parents.pop(0) or None
138 p1 = parents and parents.pop(0) or None
139 p2 = parents and parents.pop(0) or None
139 p2 = parents and parents.pop(0) or None
140 return tmpname, message, user, date, branch, nodeid, p1, p2
140 return tmpname, message, user, date, branch, nodeid, p1, p2
141
141
142 GP_PATCH = 1 << 0 # we have to run patch
142 GP_PATCH = 1 << 0 # we have to run patch
143 GP_FILTER = 1 << 1 # there's some copy/rename operation
143 GP_FILTER = 1 << 1 # there's some copy/rename operation
144 GP_BINARY = 1 << 2 # there's a binary patch
144 GP_BINARY = 1 << 2 # there's a binary patch
145
145
146 def readgitpatch(fp, firstline=None):
146 def readgitpatch(fp, firstline=None):
147 """extract git-style metadata about patches from <patchname>"""
147 """extract git-style metadata about patches from <patchname>"""
148 class gitpatch:
148 class gitpatch:
149 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
149 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
150 def __init__(self, path):
150 def __init__(self, path):
151 self.path = path
151 self.path = path
152 self.oldpath = None
152 self.oldpath = None
153 self.mode = None
153 self.mode = None
154 self.op = 'MODIFY'
154 self.op = 'MODIFY'
155 self.lineno = 0
155 self.lineno = 0
156 self.binary = False
156 self.binary = False
157
157
158 def reader(fp, firstline):
158 def reader(fp, firstline):
159 if firstline is not None:
159 if firstline is not None:
160 yield firstline
160 yield firstline
161 for line in fp:
161 for line in fp:
162 yield line
162 yield line
163
163
164 # Filter patch for git information
164 # Filter patch for git information
165 gitre = re.compile('diff --git a/(.*) b/(.*)')
165 gitre = re.compile('diff --git a/(.*) b/(.*)')
166 gp = None
166 gp = None
167 gitpatches = []
167 gitpatches = []
168 # Can have a git patch with only metadata, causing patch to complain
168 # Can have a git patch with only metadata, causing patch to complain
169 dopatch = 0
169 dopatch = 0
170
170
171 lineno = 0
171 lineno = 0
172 for line in reader(fp, firstline):
172 for line in reader(fp, firstline):
173 lineno += 1
173 lineno += 1
174 if line.startswith('diff --git'):
174 if line.startswith('diff --git'):
175 m = gitre.match(line)
175 m = gitre.match(line)
176 if m:
176 if m:
177 if gp:
177 if gp:
178 gitpatches.append(gp)
178 gitpatches.append(gp)
179 src, dst = m.group(1, 2)
179 src, dst = m.group(1, 2)
180 gp = gitpatch(dst)
180 gp = gitpatch(dst)
181 gp.lineno = lineno
181 gp.lineno = lineno
182 elif gp:
182 elif gp:
183 if line.startswith('--- '):
183 if line.startswith('--- '):
184 if gp.op in ('COPY', 'RENAME'):
184 if gp.op in ('COPY', 'RENAME'):
185 dopatch |= GP_FILTER
185 dopatch |= GP_FILTER
186 gitpatches.append(gp)
186 gitpatches.append(gp)
187 gp = None
187 gp = None
188 dopatch |= GP_PATCH
188 dopatch |= GP_PATCH
189 continue
189 continue
190 if line.startswith('rename from '):
190 if line.startswith('rename from '):
191 gp.op = 'RENAME'
191 gp.op = 'RENAME'
192 gp.oldpath = line[12:].rstrip()
192 gp.oldpath = line[12:].rstrip()
193 elif line.startswith('rename to '):
193 elif line.startswith('rename to '):
194 gp.path = line[10:].rstrip()
194 gp.path = line[10:].rstrip()
195 elif line.startswith('copy from '):
195 elif line.startswith('copy from '):
196 gp.op = 'COPY'
196 gp.op = 'COPY'
197 gp.oldpath = line[10:].rstrip()
197 gp.oldpath = line[10:].rstrip()
198 elif line.startswith('copy to '):
198 elif line.startswith('copy to '):
199 gp.path = line[8:].rstrip()
199 gp.path = line[8:].rstrip()
200 elif line.startswith('deleted file'):
200 elif line.startswith('deleted file'):
201 gp.op = 'DELETE'
201 gp.op = 'DELETE'
202 elif line.startswith('new file mode '):
202 elif line.startswith('new file mode '):
203 gp.op = 'ADD'
203 gp.op = 'ADD'
204 gp.mode = int(line.rstrip()[-6:], 8)
204 gp.mode = int(line.rstrip()[-6:], 8)
205 elif line.startswith('new mode '):
205 elif line.startswith('new mode '):
206 gp.mode = int(line.rstrip()[-6:], 8)
206 gp.mode = int(line.rstrip()[-6:], 8)
207 elif line.startswith('GIT binary patch'):
207 elif line.startswith('GIT binary patch'):
208 dopatch |= GP_BINARY
208 dopatch |= GP_BINARY
209 gp.binary = True
209 gp.binary = True
210 if gp:
210 if gp:
211 gitpatches.append(gp)
211 gitpatches.append(gp)
212
212
213 if not gitpatches:
213 if not gitpatches:
214 dopatch = GP_PATCH
214 dopatch = GP_PATCH
215
215
216 return (dopatch, gitpatches)
216 return (dopatch, gitpatches)
217
217
218 def patch(patchname, ui, strip=1, cwd=None, files={}):
218 def patch(patchname, ui, strip=1, cwd=None, files={}):
219 """apply <patchname> to the working directory.
219 """apply <patchname> to the working directory.
220 returns whether patch was applied with fuzz factor."""
220 returns whether patch was applied with fuzz factor."""
221 patcher = ui.config('ui', 'patch')
221 patcher = ui.config('ui', 'patch')
222 args = []
222 args = []
223 try:
223 try:
224 if patcher:
224 if patcher:
225 return externalpatch(patcher, args, patchname, ui, strip, cwd,
225 return externalpatch(patcher, args, patchname, ui, strip, cwd,
226 files)
226 files)
227 else:
227 else:
228 try:
228 try:
229 return internalpatch(patchname, ui, strip, cwd, files)
229 return internalpatch(patchname, ui, strip, cwd, files)
230 except NoHunks:
230 except NoHunks:
231 patcher = util.find_exe('gpatch') or util.find_exe('patch')
231 patcher = util.find_exe('gpatch') or util.find_exe('patch')
232 ui.debug('no valid hunks found; trying with %r instead\n' %
232 ui.debug('no valid hunks found; trying with %r instead\n' %
233 patcher)
233 patcher)
234 if util.needbinarypatch():
234 if util.needbinarypatch():
235 args.append('--binary')
235 args.append('--binary')
236 return externalpatch(patcher, args, patchname, ui, strip, cwd,
236 return externalpatch(patcher, args, patchname, ui, strip, cwd,
237 files)
237 files)
238 except PatchError, err:
238 except PatchError, err:
239 s = str(err)
239 s = str(err)
240 if s:
240 if s:
241 raise util.Abort(s)
241 raise util.Abort(s)
242 else:
242 else:
243 raise util.Abort(_('patch failed to apply'))
243 raise util.Abort(_('patch failed to apply'))
244
244
245 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
245 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
246 """use <patcher> to apply <patchname> to the working directory.
246 """use <patcher> to apply <patchname> to the working directory.
247 returns whether patch was applied with fuzz factor."""
247 returns whether patch was applied with fuzz factor."""
248
248
249 fuzz = False
249 fuzz = False
250 if cwd:
250 if cwd:
251 args.append('-d %s' % util.shellquote(cwd))
251 args.append('-d %s' % util.shellquote(cwd))
252 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
252 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
253 util.shellquote(patchname)))
253 util.shellquote(patchname)))
254
254
255 for line in fp:
255 for line in fp:
256 line = line.rstrip()
256 line = line.rstrip()
257 ui.note(line + '\n')
257 ui.note(line + '\n')
258 if line.startswith('patching file '):
258 if line.startswith('patching file '):
259 pf = util.parse_patch_output(line)
259 pf = util.parse_patch_output(line)
260 printed_file = False
260 printed_file = False
261 files.setdefault(pf, (None, None))
261 files.setdefault(pf, (None, None))
262 elif line.find('with fuzz') >= 0:
262 elif line.find('with fuzz') >= 0:
263 fuzz = True
263 fuzz = True
264 if not printed_file:
264 if not printed_file:
265 ui.warn(pf + '\n')
265 ui.warn(pf + '\n')
266 printed_file = True
266 printed_file = True
267 ui.warn(line + '\n')
267 ui.warn(line + '\n')
268 elif line.find('saving rejects to file') >= 0:
268 elif line.find('saving rejects to file') >= 0:
269 ui.warn(line + '\n')
269 ui.warn(line + '\n')
270 elif line.find('FAILED') >= 0:
270 elif line.find('FAILED') >= 0:
271 if not printed_file:
271 if not printed_file:
272 ui.warn(pf + '\n')
272 ui.warn(pf + '\n')
273 printed_file = True
273 printed_file = True
274 ui.warn(line + '\n')
274 ui.warn(line + '\n')
275 code = fp.close()
275 code = fp.close()
276 if code:
276 if code:
277 raise PatchError(_("patch command failed: %s") %
277 raise PatchError(_("patch command failed: %s") %
278 util.explain_exit(code)[0])
278 util.explain_exit(code)[0])
279 return fuzz
279 return fuzz
280
280
281 def internalpatch(patchobj, ui, strip, cwd, files={}):
281 def internalpatch(patchobj, ui, strip, cwd, files={}):
282 """use builtin patch to apply <patchobj> to the working directory.
282 """use builtin patch to apply <patchobj> to the working directory.
283 returns whether patch was applied with fuzz factor."""
283 returns whether patch was applied with fuzz factor."""
284 try:
284 try:
285 fp = file(patchobj, 'rb')
285 fp = file(patchobj, 'rb')
286 except TypeError:
286 except TypeError:
287 fp = patchobj
287 fp = patchobj
288 if cwd:
288 if cwd:
289 curdir = os.getcwd()
289 curdir = os.getcwd()
290 os.chdir(cwd)
290 os.chdir(cwd)
291 try:
291 try:
292 ret = applydiff(ui, fp, files, strip=strip)
292 ret = applydiff(ui, fp, files, strip=strip)
293 finally:
293 finally:
294 if cwd:
294 if cwd:
295 os.chdir(curdir)
295 os.chdir(curdir)
296 if ret < 0:
296 if ret < 0:
297 raise PatchError
297 raise PatchError
298 return ret > 0
298 return ret > 0
299
299
300 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
300 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
301 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
301 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
302 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
302 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
303
303
304 class patchfile:
304 class patchfile:
305 def __init__(self, ui, fname, missing=False):
305 def __init__(self, ui, fname, missing=False):
306 self.fname = fname
306 self.fname = fname
307 self.ui = ui
307 self.ui = ui
308 self.lines = []
308 self.lines = []
309 self.exists = False
309 self.exists = False
310 self.missing = missing
310 self.missing = missing
311 if not missing:
311 if not missing:
312 try:
312 try:
313 fp = file(fname, 'rb')
313 fp = file(fname, 'rb')
314 self.lines = fp.readlines()
314 self.lines = fp.readlines()
315 self.exists = True
315 self.exists = True
316 except IOError:
316 except IOError:
317 pass
317 pass
318 else:
318 else:
319 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
319 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
320
320
321 if not self.exists:
321 if not self.exists:
322 dirname = os.path.dirname(fname)
322 dirname = os.path.dirname(fname)
323 if dirname and not os.path.isdir(dirname):
323 if dirname and not os.path.isdir(dirname):
324 os.makedirs(dirname)
324 os.makedirs(dirname)
325
325
326 self.hash = {}
326 self.hash = {}
327 self.dirty = 0
327 self.dirty = 0
328 self.offset = 0
328 self.offset = 0
329 self.rej = []
329 self.rej = []
330 self.fileprinted = False
330 self.fileprinted = False
331 self.printfile(False)
331 self.printfile(False)
332 self.hunks = 0
332 self.hunks = 0
333
333
334 def printfile(self, warn):
334 def printfile(self, warn):
335 if self.fileprinted:
335 if self.fileprinted:
336 return
336 return
337 if warn or self.ui.verbose:
337 if warn or self.ui.verbose:
338 self.fileprinted = True
338 self.fileprinted = True
339 s = _("patching file %s\n") % self.fname
339 s = _("patching file %s\n") % self.fname
340 if warn:
340 if warn:
341 self.ui.warn(s)
341 self.ui.warn(s)
342 else:
342 else:
343 self.ui.note(s)
343 self.ui.note(s)
344
344
345
345
346 def findlines(self, l, linenum):
346 def findlines(self, l, linenum):
347 # looks through the hash and finds candidate lines. The
347 # looks through the hash and finds candidate lines. The
348 # result is a list of line numbers sorted based on distance
348 # result is a list of line numbers sorted based on distance
349 # from linenum
349 # from linenum
350 def sorter(a, b):
350 def sorter(a, b):
351 vala = abs(a - linenum)
351 vala = abs(a - linenum)
352 valb = abs(b - linenum)
352 valb = abs(b - linenum)
353 return cmp(vala, valb)
353 return cmp(vala, valb)
354
354
355 try:
355 try:
356 cand = self.hash[l]
356 cand = self.hash[l]
357 except:
357 except:
358 return []
358 return []
359
359
360 if len(cand) > 1:
360 if len(cand) > 1:
361 # resort our list of potentials forward then back.
361 # resort our list of potentials forward then back.
362 cand.sort(sorter)
362 cand.sort(sorter)
363 return cand
363 return cand
364
364
365 def hashlines(self):
365 def hashlines(self):
366 self.hash = {}
366 self.hash = {}
367 for x in xrange(len(self.lines)):
367 for x in xrange(len(self.lines)):
368 s = self.lines[x]
368 s = self.lines[x]
369 self.hash.setdefault(s, []).append(x)
369 self.hash.setdefault(s, []).append(x)
370
370
371 def write_rej(self):
371 def write_rej(self):
372 # our rejects are a little different from patch(1). This always
372 # our rejects are a little different from patch(1). This always
373 # creates rejects in the same form as the original patch. A file
373 # creates rejects in the same form as the original patch. A file
374 # header is inserted so that you can run the reject through patch again
374 # header is inserted so that you can run the reject through patch again
375 # without having to type the filename.
375 # without having to type the filename.
376
376
377 if not self.rej:
377 if not self.rej:
378 return
378 return
379 if self.hunks != 1:
380 hunkstr = "s"
381 else:
382 hunkstr = ""
383
379
384 fname = self.fname + ".rej"
380 fname = self.fname + ".rej"
385 self.ui.warn(
381 self.ui.warn(
386 _("%d out of %d hunk%s FAILED -- saving rejects to file %s\n") %
382 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
387 (len(self.rej), self.hunks, hunkstr, fname))
383 (len(self.rej), self.hunks, fname))
388 try: os.unlink(fname)
384 try: os.unlink(fname)
389 except:
385 except:
390 pass
386 pass
391 fp = file(fname, 'wb')
387 fp = file(fname, 'wb')
392 base = os.path.basename(self.fname)
388 base = os.path.basename(self.fname)
393 fp.write("--- %s\n+++ %s\n" % (base, base))
389 fp.write("--- %s\n+++ %s\n" % (base, base))
394 for x in self.rej:
390 for x in self.rej:
395 for l in x.hunk:
391 for l in x.hunk:
396 fp.write(l)
392 fp.write(l)
397 if l[-1] != '\n':
393 if l[-1] != '\n':
398 fp.write("\n\ No newline at end of file\n")
394 fp.write("\n\ No newline at end of file\n")
399
395
400 def write(self, dest=None):
396 def write(self, dest=None):
401 if self.dirty:
397 if self.dirty:
402 if not dest:
398 if not dest:
403 dest = self.fname
399 dest = self.fname
404 st = None
400 st = None
405 try:
401 try:
406 st = os.lstat(dest)
402 st = os.lstat(dest)
407 except OSError, inst:
403 except OSError, inst:
408 if inst.errno != errno.ENOENT:
404 if inst.errno != errno.ENOENT:
409 raise
405 raise
410 if st and st.st_nlink > 1:
406 if st and st.st_nlink > 1:
411 os.unlink(dest)
407 os.unlink(dest)
412 fp = file(dest, 'wb')
408 fp = file(dest, 'wb')
413 if st and st.st_nlink > 1:
409 if st and st.st_nlink > 1:
414 os.chmod(dest, st.st_mode)
410 os.chmod(dest, st.st_mode)
415 fp.writelines(self.lines)
411 fp.writelines(self.lines)
416 fp.close()
412 fp.close()
417
413
418 def close(self):
414 def close(self):
419 self.write()
415 self.write()
420 self.write_rej()
416 self.write_rej()
421
417
422 def apply(self, h, reverse):
418 def apply(self, h, reverse):
423 if not h.complete():
419 if not h.complete():
424 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
420 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
425 (h.number, h.desc, len(h.a), h.lena, len(h.b),
421 (h.number, h.desc, len(h.a), h.lena, len(h.b),
426 h.lenb))
422 h.lenb))
427
423
428 self.hunks += 1
424 self.hunks += 1
429 if reverse:
425 if reverse:
430 h.reverse()
426 h.reverse()
431
427
432 if self.missing:
428 if self.missing:
433 self.rej.append(h)
429 self.rej.append(h)
434 return -1
430 return -1
435
431
436 if self.exists and h.createfile():
432 if self.exists and h.createfile():
437 self.ui.warn(_("file %s already exists\n") % self.fname)
433 self.ui.warn(_("file %s already exists\n") % self.fname)
438 self.rej.append(h)
434 self.rej.append(h)
439 return -1
435 return -1
440
436
441 if isinstance(h, binhunk):
437 if isinstance(h, binhunk):
442 if h.rmfile():
438 if h.rmfile():
443 os.unlink(self.fname)
439 os.unlink(self.fname)
444 else:
440 else:
445 self.lines[:] = h.new()
441 self.lines[:] = h.new()
446 self.offset += len(h.new())
442 self.offset += len(h.new())
447 self.dirty = 1
443 self.dirty = 1
448 return 0
444 return 0
449
445
450 # fast case first, no offsets, no fuzz
446 # fast case first, no offsets, no fuzz
451 old = h.old()
447 old = h.old()
452 # patch starts counting at 1 unless we are adding the file
448 # patch starts counting at 1 unless we are adding the file
453 if h.starta == 0:
449 if h.starta == 0:
454 start = 0
450 start = 0
455 else:
451 else:
456 start = h.starta + self.offset - 1
452 start = h.starta + self.offset - 1
457 orig_start = start
453 orig_start = start
458 if diffhelpers.testhunk(old, self.lines, start) == 0:
454 if diffhelpers.testhunk(old, self.lines, start) == 0:
459 if h.rmfile():
455 if h.rmfile():
460 os.unlink(self.fname)
456 os.unlink(self.fname)
461 else:
457 else:
462 self.lines[start : start + h.lena] = h.new()
458 self.lines[start : start + h.lena] = h.new()
463 self.offset += h.lenb - h.lena
459 self.offset += h.lenb - h.lena
464 self.dirty = 1
460 self.dirty = 1
465 return 0
461 return 0
466
462
467 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
463 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
468 self.hashlines()
464 self.hashlines()
469 if h.hunk[-1][0] != ' ':
465 if h.hunk[-1][0] != ' ':
470 # if the hunk tried to put something at the bottom of the file
466 # if the hunk tried to put something at the bottom of the file
471 # override the start line and use eof here
467 # override the start line and use eof here
472 search_start = len(self.lines)
468 search_start = len(self.lines)
473 else:
469 else:
474 search_start = orig_start
470 search_start = orig_start
475
471
476 for fuzzlen in xrange(3):
472 for fuzzlen in xrange(3):
477 for toponly in [ True, False ]:
473 for toponly in [ True, False ]:
478 old = h.old(fuzzlen, toponly)
474 old = h.old(fuzzlen, toponly)
479
475
480 cand = self.findlines(old[0][1:], search_start)
476 cand = self.findlines(old[0][1:], search_start)
481 for l in cand:
477 for l in cand:
482 if diffhelpers.testhunk(old, self.lines, l) == 0:
478 if diffhelpers.testhunk(old, self.lines, l) == 0:
483 newlines = h.new(fuzzlen, toponly)
479 newlines = h.new(fuzzlen, toponly)
484 self.lines[l : l + len(old)] = newlines
480 self.lines[l : l + len(old)] = newlines
485 self.offset += len(newlines) - len(old)
481 self.offset += len(newlines) - len(old)
486 self.dirty = 1
482 self.dirty = 1
487 if fuzzlen:
483 if fuzzlen:
488 fuzzstr = "with fuzz %d " % fuzzlen
484 fuzzstr = "with fuzz %d " % fuzzlen
489 f = self.ui.warn
485 f = self.ui.warn
490 self.printfile(True)
486 self.printfile(True)
491 else:
487 else:
492 fuzzstr = ""
488 fuzzstr = ""
493 f = self.ui.note
489 f = self.ui.note
494 offset = l - orig_start - fuzzlen
490 offset = l - orig_start - fuzzlen
495 if offset == 1:
491 if offset == 1:
496 linestr = "line"
492 linestr = "line"
497 else:
493 else:
498 linestr = "lines"
494 linestr = "lines"
499 f(_("Hunk #%d succeeded at %d %s(offset %d %s).\n") %
495 f(_("Hunk #%d succeeded at %d %s(offset %d %s).\n") %
500 (h.number, l+1, fuzzstr, offset, linestr))
496 (h.number, l+1, fuzzstr, offset, linestr))
501 return fuzzlen
497 return fuzzlen
502 self.printfile(True)
498 self.printfile(True)
503 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
499 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
504 self.rej.append(h)
500 self.rej.append(h)
505 return -1
501 return -1
506
502
507 class hunk:
503 class hunk:
508 def __init__(self, desc, num, lr, context, create=False, remove=False):
504 def __init__(self, desc, num, lr, context, create=False, remove=False):
509 self.number = num
505 self.number = num
510 self.desc = desc
506 self.desc = desc
511 self.hunk = [ desc ]
507 self.hunk = [ desc ]
512 self.a = []
508 self.a = []
513 self.b = []
509 self.b = []
514 if context:
510 if context:
515 self.read_context_hunk(lr)
511 self.read_context_hunk(lr)
516 else:
512 else:
517 self.read_unified_hunk(lr)
513 self.read_unified_hunk(lr)
518 self.create = create
514 self.create = create
519 self.remove = remove and not create
515 self.remove = remove and not create
520
516
521 def read_unified_hunk(self, lr):
517 def read_unified_hunk(self, lr):
522 m = unidesc.match(self.desc)
518 m = unidesc.match(self.desc)
523 if not m:
519 if not m:
524 raise PatchError(_("bad hunk #%d") % self.number)
520 raise PatchError(_("bad hunk #%d") % self.number)
525 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
521 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
526 if self.lena == None:
522 if self.lena == None:
527 self.lena = 1
523 self.lena = 1
528 else:
524 else:
529 self.lena = int(self.lena)
525 self.lena = int(self.lena)
530 if self.lenb == None:
526 if self.lenb == None:
531 self.lenb = 1
527 self.lenb = 1
532 else:
528 else:
533 self.lenb = int(self.lenb)
529 self.lenb = int(self.lenb)
534 self.starta = int(self.starta)
530 self.starta = int(self.starta)
535 self.startb = int(self.startb)
531 self.startb = int(self.startb)
536 diffhelpers.addlines(lr.fp, self.hunk, self.lena, self.lenb, self.a, self.b)
532 diffhelpers.addlines(lr.fp, self.hunk, self.lena, self.lenb, self.a, self.b)
537 # if we hit eof before finishing out the hunk, the last line will
533 # if we hit eof before finishing out the hunk, the last line will
538 # be zero length. Lets try to fix it up.
534 # be zero length. Lets try to fix it up.
539 while len(self.hunk[-1]) == 0:
535 while len(self.hunk[-1]) == 0:
540 del self.hunk[-1]
536 del self.hunk[-1]
541 del self.a[-1]
537 del self.a[-1]
542 del self.b[-1]
538 del self.b[-1]
543 self.lena -= 1
539 self.lena -= 1
544 self.lenb -= 1
540 self.lenb -= 1
545
541
546 def read_context_hunk(self, lr):
542 def read_context_hunk(self, lr):
547 self.desc = lr.readline()
543 self.desc = lr.readline()
548 m = contextdesc.match(self.desc)
544 m = contextdesc.match(self.desc)
549 if not m:
545 if not m:
550 raise PatchError(_("bad hunk #%d") % self.number)
546 raise PatchError(_("bad hunk #%d") % self.number)
551 foo, self.starta, foo2, aend, foo3 = m.groups()
547 foo, self.starta, foo2, aend, foo3 = m.groups()
552 self.starta = int(self.starta)
548 self.starta = int(self.starta)
553 if aend == None:
549 if aend == None:
554 aend = self.starta
550 aend = self.starta
555 self.lena = int(aend) - self.starta
551 self.lena = int(aend) - self.starta
556 if self.starta:
552 if self.starta:
557 self.lena += 1
553 self.lena += 1
558 for x in xrange(self.lena):
554 for x in xrange(self.lena):
559 l = lr.readline()
555 l = lr.readline()
560 if l.startswith('---'):
556 if l.startswith('---'):
561 lr.push(l)
557 lr.push(l)
562 break
558 break
563 s = l[2:]
559 s = l[2:]
564 if l.startswith('- ') or l.startswith('! '):
560 if l.startswith('- ') or l.startswith('! '):
565 u = '-' + s
561 u = '-' + s
566 elif l.startswith(' '):
562 elif l.startswith(' '):
567 u = ' ' + s
563 u = ' ' + s
568 else:
564 else:
569 raise PatchError(_("bad hunk #%d old text line %d") %
565 raise PatchError(_("bad hunk #%d old text line %d") %
570 (self.number, x))
566 (self.number, x))
571 self.a.append(u)
567 self.a.append(u)
572 self.hunk.append(u)
568 self.hunk.append(u)
573
569
574 l = lr.readline()
570 l = lr.readline()
575 if l.startswith('\ '):
571 if l.startswith('\ '):
576 s = self.a[-1][:-1]
572 s = self.a[-1][:-1]
577 self.a[-1] = s
573 self.a[-1] = s
578 self.hunk[-1] = s
574 self.hunk[-1] = s
579 l = lr.readline()
575 l = lr.readline()
580 m = contextdesc.match(l)
576 m = contextdesc.match(l)
581 if not m:
577 if not m:
582 raise PatchError(_("bad hunk #%d") % self.number)
578 raise PatchError(_("bad hunk #%d") % self.number)
583 foo, self.startb, foo2, bend, foo3 = m.groups()
579 foo, self.startb, foo2, bend, foo3 = m.groups()
584 self.startb = int(self.startb)
580 self.startb = int(self.startb)
585 if bend == None:
581 if bend == None:
586 bend = self.startb
582 bend = self.startb
587 self.lenb = int(bend) - self.startb
583 self.lenb = int(bend) - self.startb
588 if self.startb:
584 if self.startb:
589 self.lenb += 1
585 self.lenb += 1
590 hunki = 1
586 hunki = 1
591 for x in xrange(self.lenb):
587 for x in xrange(self.lenb):
592 l = lr.readline()
588 l = lr.readline()
593 if l.startswith('\ '):
589 if l.startswith('\ '):
594 s = self.b[-1][:-1]
590 s = self.b[-1][:-1]
595 self.b[-1] = s
591 self.b[-1] = s
596 self.hunk[hunki-1] = s
592 self.hunk[hunki-1] = s
597 continue
593 continue
598 if not l:
594 if not l:
599 lr.push(l)
595 lr.push(l)
600 break
596 break
601 s = l[2:]
597 s = l[2:]
602 if l.startswith('+ ') or l.startswith('! '):
598 if l.startswith('+ ') or l.startswith('! '):
603 u = '+' + s
599 u = '+' + s
604 elif l.startswith(' '):
600 elif l.startswith(' '):
605 u = ' ' + s
601 u = ' ' + s
606 elif len(self.b) == 0:
602 elif len(self.b) == 0:
607 # this can happen when the hunk does not add any lines
603 # this can happen when the hunk does not add any lines
608 lr.push(l)
604 lr.push(l)
609 break
605 break
610 else:
606 else:
611 raise PatchError(_("bad hunk #%d old text line %d") %
607 raise PatchError(_("bad hunk #%d old text line %d") %
612 (self.number, x))
608 (self.number, x))
613 self.b.append(s)
609 self.b.append(s)
614 while True:
610 while True:
615 if hunki >= len(self.hunk):
611 if hunki >= len(self.hunk):
616 h = ""
612 h = ""
617 else:
613 else:
618 h = self.hunk[hunki]
614 h = self.hunk[hunki]
619 hunki += 1
615 hunki += 1
620 if h == u:
616 if h == u:
621 break
617 break
622 elif h.startswith('-'):
618 elif h.startswith('-'):
623 continue
619 continue
624 else:
620 else:
625 self.hunk.insert(hunki-1, u)
621 self.hunk.insert(hunki-1, u)
626 break
622 break
627
623
628 if not self.a:
624 if not self.a:
629 # this happens when lines were only added to the hunk
625 # this happens when lines were only added to the hunk
630 for x in self.hunk:
626 for x in self.hunk:
631 if x.startswith('-') or x.startswith(' '):
627 if x.startswith('-') or x.startswith(' '):
632 self.a.append(x)
628 self.a.append(x)
633 if not self.b:
629 if not self.b:
634 # this happens when lines were only deleted from the hunk
630 # this happens when lines were only deleted from the hunk
635 for x in self.hunk:
631 for x in self.hunk:
636 if x.startswith('+') or x.startswith(' '):
632 if x.startswith('+') or x.startswith(' '):
637 self.b.append(x[1:])
633 self.b.append(x[1:])
638 # @@ -start,len +start,len @@
634 # @@ -start,len +start,len @@
639 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
635 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
640 self.startb, self.lenb)
636 self.startb, self.lenb)
641 self.hunk[0] = self.desc
637 self.hunk[0] = self.desc
642
638
643 def reverse(self):
639 def reverse(self):
644 self.create, self.remove = self.remove, self.create
640 self.create, self.remove = self.remove, self.create
645 origlena = self.lena
641 origlena = self.lena
646 origstarta = self.starta
642 origstarta = self.starta
647 self.lena = self.lenb
643 self.lena = self.lenb
648 self.starta = self.startb
644 self.starta = self.startb
649 self.lenb = origlena
645 self.lenb = origlena
650 self.startb = origstarta
646 self.startb = origstarta
651 self.a = []
647 self.a = []
652 self.b = []
648 self.b = []
653 # self.hunk[0] is the @@ description
649 # self.hunk[0] is the @@ description
654 for x in xrange(1, len(self.hunk)):
650 for x in xrange(1, len(self.hunk)):
655 o = self.hunk[x]
651 o = self.hunk[x]
656 if o.startswith('-'):
652 if o.startswith('-'):
657 n = '+' + o[1:]
653 n = '+' + o[1:]
658 self.b.append(o[1:])
654 self.b.append(o[1:])
659 elif o.startswith('+'):
655 elif o.startswith('+'):
660 n = '-' + o[1:]
656 n = '-' + o[1:]
661 self.a.append(n)
657 self.a.append(n)
662 else:
658 else:
663 n = o
659 n = o
664 self.b.append(o[1:])
660 self.b.append(o[1:])
665 self.a.append(o)
661 self.a.append(o)
666 self.hunk[x] = o
662 self.hunk[x] = o
667
663
668 def fix_newline(self):
664 def fix_newline(self):
669 diffhelpers.fix_newline(self.hunk, self.a, self.b)
665 diffhelpers.fix_newline(self.hunk, self.a, self.b)
670
666
671 def complete(self):
667 def complete(self):
672 return len(self.a) == self.lena and len(self.b) == self.lenb
668 return len(self.a) == self.lena and len(self.b) == self.lenb
673
669
674 def createfile(self):
670 def createfile(self):
675 return self.starta == 0 and self.lena == 0 and self.create
671 return self.starta == 0 and self.lena == 0 and self.create
676
672
677 def rmfile(self):
673 def rmfile(self):
678 return self.startb == 0 and self.lenb == 0 and self.remove
674 return self.startb == 0 and self.lenb == 0 and self.remove
679
675
680 def fuzzit(self, l, fuzz, toponly):
676 def fuzzit(self, l, fuzz, toponly):
681 # this removes context lines from the top and bottom of list 'l'. It
677 # this removes context lines from the top and bottom of list 'l'. It
682 # checks the hunk to make sure only context lines are removed, and then
678 # checks the hunk to make sure only context lines are removed, and then
683 # returns a new shortened list of lines.
679 # returns a new shortened list of lines.
684 fuzz = min(fuzz, len(l)-1)
680 fuzz = min(fuzz, len(l)-1)
685 if fuzz:
681 if fuzz:
686 top = 0
682 top = 0
687 bot = 0
683 bot = 0
688 hlen = len(self.hunk)
684 hlen = len(self.hunk)
689 for x in xrange(hlen-1):
685 for x in xrange(hlen-1):
690 # the hunk starts with the @@ line, so use x+1
686 # the hunk starts with the @@ line, so use x+1
691 if self.hunk[x+1][0] == ' ':
687 if self.hunk[x+1][0] == ' ':
692 top += 1
688 top += 1
693 else:
689 else:
694 break
690 break
695 if not toponly:
691 if not toponly:
696 for x in xrange(hlen-1):
692 for x in xrange(hlen-1):
697 if self.hunk[hlen-bot-1][0] == ' ':
693 if self.hunk[hlen-bot-1][0] == ' ':
698 bot += 1
694 bot += 1
699 else:
695 else:
700 break
696 break
701
697
702 # top and bot now count context in the hunk
698 # top and bot now count context in the hunk
703 # adjust them if either one is short
699 # adjust them if either one is short
704 context = max(top, bot, 3)
700 context = max(top, bot, 3)
705 if bot < context:
701 if bot < context:
706 bot = max(0, fuzz - (context - bot))
702 bot = max(0, fuzz - (context - bot))
707 else:
703 else:
708 bot = min(fuzz, bot)
704 bot = min(fuzz, bot)
709 if top < context:
705 if top < context:
710 top = max(0, fuzz - (context - top))
706 top = max(0, fuzz - (context - top))
711 else:
707 else:
712 top = min(fuzz, top)
708 top = min(fuzz, top)
713
709
714 return l[top:len(l)-bot]
710 return l[top:len(l)-bot]
715 return l
711 return l
716
712
717 def old(self, fuzz=0, toponly=False):
713 def old(self, fuzz=0, toponly=False):
718 return self.fuzzit(self.a, fuzz, toponly)
714 return self.fuzzit(self.a, fuzz, toponly)
719
715
720 def newctrl(self):
716 def newctrl(self):
721 res = []
717 res = []
722 for x in self.hunk:
718 for x in self.hunk:
723 c = x[0]
719 c = x[0]
724 if c == ' ' or c == '+':
720 if c == ' ' or c == '+':
725 res.append(x)
721 res.append(x)
726 return res
722 return res
727
723
728 def new(self, fuzz=0, toponly=False):
724 def new(self, fuzz=0, toponly=False):
729 return self.fuzzit(self.b, fuzz, toponly)
725 return self.fuzzit(self.b, fuzz, toponly)
730
726
731 class binhunk:
727 class binhunk:
732 'A binary patch file. Only understands literals so far.'
728 'A binary patch file. Only understands literals so far.'
733 def __init__(self, gitpatch):
729 def __init__(self, gitpatch):
734 self.gitpatch = gitpatch
730 self.gitpatch = gitpatch
735 self.text = None
731 self.text = None
736 self.hunk = ['GIT binary patch\n']
732 self.hunk = ['GIT binary patch\n']
737
733
738 def createfile(self):
734 def createfile(self):
739 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
735 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
740
736
741 def rmfile(self):
737 def rmfile(self):
742 return self.gitpatch.op == 'DELETE'
738 return self.gitpatch.op == 'DELETE'
743
739
744 def complete(self):
740 def complete(self):
745 return self.text is not None
741 return self.text is not None
746
742
747 def new(self):
743 def new(self):
748 return [self.text]
744 return [self.text]
749
745
750 def extract(self, fp):
746 def extract(self, fp):
751 line = fp.readline()
747 line = fp.readline()
752 self.hunk.append(line)
748 self.hunk.append(line)
753 while line and not line.startswith('literal '):
749 while line and not line.startswith('literal '):
754 line = fp.readline()
750 line = fp.readline()
755 self.hunk.append(line)
751 self.hunk.append(line)
756 if not line:
752 if not line:
757 raise PatchError(_('could not extract binary patch'))
753 raise PatchError(_('could not extract binary patch'))
758 size = int(line[8:].rstrip())
754 size = int(line[8:].rstrip())
759 dec = []
755 dec = []
760 line = fp.readline()
756 line = fp.readline()
761 self.hunk.append(line)
757 self.hunk.append(line)
762 while len(line) > 1:
758 while len(line) > 1:
763 l = line[0]
759 l = line[0]
764 if l <= 'Z' and l >= 'A':
760 if l <= 'Z' and l >= 'A':
765 l = ord(l) - ord('A') + 1
761 l = ord(l) - ord('A') + 1
766 else:
762 else:
767 l = ord(l) - ord('a') + 27
763 l = ord(l) - ord('a') + 27
768 dec.append(base85.b85decode(line[1:-1])[:l])
764 dec.append(base85.b85decode(line[1:-1])[:l])
769 line = fp.readline()
765 line = fp.readline()
770 self.hunk.append(line)
766 self.hunk.append(line)
771 text = zlib.decompress(''.join(dec))
767 text = zlib.decompress(''.join(dec))
772 if len(text) != size:
768 if len(text) != size:
773 raise PatchError(_('binary patch is %d bytes, not %d') %
769 raise PatchError(_('binary patch is %d bytes, not %d') %
774 len(text), size)
770 len(text), size)
775 self.text = text
771 self.text = text
776
772
777 def parsefilename(str):
773 def parsefilename(str):
778 # --- filename \t|space stuff
774 # --- filename \t|space stuff
779 s = str[4:].rstrip('\r\n')
775 s = str[4:].rstrip('\r\n')
780 i = s.find('\t')
776 i = s.find('\t')
781 if i < 0:
777 if i < 0:
782 i = s.find(' ')
778 i = s.find(' ')
783 if i < 0:
779 if i < 0:
784 return s
780 return s
785 return s[:i]
781 return s[:i]
786
782
787 def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):
783 def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):
788 def pathstrip(path, count=1):
784 def pathstrip(path, count=1):
789 pathlen = len(path)
785 pathlen = len(path)
790 i = 0
786 i = 0
791 if count == 0:
787 if count == 0:
792 return '', path.rstrip()
788 return '', path.rstrip()
793 while count > 0:
789 while count > 0:
794 i = path.find('/', i)
790 i = path.find('/', i)
795 if i == -1:
791 if i == -1:
796 raise PatchError(_("unable to strip away %d dirs from %s") %
792 raise PatchError(_("unable to strip away %d dirs from %s") %
797 (count, path))
793 (count, path))
798 i += 1
794 i += 1
799 # consume '//' in the path
795 # consume '//' in the path
800 while i < pathlen - 1 and path[i] == '/':
796 while i < pathlen - 1 and path[i] == '/':
801 i += 1
797 i += 1
802 count -= 1
798 count -= 1
803 return path[:i].lstrip(), path[i:].rstrip()
799 return path[:i].lstrip(), path[i:].rstrip()
804
800
805 nulla = afile_orig == "/dev/null"
801 nulla = afile_orig == "/dev/null"
806 nullb = bfile_orig == "/dev/null"
802 nullb = bfile_orig == "/dev/null"
807 abase, afile = pathstrip(afile_orig, strip)
803 abase, afile = pathstrip(afile_orig, strip)
808 gooda = not nulla and os.path.exists(afile)
804 gooda = not nulla and os.path.exists(afile)
809 bbase, bfile = pathstrip(bfile_orig, strip)
805 bbase, bfile = pathstrip(bfile_orig, strip)
810 if afile == bfile:
806 if afile == bfile:
811 goodb = gooda
807 goodb = gooda
812 else:
808 else:
813 goodb = not nullb and os.path.exists(bfile)
809 goodb = not nullb and os.path.exists(bfile)
814 createfunc = hunk.createfile
810 createfunc = hunk.createfile
815 if reverse:
811 if reverse:
816 createfunc = hunk.rmfile
812 createfunc = hunk.rmfile
817 missing = not goodb and not gooda and not createfunc()
813 missing = not goodb and not gooda and not createfunc()
818 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
814 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
819 # diff is between a file and its backup. In this case, the original
815 # diff is between a file and its backup. In this case, the original
820 # file should be patched (see original mpatch code).
816 # file should be patched (see original mpatch code).
821 isbackup = (abase == bbase and bfile.startswith(afile))
817 isbackup = (abase == bbase and bfile.startswith(afile))
822 fname = None
818 fname = None
823 if not missing:
819 if not missing:
824 if gooda and goodb:
820 if gooda and goodb:
825 fname = isbackup and afile or bfile
821 fname = isbackup and afile or bfile
826 elif gooda:
822 elif gooda:
827 fname = afile
823 fname = afile
828
824
829 if not fname:
825 if not fname:
830 if not nullb:
826 if not nullb:
831 fname = isbackup and afile or bfile
827 fname = isbackup and afile or bfile
832 elif not nulla:
828 elif not nulla:
833 fname = afile
829 fname = afile
834 else:
830 else:
835 raise PatchError(_("undefined source and destination files"))
831 raise PatchError(_("undefined source and destination files"))
836
832
837 return fname, missing
833 return fname, missing
838
834
839 class linereader:
835 class linereader:
840 # simple class to allow pushing lines back into the input stream
836 # simple class to allow pushing lines back into the input stream
841 def __init__(self, fp):
837 def __init__(self, fp):
842 self.fp = fp
838 self.fp = fp
843 self.buf = []
839 self.buf = []
844
840
845 def push(self, line):
841 def push(self, line):
846 self.buf.append(line)
842 self.buf.append(line)
847
843
848 def readline(self):
844 def readline(self):
849 if self.buf:
845 if self.buf:
850 l = self.buf[0]
846 l = self.buf[0]
851 del self.buf[0]
847 del self.buf[0]
852 return l
848 return l
853 return self.fp.readline()
849 return self.fp.readline()
854
850
855 def iterhunks(ui, fp, sourcefile=None):
851 def iterhunks(ui, fp, sourcefile=None):
856 """Read a patch and yield the following events:
852 """Read a patch and yield the following events:
857 - ("file", afile, bfile, firsthunk): select a new target file.
853 - ("file", afile, bfile, firsthunk): select a new target file.
858 - ("hunk", hunk): a new hunk is ready to be applied, follows a
854 - ("hunk", hunk): a new hunk is ready to be applied, follows a
859 "file" event.
855 "file" event.
860 - ("git", gitchanges): current diff is in git format, gitchanges
856 - ("git", gitchanges): current diff is in git format, gitchanges
861 maps filenames to gitpatch records. Unique event.
857 maps filenames to gitpatch records. Unique event.
862 """
858 """
863
859
864 def scangitpatch(fp, firstline):
860 def scangitpatch(fp, firstline):
865 '''git patches can modify a file, then copy that file to
861 '''git patches can modify a file, then copy that file to
866 a new file, but expect the source to be the unmodified form.
862 a new file, but expect the source to be the unmodified form.
867 So we scan the patch looking for that case so we can do
863 So we scan the patch looking for that case so we can do
868 the copies ahead of time.'''
864 the copies ahead of time.'''
869
865
870 pos = 0
866 pos = 0
871 try:
867 try:
872 pos = fp.tell()
868 pos = fp.tell()
873 except IOError:
869 except IOError:
874 fp = cStringIO.StringIO(fp.read())
870 fp = cStringIO.StringIO(fp.read())
875
871
876 (dopatch, gitpatches) = readgitpatch(fp, firstline)
872 (dopatch, gitpatches) = readgitpatch(fp, firstline)
877 fp.seek(pos)
873 fp.seek(pos)
878
874
879 return fp, dopatch, gitpatches
875 return fp, dopatch, gitpatches
880
876
881 changed = {}
877 changed = {}
882 current_hunk = None
878 current_hunk = None
883 afile = ""
879 afile = ""
884 bfile = ""
880 bfile = ""
885 state = None
881 state = None
886 hunknum = 0
882 hunknum = 0
887 emitfile = False
883 emitfile = False
888
884
889 git = False
885 git = False
890 gitre = re.compile('diff --git (a/.*) (b/.*)')
886 gitre = re.compile('diff --git (a/.*) (b/.*)')
891
887
892 # our states
888 # our states
893 BFILE = 1
889 BFILE = 1
894 context = None
890 context = None
895 lr = linereader(fp)
891 lr = linereader(fp)
896 dopatch = True
892 dopatch = True
897 # gitworkdone is True if a git operation (copy, rename, ...) was
893 # gitworkdone is True if a git operation (copy, rename, ...) was
898 # performed already for the current file. Useful when the file
894 # performed already for the current file. Useful when the file
899 # section may have no hunk.
895 # section may have no hunk.
900 gitworkdone = False
896 gitworkdone = False
901
897
902 while True:
898 while True:
903 newfile = False
899 newfile = False
904 x = lr.readline()
900 x = lr.readline()
905 if not x:
901 if not x:
906 break
902 break
907 if current_hunk:
903 if current_hunk:
908 if x.startswith('\ '):
904 if x.startswith('\ '):
909 current_hunk.fix_newline()
905 current_hunk.fix_newline()
910 yield 'hunk', current_hunk
906 yield 'hunk', current_hunk
911 current_hunk = None
907 current_hunk = None
912 gitworkdone = False
908 gitworkdone = False
913 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
909 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
914 ((context or context == None) and x.startswith('***************')))):
910 ((context or context == None) and x.startswith('***************')))):
915 try:
911 try:
916 if context == None and x.startswith('***************'):
912 if context == None and x.startswith('***************'):
917 context = True
913 context = True
918 gpatch = changed.get(bfile[2:], (None, None))[1]
914 gpatch = changed.get(bfile[2:], (None, None))[1]
919 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
915 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
920 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
916 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
921 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
917 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
922 except PatchError, err:
918 except PatchError, err:
923 ui.debug(err)
919 ui.debug(err)
924 current_hunk = None
920 current_hunk = None
925 continue
921 continue
926 hunknum += 1
922 hunknum += 1
927 if emitfile:
923 if emitfile:
928 emitfile = False
924 emitfile = False
929 yield 'file', (afile, bfile, current_hunk)
925 yield 'file', (afile, bfile, current_hunk)
930 elif state == BFILE and x.startswith('GIT binary patch'):
926 elif state == BFILE and x.startswith('GIT binary patch'):
931 current_hunk = binhunk(changed[bfile[2:]][1])
927 current_hunk = binhunk(changed[bfile[2:]][1])
932 hunknum += 1
928 hunknum += 1
933 if emitfile:
929 if emitfile:
934 emitfile = False
930 emitfile = False
935 yield 'file', (afile, bfile, current_hunk)
931 yield 'file', (afile, bfile, current_hunk)
936 current_hunk.extract(fp)
932 current_hunk.extract(fp)
937 elif x.startswith('diff --git'):
933 elif x.startswith('diff --git'):
938 # check for git diff, scanning the whole patch file if needed
934 # check for git diff, scanning the whole patch file if needed
939 m = gitre.match(x)
935 m = gitre.match(x)
940 if m:
936 if m:
941 afile, bfile = m.group(1, 2)
937 afile, bfile = m.group(1, 2)
942 if not git:
938 if not git:
943 git = True
939 git = True
944 fp, dopatch, gitpatches = scangitpatch(fp, x)
940 fp, dopatch, gitpatches = scangitpatch(fp, x)
945 yield 'git', gitpatches
941 yield 'git', gitpatches
946 for gp in gitpatches:
942 for gp in gitpatches:
947 changed[gp.path] = (gp.op, gp)
943 changed[gp.path] = (gp.op, gp)
948 # else error?
944 # else error?
949 # copy/rename + modify should modify target, not source
945 # copy/rename + modify should modify target, not source
950 gitop = changed.get(bfile[2:], (None, None))[0]
946 gitop = changed.get(bfile[2:], (None, None))[0]
951 if gitop in ('COPY', 'DELETE', 'RENAME'):
947 if gitop in ('COPY', 'DELETE', 'RENAME'):
952 afile = bfile
948 afile = bfile
953 gitworkdone = True
949 gitworkdone = True
954 newfile = True
950 newfile = True
955 elif x.startswith('---'):
951 elif x.startswith('---'):
956 # check for a unified diff
952 # check for a unified diff
957 l2 = lr.readline()
953 l2 = lr.readline()
958 if not l2.startswith('+++'):
954 if not l2.startswith('+++'):
959 lr.push(l2)
955 lr.push(l2)
960 continue
956 continue
961 newfile = True
957 newfile = True
962 context = False
958 context = False
963 afile = parsefilename(x)
959 afile = parsefilename(x)
964 bfile = parsefilename(l2)
960 bfile = parsefilename(l2)
965 elif x.startswith('***'):
961 elif x.startswith('***'):
966 # check for a context diff
962 # check for a context diff
967 l2 = lr.readline()
963 l2 = lr.readline()
968 if not l2.startswith('---'):
964 if not l2.startswith('---'):
969 lr.push(l2)
965 lr.push(l2)
970 continue
966 continue
971 l3 = lr.readline()
967 l3 = lr.readline()
972 lr.push(l3)
968 lr.push(l3)
973 if not l3.startswith("***************"):
969 if not l3.startswith("***************"):
974 lr.push(l2)
970 lr.push(l2)
975 continue
971 continue
976 newfile = True
972 newfile = True
977 context = True
973 context = True
978 afile = parsefilename(x)
974 afile = parsefilename(x)
979 bfile = parsefilename(l2)
975 bfile = parsefilename(l2)
980
976
981 if newfile:
977 if newfile:
982 emitfile = True
978 emitfile = True
983 state = BFILE
979 state = BFILE
984 hunknum = 0
980 hunknum = 0
985 if current_hunk:
981 if current_hunk:
986 if current_hunk.complete():
982 if current_hunk.complete():
987 yield 'hunk', current_hunk
983 yield 'hunk', current_hunk
988 else:
984 else:
989 raise PatchError(_("malformed patch %s %s") % (afile,
985 raise PatchError(_("malformed patch %s %s") % (afile,
990 current_hunk.desc))
986 current_hunk.desc))
991
987
992 if hunknum == 0 and dopatch and not gitworkdone:
988 if hunknum == 0 and dopatch and not gitworkdone:
993 raise NoHunks
989 raise NoHunks
994
990
995 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
991 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
996 rejmerge=None, updatedir=None):
992 rejmerge=None, updatedir=None):
997 """reads a patch from fp and tries to apply it. The dict 'changed' is
993 """reads a patch from fp and tries to apply it. The dict 'changed' is
998 filled in with all of the filenames changed by the patch. Returns 0
994 filled in with all of the filenames changed by the patch. Returns 0
999 for a clean patch, -1 if any rejects were found and 1 if there was
995 for a clean patch, -1 if any rejects were found and 1 if there was
1000 any fuzz."""
996 any fuzz."""
1001
997
1002 rejects = 0
998 rejects = 0
1003 err = 0
999 err = 0
1004 current_file = None
1000 current_file = None
1005 gitpatches = None
1001 gitpatches = None
1006
1002
1007 def closefile():
1003 def closefile():
1008 if not current_file:
1004 if not current_file:
1009 return 0
1005 return 0
1010 current_file.close()
1006 current_file.close()
1011 if rejmerge:
1007 if rejmerge:
1012 rejmerge(current_file)
1008 rejmerge(current_file)
1013 return len(current_file.rej)
1009 return len(current_file.rej)
1014
1010
1015 for state, values in iterhunks(ui, fp, sourcefile):
1011 for state, values in iterhunks(ui, fp, sourcefile):
1016 if state == 'hunk':
1012 if state == 'hunk':
1017 if not current_file:
1013 if not current_file:
1018 continue
1014 continue
1019 current_hunk = values
1015 current_hunk = values
1020 ret = current_file.apply(current_hunk, reverse)
1016 ret = current_file.apply(current_hunk, reverse)
1021 if ret >= 0:
1017 if ret >= 0:
1022 changed.setdefault(current_file.fname, (None, None))
1018 changed.setdefault(current_file.fname, (None, None))
1023 if ret > 0:
1019 if ret > 0:
1024 err = 1
1020 err = 1
1025 elif state == 'file':
1021 elif state == 'file':
1026 rejects += closefile()
1022 rejects += closefile()
1027 afile, bfile, first_hunk = values
1023 afile, bfile, first_hunk = values
1028 try:
1024 try:
1029 if sourcefile:
1025 if sourcefile:
1030 current_file = patchfile(ui, sourcefile)
1026 current_file = patchfile(ui, sourcefile)
1031 else:
1027 else:
1032 current_file, missing = selectfile(afile, bfile, first_hunk,
1028 current_file, missing = selectfile(afile, bfile, first_hunk,
1033 strip, reverse)
1029 strip, reverse)
1034 current_file = patchfile(ui, current_file, missing)
1030 current_file = patchfile(ui, current_file, missing)
1035 except PatchError, err:
1031 except PatchError, err:
1036 ui.warn(str(err) + '\n')
1032 ui.warn(str(err) + '\n')
1037 current_file, current_hunk = None, None
1033 current_file, current_hunk = None, None
1038 rejects += 1
1034 rejects += 1
1039 continue
1035 continue
1040 elif state == 'git':
1036 elif state == 'git':
1041 gitpatches = values
1037 gitpatches = values
1042 cwd = os.getcwd()
1038 cwd = os.getcwd()
1043 for gp in gitpatches:
1039 for gp in gitpatches:
1044 if gp.op in ('COPY', 'RENAME'):
1040 if gp.op in ('COPY', 'RENAME'):
1045 src, dst = [util.canonpath(cwd, cwd, x)
1041 src, dst = [util.canonpath(cwd, cwd, x)
1046 for x in [gp.oldpath, gp.path]]
1042 for x in [gp.oldpath, gp.path]]
1047 copyfile(src, dst)
1043 copyfile(src, dst)
1048 changed[gp.path] = (gp.op, gp)
1044 changed[gp.path] = (gp.op, gp)
1049 else:
1045 else:
1050 raise util.Abort(_('unsupported parser state: %s') % state)
1046 raise util.Abort(_('unsupported parser state: %s') % state)
1051
1047
1052 rejects += closefile()
1048 rejects += closefile()
1053
1049
1054 if updatedir and gitpatches:
1050 if updatedir and gitpatches:
1055 updatedir(gitpatches)
1051 updatedir(gitpatches)
1056 if rejects:
1052 if rejects:
1057 return -1
1053 return -1
1058 return err
1054 return err
1059
1055
1060 def diffopts(ui, opts={}, untrusted=False):
1056 def diffopts(ui, opts={}, untrusted=False):
1061 def get(key, name=None, getter=ui.configbool):
1057 def get(key, name=None, getter=ui.configbool):
1062 return (opts.get(key) or
1058 return (opts.get(key) or
1063 getter('diff', name or key, None, untrusted=untrusted))
1059 getter('diff', name or key, None, untrusted=untrusted))
1064 return mdiff.diffopts(
1060 return mdiff.diffopts(
1065 text=opts.get('text'),
1061 text=opts.get('text'),
1066 git=get('git'),
1062 git=get('git'),
1067 nodates=get('nodates'),
1063 nodates=get('nodates'),
1068 showfunc=get('show_function', 'showfunc'),
1064 showfunc=get('show_function', 'showfunc'),
1069 ignorews=get('ignore_all_space', 'ignorews'),
1065 ignorews=get('ignore_all_space', 'ignorews'),
1070 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1066 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1071 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1067 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1072 context=get('unified', getter=ui.config))
1068 context=get('unified', getter=ui.config))
1073
1069
1074 def updatedir(ui, repo, patches):
1070 def updatedir(ui, repo, patches):
1075 '''Update dirstate after patch application according to metadata'''
1071 '''Update dirstate after patch application according to metadata'''
1076 if not patches:
1072 if not patches:
1077 return
1073 return
1078 copies = []
1074 copies = []
1079 removes = {}
1075 removes = {}
1080 cfiles = patches.keys()
1076 cfiles = patches.keys()
1081 cwd = repo.getcwd()
1077 cwd = repo.getcwd()
1082 if cwd:
1078 if cwd:
1083 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1079 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1084 for f in patches:
1080 for f in patches:
1085 ctype, gp = patches[f]
1081 ctype, gp = patches[f]
1086 if ctype == 'RENAME':
1082 if ctype == 'RENAME':
1087 copies.append((gp.oldpath, gp.path))
1083 copies.append((gp.oldpath, gp.path))
1088 removes[gp.oldpath] = 1
1084 removes[gp.oldpath] = 1
1089 elif ctype == 'COPY':
1085 elif ctype == 'COPY':
1090 copies.append((gp.oldpath, gp.path))
1086 copies.append((gp.oldpath, gp.path))
1091 elif ctype == 'DELETE':
1087 elif ctype == 'DELETE':
1092 removes[gp.path] = 1
1088 removes[gp.path] = 1
1093 for src, dst in copies:
1089 for src, dst in copies:
1094 repo.copy(src, dst)
1090 repo.copy(src, dst)
1095 removes = removes.keys()
1091 removes = removes.keys()
1096 if removes:
1092 if removes:
1097 repo.remove(util.sort(removes), True)
1093 repo.remove(util.sort(removes), True)
1098 for f in patches:
1094 for f in patches:
1099 ctype, gp = patches[f]
1095 ctype, gp = patches[f]
1100 if gp and gp.mode:
1096 if gp and gp.mode:
1101 flags = ''
1097 flags = ''
1102 if gp.mode & 0100:
1098 if gp.mode & 0100:
1103 flags = 'x'
1099 flags = 'x'
1104 elif gp.mode & 020000:
1100 elif gp.mode & 020000:
1105 flags = 'l'
1101 flags = 'l'
1106 dst = os.path.join(repo.root, gp.path)
1102 dst = os.path.join(repo.root, gp.path)
1107 # patch won't create empty files
1103 # patch won't create empty files
1108 if ctype == 'ADD' and not os.path.exists(dst):
1104 if ctype == 'ADD' and not os.path.exists(dst):
1109 repo.wwrite(gp.path, '', flags)
1105 repo.wwrite(gp.path, '', flags)
1110 else:
1106 else:
1111 util.set_flags(dst, 'l' in flags, 'x' in flags)
1107 util.set_flags(dst, 'l' in flags, 'x' in flags)
1112 cmdutil.addremove(repo, cfiles)
1108 cmdutil.addremove(repo, cfiles)
1113 files = patches.keys()
1109 files = patches.keys()
1114 files.extend([r for r in removes if r not in files])
1110 files.extend([r for r in removes if r not in files])
1115 return util.sort(files)
1111 return util.sort(files)
1116
1112
1117 def b85diff(to, tn):
1113 def b85diff(to, tn):
1118 '''print base85-encoded binary diff'''
1114 '''print base85-encoded binary diff'''
1119 def gitindex(text):
1115 def gitindex(text):
1120 if not text:
1116 if not text:
1121 return '0' * 40
1117 return '0' * 40
1122 l = len(text)
1118 l = len(text)
1123 s = util.sha1('blob %d\0' % l)
1119 s = util.sha1('blob %d\0' % l)
1124 s.update(text)
1120 s.update(text)
1125 return s.hexdigest()
1121 return s.hexdigest()
1126
1122
1127 def fmtline(line):
1123 def fmtline(line):
1128 l = len(line)
1124 l = len(line)
1129 if l <= 26:
1125 if l <= 26:
1130 l = chr(ord('A') + l - 1)
1126 l = chr(ord('A') + l - 1)
1131 else:
1127 else:
1132 l = chr(l - 26 + ord('a') - 1)
1128 l = chr(l - 26 + ord('a') - 1)
1133 return '%c%s\n' % (l, base85.b85encode(line, True))
1129 return '%c%s\n' % (l, base85.b85encode(line, True))
1134
1130
1135 def chunk(text, csize=52):
1131 def chunk(text, csize=52):
1136 l = len(text)
1132 l = len(text)
1137 i = 0
1133 i = 0
1138 while i < l:
1134 while i < l:
1139 yield text[i:i+csize]
1135 yield text[i:i+csize]
1140 i += csize
1136 i += csize
1141
1137
1142 tohash = gitindex(to)
1138 tohash = gitindex(to)
1143 tnhash = gitindex(tn)
1139 tnhash = gitindex(tn)
1144 if tohash == tnhash:
1140 if tohash == tnhash:
1145 return ""
1141 return ""
1146
1142
1147 # TODO: deltas
1143 # TODO: deltas
1148 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1144 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1149 (tohash, tnhash, len(tn))]
1145 (tohash, tnhash, len(tn))]
1150 for l in chunk(zlib.compress(tn)):
1146 for l in chunk(zlib.compress(tn)):
1151 ret.append(fmtline(l))
1147 ret.append(fmtline(l))
1152 ret.append('\n')
1148 ret.append('\n')
1153 return ''.join(ret)
1149 return ''.join(ret)
1154
1150
1155 def diff(repo, node1=None, node2=None, match=None,
1151 def diff(repo, node1=None, node2=None, match=None,
1156 fp=None, changes=None, opts=None):
1152 fp=None, changes=None, opts=None):
1157 '''print diff of changes to files between two nodes, or node and
1153 '''print diff of changes to files between two nodes, or node and
1158 working directory.
1154 working directory.
1159
1155
1160 if node1 is None, use first dirstate parent instead.
1156 if node1 is None, use first dirstate parent instead.
1161 if node2 is None, compare node1 with working directory.'''
1157 if node2 is None, compare node1 with working directory.'''
1162
1158
1163 if not match:
1159 if not match:
1164 match = cmdutil.matchall(repo)
1160 match = cmdutil.matchall(repo)
1165
1161
1166 if opts is None:
1162 if opts is None:
1167 opts = mdiff.defaultopts
1163 opts = mdiff.defaultopts
1168 if fp is None:
1164 if fp is None:
1169 fp = repo.ui
1165 fp = repo.ui
1170
1166
1171 if not node1:
1167 if not node1:
1172 node1 = repo.dirstate.parents()[0]
1168 node1 = repo.dirstate.parents()[0]
1173
1169
1174 flcache = {}
1170 flcache = {}
1175 def getfilectx(f, ctx):
1171 def getfilectx(f, ctx):
1176 flctx = ctx.filectx(f, filelog=flcache.get(f))
1172 flctx = ctx.filectx(f, filelog=flcache.get(f))
1177 if f not in flcache:
1173 if f not in flcache:
1178 flcache[f] = flctx._filelog
1174 flcache[f] = flctx._filelog
1179 return flctx
1175 return flctx
1180
1176
1181 # reading the data for node1 early allows it to play nicely
1177 # reading the data for node1 early allows it to play nicely
1182 # with repo.status and the revlog cache.
1178 # with repo.status and the revlog cache.
1183 ctx1 = repo[node1]
1179 ctx1 = repo[node1]
1184 # force manifest reading
1180 # force manifest reading
1185 man1 = ctx1.manifest()
1181 man1 = ctx1.manifest()
1186 date1 = util.datestr(ctx1.date())
1182 date1 = util.datestr(ctx1.date())
1187
1183
1188 if not changes:
1184 if not changes:
1189 changes = repo.status(node1, node2, match=match)
1185 changes = repo.status(node1, node2, match=match)
1190 modified, added, removed = changes[:3]
1186 modified, added, removed = changes[:3]
1191
1187
1192 if not modified and not added and not removed:
1188 if not modified and not added and not removed:
1193 return
1189 return
1194
1190
1195 ctx2 = repo[node2]
1191 ctx2 = repo[node2]
1196
1192
1197 if repo.ui.quiet:
1193 if repo.ui.quiet:
1198 r = None
1194 r = None
1199 else:
1195 else:
1200 hexfunc = repo.ui.debugflag and hex or short
1196 hexfunc = repo.ui.debugflag and hex or short
1201 r = [hexfunc(node) for node in [node1, node2] if node]
1197 r = [hexfunc(node) for node in [node1, node2] if node]
1202
1198
1203 if opts.git:
1199 if opts.git:
1204 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1200 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1205 for k, v in copy.items():
1201 for k, v in copy.items():
1206 copy[v] = k
1202 copy[v] = k
1207
1203
1208 gone = {}
1204 gone = {}
1209 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1205 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1210
1206
1211 for f in util.sort(modified + added + removed):
1207 for f in util.sort(modified + added + removed):
1212 to = None
1208 to = None
1213 tn = None
1209 tn = None
1214 dodiff = True
1210 dodiff = True
1215 header = []
1211 header = []
1216 if f in man1:
1212 if f in man1:
1217 to = getfilectx(f, ctx1).data()
1213 to = getfilectx(f, ctx1).data()
1218 if f not in removed:
1214 if f not in removed:
1219 tn = getfilectx(f, ctx2).data()
1215 tn = getfilectx(f, ctx2).data()
1220 a, b = f, f
1216 a, b = f, f
1221 if opts.git:
1217 if opts.git:
1222 def addmodehdr(header, omode, nmode):
1218 def addmodehdr(header, omode, nmode):
1223 if omode != nmode:
1219 if omode != nmode:
1224 header.append('old mode %s\n' % omode)
1220 header.append('old mode %s\n' % omode)
1225 header.append('new mode %s\n' % nmode)
1221 header.append('new mode %s\n' % nmode)
1226
1222
1227 if f in added:
1223 if f in added:
1228 mode = gitmode[ctx2.flags(f)]
1224 mode = gitmode[ctx2.flags(f)]
1229 if f in copy:
1225 if f in copy:
1230 a = copy[f]
1226 a = copy[f]
1231 omode = gitmode[man1.flags(a)]
1227 omode = gitmode[man1.flags(a)]
1232 addmodehdr(header, omode, mode)
1228 addmodehdr(header, omode, mode)
1233 if a in removed and a not in gone:
1229 if a in removed and a not in gone:
1234 op = 'rename'
1230 op = 'rename'
1235 gone[a] = 1
1231 gone[a] = 1
1236 else:
1232 else:
1237 op = 'copy'
1233 op = 'copy'
1238 header.append('%s from %s\n' % (op, a))
1234 header.append('%s from %s\n' % (op, a))
1239 header.append('%s to %s\n' % (op, f))
1235 header.append('%s to %s\n' % (op, f))
1240 to = getfilectx(a, ctx1).data()
1236 to = getfilectx(a, ctx1).data()
1241 else:
1237 else:
1242 header.append('new file mode %s\n' % mode)
1238 header.append('new file mode %s\n' % mode)
1243 if util.binary(tn):
1239 if util.binary(tn):
1244 dodiff = 'binary'
1240 dodiff = 'binary'
1245 elif f in removed:
1241 elif f in removed:
1246 # have we already reported a copy above?
1242 # have we already reported a copy above?
1247 if f in copy and copy[f] in added and copy[copy[f]] == f:
1243 if f in copy and copy[f] in added and copy[copy[f]] == f:
1248 dodiff = False
1244 dodiff = False
1249 else:
1245 else:
1250 header.append('deleted file mode %s\n' %
1246 header.append('deleted file mode %s\n' %
1251 gitmode[man1.flags(f)])
1247 gitmode[man1.flags(f)])
1252 else:
1248 else:
1253 omode = gitmode[man1.flags(f)]
1249 omode = gitmode[man1.flags(f)]
1254 nmode = gitmode[ctx2.flags(f)]
1250 nmode = gitmode[ctx2.flags(f)]
1255 addmodehdr(header, omode, nmode)
1251 addmodehdr(header, omode, nmode)
1256 if util.binary(to) or util.binary(tn):
1252 if util.binary(to) or util.binary(tn):
1257 dodiff = 'binary'
1253 dodiff = 'binary'
1258 r = None
1254 r = None
1259 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
1255 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
1260 if dodiff:
1256 if dodiff:
1261 if dodiff == 'binary':
1257 if dodiff == 'binary':
1262 text = b85diff(to, tn)
1258 text = b85diff(to, tn)
1263 else:
1259 else:
1264 text = mdiff.unidiff(to, date1,
1260 text = mdiff.unidiff(to, date1,
1265 # ctx2 date may be dynamic
1261 # ctx2 date may be dynamic
1266 tn, util.datestr(ctx2.date()),
1262 tn, util.datestr(ctx2.date()),
1267 a, b, r, opts=opts)
1263 a, b, r, opts=opts)
1268 if text or len(header) > 1:
1264 if text or len(header) > 1:
1269 fp.write(''.join(header))
1265 fp.write(''.join(header))
1270 fp.write(text)
1266 fp.write(text)
1271
1267
1272 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1268 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1273 opts=None):
1269 opts=None):
1274 '''export changesets as hg patches.'''
1270 '''export changesets as hg patches.'''
1275
1271
1276 total = len(revs)
1272 total = len(revs)
1277 revwidth = max([len(str(rev)) for rev in revs])
1273 revwidth = max([len(str(rev)) for rev in revs])
1278
1274
1279 def single(rev, seqno, fp):
1275 def single(rev, seqno, fp):
1280 ctx = repo[rev]
1276 ctx = repo[rev]
1281 node = ctx.node()
1277 node = ctx.node()
1282 parents = [p.node() for p in ctx.parents() if p]
1278 parents = [p.node() for p in ctx.parents() if p]
1283 branch = ctx.branch()
1279 branch = ctx.branch()
1284 if switch_parent:
1280 if switch_parent:
1285 parents.reverse()
1281 parents.reverse()
1286 prev = (parents and parents[0]) or nullid
1282 prev = (parents and parents[0]) or nullid
1287
1283
1288 if not fp:
1284 if not fp:
1289 fp = cmdutil.make_file(repo, template, node, total=total,
1285 fp = cmdutil.make_file(repo, template, node, total=total,
1290 seqno=seqno, revwidth=revwidth)
1286 seqno=seqno, revwidth=revwidth)
1291 if fp != sys.stdout and hasattr(fp, 'name'):
1287 if fp != sys.stdout and hasattr(fp, 'name'):
1292 repo.ui.note("%s\n" % fp.name)
1288 repo.ui.note("%s\n" % fp.name)
1293
1289
1294 fp.write("# HG changeset patch\n")
1290 fp.write("# HG changeset patch\n")
1295 fp.write("# User %s\n" % ctx.user())
1291 fp.write("# User %s\n" % ctx.user())
1296 fp.write("# Date %d %d\n" % ctx.date())
1292 fp.write("# Date %d %d\n" % ctx.date())
1297 if branch and (branch != 'default'):
1293 if branch and (branch != 'default'):
1298 fp.write("# Branch %s\n" % branch)
1294 fp.write("# Branch %s\n" % branch)
1299 fp.write("# Node ID %s\n" % hex(node))
1295 fp.write("# Node ID %s\n" % hex(node))
1300 fp.write("# Parent %s\n" % hex(prev))
1296 fp.write("# Parent %s\n" % hex(prev))
1301 if len(parents) > 1:
1297 if len(parents) > 1:
1302 fp.write("# Parent %s\n" % hex(parents[1]))
1298 fp.write("# Parent %s\n" % hex(parents[1]))
1303 fp.write(ctx.description().rstrip())
1299 fp.write(ctx.description().rstrip())
1304 fp.write("\n\n")
1300 fp.write("\n\n")
1305
1301
1306 diff(repo, prev, node, fp=fp, opts=opts)
1302 diff(repo, prev, node, fp=fp, opts=opts)
1307 if fp not in (sys.stdout, repo.ui):
1303 if fp not in (sys.stdout, repo.ui):
1308 fp.close()
1304 fp.close()
1309
1305
1310 for seqno, rev in enumerate(revs):
1306 for seqno, rev in enumerate(revs):
1311 single(rev, seqno+1, fp)
1307 single(rev, seqno+1, fp)
1312
1308
1313 def diffstat(patchlines):
1309 def diffstat(patchlines):
1314 if not util.find_exe('diffstat'):
1310 if not util.find_exe('diffstat'):
1315 return
1311 return
1316 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
1312 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
1317 try:
1313 try:
1318 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
1314 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
1319 try:
1315 try:
1320 for line in patchlines:
1316 for line in patchlines:
1321 p.tochild.write(line + "\n")
1317 p.tochild.write(line + "\n")
1322 p.tochild.close()
1318 p.tochild.close()
1323 if p.wait(): return
1319 if p.wait(): return
1324 fp = os.fdopen(fd, 'r')
1320 fp = os.fdopen(fd, 'r')
1325 stat = []
1321 stat = []
1326 for line in fp: stat.append(line.lstrip())
1322 for line in fp: stat.append(line.lstrip())
1327 last = stat.pop()
1323 last = stat.pop()
1328 stat.insert(0, last)
1324 stat.insert(0, last)
1329 stat = ''.join(stat)
1325 stat = ''.join(stat)
1330 return stat
1326 return stat
1331 except: raise
1327 except: raise
1332 finally:
1328 finally:
1333 try: os.unlink(name)
1329 try: os.unlink(name)
1334 except: pass
1330 except: pass
@@ -1,49 +1,49 b''
1 adding b
1 adding b
2 Patch queue now empty
2 Patch queue now empty
3 % push patch with missing target
3 % push patch with missing target
4 applying changeb
4 applying changeb
5 unable to find 'b' for patching
5 unable to find 'b' for patching
6 2 out of 2 hunks FAILED -- saving rejects to file b.rej
6 2 out of 2 hunks FAILED -- saving rejects to file b.rej
7 patch failed, unable to continue (try -v)
7 patch failed, unable to continue (try -v)
8 patch failed, rejects left in working dir
8 patch failed, rejects left in working dir
9 Errors during apply, please fix and refresh changeb
9 Errors during apply, please fix and refresh changeb
10 % display added files
10 % display added files
11 a
11 a
12 c
12 c
13 % display rejections
13 % display rejections
14 --- b
14 --- b
15 +++ b
15 +++ b
16 @@ -1,3 +1,5 @@
16 @@ -1,3 +1,5 @@
17 +b
17 +b
18 +b
18 +b
19 a
19 a
20 a
20 a
21 a
21 a
22 @@ -8,3 +10,5 @@
22 @@ -8,3 +10,5 @@
23 a
23 a
24 a
24 a
25 a
25 a
26 +c
26 +c
27 +c
27 +c
28 adding b
28 adding b
29 Patch queue now empty
29 Patch queue now empty
30 % push git patch with missing target
30 % push git patch with missing target
31 applying changeb
31 applying changeb
32 unable to find 'b' for patching
32 unable to find 'b' for patching
33 1 out of 1 hunk FAILED -- saving rejects to file b.rej
33 1 out of 1 hunks FAILED -- saving rejects to file b.rej
34 patch failed, unable to continue (try -v)
34 patch failed, unable to continue (try -v)
35 b: No such file or directory
35 b: No such file or directory
36 b not tracked!
36 b not tracked!
37 patch failed, rejects left in working dir
37 patch failed, rejects left in working dir
38 Errors during apply, please fix and refresh changeb
38 Errors during apply, please fix and refresh changeb
39 ? b.rej
39 ? b.rej
40 % display added files
40 % display added files
41 a
41 a
42 c
42 c
43 % display rejections
43 % display rejections
44 --- b
44 --- b
45 +++ b
45 +++ b
46 GIT binary patch
46 GIT binary patch
47 literal 2
47 literal 2
48 Jc${No0000400IC2
48 Jc${No0000400IC2
49
49
@@ -1,554 +1,554 b''
1 % help
1 % help
2 mq extension - patch management and development
2 mq extension - patch management and development
3
3
4 This extension lets you work with a stack of patches in a Mercurial
4 This extension lets you work with a stack of patches in a Mercurial
5 repository. It manages two stacks of patches - all known patches, and
5 repository. It manages two stacks of patches - all known patches, and
6 applied patches (subset of known patches).
6 applied patches (subset of known patches).
7
7
8 Known patches are represented as patch files in the .hg/patches
8 Known patches are represented as patch files in the .hg/patches
9 directory. Applied patches are both patch files and changesets.
9 directory. Applied patches are both patch files and changesets.
10
10
11 Common tasks (use "hg help command" for more details):
11 Common tasks (use "hg help command" for more details):
12
12
13 prepare repository to work with patches qinit
13 prepare repository to work with patches qinit
14 create new patch qnew
14 create new patch qnew
15 import existing patch qimport
15 import existing patch qimport
16
16
17 print patch series qseries
17 print patch series qseries
18 print applied patches qapplied
18 print applied patches qapplied
19 print name of top applied patch qtop
19 print name of top applied patch qtop
20
20
21 add known patch to applied stack qpush
21 add known patch to applied stack qpush
22 remove patch from applied stack qpop
22 remove patch from applied stack qpop
23 refresh contents of top applied patch qrefresh
23 refresh contents of top applied patch qrefresh
24
24
25 list of commands:
25 list of commands:
26
26
27 qapplied print the patches already applied
27 qapplied print the patches already applied
28 qclone clone main and patch repository at same time
28 qclone clone main and patch repository at same time
29 qcommit commit changes in the queue repository
29 qcommit commit changes in the queue repository
30 qdelete remove patches from queue
30 qdelete remove patches from queue
31 qdiff diff of the current patch and subsequent modifications
31 qdiff diff of the current patch and subsequent modifications
32 qfinish move applied patches into repository history
32 qfinish move applied patches into repository history
33 qfold fold the named patches into the current patch
33 qfold fold the named patches into the current patch
34 qgoto push or pop patches until named patch is at top of stack
34 qgoto push or pop patches until named patch is at top of stack
35 qguard set or print guards for a patch
35 qguard set or print guards for a patch
36 qheader Print the header of the topmost or specified patch
36 qheader Print the header of the topmost or specified patch
37 qimport import a patch
37 qimport import a patch
38 qinit init a new queue repository
38 qinit init a new queue repository
39 qnew create a new patch
39 qnew create a new patch
40 qnext print the name of the next patch
40 qnext print the name of the next patch
41 qpop pop the current patch off the stack
41 qpop pop the current patch off the stack
42 qprev print the name of the previous patch
42 qprev print the name of the previous patch
43 qpush push the next patch onto the stack
43 qpush push the next patch onto the stack
44 qrefresh update the current patch
44 qrefresh update the current patch
45 qrename rename a patch
45 qrename rename a patch
46 qrestore restore the queue state saved by a rev
46 qrestore restore the queue state saved by a rev
47 qsave save current queue state
47 qsave save current queue state
48 qselect set or print guarded patches to push
48 qselect set or print guarded patches to push
49 qseries print the entire series file
49 qseries print the entire series file
50 qtop print the name of the current patch
50 qtop print the name of the current patch
51 qunapplied print the patches not yet applied
51 qunapplied print the patches not yet applied
52 strip strip a revision and all its descendants from the repository
52 strip strip a revision and all its descendants from the repository
53
53
54 use "hg -v help mq" to show aliases and global options
54 use "hg -v help mq" to show aliases and global options
55 adding a
55 adding a
56 updating working directory
56 updating working directory
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 adding b/z
58 adding b/z
59 % qinit
59 % qinit
60 % -R qinit
60 % -R qinit
61 % qinit -c
61 % qinit -c
62 A .hgignore
62 A .hgignore
63 A series
63 A series
64 % qnew should refuse bad patch names
64 % qnew should refuse bad patch names
65 abort: "series" cannot be used as the name of a patch
65 abort: "series" cannot be used as the name of a patch
66 abort: "status" cannot be used as the name of a patch
66 abort: "status" cannot be used as the name of a patch
67 abort: "guards" cannot be used as the name of a patch
67 abort: "guards" cannot be used as the name of a patch
68 abort: ".hgignore" cannot be used as the name of a patch
68 abort: ".hgignore" cannot be used as the name of a patch
69 % qnew implies add
69 % qnew implies add
70 A .hgignore
70 A .hgignore
71 A series
71 A series
72 A test.patch
72 A test.patch
73 % qinit; qinit -c
73 % qinit; qinit -c
74 .hgignore:
74 .hgignore:
75 ^\.hg
75 ^\.hg
76 ^\.mq
76 ^\.mq
77 syntax: glob
77 syntax: glob
78 status
78 status
79 guards
79 guards
80 series:
80 series:
81 abort: repository already exists!
81 abort: repository already exists!
82 % qinit; <stuff>; qinit -c
82 % qinit; <stuff>; qinit -c
83 adding .hg/patches/A
83 adding .hg/patches/A
84 adding .hg/patches/B
84 adding .hg/patches/B
85 A .hgignore
85 A .hgignore
86 A A
86 A A
87 A B
87 A B
88 A series
88 A series
89 .hgignore:
89 .hgignore:
90 status
90 status
91 bleh
91 bleh
92 series:
92 series:
93 A
93 A
94 B
94 B
95 % qnew with uncommitted changes
95 % qnew with uncommitted changes
96 abort: local changes found, refresh first
96 abort: local changes found, refresh first
97 A somefile
97 A somefile
98 % qnew with uncommitted changes and missing file (issue 803)
98 % qnew with uncommitted changes and missing file (issue 803)
99 someotherfile: No such file or directory
99 someotherfile: No such file or directory
100 someotherfile: No such file or directory
100 someotherfile: No such file or directory
101 A somefile
101 A somefile
102 issue803.patch
102 issue803.patch
103 Patch queue now empty
103 Patch queue now empty
104 % qnew -m
104 % qnew -m
105 foo bar
105 foo bar
106 % qrefresh
106 % qrefresh
107 foo bar
107 foo bar
108
108
109 diff -r xa
109 diff -r xa
110 --- a/a
110 --- a/a
111 +++ b/a
111 +++ b/a
112 @@ -1,1 +1,2 @@
112 @@ -1,1 +1,2 @@
113 a
113 a
114 +a
114 +a
115 % empty qrefresh
115 % empty qrefresh
116 revision:
116 revision:
117 patch:
117 patch:
118 foo bar
118 foo bar
119
119
120 working dir diff:
120 working dir diff:
121 --- a/a
121 --- a/a
122 +++ b/a
122 +++ b/a
123 @@ -1,1 +1,2 @@
123 @@ -1,1 +1,2 @@
124 a
124 a
125 +a
125 +a
126 % qpop
126 % qpop
127 Patch queue now empty
127 Patch queue now empty
128 % qpush
128 % qpush
129 applying test.patch
129 applying test.patch
130 Now at: test.patch
130 Now at: test.patch
131 % pop/push outside repo
131 % pop/push outside repo
132 Patch queue now empty
132 Patch queue now empty
133 applying test.patch
133 applying test.patch
134 Now at: test.patch
134 Now at: test.patch
135 % qrefresh in subdir
135 % qrefresh in subdir
136 % pop/push -a in subdir
136 % pop/push -a in subdir
137 Patch queue now empty
137 Patch queue now empty
138 applying test.patch
138 applying test.patch
139 applying test2.patch
139 applying test2.patch
140 Now at: test2.patch
140 Now at: test2.patch
141 % qseries
141 % qseries
142 test.patch
142 test.patch
143 test2.patch
143 test2.patch
144 Now at: test.patch
144 Now at: test.patch
145 0 A test.patch: foo bar
145 0 A test.patch: foo bar
146 1 U test2.patch:
146 1 U test2.patch:
147 applying test2.patch
147 applying test2.patch
148 Now at: test2.patch
148 Now at: test2.patch
149 % qapplied
149 % qapplied
150 test.patch
150 test.patch
151 test2.patch
151 test2.patch
152 % qtop
152 % qtop
153 test2.patch
153 test2.patch
154 % qprev
154 % qprev
155 test.patch
155 test.patch
156 % qnext
156 % qnext
157 All patches applied
157 All patches applied
158 % pop, qnext, qprev, qapplied
158 % pop, qnext, qprev, qapplied
159 Now at: test.patch
159 Now at: test.patch
160 test2.patch
160 test2.patch
161 Only one patch applied
161 Only one patch applied
162 test.patch
162 test.patch
163 % commit should fail
163 % commit should fail
164 abort: cannot commit over an applied mq patch
164 abort: cannot commit over an applied mq patch
165 % push should fail
165 % push should fail
166 pushing to ../../k
166 pushing to ../../k
167 abort: source has mq patches applied
167 abort: source has mq patches applied
168 % qunapplied
168 % qunapplied
169 test2.patch
169 test2.patch
170 % qpush/qpop with index
170 % qpush/qpop with index
171 applying test2.patch
171 applying test2.patch
172 Now at: test2.patch
172 Now at: test2.patch
173 Now at: test.patch
173 Now at: test.patch
174 applying test1b.patch
174 applying test1b.patch
175 Now at: test1b.patch
175 Now at: test1b.patch
176 applying test2.patch
176 applying test2.patch
177 Now at: test2.patch
177 Now at: test2.patch
178 Now at: test1b.patch
178 Now at: test1b.patch
179 Now at: test.patch
179 Now at: test.patch
180 applying test1b.patch
180 applying test1b.patch
181 applying test2.patch
181 applying test2.patch
182 Now at: test2.patch
182 Now at: test2.patch
183 % push should succeed
183 % push should succeed
184 Patch queue now empty
184 Patch queue now empty
185 pushing to ../../k
185 pushing to ../../k
186 searching for changes
186 searching for changes
187 adding changesets
187 adding changesets
188 adding manifests
188 adding manifests
189 adding file changes
189 adding file changes
190 added 1 changesets with 1 changes to 1 files
190 added 1 changesets with 1 changes to 1 files
191 % qpush/qpop error codes
191 % qpush/qpop error codes
192 applying test.patch
192 applying test.patch
193 applying test1b.patch
193 applying test1b.patch
194 applying test2.patch
194 applying test2.patch
195 Now at: test2.patch
195 Now at: test2.patch
196 % pops all patches and succeeds
196 % pops all patches and succeeds
197 Patch queue now empty
197 Patch queue now empty
198 qpop -a succeeds
198 qpop -a succeeds
199 % does nothing and succeeds
199 % does nothing and succeeds
200 no patches applied
200 no patches applied
201 qpop -a succeeds
201 qpop -a succeeds
202 % fails - nothing else to pop
202 % fails - nothing else to pop
203 no patches applied
203 no patches applied
204 qpop fails
204 qpop fails
205 % pushes a patch and succeeds
205 % pushes a patch and succeeds
206 applying test.patch
206 applying test.patch
207 Now at: test.patch
207 Now at: test.patch
208 qpush succeeds
208 qpush succeeds
209 % pops a patch and succeeds
209 % pops a patch and succeeds
210 Patch queue now empty
210 Patch queue now empty
211 qpop succeeds
211 qpop succeeds
212 % pushes up to test1b.patch and succeeds
212 % pushes up to test1b.patch and succeeds
213 applying test.patch
213 applying test.patch
214 applying test1b.patch
214 applying test1b.patch
215 Now at: test1b.patch
215 Now at: test1b.patch
216 qpush test1b.patch succeeds
216 qpush test1b.patch succeeds
217 % does nothing and succeeds
217 % does nothing and succeeds
218 qpush: test1b.patch is already at the top
218 qpush: test1b.patch is already at the top
219 qpush test1b.patch succeeds
219 qpush test1b.patch succeeds
220 % does nothing and succeeds
220 % does nothing and succeeds
221 qpop: test1b.patch is already at the top
221 qpop: test1b.patch is already at the top
222 qpop test1b.patch succeeds
222 qpop test1b.patch succeeds
223 % fails - can't push to this patch
223 % fails - can't push to this patch
224 abort: cannot push to a previous patch: test.patch
224 abort: cannot push to a previous patch: test.patch
225 qpush test.patch fails
225 qpush test.patch fails
226 % fails - can't pop to this patch
226 % fails - can't pop to this patch
227 abort: patch test2.patch is not applied
227 abort: patch test2.patch is not applied
228 qpop test2.patch fails
228 qpop test2.patch fails
229 % pops up to test.patch and succeeds
229 % pops up to test.patch and succeeds
230 Now at: test.patch
230 Now at: test.patch
231 qpop test.patch succeeds
231 qpop test.patch succeeds
232 % pushes all patches and succeeds
232 % pushes all patches and succeeds
233 applying test1b.patch
233 applying test1b.patch
234 applying test2.patch
234 applying test2.patch
235 Now at: test2.patch
235 Now at: test2.patch
236 qpush -a succeeds
236 qpush -a succeeds
237 % does nothing and succeeds
237 % does nothing and succeeds
238 all patches are currently applied
238 all patches are currently applied
239 qpush -a succeeds
239 qpush -a succeeds
240 % fails - nothing else to push
240 % fails - nothing else to push
241 patch series already fully applied
241 patch series already fully applied
242 qpush fails
242 qpush fails
243 % does nothing and succeeds
243 % does nothing and succeeds
244 all patches are currently applied
244 all patches are currently applied
245 qpush test2.patch succeeds
245 qpush test2.patch succeeds
246 % strip
246 % strip
247 adding x
247 adding x
248 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
248 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
249 saving bundle to
249 saving bundle to
250 adding changesets
250 adding changesets
251 adding manifests
251 adding manifests
252 adding file changes
252 adding file changes
253 added 1 changesets with 1 changes to 1 files
253 added 1 changesets with 1 changes to 1 files
254 (run 'hg update' to get a working copy)
254 (run 'hg update' to get a working copy)
255 % strip with local changes, should complain
255 % strip with local changes, should complain
256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 abort: local changes found
257 abort: local changes found
258 % --force strip with local changes
258 % --force strip with local changes
259 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
259 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
260 saving bundle to
260 saving bundle to
261 % cd b; hg qrefresh
261 % cd b; hg qrefresh
262 adding a
262 adding a
263 foo
263 foo
264
264
265 diff -r cb9a9f314b8b a
265 diff -r cb9a9f314b8b a
266 --- a/a
266 --- a/a
267 +++ b/a
267 +++ b/a
268 @@ -1,1 +1,2 @@
268 @@ -1,1 +1,2 @@
269 a
269 a
270 +a
270 +a
271 diff -r cb9a9f314b8b b/f
271 diff -r cb9a9f314b8b b/f
272 --- /dev/null
272 --- /dev/null
273 +++ b/b/f
273 +++ b/b/f
274 @@ -0,0 +1,1 @@
274 @@ -0,0 +1,1 @@
275 +f
275 +f
276 % hg qrefresh .
276 % hg qrefresh .
277 foo
277 foo
278
278
279 diff -r cb9a9f314b8b b/f
279 diff -r cb9a9f314b8b b/f
280 --- /dev/null
280 --- /dev/null
281 +++ b/b/f
281 +++ b/b/f
282 @@ -0,0 +1,1 @@
282 @@ -0,0 +1,1 @@
283 +f
283 +f
284 M a
284 M a
285 % qpush failure
285 % qpush failure
286 Patch queue now empty
286 Patch queue now empty
287 applying foo
287 applying foo
288 applying bar
288 applying bar
289 file foo already exists
289 file foo already exists
290 1 out of 1 hunk FAILED -- saving rejects to file foo.rej
290 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
291 patch failed, unable to continue (try -v)
291 patch failed, unable to continue (try -v)
292 patch failed, rejects left in working dir
292 patch failed, rejects left in working dir
293 Errors during apply, please fix and refresh bar
293 Errors during apply, please fix and refresh bar
294 ? foo
294 ? foo
295 ? foo.rej
295 ? foo.rej
296 % mq tags
296 % mq tags
297 0 qparent
297 0 qparent
298 1 qbase foo
298 1 qbase foo
299 2 qtip bar tip
299 2 qtip bar tip
300 % bad node in status
300 % bad node in status
301 Now at: foo
301 Now at: foo
302 changeset: 0:cb9a9f314b8b
302 changeset: 0:cb9a9f314b8b
303 mq status file refers to unknown node
303 mq status file refers to unknown node
304 tag: tip
304 tag: tip
305 user: test
305 user: test
306 date: Thu Jan 01 00:00:00 1970 +0000
306 date: Thu Jan 01 00:00:00 1970 +0000
307 summary: a
307 summary: a
308
308
309 mq status file refers to unknown node
309 mq status file refers to unknown node
310 default 0:cb9a9f314b8b
310 default 0:cb9a9f314b8b
311 abort: working directory revision is not qtip
311 abort: working directory revision is not qtip
312 new file
312 new file
313
313
314 diff --git a/new b/new
314 diff --git a/new b/new
315 new file mode 100755
315 new file mode 100755
316 --- /dev/null
316 --- /dev/null
317 +++ b/new
317 +++ b/new
318 @@ -0,0 +1,1 @@
318 @@ -0,0 +1,1 @@
319 +foo
319 +foo
320 copy file
320 copy file
321
321
322 diff --git a/new b/copy
322 diff --git a/new b/copy
323 copy from new
323 copy from new
324 copy to copy
324 copy to copy
325 Now at: new
325 Now at: new
326 applying copy
326 applying copy
327 Now at: copy
327 Now at: copy
328 diff --git a/new b/copy
328 diff --git a/new b/copy
329 copy from new
329 copy from new
330 copy to copy
330 copy to copy
331 diff --git a/new b/copy
331 diff --git a/new b/copy
332 copy from new
332 copy from new
333 copy to copy
333 copy to copy
334 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
334 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
335 created new head
335 created new head
336 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
336 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
337 adding branch
337 adding branch
338 adding changesets
338 adding changesets
339 adding manifests
339 adding manifests
340 adding file changes
340 adding file changes
341 added 1 changesets with 1 changes to 1 files
341 added 1 changesets with 1 changes to 1 files
342 Patch queue now empty
342 Patch queue now empty
343 (working directory not at tip)
343 (working directory not at tip)
344 applying bar
344 applying bar
345 Now at: bar
345 Now at: bar
346 diff --git a/bar b/bar
346 diff --git a/bar b/bar
347 new file mode 100644
347 new file mode 100644
348 --- /dev/null
348 --- /dev/null
349 +++ b/bar
349 +++ b/bar
350 @@ -0,0 +1,1 @@
350 @@ -0,0 +1,1 @@
351 +bar
351 +bar
352 diff --git a/foo b/baz
352 diff --git a/foo b/baz
353 rename from foo
353 rename from foo
354 rename to baz
354 rename to baz
355 2 baz (foo)
355 2 baz (foo)
356 diff --git a/bar b/bar
356 diff --git a/bar b/bar
357 new file mode 100644
357 new file mode 100644
358 --- /dev/null
358 --- /dev/null
359 +++ b/bar
359 +++ b/bar
360 @@ -0,0 +1,1 @@
360 @@ -0,0 +1,1 @@
361 +bar
361 +bar
362 diff --git a/foo b/baz
362 diff --git a/foo b/baz
363 rename from foo
363 rename from foo
364 rename to baz
364 rename to baz
365 2 baz (foo)
365 2 baz (foo)
366 diff --git a/bar b/bar
366 diff --git a/bar b/bar
367 diff --git a/foo b/baz
367 diff --git a/foo b/baz
368
368
369 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
369 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
370 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
370 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
371 adding branch
371 adding branch
372 adding changesets
372 adding changesets
373 adding manifests
373 adding manifests
374 adding file changes
374 adding file changes
375 added 1 changesets with 1 changes to 1 files
375 added 1 changesets with 1 changes to 1 files
376 Patch queue now empty
376 Patch queue now empty
377 (working directory not at tip)
377 (working directory not at tip)
378 applying bar
378 applying bar
379 Now at: bar
379 Now at: bar
380 diff --git a/foo b/bleh
380 diff --git a/foo b/bleh
381 rename from foo
381 rename from foo
382 rename to bleh
382 rename to bleh
383 diff --git a/quux b/quux
383 diff --git a/quux b/quux
384 new file mode 100644
384 new file mode 100644
385 --- /dev/null
385 --- /dev/null
386 +++ b/quux
386 +++ b/quux
387 @@ -0,0 +1,1 @@
387 @@ -0,0 +1,1 @@
388 +bar
388 +bar
389 3 bleh (foo)
389 3 bleh (foo)
390 diff --git a/foo b/barney
390 diff --git a/foo b/barney
391 rename from foo
391 rename from foo
392 rename to barney
392 rename to barney
393 diff --git a/fred b/fred
393 diff --git a/fred b/fred
394 new file mode 100644
394 new file mode 100644
395 --- /dev/null
395 --- /dev/null
396 +++ b/fred
396 +++ b/fred
397 @@ -0,0 +1,1 @@
397 @@ -0,0 +1,1 @@
398 +bar
398 +bar
399 3 barney (foo)
399 3 barney (foo)
400 % refresh omitting an added file
400 % refresh omitting an added file
401 C newfile
401 C newfile
402 A newfile
402 A newfile
403 Now at: bar
403 Now at: bar
404 % create a git patch
404 % create a git patch
405 diff --git a/alexander b/alexander
405 diff --git a/alexander b/alexander
406 % create a git binary patch
406 % create a git binary patch
407 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
407 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
408 diff --git a/bucephalus b/bucephalus
408 diff --git a/bucephalus b/bucephalus
409 % check binary patches can be popped and pushed
409 % check binary patches can be popped and pushed
410 Now at: addalexander
410 Now at: addalexander
411 applying addbucephalus
411 applying addbucephalus
412 Now at: addbucephalus
412 Now at: addbucephalus
413 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
413 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
414 % strip again
414 % strip again
415 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
415 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
416 created new head
416 created new head
417 merging foo
417 merging foo
418 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
418 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
419 (branch merge, don't forget to commit)
419 (branch merge, don't forget to commit)
420 changeset: 3:99615015637b
420 changeset: 3:99615015637b
421 tag: tip
421 tag: tip
422 parent: 2:20cbbe65cff7
422 parent: 2:20cbbe65cff7
423 parent: 1:d2871fc282d4
423 parent: 1:d2871fc282d4
424 user: test
424 user: test
425 date: Thu Jan 01 00:00:00 1970 +0000
425 date: Thu Jan 01 00:00:00 1970 +0000
426 summary: merge
426 summary: merge
427
427
428 changeset: 2:20cbbe65cff7
428 changeset: 2:20cbbe65cff7
429 parent: 0:53245c60e682
429 parent: 0:53245c60e682
430 user: test
430 user: test
431 date: Thu Jan 01 00:00:00 1970 +0000
431 date: Thu Jan 01 00:00:00 1970 +0000
432 summary: change foo 2
432 summary: change foo 2
433
433
434 changeset: 1:d2871fc282d4
434 changeset: 1:d2871fc282d4
435 user: test
435 user: test
436 date: Thu Jan 01 00:00:00 1970 +0000
436 date: Thu Jan 01 00:00:00 1970 +0000
437 summary: change foo 1
437 summary: change foo 1
438
438
439 changeset: 0:53245c60e682
439 changeset: 0:53245c60e682
440 user: test
440 user: test
441 date: Thu Jan 01 00:00:00 1970 +0000
441 date: Thu Jan 01 00:00:00 1970 +0000
442 summary: add foo
442 summary: add foo
443
443
444 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
444 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
445 saving bundle to
445 saving bundle to
446 saving bundle to
446 saving bundle to
447 adding branch
447 adding branch
448 adding changesets
448 adding changesets
449 adding manifests
449 adding manifests
450 adding file changes
450 adding file changes
451 added 1 changesets with 1 changes to 1 files
451 added 1 changesets with 1 changes to 1 files
452 changeset: 1:20cbbe65cff7
452 changeset: 1:20cbbe65cff7
453 tag: tip
453 tag: tip
454 user: test
454 user: test
455 date: Thu Jan 01 00:00:00 1970 +0000
455 date: Thu Jan 01 00:00:00 1970 +0000
456 summary: change foo 2
456 summary: change foo 2
457
457
458 changeset: 0:53245c60e682
458 changeset: 0:53245c60e682
459 user: test
459 user: test
460 date: Thu Jan 01 00:00:00 1970 +0000
460 date: Thu Jan 01 00:00:00 1970 +0000
461 summary: add foo
461 summary: add foo
462
462
463 % qclone
463 % qclone
464 abort: versioned patch repository not found (see qinit -c)
464 abort: versioned patch repository not found (see qinit -c)
465 adding .hg/patches/patch1
465 adding .hg/patches/patch1
466 main repo:
466 main repo:
467 rev 1: change foo
467 rev 1: change foo
468 rev 0: add foo
468 rev 0: add foo
469 patch repo:
469 patch repo:
470 rev 0: checkpoint
470 rev 0: checkpoint
471 updating working directory
471 updating working directory
472 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
472 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
473 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
473 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
474 main repo:
474 main repo:
475 rev 0: add foo
475 rev 0: add foo
476 patch repo:
476 patch repo:
477 rev 0: checkpoint
477 rev 0: checkpoint
478 Patch queue now empty
478 Patch queue now empty
479 main repo:
479 main repo:
480 rev 0: add foo
480 rev 0: add foo
481 patch repo:
481 patch repo:
482 rev 0: checkpoint
482 rev 0: checkpoint
483 updating working directory
483 updating working directory
484 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
484 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 main repo:
486 main repo:
487 rev 0: add foo
487 rev 0: add foo
488 patch repo:
488 patch repo:
489 rev 0: checkpoint
489 rev 0: checkpoint
490 % test applying on an empty file (issue 1033)
490 % test applying on an empty file (issue 1033)
491 adding a
491 adding a
492 Patch queue now empty
492 Patch queue now empty
493 applying changea
493 applying changea
494 Now at: changea
494 Now at: changea
495 % test qpush with --force, issue1087
495 % test qpush with --force, issue1087
496 adding bye.txt
496 adding bye.txt
497 adding hello.txt
497 adding hello.txt
498 Patch queue now empty
498 Patch queue now empty
499 % qpush should fail, local changes
499 % qpush should fail, local changes
500 abort: local changes found, refresh first
500 abort: local changes found, refresh first
501 % apply force, should not discard changes with empty patch
501 % apply force, should not discard changes with empty patch
502 applying empty
502 applying empty
503 /usr/bin/patch: **** Only garbage was found in the patch input.
503 /usr/bin/patch: **** Only garbage was found in the patch input.
504 patch failed, unable to continue (try -v)
504 patch failed, unable to continue (try -v)
505 patch empty is empty
505 patch empty is empty
506 Now at: empty
506 Now at: empty
507 diff -r bf5fc3f07a0a hello.txt
507 diff -r bf5fc3f07a0a hello.txt
508 --- a/hello.txt
508 --- a/hello.txt
509 +++ b/hello.txt
509 +++ b/hello.txt
510 @@ -1,1 +1,2 @@
510 @@ -1,1 +1,2 @@
511 hello
511 hello
512 +world
512 +world
513 diff -r 9ecee4f634e3 hello.txt
513 diff -r 9ecee4f634e3 hello.txt
514 --- a/hello.txt
514 --- a/hello.txt
515 +++ b/hello.txt
515 +++ b/hello.txt
516 @@ -1,1 +1,2 @@
516 @@ -1,1 +1,2 @@
517 hello
517 hello
518 +world
518 +world
519 changeset: 1:bf5fc3f07a0a
519 changeset: 1:bf5fc3f07a0a
520 tag: qtip
520 tag: qtip
521 tag: tip
521 tag: tip
522 tag: empty
522 tag: empty
523 tag: qbase
523 tag: qbase
524 user: test
524 user: test
525 date: Thu Jan 01 00:00:00 1970 +0000
525 date: Thu Jan 01 00:00:00 1970 +0000
526 summary: imported patch empty
526 summary: imported patch empty
527
527
528
528
529 Patch queue now empty
529 Patch queue now empty
530 % qpush should fail, local changes
530 % qpush should fail, local changes
531 abort: local changes found, refresh first
531 abort: local changes found, refresh first
532 % apply force, should discard changes in hello, but not bye
532 % apply force, should discard changes in hello, but not bye
533 applying empty
533 applying empty
534 Now at: empty
534 Now at: empty
535 M bye.txt
535 M bye.txt
536 diff -r ba252371dbc1 bye.txt
536 diff -r ba252371dbc1 bye.txt
537 --- a/bye.txt
537 --- a/bye.txt
538 +++ b/bye.txt
538 +++ b/bye.txt
539 @@ -1,1 +1,2 @@
539 @@ -1,1 +1,2 @@
540 bye
540 bye
541 +universe
541 +universe
542 diff -r 9ecee4f634e3 bye.txt
542 diff -r 9ecee4f634e3 bye.txt
543 --- a/bye.txt
543 --- a/bye.txt
544 +++ b/bye.txt
544 +++ b/bye.txt
545 @@ -1,1 +1,2 @@
545 @@ -1,1 +1,2 @@
546 bye
546 bye
547 +universe
547 +universe
548 diff -r 9ecee4f634e3 hello.txt
548 diff -r 9ecee4f634e3 hello.txt
549 --- a/hello.txt
549 --- a/hello.txt
550 +++ b/hello.txt
550 +++ b/hello.txt
551 @@ -1,1 +1,3 @@
551 @@ -1,1 +1,3 @@
552 hello
552 hello
553 +world
553 +world
554 +universe
554 +universe
@@ -1,138 +1,138 b''
1 adding r1
1 adding r1
2 adding r2
2 adding r2
3 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
4 adding b1
4 adding b1
5 created new head
5 created new head
6 adding b2
6 adding b2
7 adding b3
7 adding b3
8 4 b3
8 4 b3
9 3 b2
9 3 b2
10 2 0:17ab29e464c6 b1
10 2 0:17ab29e464c6 b1
11 1 r2
11 1 r2
12 0 r1
12 0 r1
13 updating working directory
13 updating working directory
14 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
14 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
16 % rebase b onto r1
16 % rebase b onto r1
17 applying 37a1297eb21b
17 applying 37a1297eb21b
18 37a1297eb21b transplanted to e234d668f844
18 37a1297eb21b transplanted to e234d668f844
19 applying 722f4667af76
19 applying 722f4667af76
20 722f4667af76 transplanted to 539f377d78df
20 722f4667af76 transplanted to 539f377d78df
21 applying a53251cdf717
21 applying a53251cdf717
22 a53251cdf717 transplanted to ffd6818a3975
22 a53251cdf717 transplanted to ffd6818a3975
23 7 b3
23 7 b3
24 6 b2
24 6 b2
25 5 1:d11e3596cc1a b1
25 5 1:d11e3596cc1a b1
26 4 b3
26 4 b3
27 3 b2
27 3 b2
28 2 0:17ab29e464c6 b1
28 2 0:17ab29e464c6 b1
29 1 r2
29 1 r2
30 0 r1
30 0 r1
31 updating working directory
31 updating working directory
32 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
33 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
34 % rebase b onto r1, skipping b2
34 % rebase b onto r1, skipping b2
35 applying 37a1297eb21b
35 applying 37a1297eb21b
36 37a1297eb21b transplanted to e234d668f844
36 37a1297eb21b transplanted to e234d668f844
37 applying a53251cdf717
37 applying a53251cdf717
38 a53251cdf717 transplanted to 7275fda4d04f
38 a53251cdf717 transplanted to 7275fda4d04f
39 6 b3
39 6 b3
40 5 1:d11e3596cc1a b1
40 5 1:d11e3596cc1a b1
41 4 b3
41 4 b3
42 3 b2
42 3 b2
43 2 0:17ab29e464c6 b1
43 2 0:17ab29e464c6 b1
44 1 r2
44 1 r2
45 0 r1
45 0 r1
46 % remote transplant
46 % remote transplant
47 requesting all changes
47 requesting all changes
48 adding changesets
48 adding changesets
49 adding manifests
49 adding manifests
50 adding file changes
50 adding file changes
51 added 2 changesets with 2 changes to 2 files
51 added 2 changesets with 2 changes to 2 files
52 updating working directory
52 updating working directory
53 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 searching for changes
54 searching for changes
55 applying 37a1297eb21b
55 applying 37a1297eb21b
56 37a1297eb21b transplanted to c19cf0ccb069
56 37a1297eb21b transplanted to c19cf0ccb069
57 applying a53251cdf717
57 applying a53251cdf717
58 a53251cdf717 transplanted to f7fe5bf98525
58 a53251cdf717 transplanted to f7fe5bf98525
59 3 b3
59 3 b3
60 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
60 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
61 2 b1
61 2 b1
62 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
62 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
63 1 r2
63 1 r2
64 0 r1
64 0 r1
65 % skip previous transplants
65 % skip previous transplants
66 searching for changes
66 searching for changes
67 applying 722f4667af76
67 applying 722f4667af76
68 722f4667af76 transplanted to 47156cd86c0b
68 722f4667af76 transplanted to 47156cd86c0b
69 4 b2
69 4 b2
70 3 b3
70 3 b3
71 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
71 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
72 2 b1
72 2 b1
73 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
73 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
74 1 r2
74 1 r2
75 0 r1
75 0 r1
76 % skip local changes transplanted to the source
76 % skip local changes transplanted to the source
77 adding b4
77 adding b4
78 updating working directory
78 updating working directory
79 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 searching for changes
80 searching for changes
81 applying 4333daefcb15
81 applying 4333daefcb15
82 4333daefcb15 transplanted to 5f42c04e07cc
82 4333daefcb15 transplanted to 5f42c04e07cc
83 % remote transplant with pull
83 % remote transplant with pull
84 requesting all changes
84 requesting all changes
85 adding changesets
85 adding changesets
86 adding manifests
86 adding manifests
87 adding file changes
87 adding file changes
88 added 1 changesets with 1 changes to 1 files
88 added 1 changesets with 1 changes to 1 files
89 updating working directory
89 updating working directory
90 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 searching for changes
91 searching for changes
92 searching for changes
92 searching for changes
93 adding changesets
93 adding changesets
94 adding manifests
94 adding manifests
95 adding file changes
95 adding file changes
96 added 1 changesets with 1 changes to 1 files
96 added 1 changesets with 1 changes to 1 files
97 applying a53251cdf717
97 applying a53251cdf717
98 a53251cdf717 transplanted to 8d9279348abb
98 a53251cdf717 transplanted to 8d9279348abb
99 2 b3
99 2 b3
100 1 b1
100 1 b1
101 0 r1
101 0 r1
102 % transplant --continue
102 % transplant --continue
103 adding foo
103 adding foo
104 adding toremove
104 adding toremove
105 adding added
105 adding added
106 removing toremove
106 removing toremove
107 adding bar
107 adding bar
108 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
108 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
109 created new head
109 created new head
110 applying a1e30dd1b8e7
110 applying a1e30dd1b8e7
111 patching file foo
111 patching file foo
112 Hunk #1 FAILED at 0
112 Hunk #1 FAILED at 0
113 1 out of 1 hunk FAILED -- saving rejects to file foo.rej
113 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
114 patch failed to apply
114 patch failed to apply
115 abort: Fix up the merge and run hg transplant --continue
115 abort: Fix up the merge and run hg transplant --continue
116 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
116 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
117 applying a1e30dd1b8e7
117 applying a1e30dd1b8e7
118 patching file foo
118 patching file foo
119 Hunk #1 FAILED at 0
119 Hunk #1 FAILED at 0
120 1 out of 1 hunk FAILED -- saving rejects to file foo.rej
120 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
121 patch failed to apply
121 patch failed to apply
122 abort: Fix up the merge and run hg transplant --continue
122 abort: Fix up the merge and run hg transplant --continue
123 a1e30dd1b8e7 transplanted as f1563cf27039
123 a1e30dd1b8e7 transplanted as f1563cf27039
124 skipping already applied revision 1:a1e30dd1b8e7
124 skipping already applied revision 1:a1e30dd1b8e7
125 applying 1739ac5f6139
125 applying 1739ac5f6139
126 1739ac5f6139 transplanted to d649c221319f
126 1739ac5f6139 transplanted to d649c221319f
127 applying 0282d5fbbe02
127 applying 0282d5fbbe02
128 0282d5fbbe02 transplanted to 77418277ccb3
128 0282d5fbbe02 transplanted to 77418277ccb3
129 added
129 added
130 bar
130 bar
131 foo
131 foo
132 % test transplant merge
132 % test transplant merge
133 adding a
133 adding a
134 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
134 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
135 created new head
135 created new head
136 % tranplant
136 % tranplant
137 applying 42dc4432fd35
137 applying 42dc4432fd35
138 1:42dc4432fd35 merged at a9f4acbac129
138 1:42dc4432fd35 merged at a9f4acbac129
General Comments 0
You need to be logged in to leave comments. Login now