##// END OF EJS Templates
mq: really remove undo after a qpush (and after a strip)...
Alexis S. L. Carvalho -
r5527:0b3f910d default
parent child Browse files
Show More
@@ -1,2256 +1,2259 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
33 from mercurial import commands, cmdutil, hg, patch, revlog, util
34 from mercurial import repair
34 from mercurial import repair
35 import os, sys, re, errno
35 import os, sys, re, errno
36
36
37 commands.norepo += " qclone qversion"
37 commands.norepo += " qclone qversion"
38
38
39 # Patch names looks like unix-file names.
39 # Patch names looks like unix-file names.
40 # They must be joinable with queue directory and result in the patch path.
40 # They must be joinable with queue directory and result in the patch path.
41 normname = util.normpath
41 normname = util.normpath
42
42
43 class statusentry:
43 class statusentry:
44 def __init__(self, rev, name=None):
44 def __init__(self, rev, name=None):
45 if not name:
45 if not name:
46 fields = rev.split(':', 1)
46 fields = rev.split(':', 1)
47 if len(fields) == 2:
47 if len(fields) == 2:
48 self.rev, self.name = fields
48 self.rev, self.name = fields
49 else:
49 else:
50 self.rev, self.name = None, None
50 self.rev, self.name = None, None
51 else:
51 else:
52 self.rev, self.name = rev, name
52 self.rev, self.name = rev, name
53
53
54 def __str__(self):
54 def __str__(self):
55 return self.rev + ':' + self.name
55 return self.rev + ':' + self.name
56
56
57 class queue:
57 class queue:
58 def __init__(self, ui, path, patchdir=None):
58 def __init__(self, ui, path, patchdir=None):
59 self.basepath = path
59 self.basepath = path
60 self.path = patchdir or os.path.join(path, "patches")
60 self.path = patchdir or os.path.join(path, "patches")
61 self.opener = util.opener(self.path)
61 self.opener = util.opener(self.path)
62 self.ui = ui
62 self.ui = ui
63 self.applied = []
63 self.applied = []
64 self.full_series = []
64 self.full_series = []
65 self.applied_dirty = 0
65 self.applied_dirty = 0
66 self.series_dirty = 0
66 self.series_dirty = 0
67 self.series_path = "series"
67 self.series_path = "series"
68 self.status_path = "status"
68 self.status_path = "status"
69 self.guards_path = "guards"
69 self.guards_path = "guards"
70 self.active_guards = None
70 self.active_guards = None
71 self.guards_dirty = False
71 self.guards_dirty = False
72 self._diffopts = None
72 self._diffopts = None
73
73
74 if os.path.exists(self.join(self.series_path)):
74 if os.path.exists(self.join(self.series_path)):
75 self.full_series = self.opener(self.series_path).read().splitlines()
75 self.full_series = self.opener(self.series_path).read().splitlines()
76 self.parse_series()
76 self.parse_series()
77
77
78 if os.path.exists(self.join(self.status_path)):
78 if os.path.exists(self.join(self.status_path)):
79 lines = self.opener(self.status_path).read().splitlines()
79 lines = self.opener(self.status_path).read().splitlines()
80 self.applied = [statusentry(l) for l in lines]
80 self.applied = [statusentry(l) for l in lines]
81
81
82 def diffopts(self):
82 def diffopts(self):
83 if self._diffopts is None:
83 if self._diffopts is None:
84 self._diffopts = patch.diffopts(self.ui)
84 self._diffopts = patch.diffopts(self.ui)
85 return self._diffopts
85 return self._diffopts
86
86
87 def join(self, *p):
87 def join(self, *p):
88 return os.path.join(self.path, *p)
88 return os.path.join(self.path, *p)
89
89
90 def find_series(self, patch):
90 def find_series(self, patch):
91 pre = re.compile("(\s*)([^#]+)")
91 pre = re.compile("(\s*)([^#]+)")
92 index = 0
92 index = 0
93 for l in self.full_series:
93 for l in self.full_series:
94 m = pre.match(l)
94 m = pre.match(l)
95 if m:
95 if m:
96 s = m.group(2)
96 s = m.group(2)
97 s = s.rstrip()
97 s = s.rstrip()
98 if s == patch:
98 if s == patch:
99 return index
99 return index
100 index += 1
100 index += 1
101 return None
101 return None
102
102
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
103 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
104
104
105 def parse_series(self):
105 def parse_series(self):
106 self.series = []
106 self.series = []
107 self.series_guards = []
107 self.series_guards = []
108 for l in self.full_series:
108 for l in self.full_series:
109 h = l.find('#')
109 h = l.find('#')
110 if h == -1:
110 if h == -1:
111 patch = l
111 patch = l
112 comment = ''
112 comment = ''
113 elif h == 0:
113 elif h == 0:
114 continue
114 continue
115 else:
115 else:
116 patch = l[:h]
116 patch = l[:h]
117 comment = l[h:]
117 comment = l[h:]
118 patch = patch.strip()
118 patch = patch.strip()
119 if patch:
119 if patch:
120 if patch in self.series:
120 if patch in self.series:
121 raise util.Abort(_('%s appears more than once in %s') %
121 raise util.Abort(_('%s appears more than once in %s') %
122 (patch, self.join(self.series_path)))
122 (patch, self.join(self.series_path)))
123 self.series.append(patch)
123 self.series.append(patch)
124 self.series_guards.append(self.guard_re.findall(comment))
124 self.series_guards.append(self.guard_re.findall(comment))
125
125
126 def check_guard(self, guard):
126 def check_guard(self, guard):
127 bad_chars = '# \t\r\n\f'
127 bad_chars = '# \t\r\n\f'
128 first = guard[0]
128 first = guard[0]
129 for c in '-+':
129 for c in '-+':
130 if first == c:
130 if first == c:
131 return (_('guard %r starts with invalid character: %r') %
131 return (_('guard %r starts with invalid character: %r') %
132 (guard, c))
132 (guard, c))
133 for c in bad_chars:
133 for c in bad_chars:
134 if c in guard:
134 if c in guard:
135 return _('invalid character in guard %r: %r') % (guard, c)
135 return _('invalid character in guard %r: %r') % (guard, c)
136
136
137 def set_active(self, guards):
137 def set_active(self, guards):
138 for guard in guards:
138 for guard in guards:
139 bad = self.check_guard(guard)
139 bad = self.check_guard(guard)
140 if bad:
140 if bad:
141 raise util.Abort(bad)
141 raise util.Abort(bad)
142 guards = dict.fromkeys(guards).keys()
142 guards = dict.fromkeys(guards).keys()
143 guards.sort()
143 guards.sort()
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
144 self.ui.debug('active guards: %s\n' % ' '.join(guards))
145 self.active_guards = guards
145 self.active_guards = guards
146 self.guards_dirty = True
146 self.guards_dirty = True
147
147
148 def active(self):
148 def active(self):
149 if self.active_guards is None:
149 if self.active_guards is None:
150 self.active_guards = []
150 self.active_guards = []
151 try:
151 try:
152 guards = self.opener(self.guards_path).read().split()
152 guards = self.opener(self.guards_path).read().split()
153 except IOError, err:
153 except IOError, err:
154 if err.errno != errno.ENOENT: raise
154 if err.errno != errno.ENOENT: raise
155 guards = []
155 guards = []
156 for i, guard in enumerate(guards):
156 for i, guard in enumerate(guards):
157 bad = self.check_guard(guard)
157 bad = self.check_guard(guard)
158 if bad:
158 if bad:
159 self.ui.warn('%s:%d: %s\n' %
159 self.ui.warn('%s:%d: %s\n' %
160 (self.join(self.guards_path), i + 1, bad))
160 (self.join(self.guards_path), i + 1, bad))
161 else:
161 else:
162 self.active_guards.append(guard)
162 self.active_guards.append(guard)
163 return self.active_guards
163 return self.active_guards
164
164
165 def set_guards(self, idx, guards):
165 def set_guards(self, idx, guards):
166 for g in guards:
166 for g in guards:
167 if len(g) < 2:
167 if len(g) < 2:
168 raise util.Abort(_('guard %r too short') % g)
168 raise util.Abort(_('guard %r too short') % g)
169 if g[0] not in '-+':
169 if g[0] not in '-+':
170 raise util.Abort(_('guard %r starts with invalid char') % g)
170 raise util.Abort(_('guard %r starts with invalid char') % g)
171 bad = self.check_guard(g[1:])
171 bad = self.check_guard(g[1:])
172 if bad:
172 if bad:
173 raise util.Abort(bad)
173 raise util.Abort(bad)
174 drop = self.guard_re.sub('', self.full_series[idx])
174 drop = self.guard_re.sub('', self.full_series[idx])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
175 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
176 self.parse_series()
176 self.parse_series()
177 self.series_dirty = True
177 self.series_dirty = True
178
178
179 def pushable(self, idx):
179 def pushable(self, idx):
180 if isinstance(idx, str):
180 if isinstance(idx, str):
181 idx = self.series.index(idx)
181 idx = self.series.index(idx)
182 patchguards = self.series_guards[idx]
182 patchguards = self.series_guards[idx]
183 if not patchguards:
183 if not patchguards:
184 return True, None
184 return True, None
185 default = False
185 default = False
186 guards = self.active()
186 guards = self.active()
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
187 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
188 if exactneg:
188 if exactneg:
189 return False, exactneg[0]
189 return False, exactneg[0]
190 pos = [g for g in patchguards if g[0] == '+']
190 pos = [g for g in patchguards if g[0] == '+']
191 exactpos = [g for g in pos if g[1:] in guards]
191 exactpos = [g for g in pos if g[1:] in guards]
192 if pos:
192 if pos:
193 if exactpos:
193 if exactpos:
194 return True, exactpos[0]
194 return True, exactpos[0]
195 return False, pos
195 return False, pos
196 return True, ''
196 return True, ''
197
197
198 def explain_pushable(self, idx, all_patches=False):
198 def explain_pushable(self, idx, all_patches=False):
199 write = all_patches and self.ui.write or self.ui.warn
199 write = all_patches and self.ui.write or self.ui.warn
200 if all_patches or self.ui.verbose:
200 if all_patches or self.ui.verbose:
201 if isinstance(idx, str):
201 if isinstance(idx, str):
202 idx = self.series.index(idx)
202 idx = self.series.index(idx)
203 pushable, why = self.pushable(idx)
203 pushable, why = self.pushable(idx)
204 if all_patches and pushable:
204 if all_patches and pushable:
205 if why is None:
205 if why is None:
206 write(_('allowing %s - no guards in effect\n') %
206 write(_('allowing %s - no guards in effect\n') %
207 self.series[idx])
207 self.series[idx])
208 else:
208 else:
209 if not why:
209 if not why:
210 write(_('allowing %s - no matching negative guards\n') %
210 write(_('allowing %s - no matching negative guards\n') %
211 self.series[idx])
211 self.series[idx])
212 else:
212 else:
213 write(_('allowing %s - guarded by %r\n') %
213 write(_('allowing %s - guarded by %r\n') %
214 (self.series[idx], why))
214 (self.series[idx], why))
215 if not pushable:
215 if not pushable:
216 if why:
216 if why:
217 write(_('skipping %s - guarded by %r\n') %
217 write(_('skipping %s - guarded by %r\n') %
218 (self.series[idx], why))
218 (self.series[idx], why))
219 else:
219 else:
220 write(_('skipping %s - no matching guards\n') %
220 write(_('skipping %s - no matching guards\n') %
221 self.series[idx])
221 self.series[idx])
222
222
223 def save_dirty(self):
223 def save_dirty(self):
224 def write_list(items, path):
224 def write_list(items, path):
225 fp = self.opener(path, 'w')
225 fp = self.opener(path, 'w')
226 for i in items:
226 for i in items:
227 print >> fp, i
227 print >> fp, i
228 fp.close()
228 fp.close()
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
229 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
230 if self.series_dirty: write_list(self.full_series, self.series_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
231 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
232
232
233 def readheaders(self, patch):
233 def readheaders(self, patch):
234 def eatdiff(lines):
234 def eatdiff(lines):
235 while lines:
235 while lines:
236 l = lines[-1]
236 l = lines[-1]
237 if (l.startswith("diff -") or
237 if (l.startswith("diff -") or
238 l.startswith("Index:") or
238 l.startswith("Index:") or
239 l.startswith("===========")):
239 l.startswith("===========")):
240 del lines[-1]
240 del lines[-1]
241 else:
241 else:
242 break
242 break
243 def eatempty(lines):
243 def eatempty(lines):
244 while lines:
244 while lines:
245 l = lines[-1]
245 l = lines[-1]
246 if re.match('\s*$', l):
246 if re.match('\s*$', l):
247 del lines[-1]
247 del lines[-1]
248 else:
248 else:
249 break
249 break
250
250
251 pf = self.join(patch)
251 pf = self.join(patch)
252 message = []
252 message = []
253 comments = []
253 comments = []
254 user = None
254 user = None
255 date = None
255 date = None
256 format = None
256 format = None
257 subject = None
257 subject = None
258 diffstart = 0
258 diffstart = 0
259
259
260 for line in file(pf):
260 for line in file(pf):
261 line = line.rstrip()
261 line = line.rstrip()
262 if line.startswith('diff --git'):
262 if line.startswith('diff --git'):
263 diffstart = 2
263 diffstart = 2
264 break
264 break
265 if diffstart:
265 if diffstart:
266 if line.startswith('+++ '):
266 if line.startswith('+++ '):
267 diffstart = 2
267 diffstart = 2
268 break
268 break
269 if line.startswith("--- "):
269 if line.startswith("--- "):
270 diffstart = 1
270 diffstart = 1
271 continue
271 continue
272 elif format == "hgpatch":
272 elif format == "hgpatch":
273 # parse values when importing the result of an hg export
273 # parse values when importing the result of an hg export
274 if line.startswith("# User "):
274 if line.startswith("# User "):
275 user = line[7:]
275 user = line[7:]
276 elif line.startswith("# Date "):
276 elif line.startswith("# Date "):
277 date = line[7:]
277 date = line[7:]
278 elif not line.startswith("# ") and line:
278 elif not line.startswith("# ") and line:
279 message.append(line)
279 message.append(line)
280 format = None
280 format = None
281 elif line == '# HG changeset patch':
281 elif line == '# HG changeset patch':
282 format = "hgpatch"
282 format = "hgpatch"
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
283 elif (format != "tagdone" and (line.startswith("Subject: ") or
284 line.startswith("subject: "))):
284 line.startswith("subject: "))):
285 subject = line[9:]
285 subject = line[9:]
286 format = "tag"
286 format = "tag"
287 elif (format != "tagdone" and (line.startswith("From: ") or
287 elif (format != "tagdone" and (line.startswith("From: ") or
288 line.startswith("from: "))):
288 line.startswith("from: "))):
289 user = line[6:]
289 user = line[6:]
290 format = "tag"
290 format = "tag"
291 elif format == "tag" and line == "":
291 elif format == "tag" and line == "":
292 # when looking for tags (subject: from: etc) they
292 # when looking for tags (subject: from: etc) they
293 # end once you find a blank line in the source
293 # end once you find a blank line in the source
294 format = "tagdone"
294 format = "tagdone"
295 elif message or line:
295 elif message or line:
296 message.append(line)
296 message.append(line)
297 comments.append(line)
297 comments.append(line)
298
298
299 eatdiff(message)
299 eatdiff(message)
300 eatdiff(comments)
300 eatdiff(comments)
301 eatempty(message)
301 eatempty(message)
302 eatempty(comments)
302 eatempty(comments)
303
303
304 # make sure message isn't empty
304 # make sure message isn't empty
305 if format and format.startswith("tag") and subject:
305 if format and format.startswith("tag") and subject:
306 message.insert(0, "")
306 message.insert(0, "")
307 message.insert(0, subject)
307 message.insert(0, subject)
308 return (message, comments, user, date, diffstart > 1)
308 return (message, comments, user, date, diffstart > 1)
309
309
310 def removeundo(self, repo):
310 def removeundo(self, repo):
311 undo = repo.sjoin('undo')
311 undo = repo.sjoin('undo')
312 if not os.path.exists(undo):
312 if not os.path.exists(undo):
313 return
313 return
314 try:
314 try:
315 os.unlink(undo)
315 os.unlink(undo)
316 except OSError, inst:
316 except OSError, inst:
317 self.ui.warn('error removing undo: %s\n' % str(inst))
317 self.ui.warn('error removing undo: %s\n' % str(inst))
318
318
319 def printdiff(self, repo, node1, node2=None, files=None,
319 def printdiff(self, repo, node1, node2=None, files=None,
320 fp=None, changes=None, opts={}):
320 fp=None, changes=None, opts={}):
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
321 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
322
322
323 patch.diff(repo, node1, node2, fns, match=matchfn,
323 patch.diff(repo, node1, node2, fns, match=matchfn,
324 fp=fp, changes=changes, opts=self.diffopts())
324 fp=fp, changes=changes, opts=self.diffopts())
325
325
326 def mergeone(self, repo, mergeq, head, patch, rev):
326 def mergeone(self, repo, mergeq, head, patch, rev):
327 # first try just applying the patch
327 # first try just applying the patch
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
328 (err, n) = self.apply(repo, [ patch ], update_status=False,
329 strict=True, merge=rev)
329 strict=True, merge=rev)
330
330
331 if err == 0:
331 if err == 0:
332 return (err, n)
332 return (err, n)
333
333
334 if n is None:
334 if n is None:
335 raise util.Abort(_("apply failed for patch %s") % patch)
335 raise util.Abort(_("apply failed for patch %s") % patch)
336
336
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
337 self.ui.warn("patch didn't work out, merging %s\n" % patch)
338
338
339 # apply failed, strip away that rev and merge.
339 # apply failed, strip away that rev and merge.
340 hg.clean(repo, head)
340 hg.clean(repo, head)
341 self.strip(repo, n, update=False, backup='strip')
341 self.strip(repo, n, update=False, backup='strip')
342
342
343 ctx = repo.changectx(rev)
343 ctx = repo.changectx(rev)
344 ret = hg.merge(repo, rev)
344 ret = hg.merge(repo, rev)
345 if ret:
345 if ret:
346 raise util.Abort(_("update returned %d") % ret)
346 raise util.Abort(_("update returned %d") % ret)
347 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
347 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
348 if n == None:
348 if n == None:
349 raise util.Abort(_("repo commit failed"))
349 raise util.Abort(_("repo commit failed"))
350 try:
350 try:
351 message, comments, user, date, patchfound = mergeq.readheaders(patch)
351 message, comments, user, date, patchfound = mergeq.readheaders(patch)
352 except:
352 except:
353 raise util.Abort(_("unable to read %s") % patch)
353 raise util.Abort(_("unable to read %s") % patch)
354
354
355 patchf = self.opener(patch, "w")
355 patchf = self.opener(patch, "w")
356 if comments:
356 if comments:
357 comments = "\n".join(comments) + '\n\n'
357 comments = "\n".join(comments) + '\n\n'
358 patchf.write(comments)
358 patchf.write(comments)
359 self.printdiff(repo, head, n, fp=patchf)
359 self.printdiff(repo, head, n, fp=patchf)
360 patchf.close()
360 patchf.close()
361 self.removeundo(repo)
361 self.removeundo(repo)
362 return (0, n)
362 return (0, n)
363
363
364 def qparents(self, repo, rev=None):
364 def qparents(self, repo, rev=None):
365 if rev is None:
365 if rev is None:
366 (p1, p2) = repo.dirstate.parents()
366 (p1, p2) = repo.dirstate.parents()
367 if p2 == revlog.nullid:
367 if p2 == revlog.nullid:
368 return p1
368 return p1
369 if len(self.applied) == 0:
369 if len(self.applied) == 0:
370 return None
370 return None
371 return revlog.bin(self.applied[-1].rev)
371 return revlog.bin(self.applied[-1].rev)
372 pp = repo.changelog.parents(rev)
372 pp = repo.changelog.parents(rev)
373 if pp[1] != revlog.nullid:
373 if pp[1] != revlog.nullid:
374 arevs = [ x.rev for x in self.applied ]
374 arevs = [ x.rev for x in self.applied ]
375 p0 = revlog.hex(pp[0])
375 p0 = revlog.hex(pp[0])
376 p1 = revlog.hex(pp[1])
376 p1 = revlog.hex(pp[1])
377 if p0 in arevs:
377 if p0 in arevs:
378 return pp[0]
378 return pp[0]
379 if p1 in arevs:
379 if p1 in arevs:
380 return pp[1]
380 return pp[1]
381 return pp[0]
381 return pp[0]
382
382
383 def mergepatch(self, repo, mergeq, series):
383 def mergepatch(self, repo, mergeq, series):
384 if len(self.applied) == 0:
384 if len(self.applied) == 0:
385 # each of the patches merged in will have two parents. This
385 # each of the patches merged in will have two parents. This
386 # can confuse the qrefresh, qdiff, and strip code because it
386 # can confuse the qrefresh, qdiff, and strip code because it
387 # needs to know which parent is actually in the patch queue.
387 # needs to know which parent is actually in the patch queue.
388 # so, we insert a merge marker with only one parent. This way
388 # so, we insert a merge marker with only one parent. This way
389 # the first patch in the queue is never a merge patch
389 # the first patch in the queue is never a merge patch
390 #
390 #
391 pname = ".hg.patches.merge.marker"
391 pname = ".hg.patches.merge.marker"
392 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
392 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
393 self.removeundo(repo)
393 self.removeundo(repo)
394 self.applied.append(statusentry(revlog.hex(n), pname))
394 self.applied.append(statusentry(revlog.hex(n), pname))
395 self.applied_dirty = 1
395 self.applied_dirty = 1
396
396
397 head = self.qparents(repo)
397 head = self.qparents(repo)
398
398
399 for patch in series:
399 for patch in series:
400 patch = mergeq.lookup(patch, strict=True)
400 patch = mergeq.lookup(patch, strict=True)
401 if not patch:
401 if not patch:
402 self.ui.warn("patch %s does not exist\n" % patch)
402 self.ui.warn("patch %s does not exist\n" % patch)
403 return (1, None)
403 return (1, None)
404 pushable, reason = self.pushable(patch)
404 pushable, reason = self.pushable(patch)
405 if not pushable:
405 if not pushable:
406 self.explain_pushable(patch, all_patches=True)
406 self.explain_pushable(patch, all_patches=True)
407 continue
407 continue
408 info = mergeq.isapplied(patch)
408 info = mergeq.isapplied(patch)
409 if not info:
409 if not info:
410 self.ui.warn("patch %s is not applied\n" % patch)
410 self.ui.warn("patch %s is not applied\n" % patch)
411 return (1, None)
411 return (1, None)
412 rev = revlog.bin(info[1])
412 rev = revlog.bin(info[1])
413 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
413 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
414 if head:
414 if head:
415 self.applied.append(statusentry(revlog.hex(head), patch))
415 self.applied.append(statusentry(revlog.hex(head), patch))
416 self.applied_dirty = 1
416 self.applied_dirty = 1
417 if err:
417 if err:
418 return (err, head)
418 return (err, head)
419 self.save_dirty()
419 self.save_dirty()
420 return (0, head)
420 return (0, head)
421
421
422 def patch(self, repo, patchfile):
422 def patch(self, repo, patchfile):
423 '''Apply patchfile to the working directory.
423 '''Apply patchfile to the working directory.
424 patchfile: file name of patch'''
424 patchfile: file name of patch'''
425 files = {}
425 files = {}
426 try:
426 try:
427 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
427 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
428 files=files)
428 files=files)
429 except Exception, inst:
429 except Exception, inst:
430 self.ui.note(str(inst) + '\n')
430 self.ui.note(str(inst) + '\n')
431 if not self.ui.verbose:
431 if not self.ui.verbose:
432 self.ui.warn("patch failed, unable to continue (try -v)\n")
432 self.ui.warn("patch failed, unable to continue (try -v)\n")
433 return (False, files, False)
433 return (False, files, False)
434
434
435 return (True, files, fuzz)
435 return (True, files, fuzz)
436
436
437 def apply(self, repo, series, list=False, update_status=True,
437 def apply(self, repo, series, list=False, update_status=True,
438 strict=False, patchdir=None, merge=None, all_files={}):
438 strict=False, patchdir=None, merge=None, all_files={}):
439 wlock = lock = tr = None
439 wlock = lock = tr = None
440 try:
440 try:
441 wlock = repo.wlock()
441 wlock = repo.wlock()
442 lock = repo.lock()
442 lock = repo.lock()
443 tr = repo.transaction()
443 tr = repo.transaction()
444 try:
444 try:
445 ret = self._apply(repo, series, list, update_status,
445 ret = self._apply(repo, series, list, update_status,
446 strict, patchdir, merge, all_files=all_files)
446 strict, patchdir, merge, all_files=all_files)
447 tr.close()
447 tr.close()
448 self.save_dirty()
448 self.save_dirty()
449 return ret
449 return ret
450 except:
450 except:
451 try:
451 try:
452 tr.abort()
452 tr.abort()
453 finally:
453 finally:
454 repo.invalidate()
454 repo.invalidate()
455 repo.dirstate.invalidate()
455 repo.dirstate.invalidate()
456 raise
456 raise
457 finally:
457 finally:
458 del tr, lock, wlock
458 del tr, lock, wlock
459 self.removeundo(repo)
459
460
460 def _apply(self, repo, series, list=False, update_status=True,
461 def _apply(self, repo, series, list=False, update_status=True,
461 strict=False, patchdir=None, merge=None, all_files={}):
462 strict=False, patchdir=None, merge=None, all_files={}):
462 # TODO unify with commands.py
463 # TODO unify with commands.py
463 if not patchdir:
464 if not patchdir:
464 patchdir = self.path
465 patchdir = self.path
465 err = 0
466 err = 0
466 n = None
467 n = None
467 for patchname in series:
468 for patchname in series:
468 pushable, reason = self.pushable(patchname)
469 pushable, reason = self.pushable(patchname)
469 if not pushable:
470 if not pushable:
470 self.explain_pushable(patchname, all_patches=True)
471 self.explain_pushable(patchname, all_patches=True)
471 continue
472 continue
472 self.ui.warn("applying %s\n" % patchname)
473 self.ui.warn("applying %s\n" % patchname)
473 pf = os.path.join(patchdir, patchname)
474 pf = os.path.join(patchdir, patchname)
474
475
475 try:
476 try:
476 message, comments, user, date, patchfound = self.readheaders(patchname)
477 message, comments, user, date, patchfound = self.readheaders(patchname)
477 except:
478 except:
478 self.ui.warn("Unable to read %s\n" % patchname)
479 self.ui.warn("Unable to read %s\n" % patchname)
479 err = 1
480 err = 1
480 break
481 break
481
482
482 if not message:
483 if not message:
483 message = "imported patch %s\n" % patchname
484 message = "imported patch %s\n" % patchname
484 else:
485 else:
485 if list:
486 if list:
486 message.append("\nimported patch %s" % patchname)
487 message.append("\nimported patch %s" % patchname)
487 message = '\n'.join(message)
488 message = '\n'.join(message)
488
489
489 (patcherr, files, fuzz) = self.patch(repo, pf)
490 (patcherr, files, fuzz) = self.patch(repo, pf)
490 all_files.update(files)
491 all_files.update(files)
491 patcherr = not patcherr
492 patcherr = not patcherr
492
493
493 if merge and files:
494 if merge and files:
494 # Mark as removed/merged and update dirstate parent info
495 # Mark as removed/merged and update dirstate parent info
495 removed = []
496 removed = []
496 merged = []
497 merged = []
497 for f in files:
498 for f in files:
498 if os.path.exists(repo.wjoin(f)):
499 if os.path.exists(repo.wjoin(f)):
499 merged.append(f)
500 merged.append(f)
500 else:
501 else:
501 removed.append(f)
502 removed.append(f)
502 for f in removed:
503 for f in removed:
503 repo.dirstate.remove(f)
504 repo.dirstate.remove(f)
504 for f in merged:
505 for f in merged:
505 repo.dirstate.merge(f)
506 repo.dirstate.merge(f)
506 p1, p2 = repo.dirstate.parents()
507 p1, p2 = repo.dirstate.parents()
507 repo.dirstate.setparents(p1, merge)
508 repo.dirstate.setparents(p1, merge)
508 files = patch.updatedir(self.ui, repo, files)
509 files = patch.updatedir(self.ui, repo, files)
509 n = repo.commit(files, message, user, date, force=1)
510 n = repo.commit(files, message, user, date, force=1)
510
511
511 if n == None:
512 if n == None:
512 raise util.Abort(_("repo commit failed"))
513 raise util.Abort(_("repo commit failed"))
513
514
514 if update_status:
515 if update_status:
515 self.applied.append(statusentry(revlog.hex(n), patchname))
516 self.applied.append(statusentry(revlog.hex(n), patchname))
516
517
517 if patcherr:
518 if patcherr:
518 if not patchfound:
519 if not patchfound:
519 self.ui.warn("patch %s is empty\n" % patchname)
520 self.ui.warn("patch %s is empty\n" % patchname)
520 err = 0
521 err = 0
521 else:
522 else:
522 self.ui.warn("patch failed, rejects left in working dir\n")
523 self.ui.warn("patch failed, rejects left in working dir\n")
523 err = 1
524 err = 1
524 break
525 break
525
526
526 if fuzz and strict:
527 if fuzz and strict:
527 self.ui.warn("fuzz found when applying patch, stopping\n")
528 self.ui.warn("fuzz found when applying patch, stopping\n")
528 err = 1
529 err = 1
529 break
530 break
530 self.removeundo(repo)
531 return (err, n)
531 return (err, n)
532
532
533 def delete(self, repo, patches, opts):
533 def delete(self, repo, patches, opts):
534 if not patches and not opts.get('rev'):
534 if not patches and not opts.get('rev'):
535 raise util.Abort(_('qdelete requires at least one revision or '
535 raise util.Abort(_('qdelete requires at least one revision or '
536 'patch name'))
536 'patch name'))
537
537
538 realpatches = []
538 realpatches = []
539 for patch in patches:
539 for patch in patches:
540 patch = self.lookup(patch, strict=True)
540 patch = self.lookup(patch, strict=True)
541 info = self.isapplied(patch)
541 info = self.isapplied(patch)
542 if info:
542 if info:
543 raise util.Abort(_("cannot delete applied patch %s") % patch)
543 raise util.Abort(_("cannot delete applied patch %s") % patch)
544 if patch not in self.series:
544 if patch not in self.series:
545 raise util.Abort(_("patch %s not in series file") % patch)
545 raise util.Abort(_("patch %s not in series file") % patch)
546 realpatches.append(patch)
546 realpatches.append(patch)
547
547
548 appliedbase = 0
548 appliedbase = 0
549 if opts.get('rev'):
549 if opts.get('rev'):
550 if not self.applied:
550 if not self.applied:
551 raise util.Abort(_('no patches applied'))
551 raise util.Abort(_('no patches applied'))
552 revs = cmdutil.revrange(repo, opts['rev'])
552 revs = cmdutil.revrange(repo, opts['rev'])
553 if len(revs) > 1 and revs[0] > revs[1]:
553 if len(revs) > 1 and revs[0] > revs[1]:
554 revs.reverse()
554 revs.reverse()
555 for rev in revs:
555 for rev in revs:
556 if appliedbase >= len(self.applied):
556 if appliedbase >= len(self.applied):
557 raise util.Abort(_("revision %d is not managed") % rev)
557 raise util.Abort(_("revision %d is not managed") % rev)
558
558
559 base = revlog.bin(self.applied[appliedbase].rev)
559 base = revlog.bin(self.applied[appliedbase].rev)
560 node = repo.changelog.node(rev)
560 node = repo.changelog.node(rev)
561 if node != base:
561 if node != base:
562 raise util.Abort(_("cannot delete revision %d above "
562 raise util.Abort(_("cannot delete revision %d above "
563 "applied patches") % rev)
563 "applied patches") % rev)
564 realpatches.append(self.applied[appliedbase].name)
564 realpatches.append(self.applied[appliedbase].name)
565 appliedbase += 1
565 appliedbase += 1
566
566
567 if not opts.get('keep'):
567 if not opts.get('keep'):
568 r = self.qrepo()
568 r = self.qrepo()
569 if r:
569 if r:
570 r.remove(realpatches, True)
570 r.remove(realpatches, True)
571 else:
571 else:
572 for p in realpatches:
572 for p in realpatches:
573 os.unlink(self.join(p))
573 os.unlink(self.join(p))
574
574
575 if appliedbase:
575 if appliedbase:
576 del self.applied[:appliedbase]
576 del self.applied[:appliedbase]
577 self.applied_dirty = 1
577 self.applied_dirty = 1
578 indices = [self.find_series(p) for p in realpatches]
578 indices = [self.find_series(p) for p in realpatches]
579 indices.sort()
579 indices.sort()
580 for i in indices[-1::-1]:
580 for i in indices[-1::-1]:
581 del self.full_series[i]
581 del self.full_series[i]
582 self.parse_series()
582 self.parse_series()
583 self.series_dirty = 1
583 self.series_dirty = 1
584
584
585 def check_toppatch(self, repo):
585 def check_toppatch(self, repo):
586 if len(self.applied) > 0:
586 if len(self.applied) > 0:
587 top = revlog.bin(self.applied[-1].rev)
587 top = revlog.bin(self.applied[-1].rev)
588 pp = repo.dirstate.parents()
588 pp = repo.dirstate.parents()
589 if top not in pp:
589 if top not in pp:
590 raise util.Abort(_("working directory revision is not qtip"))
590 raise util.Abort(_("working directory revision is not qtip"))
591 return top
591 return top
592 return None
592 return None
593 def check_localchanges(self, repo, force=False, refresh=True):
593 def check_localchanges(self, repo, force=False, refresh=True):
594 m, a, r, d = repo.status()[:4]
594 m, a, r, d = repo.status()[:4]
595 if m or a or r or d:
595 if m or a or r or d:
596 if not force:
596 if not force:
597 if refresh:
597 if refresh:
598 raise util.Abort(_("local changes found, refresh first"))
598 raise util.Abort(_("local changes found, refresh first"))
599 else:
599 else:
600 raise util.Abort(_("local changes found"))
600 raise util.Abort(_("local changes found"))
601 return m, a, r, d
601 return m, a, r, d
602
602
603 def new(self, repo, patch, *pats, **opts):
603 def new(self, repo, patch, *pats, **opts):
604 msg = opts.get('msg')
604 msg = opts.get('msg')
605 force = opts.get('force')
605 force = opts.get('force')
606 if os.path.exists(self.join(patch)):
606 if os.path.exists(self.join(patch)):
607 raise util.Abort(_('patch "%s" already exists') % patch)
607 raise util.Abort(_('patch "%s" already exists') % patch)
608 if opts.get('include') or opts.get('exclude') or pats:
608 if opts.get('include') or opts.get('exclude') or pats:
609 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
609 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
610 m, a, r, d = repo.status(files=fns, match=match)[:4]
610 m, a, r, d = repo.status(files=fns, match=match)[:4]
611 else:
611 else:
612 m, a, r, d = self.check_localchanges(repo, force)
612 m, a, r, d = self.check_localchanges(repo, force)
613 commitfiles = m + a + r
613 commitfiles = m + a + r
614 self.check_toppatch(repo)
614 self.check_toppatch(repo)
615 wlock = repo.wlock()
615 wlock = repo.wlock()
616 try:
616 try:
617 insert = self.full_series_end()
617 insert = self.full_series_end()
618 if msg:
618 if msg:
619 n = repo.commit(commitfiles, msg, force=True)
619 n = repo.commit(commitfiles, msg, force=True)
620 else:
620 else:
621 n = repo.commit(commitfiles, "[mq]: %s" % patch, force=True)
621 n = repo.commit(commitfiles, "[mq]: %s" % patch, force=True)
622 if n == None:
622 if n == None:
623 raise util.Abort(_("repo commit failed"))
623 raise util.Abort(_("repo commit failed"))
624 self.full_series[insert:insert] = [patch]
624 self.full_series[insert:insert] = [patch]
625 self.applied.append(statusentry(revlog.hex(n), patch))
625 self.applied.append(statusentry(revlog.hex(n), patch))
626 self.parse_series()
626 self.parse_series()
627 self.series_dirty = 1
627 self.series_dirty = 1
628 self.applied_dirty = 1
628 self.applied_dirty = 1
629 p = self.opener(patch, "w")
629 p = self.opener(patch, "w")
630 if msg:
630 if msg:
631 msg = msg + "\n"
631 msg = msg + "\n"
632 p.write(msg)
632 p.write(msg)
633 p.close()
633 p.close()
634 wlock = None
634 wlock = None
635 r = self.qrepo()
635 r = self.qrepo()
636 if r: r.add([patch])
636 if r: r.add([patch])
637 if commitfiles:
637 if commitfiles:
638 self.refresh(repo, short=True, git=opts.get('git'))
638 self.refresh(repo, short=True, git=opts.get('git'))
639 self.removeundo(repo)
639 self.removeundo(repo)
640 finally:
640 finally:
641 del wlock
641 del wlock
642
642
643 def strip(self, repo, rev, update=True, backup="all"):
643 def strip(self, repo, rev, update=True, backup="all"):
644 wlock = lock = None
644 wlock = lock = None
645 try:
645 try:
646 wlock = repo.wlock()
646 wlock = repo.wlock()
647 lock = repo.lock()
647 lock = repo.lock()
648
648
649 if update:
649 if update:
650 self.check_localchanges(repo, refresh=False)
650 self.check_localchanges(repo, refresh=False)
651 urev = self.qparents(repo, rev)
651 urev = self.qparents(repo, rev)
652 hg.clean(repo, urev)
652 hg.clean(repo, urev)
653 repo.dirstate.write()
653 repo.dirstate.write()
654
654
655 self.removeundo(repo)
655 self.removeundo(repo)
656 repair.strip(self.ui, repo, rev, backup)
656 repair.strip(self.ui, repo, rev, backup)
657 # strip may have unbundled a set of backed up revisions after
658 # the actual strip
659 self.removeundo(repo)
657 finally:
660 finally:
658 del lock, wlock
661 del lock, wlock
659
662
660 def isapplied(self, patch):
663 def isapplied(self, patch):
661 """returns (index, rev, patch)"""
664 """returns (index, rev, patch)"""
662 for i in xrange(len(self.applied)):
665 for i in xrange(len(self.applied)):
663 a = self.applied[i]
666 a = self.applied[i]
664 if a.name == patch:
667 if a.name == patch:
665 return (i, a.rev, a.name)
668 return (i, a.rev, a.name)
666 return None
669 return None
667
670
668 # if the exact patch name does not exist, we try a few
671 # if the exact patch name does not exist, we try a few
669 # variations. If strict is passed, we try only #1
672 # variations. If strict is passed, we try only #1
670 #
673 #
671 # 1) a number to indicate an offset in the series file
674 # 1) a number to indicate an offset in the series file
672 # 2) a unique substring of the patch name was given
675 # 2) a unique substring of the patch name was given
673 # 3) patchname[-+]num to indicate an offset in the series file
676 # 3) patchname[-+]num to indicate an offset in the series file
674 def lookup(self, patch, strict=False):
677 def lookup(self, patch, strict=False):
675 patch = patch and str(patch)
678 patch = patch and str(patch)
676
679
677 def partial_name(s):
680 def partial_name(s):
678 if s in self.series:
681 if s in self.series:
679 return s
682 return s
680 matches = [x for x in self.series if s in x]
683 matches = [x for x in self.series if s in x]
681 if len(matches) > 1:
684 if len(matches) > 1:
682 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
685 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
683 for m in matches:
686 for m in matches:
684 self.ui.warn(' %s\n' % m)
687 self.ui.warn(' %s\n' % m)
685 return None
688 return None
686 if matches:
689 if matches:
687 return matches[0]
690 return matches[0]
688 if len(self.series) > 0 and len(self.applied) > 0:
691 if len(self.series) > 0 and len(self.applied) > 0:
689 if s == 'qtip':
692 if s == 'qtip':
690 return self.series[self.series_end(True)-1]
693 return self.series[self.series_end(True)-1]
691 if s == 'qbase':
694 if s == 'qbase':
692 return self.series[0]
695 return self.series[0]
693 return None
696 return None
694 if patch == None:
697 if patch == None:
695 return None
698 return None
696
699
697 # we don't want to return a partial match until we make
700 # we don't want to return a partial match until we make
698 # sure the file name passed in does not exist (checked below)
701 # sure the file name passed in does not exist (checked below)
699 res = partial_name(patch)
702 res = partial_name(patch)
700 if res and res == patch:
703 if res and res == patch:
701 return res
704 return res
702
705
703 if not os.path.isfile(self.join(patch)):
706 if not os.path.isfile(self.join(patch)):
704 try:
707 try:
705 sno = int(patch)
708 sno = int(patch)
706 except(ValueError, OverflowError):
709 except(ValueError, OverflowError):
707 pass
710 pass
708 else:
711 else:
709 if sno < len(self.series):
712 if sno < len(self.series):
710 return self.series[sno]
713 return self.series[sno]
711 if not strict:
714 if not strict:
712 # return any partial match made above
715 # return any partial match made above
713 if res:
716 if res:
714 return res
717 return res
715 minus = patch.rfind('-')
718 minus = patch.rfind('-')
716 if minus >= 0:
719 if minus >= 0:
717 res = partial_name(patch[:minus])
720 res = partial_name(patch[:minus])
718 if res:
721 if res:
719 i = self.series.index(res)
722 i = self.series.index(res)
720 try:
723 try:
721 off = int(patch[minus+1:] or 1)
724 off = int(patch[minus+1:] or 1)
722 except(ValueError, OverflowError):
725 except(ValueError, OverflowError):
723 pass
726 pass
724 else:
727 else:
725 if i - off >= 0:
728 if i - off >= 0:
726 return self.series[i - off]
729 return self.series[i - off]
727 plus = patch.rfind('+')
730 plus = patch.rfind('+')
728 if plus >= 0:
731 if plus >= 0:
729 res = partial_name(patch[:plus])
732 res = partial_name(patch[:plus])
730 if res:
733 if res:
731 i = self.series.index(res)
734 i = self.series.index(res)
732 try:
735 try:
733 off = int(patch[plus+1:] or 1)
736 off = int(patch[plus+1:] or 1)
734 except(ValueError, OverflowError):
737 except(ValueError, OverflowError):
735 pass
738 pass
736 else:
739 else:
737 if i + off < len(self.series):
740 if i + off < len(self.series):
738 return self.series[i + off]
741 return self.series[i + off]
739 raise util.Abort(_("patch %s not in series") % patch)
742 raise util.Abort(_("patch %s not in series") % patch)
740
743
741 def push(self, repo, patch=None, force=False, list=False,
744 def push(self, repo, patch=None, force=False, list=False,
742 mergeq=None):
745 mergeq=None):
743 wlock = repo.wlock()
746 wlock = repo.wlock()
744 try:
747 try:
745 patch = self.lookup(patch)
748 patch = self.lookup(patch)
746 # Suppose our series file is: A B C and the current 'top'
749 # Suppose our series file is: A B C and the current 'top'
747 # patch is B. qpush C should be performed (moving forward)
750 # patch is B. qpush C should be performed (moving forward)
748 # qpush B is a NOP (no change) qpush A is an error (can't
751 # qpush B is a NOP (no change) qpush A is an error (can't
749 # go backwards with qpush)
752 # go backwards with qpush)
750 if patch:
753 if patch:
751 info = self.isapplied(patch)
754 info = self.isapplied(patch)
752 if info:
755 if info:
753 if info[0] < len(self.applied) - 1:
756 if info[0] < len(self.applied) - 1:
754 raise util.Abort(
757 raise util.Abort(
755 _("cannot push to a previous patch: %s") % patch)
758 _("cannot push to a previous patch: %s") % patch)
756 if info[0] < len(self.series) - 1:
759 if info[0] < len(self.series) - 1:
757 self.ui.warn(
760 self.ui.warn(
758 _('qpush: %s is already at the top\n') % patch)
761 _('qpush: %s is already at the top\n') % patch)
759 else:
762 else:
760 self.ui.warn(_('all patches are currently applied\n'))
763 self.ui.warn(_('all patches are currently applied\n'))
761 return
764 return
762
765
763 # Following the above example, starting at 'top' of B:
766 # Following the above example, starting at 'top' of B:
764 # qpush should be performed (pushes C), but a subsequent
767 # qpush should be performed (pushes C), but a subsequent
765 # qpush without an argument is an error (nothing to
768 # qpush without an argument is an error (nothing to
766 # apply). This allows a loop of "...while hg qpush..." to
769 # apply). This allows a loop of "...while hg qpush..." to
767 # work as it detects an error when done
770 # work as it detects an error when done
768 if self.series_end() == len(self.series):
771 if self.series_end() == len(self.series):
769 self.ui.warn(_('patch series already fully applied\n'))
772 self.ui.warn(_('patch series already fully applied\n'))
770 return 1
773 return 1
771 if not force:
774 if not force:
772 self.check_localchanges(repo)
775 self.check_localchanges(repo)
773
776
774 self.applied_dirty = 1;
777 self.applied_dirty = 1;
775 start = self.series_end()
778 start = self.series_end()
776 if start > 0:
779 if start > 0:
777 self.check_toppatch(repo)
780 self.check_toppatch(repo)
778 if not patch:
781 if not patch:
779 patch = self.series[start]
782 patch = self.series[start]
780 end = start + 1
783 end = start + 1
781 else:
784 else:
782 end = self.series.index(patch, start) + 1
785 end = self.series.index(patch, start) + 1
783 s = self.series[start:end]
786 s = self.series[start:end]
784 all_files = {}
787 all_files = {}
785 try:
788 try:
786 if mergeq:
789 if mergeq:
787 ret = self.mergepatch(repo, mergeq, s)
790 ret = self.mergepatch(repo, mergeq, s)
788 else:
791 else:
789 ret = self.apply(repo, s, list, all_files=all_files)
792 ret = self.apply(repo, s, list, all_files=all_files)
790 except:
793 except:
791 self.ui.warn(_('cleaning up working directory...'))
794 self.ui.warn(_('cleaning up working directory...'))
792 node = repo.dirstate.parents()[0]
795 node = repo.dirstate.parents()[0]
793 hg.revert(repo, node, None)
796 hg.revert(repo, node, None)
794 unknown = repo.status()[4]
797 unknown = repo.status()[4]
795 # only remove unknown files that we know we touched or
798 # only remove unknown files that we know we touched or
796 # created while patching
799 # created while patching
797 for f in unknown:
800 for f in unknown:
798 if f in all_files:
801 if f in all_files:
799 util.unlink(repo.wjoin(f))
802 util.unlink(repo.wjoin(f))
800 self.ui.warn(_('done\n'))
803 self.ui.warn(_('done\n'))
801 raise
804 raise
802 top = self.applied[-1].name
805 top = self.applied[-1].name
803 if ret[0]:
806 if ret[0]:
804 self.ui.write(
807 self.ui.write(
805 "Errors during apply, please fix and refresh %s\n" % top)
808 "Errors during apply, please fix and refresh %s\n" % top)
806 else:
809 else:
807 self.ui.write("Now at: %s\n" % top)
810 self.ui.write("Now at: %s\n" % top)
808 return ret[0]
811 return ret[0]
809 finally:
812 finally:
810 del wlock
813 del wlock
811
814
812 def pop(self, repo, patch=None, force=False, update=True, all=False):
815 def pop(self, repo, patch=None, force=False, update=True, all=False):
813 def getfile(f, rev, flags):
816 def getfile(f, rev, flags):
814 t = repo.file(f).read(rev)
817 t = repo.file(f).read(rev)
815 repo.wwrite(f, t, flags)
818 repo.wwrite(f, t, flags)
816
819
817 wlock = repo.wlock()
820 wlock = repo.wlock()
818 try:
821 try:
819 if patch:
822 if patch:
820 # index, rev, patch
823 # index, rev, patch
821 info = self.isapplied(patch)
824 info = self.isapplied(patch)
822 if not info:
825 if not info:
823 patch = self.lookup(patch)
826 patch = self.lookup(patch)
824 info = self.isapplied(patch)
827 info = self.isapplied(patch)
825 if not info:
828 if not info:
826 raise util.Abort(_("patch %s is not applied") % patch)
829 raise util.Abort(_("patch %s is not applied") % patch)
827
830
828 if len(self.applied) == 0:
831 if len(self.applied) == 0:
829 # Allow qpop -a to work repeatedly,
832 # Allow qpop -a to work repeatedly,
830 # but not qpop without an argument
833 # but not qpop without an argument
831 self.ui.warn(_("no patches applied\n"))
834 self.ui.warn(_("no patches applied\n"))
832 return not all
835 return not all
833
836
834 if not update:
837 if not update:
835 parents = repo.dirstate.parents()
838 parents = repo.dirstate.parents()
836 rr = [ revlog.bin(x.rev) for x in self.applied ]
839 rr = [ revlog.bin(x.rev) for x in self.applied ]
837 for p in parents:
840 for p in parents:
838 if p in rr:
841 if p in rr:
839 self.ui.warn("qpop: forcing dirstate update\n")
842 self.ui.warn("qpop: forcing dirstate update\n")
840 update = True
843 update = True
841
844
842 if not force and update:
845 if not force and update:
843 self.check_localchanges(repo)
846 self.check_localchanges(repo)
844
847
845 self.applied_dirty = 1;
848 self.applied_dirty = 1;
846 end = len(self.applied)
849 end = len(self.applied)
847 if not patch:
850 if not patch:
848 if all:
851 if all:
849 popi = 0
852 popi = 0
850 else:
853 else:
851 popi = len(self.applied) - 1
854 popi = len(self.applied) - 1
852 else:
855 else:
853 popi = info[0] + 1
856 popi = info[0] + 1
854 if popi >= end:
857 if popi >= end:
855 self.ui.warn("qpop: %s is already at the top\n" % patch)
858 self.ui.warn("qpop: %s is already at the top\n" % patch)
856 return
859 return
857 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
860 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
858
861
859 start = info[0]
862 start = info[0]
860 rev = revlog.bin(info[1])
863 rev = revlog.bin(info[1])
861
864
862 # we know there are no local changes, so we can make a simplified
865 # we know there are no local changes, so we can make a simplified
863 # form of hg.update.
866 # form of hg.update.
864 if update:
867 if update:
865 top = self.check_toppatch(repo)
868 top = self.check_toppatch(repo)
866 qp = self.qparents(repo, rev)
869 qp = self.qparents(repo, rev)
867 changes = repo.changelog.read(qp)
870 changes = repo.changelog.read(qp)
868 mmap = repo.manifest.read(changes[0])
871 mmap = repo.manifest.read(changes[0])
869 m, a, r, d, u = repo.status(qp, top)[:5]
872 m, a, r, d, u = repo.status(qp, top)[:5]
870 if d:
873 if d:
871 raise util.Abort("deletions found between repo revs")
874 raise util.Abort("deletions found between repo revs")
872 for f in m:
875 for f in m:
873 getfile(f, mmap[f], mmap.flags(f))
876 getfile(f, mmap[f], mmap.flags(f))
874 for f in r:
877 for f in r:
875 getfile(f, mmap[f], mmap.flags(f))
878 getfile(f, mmap[f], mmap.flags(f))
876 for f in m + r:
879 for f in m + r:
877 repo.dirstate.normal(f)
880 repo.dirstate.normal(f)
878 for f in a:
881 for f in a:
879 try:
882 try:
880 os.unlink(repo.wjoin(f))
883 os.unlink(repo.wjoin(f))
881 except OSError, e:
884 except OSError, e:
882 if e.errno != errno.ENOENT:
885 if e.errno != errno.ENOENT:
883 raise
886 raise
884 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
887 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
885 except: pass
888 except: pass
886 repo.dirstate.forget(f)
889 repo.dirstate.forget(f)
887 repo.dirstate.setparents(qp, revlog.nullid)
890 repo.dirstate.setparents(qp, revlog.nullid)
888 self.strip(repo, rev, update=False, backup='strip')
891 self.strip(repo, rev, update=False, backup='strip')
889 del self.applied[start:end]
892 del self.applied[start:end]
890 if len(self.applied):
893 if len(self.applied):
891 self.ui.write("Now at: %s\n" % self.applied[-1].name)
894 self.ui.write("Now at: %s\n" % self.applied[-1].name)
892 else:
895 else:
893 self.ui.write("Patch queue now empty\n")
896 self.ui.write("Patch queue now empty\n")
894 finally:
897 finally:
895 del wlock
898 del wlock
896
899
897 def diff(self, repo, pats, opts):
900 def diff(self, repo, pats, opts):
898 top = self.check_toppatch(repo)
901 top = self.check_toppatch(repo)
899 if not top:
902 if not top:
900 self.ui.write("No patches applied\n")
903 self.ui.write("No patches applied\n")
901 return
904 return
902 qp = self.qparents(repo, top)
905 qp = self.qparents(repo, top)
903 if opts.get('git'):
906 if opts.get('git'):
904 self.diffopts().git = True
907 self.diffopts().git = True
905 self.printdiff(repo, qp, files=pats, opts=opts)
908 self.printdiff(repo, qp, files=pats, opts=opts)
906
909
907 def refresh(self, repo, pats=None, **opts):
910 def refresh(self, repo, pats=None, **opts):
908 if len(self.applied) == 0:
911 if len(self.applied) == 0:
909 self.ui.write("No patches applied\n")
912 self.ui.write("No patches applied\n")
910 return 1
913 return 1
911 wlock = repo.wlock()
914 wlock = repo.wlock()
912 try:
915 try:
913 self.check_toppatch(repo)
916 self.check_toppatch(repo)
914 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
917 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
915 top = revlog.bin(top)
918 top = revlog.bin(top)
916 cparents = repo.changelog.parents(top)
919 cparents = repo.changelog.parents(top)
917 patchparent = self.qparents(repo, top)
920 patchparent = self.qparents(repo, top)
918 message, comments, user, date, patchfound = self.readheaders(patchfn)
921 message, comments, user, date, patchfound = self.readheaders(patchfn)
919
922
920 patchf = self.opener(patchfn, 'r+')
923 patchf = self.opener(patchfn, 'r+')
921
924
922 # if the patch was a git patch, refresh it as a git patch
925 # if the patch was a git patch, refresh it as a git patch
923 for line in patchf:
926 for line in patchf:
924 if line.startswith('diff --git'):
927 if line.startswith('diff --git'):
925 self.diffopts().git = True
928 self.diffopts().git = True
926 break
929 break
927
930
928 msg = opts.get('msg', '').rstrip()
931 msg = opts.get('msg', '').rstrip()
929 if msg and comments:
932 if msg and comments:
930 # Remove existing message, keeping the rest of the comments
933 # Remove existing message, keeping the rest of the comments
931 # fields.
934 # fields.
932 # If comments contains 'subject: ', message will prepend
935 # If comments contains 'subject: ', message will prepend
933 # the field and a blank line.
936 # the field and a blank line.
934 if message:
937 if message:
935 subj = 'subject: ' + message[0].lower()
938 subj = 'subject: ' + message[0].lower()
936 for i in xrange(len(comments)):
939 for i in xrange(len(comments)):
937 if subj == comments[i].lower():
940 if subj == comments[i].lower():
938 del comments[i]
941 del comments[i]
939 message = message[2:]
942 message = message[2:]
940 break
943 break
941 ci = 0
944 ci = 0
942 for mi in xrange(len(message)):
945 for mi in xrange(len(message)):
943 while message[mi] != comments[ci]:
946 while message[mi] != comments[ci]:
944 ci += 1
947 ci += 1
945 del comments[ci]
948 del comments[ci]
946 if msg:
949 if msg:
947 comments.append(msg)
950 comments.append(msg)
948
951
949 patchf.seek(0)
952 patchf.seek(0)
950 patchf.truncate()
953 patchf.truncate()
951
954
952 if comments:
955 if comments:
953 comments = "\n".join(comments) + '\n\n'
956 comments = "\n".join(comments) + '\n\n'
954 patchf.write(comments)
957 patchf.write(comments)
955
958
956 if opts.get('git'):
959 if opts.get('git'):
957 self.diffopts().git = True
960 self.diffopts().git = True
958 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
961 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
959 tip = repo.changelog.tip()
962 tip = repo.changelog.tip()
960 if top == tip:
963 if top == tip:
961 # if the top of our patch queue is also the tip, there is an
964 # if the top of our patch queue is also the tip, there is an
962 # optimization here. We update the dirstate in place and strip
965 # optimization here. We update the dirstate in place and strip
963 # off the tip commit. Then just commit the current directory
966 # off the tip commit. Then just commit the current directory
964 # tree. We can also send repo.commit the list of files
967 # tree. We can also send repo.commit the list of files
965 # changed to speed up the diff
968 # changed to speed up the diff
966 #
969 #
967 # in short mode, we only diff the files included in the
970 # in short mode, we only diff the files included in the
968 # patch already
971 # patch already
969 #
972 #
970 # this should really read:
973 # this should really read:
971 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
974 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
972 # but we do it backwards to take advantage of manifest/chlog
975 # but we do it backwards to take advantage of manifest/chlog
973 # caching against the next repo.status call
976 # caching against the next repo.status call
974 #
977 #
975 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
978 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
976 changes = repo.changelog.read(tip)
979 changes = repo.changelog.read(tip)
977 man = repo.manifest.read(changes[0])
980 man = repo.manifest.read(changes[0])
978 aaa = aa[:]
981 aaa = aa[:]
979 if opts.get('short'):
982 if opts.get('short'):
980 filelist = mm + aa + dd
983 filelist = mm + aa + dd
981 match = dict.fromkeys(filelist).__contains__
984 match = dict.fromkeys(filelist).__contains__
982 else:
985 else:
983 filelist = None
986 filelist = None
984 match = util.always
987 match = util.always
985 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
988 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
986
989
987 # we might end up with files that were added between
990 # we might end up with files that were added between
988 # tip and the dirstate parent, but then changed in the
991 # tip and the dirstate parent, but then changed in the
989 # local dirstate. in this case, we want them to only
992 # local dirstate. in this case, we want them to only
990 # show up in the added section
993 # show up in the added section
991 for x in m:
994 for x in m:
992 if x not in aa:
995 if x not in aa:
993 mm.append(x)
996 mm.append(x)
994 # we might end up with files added by the local dirstate that
997 # we might end up with files added by the local dirstate that
995 # were deleted by the patch. In this case, they should only
998 # were deleted by the patch. In this case, they should only
996 # show up in the changed section.
999 # show up in the changed section.
997 for x in a:
1000 for x in a:
998 if x in dd:
1001 if x in dd:
999 del dd[dd.index(x)]
1002 del dd[dd.index(x)]
1000 mm.append(x)
1003 mm.append(x)
1001 else:
1004 else:
1002 aa.append(x)
1005 aa.append(x)
1003 # make sure any files deleted in the local dirstate
1006 # make sure any files deleted in the local dirstate
1004 # are not in the add or change column of the patch
1007 # are not in the add or change column of the patch
1005 forget = []
1008 forget = []
1006 for x in d + r:
1009 for x in d + r:
1007 if x in aa:
1010 if x in aa:
1008 del aa[aa.index(x)]
1011 del aa[aa.index(x)]
1009 forget.append(x)
1012 forget.append(x)
1010 continue
1013 continue
1011 elif x in mm:
1014 elif x in mm:
1012 del mm[mm.index(x)]
1015 del mm[mm.index(x)]
1013 dd.append(x)
1016 dd.append(x)
1014
1017
1015 m = util.unique(mm)
1018 m = util.unique(mm)
1016 r = util.unique(dd)
1019 r = util.unique(dd)
1017 a = util.unique(aa)
1020 a = util.unique(aa)
1018 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1021 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1019 filelist = util.unique(c[0] + c[1] + c[2])
1022 filelist = util.unique(c[0] + c[1] + c[2])
1020 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1023 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1021 fp=patchf, changes=c, opts=self.diffopts())
1024 fp=patchf, changes=c, opts=self.diffopts())
1022 patchf.close()
1025 patchf.close()
1023
1026
1024 repo.dirstate.setparents(*cparents)
1027 repo.dirstate.setparents(*cparents)
1025 copies = {}
1028 copies = {}
1026 for dst in a:
1029 for dst in a:
1027 src = repo.dirstate.copied(dst)
1030 src = repo.dirstate.copied(dst)
1028 if src is not None:
1031 if src is not None:
1029 copies.setdefault(src, []).append(dst)
1032 copies.setdefault(src, []).append(dst)
1030 repo.dirstate.add(dst)
1033 repo.dirstate.add(dst)
1031 # remember the copies between patchparent and tip
1034 # remember the copies between patchparent and tip
1032 # this may be slow, so don't do it if we're not tracking copies
1035 # this may be slow, so don't do it if we're not tracking copies
1033 if self.diffopts().git:
1036 if self.diffopts().git:
1034 for dst in aaa:
1037 for dst in aaa:
1035 f = repo.file(dst)
1038 f = repo.file(dst)
1036 src = f.renamed(man[dst])
1039 src = f.renamed(man[dst])
1037 if src:
1040 if src:
1038 copies[src[0]] = copies.get(dst, [])
1041 copies[src[0]] = copies.get(dst, [])
1039 if dst in a:
1042 if dst in a:
1040 copies[src[0]].append(dst)
1043 copies[src[0]].append(dst)
1041 # we can't copy a file created by the patch itself
1044 # we can't copy a file created by the patch itself
1042 if dst in copies:
1045 if dst in copies:
1043 del copies[dst]
1046 del copies[dst]
1044 for src, dsts in copies.iteritems():
1047 for src, dsts in copies.iteritems():
1045 for dst in dsts:
1048 for dst in dsts:
1046 repo.dirstate.copy(src, dst)
1049 repo.dirstate.copy(src, dst)
1047 for f in r:
1050 for f in r:
1048 repo.dirstate.remove(f)
1051 repo.dirstate.remove(f)
1049 # if the patch excludes a modified file, mark that
1052 # if the patch excludes a modified file, mark that
1050 # file with mtime=0 so status can see it.
1053 # file with mtime=0 so status can see it.
1051 mm = []
1054 mm = []
1052 for i in xrange(len(m)-1, -1, -1):
1055 for i in xrange(len(m)-1, -1, -1):
1053 if not matchfn(m[i]):
1056 if not matchfn(m[i]):
1054 mm.append(m[i])
1057 mm.append(m[i])
1055 del m[i]
1058 del m[i]
1056 for f in m:
1059 for f in m:
1057 repo.dirstate.normal(f)
1060 repo.dirstate.normal(f)
1058 for f in mm:
1061 for f in mm:
1059 repo.dirstate.normallookup(f)
1062 repo.dirstate.normallookup(f)
1060 for f in forget:
1063 for f in forget:
1061 repo.dirstate.forget(f)
1064 repo.dirstate.forget(f)
1062
1065
1063 if not msg:
1066 if not msg:
1064 if not message:
1067 if not message:
1065 message = "[mq]: %s\n" % patchfn
1068 message = "[mq]: %s\n" % patchfn
1066 else:
1069 else:
1067 message = "\n".join(message)
1070 message = "\n".join(message)
1068 else:
1071 else:
1069 message = msg
1072 message = msg
1070
1073
1071 self.strip(repo, top, update=False,
1074 self.strip(repo, top, update=False,
1072 backup='strip')
1075 backup='strip')
1073 n = repo.commit(filelist, message, changes[1], match=matchfn,
1076 n = repo.commit(filelist, message, changes[1], match=matchfn,
1074 force=1)
1077 force=1)
1075 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1078 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1076 self.applied_dirty = 1
1079 self.applied_dirty = 1
1077 self.removeundo(repo)
1080 self.removeundo(repo)
1078 else:
1081 else:
1079 self.printdiff(repo, patchparent, fp=patchf)
1082 self.printdiff(repo, patchparent, fp=patchf)
1080 patchf.close()
1083 patchf.close()
1081 added = repo.status()[1]
1084 added = repo.status()[1]
1082 for a in added:
1085 for a in added:
1083 f = repo.wjoin(a)
1086 f = repo.wjoin(a)
1084 try:
1087 try:
1085 os.unlink(f)
1088 os.unlink(f)
1086 except OSError, e:
1089 except OSError, e:
1087 if e.errno != errno.ENOENT:
1090 if e.errno != errno.ENOENT:
1088 raise
1091 raise
1089 try: os.removedirs(os.path.dirname(f))
1092 try: os.removedirs(os.path.dirname(f))
1090 except: pass
1093 except: pass
1091 # forget the file copies in the dirstate
1094 # forget the file copies in the dirstate
1092 # push should readd the files later on
1095 # push should readd the files later on
1093 repo.dirstate.forget(a)
1096 repo.dirstate.forget(a)
1094 self.pop(repo, force=True)
1097 self.pop(repo, force=True)
1095 self.push(repo, force=True)
1098 self.push(repo, force=True)
1096 finally:
1099 finally:
1097 del wlock
1100 del wlock
1098
1101
1099 def init(self, repo, create=False):
1102 def init(self, repo, create=False):
1100 if not create and os.path.isdir(self.path):
1103 if not create and os.path.isdir(self.path):
1101 raise util.Abort(_("patch queue directory already exists"))
1104 raise util.Abort(_("patch queue directory already exists"))
1102 try:
1105 try:
1103 os.mkdir(self.path)
1106 os.mkdir(self.path)
1104 except OSError, inst:
1107 except OSError, inst:
1105 if inst.errno != errno.EEXIST or not create:
1108 if inst.errno != errno.EEXIST or not create:
1106 raise
1109 raise
1107 if create:
1110 if create:
1108 return self.qrepo(create=True)
1111 return self.qrepo(create=True)
1109
1112
1110 def unapplied(self, repo, patch=None):
1113 def unapplied(self, repo, patch=None):
1111 if patch and patch not in self.series:
1114 if patch and patch not in self.series:
1112 raise util.Abort(_("patch %s is not in series file") % patch)
1115 raise util.Abort(_("patch %s is not in series file") % patch)
1113 if not patch:
1116 if not patch:
1114 start = self.series_end()
1117 start = self.series_end()
1115 else:
1118 else:
1116 start = self.series.index(patch) + 1
1119 start = self.series.index(patch) + 1
1117 unapplied = []
1120 unapplied = []
1118 for i in xrange(start, len(self.series)):
1121 for i in xrange(start, len(self.series)):
1119 pushable, reason = self.pushable(i)
1122 pushable, reason = self.pushable(i)
1120 if pushable:
1123 if pushable:
1121 unapplied.append((i, self.series[i]))
1124 unapplied.append((i, self.series[i]))
1122 self.explain_pushable(i)
1125 self.explain_pushable(i)
1123 return unapplied
1126 return unapplied
1124
1127
1125 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1128 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1126 summary=False):
1129 summary=False):
1127 def displayname(patchname):
1130 def displayname(patchname):
1128 if summary:
1131 if summary:
1129 msg = self.readheaders(patchname)[0]
1132 msg = self.readheaders(patchname)[0]
1130 msg = msg and ': ' + msg[0] or ': '
1133 msg = msg and ': ' + msg[0] or ': '
1131 else:
1134 else:
1132 msg = ''
1135 msg = ''
1133 return '%s%s' % (patchname, msg)
1136 return '%s%s' % (patchname, msg)
1134
1137
1135 applied = dict.fromkeys([p.name for p in self.applied])
1138 applied = dict.fromkeys([p.name for p in self.applied])
1136 if length is None:
1139 if length is None:
1137 length = len(self.series) - start
1140 length = len(self.series) - start
1138 if not missing:
1141 if not missing:
1139 for i in xrange(start, start+length):
1142 for i in xrange(start, start+length):
1140 patch = self.series[i]
1143 patch = self.series[i]
1141 if patch in applied:
1144 if patch in applied:
1142 stat = 'A'
1145 stat = 'A'
1143 elif self.pushable(i)[0]:
1146 elif self.pushable(i)[0]:
1144 stat = 'U'
1147 stat = 'U'
1145 else:
1148 else:
1146 stat = 'G'
1149 stat = 'G'
1147 pfx = ''
1150 pfx = ''
1148 if self.ui.verbose:
1151 if self.ui.verbose:
1149 pfx = '%d %s ' % (i, stat)
1152 pfx = '%d %s ' % (i, stat)
1150 elif status and status != stat:
1153 elif status and status != stat:
1151 continue
1154 continue
1152 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1155 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1153 else:
1156 else:
1154 msng_list = []
1157 msng_list = []
1155 for root, dirs, files in os.walk(self.path):
1158 for root, dirs, files in os.walk(self.path):
1156 d = root[len(self.path) + 1:]
1159 d = root[len(self.path) + 1:]
1157 for f in files:
1160 for f in files:
1158 fl = os.path.join(d, f)
1161 fl = os.path.join(d, f)
1159 if (fl not in self.series and
1162 if (fl not in self.series and
1160 fl not in (self.status_path, self.series_path,
1163 fl not in (self.status_path, self.series_path,
1161 self.guards_path)
1164 self.guards_path)
1162 and not fl.startswith('.')):
1165 and not fl.startswith('.')):
1163 msng_list.append(fl)
1166 msng_list.append(fl)
1164 msng_list.sort()
1167 msng_list.sort()
1165 for x in msng_list:
1168 for x in msng_list:
1166 pfx = self.ui.verbose and ('D ') or ''
1169 pfx = self.ui.verbose and ('D ') or ''
1167 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1170 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1168
1171
1169 def issaveline(self, l):
1172 def issaveline(self, l):
1170 if l.name == '.hg.patches.save.line':
1173 if l.name == '.hg.patches.save.line':
1171 return True
1174 return True
1172
1175
1173 def qrepo(self, create=False):
1176 def qrepo(self, create=False):
1174 if create or os.path.isdir(self.join(".hg")):
1177 if create or os.path.isdir(self.join(".hg")):
1175 return hg.repository(self.ui, path=self.path, create=create)
1178 return hg.repository(self.ui, path=self.path, create=create)
1176
1179
1177 def restore(self, repo, rev, delete=None, qupdate=None):
1180 def restore(self, repo, rev, delete=None, qupdate=None):
1178 c = repo.changelog.read(rev)
1181 c = repo.changelog.read(rev)
1179 desc = c[4].strip()
1182 desc = c[4].strip()
1180 lines = desc.splitlines()
1183 lines = desc.splitlines()
1181 i = 0
1184 i = 0
1182 datastart = None
1185 datastart = None
1183 series = []
1186 series = []
1184 applied = []
1187 applied = []
1185 qpp = None
1188 qpp = None
1186 for i in xrange(0, len(lines)):
1189 for i in xrange(0, len(lines)):
1187 if lines[i] == 'Patch Data:':
1190 if lines[i] == 'Patch Data:':
1188 datastart = i + 1
1191 datastart = i + 1
1189 elif lines[i].startswith('Dirstate:'):
1192 elif lines[i].startswith('Dirstate:'):
1190 l = lines[i].rstrip()
1193 l = lines[i].rstrip()
1191 l = l[10:].split(' ')
1194 l = l[10:].split(' ')
1192 qpp = [ hg.bin(x) for x in l ]
1195 qpp = [ hg.bin(x) for x in l ]
1193 elif datastart != None:
1196 elif datastart != None:
1194 l = lines[i].rstrip()
1197 l = lines[i].rstrip()
1195 se = statusentry(l)
1198 se = statusentry(l)
1196 file_ = se.name
1199 file_ = se.name
1197 if se.rev:
1200 if se.rev:
1198 applied.append(se)
1201 applied.append(se)
1199 else:
1202 else:
1200 series.append(file_)
1203 series.append(file_)
1201 if datastart == None:
1204 if datastart == None:
1202 self.ui.warn("No saved patch data found\n")
1205 self.ui.warn("No saved patch data found\n")
1203 return 1
1206 return 1
1204 self.ui.warn("restoring status: %s\n" % lines[0])
1207 self.ui.warn("restoring status: %s\n" % lines[0])
1205 self.full_series = series
1208 self.full_series = series
1206 self.applied = applied
1209 self.applied = applied
1207 self.parse_series()
1210 self.parse_series()
1208 self.series_dirty = 1
1211 self.series_dirty = 1
1209 self.applied_dirty = 1
1212 self.applied_dirty = 1
1210 heads = repo.changelog.heads()
1213 heads = repo.changelog.heads()
1211 if delete:
1214 if delete:
1212 if rev not in heads:
1215 if rev not in heads:
1213 self.ui.warn("save entry has children, leaving it alone\n")
1216 self.ui.warn("save entry has children, leaving it alone\n")
1214 else:
1217 else:
1215 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1218 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1216 pp = repo.dirstate.parents()
1219 pp = repo.dirstate.parents()
1217 if rev in pp:
1220 if rev in pp:
1218 update = True
1221 update = True
1219 else:
1222 else:
1220 update = False
1223 update = False
1221 self.strip(repo, rev, update=update, backup='strip')
1224 self.strip(repo, rev, update=update, backup='strip')
1222 if qpp:
1225 if qpp:
1223 self.ui.warn("saved queue repository parents: %s %s\n" %
1226 self.ui.warn("saved queue repository parents: %s %s\n" %
1224 (hg.short(qpp[0]), hg.short(qpp[1])))
1227 (hg.short(qpp[0]), hg.short(qpp[1])))
1225 if qupdate:
1228 if qupdate:
1226 print "queue directory updating"
1229 print "queue directory updating"
1227 r = self.qrepo()
1230 r = self.qrepo()
1228 if not r:
1231 if not r:
1229 self.ui.warn("Unable to load queue repository\n")
1232 self.ui.warn("Unable to load queue repository\n")
1230 return 1
1233 return 1
1231 hg.clean(r, qpp[0])
1234 hg.clean(r, qpp[0])
1232
1235
1233 def save(self, repo, msg=None):
1236 def save(self, repo, msg=None):
1234 if len(self.applied) == 0:
1237 if len(self.applied) == 0:
1235 self.ui.warn("save: no patches applied, exiting\n")
1238 self.ui.warn("save: no patches applied, exiting\n")
1236 return 1
1239 return 1
1237 if self.issaveline(self.applied[-1]):
1240 if self.issaveline(self.applied[-1]):
1238 self.ui.warn("status is already saved\n")
1241 self.ui.warn("status is already saved\n")
1239 return 1
1242 return 1
1240
1243
1241 ar = [ ':' + x for x in self.full_series ]
1244 ar = [ ':' + x for x in self.full_series ]
1242 if not msg:
1245 if not msg:
1243 msg = "hg patches saved state"
1246 msg = "hg patches saved state"
1244 else:
1247 else:
1245 msg = "hg patches: " + msg.rstrip('\r\n')
1248 msg = "hg patches: " + msg.rstrip('\r\n')
1246 r = self.qrepo()
1249 r = self.qrepo()
1247 if r:
1250 if r:
1248 pp = r.dirstate.parents()
1251 pp = r.dirstate.parents()
1249 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1252 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1250 msg += "\n\nPatch Data:\n"
1253 msg += "\n\nPatch Data:\n"
1251 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1254 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1252 "\n".join(ar) + '\n' or "")
1255 "\n".join(ar) + '\n' or "")
1253 n = repo.commit(None, text, user=None, force=1)
1256 n = repo.commit(None, text, user=None, force=1)
1254 if not n:
1257 if not n:
1255 self.ui.warn("repo commit failed\n")
1258 self.ui.warn("repo commit failed\n")
1256 return 1
1259 return 1
1257 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1260 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1258 self.applied_dirty = 1
1261 self.applied_dirty = 1
1259 self.removeundo(repo)
1262 self.removeundo(repo)
1260
1263
1261 def full_series_end(self):
1264 def full_series_end(self):
1262 if len(self.applied) > 0:
1265 if len(self.applied) > 0:
1263 p = self.applied[-1].name
1266 p = self.applied[-1].name
1264 end = self.find_series(p)
1267 end = self.find_series(p)
1265 if end == None:
1268 if end == None:
1266 return len(self.full_series)
1269 return len(self.full_series)
1267 return end + 1
1270 return end + 1
1268 return 0
1271 return 0
1269
1272
1270 def series_end(self, all_patches=False):
1273 def series_end(self, all_patches=False):
1271 """If all_patches is False, return the index of the next pushable patch
1274 """If all_patches is False, return the index of the next pushable patch
1272 in the series, or the series length. If all_patches is True, return the
1275 in the series, or the series length. If all_patches is True, return the
1273 index of the first patch past the last applied one.
1276 index of the first patch past the last applied one.
1274 """
1277 """
1275 end = 0
1278 end = 0
1276 def next(start):
1279 def next(start):
1277 if all_patches:
1280 if all_patches:
1278 return start
1281 return start
1279 i = start
1282 i = start
1280 while i < len(self.series):
1283 while i < len(self.series):
1281 p, reason = self.pushable(i)
1284 p, reason = self.pushable(i)
1282 if p:
1285 if p:
1283 break
1286 break
1284 self.explain_pushable(i)
1287 self.explain_pushable(i)
1285 i += 1
1288 i += 1
1286 return i
1289 return i
1287 if len(self.applied) > 0:
1290 if len(self.applied) > 0:
1288 p = self.applied[-1].name
1291 p = self.applied[-1].name
1289 try:
1292 try:
1290 end = self.series.index(p)
1293 end = self.series.index(p)
1291 except ValueError:
1294 except ValueError:
1292 return 0
1295 return 0
1293 return next(end + 1)
1296 return next(end + 1)
1294 return next(end)
1297 return next(end)
1295
1298
1296 def appliedname(self, index):
1299 def appliedname(self, index):
1297 pname = self.applied[index].name
1300 pname = self.applied[index].name
1298 if not self.ui.verbose:
1301 if not self.ui.verbose:
1299 p = pname
1302 p = pname
1300 else:
1303 else:
1301 p = str(self.series.index(pname)) + " " + pname
1304 p = str(self.series.index(pname)) + " " + pname
1302 return p
1305 return p
1303
1306
1304 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1307 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1305 force=None, git=False):
1308 force=None, git=False):
1306 def checkseries(patchname):
1309 def checkseries(patchname):
1307 if patchname in self.series:
1310 if patchname in self.series:
1308 raise util.Abort(_('patch %s is already in the series file')
1311 raise util.Abort(_('patch %s is already in the series file')
1309 % patchname)
1312 % patchname)
1310 def checkfile(patchname):
1313 def checkfile(patchname):
1311 if not force and os.path.exists(self.join(patchname)):
1314 if not force and os.path.exists(self.join(patchname)):
1312 raise util.Abort(_('patch "%s" already exists')
1315 raise util.Abort(_('patch "%s" already exists')
1313 % patchname)
1316 % patchname)
1314
1317
1315 if rev:
1318 if rev:
1316 if files:
1319 if files:
1317 raise util.Abort(_('option "-r" not valid when importing '
1320 raise util.Abort(_('option "-r" not valid when importing '
1318 'files'))
1321 'files'))
1319 rev = cmdutil.revrange(repo, rev)
1322 rev = cmdutil.revrange(repo, rev)
1320 rev.sort(lambda x, y: cmp(y, x))
1323 rev.sort(lambda x, y: cmp(y, x))
1321 if (len(files) > 1 or len(rev) > 1) and patchname:
1324 if (len(files) > 1 or len(rev) > 1) and patchname:
1322 raise util.Abort(_('option "-n" not valid when importing multiple '
1325 raise util.Abort(_('option "-n" not valid when importing multiple '
1323 'patches'))
1326 'patches'))
1324 i = 0
1327 i = 0
1325 added = []
1328 added = []
1326 if rev:
1329 if rev:
1327 # If mq patches are applied, we can only import revisions
1330 # If mq patches are applied, we can only import revisions
1328 # that form a linear path to qbase.
1331 # that form a linear path to qbase.
1329 # Otherwise, they should form a linear path to a head.
1332 # Otherwise, they should form a linear path to a head.
1330 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1333 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1331 if len(heads) > 1:
1334 if len(heads) > 1:
1332 raise util.Abort(_('revision %d is the root of more than one '
1335 raise util.Abort(_('revision %d is the root of more than one '
1333 'branch') % rev[-1])
1336 'branch') % rev[-1])
1334 if self.applied:
1337 if self.applied:
1335 base = revlog.hex(repo.changelog.node(rev[0]))
1338 base = revlog.hex(repo.changelog.node(rev[0]))
1336 if base in [n.rev for n in self.applied]:
1339 if base in [n.rev for n in self.applied]:
1337 raise util.Abort(_('revision %d is already managed')
1340 raise util.Abort(_('revision %d is already managed')
1338 % rev[0])
1341 % rev[0])
1339 if heads != [revlog.bin(self.applied[-1].rev)]:
1342 if heads != [revlog.bin(self.applied[-1].rev)]:
1340 raise util.Abort(_('revision %d is not the parent of '
1343 raise util.Abort(_('revision %d is not the parent of '
1341 'the queue') % rev[0])
1344 'the queue') % rev[0])
1342 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1345 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1343 lastparent = repo.changelog.parentrevs(base)[0]
1346 lastparent = repo.changelog.parentrevs(base)[0]
1344 else:
1347 else:
1345 if heads != [repo.changelog.node(rev[0])]:
1348 if heads != [repo.changelog.node(rev[0])]:
1346 raise util.Abort(_('revision %d has unmanaged children')
1349 raise util.Abort(_('revision %d has unmanaged children')
1347 % rev[0])
1350 % rev[0])
1348 lastparent = None
1351 lastparent = None
1349
1352
1350 if git:
1353 if git:
1351 self.diffopts().git = True
1354 self.diffopts().git = True
1352
1355
1353 for r in rev:
1356 for r in rev:
1354 p1, p2 = repo.changelog.parentrevs(r)
1357 p1, p2 = repo.changelog.parentrevs(r)
1355 n = repo.changelog.node(r)
1358 n = repo.changelog.node(r)
1356 if p2 != revlog.nullrev:
1359 if p2 != revlog.nullrev:
1357 raise util.Abort(_('cannot import merge revision %d') % r)
1360 raise util.Abort(_('cannot import merge revision %d') % r)
1358 if lastparent and lastparent != r:
1361 if lastparent and lastparent != r:
1359 raise util.Abort(_('revision %d is not the parent of %d')
1362 raise util.Abort(_('revision %d is not the parent of %d')
1360 % (r, lastparent))
1363 % (r, lastparent))
1361 lastparent = p1
1364 lastparent = p1
1362
1365
1363 if not patchname:
1366 if not patchname:
1364 patchname = normname('%d.diff' % r)
1367 patchname = normname('%d.diff' % r)
1365 checkseries(patchname)
1368 checkseries(patchname)
1366 checkfile(patchname)
1369 checkfile(patchname)
1367 self.full_series.insert(0, patchname)
1370 self.full_series.insert(0, patchname)
1368
1371
1369 patchf = self.opener(patchname, "w")
1372 patchf = self.opener(patchname, "w")
1370 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1373 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1371 patchf.close()
1374 patchf.close()
1372
1375
1373 se = statusentry(revlog.hex(n), patchname)
1376 se = statusentry(revlog.hex(n), patchname)
1374 self.applied.insert(0, se)
1377 self.applied.insert(0, se)
1375
1378
1376 added.append(patchname)
1379 added.append(patchname)
1377 patchname = None
1380 patchname = None
1378 self.parse_series()
1381 self.parse_series()
1379 self.applied_dirty = 1
1382 self.applied_dirty = 1
1380
1383
1381 for filename in files:
1384 for filename in files:
1382 if existing:
1385 if existing:
1383 if filename == '-':
1386 if filename == '-':
1384 raise util.Abort(_('-e is incompatible with import from -'))
1387 raise util.Abort(_('-e is incompatible with import from -'))
1385 if not patchname:
1388 if not patchname:
1386 patchname = normname(filename)
1389 patchname = normname(filename)
1387 if not os.path.isfile(self.join(patchname)):
1390 if not os.path.isfile(self.join(patchname)):
1388 raise util.Abort(_("patch %s does not exist") % patchname)
1391 raise util.Abort(_("patch %s does not exist") % patchname)
1389 else:
1392 else:
1390 try:
1393 try:
1391 if filename == '-':
1394 if filename == '-':
1392 if not patchname:
1395 if not patchname:
1393 raise util.Abort(_('need --name to import a patch from -'))
1396 raise util.Abort(_('need --name to import a patch from -'))
1394 text = sys.stdin.read()
1397 text = sys.stdin.read()
1395 else:
1398 else:
1396 text = file(filename).read()
1399 text = file(filename).read()
1397 except IOError:
1400 except IOError:
1398 raise util.Abort(_("unable to read %s") % patchname)
1401 raise util.Abort(_("unable to read %s") % patchname)
1399 if not patchname:
1402 if not patchname:
1400 patchname = normname(os.path.basename(filename))
1403 patchname = normname(os.path.basename(filename))
1401 checkfile(patchname)
1404 checkfile(patchname)
1402 patchf = self.opener(patchname, "w")
1405 patchf = self.opener(patchname, "w")
1403 patchf.write(text)
1406 patchf.write(text)
1404 checkseries(patchname)
1407 checkseries(patchname)
1405 index = self.full_series_end() + i
1408 index = self.full_series_end() + i
1406 self.full_series[index:index] = [patchname]
1409 self.full_series[index:index] = [patchname]
1407 self.parse_series()
1410 self.parse_series()
1408 self.ui.warn("adding %s to series file\n" % patchname)
1411 self.ui.warn("adding %s to series file\n" % patchname)
1409 i += 1
1412 i += 1
1410 added.append(patchname)
1413 added.append(patchname)
1411 patchname = None
1414 patchname = None
1412 self.series_dirty = 1
1415 self.series_dirty = 1
1413 qrepo = self.qrepo()
1416 qrepo = self.qrepo()
1414 if qrepo:
1417 if qrepo:
1415 qrepo.add(added)
1418 qrepo.add(added)
1416
1419
1417 def delete(ui, repo, *patches, **opts):
1420 def delete(ui, repo, *patches, **opts):
1418 """remove patches from queue
1421 """remove patches from queue
1419
1422
1420 The patches must not be applied, unless they are arguments to
1423 The patches must not be applied, unless they are arguments to
1421 the --rev parameter. At least one patch or revision is required.
1424 the --rev parameter. At least one patch or revision is required.
1422
1425
1423 With --rev, mq will stop managing the named revisions (converting
1426 With --rev, mq will stop managing the named revisions (converting
1424 them to regular mercurial changesets). The patches must be applied
1427 them to regular mercurial changesets). The patches must be applied
1425 and at the base of the stack. This option is useful when the patches
1428 and at the base of the stack. This option is useful when the patches
1426 have been applied upstream.
1429 have been applied upstream.
1427
1430
1428 With --keep, the patch files are preserved in the patch directory."""
1431 With --keep, the patch files are preserved in the patch directory."""
1429 q = repo.mq
1432 q = repo.mq
1430 q.delete(repo, patches, opts)
1433 q.delete(repo, patches, opts)
1431 q.save_dirty()
1434 q.save_dirty()
1432 return 0
1435 return 0
1433
1436
1434 def applied(ui, repo, patch=None, **opts):
1437 def applied(ui, repo, patch=None, **opts):
1435 """print the patches already applied"""
1438 """print the patches already applied"""
1436 q = repo.mq
1439 q = repo.mq
1437 if patch:
1440 if patch:
1438 if patch not in q.series:
1441 if patch not in q.series:
1439 raise util.Abort(_("patch %s is not in series file") % patch)
1442 raise util.Abort(_("patch %s is not in series file") % patch)
1440 end = q.series.index(patch) + 1
1443 end = q.series.index(patch) + 1
1441 else:
1444 else:
1442 end = q.series_end(True)
1445 end = q.series_end(True)
1443 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1446 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1444
1447
1445 def unapplied(ui, repo, patch=None, **opts):
1448 def unapplied(ui, repo, patch=None, **opts):
1446 """print the patches not yet applied"""
1449 """print the patches not yet applied"""
1447 q = repo.mq
1450 q = repo.mq
1448 if patch:
1451 if patch:
1449 if patch not in q.series:
1452 if patch not in q.series:
1450 raise util.Abort(_("patch %s is not in series file") % patch)
1453 raise util.Abort(_("patch %s is not in series file") % patch)
1451 start = q.series.index(patch) + 1
1454 start = q.series.index(patch) + 1
1452 else:
1455 else:
1453 start = q.series_end(True)
1456 start = q.series_end(True)
1454 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1457 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1455
1458
1456 def qimport(ui, repo, *filename, **opts):
1459 def qimport(ui, repo, *filename, **opts):
1457 """import a patch
1460 """import a patch
1458
1461
1459 The patch will have the same name as its source file unless you
1462 The patch will have the same name as its source file unless you
1460 give it a new one with --name.
1463 give it a new one with --name.
1461
1464
1462 You can register an existing patch inside the patch directory
1465 You can register an existing patch inside the patch directory
1463 with the --existing flag.
1466 with the --existing flag.
1464
1467
1465 With --force, an existing patch of the same name will be overwritten.
1468 With --force, an existing patch of the same name will be overwritten.
1466
1469
1467 An existing changeset may be placed under mq control with --rev
1470 An existing changeset may be placed under mq control with --rev
1468 (e.g. qimport --rev tip -n patch will place tip under mq control).
1471 (e.g. qimport --rev tip -n patch will place tip under mq control).
1469 With --git, patches imported with --rev will use the git diff
1472 With --git, patches imported with --rev will use the git diff
1470 format.
1473 format.
1471 """
1474 """
1472 q = repo.mq
1475 q = repo.mq
1473 q.qimport(repo, filename, patchname=opts['name'],
1476 q.qimport(repo, filename, patchname=opts['name'],
1474 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1477 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1475 git=opts['git'])
1478 git=opts['git'])
1476 q.save_dirty()
1479 q.save_dirty()
1477 return 0
1480 return 0
1478
1481
1479 def init(ui, repo, **opts):
1482 def init(ui, repo, **opts):
1480 """init a new queue repository
1483 """init a new queue repository
1481
1484
1482 The queue repository is unversioned by default. If -c is
1485 The queue repository is unversioned by default. If -c is
1483 specified, qinit will create a separate nested repository
1486 specified, qinit will create a separate nested repository
1484 for patches (qinit -c may also be run later to convert
1487 for patches (qinit -c may also be run later to convert
1485 an unversioned patch repository into a versioned one).
1488 an unversioned patch repository into a versioned one).
1486 You can use qcommit to commit changes to this queue repository."""
1489 You can use qcommit to commit changes to this queue repository."""
1487 q = repo.mq
1490 q = repo.mq
1488 r = q.init(repo, create=opts['create_repo'])
1491 r = q.init(repo, create=opts['create_repo'])
1489 q.save_dirty()
1492 q.save_dirty()
1490 if r:
1493 if r:
1491 if not os.path.exists(r.wjoin('.hgignore')):
1494 if not os.path.exists(r.wjoin('.hgignore')):
1492 fp = r.wopener('.hgignore', 'w')
1495 fp = r.wopener('.hgignore', 'w')
1493 fp.write('syntax: glob\n')
1496 fp.write('syntax: glob\n')
1494 fp.write('status\n')
1497 fp.write('status\n')
1495 fp.write('guards\n')
1498 fp.write('guards\n')
1496 fp.close()
1499 fp.close()
1497 if not os.path.exists(r.wjoin('series')):
1500 if not os.path.exists(r.wjoin('series')):
1498 r.wopener('series', 'w').close()
1501 r.wopener('series', 'w').close()
1499 r.add(['.hgignore', 'series'])
1502 r.add(['.hgignore', 'series'])
1500 commands.add(ui, r)
1503 commands.add(ui, r)
1501 return 0
1504 return 0
1502
1505
1503 def clone(ui, source, dest=None, **opts):
1506 def clone(ui, source, dest=None, **opts):
1504 '''clone main and patch repository at same time
1507 '''clone main and patch repository at same time
1505
1508
1506 If source is local, destination will have no patches applied. If
1509 If source is local, destination will have no patches applied. If
1507 source is remote, this command can not check if patches are
1510 source is remote, this command can not check if patches are
1508 applied in source, so cannot guarantee that patches are not
1511 applied in source, so cannot guarantee that patches are not
1509 applied in destination. If you clone remote repository, be sure
1512 applied in destination. If you clone remote repository, be sure
1510 before that it has no patches applied.
1513 before that it has no patches applied.
1511
1514
1512 Source patch repository is looked for in <src>/.hg/patches by
1515 Source patch repository is looked for in <src>/.hg/patches by
1513 default. Use -p <url> to change.
1516 default. Use -p <url> to change.
1514
1517
1515 The patch directory must be a nested mercurial repository, as
1518 The patch directory must be a nested mercurial repository, as
1516 would be created by qinit -c.
1519 would be created by qinit -c.
1517 '''
1520 '''
1518 def patchdir(repo):
1521 def patchdir(repo):
1519 url = repo.url()
1522 url = repo.url()
1520 if url.endswith('/'):
1523 if url.endswith('/'):
1521 url = url[:-1]
1524 url = url[:-1]
1522 return url + '/.hg/patches'
1525 return url + '/.hg/patches'
1523 cmdutil.setremoteconfig(ui, opts)
1526 cmdutil.setremoteconfig(ui, opts)
1524 if dest is None:
1527 if dest is None:
1525 dest = hg.defaultdest(source)
1528 dest = hg.defaultdest(source)
1526 sr = hg.repository(ui, ui.expandpath(source))
1529 sr = hg.repository(ui, ui.expandpath(source))
1527 patchespath = opts['patches'] or patchdir(sr)
1530 patchespath = opts['patches'] or patchdir(sr)
1528 try:
1531 try:
1529 pr = hg.repository(ui, patchespath)
1532 pr = hg.repository(ui, patchespath)
1530 except hg.RepoError:
1533 except hg.RepoError:
1531 raise util.Abort(_('versioned patch repository not found'
1534 raise util.Abort(_('versioned patch repository not found'
1532 ' (see qinit -c)'))
1535 ' (see qinit -c)'))
1533 qbase, destrev = None, None
1536 qbase, destrev = None, None
1534 if sr.local():
1537 if sr.local():
1535 if sr.mq.applied:
1538 if sr.mq.applied:
1536 qbase = revlog.bin(sr.mq.applied[0].rev)
1539 qbase = revlog.bin(sr.mq.applied[0].rev)
1537 if not hg.islocal(dest):
1540 if not hg.islocal(dest):
1538 heads = dict.fromkeys(sr.heads())
1541 heads = dict.fromkeys(sr.heads())
1539 for h in sr.heads(qbase):
1542 for h in sr.heads(qbase):
1540 del heads[h]
1543 del heads[h]
1541 destrev = heads.keys()
1544 destrev = heads.keys()
1542 destrev.append(sr.changelog.parents(qbase)[0])
1545 destrev.append(sr.changelog.parents(qbase)[0])
1543 ui.note(_('cloning main repo\n'))
1546 ui.note(_('cloning main repo\n'))
1544 sr, dr = hg.clone(ui, sr.url(), dest,
1547 sr, dr = hg.clone(ui, sr.url(), dest,
1545 pull=opts['pull'],
1548 pull=opts['pull'],
1546 rev=destrev,
1549 rev=destrev,
1547 update=False,
1550 update=False,
1548 stream=opts['uncompressed'])
1551 stream=opts['uncompressed'])
1549 ui.note(_('cloning patch repo\n'))
1552 ui.note(_('cloning patch repo\n'))
1550 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1553 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1551 pull=opts['pull'], update=not opts['noupdate'],
1554 pull=opts['pull'], update=not opts['noupdate'],
1552 stream=opts['uncompressed'])
1555 stream=opts['uncompressed'])
1553 if dr.local():
1556 if dr.local():
1554 if qbase:
1557 if qbase:
1555 ui.note(_('stripping applied patches from destination repo\n'))
1558 ui.note(_('stripping applied patches from destination repo\n'))
1556 dr.mq.strip(dr, qbase, update=False, backup=None)
1559 dr.mq.strip(dr, qbase, update=False, backup=None)
1557 if not opts['noupdate']:
1560 if not opts['noupdate']:
1558 ui.note(_('updating destination repo\n'))
1561 ui.note(_('updating destination repo\n'))
1559 hg.update(dr, dr.changelog.tip())
1562 hg.update(dr, dr.changelog.tip())
1560
1563
1561 def commit(ui, repo, *pats, **opts):
1564 def commit(ui, repo, *pats, **opts):
1562 """commit changes in the queue repository"""
1565 """commit changes in the queue repository"""
1563 q = repo.mq
1566 q = repo.mq
1564 r = q.qrepo()
1567 r = q.qrepo()
1565 if not r: raise util.Abort('no queue repository')
1568 if not r: raise util.Abort('no queue repository')
1566 commands.commit(r.ui, r, *pats, **opts)
1569 commands.commit(r.ui, r, *pats, **opts)
1567
1570
1568 def series(ui, repo, **opts):
1571 def series(ui, repo, **opts):
1569 """print the entire series file"""
1572 """print the entire series file"""
1570 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1573 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1571 return 0
1574 return 0
1572
1575
1573 def top(ui, repo, **opts):
1576 def top(ui, repo, **opts):
1574 """print the name of the current patch"""
1577 """print the name of the current patch"""
1575 q = repo.mq
1578 q = repo.mq
1576 t = q.applied and q.series_end(True) or 0
1579 t = q.applied and q.series_end(True) or 0
1577 if t:
1580 if t:
1578 return q.qseries(repo, start=t-1, length=1, status='A',
1581 return q.qseries(repo, start=t-1, length=1, status='A',
1579 summary=opts.get('summary'))
1582 summary=opts.get('summary'))
1580 else:
1583 else:
1581 ui.write("No patches applied\n")
1584 ui.write("No patches applied\n")
1582 return 1
1585 return 1
1583
1586
1584 def next(ui, repo, **opts):
1587 def next(ui, repo, **opts):
1585 """print the name of the next patch"""
1588 """print the name of the next patch"""
1586 q = repo.mq
1589 q = repo.mq
1587 end = q.series_end()
1590 end = q.series_end()
1588 if end == len(q.series):
1591 if end == len(q.series):
1589 ui.write("All patches applied\n")
1592 ui.write("All patches applied\n")
1590 return 1
1593 return 1
1591 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1594 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1592
1595
1593 def prev(ui, repo, **opts):
1596 def prev(ui, repo, **opts):
1594 """print the name of the previous patch"""
1597 """print the name of the previous patch"""
1595 q = repo.mq
1598 q = repo.mq
1596 l = len(q.applied)
1599 l = len(q.applied)
1597 if l == 1:
1600 if l == 1:
1598 ui.write("Only one patch applied\n")
1601 ui.write("Only one patch applied\n")
1599 return 1
1602 return 1
1600 if not l:
1603 if not l:
1601 ui.write("No patches applied\n")
1604 ui.write("No patches applied\n")
1602 return 1
1605 return 1
1603 return q.qseries(repo, start=l-2, length=1, status='A',
1606 return q.qseries(repo, start=l-2, length=1, status='A',
1604 summary=opts.get('summary'))
1607 summary=opts.get('summary'))
1605
1608
1606 def new(ui, repo, patch, *args, **opts):
1609 def new(ui, repo, patch, *args, **opts):
1607 """create a new patch
1610 """create a new patch
1608
1611
1609 qnew creates a new patch on top of the currently-applied patch
1612 qnew creates a new patch on top of the currently-applied patch
1610 (if any). It will refuse to run if there are any outstanding
1613 (if any). It will refuse to run if there are any outstanding
1611 changes unless -f is specified, in which case the patch will
1614 changes unless -f is specified, in which case the patch will
1612 be initialised with them. You may also use -I, -X, and/or a list of
1615 be initialised with them. You may also use -I, -X, and/or a list of
1613 files after the patch name to add only changes to matching files
1616 files after the patch name to add only changes to matching files
1614 to the new patch, leaving the rest as uncommitted modifications.
1617 to the new patch, leaving the rest as uncommitted modifications.
1615
1618
1616 -e, -m or -l set the patch header as well as the commit message.
1619 -e, -m or -l set the patch header as well as the commit message.
1617 If none is specified, the patch header is empty and the
1620 If none is specified, the patch header is empty and the
1618 commit message is '[mq]: PATCH'"""
1621 commit message is '[mq]: PATCH'"""
1619 q = repo.mq
1622 q = repo.mq
1620 message = cmdutil.logmessage(opts)
1623 message = cmdutil.logmessage(opts)
1621 if opts['edit']:
1624 if opts['edit']:
1622 message = ui.edit(message, ui.username())
1625 message = ui.edit(message, ui.username())
1623 opts['msg'] = message
1626 opts['msg'] = message
1624 q.new(repo, patch, *args, **opts)
1627 q.new(repo, patch, *args, **opts)
1625 q.save_dirty()
1628 q.save_dirty()
1626 return 0
1629 return 0
1627
1630
1628 def refresh(ui, repo, *pats, **opts):
1631 def refresh(ui, repo, *pats, **opts):
1629 """update the current patch
1632 """update the current patch
1630
1633
1631 If any file patterns are provided, the refreshed patch will contain only
1634 If any file patterns are provided, the refreshed patch will contain only
1632 the modifications that match those patterns; the remaining modifications
1635 the modifications that match those patterns; the remaining modifications
1633 will remain in the working directory.
1636 will remain in the working directory.
1634
1637
1635 hg add/remove/copy/rename work as usual, though you might want to use
1638 hg add/remove/copy/rename work as usual, though you might want to use
1636 git-style patches (--git or [diff] git=1) to track copies and renames.
1639 git-style patches (--git or [diff] git=1) to track copies and renames.
1637 """
1640 """
1638 q = repo.mq
1641 q = repo.mq
1639 message = cmdutil.logmessage(opts)
1642 message = cmdutil.logmessage(opts)
1640 if opts['edit']:
1643 if opts['edit']:
1641 if not q.applied:
1644 if not q.applied:
1642 ui.write(_("No patches applied\n"))
1645 ui.write(_("No patches applied\n"))
1643 return 1
1646 return 1
1644 if message:
1647 if message:
1645 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1648 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1646 patch = q.applied[-1].name
1649 patch = q.applied[-1].name
1647 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1650 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1648 message = ui.edit('\n'.join(message), user or ui.username())
1651 message = ui.edit('\n'.join(message), user or ui.username())
1649 ret = q.refresh(repo, pats, msg=message, **opts)
1652 ret = q.refresh(repo, pats, msg=message, **opts)
1650 q.save_dirty()
1653 q.save_dirty()
1651 return ret
1654 return ret
1652
1655
1653 def diff(ui, repo, *pats, **opts):
1656 def diff(ui, repo, *pats, **opts):
1654 """diff of the current patch"""
1657 """diff of the current patch"""
1655 repo.mq.diff(repo, pats, opts)
1658 repo.mq.diff(repo, pats, opts)
1656 return 0
1659 return 0
1657
1660
1658 def fold(ui, repo, *files, **opts):
1661 def fold(ui, repo, *files, **opts):
1659 """fold the named patches into the current patch
1662 """fold the named patches into the current patch
1660
1663
1661 Patches must not yet be applied. Each patch will be successively
1664 Patches must not yet be applied. Each patch will be successively
1662 applied to the current patch in the order given. If all the
1665 applied to the current patch in the order given. If all the
1663 patches apply successfully, the current patch will be refreshed
1666 patches apply successfully, the current patch will be refreshed
1664 with the new cumulative patch, and the folded patches will
1667 with the new cumulative patch, and the folded patches will
1665 be deleted. With -k/--keep, the folded patch files will not
1668 be deleted. With -k/--keep, the folded patch files will not
1666 be removed afterwards.
1669 be removed afterwards.
1667
1670
1668 The header for each folded patch will be concatenated with
1671 The header for each folded patch will be concatenated with
1669 the current patch header, separated by a line of '* * *'."""
1672 the current patch header, separated by a line of '* * *'."""
1670
1673
1671 q = repo.mq
1674 q = repo.mq
1672
1675
1673 if not files:
1676 if not files:
1674 raise util.Abort(_('qfold requires at least one patch name'))
1677 raise util.Abort(_('qfold requires at least one patch name'))
1675 if not q.check_toppatch(repo):
1678 if not q.check_toppatch(repo):
1676 raise util.Abort(_('No patches applied'))
1679 raise util.Abort(_('No patches applied'))
1677
1680
1678 message = cmdutil.logmessage(opts)
1681 message = cmdutil.logmessage(opts)
1679 if opts['edit']:
1682 if opts['edit']:
1680 if message:
1683 if message:
1681 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1684 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1682
1685
1683 parent = q.lookup('qtip')
1686 parent = q.lookup('qtip')
1684 patches = []
1687 patches = []
1685 messages = []
1688 messages = []
1686 for f in files:
1689 for f in files:
1687 p = q.lookup(f)
1690 p = q.lookup(f)
1688 if p in patches or p == parent:
1691 if p in patches or p == parent:
1689 ui.warn(_('Skipping already folded patch %s') % p)
1692 ui.warn(_('Skipping already folded patch %s') % p)
1690 if q.isapplied(p):
1693 if q.isapplied(p):
1691 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1694 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1692 patches.append(p)
1695 patches.append(p)
1693
1696
1694 for p in patches:
1697 for p in patches:
1695 if not message:
1698 if not message:
1696 messages.append(q.readheaders(p)[0])
1699 messages.append(q.readheaders(p)[0])
1697 pf = q.join(p)
1700 pf = q.join(p)
1698 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1701 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1699 if not patchsuccess:
1702 if not patchsuccess:
1700 raise util.Abort(_('Error folding patch %s') % p)
1703 raise util.Abort(_('Error folding patch %s') % p)
1701 patch.updatedir(ui, repo, files)
1704 patch.updatedir(ui, repo, files)
1702
1705
1703 if not message:
1706 if not message:
1704 message, comments, user = q.readheaders(parent)[0:3]
1707 message, comments, user = q.readheaders(parent)[0:3]
1705 for msg in messages:
1708 for msg in messages:
1706 message.append('* * *')
1709 message.append('* * *')
1707 message.extend(msg)
1710 message.extend(msg)
1708 message = '\n'.join(message)
1711 message = '\n'.join(message)
1709
1712
1710 if opts['edit']:
1713 if opts['edit']:
1711 message = ui.edit(message, user or ui.username())
1714 message = ui.edit(message, user or ui.username())
1712
1715
1713 q.refresh(repo, msg=message)
1716 q.refresh(repo, msg=message)
1714 q.delete(repo, patches, opts)
1717 q.delete(repo, patches, opts)
1715 q.save_dirty()
1718 q.save_dirty()
1716
1719
1717 def goto(ui, repo, patch, **opts):
1720 def goto(ui, repo, patch, **opts):
1718 '''push or pop patches until named patch is at top of stack'''
1721 '''push or pop patches until named patch is at top of stack'''
1719 q = repo.mq
1722 q = repo.mq
1720 patch = q.lookup(patch)
1723 patch = q.lookup(patch)
1721 if q.isapplied(patch):
1724 if q.isapplied(patch):
1722 ret = q.pop(repo, patch, force=opts['force'])
1725 ret = q.pop(repo, patch, force=opts['force'])
1723 else:
1726 else:
1724 ret = q.push(repo, patch, force=opts['force'])
1727 ret = q.push(repo, patch, force=opts['force'])
1725 q.save_dirty()
1728 q.save_dirty()
1726 return ret
1729 return ret
1727
1730
1728 def guard(ui, repo, *args, **opts):
1731 def guard(ui, repo, *args, **opts):
1729 '''set or print guards for a patch
1732 '''set or print guards for a patch
1730
1733
1731 Guards control whether a patch can be pushed. A patch with no
1734 Guards control whether a patch can be pushed. A patch with no
1732 guards is always pushed. A patch with a positive guard ("+foo") is
1735 guards is always pushed. A patch with a positive guard ("+foo") is
1733 pushed only if the qselect command has activated it. A patch with
1736 pushed only if the qselect command has activated it. A patch with
1734 a negative guard ("-foo") is never pushed if the qselect command
1737 a negative guard ("-foo") is never pushed if the qselect command
1735 has activated it.
1738 has activated it.
1736
1739
1737 With no arguments, print the currently active guards.
1740 With no arguments, print the currently active guards.
1738 With arguments, set guards for the named patch.
1741 With arguments, set guards for the named patch.
1739
1742
1740 To set a negative guard "-foo" on topmost patch ("--" is needed so
1743 To set a negative guard "-foo" on topmost patch ("--" is needed so
1741 hg will not interpret "-foo" as an option):
1744 hg will not interpret "-foo" as an option):
1742 hg qguard -- -foo
1745 hg qguard -- -foo
1743
1746
1744 To set guards on another patch:
1747 To set guards on another patch:
1745 hg qguard other.patch +2.6.17 -stable
1748 hg qguard other.patch +2.6.17 -stable
1746 '''
1749 '''
1747 def status(idx):
1750 def status(idx):
1748 guards = q.series_guards[idx] or ['unguarded']
1751 guards = q.series_guards[idx] or ['unguarded']
1749 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1752 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1750 q = repo.mq
1753 q = repo.mq
1751 patch = None
1754 patch = None
1752 args = list(args)
1755 args = list(args)
1753 if opts['list']:
1756 if opts['list']:
1754 if args or opts['none']:
1757 if args or opts['none']:
1755 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1758 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1756 for i in xrange(len(q.series)):
1759 for i in xrange(len(q.series)):
1757 status(i)
1760 status(i)
1758 return
1761 return
1759 if not args or args[0][0:1] in '-+':
1762 if not args or args[0][0:1] in '-+':
1760 if not q.applied:
1763 if not q.applied:
1761 raise util.Abort(_('no patches applied'))
1764 raise util.Abort(_('no patches applied'))
1762 patch = q.applied[-1].name
1765 patch = q.applied[-1].name
1763 if patch is None and args[0][0:1] not in '-+':
1766 if patch is None and args[0][0:1] not in '-+':
1764 patch = args.pop(0)
1767 patch = args.pop(0)
1765 if patch is None:
1768 if patch is None:
1766 raise util.Abort(_('no patch to work with'))
1769 raise util.Abort(_('no patch to work with'))
1767 if args or opts['none']:
1770 if args or opts['none']:
1768 idx = q.find_series(patch)
1771 idx = q.find_series(patch)
1769 if idx is None:
1772 if idx is None:
1770 raise util.Abort(_('no patch named %s') % patch)
1773 raise util.Abort(_('no patch named %s') % patch)
1771 q.set_guards(idx, args)
1774 q.set_guards(idx, args)
1772 q.save_dirty()
1775 q.save_dirty()
1773 else:
1776 else:
1774 status(q.series.index(q.lookup(patch)))
1777 status(q.series.index(q.lookup(patch)))
1775
1778
1776 def header(ui, repo, patch=None):
1779 def header(ui, repo, patch=None):
1777 """Print the header of the topmost or specified patch"""
1780 """Print the header of the topmost or specified patch"""
1778 q = repo.mq
1781 q = repo.mq
1779
1782
1780 if patch:
1783 if patch:
1781 patch = q.lookup(patch)
1784 patch = q.lookup(patch)
1782 else:
1785 else:
1783 if not q.applied:
1786 if not q.applied:
1784 ui.write('No patches applied\n')
1787 ui.write('No patches applied\n')
1785 return 1
1788 return 1
1786 patch = q.lookup('qtip')
1789 patch = q.lookup('qtip')
1787 message = repo.mq.readheaders(patch)[0]
1790 message = repo.mq.readheaders(patch)[0]
1788
1791
1789 ui.write('\n'.join(message) + '\n')
1792 ui.write('\n'.join(message) + '\n')
1790
1793
1791 def lastsavename(path):
1794 def lastsavename(path):
1792 (directory, base) = os.path.split(path)
1795 (directory, base) = os.path.split(path)
1793 names = os.listdir(directory)
1796 names = os.listdir(directory)
1794 namere = re.compile("%s.([0-9]+)" % base)
1797 namere = re.compile("%s.([0-9]+)" % base)
1795 maxindex = None
1798 maxindex = None
1796 maxname = None
1799 maxname = None
1797 for f in names:
1800 for f in names:
1798 m = namere.match(f)
1801 m = namere.match(f)
1799 if m:
1802 if m:
1800 index = int(m.group(1))
1803 index = int(m.group(1))
1801 if maxindex == None or index > maxindex:
1804 if maxindex == None or index > maxindex:
1802 maxindex = index
1805 maxindex = index
1803 maxname = f
1806 maxname = f
1804 if maxname:
1807 if maxname:
1805 return (os.path.join(directory, maxname), maxindex)
1808 return (os.path.join(directory, maxname), maxindex)
1806 return (None, None)
1809 return (None, None)
1807
1810
1808 def savename(path):
1811 def savename(path):
1809 (last, index) = lastsavename(path)
1812 (last, index) = lastsavename(path)
1810 if last is None:
1813 if last is None:
1811 index = 0
1814 index = 0
1812 newpath = path + ".%d" % (index + 1)
1815 newpath = path + ".%d" % (index + 1)
1813 return newpath
1816 return newpath
1814
1817
1815 def push(ui, repo, patch=None, **opts):
1818 def push(ui, repo, patch=None, **opts):
1816 """push the next patch onto the stack"""
1819 """push the next patch onto the stack"""
1817 q = repo.mq
1820 q = repo.mq
1818 mergeq = None
1821 mergeq = None
1819
1822
1820 if opts['all']:
1823 if opts['all']:
1821 if not q.series:
1824 if not q.series:
1822 ui.warn(_('no patches in series\n'))
1825 ui.warn(_('no patches in series\n'))
1823 return 0
1826 return 0
1824 patch = q.series[-1]
1827 patch = q.series[-1]
1825 if opts['merge']:
1828 if opts['merge']:
1826 if opts['name']:
1829 if opts['name']:
1827 newpath = opts['name']
1830 newpath = opts['name']
1828 else:
1831 else:
1829 newpath, i = lastsavename(q.path)
1832 newpath, i = lastsavename(q.path)
1830 if not newpath:
1833 if not newpath:
1831 ui.warn("no saved queues found, please use -n\n")
1834 ui.warn("no saved queues found, please use -n\n")
1832 return 1
1835 return 1
1833 mergeq = queue(ui, repo.join(""), newpath)
1836 mergeq = queue(ui, repo.join(""), newpath)
1834 ui.warn("merging with queue at: %s\n" % mergeq.path)
1837 ui.warn("merging with queue at: %s\n" % mergeq.path)
1835 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1838 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1836 mergeq=mergeq)
1839 mergeq=mergeq)
1837 return ret
1840 return ret
1838
1841
1839 def pop(ui, repo, patch=None, **opts):
1842 def pop(ui, repo, patch=None, **opts):
1840 """pop the current patch off the stack"""
1843 """pop the current patch off the stack"""
1841 localupdate = True
1844 localupdate = True
1842 if opts['name']:
1845 if opts['name']:
1843 q = queue(ui, repo.join(""), repo.join(opts['name']))
1846 q = queue(ui, repo.join(""), repo.join(opts['name']))
1844 ui.warn('using patch queue: %s\n' % q.path)
1847 ui.warn('using patch queue: %s\n' % q.path)
1845 localupdate = False
1848 localupdate = False
1846 else:
1849 else:
1847 q = repo.mq
1850 q = repo.mq
1848 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1851 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1849 all=opts['all'])
1852 all=opts['all'])
1850 q.save_dirty()
1853 q.save_dirty()
1851 return ret
1854 return ret
1852
1855
1853 def rename(ui, repo, patch, name=None, **opts):
1856 def rename(ui, repo, patch, name=None, **opts):
1854 """rename a patch
1857 """rename a patch
1855
1858
1856 With one argument, renames the current patch to PATCH1.
1859 With one argument, renames the current patch to PATCH1.
1857 With two arguments, renames PATCH1 to PATCH2."""
1860 With two arguments, renames PATCH1 to PATCH2."""
1858
1861
1859 q = repo.mq
1862 q = repo.mq
1860
1863
1861 if not name:
1864 if not name:
1862 name = patch
1865 name = patch
1863 patch = None
1866 patch = None
1864
1867
1865 if patch:
1868 if patch:
1866 patch = q.lookup(patch)
1869 patch = q.lookup(patch)
1867 else:
1870 else:
1868 if not q.applied:
1871 if not q.applied:
1869 ui.write(_('No patches applied\n'))
1872 ui.write(_('No patches applied\n'))
1870 return
1873 return
1871 patch = q.lookup('qtip')
1874 patch = q.lookup('qtip')
1872 absdest = q.join(name)
1875 absdest = q.join(name)
1873 if os.path.isdir(absdest):
1876 if os.path.isdir(absdest):
1874 name = normname(os.path.join(name, os.path.basename(patch)))
1877 name = normname(os.path.join(name, os.path.basename(patch)))
1875 absdest = q.join(name)
1878 absdest = q.join(name)
1876 if os.path.exists(absdest):
1879 if os.path.exists(absdest):
1877 raise util.Abort(_('%s already exists') % absdest)
1880 raise util.Abort(_('%s already exists') % absdest)
1878
1881
1879 if name in q.series:
1882 if name in q.series:
1880 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1883 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1881
1884
1882 if ui.verbose:
1885 if ui.verbose:
1883 ui.write('Renaming %s to %s\n' % (patch, name))
1886 ui.write('Renaming %s to %s\n' % (patch, name))
1884 i = q.find_series(patch)
1887 i = q.find_series(patch)
1885 guards = q.guard_re.findall(q.full_series[i])
1888 guards = q.guard_re.findall(q.full_series[i])
1886 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1889 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1887 q.parse_series()
1890 q.parse_series()
1888 q.series_dirty = 1
1891 q.series_dirty = 1
1889
1892
1890 info = q.isapplied(patch)
1893 info = q.isapplied(patch)
1891 if info:
1894 if info:
1892 q.applied[info[0]] = statusentry(info[1], name)
1895 q.applied[info[0]] = statusentry(info[1], name)
1893 q.applied_dirty = 1
1896 q.applied_dirty = 1
1894
1897
1895 util.rename(q.join(patch), absdest)
1898 util.rename(q.join(patch), absdest)
1896 r = q.qrepo()
1899 r = q.qrepo()
1897 if r:
1900 if r:
1898 wlock = r.wlock()
1901 wlock = r.wlock()
1899 try:
1902 try:
1900 if r.dirstate[name] == 'r':
1903 if r.dirstate[name] == 'r':
1901 r.undelete([name])
1904 r.undelete([name])
1902 r.copy(patch, name)
1905 r.copy(patch, name)
1903 r.remove([patch], False)
1906 r.remove([patch], False)
1904 finally:
1907 finally:
1905 del wlock
1908 del wlock
1906
1909
1907 q.save_dirty()
1910 q.save_dirty()
1908
1911
1909 def restore(ui, repo, rev, **opts):
1912 def restore(ui, repo, rev, **opts):
1910 """restore the queue state saved by a rev"""
1913 """restore the queue state saved by a rev"""
1911 rev = repo.lookup(rev)
1914 rev = repo.lookup(rev)
1912 q = repo.mq
1915 q = repo.mq
1913 q.restore(repo, rev, delete=opts['delete'],
1916 q.restore(repo, rev, delete=opts['delete'],
1914 qupdate=opts['update'])
1917 qupdate=opts['update'])
1915 q.save_dirty()
1918 q.save_dirty()
1916 return 0
1919 return 0
1917
1920
1918 def save(ui, repo, **opts):
1921 def save(ui, repo, **opts):
1919 """save current queue state"""
1922 """save current queue state"""
1920 q = repo.mq
1923 q = repo.mq
1921 message = cmdutil.logmessage(opts)
1924 message = cmdutil.logmessage(opts)
1922 ret = q.save(repo, msg=message)
1925 ret = q.save(repo, msg=message)
1923 if ret:
1926 if ret:
1924 return ret
1927 return ret
1925 q.save_dirty()
1928 q.save_dirty()
1926 if opts['copy']:
1929 if opts['copy']:
1927 path = q.path
1930 path = q.path
1928 if opts['name']:
1931 if opts['name']:
1929 newpath = os.path.join(q.basepath, opts['name'])
1932 newpath = os.path.join(q.basepath, opts['name'])
1930 if os.path.exists(newpath):
1933 if os.path.exists(newpath):
1931 if not os.path.isdir(newpath):
1934 if not os.path.isdir(newpath):
1932 raise util.Abort(_('destination %s exists and is not '
1935 raise util.Abort(_('destination %s exists and is not '
1933 'a directory') % newpath)
1936 'a directory') % newpath)
1934 if not opts['force']:
1937 if not opts['force']:
1935 raise util.Abort(_('destination %s exists, '
1938 raise util.Abort(_('destination %s exists, '
1936 'use -f to force') % newpath)
1939 'use -f to force') % newpath)
1937 else:
1940 else:
1938 newpath = savename(path)
1941 newpath = savename(path)
1939 ui.warn("copy %s to %s\n" % (path, newpath))
1942 ui.warn("copy %s to %s\n" % (path, newpath))
1940 util.copyfiles(path, newpath)
1943 util.copyfiles(path, newpath)
1941 if opts['empty']:
1944 if opts['empty']:
1942 try:
1945 try:
1943 os.unlink(q.join(q.status_path))
1946 os.unlink(q.join(q.status_path))
1944 except:
1947 except:
1945 pass
1948 pass
1946 return 0
1949 return 0
1947
1950
1948 def strip(ui, repo, rev, **opts):
1951 def strip(ui, repo, rev, **opts):
1949 """strip a revision and all later revs on the same branch"""
1952 """strip a revision and all later revs on the same branch"""
1950 rev = repo.lookup(rev)
1953 rev = repo.lookup(rev)
1951 backup = 'all'
1954 backup = 'all'
1952 if opts['backup']:
1955 if opts['backup']:
1953 backup = 'strip'
1956 backup = 'strip'
1954 elif opts['nobackup']:
1957 elif opts['nobackup']:
1955 backup = 'none'
1958 backup = 'none'
1956 update = repo.dirstate.parents()[0] != revlog.nullid
1959 update = repo.dirstate.parents()[0] != revlog.nullid
1957 repo.mq.strip(repo, rev, backup=backup, update=update)
1960 repo.mq.strip(repo, rev, backup=backup, update=update)
1958 return 0
1961 return 0
1959
1962
1960 def select(ui, repo, *args, **opts):
1963 def select(ui, repo, *args, **opts):
1961 '''set or print guarded patches to push
1964 '''set or print guarded patches to push
1962
1965
1963 Use the qguard command to set or print guards on patch, then use
1966 Use the qguard command to set or print guards on patch, then use
1964 qselect to tell mq which guards to use. A patch will be pushed if it
1967 qselect to tell mq which guards to use. A patch will be pushed if it
1965 has no guards or any positive guards match the currently selected guard,
1968 has no guards or any positive guards match the currently selected guard,
1966 but will not be pushed if any negative guards match the current guard.
1969 but will not be pushed if any negative guards match the current guard.
1967 For example:
1970 For example:
1968
1971
1969 qguard foo.patch -stable (negative guard)
1972 qguard foo.patch -stable (negative guard)
1970 qguard bar.patch +stable (positive guard)
1973 qguard bar.patch +stable (positive guard)
1971 qselect stable
1974 qselect stable
1972
1975
1973 This activates the "stable" guard. mq will skip foo.patch (because
1976 This activates the "stable" guard. mq will skip foo.patch (because
1974 it has a negative match) but push bar.patch (because it
1977 it has a negative match) but push bar.patch (because it
1975 has a positive match).
1978 has a positive match).
1976
1979
1977 With no arguments, prints the currently active guards.
1980 With no arguments, prints the currently active guards.
1978 With one argument, sets the active guard.
1981 With one argument, sets the active guard.
1979
1982
1980 Use -n/--none to deactivate guards (no other arguments needed).
1983 Use -n/--none to deactivate guards (no other arguments needed).
1981 When no guards are active, patches with positive guards are skipped
1984 When no guards are active, patches with positive guards are skipped
1982 and patches with negative guards are pushed.
1985 and patches with negative guards are pushed.
1983
1986
1984 qselect can change the guards on applied patches. It does not pop
1987 qselect can change the guards on applied patches. It does not pop
1985 guarded patches by default. Use --pop to pop back to the last applied
1988 guarded patches by default. Use --pop to pop back to the last applied
1986 patch that is not guarded. Use --reapply (which implies --pop) to push
1989 patch that is not guarded. Use --reapply (which implies --pop) to push
1987 back to the current patch afterwards, but skip guarded patches.
1990 back to the current patch afterwards, but skip guarded patches.
1988
1991
1989 Use -s/--series to print a list of all guards in the series file (no
1992 Use -s/--series to print a list of all guards in the series file (no
1990 other arguments needed). Use -v for more information.'''
1993 other arguments needed). Use -v for more information.'''
1991
1994
1992 q = repo.mq
1995 q = repo.mq
1993 guards = q.active()
1996 guards = q.active()
1994 if args or opts['none']:
1997 if args or opts['none']:
1995 old_unapplied = q.unapplied(repo)
1998 old_unapplied = q.unapplied(repo)
1996 old_guarded = [i for i in xrange(len(q.applied)) if
1999 old_guarded = [i for i in xrange(len(q.applied)) if
1997 not q.pushable(i)[0]]
2000 not q.pushable(i)[0]]
1998 q.set_active(args)
2001 q.set_active(args)
1999 q.save_dirty()
2002 q.save_dirty()
2000 if not args:
2003 if not args:
2001 ui.status(_('guards deactivated\n'))
2004 ui.status(_('guards deactivated\n'))
2002 if not opts['pop'] and not opts['reapply']:
2005 if not opts['pop'] and not opts['reapply']:
2003 unapplied = q.unapplied(repo)
2006 unapplied = q.unapplied(repo)
2004 guarded = [i for i in xrange(len(q.applied))
2007 guarded = [i for i in xrange(len(q.applied))
2005 if not q.pushable(i)[0]]
2008 if not q.pushable(i)[0]]
2006 if len(unapplied) != len(old_unapplied):
2009 if len(unapplied) != len(old_unapplied):
2007 ui.status(_('number of unguarded, unapplied patches has '
2010 ui.status(_('number of unguarded, unapplied patches has '
2008 'changed from %d to %d\n') %
2011 'changed from %d to %d\n') %
2009 (len(old_unapplied), len(unapplied)))
2012 (len(old_unapplied), len(unapplied)))
2010 if len(guarded) != len(old_guarded):
2013 if len(guarded) != len(old_guarded):
2011 ui.status(_('number of guarded, applied patches has changed '
2014 ui.status(_('number of guarded, applied patches has changed '
2012 'from %d to %d\n') %
2015 'from %d to %d\n') %
2013 (len(old_guarded), len(guarded)))
2016 (len(old_guarded), len(guarded)))
2014 elif opts['series']:
2017 elif opts['series']:
2015 guards = {}
2018 guards = {}
2016 noguards = 0
2019 noguards = 0
2017 for gs in q.series_guards:
2020 for gs in q.series_guards:
2018 if not gs:
2021 if not gs:
2019 noguards += 1
2022 noguards += 1
2020 for g in gs:
2023 for g in gs:
2021 guards.setdefault(g, 0)
2024 guards.setdefault(g, 0)
2022 guards[g] += 1
2025 guards[g] += 1
2023 if ui.verbose:
2026 if ui.verbose:
2024 guards['NONE'] = noguards
2027 guards['NONE'] = noguards
2025 guards = guards.items()
2028 guards = guards.items()
2026 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2029 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2027 if guards:
2030 if guards:
2028 ui.note(_('guards in series file:\n'))
2031 ui.note(_('guards in series file:\n'))
2029 for guard, count in guards:
2032 for guard, count in guards:
2030 ui.note('%2d ' % count)
2033 ui.note('%2d ' % count)
2031 ui.write(guard, '\n')
2034 ui.write(guard, '\n')
2032 else:
2035 else:
2033 ui.note(_('no guards in series file\n'))
2036 ui.note(_('no guards in series file\n'))
2034 else:
2037 else:
2035 if guards:
2038 if guards:
2036 ui.note(_('active guards:\n'))
2039 ui.note(_('active guards:\n'))
2037 for g in guards:
2040 for g in guards:
2038 ui.write(g, '\n')
2041 ui.write(g, '\n')
2039 else:
2042 else:
2040 ui.write(_('no active guards\n'))
2043 ui.write(_('no active guards\n'))
2041 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2044 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2042 popped = False
2045 popped = False
2043 if opts['pop'] or opts['reapply']:
2046 if opts['pop'] or opts['reapply']:
2044 for i in xrange(len(q.applied)):
2047 for i in xrange(len(q.applied)):
2045 pushable, reason = q.pushable(i)
2048 pushable, reason = q.pushable(i)
2046 if not pushable:
2049 if not pushable:
2047 ui.status(_('popping guarded patches\n'))
2050 ui.status(_('popping guarded patches\n'))
2048 popped = True
2051 popped = True
2049 if i == 0:
2052 if i == 0:
2050 q.pop(repo, all=True)
2053 q.pop(repo, all=True)
2051 else:
2054 else:
2052 q.pop(repo, i-1)
2055 q.pop(repo, i-1)
2053 break
2056 break
2054 if popped:
2057 if popped:
2055 try:
2058 try:
2056 if reapply:
2059 if reapply:
2057 ui.status(_('reapplying unguarded patches\n'))
2060 ui.status(_('reapplying unguarded patches\n'))
2058 q.push(repo, reapply)
2061 q.push(repo, reapply)
2059 finally:
2062 finally:
2060 q.save_dirty()
2063 q.save_dirty()
2061
2064
2062 def reposetup(ui, repo):
2065 def reposetup(ui, repo):
2063 class mqrepo(repo.__class__):
2066 class mqrepo(repo.__class__):
2064 def abort_if_wdir_patched(self, errmsg, force=False):
2067 def abort_if_wdir_patched(self, errmsg, force=False):
2065 if self.mq.applied and not force:
2068 if self.mq.applied and not force:
2066 parent = revlog.hex(self.dirstate.parents()[0])
2069 parent = revlog.hex(self.dirstate.parents()[0])
2067 if parent in [s.rev for s in self.mq.applied]:
2070 if parent in [s.rev for s in self.mq.applied]:
2068 raise util.Abort(errmsg)
2071 raise util.Abort(errmsg)
2069
2072
2070 def commit(self, *args, **opts):
2073 def commit(self, *args, **opts):
2071 if len(args) >= 6:
2074 if len(args) >= 6:
2072 force = args[5]
2075 force = args[5]
2073 else:
2076 else:
2074 force = opts.get('force')
2077 force = opts.get('force')
2075 self.abort_if_wdir_patched(
2078 self.abort_if_wdir_patched(
2076 _('cannot commit over an applied mq patch'),
2079 _('cannot commit over an applied mq patch'),
2077 force)
2080 force)
2078
2081
2079 return super(mqrepo, self).commit(*args, **opts)
2082 return super(mqrepo, self).commit(*args, **opts)
2080
2083
2081 def push(self, remote, force=False, revs=None):
2084 def push(self, remote, force=False, revs=None):
2082 if self.mq.applied and not force and not revs:
2085 if self.mq.applied and not force and not revs:
2083 raise util.Abort(_('source has mq patches applied'))
2086 raise util.Abort(_('source has mq patches applied'))
2084 return super(mqrepo, self).push(remote, force, revs)
2087 return super(mqrepo, self).push(remote, force, revs)
2085
2088
2086 def tags(self):
2089 def tags(self):
2087 if self.tagscache:
2090 if self.tagscache:
2088 return self.tagscache
2091 return self.tagscache
2089
2092
2090 tagscache = super(mqrepo, self).tags()
2093 tagscache = super(mqrepo, self).tags()
2091
2094
2092 q = self.mq
2095 q = self.mq
2093 if not q.applied:
2096 if not q.applied:
2094 return tagscache
2097 return tagscache
2095
2098
2096 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2099 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2097 mqtags.append((mqtags[-1][0], 'qtip'))
2100 mqtags.append((mqtags[-1][0], 'qtip'))
2098 mqtags.append((mqtags[0][0], 'qbase'))
2101 mqtags.append((mqtags[0][0], 'qbase'))
2099 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2102 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2100 for patch in mqtags:
2103 for patch in mqtags:
2101 if patch[1] in tagscache:
2104 if patch[1] in tagscache:
2102 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2105 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2103 else:
2106 else:
2104 tagscache[patch[1]] = patch[0]
2107 tagscache[patch[1]] = patch[0]
2105
2108
2106 return tagscache
2109 return tagscache
2107
2110
2108 def _branchtags(self):
2111 def _branchtags(self):
2109 q = self.mq
2112 q = self.mq
2110 if not q.applied:
2113 if not q.applied:
2111 return super(mqrepo, self)._branchtags()
2114 return super(mqrepo, self)._branchtags()
2112
2115
2113 self.branchcache = {} # avoid recursion in changectx
2116 self.branchcache = {} # avoid recursion in changectx
2114 cl = self.changelog
2117 cl = self.changelog
2115 partial, last, lrev = self._readbranchcache()
2118 partial, last, lrev = self._readbranchcache()
2116
2119
2117 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2120 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2118 start = lrev + 1
2121 start = lrev + 1
2119 if start < qbase:
2122 if start < qbase:
2120 # update the cache (excluding the patches) and save it
2123 # update the cache (excluding the patches) and save it
2121 self._updatebranchcache(partial, lrev+1, qbase)
2124 self._updatebranchcache(partial, lrev+1, qbase)
2122 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2125 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2123 start = qbase
2126 start = qbase
2124 # if start = qbase, the cache is as updated as it should be.
2127 # if start = qbase, the cache is as updated as it should be.
2125 # if start > qbase, the cache includes (part of) the patches.
2128 # if start > qbase, the cache includes (part of) the patches.
2126 # we might as well use it, but we won't save it.
2129 # we might as well use it, but we won't save it.
2127
2130
2128 # update the cache up to the tip
2131 # update the cache up to the tip
2129 self._updatebranchcache(partial, start, cl.count())
2132 self._updatebranchcache(partial, start, cl.count())
2130
2133
2131 return partial
2134 return partial
2132
2135
2133 if repo.local():
2136 if repo.local():
2134 repo.__class__ = mqrepo
2137 repo.__class__ = mqrepo
2135 repo.mq = queue(ui, repo.join(""))
2138 repo.mq = queue(ui, repo.join(""))
2136
2139
2137 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2140 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2138
2141
2139 cmdtable = {
2142 cmdtable = {
2140 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2143 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2141 "qclone":
2144 "qclone":
2142 (clone,
2145 (clone,
2143 [('', 'pull', None, _('use pull protocol to copy metadata')),
2146 [('', 'pull', None, _('use pull protocol to copy metadata')),
2144 ('U', 'noupdate', None, _('do not update the new working directories')),
2147 ('U', 'noupdate', None, _('do not update the new working directories')),
2145 ('', 'uncompressed', None,
2148 ('', 'uncompressed', None,
2146 _('use uncompressed transfer (fast over LAN)')),
2149 _('use uncompressed transfer (fast over LAN)')),
2147 ('p', 'patches', '', _('location of source patch repo')),
2150 ('p', 'patches', '', _('location of source patch repo')),
2148 ] + commands.remoteopts,
2151 ] + commands.remoteopts,
2149 _('hg qclone [OPTION]... SOURCE [DEST]')),
2152 _('hg qclone [OPTION]... SOURCE [DEST]')),
2150 "qcommit|qci":
2153 "qcommit|qci":
2151 (commit,
2154 (commit,
2152 commands.table["^commit|ci"][1],
2155 commands.table["^commit|ci"][1],
2153 _('hg qcommit [OPTION]... [FILE]...')),
2156 _('hg qcommit [OPTION]... [FILE]...')),
2154 "^qdiff":
2157 "^qdiff":
2155 (diff,
2158 (diff,
2156 [('g', 'git', None, _('use git extended diff format')),
2159 [('g', 'git', None, _('use git extended diff format')),
2157 ] + commands.walkopts,
2160 ] + commands.walkopts,
2158 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2161 _('hg qdiff [-I] [-X] [-g] [FILE]...')),
2159 "qdelete|qremove|qrm":
2162 "qdelete|qremove|qrm":
2160 (delete,
2163 (delete,
2161 [('k', 'keep', None, _('keep patch file')),
2164 [('k', 'keep', None, _('keep patch file')),
2162 ('r', 'rev', [], _('stop managing a revision'))],
2165 ('r', 'rev', [], _('stop managing a revision'))],
2163 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2166 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2164 'qfold':
2167 'qfold':
2165 (fold,
2168 (fold,
2166 [('e', 'edit', None, _('edit patch header')),
2169 [('e', 'edit', None, _('edit patch header')),
2167 ('k', 'keep', None, _('keep folded patch files')),
2170 ('k', 'keep', None, _('keep folded patch files')),
2168 ] + commands.commitopts,
2171 ] + commands.commitopts,
2169 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2172 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2170 'qgoto':
2173 'qgoto':
2171 (goto,
2174 (goto,
2172 [('f', 'force', None, _('overwrite any local changes'))],
2175 [('f', 'force', None, _('overwrite any local changes'))],
2173 _('hg qgoto [OPTION]... PATCH')),
2176 _('hg qgoto [OPTION]... PATCH')),
2174 'qguard':
2177 'qguard':
2175 (guard,
2178 (guard,
2176 [('l', 'list', None, _('list all patches and guards')),
2179 [('l', 'list', None, _('list all patches and guards')),
2177 ('n', 'none', None, _('drop all guards'))],
2180 ('n', 'none', None, _('drop all guards'))],
2178 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2181 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2179 'qheader': (header, [], _('hg qheader [PATCH]')),
2182 'qheader': (header, [], _('hg qheader [PATCH]')),
2180 "^qimport":
2183 "^qimport":
2181 (qimport,
2184 (qimport,
2182 [('e', 'existing', None, 'import file in patch dir'),
2185 [('e', 'existing', None, 'import file in patch dir'),
2183 ('n', 'name', '', 'patch file name'),
2186 ('n', 'name', '', 'patch file name'),
2184 ('f', 'force', None, 'overwrite existing files'),
2187 ('f', 'force', None, 'overwrite existing files'),
2185 ('r', 'rev', [], 'place existing revisions under mq control'),
2188 ('r', 'rev', [], 'place existing revisions under mq control'),
2186 ('g', 'git', None, _('use git extended diff format'))],
2189 ('g', 'git', None, _('use git extended diff format'))],
2187 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2190 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2188 "^qinit":
2191 "^qinit":
2189 (init,
2192 (init,
2190 [('c', 'create-repo', None, 'create queue repository')],
2193 [('c', 'create-repo', None, 'create queue repository')],
2191 _('hg qinit [-c]')),
2194 _('hg qinit [-c]')),
2192 "qnew":
2195 "qnew":
2193 (new,
2196 (new,
2194 [('e', 'edit', None, _('edit commit message')),
2197 [('e', 'edit', None, _('edit commit message')),
2195 ('f', 'force', None, _('import uncommitted changes into patch')),
2198 ('f', 'force', None, _('import uncommitted changes into patch')),
2196 ('g', 'git', None, _('use git extended diff format')),
2199 ('g', 'git', None, _('use git extended diff format')),
2197 ] + commands.walkopts + commands.commitopts,
2200 ] + commands.walkopts + commands.commitopts,
2198 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2201 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2199 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2202 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2200 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2203 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2201 "^qpop":
2204 "^qpop":
2202 (pop,
2205 (pop,
2203 [('a', 'all', None, _('pop all patches')),
2206 [('a', 'all', None, _('pop all patches')),
2204 ('n', 'name', '', _('queue name to pop')),
2207 ('n', 'name', '', _('queue name to pop')),
2205 ('f', 'force', None, _('forget any local changes'))],
2208 ('f', 'force', None, _('forget any local changes'))],
2206 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2209 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2207 "^qpush":
2210 "^qpush":
2208 (push,
2211 (push,
2209 [('f', 'force', None, _('apply if the patch has rejects')),
2212 [('f', 'force', None, _('apply if the patch has rejects')),
2210 ('l', 'list', None, _('list patch name in commit text')),
2213 ('l', 'list', None, _('list patch name in commit text')),
2211 ('a', 'all', None, _('apply all patches')),
2214 ('a', 'all', None, _('apply all patches')),
2212 ('m', 'merge', None, _('merge from another queue')),
2215 ('m', 'merge', None, _('merge from another queue')),
2213 ('n', 'name', '', _('merge queue name'))],
2216 ('n', 'name', '', _('merge queue name'))],
2214 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2217 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2215 "^qrefresh":
2218 "^qrefresh":
2216 (refresh,
2219 (refresh,
2217 [('e', 'edit', None, _('edit commit message')),
2220 [('e', 'edit', None, _('edit commit message')),
2218 ('g', 'git', None, _('use git extended diff format')),
2221 ('g', 'git', None, _('use git extended diff format')),
2219 ('s', 'short', None, _('refresh only files already in the patch')),
2222 ('s', 'short', None, _('refresh only files already in the patch')),
2220 ] + commands.walkopts + commands.commitopts,
2223 ] + commands.walkopts + commands.commitopts,
2221 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2224 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2222 'qrename|qmv':
2225 'qrename|qmv':
2223 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2226 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2224 "qrestore":
2227 "qrestore":
2225 (restore,
2228 (restore,
2226 [('d', 'delete', None, _('delete save entry')),
2229 [('d', 'delete', None, _('delete save entry')),
2227 ('u', 'update', None, _('update queue working dir'))],
2230 ('u', 'update', None, _('update queue working dir'))],
2228 _('hg qrestore [-d] [-u] REV')),
2231 _('hg qrestore [-d] [-u] REV')),
2229 "qsave":
2232 "qsave":
2230 (save,
2233 (save,
2231 [('c', 'copy', None, _('copy patch directory')),
2234 [('c', 'copy', None, _('copy patch directory')),
2232 ('n', 'name', '', _('copy directory name')),
2235 ('n', 'name', '', _('copy directory name')),
2233 ('e', 'empty', None, _('clear queue status file')),
2236 ('e', 'empty', None, _('clear queue status file')),
2234 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2237 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2235 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2238 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2236 "qselect":
2239 "qselect":
2237 (select,
2240 (select,
2238 [('n', 'none', None, _('disable all guards')),
2241 [('n', 'none', None, _('disable all guards')),
2239 ('s', 'series', None, _('list all guards in series file')),
2242 ('s', 'series', None, _('list all guards in series file')),
2240 ('', 'pop', None, _('pop to before first guarded applied patch')),
2243 ('', 'pop', None, _('pop to before first guarded applied patch')),
2241 ('', 'reapply', None, _('pop, then reapply patches'))],
2244 ('', 'reapply', None, _('pop, then reapply patches'))],
2242 _('hg qselect [OPTION]... [GUARD]...')),
2245 _('hg qselect [OPTION]... [GUARD]...')),
2243 "qseries":
2246 "qseries":
2244 (series,
2247 (series,
2245 [('m', 'missing', None, _('print patches not in series')),
2248 [('m', 'missing', None, _('print patches not in series')),
2246 ] + seriesopts,
2249 ] + seriesopts,
2247 _('hg qseries [-ms]')),
2250 _('hg qseries [-ms]')),
2248 "^strip":
2251 "^strip":
2249 (strip,
2252 (strip,
2250 [('f', 'force', None, _('force multi-head removal')),
2253 [('f', 'force', None, _('force multi-head removal')),
2251 ('b', 'backup', None, _('bundle unrelated changesets')),
2254 ('b', 'backup', None, _('bundle unrelated changesets')),
2252 ('n', 'nobackup', None, _('no backups'))],
2255 ('n', 'nobackup', None, _('no backups'))],
2253 _('hg strip [-f] [-b] [-n] REV')),
2256 _('hg strip [-f] [-b] [-n] REV')),
2254 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2257 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2255 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2258 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2256 }
2259 }
@@ -1,443 +1,455 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 checkundo()
4 {
5 if [ -f .hg/store/undo ]; then
6 echo ".hg/store/undo still exists after $1"
7 fi
8 }
9
3 echo "[extensions]" >> $HGRCPATH
10 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
11 echo "mq=" >> $HGRCPATH
5
12
6 echo % help
13 echo % help
7 hg help mq
14 hg help mq
8
15
9 hg init a
16 hg init a
10 cd a
17 cd a
11 echo a > a
18 echo a > a
12 hg ci -Ama
19 hg ci -Ama
13
20
14 hg clone . ../k
21 hg clone . ../k
15
22
16 mkdir b
23 mkdir b
17 echo z > b/z
24 echo z > b/z
18 hg ci -Ama
25 hg ci -Ama
19
26
20 echo % qinit
27 echo % qinit
21
28
22 hg qinit
29 hg qinit
23
30
24 cd ..
31 cd ..
25 hg init b
32 hg init b
26
33
27 echo % -R qinit
34 echo % -R qinit
28
35
29 hg -R b qinit
36 hg -R b qinit
30
37
31 hg init c
38 hg init c
32
39
33 echo % qinit -c
40 echo % qinit -c
34
41
35 hg --cwd c qinit -c
42 hg --cwd c qinit -c
36 hg -R c/.hg/patches st
43 hg -R c/.hg/patches st
37
44
38 echo % qnew implies add
45 echo % qnew implies add
39
46
40 hg -R c qnew test.patch
47 hg -R c qnew test.patch
41 hg -R c/.hg/patches st
48 hg -R c/.hg/patches st
42
49
43 echo '% qinit; qinit -c'
50 echo '% qinit; qinit -c'
44 hg init d
51 hg init d
45 cd d
52 cd d
46 hg qinit
53 hg qinit
47 hg qinit -c
54 hg qinit -c
48 # qinit -c should create both files if they don't exist
55 # qinit -c should create both files if they don't exist
49 echo ' .hgignore:'
56 echo ' .hgignore:'
50 cat .hg/patches/.hgignore
57 cat .hg/patches/.hgignore
51 echo ' series:'
58 echo ' series:'
52 cat .hg/patches/series
59 cat .hg/patches/series
53 hg qinit -c 2>&1 | sed -e 's/repository.*already/repository already/'
60 hg qinit -c 2>&1 | sed -e 's/repository.*already/repository already/'
54 cd ..
61 cd ..
55
62
56 echo '% qinit; <stuff>; qinit -c'
63 echo '% qinit; <stuff>; qinit -c'
57 hg init e
64 hg init e
58 cd e
65 cd e
59 hg qnew A
66 hg qnew A
67 checkundo qnew
60 echo foo > foo
68 echo foo > foo
61 hg add foo
69 hg add foo
62 hg qrefresh
70 hg qrefresh
63 hg qnew B
71 hg qnew B
64 echo >> foo
72 echo >> foo
65 hg qrefresh
73 hg qrefresh
66 echo status >> .hg/patches/.hgignore
74 echo status >> .hg/patches/.hgignore
67 echo bleh >> .hg/patches/.hgignore
75 echo bleh >> .hg/patches/.hgignore
68 hg qinit -c
76 hg qinit -c
69 hg -R .hg/patches status
77 hg -R .hg/patches status
70 # qinit -c shouldn't touch these files if they already exist
78 # qinit -c shouldn't touch these files if they already exist
71 echo ' .hgignore:'
79 echo ' .hgignore:'
72 cat .hg/patches/.hgignore
80 cat .hg/patches/.hgignore
73 echo ' series:'
81 echo ' series:'
74 cat .hg/patches/series
82 cat .hg/patches/series
75 cd ..
83 cd ..
76
84
77 cd a
85 cd a
78
86
79 echo % qnew -m
87 echo % qnew -m
80
88
81 hg qnew -m 'foo bar' test.patch
89 hg qnew -m 'foo bar' test.patch
82 cat .hg/patches/test.patch
90 cat .hg/patches/test.patch
83
91
84 echo % qrefresh
92 echo % qrefresh
85
93
86 echo a >> a
94 echo a >> a
87 hg qrefresh
95 hg qrefresh
88 sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
96 sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
89 -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
97 -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
90 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
98 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
91
99
92 echo % empty qrefresh
100 echo % empty qrefresh
93
101
94 hg qrefresh -X a
102 hg qrefresh -X a
95 echo 'revision:'
103 echo 'revision:'
96 hg diff -r -2 -r -1
104 hg diff -r -2 -r -1
97 echo 'patch:'
105 echo 'patch:'
98 cat .hg/patches/test.patch
106 cat .hg/patches/test.patch
99 echo 'working dir diff:'
107 echo 'working dir diff:'
100 hg diff --nodates -q
108 hg diff --nodates -q
101 # restore things
109 # restore things
102 hg qrefresh
110 hg qrefresh
111 checkundo qrefresh
103
112
104 echo % qpop
113 echo % qpop
105
114
106 hg qpop
115 hg qpop
116 checkundo qpop
107
117
108 echo % qpush
118 echo % qpush
109
119
110 hg qpush
120 hg qpush
121 checkundo qpush
111
122
112 cd ..
123 cd ..
113
124
114 echo % pop/push outside repo
125 echo % pop/push outside repo
115
126
116 hg -R a qpop
127 hg -R a qpop
117 hg -R a qpush
128 hg -R a qpush
118
129
119 cd a
130 cd a
120 hg qnew test2.patch
131 hg qnew test2.patch
121
132
122 echo % qrefresh in subdir
133 echo % qrefresh in subdir
123
134
124 cd b
135 cd b
125 echo a > a
136 echo a > a
126 hg add a
137 hg add a
127 hg qrefresh
138 hg qrefresh
128
139
129 echo % pop/push -a in subdir
140 echo % pop/push -a in subdir
130
141
131 hg qpop -a
142 hg qpop -a
132 hg --traceback qpush -a
143 hg --traceback qpush -a
133
144
134 echo % qseries
145 echo % qseries
135 hg qseries
146 hg qseries
136 hg qpop
147 hg qpop
137 hg qseries -vs
148 hg qseries -vs
138 hg qpush
149 hg qpush
139
150
140 echo % qapplied
151 echo % qapplied
141 hg qapplied
152 hg qapplied
142
153
143 echo % qtop
154 echo % qtop
144 hg qtop
155 hg qtop
145
156
146 echo % qprev
157 echo % qprev
147 hg qprev
158 hg qprev
148
159
149 echo % qnext
160 echo % qnext
150 hg qnext
161 hg qnext
151
162
152 echo % pop, qnext, qprev, qapplied
163 echo % pop, qnext, qprev, qapplied
153 hg qpop
164 hg qpop
154 hg qnext
165 hg qnext
155 hg qprev
166 hg qprev
156 hg qapplied
167 hg qapplied
157
168
158 echo % commit should fail
169 echo % commit should fail
159 hg commit
170 hg commit
160
171
161 echo % push should fail
172 echo % push should fail
162 hg push ../../k
173 hg push ../../k
163
174
164 echo % qunapplied
175 echo % qunapplied
165 hg qunapplied
176 hg qunapplied
166
177
167 echo % qpush/qpop with index
178 echo % qpush/qpop with index
168 hg qnew test1b.patch
179 hg qnew test1b.patch
169 echo 1b > 1b
180 echo 1b > 1b
170 hg add 1b
181 hg add 1b
171 hg qrefresh
182 hg qrefresh
172 hg qpush 2
183 hg qpush 2
173 hg qpop 0
184 hg qpop 0
174 hg qpush test.patch+1
185 hg qpush test.patch+1
175 hg qpush test.patch+2
186 hg qpush test.patch+2
176 hg qpop test2.patch-1
187 hg qpop test2.patch-1
177 hg qpop test2.patch-2
188 hg qpop test2.patch-2
178 hg qpush test1b.patch+1
189 hg qpush test1b.patch+1
179
190
180 echo % push should succeed
191 echo % push should succeed
181 hg qpop -a
192 hg qpop -a
182 hg push ../../k
193 hg push ../../k
183
194
184 echo % qpush/qpop error codes
195 echo % qpush/qpop error codes
185 errorcode()
196 errorcode()
186 {
197 {
187 hg "$@" && echo " $@ succeeds" || echo " $@ fails"
198 hg "$@" && echo " $@ succeeds" || echo " $@ fails"
188 }
199 }
189
200
190 # we want to start with some patches applied
201 # we want to start with some patches applied
191 hg qpush -a
202 hg qpush -a
192 echo " % pops all patches and succeeds"
203 echo " % pops all patches and succeeds"
193 errorcode qpop -a
204 errorcode qpop -a
194 echo " % does nothing and succeeds"
205 echo " % does nothing and succeeds"
195 errorcode qpop -a
206 errorcode qpop -a
196 echo " % fails - nothing else to pop"
207 echo " % fails - nothing else to pop"
197 errorcode qpop
208 errorcode qpop
198 echo " % pushes a patch and succeeds"
209 echo " % pushes a patch and succeeds"
199 errorcode qpush
210 errorcode qpush
200 echo " % pops a patch and succeeds"
211 echo " % pops a patch and succeeds"
201 errorcode qpop
212 errorcode qpop
202 echo " % pushes up to test1b.patch and succeeds"
213 echo " % pushes up to test1b.patch and succeeds"
203 errorcode qpush test1b.patch
214 errorcode qpush test1b.patch
204 echo " % does nothing and succeeds"
215 echo " % does nothing and succeeds"
205 errorcode qpush test1b.patch
216 errorcode qpush test1b.patch
206 echo " % does nothing and succeeds"
217 echo " % does nothing and succeeds"
207 errorcode qpop test1b.patch
218 errorcode qpop test1b.patch
208 echo " % fails - can't push to this patch"
219 echo " % fails - can't push to this patch"
209 errorcode qpush test.patch
220 errorcode qpush test.patch
210 echo " % fails - can't pop to this patch"
221 echo " % fails - can't pop to this patch"
211 errorcode qpop test2.patch
222 errorcode qpop test2.patch
212 echo " % pops up to test.patch and succeeds"
223 echo " % pops up to test.patch and succeeds"
213 errorcode qpop test.patch
224 errorcode qpop test.patch
214 echo " % pushes all patches and succeeds"
225 echo " % pushes all patches and succeeds"
215 errorcode qpush -a
226 errorcode qpush -a
216 echo " % does nothing and succeeds"
227 echo " % does nothing and succeeds"
217 errorcode qpush -a
228 errorcode qpush -a
218 echo " % fails - nothing else to push"
229 echo " % fails - nothing else to push"
219 errorcode qpush
230 errorcode qpush
220 echo " % does nothing and succeeds"
231 echo " % does nothing and succeeds"
221 errorcode qpush test2.patch
232 errorcode qpush test2.patch
222
233
223
234
224 echo % strip
235 echo % strip
225 cd ../../b
236 cd ../../b
226 echo x>x
237 echo x>x
227 hg ci -Ama
238 hg ci -Ama
228 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
239 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
229 hg unbundle .hg/strip-backup/*
240 hg unbundle .hg/strip-backup/*
230
241
231 echo '% cd b; hg qrefresh'
242 echo '% cd b; hg qrefresh'
232 hg init refresh
243 hg init refresh
233 cd refresh
244 cd refresh
234 echo a > a
245 echo a > a
235 hg ci -Ama -d'0 0'
246 hg ci -Ama -d'0 0'
236 hg qnew -mfoo foo
247 hg qnew -mfoo foo
237 echo a >> a
248 echo a >> a
238 hg qrefresh
249 hg qrefresh
239 mkdir b
250 mkdir b
240 cd b
251 cd b
241 echo f > f
252 echo f > f
242 hg add f
253 hg add f
243 hg qrefresh
254 hg qrefresh
244 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
255 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
245 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
256 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
246 echo % hg qrefresh .
257 echo % hg qrefresh .
247 hg qrefresh .
258 hg qrefresh .
248 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
259 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
249 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
260 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
250 hg status
261 hg status
251
262
252 echo % qpush failure
263 echo % qpush failure
253 cd ..
264 cd ..
254 hg qrefresh
265 hg qrefresh
255 hg qnew -mbar bar
266 hg qnew -mbar bar
256 echo foo > foo
267 echo foo > foo
257 echo bar > bar
268 echo bar > bar
258 hg add foo bar
269 hg add foo bar
259 hg qrefresh
270 hg qrefresh
260 hg qpop -a
271 hg qpop -a
261 echo bar > foo
272 echo bar > foo
262 hg qpush -a
273 hg qpush -a
263 hg st
274 hg st
264
275
265 echo % mq tags
276 echo % mq tags
266 hg log --template '{rev} {tags}\n' -r qparent:qtip
277 hg log --template '{rev} {tags}\n' -r qparent:qtip
267
278
268 cat >>$HGRCPATH <<EOF
279 cat >>$HGRCPATH <<EOF
269 [diff]
280 [diff]
270 git = True
281 git = True
271 EOF
282 EOF
272 cd ..
283 cd ..
273 hg init git
284 hg init git
274 cd git
285 cd git
275 hg qinit
286 hg qinit
276
287
277 hg qnew -m'new file' new
288 hg qnew -m'new file' new
278 echo foo > new
289 echo foo > new
279 chmod +x new
290 chmod +x new
280 hg add new
291 hg add new
281 hg qrefresh
292 hg qrefresh
282 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
293 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
283 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
294 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
284
295
285 hg qnew -m'copy file' copy
296 hg qnew -m'copy file' copy
286 hg cp new copy
297 hg cp new copy
287 hg qrefresh
298 hg qrefresh
288 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
299 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
289 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
300 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
290
301
291 hg qpop
302 hg qpop
292 hg qpush
303 hg qpush
293 hg qdiff
304 hg qdiff
294 cat >>$HGRCPATH <<EOF
305 cat >>$HGRCPATH <<EOF
295 [diff]
306 [diff]
296 git = False
307 git = False
297 EOF
308 EOF
298 hg qdiff --git
309 hg qdiff --git
299
310
300 cd ..
311 cd ..
301 hg init slow
312 hg init slow
302 cd slow
313 cd slow
303 hg qinit
314 hg qinit
304 echo foo > foo
315 echo foo > foo
305 hg add foo
316 hg add foo
306 hg ci -m 'add foo'
317 hg ci -m 'add foo'
307 hg qnew bar
318 hg qnew bar
308 echo bar > bar
319 echo bar > bar
309 hg add bar
320 hg add bar
310 hg mv foo baz
321 hg mv foo baz
311 hg qrefresh --git
322 hg qrefresh --git
312 hg up -C 0
323 hg up -C 0
313 echo >> foo
324 echo >> foo
314 hg ci -m 'change foo'
325 hg ci -m 'change foo'
315 hg up -C 1
326 hg up -C 1
316 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
327 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
317 cat .hg/patches/bar
328 cat .hg/patches/bar
318 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
329 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
319 hg qrefresh --git
330 hg qrefresh --git
320 cat .hg/patches/bar
331 cat .hg/patches/bar
321 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
332 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
322 hg qrefresh
333 hg qrefresh
323 grep 'diff --git' .hg/patches/bar
334 grep 'diff --git' .hg/patches/bar
324
335
325 echo
336 echo
326 hg up -C 1
337 hg up -C 1
327 echo >> foo
338 echo >> foo
328 hg ci -m 'change foo again'
339 hg ci -m 'change foo again'
329 hg up -C 2
340 hg up -C 2
330 hg mv bar quux
341 hg mv bar quux
331 hg mv baz bleh
342 hg mv baz bleh
332 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
343 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
333 cat .hg/patches/bar
344 cat .hg/patches/bar
334 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
345 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
335 hg mv quux fred
346 hg mv quux fred
336 hg mv bleh barney
347 hg mv bleh barney
337 hg qrefresh --git
348 hg qrefresh --git
338 cat .hg/patches/bar
349 cat .hg/patches/bar
339 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
350 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
340
351
341 echo % refresh omitting an added file
352 echo % refresh omitting an added file
342 hg qnew baz
353 hg qnew baz
343 echo newfile > newfile
354 echo newfile > newfile
344 hg add newfile
355 hg add newfile
345 hg qrefresh
356 hg qrefresh
346 hg st -A newfile
357 hg st -A newfile
347 hg qrefresh -X newfile
358 hg qrefresh -X newfile
348 hg st -A newfile
359 hg st -A newfile
349 hg revert newfile
360 hg revert newfile
350 rm newfile
361 rm newfile
351 hg qpop
362 hg qpop
352 hg qdel baz
363 hg qdel baz
353
364
354 echo % create a git patch
365 echo % create a git patch
355 echo a > alexander
366 echo a > alexander
356 hg add alexander
367 hg add alexander
357 hg qnew -f --git addalexander
368 hg qnew -f --git addalexander
358 grep diff .hg/patches/addalexander
369 grep diff .hg/patches/addalexander
359
370
360 echo % create a git binary patch
371 echo % create a git binary patch
361 cat > writebin.py <<EOF
372 cat > writebin.py <<EOF
362 import sys
373 import sys
363 path = sys.argv[1]
374 path = sys.argv[1]
364 open(path, 'wb').write('BIN\x00ARY')
375 open(path, 'wb').write('BIN\x00ARY')
365 EOF
376 EOF
366 python writebin.py bucephalus
377 python writebin.py bucephalus
367
378
368 python "$TESTDIR/md5sum.py" bucephalus
379 python "$TESTDIR/md5sum.py" bucephalus
369 hg add bucephalus
380 hg add bucephalus
370 hg qnew -f --git addbucephalus
381 hg qnew -f --git addbucephalus
371 grep diff .hg/patches/addbucephalus
382 grep diff .hg/patches/addbucephalus
372
383
373 echo % check binary patches can be popped and pushed
384 echo % check binary patches can be popped and pushed
374 hg qpop
385 hg qpop
375 test -f bucephalus && echo % bucephalus should not be there
386 test -f bucephalus && echo % bucephalus should not be there
376 hg qpush
387 hg qpush
377 test -f bucephalus || echo % bucephalus should be there
388 test -f bucephalus || echo % bucephalus should be there
378 python "$TESTDIR/md5sum.py" bucephalus
389 python "$TESTDIR/md5sum.py" bucephalus
379
390
380
391
381 echo '% strip again'
392 echo '% strip again'
382 cd ..
393 cd ..
383 hg init strip
394 hg init strip
384 cd strip
395 cd strip
385 touch foo
396 touch foo
386 hg add foo
397 hg add foo
387 hg ci -m 'add foo' -d '0 0'
398 hg ci -m 'add foo' -d '0 0'
388 echo >> foo
399 echo >> foo
389 hg ci -m 'change foo 1' -d '0 0'
400 hg ci -m 'change foo 1' -d '0 0'
390 hg up -C 0
401 hg up -C 0
391 echo 1 >> foo
402 echo 1 >> foo
392 hg ci -m 'change foo 2' -d '0 0'
403 hg ci -m 'change foo 2' -d '0 0'
393 HGMERGE=true hg merge
404 HGMERGE=true hg merge
394 hg ci -m merge -d '0 0'
405 hg ci -m merge -d '0 0'
395 hg log
406 hg log
396 hg strip 1 2>&1 | sed 's/\(saving bundle to \).*/\1/'
407 hg strip 1 2>&1 | sed 's/\(saving bundle to \).*/\1/'
408 checkundo strip
397 hg log
409 hg log
398 cd ..
410 cd ..
399
411
400 echo '% qclone'
412 echo '% qclone'
401 qlog()
413 qlog()
402 {
414 {
403 echo 'main repo:'
415 echo 'main repo:'
404 hg log --template ' rev {rev}: {desc}\n'
416 hg log --template ' rev {rev}: {desc}\n'
405 echo 'patch repo:'
417 echo 'patch repo:'
406 hg -R .hg/patches log --template ' rev {rev}: {desc}\n'
418 hg -R .hg/patches log --template ' rev {rev}: {desc}\n'
407 }
419 }
408 hg init qclonesource
420 hg init qclonesource
409 cd qclonesource
421 cd qclonesource
410 echo foo > foo
422 echo foo > foo
411 hg add foo
423 hg add foo
412 hg ci -m 'add foo'
424 hg ci -m 'add foo'
413 hg qinit
425 hg qinit
414 hg qnew patch1
426 hg qnew patch1
415 echo bar >> foo
427 echo bar >> foo
416 hg qrefresh -m 'change foo'
428 hg qrefresh -m 'change foo'
417 cd ..
429 cd ..
418
430
419 # repo with unversioned patch dir
431 # repo with unversioned patch dir
420 hg qclone qclonesource failure
432 hg qclone qclonesource failure
421
433
422 cd qclonesource
434 cd qclonesource
423 hg qinit -c
435 hg qinit -c
424 hg qci -m checkpoint
436 hg qci -m checkpoint
425 qlog
437 qlog
426 cd ..
438 cd ..
427
439
428 # repo with patches applied
440 # repo with patches applied
429 hg qclone qclonesource qclonedest
441 hg qclone qclonesource qclonedest
430 cd qclonedest
442 cd qclonedest
431 qlog
443 qlog
432 cd ..
444 cd ..
433
445
434 # repo with patches unapplied
446 # repo with patches unapplied
435 cd qclonesource
447 cd qclonesource
436 hg qpop -a
448 hg qpop -a
437 qlog
449 qlog
438 cd ..
450 cd ..
439 hg qclone qclonesource qclonedest2
451 hg qclone qclonesource qclonedest2
440 cd qclonedest2
452 cd qclonedest2
441 qlog
453 qlog
442 cd ..
454 cd ..
443
455
@@ -1,42 +1,51 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 # Test issue 529 - mq aborts when merging patch deleting files
3 # Test issue 529 - mq aborts when merging patch deleting files
4
4
5 rewrite_path()
5 rewrite_path()
6 {
6 {
7 sed -e 's:\\:/:g' -e 's:[^ ]*/t/::g'
7 sed -e 's:\\:/:g' -e 's:[^ ]*/t/::g'
8 }
8 }
9
9
10 checkundo()
11 {
12 if [ -f .hg/store/undo ]; then
13 echo ".hg/store/undo still exists after $1"
14 fi
15 }
16
10 echo "[extensions]" >> $HGRCPATH
17 echo "[extensions]" >> $HGRCPATH
11 echo "hgext.mq=" >> $HGRCPATH
18 echo "hgext.mq=" >> $HGRCPATH
12
19
13 # Commit two dummy files in "init" changeset
20 # Commit two dummy files in "init" changeset
14 hg init t
21 hg init t
15 cd t
22 cd t
16 echo a > a
23 echo a > a
17 echo b > b
24 echo b > b
18 hg ci -Am init
25 hg ci -Am init
19 hg tag -l init
26 hg tag -l init
20
27
21 # Create a patch removing a
28 # Create a patch removing a
22 hg qnew rm_a
29 hg qnew rm_a
23 hg rm a
30 hg rm a
24 hg qrefresh -m "rm a"
31 hg qrefresh -m "rm a"
25
32
26 # Save the patch queue so we can merge it later
33 # Save the patch queue so we can merge it later
27 hg qsave -c -e 2>&1 | rewrite_path
34 hg qsave -c -e 2>&1 | rewrite_path
35 checkundo qsave
28
36
29 # Update b and commit in an "update" changeset
37 # Update b and commit in an "update" changeset
30 hg up -C init
38 hg up -C init
31 echo b >> b
39 echo b >> b
32 hg st
40 hg st
33 hg ci -m update
41 hg ci -m update
34
42
35 # Here, qpush used to abort with :
43 # Here, qpush used to abort with :
36 # The system cannot find the file specified => a
44 # The system cannot find the file specified => a
37 hg manifest
45 hg manifest
38 hg qpush -a -m 2>&1 | rewrite_path
46 hg qpush -a -m 2>&1 | rewrite_path
47 checkundo 'qpush -m'
39 hg manifest
48 hg manifest
40
49
41 # ensure status is correct after merge
50 # ensure status is correct after merge
42 hg qpop -a
51 hg qpop -a
General Comments 0
You need to be logged in to leave comments. Login now