##// END OF EJS Templates
mq: support multiple patch queues using qqueue
Henrik Stuart -
r11229:1e701ffd default
parent child Browse files
Show More
@@ -0,0 +1,47 b''
1 #!/bin/sh
2
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
5
6 hg init foo
7 cd foo
8 echo a > a
9 hg ci -qAm a
10
11 echo %% default queue
12 hg qqueue
13
14 echo b > a
15 hg qnew -fgDU somestuff
16
17 echo %% applied patches in default queue
18 hg qap
19
20 echo %% try to change patch \(create succeeds, switch fails\)
21 hg qqueue foo --create
22 hg qqueue
23
24 echo %% empty default queue
25 hg qpop
26
27 echo %% switch queue
28 hg qqueue foo
29 hg qqueue
30
31 echo %% unapplied patches
32 hg qun
33 echo c > a
34 hg qnew -fgDU otherstuff
35
36 echo %% fail switching back
37 hg qqueue patches
38
39 echo %% fail deleting current
40 hg qqueue foo --delete
41
42 echo %% switch back and delete foo
43 hg qpop -a
44 hg qqueue patches
45 hg qqueue foo --delete
46 hg qqueue
47 cd ..
@@ -0,0 +1,23 b''
1 %% default queue
2 patches (active)
3 %% applied patches in default queue
4 somestuff
5 %% try to change patch (create succeeds, switch fails)
6 abort: patches applied - cannot set new queue active
7 foo
8 patches (active)
9 %% empty default queue
10 popping somestuff
11 patch queue now empty
12 %% switch queue
13 foo (active)
14 patches
15 %% unapplied patches
16 %% fail switching back
17 abort: patches applied - cannot set new queue active
18 %% fail deleting current
19 abort: cannot delete currently active queue
20 %% switch back and delete foo
21 popping otherstuff
22 patch queue now empty
23 patches (active)
@@ -1,2850 +1,2964 b''
1 1 # mq.py - patch queues for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''manage a stack of patches
9 9
10 10 This extension lets you work with a stack of patches in a Mercurial
11 11 repository. It manages two stacks of patches - all known patches, and
12 12 applied patches (subset of known patches).
13 13
14 14 Known patches are represented as patch files in the .hg/patches
15 15 directory. Applied patches are both patch files and changesets.
16 16
17 17 Common tasks (use :hg:`help command` for more details)::
18 18
19 19 create new patch qnew
20 20 import existing patch qimport
21 21
22 22 print patch series qseries
23 23 print applied patches qapplied
24 24
25 25 add known patch to applied stack qpush
26 26 remove patch from applied stack qpop
27 27 refresh contents of top applied patch qrefresh
28 28
29 29 By default, mq will automatically use git patches when required to
30 30 avoid losing file mode changes, copy records, binary files or empty
31 31 files creations or deletions. This behaviour can be configured with::
32 32
33 33 [mq]
34 34 git = auto/keep/yes/no
35 35
36 36 If set to 'keep', mq will obey the [diff] section configuration while
37 37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 38 'no', mq will override the [diff] section and always generate git or
39 39 regular patches, possibly losing data in the second case.
40 40 '''
41 41
42 42 from mercurial.i18n import _
43 43 from mercurial.node import bin, hex, short, nullid, nullrev
44 44 from mercurial.lock import release
45 45 from mercurial import commands, cmdutil, hg, patch, util
46 46 from mercurial import repair, extensions, url, error
47 47 import os, sys, re, errno
48 48
49 49 commands.norepo += " qclone"
50 50
51 51 # Patch names looks like unix-file names.
52 52 # They must be joinable with queue directory and result in the patch path.
53 53 normname = util.normpath
54 54
55 55 class statusentry(object):
56 56 def __init__(self, node, name):
57 57 self.node, self.name = node, name
58 58
59 59 def __str__(self):
60 60 return hex(self.node) + ':' + self.name
61 61
62 62 class patchheader(object):
63 63 def __init__(self, pf, plainmode=False):
64 64 def eatdiff(lines):
65 65 while lines:
66 66 l = lines[-1]
67 67 if (l.startswith("diff -") or
68 68 l.startswith("Index:") or
69 69 l.startswith("===========")):
70 70 del lines[-1]
71 71 else:
72 72 break
73 73 def eatempty(lines):
74 74 while lines:
75 75 if not lines[-1].strip():
76 76 del lines[-1]
77 77 else:
78 78 break
79 79
80 80 message = []
81 81 comments = []
82 82 user = None
83 83 date = None
84 84 parent = None
85 85 format = None
86 86 subject = None
87 87 diffstart = 0
88 88
89 89 for line in file(pf):
90 90 line = line.rstrip()
91 91 if (line.startswith('diff --git')
92 92 or (diffstart and line.startswith('+++ '))):
93 93 diffstart = 2
94 94 break
95 95 diffstart = 0 # reset
96 96 if line.startswith("--- "):
97 97 diffstart = 1
98 98 continue
99 99 elif format == "hgpatch":
100 100 # parse values when importing the result of an hg export
101 101 if line.startswith("# User "):
102 102 user = line[7:]
103 103 elif line.startswith("# Date "):
104 104 date = line[7:]
105 105 elif line.startswith("# Parent "):
106 106 parent = line[9:]
107 107 elif not line.startswith("# ") and line:
108 108 message.append(line)
109 109 format = None
110 110 elif line == '# HG changeset patch':
111 111 message = []
112 112 format = "hgpatch"
113 113 elif (format != "tagdone" and (line.startswith("Subject: ") or
114 114 line.startswith("subject: "))):
115 115 subject = line[9:]
116 116 format = "tag"
117 117 elif (format != "tagdone" and (line.startswith("From: ") or
118 118 line.startswith("from: "))):
119 119 user = line[6:]
120 120 format = "tag"
121 121 elif (format != "tagdone" and (line.startswith("Date: ") or
122 122 line.startswith("date: "))):
123 123 date = line[6:]
124 124 format = "tag"
125 125 elif format == "tag" and line == "":
126 126 # when looking for tags (subject: from: etc) they
127 127 # end once you find a blank line in the source
128 128 format = "tagdone"
129 129 elif message or line:
130 130 message.append(line)
131 131 comments.append(line)
132 132
133 133 eatdiff(message)
134 134 eatdiff(comments)
135 135 eatempty(message)
136 136 eatempty(comments)
137 137
138 138 # make sure message isn't empty
139 139 if format and format.startswith("tag") and subject:
140 140 message.insert(0, "")
141 141 message.insert(0, subject)
142 142
143 143 self.message = message
144 144 self.comments = comments
145 145 self.user = user
146 146 self.date = date
147 147 self.parent = parent
148 148 self.haspatch = diffstart > 1
149 149 self.plainmode = plainmode
150 150
151 151 def setuser(self, user):
152 152 if not self.updateheader(['From: ', '# User '], user):
153 153 try:
154 154 patchheaderat = self.comments.index('# HG changeset patch')
155 155 self.comments.insert(patchheaderat + 1, '# User ' + user)
156 156 except ValueError:
157 157 if self.plainmode or self._hasheader(['Date: ']):
158 158 self.comments = ['From: ' + user] + self.comments
159 159 else:
160 160 tmp = ['# HG changeset patch', '# User ' + user, '']
161 161 self.comments = tmp + self.comments
162 162 self.user = user
163 163
164 164 def setdate(self, date):
165 165 if not self.updateheader(['Date: ', '# Date '], date):
166 166 try:
167 167 patchheaderat = self.comments.index('# HG changeset patch')
168 168 self.comments.insert(patchheaderat + 1, '# Date ' + date)
169 169 except ValueError:
170 170 if self.plainmode or self._hasheader(['From: ']):
171 171 self.comments = ['Date: ' + date] + self.comments
172 172 else:
173 173 tmp = ['# HG changeset patch', '# Date ' + date, '']
174 174 self.comments = tmp + self.comments
175 175 self.date = date
176 176
177 177 def setparent(self, parent):
178 178 if not self.updateheader(['# Parent '], parent):
179 179 try:
180 180 patchheaderat = self.comments.index('# HG changeset patch')
181 181 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
182 182 except ValueError:
183 183 pass
184 184 self.parent = parent
185 185
186 186 def setmessage(self, message):
187 187 if self.comments:
188 188 self._delmsg()
189 189 self.message = [message]
190 190 self.comments += self.message
191 191
192 192 def updateheader(self, prefixes, new):
193 193 '''Update all references to a field in the patch header.
194 194 Return whether the field is present.'''
195 195 res = False
196 196 for prefix in prefixes:
197 197 for i in xrange(len(self.comments)):
198 198 if self.comments[i].startswith(prefix):
199 199 self.comments[i] = prefix + new
200 200 res = True
201 201 break
202 202 return res
203 203
204 204 def _hasheader(self, prefixes):
205 205 '''Check if a header starts with any of the given prefixes.'''
206 206 for prefix in prefixes:
207 207 for comment in self.comments:
208 208 if comment.startswith(prefix):
209 209 return True
210 210 return False
211 211
212 212 def __str__(self):
213 213 if not self.comments:
214 214 return ''
215 215 return '\n'.join(self.comments) + '\n\n'
216 216
217 217 def _delmsg(self):
218 218 '''Remove existing message, keeping the rest of the comments fields.
219 219 If comments contains 'subject: ', message will prepend
220 220 the field and a blank line.'''
221 221 if self.message:
222 222 subj = 'subject: ' + self.message[0].lower()
223 223 for i in xrange(len(self.comments)):
224 224 if subj == self.comments[i].lower():
225 225 del self.comments[i]
226 226 self.message = self.message[2:]
227 227 break
228 228 ci = 0
229 229 for mi in self.message:
230 230 while mi != self.comments[ci]:
231 231 ci += 1
232 232 del self.comments[ci]
233 233
234 234 class queue(object):
235 235 def __init__(self, ui, path, patchdir=None):
236 236 self.basepath = path
237 self.path = patchdir or os.path.join(path, "patches")
237 try:
238 fh = open(os.path.join(path, '.queue'))
239 curpath = os.path.join(path, fh.read().rstrip())
240 except IOError:
241 curpath = os.path.join(path, 'patches')
242 self.path = patchdir or curpath
238 243 self.opener = util.opener(self.path)
239 244 self.ui = ui
240 245 self.applied_dirty = 0
241 246 self.series_dirty = 0
242 247 self.series_path = "series"
243 248 self.status_path = "status"
244 249 self.guards_path = "guards"
245 250 self.active_guards = None
246 251 self.guards_dirty = False
247 252 # Handle mq.git as a bool with extended values
248 253 try:
249 254 gitmode = ui.configbool('mq', 'git', None)
250 255 if gitmode is None:
251 256 raise error.ConfigError()
252 257 self.gitmode = gitmode and 'yes' or 'no'
253 258 except error.ConfigError:
254 259 self.gitmode = ui.config('mq', 'git', 'auto').lower()
255 260 self.plainmode = ui.configbool('mq', 'plain', False)
256 261
257 262 @util.propertycache
258 263 def applied(self):
259 264 if os.path.exists(self.join(self.status_path)):
260 265 def parse(l):
261 266 n, name = l.split(':', 1)
262 267 return statusentry(bin(n), name)
263 268 lines = self.opener(self.status_path).read().splitlines()
264 269 return [parse(l) for l in lines]
265 270 return []
266 271
267 272 @util.propertycache
268 273 def full_series(self):
269 274 if os.path.exists(self.join(self.series_path)):
270 275 return self.opener(self.series_path).read().splitlines()
271 276 return []
272 277
273 278 @util.propertycache
274 279 def series(self):
275 280 self.parse_series()
276 281 return self.series
277 282
278 283 @util.propertycache
279 284 def series_guards(self):
280 285 self.parse_series()
281 286 return self.series_guards
282 287
283 288 def invalidate(self):
284 289 for a in 'applied full_series series series_guards'.split():
285 290 if a in self.__dict__:
286 291 delattr(self, a)
287 292 self.applied_dirty = 0
288 293 self.series_dirty = 0
289 294 self.guards_dirty = False
290 295 self.active_guards = None
291 296
292 297 def diffopts(self, opts={}, patchfn=None):
293 298 diffopts = patch.diffopts(self.ui, opts)
294 299 if self.gitmode == 'auto':
295 300 diffopts.upgrade = True
296 301 elif self.gitmode == 'keep':
297 302 pass
298 303 elif self.gitmode in ('yes', 'no'):
299 304 diffopts.git = self.gitmode == 'yes'
300 305 else:
301 306 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
302 307 ' got %s') % self.gitmode)
303 308 if patchfn:
304 309 diffopts = self.patchopts(diffopts, patchfn)
305 310 return diffopts
306 311
307 312 def patchopts(self, diffopts, *patches):
308 313 """Return a copy of input diff options with git set to true if
309 314 referenced patch is a git patch and should be preserved as such.
310 315 """
311 316 diffopts = diffopts.copy()
312 317 if not diffopts.git and self.gitmode == 'keep':
313 318 for patchfn in patches:
314 319 patchf = self.opener(patchfn, 'r')
315 320 # if the patch was a git patch, refresh it as a git patch
316 321 for line in patchf:
317 322 if line.startswith('diff --git'):
318 323 diffopts.git = True
319 324 break
320 325 patchf.close()
321 326 return diffopts
322 327
323 328 def join(self, *p):
324 329 return os.path.join(self.path, *p)
325 330
326 331 def find_series(self, patch):
327 332 def matchpatch(l):
328 333 l = l.split('#', 1)[0]
329 334 return l.strip() == patch
330 335 for index, l in enumerate(self.full_series):
331 336 if matchpatch(l):
332 337 return index
333 338 return None
334 339
335 340 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
336 341
337 342 def parse_series(self):
338 343 self.series = []
339 344 self.series_guards = []
340 345 for l in self.full_series:
341 346 h = l.find('#')
342 347 if h == -1:
343 348 patch = l
344 349 comment = ''
345 350 elif h == 0:
346 351 continue
347 352 else:
348 353 patch = l[:h]
349 354 comment = l[h:]
350 355 patch = patch.strip()
351 356 if patch:
352 357 if patch in self.series:
353 358 raise util.Abort(_('%s appears more than once in %s') %
354 359 (patch, self.join(self.series_path)))
355 360 self.series.append(patch)
356 361 self.series_guards.append(self.guard_re.findall(comment))
357 362
358 363 def check_guard(self, guard):
359 364 if not guard:
360 365 return _('guard cannot be an empty string')
361 366 bad_chars = '# \t\r\n\f'
362 367 first = guard[0]
363 368 if first in '-+':
364 369 return (_('guard %r starts with invalid character: %r') %
365 370 (guard, first))
366 371 for c in bad_chars:
367 372 if c in guard:
368 373 return _('invalid character in guard %r: %r') % (guard, c)
369 374
370 375 def set_active(self, guards):
371 376 for guard in guards:
372 377 bad = self.check_guard(guard)
373 378 if bad:
374 379 raise util.Abort(bad)
375 380 guards = sorted(set(guards))
376 381 self.ui.debug('active guards: %s\n' % ' '.join(guards))
377 382 self.active_guards = guards
378 383 self.guards_dirty = True
379 384
380 385 def active(self):
381 386 if self.active_guards is None:
382 387 self.active_guards = []
383 388 try:
384 389 guards = self.opener(self.guards_path).read().split()
385 390 except IOError, err:
386 391 if err.errno != errno.ENOENT:
387 392 raise
388 393 guards = []
389 394 for i, guard in enumerate(guards):
390 395 bad = self.check_guard(guard)
391 396 if bad:
392 397 self.ui.warn('%s:%d: %s\n' %
393 398 (self.join(self.guards_path), i + 1, bad))
394 399 else:
395 400 self.active_guards.append(guard)
396 401 return self.active_guards
397 402
398 403 def set_guards(self, idx, guards):
399 404 for g in guards:
400 405 if len(g) < 2:
401 406 raise util.Abort(_('guard %r too short') % g)
402 407 if g[0] not in '-+':
403 408 raise util.Abort(_('guard %r starts with invalid char') % g)
404 409 bad = self.check_guard(g[1:])
405 410 if bad:
406 411 raise util.Abort(bad)
407 412 drop = self.guard_re.sub('', self.full_series[idx])
408 413 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
409 414 self.parse_series()
410 415 self.series_dirty = True
411 416
412 417 def pushable(self, idx):
413 418 if isinstance(idx, str):
414 419 idx = self.series.index(idx)
415 420 patchguards = self.series_guards[idx]
416 421 if not patchguards:
417 422 return True, None
418 423 guards = self.active()
419 424 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
420 425 if exactneg:
421 426 return False, exactneg[0]
422 427 pos = [g for g in patchguards if g[0] == '+']
423 428 exactpos = [g for g in pos if g[1:] in guards]
424 429 if pos:
425 430 if exactpos:
426 431 return True, exactpos[0]
427 432 return False, pos
428 433 return True, ''
429 434
430 435 def explain_pushable(self, idx, all_patches=False):
431 436 write = all_patches and self.ui.write or self.ui.warn
432 437 if all_patches or self.ui.verbose:
433 438 if isinstance(idx, str):
434 439 idx = self.series.index(idx)
435 440 pushable, why = self.pushable(idx)
436 441 if all_patches and pushable:
437 442 if why is None:
438 443 write(_('allowing %s - no guards in effect\n') %
439 444 self.series[idx])
440 445 else:
441 446 if not why:
442 447 write(_('allowing %s - no matching negative guards\n') %
443 448 self.series[idx])
444 449 else:
445 450 write(_('allowing %s - guarded by %r\n') %
446 451 (self.series[idx], why))
447 452 if not pushable:
448 453 if why:
449 454 write(_('skipping %s - guarded by %r\n') %
450 455 (self.series[idx], why))
451 456 else:
452 457 write(_('skipping %s - no matching guards\n') %
453 458 self.series[idx])
454 459
455 460 def save_dirty(self):
456 461 def write_list(items, path):
457 462 fp = self.opener(path, 'w')
458 463 for i in items:
459 464 fp.write("%s\n" % i)
460 465 fp.close()
461 466 if self.applied_dirty:
462 467 write_list(map(str, self.applied), self.status_path)
463 468 if self.series_dirty:
464 469 write_list(self.full_series, self.series_path)
465 470 if self.guards_dirty:
466 471 write_list(self.active_guards, self.guards_path)
467 472
468 473 def removeundo(self, repo):
469 474 undo = repo.sjoin('undo')
470 475 if not os.path.exists(undo):
471 476 return
472 477 try:
473 478 os.unlink(undo)
474 479 except OSError, inst:
475 480 self.ui.warn(_('error removing undo: %s\n') % str(inst))
476 481
477 482 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
478 483 fp=None, changes=None, opts={}):
479 484 stat = opts.get('stat')
480 485 m = cmdutil.match(repo, files, opts)
481 486 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
482 487 changes, stat, fp)
483 488
484 489 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
485 490 # first try just applying the patch
486 491 (err, n) = self.apply(repo, [patch], update_status=False,
487 492 strict=True, merge=rev)
488 493
489 494 if err == 0:
490 495 return (err, n)
491 496
492 497 if n is None:
493 498 raise util.Abort(_("apply failed for patch %s") % patch)
494 499
495 500 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
496 501
497 502 # apply failed, strip away that rev and merge.
498 503 hg.clean(repo, head)
499 504 self.strip(repo, n, update=False, backup='strip')
500 505
501 506 ctx = repo[rev]
502 507 ret = hg.merge(repo, rev)
503 508 if ret:
504 509 raise util.Abort(_("update returned %d") % ret)
505 510 n = repo.commit(ctx.description(), ctx.user(), force=True)
506 511 if n is None:
507 512 raise util.Abort(_("repo commit failed"))
508 513 try:
509 514 ph = patchheader(mergeq.join(patch), self.plainmode)
510 515 except:
511 516 raise util.Abort(_("unable to read %s") % patch)
512 517
513 518 diffopts = self.patchopts(diffopts, patch)
514 519 patchf = self.opener(patch, "w")
515 520 comments = str(ph)
516 521 if comments:
517 522 patchf.write(comments)
518 523 self.printdiff(repo, diffopts, head, n, fp=patchf)
519 524 patchf.close()
520 525 self.removeundo(repo)
521 526 return (0, n)
522 527
523 528 def qparents(self, repo, rev=None):
524 529 if rev is None:
525 530 (p1, p2) = repo.dirstate.parents()
526 531 if p2 == nullid:
527 532 return p1
528 533 if not self.applied:
529 534 return None
530 535 return self.applied[-1].node
531 536 p1, p2 = repo.changelog.parents(rev)
532 537 if p2 != nullid and p2 in [x.node for x in self.applied]:
533 538 return p2
534 539 return p1
535 540
536 541 def mergepatch(self, repo, mergeq, series, diffopts):
537 542 if not self.applied:
538 543 # each of the patches merged in will have two parents. This
539 544 # can confuse the qrefresh, qdiff, and strip code because it
540 545 # needs to know which parent is actually in the patch queue.
541 546 # so, we insert a merge marker with only one parent. This way
542 547 # the first patch in the queue is never a merge patch
543 548 #
544 549 pname = ".hg.patches.merge.marker"
545 550 n = repo.commit('[mq]: merge marker', force=True)
546 551 self.removeundo(repo)
547 552 self.applied.append(statusentry(n, pname))
548 553 self.applied_dirty = 1
549 554
550 555 head = self.qparents(repo)
551 556
552 557 for patch in series:
553 558 patch = mergeq.lookup(patch, strict=True)
554 559 if not patch:
555 560 self.ui.warn(_("patch %s does not exist\n") % patch)
556 561 return (1, None)
557 562 pushable, reason = self.pushable(patch)
558 563 if not pushable:
559 564 self.explain_pushable(patch, all_patches=True)
560 565 continue
561 566 info = mergeq.isapplied(patch)
562 567 if not info:
563 568 self.ui.warn(_("patch %s is not applied\n") % patch)
564 569 return (1, None)
565 570 rev = info[1]
566 571 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
567 572 if head:
568 573 self.applied.append(statusentry(head, patch))
569 574 self.applied_dirty = 1
570 575 if err:
571 576 return (err, head)
572 577 self.save_dirty()
573 578 return (0, head)
574 579
575 580 def patch(self, repo, patchfile):
576 581 '''Apply patchfile to the working directory.
577 582 patchfile: name of patch file'''
578 583 files = {}
579 584 try:
580 585 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
581 586 files=files, eolmode=None)
582 587 except Exception, inst:
583 588 self.ui.note(str(inst) + '\n')
584 589 if not self.ui.verbose:
585 590 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
586 591 return (False, files, False)
587 592
588 593 return (True, files, fuzz)
589 594
590 595 def apply(self, repo, series, list=False, update_status=True,
591 596 strict=False, patchdir=None, merge=None, all_files=None):
592 597 wlock = lock = tr = None
593 598 try:
594 599 wlock = repo.wlock()
595 600 lock = repo.lock()
596 601 tr = repo.transaction("qpush")
597 602 try:
598 603 ret = self._apply(repo, series, list, update_status,
599 604 strict, patchdir, merge, all_files=all_files)
600 605 tr.close()
601 606 self.save_dirty()
602 607 return ret
603 608 except:
604 609 try:
605 610 tr.abort()
606 611 finally:
607 612 repo.invalidate()
608 613 repo.dirstate.invalidate()
609 614 raise
610 615 finally:
611 616 del tr
612 617 release(lock, wlock)
613 618 self.removeundo(repo)
614 619
615 620 def _apply(self, repo, series, list=False, update_status=True,
616 621 strict=False, patchdir=None, merge=None, all_files=None):
617 622 '''returns (error, hash)
618 623 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
619 624 # TODO unify with commands.py
620 625 if not patchdir:
621 626 patchdir = self.path
622 627 err = 0
623 628 n = None
624 629 for patchname in series:
625 630 pushable, reason = self.pushable(patchname)
626 631 if not pushable:
627 632 self.explain_pushable(patchname, all_patches=True)
628 633 continue
629 634 self.ui.status(_("applying %s\n") % patchname)
630 635 pf = os.path.join(patchdir, patchname)
631 636
632 637 try:
633 638 ph = patchheader(self.join(patchname), self.plainmode)
634 639 except:
635 640 self.ui.warn(_("unable to read %s\n") % patchname)
636 641 err = 1
637 642 break
638 643
639 644 message = ph.message
640 645 if not message:
641 646 message = "imported patch %s\n" % patchname
642 647 else:
643 648 if list:
644 649 message.append("\nimported patch %s" % patchname)
645 650 message = '\n'.join(message)
646 651
647 652 if ph.haspatch:
648 653 (patcherr, files, fuzz) = self.patch(repo, pf)
649 654 if all_files is not None:
650 655 all_files.update(files)
651 656 patcherr = not patcherr
652 657 else:
653 658 self.ui.warn(_("patch %s is empty\n") % patchname)
654 659 patcherr, files, fuzz = 0, [], 0
655 660
656 661 if merge and files:
657 662 # Mark as removed/merged and update dirstate parent info
658 663 removed = []
659 664 merged = []
660 665 for f in files:
661 666 if os.path.exists(repo.wjoin(f)):
662 667 merged.append(f)
663 668 else:
664 669 removed.append(f)
665 670 for f in removed:
666 671 repo.dirstate.remove(f)
667 672 for f in merged:
668 673 repo.dirstate.merge(f)
669 674 p1, p2 = repo.dirstate.parents()
670 675 repo.dirstate.setparents(p1, merge)
671 676
672 677 files = patch.updatedir(self.ui, repo, files)
673 678 match = cmdutil.matchfiles(repo, files or [])
674 679 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
675 680
676 681 if n is None:
677 682 raise util.Abort(_("repo commit failed"))
678 683
679 684 if update_status:
680 685 self.applied.append(statusentry(n, patchname))
681 686
682 687 if patcherr:
683 688 self.ui.warn(_("patch failed, rejects left in working dir\n"))
684 689 err = 2
685 690 break
686 691
687 692 if fuzz and strict:
688 693 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
689 694 err = 3
690 695 break
691 696 return (err, n)
692 697
693 698 def _cleanup(self, patches, numrevs, keep=False):
694 699 if not keep:
695 700 r = self.qrepo()
696 701 if r:
697 702 r.remove(patches, True)
698 703 else:
699 704 for p in patches:
700 705 os.unlink(self.join(p))
701 706
702 707 if numrevs:
703 708 del self.applied[:numrevs]
704 709 self.applied_dirty = 1
705 710
706 711 for i in sorted([self.find_series(p) for p in patches], reverse=True):
707 712 del self.full_series[i]
708 713 self.parse_series()
709 714 self.series_dirty = 1
710 715
711 716 def _revpatches(self, repo, revs):
712 717 firstrev = repo[self.applied[0].node].rev()
713 718 patches = []
714 719 for i, rev in enumerate(revs):
715 720
716 721 if rev < firstrev:
717 722 raise util.Abort(_('revision %d is not managed') % rev)
718 723
719 724 ctx = repo[rev]
720 725 base = self.applied[i].node
721 726 if ctx.node() != base:
722 727 msg = _('cannot delete revision %d above applied patches')
723 728 raise util.Abort(msg % rev)
724 729
725 730 patch = self.applied[i].name
726 731 for fmt in ('[mq]: %s', 'imported patch %s'):
727 732 if ctx.description() == fmt % patch:
728 733 msg = _('patch %s finalized without changeset message\n')
729 734 repo.ui.status(msg % patch)
730 735 break
731 736
732 737 patches.append(patch)
733 738 return patches
734 739
735 740 def finish(self, repo, revs):
736 741 patches = self._revpatches(repo, sorted(revs))
737 742 self._cleanup(patches, len(patches))
738 743
739 744 def delete(self, repo, patches, opts):
740 745 if not patches and not opts.get('rev'):
741 746 raise util.Abort(_('qdelete requires at least one revision or '
742 747 'patch name'))
743 748
744 749 for patch in patches:
745 750 patch = self.lookup(patch, strict=True)
746 751 info = self.isapplied(patch)
747 752 if info:
748 753 raise util.Abort(_("cannot delete applied patch %s") % patch)
749 754 if patch not in self.series:
750 755 raise util.Abort(_("patch %s not in series file") % patch)
751 756
752 757 patches = list(patches)
753 758 numrevs = 0
754 759 if opts.get('rev'):
755 760 if not self.applied:
756 761 raise util.Abort(_('no patches applied'))
757 762 revs = cmdutil.revrange(repo, opts['rev'])
758 763 if len(revs) > 1 and revs[0] > revs[1]:
759 764 revs.reverse()
760 765 revpatches = self._revpatches(repo, revs)
761 766 patches += revpatches
762 767 numrevs = len(revpatches)
763 768
764 769 self._cleanup(patches, numrevs, opts.get('keep'))
765 770
766 771 def check_toppatch(self, repo):
767 772 if self.applied:
768 773 top = self.applied[-1].node
769 774 patch = self.applied[-1].name
770 775 pp = repo.dirstate.parents()
771 776 if top not in pp:
772 777 raise util.Abort(_("working directory revision is not qtip"))
773 778 return top, patch
774 779 return None, None
775 780
776 781 def check_localchanges(self, repo, force=False, refresh=True):
777 782 m, a, r, d = repo.status()[:4]
778 783 if (m or a or r or d) and not force:
779 784 if refresh:
780 785 raise util.Abort(_("local changes found, refresh first"))
781 786 else:
782 787 raise util.Abort(_("local changes found"))
783 788 return m, a, r, d
784 789
785 790 _reserved = ('series', 'status', 'guards')
786 791 def check_reserved_name(self, name):
787 792 if (name in self._reserved or name.startswith('.hg')
788 793 or name.startswith('.mq') or '#' in name or ':' in name):
789 794 raise util.Abort(_('"%s" cannot be used as the name of a patch')
790 795 % name)
791 796
792 797 def new(self, repo, patchfn, *pats, **opts):
793 798 """options:
794 799 msg: a string or a no-argument function returning a string
795 800 """
796 801 msg = opts.get('msg')
797 802 user = opts.get('user')
798 803 date = opts.get('date')
799 804 if date:
800 805 date = util.parsedate(date)
801 806 diffopts = self.diffopts({'git': opts.get('git')})
802 807 self.check_reserved_name(patchfn)
803 808 if os.path.exists(self.join(patchfn)):
804 809 raise util.Abort(_('patch "%s" already exists') % patchfn)
805 810 if opts.get('include') or opts.get('exclude') or pats:
806 811 match = cmdutil.match(repo, pats, opts)
807 812 # detect missing files in pats
808 813 def badfn(f, msg):
809 814 raise util.Abort('%s: %s' % (f, msg))
810 815 match.bad = badfn
811 816 m, a, r, d = repo.status(match=match)[:4]
812 817 else:
813 818 m, a, r, d = self.check_localchanges(repo, force=True)
814 819 match = cmdutil.matchfiles(repo, m + a + r)
815 820 if len(repo[None].parents()) > 1:
816 821 raise util.Abort(_('cannot manage merge changesets'))
817 822 commitfiles = m + a + r
818 823 self.check_toppatch(repo)
819 824 insert = self.full_series_end()
820 825 wlock = repo.wlock()
821 826 try:
822 827 # if patch file write fails, abort early
823 828 p = self.opener(patchfn, "w")
824 829 try:
825 830 if self.plainmode:
826 831 if user:
827 832 p.write("From: " + user + "\n")
828 833 if not date:
829 834 p.write("\n")
830 835 if date:
831 836 p.write("Date: %d %d\n\n" % date)
832 837 else:
833 838 p.write("# HG changeset patch\n")
834 839 p.write("# Parent "
835 840 + hex(repo[None].parents()[0].node()) + "\n")
836 841 if user:
837 842 p.write("# User " + user + "\n")
838 843 if date:
839 844 p.write("# Date %s %s\n\n" % date)
840 845 if hasattr(msg, '__call__'):
841 846 msg = msg()
842 847 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
843 848 n = repo.commit(commitmsg, user, date, match=match, force=True)
844 849 if n is None:
845 850 raise util.Abort(_("repo commit failed"))
846 851 try:
847 852 self.full_series[insert:insert] = [patchfn]
848 853 self.applied.append(statusentry(n, patchfn))
849 854 self.parse_series()
850 855 self.series_dirty = 1
851 856 self.applied_dirty = 1
852 857 if msg:
853 858 msg = msg + "\n\n"
854 859 p.write(msg)
855 860 if commitfiles:
856 861 parent = self.qparents(repo, n)
857 862 chunks = patch.diff(repo, node1=parent, node2=n,
858 863 match=match, opts=diffopts)
859 864 for chunk in chunks:
860 865 p.write(chunk)
861 866 p.close()
862 867 wlock.release()
863 868 wlock = None
864 869 r = self.qrepo()
865 870 if r:
866 871 r.add([patchfn])
867 872 except:
868 873 repo.rollback()
869 874 raise
870 875 except Exception:
871 876 patchpath = self.join(patchfn)
872 877 try:
873 878 os.unlink(patchpath)
874 879 except:
875 880 self.ui.warn(_('error unlinking %s\n') % patchpath)
876 881 raise
877 882 self.removeundo(repo)
878 883 finally:
879 884 release(wlock)
880 885
881 886 def strip(self, repo, rev, update=True, backup="all", force=None):
882 887 wlock = lock = None
883 888 try:
884 889 wlock = repo.wlock()
885 890 lock = repo.lock()
886 891
887 892 if update:
888 893 self.check_localchanges(repo, force=force, refresh=False)
889 894 urev = self.qparents(repo, rev)
890 895 hg.clean(repo, urev)
891 896 repo.dirstate.write()
892 897
893 898 self.removeundo(repo)
894 899 repair.strip(self.ui, repo, rev, backup)
895 900 # strip may have unbundled a set of backed up revisions after
896 901 # the actual strip
897 902 self.removeundo(repo)
898 903 finally:
899 904 release(lock, wlock)
900 905
901 906 def isapplied(self, patch):
902 907 """returns (index, rev, patch)"""
903 908 for i, a in enumerate(self.applied):
904 909 if a.name == patch:
905 910 return (i, a.node, a.name)
906 911 return None
907 912
908 913 # if the exact patch name does not exist, we try a few
909 914 # variations. If strict is passed, we try only #1
910 915 #
911 916 # 1) a number to indicate an offset in the series file
912 917 # 2) a unique substring of the patch name was given
913 918 # 3) patchname[-+]num to indicate an offset in the series file
914 919 def lookup(self, patch, strict=False):
915 920 patch = patch and str(patch)
916 921
917 922 def partial_name(s):
918 923 if s in self.series:
919 924 return s
920 925 matches = [x for x in self.series if s in x]
921 926 if len(matches) > 1:
922 927 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
923 928 for m in matches:
924 929 self.ui.warn(' %s\n' % m)
925 930 return None
926 931 if matches:
927 932 return matches[0]
928 933 if self.series and self.applied:
929 934 if s == 'qtip':
930 935 return self.series[self.series_end(True)-1]
931 936 if s == 'qbase':
932 937 return self.series[0]
933 938 return None
934 939
935 940 if patch is None:
936 941 return None
937 942 if patch in self.series:
938 943 return patch
939 944
940 945 if not os.path.isfile(self.join(patch)):
941 946 try:
942 947 sno = int(patch)
943 948 except (ValueError, OverflowError):
944 949 pass
945 950 else:
946 951 if -len(self.series) <= sno < len(self.series):
947 952 return self.series[sno]
948 953
949 954 if not strict:
950 955 res = partial_name(patch)
951 956 if res:
952 957 return res
953 958 minus = patch.rfind('-')
954 959 if minus >= 0:
955 960 res = partial_name(patch[:minus])
956 961 if res:
957 962 i = self.series.index(res)
958 963 try:
959 964 off = int(patch[minus + 1:] or 1)
960 965 except (ValueError, OverflowError):
961 966 pass
962 967 else:
963 968 if i - off >= 0:
964 969 return self.series[i - off]
965 970 plus = patch.rfind('+')
966 971 if plus >= 0:
967 972 res = partial_name(patch[:plus])
968 973 if res:
969 974 i = self.series.index(res)
970 975 try:
971 976 off = int(patch[plus + 1:] or 1)
972 977 except (ValueError, OverflowError):
973 978 pass
974 979 else:
975 980 if i + off < len(self.series):
976 981 return self.series[i + off]
977 982 raise util.Abort(_("patch %s not in series") % patch)
978 983
979 984 def push(self, repo, patch=None, force=False, list=False,
980 985 mergeq=None, all=False, move=False):
981 986 diffopts = self.diffopts()
982 987 wlock = repo.wlock()
983 988 try:
984 989 heads = []
985 990 for b, ls in repo.branchmap().iteritems():
986 991 heads += ls
987 992 if not heads:
988 993 heads = [nullid]
989 994 if repo.dirstate.parents()[0] not in heads:
990 995 self.ui.status(_("(working directory not at a head)\n"))
991 996
992 997 if not self.series:
993 998 self.ui.warn(_('no patches in series\n'))
994 999 return 0
995 1000
996 1001 patch = self.lookup(patch)
997 1002 # Suppose our series file is: A B C and the current 'top'
998 1003 # patch is B. qpush C should be performed (moving forward)
999 1004 # qpush B is a NOP (no change) qpush A is an error (can't
1000 1005 # go backwards with qpush)
1001 1006 if patch:
1002 1007 info = self.isapplied(patch)
1003 1008 if info:
1004 1009 if info[0] < len(self.applied) - 1:
1005 1010 raise util.Abort(
1006 1011 _("cannot push to a previous patch: %s") % patch)
1007 1012 self.ui.warn(
1008 1013 _('qpush: %s is already at the top\n') % patch)
1009 1014 return
1010 1015 pushable, reason = self.pushable(patch)
1011 1016 if not pushable:
1012 1017 if reason:
1013 1018 reason = _('guarded by %r') % reason
1014 1019 else:
1015 1020 reason = _('no matching guards')
1016 1021 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1017 1022 return 1
1018 1023 elif all:
1019 1024 patch = self.series[-1]
1020 1025 if self.isapplied(patch):
1021 1026 self.ui.warn(_('all patches are currently applied\n'))
1022 1027 return 0
1023 1028
1024 1029 # Following the above example, starting at 'top' of B:
1025 1030 # qpush should be performed (pushes C), but a subsequent
1026 1031 # qpush without an argument is an error (nothing to
1027 1032 # apply). This allows a loop of "...while hg qpush..." to
1028 1033 # work as it detects an error when done
1029 1034 start = self.series_end()
1030 1035 if start == len(self.series):
1031 1036 self.ui.warn(_('patch series already fully applied\n'))
1032 1037 return 1
1033 1038 if not force:
1034 1039 self.check_localchanges(repo)
1035 1040
1036 1041 if move:
1037 1042 try:
1038 1043 del self.full_series[self.full_series.index(patch, start)]
1039 1044 except ValueError:
1040 1045 raise util.Abort(_("patch '%s' not found") % patch)
1041 1046 self.full_series.insert(start, patch)
1042 1047 self.parse_series()
1043 1048 self.series_dirty = 1
1044 1049
1045 1050 self.applied_dirty = 1
1046 1051 if start > 0:
1047 1052 self.check_toppatch(repo)
1048 1053 if not patch:
1049 1054 patch = self.series[start]
1050 1055 end = start + 1
1051 1056 else:
1052 1057 end = self.series.index(patch, start) + 1
1053 1058
1054 1059 s = self.series[start:end]
1055 1060 all_files = set()
1056 1061 try:
1057 1062 if mergeq:
1058 1063 ret = self.mergepatch(repo, mergeq, s, diffopts)
1059 1064 else:
1060 1065 ret = self.apply(repo, s, list, all_files=all_files)
1061 1066 except:
1062 1067 self.ui.warn(_('cleaning up working directory...'))
1063 1068 node = repo.dirstate.parents()[0]
1064 1069 hg.revert(repo, node, None)
1065 1070 # only remove unknown files that we know we touched or
1066 1071 # created while patching
1067 1072 for f in all_files:
1068 1073 if f not in repo.dirstate:
1069 1074 try:
1070 1075 util.unlink(repo.wjoin(f))
1071 1076 except OSError, inst:
1072 1077 if inst.errno != errno.ENOENT:
1073 1078 raise
1074 1079 self.ui.warn(_('done\n'))
1075 1080 raise
1076 1081
1077 1082 if not self.applied:
1078 1083 return ret[0]
1079 1084 top = self.applied[-1].name
1080 1085 if ret[0] and ret[0] > 1:
1081 1086 msg = _("errors during apply, please fix and refresh %s\n")
1082 1087 self.ui.write(msg % top)
1083 1088 else:
1084 1089 self.ui.write(_("now at: %s\n") % top)
1085 1090 return ret[0]
1086 1091
1087 1092 finally:
1088 1093 wlock.release()
1089 1094
1090 1095 def pop(self, repo, patch=None, force=False, update=True, all=False):
1091 1096 wlock = repo.wlock()
1092 1097 try:
1093 1098 if patch:
1094 1099 # index, rev, patch
1095 1100 info = self.isapplied(patch)
1096 1101 if not info:
1097 1102 patch = self.lookup(patch)
1098 1103 info = self.isapplied(patch)
1099 1104 if not info:
1100 1105 raise util.Abort(_("patch %s is not applied") % patch)
1101 1106
1102 1107 if not self.applied:
1103 1108 # Allow qpop -a to work repeatedly,
1104 1109 # but not qpop without an argument
1105 1110 self.ui.warn(_("no patches applied\n"))
1106 1111 return not all
1107 1112
1108 1113 if all:
1109 1114 start = 0
1110 1115 elif patch:
1111 1116 start = info[0] + 1
1112 1117 else:
1113 1118 start = len(self.applied) - 1
1114 1119
1115 1120 if start >= len(self.applied):
1116 1121 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1117 1122 return
1118 1123
1119 1124 if not update:
1120 1125 parents = repo.dirstate.parents()
1121 1126 rr = [x.node for x in self.applied]
1122 1127 for p in parents:
1123 1128 if p in rr:
1124 1129 self.ui.warn(_("qpop: forcing dirstate update\n"))
1125 1130 update = True
1126 1131 else:
1127 1132 parents = [p.node() for p in repo[None].parents()]
1128 1133 needupdate = False
1129 1134 for entry in self.applied[start:]:
1130 1135 if entry.node in parents:
1131 1136 needupdate = True
1132 1137 break
1133 1138 update = needupdate
1134 1139
1135 1140 if not force and update:
1136 1141 self.check_localchanges(repo)
1137 1142
1138 1143 self.applied_dirty = 1
1139 1144 end = len(self.applied)
1140 1145 rev = self.applied[start].node
1141 1146 if update:
1142 1147 top = self.check_toppatch(repo)[0]
1143 1148
1144 1149 try:
1145 1150 heads = repo.changelog.heads(rev)
1146 1151 except error.LookupError:
1147 1152 node = short(rev)
1148 1153 raise util.Abort(_('trying to pop unknown node %s') % node)
1149 1154
1150 1155 if heads != [self.applied[-1].node]:
1151 1156 raise util.Abort(_("popping would remove a revision not "
1152 1157 "managed by this patch queue"))
1153 1158
1154 1159 # we know there are no local changes, so we can make a simplified
1155 1160 # form of hg.update.
1156 1161 if update:
1157 1162 qp = self.qparents(repo, rev)
1158 1163 ctx = repo[qp]
1159 1164 m, a, r, d = repo.status(qp, top)[:4]
1160 1165 if d:
1161 1166 raise util.Abort(_("deletions found between repo revs"))
1162 1167 for f in a:
1163 1168 try:
1164 1169 util.unlink(repo.wjoin(f))
1165 1170 except OSError, e:
1166 1171 if e.errno != errno.ENOENT:
1167 1172 raise
1168 1173 repo.dirstate.forget(f)
1169 1174 for f in m + r:
1170 1175 fctx = ctx[f]
1171 1176 repo.wwrite(f, fctx.data(), fctx.flags())
1172 1177 repo.dirstate.normal(f)
1173 1178 repo.dirstate.setparents(qp, nullid)
1174 1179 for patch in reversed(self.applied[start:end]):
1175 1180 self.ui.status(_("popping %s\n") % patch.name)
1176 1181 del self.applied[start:end]
1177 1182 self.strip(repo, rev, update=False, backup='strip')
1178 1183 if self.applied:
1179 1184 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1180 1185 else:
1181 1186 self.ui.write(_("patch queue now empty\n"))
1182 1187 finally:
1183 1188 wlock.release()
1184 1189
1185 1190 def diff(self, repo, pats, opts):
1186 1191 top, patch = self.check_toppatch(repo)
1187 1192 if not top:
1188 1193 self.ui.write(_("no patches applied\n"))
1189 1194 return
1190 1195 qp = self.qparents(repo, top)
1191 1196 if opts.get('reverse'):
1192 1197 node1, node2 = None, qp
1193 1198 else:
1194 1199 node1, node2 = qp, None
1195 1200 diffopts = self.diffopts(opts, patch)
1196 1201 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1197 1202
1198 1203 def refresh(self, repo, pats=None, **opts):
1199 1204 if not self.applied:
1200 1205 self.ui.write(_("no patches applied\n"))
1201 1206 return 1
1202 1207 msg = opts.get('msg', '').rstrip()
1203 1208 newuser = opts.get('user')
1204 1209 newdate = opts.get('date')
1205 1210 if newdate:
1206 1211 newdate = '%d %d' % util.parsedate(newdate)
1207 1212 wlock = repo.wlock()
1208 1213
1209 1214 try:
1210 1215 self.check_toppatch(repo)
1211 1216 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1212 1217 if repo.changelog.heads(top) != [top]:
1213 1218 raise util.Abort(_("cannot refresh a revision with children"))
1214 1219
1215 1220 cparents = repo.changelog.parents(top)
1216 1221 patchparent = self.qparents(repo, top)
1217 1222 ph = patchheader(self.join(patchfn), self.plainmode)
1218 1223 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1219 1224 if msg:
1220 1225 ph.setmessage(msg)
1221 1226 if newuser:
1222 1227 ph.setuser(newuser)
1223 1228 if newdate:
1224 1229 ph.setdate(newdate)
1225 1230 ph.setparent(hex(patchparent))
1226 1231
1227 1232 # only commit new patch when write is complete
1228 1233 patchf = self.opener(patchfn, 'w', atomictemp=True)
1229 1234
1230 1235 comments = str(ph)
1231 1236 if comments:
1232 1237 patchf.write(comments)
1233 1238
1234 1239 # update the dirstate in place, strip off the qtip commit
1235 1240 # and then commit.
1236 1241 #
1237 1242 # this should really read:
1238 1243 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1239 1244 # but we do it backwards to take advantage of manifest/chlog
1240 1245 # caching against the next repo.status call
1241 1246 mm, aa, dd, aa2 = repo.status(patchparent, top)[:4]
1242 1247 changes = repo.changelog.read(top)
1243 1248 man = repo.manifest.read(changes[0])
1244 1249 aaa = aa[:]
1245 1250 matchfn = cmdutil.match(repo, pats, opts)
1246 1251 # in short mode, we only diff the files included in the
1247 1252 # patch already plus specified files
1248 1253 if opts.get('short'):
1249 1254 # if amending a patch, we start with existing
1250 1255 # files plus specified files - unfiltered
1251 1256 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1252 1257 # filter with inc/exl options
1253 1258 matchfn = cmdutil.match(repo, opts=opts)
1254 1259 else:
1255 1260 match = cmdutil.matchall(repo)
1256 1261 m, a, r, d = repo.status(match=match)[:4]
1257 1262
1258 1263 # we might end up with files that were added between
1259 1264 # qtip and the dirstate parent, but then changed in the
1260 1265 # local dirstate. in this case, we want them to only
1261 1266 # show up in the added section
1262 1267 for x in m:
1263 1268 if x not in aa:
1264 1269 mm.append(x)
1265 1270 # we might end up with files added by the local dirstate that
1266 1271 # were deleted by the patch. In this case, they should only
1267 1272 # show up in the changed section.
1268 1273 for x in a:
1269 1274 if x in dd:
1270 1275 del dd[dd.index(x)]
1271 1276 mm.append(x)
1272 1277 else:
1273 1278 aa.append(x)
1274 1279 # make sure any files deleted in the local dirstate
1275 1280 # are not in the add or change column of the patch
1276 1281 forget = []
1277 1282 for x in d + r:
1278 1283 if x in aa:
1279 1284 del aa[aa.index(x)]
1280 1285 forget.append(x)
1281 1286 continue
1282 1287 elif x in mm:
1283 1288 del mm[mm.index(x)]
1284 1289 dd.append(x)
1285 1290
1286 1291 m = list(set(mm))
1287 1292 r = list(set(dd))
1288 1293 a = list(set(aa))
1289 1294 c = [filter(matchfn, l) for l in (m, a, r)]
1290 1295 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1291 1296 chunks = patch.diff(repo, patchparent, match=match,
1292 1297 changes=c, opts=diffopts)
1293 1298 for chunk in chunks:
1294 1299 patchf.write(chunk)
1295 1300
1296 1301 try:
1297 1302 if diffopts.git or diffopts.upgrade:
1298 1303 copies = {}
1299 1304 for dst in a:
1300 1305 src = repo.dirstate.copied(dst)
1301 1306 # during qfold, the source file for copies may
1302 1307 # be removed. Treat this as a simple add.
1303 1308 if src is not None and src in repo.dirstate:
1304 1309 copies.setdefault(src, []).append(dst)
1305 1310 repo.dirstate.add(dst)
1306 1311 # remember the copies between patchparent and qtip
1307 1312 for dst in aaa:
1308 1313 f = repo.file(dst)
1309 1314 src = f.renamed(man[dst])
1310 1315 if src:
1311 1316 copies.setdefault(src[0], []).extend(
1312 1317 copies.get(dst, []))
1313 1318 if dst in a:
1314 1319 copies[src[0]].append(dst)
1315 1320 # we can't copy a file created by the patch itself
1316 1321 if dst in copies:
1317 1322 del copies[dst]
1318 1323 for src, dsts in copies.iteritems():
1319 1324 for dst in dsts:
1320 1325 repo.dirstate.copy(src, dst)
1321 1326 else:
1322 1327 for dst in a:
1323 1328 repo.dirstate.add(dst)
1324 1329 # Drop useless copy information
1325 1330 for f in list(repo.dirstate.copies()):
1326 1331 repo.dirstate.copy(None, f)
1327 1332 for f in r:
1328 1333 repo.dirstate.remove(f)
1329 1334 # if the patch excludes a modified file, mark that
1330 1335 # file with mtime=0 so status can see it.
1331 1336 mm = []
1332 1337 for i in xrange(len(m)-1, -1, -1):
1333 1338 if not matchfn(m[i]):
1334 1339 mm.append(m[i])
1335 1340 del m[i]
1336 1341 for f in m:
1337 1342 repo.dirstate.normal(f)
1338 1343 for f in mm:
1339 1344 repo.dirstate.normallookup(f)
1340 1345 for f in forget:
1341 1346 repo.dirstate.forget(f)
1342 1347
1343 1348 if not msg:
1344 1349 if not ph.message:
1345 1350 message = "[mq]: %s\n" % patchfn
1346 1351 else:
1347 1352 message = "\n".join(ph.message)
1348 1353 else:
1349 1354 message = msg
1350 1355
1351 1356 user = ph.user or changes[1]
1352 1357
1353 1358 # assumes strip can roll itself back if interrupted
1354 1359 repo.dirstate.setparents(*cparents)
1355 1360 self.applied.pop()
1356 1361 self.applied_dirty = 1
1357 1362 self.strip(repo, top, update=False,
1358 1363 backup='strip')
1359 1364 except:
1360 1365 repo.dirstate.invalidate()
1361 1366 raise
1362 1367
1363 1368 try:
1364 1369 # might be nice to attempt to roll back strip after this
1365 1370 patchf.rename()
1366 1371 n = repo.commit(message, user, ph.date, match=match,
1367 1372 force=True)
1368 1373 self.applied.append(statusentry(n, patchfn))
1369 1374 except:
1370 1375 ctx = repo[cparents[0]]
1371 1376 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1372 1377 self.save_dirty()
1373 1378 self.ui.warn(_('refresh interrupted while patch was popped! '
1374 1379 '(revert --all, qpush to recover)\n'))
1375 1380 raise
1376 1381 finally:
1377 1382 wlock.release()
1378 1383 self.removeundo(repo)
1379 1384
1380 1385 def init(self, repo, create=False):
1381 1386 if not create and os.path.isdir(self.path):
1382 1387 raise util.Abort(_("patch queue directory already exists"))
1383 1388 try:
1384 1389 os.mkdir(self.path)
1385 1390 except OSError, inst:
1386 1391 if inst.errno != errno.EEXIST or not create:
1387 1392 raise
1388 1393 if create:
1389 1394 return self.qrepo(create=True)
1390 1395
1391 1396 def unapplied(self, repo, patch=None):
1392 1397 if patch and patch not in self.series:
1393 1398 raise util.Abort(_("patch %s is not in series file") % patch)
1394 1399 if not patch:
1395 1400 start = self.series_end()
1396 1401 else:
1397 1402 start = self.series.index(patch) + 1
1398 1403 unapplied = []
1399 1404 for i in xrange(start, len(self.series)):
1400 1405 pushable, reason = self.pushable(i)
1401 1406 if pushable:
1402 1407 unapplied.append((i, self.series[i]))
1403 1408 self.explain_pushable(i)
1404 1409 return unapplied
1405 1410
1406 1411 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1407 1412 summary=False):
1408 1413 def displayname(pfx, patchname, state):
1409 1414 if pfx:
1410 1415 self.ui.write(pfx)
1411 1416 if summary:
1412 1417 ph = patchheader(self.join(patchname), self.plainmode)
1413 1418 msg = ph.message and ph.message[0] or ''
1414 1419 if not self.ui.plain():
1415 1420 width = util.termwidth() - len(pfx) - len(patchname) - 2
1416 1421 if width > 0:
1417 1422 msg = util.ellipsis(msg, width)
1418 1423 else:
1419 1424 msg = ''
1420 1425 self.ui.write(patchname, label='qseries.' + state)
1421 1426 self.ui.write(': ')
1422 1427 self.ui.write(msg, label='qseries.message.' + state)
1423 1428 else:
1424 1429 self.ui.write(patchname, label='qseries.' + state)
1425 1430 self.ui.write('\n')
1426 1431
1427 1432 applied = set([p.name for p in self.applied])
1428 1433 if length is None:
1429 1434 length = len(self.series) - start
1430 1435 if not missing:
1431 1436 if self.ui.verbose:
1432 1437 idxwidth = len(str(start + length - 1))
1433 1438 for i in xrange(start, start + length):
1434 1439 patch = self.series[i]
1435 1440 if patch in applied:
1436 1441 char, state = 'A', 'applied'
1437 1442 elif self.pushable(i)[0]:
1438 1443 char, state = 'U', 'unapplied'
1439 1444 else:
1440 1445 char, state = 'G', 'guarded'
1441 1446 pfx = ''
1442 1447 if self.ui.verbose:
1443 1448 pfx = '%*d %s ' % (idxwidth, i, char)
1444 1449 elif status and status != char:
1445 1450 continue
1446 1451 displayname(pfx, patch, state)
1447 1452 else:
1448 1453 msng_list = []
1449 1454 for root, dirs, files in os.walk(self.path):
1450 1455 d = root[len(self.path) + 1:]
1451 1456 for f in files:
1452 1457 fl = os.path.join(d, f)
1453 1458 if (fl not in self.series and
1454 1459 fl not in (self.status_path, self.series_path,
1455 1460 self.guards_path)
1456 1461 and not fl.startswith('.')):
1457 1462 msng_list.append(fl)
1458 1463 for x in sorted(msng_list):
1459 1464 pfx = self.ui.verbose and ('D ') or ''
1460 1465 displayname(pfx, x, 'missing')
1461 1466
1462 1467 def issaveline(self, l):
1463 1468 if l.name == '.hg.patches.save.line':
1464 1469 return True
1465 1470
1466 1471 def qrepo(self, create=False):
1467 1472 if create or os.path.isdir(self.join(".hg")):
1468 1473 return hg.repository(self.ui, path=self.path, create=create)
1469 1474
1470 1475 def restore(self, repo, rev, delete=None, qupdate=None):
1471 1476 desc = repo[rev].description().strip()
1472 1477 lines = desc.splitlines()
1473 1478 i = 0
1474 1479 datastart = None
1475 1480 series = []
1476 1481 applied = []
1477 1482 qpp = None
1478 1483 for i, line in enumerate(lines):
1479 1484 if line == 'Patch Data:':
1480 1485 datastart = i + 1
1481 1486 elif line.startswith('Dirstate:'):
1482 1487 l = line.rstrip()
1483 1488 l = l[10:].split(' ')
1484 1489 qpp = [bin(x) for x in l]
1485 1490 elif datastart != None:
1486 1491 l = line.rstrip()
1487 1492 n, name = l.split(':', 1)
1488 1493 if n:
1489 1494 applied.append(statusentry(bin(n), name))
1490 1495 else:
1491 1496 series.append(l)
1492 1497 if datastart is None:
1493 1498 self.ui.warn(_("No saved patch data found\n"))
1494 1499 return 1
1495 1500 self.ui.warn(_("restoring status: %s\n") % lines[0])
1496 1501 self.full_series = series
1497 1502 self.applied = applied
1498 1503 self.parse_series()
1499 1504 self.series_dirty = 1
1500 1505 self.applied_dirty = 1
1501 1506 heads = repo.changelog.heads()
1502 1507 if delete:
1503 1508 if rev not in heads:
1504 1509 self.ui.warn(_("save entry has children, leaving it alone\n"))
1505 1510 else:
1506 1511 self.ui.warn(_("removing save entry %s\n") % short(rev))
1507 1512 pp = repo.dirstate.parents()
1508 1513 if rev in pp:
1509 1514 update = True
1510 1515 else:
1511 1516 update = False
1512 1517 self.strip(repo, rev, update=update, backup='strip')
1513 1518 if qpp:
1514 1519 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1515 1520 (short(qpp[0]), short(qpp[1])))
1516 1521 if qupdate:
1517 1522 self.ui.status(_("queue directory updating\n"))
1518 1523 r = self.qrepo()
1519 1524 if not r:
1520 1525 self.ui.warn(_("Unable to load queue repository\n"))
1521 1526 return 1
1522 1527 hg.clean(r, qpp[0])
1523 1528
1524 1529 def save(self, repo, msg=None):
1525 1530 if not self.applied:
1526 1531 self.ui.warn(_("save: no patches applied, exiting\n"))
1527 1532 return 1
1528 1533 if self.issaveline(self.applied[-1]):
1529 1534 self.ui.warn(_("status is already saved\n"))
1530 1535 return 1
1531 1536
1532 1537 if not msg:
1533 1538 msg = _("hg patches saved state")
1534 1539 else:
1535 1540 msg = "hg patches: " + msg.rstrip('\r\n')
1536 1541 r = self.qrepo()
1537 1542 if r:
1538 1543 pp = r.dirstate.parents()
1539 1544 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1540 1545 msg += "\n\nPatch Data:\n"
1541 1546 msg += ''.join('%s\n' % x for x in self.applied)
1542 1547 msg += ''.join(':%s\n' % x for x in self.full_series)
1543 1548 n = repo.commit(msg, force=True)
1544 1549 if not n:
1545 1550 self.ui.warn(_("repo commit failed\n"))
1546 1551 return 1
1547 1552 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1548 1553 self.applied_dirty = 1
1549 1554 self.removeundo(repo)
1550 1555
1551 1556 def full_series_end(self):
1552 1557 if self.applied:
1553 1558 p = self.applied[-1].name
1554 1559 end = self.find_series(p)
1555 1560 if end is None:
1556 1561 return len(self.full_series)
1557 1562 return end + 1
1558 1563 return 0
1559 1564
1560 1565 def series_end(self, all_patches=False):
1561 1566 """If all_patches is False, return the index of the next pushable patch
1562 1567 in the series, or the series length. If all_patches is True, return the
1563 1568 index of the first patch past the last applied one.
1564 1569 """
1565 1570 end = 0
1566 1571 def next(start):
1567 1572 if all_patches or start >= len(self.series):
1568 1573 return start
1569 1574 for i in xrange(start, len(self.series)):
1570 1575 p, reason = self.pushable(i)
1571 1576 if p:
1572 1577 break
1573 1578 self.explain_pushable(i)
1574 1579 return i
1575 1580 if self.applied:
1576 1581 p = self.applied[-1].name
1577 1582 try:
1578 1583 end = self.series.index(p)
1579 1584 except ValueError:
1580 1585 return 0
1581 1586 return next(end + 1)
1582 1587 return next(end)
1583 1588
1584 1589 def appliedname(self, index):
1585 1590 pname = self.applied[index].name
1586 1591 if not self.ui.verbose:
1587 1592 p = pname
1588 1593 else:
1589 1594 p = str(self.series.index(pname)) + " " + pname
1590 1595 return p
1591 1596
1592 1597 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1593 1598 force=None, git=False):
1594 1599 def checkseries(patchname):
1595 1600 if patchname in self.series:
1596 1601 raise util.Abort(_('patch %s is already in the series file')
1597 1602 % patchname)
1598 1603 def checkfile(patchname):
1599 1604 if not force and os.path.exists(self.join(patchname)):
1600 1605 raise util.Abort(_('patch "%s" already exists')
1601 1606 % patchname)
1602 1607
1603 1608 if rev:
1604 1609 if files:
1605 1610 raise util.Abort(_('option "-r" not valid when importing '
1606 1611 'files'))
1607 1612 rev = cmdutil.revrange(repo, rev)
1608 1613 rev.sort(reverse=True)
1609 1614 if (len(files) > 1 or len(rev) > 1) and patchname:
1610 1615 raise util.Abort(_('option "-n" not valid when importing multiple '
1611 1616 'patches'))
1612 1617 added = []
1613 1618 if rev:
1614 1619 # If mq patches are applied, we can only import revisions
1615 1620 # that form a linear path to qbase.
1616 1621 # Otherwise, they should form a linear path to a head.
1617 1622 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1618 1623 if len(heads) > 1:
1619 1624 raise util.Abort(_('revision %d is the root of more than one '
1620 1625 'branch') % rev[-1])
1621 1626 if self.applied:
1622 1627 base = repo.changelog.node(rev[0])
1623 1628 if base in [n.node for n in self.applied]:
1624 1629 raise util.Abort(_('revision %d is already managed')
1625 1630 % rev[0])
1626 1631 if heads != [self.applied[-1].node]:
1627 1632 raise util.Abort(_('revision %d is not the parent of '
1628 1633 'the queue') % rev[0])
1629 1634 base = repo.changelog.rev(self.applied[0].node)
1630 1635 lastparent = repo.changelog.parentrevs(base)[0]
1631 1636 else:
1632 1637 if heads != [repo.changelog.node(rev[0])]:
1633 1638 raise util.Abort(_('revision %d has unmanaged children')
1634 1639 % rev[0])
1635 1640 lastparent = None
1636 1641
1637 1642 diffopts = self.diffopts({'git': git})
1638 1643 for r in rev:
1639 1644 p1, p2 = repo.changelog.parentrevs(r)
1640 1645 n = repo.changelog.node(r)
1641 1646 if p2 != nullrev:
1642 1647 raise util.Abort(_('cannot import merge revision %d') % r)
1643 1648 if lastparent and lastparent != r:
1644 1649 raise util.Abort(_('revision %d is not the parent of %d')
1645 1650 % (r, lastparent))
1646 1651 lastparent = p1
1647 1652
1648 1653 if not patchname:
1649 1654 patchname = normname('%d.diff' % r)
1650 1655 self.check_reserved_name(patchname)
1651 1656 checkseries(patchname)
1652 1657 checkfile(patchname)
1653 1658 self.full_series.insert(0, patchname)
1654 1659
1655 1660 patchf = self.opener(patchname, "w")
1656 1661 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1657 1662 patchf.close()
1658 1663
1659 1664 se = statusentry(n, patchname)
1660 1665 self.applied.insert(0, se)
1661 1666
1662 1667 added.append(patchname)
1663 1668 patchname = None
1664 1669 self.parse_series()
1665 1670 self.applied_dirty = 1
1666 1671
1667 1672 for i, filename in enumerate(files):
1668 1673 if existing:
1669 1674 if filename == '-':
1670 1675 raise util.Abort(_('-e is incompatible with import from -'))
1671 1676 if not patchname:
1672 1677 patchname = normname(filename)
1673 1678 self.check_reserved_name(patchname)
1674 1679 if not os.path.isfile(self.join(patchname)):
1675 1680 raise util.Abort(_("patch %s does not exist") % patchname)
1676 1681 else:
1677 1682 try:
1678 1683 if filename == '-':
1679 1684 if not patchname:
1680 1685 raise util.Abort(
1681 1686 _('need --name to import a patch from -'))
1682 1687 text = sys.stdin.read()
1683 1688 else:
1684 1689 text = url.open(self.ui, filename).read()
1685 1690 except (OSError, IOError):
1686 1691 raise util.Abort(_("unable to read %s") % filename)
1687 1692 if not patchname:
1688 1693 patchname = normname(os.path.basename(filename))
1689 1694 self.check_reserved_name(patchname)
1690 1695 checkfile(patchname)
1691 1696 patchf = self.opener(patchname, "w")
1692 1697 patchf.write(text)
1693 1698 if not force:
1694 1699 checkseries(patchname)
1695 1700 if patchname not in self.series:
1696 1701 index = self.full_series_end() + i
1697 1702 self.full_series[index:index] = [patchname]
1698 1703 self.parse_series()
1699 1704 self.ui.warn(_("adding %s to series file\n") % patchname)
1700 1705 added.append(patchname)
1701 1706 patchname = None
1702 1707 self.series_dirty = 1
1703 1708 qrepo = self.qrepo()
1704 1709 if qrepo:
1705 1710 qrepo.add(added)
1706 1711
1707 1712 def delete(ui, repo, *patches, **opts):
1708 1713 """remove patches from queue
1709 1714
1710 1715 The patches must not be applied, and at least one patch is required. With
1711 1716 -k/--keep, the patch files are preserved in the patch directory.
1712 1717
1713 1718 To stop managing a patch and move it into permanent history,
1714 1719 use the qfinish command."""
1715 1720 q = repo.mq
1716 1721 q.delete(repo, patches, opts)
1717 1722 q.save_dirty()
1718 1723 return 0
1719 1724
1720 1725 def applied(ui, repo, patch=None, **opts):
1721 1726 """print the patches already applied"""
1722 1727
1723 1728 q = repo.mq
1724 1729 l = len(q.applied)
1725 1730
1726 1731 if patch:
1727 1732 if patch not in q.series:
1728 1733 raise util.Abort(_("patch %s is not in series file") % patch)
1729 1734 end = q.series.index(patch) + 1
1730 1735 else:
1731 1736 end = q.series_end(True)
1732 1737
1733 1738 if opts.get('last') and not end:
1734 1739 ui.write(_("no patches applied\n"))
1735 1740 return 1
1736 1741 elif opts.get('last') and end == 1:
1737 1742 ui.write(_("only one patch applied\n"))
1738 1743 return 1
1739 1744 elif opts.get('last'):
1740 1745 start = end - 2
1741 1746 end = 1
1742 1747 else:
1743 1748 start = 0
1744 1749
1745 1750 return q.qseries(repo, length=end, start=start, status='A',
1746 1751 summary=opts.get('summary'))
1747 1752
1748 1753 def unapplied(ui, repo, patch=None, **opts):
1749 1754 """print the patches not yet applied"""
1750 1755
1751 1756 q = repo.mq
1752 1757 if patch:
1753 1758 if patch not in q.series:
1754 1759 raise util.Abort(_("patch %s is not in series file") % patch)
1755 1760 start = q.series.index(patch) + 1
1756 1761 else:
1757 1762 start = q.series_end(True)
1758 1763
1759 1764 if start == len(q.series) and opts.get('first'):
1760 1765 ui.write(_("all patches applied\n"))
1761 1766 return 1
1762 1767
1763 1768 length = opts.get('first') and 1 or None
1764 1769 return q.qseries(repo, start=start, length=length, status='U',
1765 1770 summary=opts.get('summary'))
1766 1771
1767 1772 def qimport(ui, repo, *filename, **opts):
1768 1773 """import a patch
1769 1774
1770 1775 The patch is inserted into the series after the last applied
1771 1776 patch. If no patches have been applied, qimport prepends the patch
1772 1777 to the series.
1773 1778
1774 1779 The patch will have the same name as its source file unless you
1775 1780 give it a new one with -n/--name.
1776 1781
1777 1782 You can register an existing patch inside the patch directory with
1778 1783 the -e/--existing flag.
1779 1784
1780 1785 With -f/--force, an existing patch of the same name will be
1781 1786 overwritten.
1782 1787
1783 1788 An existing changeset may be placed under mq control with -r/--rev
1784 1789 (e.g. qimport --rev tip -n patch will place tip under mq control).
1785 1790 With -g/--git, patches imported with --rev will use the git diff
1786 1791 format. See the diffs help topic for information on why this is
1787 1792 important for preserving rename/copy information and permission
1788 1793 changes.
1789 1794
1790 1795 To import a patch from standard input, pass - as the patch file.
1791 1796 When importing from standard input, a patch name must be specified
1792 1797 using the --name flag.
1793 1798 """
1794 1799 q = repo.mq
1795 1800 q.qimport(repo, filename, patchname=opts['name'],
1796 1801 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1797 1802 git=opts['git'])
1798 1803 q.save_dirty()
1799 1804
1800 1805 if opts.get('push') and not opts.get('rev'):
1801 1806 return q.push(repo, None)
1802 1807 return 0
1803 1808
1804 1809 def qinit(ui, repo, create):
1805 1810 """initialize a new queue repository
1806 1811
1807 1812 This command also creates a series file for ordering patches, and
1808 1813 an mq-specific .hgignore file in the queue repository, to exclude
1809 1814 the status and guards files (these contain mostly transient state)."""
1810 1815 q = repo.mq
1811 1816 r = q.init(repo, create)
1812 1817 q.save_dirty()
1813 1818 if r:
1814 1819 if not os.path.exists(r.wjoin('.hgignore')):
1815 1820 fp = r.wopener('.hgignore', 'w')
1816 1821 fp.write('^\\.hg\n')
1817 1822 fp.write('^\\.mq\n')
1818 1823 fp.write('syntax: glob\n')
1819 1824 fp.write('status\n')
1820 1825 fp.write('guards\n')
1821 1826 fp.close()
1822 1827 if not os.path.exists(r.wjoin('series')):
1823 1828 r.wopener('series', 'w').close()
1824 1829 r.add(['.hgignore', 'series'])
1825 1830 commands.add(ui, r)
1826 1831 return 0
1827 1832
1828 1833 def init(ui, repo, **opts):
1829 1834 """init a new queue repository (DEPRECATED)
1830 1835
1831 1836 The queue repository is unversioned by default. If
1832 1837 -c/--create-repo is specified, qinit will create a separate nested
1833 1838 repository for patches (qinit -c may also be run later to convert
1834 1839 an unversioned patch repository into a versioned one). You can use
1835 1840 qcommit to commit changes to this queue repository.
1836 1841
1837 1842 This command is deprecated. Without -c, it's implied by other relevant
1838 1843 commands. With -c, use :hg:`init --mq` instead."""
1839 1844 return qinit(ui, repo, create=opts['create_repo'])
1840 1845
1841 1846 def clone(ui, source, dest=None, **opts):
1842 1847 '''clone main and patch repository at same time
1843 1848
1844 1849 If source is local, destination will have no patches applied. If
1845 1850 source is remote, this command can not check if patches are
1846 1851 applied in source, so cannot guarantee that patches are not
1847 1852 applied in destination. If you clone remote repository, be sure
1848 1853 before that it has no patches applied.
1849 1854
1850 1855 Source patch repository is looked for in <src>/.hg/patches by
1851 1856 default. Use -p <url> to change.
1852 1857
1853 1858 The patch directory must be a nested Mercurial repository, as
1854 1859 would be created by :hg:`init --mq`.
1855 1860 '''
1856 1861 def patchdir(repo):
1857 1862 url = repo.url()
1858 1863 if url.endswith('/'):
1859 1864 url = url[:-1]
1860 1865 return url + '/.hg/patches'
1861 1866 if dest is None:
1862 1867 dest = hg.defaultdest(source)
1863 1868 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1864 1869 if opts['patches']:
1865 1870 patchespath = ui.expandpath(opts['patches'])
1866 1871 else:
1867 1872 patchespath = patchdir(sr)
1868 1873 try:
1869 1874 hg.repository(ui, patchespath)
1870 1875 except error.RepoError:
1871 1876 raise util.Abort(_('versioned patch repository not found'
1872 1877 ' (see init --mq)'))
1873 1878 qbase, destrev = None, None
1874 1879 if sr.local():
1875 1880 if sr.mq.applied:
1876 1881 qbase = sr.mq.applied[0].node
1877 1882 if not hg.islocal(dest):
1878 1883 heads = set(sr.heads())
1879 1884 destrev = list(heads.difference(sr.heads(qbase)))
1880 1885 destrev.append(sr.changelog.parents(qbase)[0])
1881 1886 elif sr.capable('lookup'):
1882 1887 try:
1883 1888 qbase = sr.lookup('qbase')
1884 1889 except error.RepoError:
1885 1890 pass
1886 1891 ui.note(_('cloning main repository\n'))
1887 1892 sr, dr = hg.clone(ui, sr.url(), dest,
1888 1893 pull=opts['pull'],
1889 1894 rev=destrev,
1890 1895 update=False,
1891 1896 stream=opts['uncompressed'])
1892 1897 ui.note(_('cloning patch repository\n'))
1893 1898 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1894 1899 pull=opts['pull'], update=not opts['noupdate'],
1895 1900 stream=opts['uncompressed'])
1896 1901 if dr.local():
1897 1902 if qbase:
1898 1903 ui.note(_('stripping applied patches from destination '
1899 1904 'repository\n'))
1900 1905 dr.mq.strip(dr, qbase, update=False, backup=None)
1901 1906 if not opts['noupdate']:
1902 1907 ui.note(_('updating destination repository\n'))
1903 1908 hg.update(dr, dr.changelog.tip())
1904 1909
1905 1910 def commit(ui, repo, *pats, **opts):
1906 1911 """commit changes in the queue repository (DEPRECATED)
1907 1912
1908 1913 This command is deprecated; use :hg:`commit --mq` instead."""
1909 1914 q = repo.mq
1910 1915 r = q.qrepo()
1911 1916 if not r:
1912 1917 raise util.Abort('no queue repository')
1913 1918 commands.commit(r.ui, r, *pats, **opts)
1914 1919
1915 1920 def series(ui, repo, **opts):
1916 1921 """print the entire series file"""
1917 1922 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1918 1923 return 0
1919 1924
1920 1925 def top(ui, repo, **opts):
1921 1926 """print the name of the current patch"""
1922 1927 q = repo.mq
1923 1928 t = q.applied and q.series_end(True) or 0
1924 1929 if t:
1925 1930 return q.qseries(repo, start=t - 1, length=1, status='A',
1926 1931 summary=opts.get('summary'))
1927 1932 else:
1928 1933 ui.write(_("no patches applied\n"))
1929 1934 return 1
1930 1935
1931 1936 def next(ui, repo, **opts):
1932 1937 """print the name of the next patch"""
1933 1938 q = repo.mq
1934 1939 end = q.series_end()
1935 1940 if end == len(q.series):
1936 1941 ui.write(_("all patches applied\n"))
1937 1942 return 1
1938 1943 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1939 1944
1940 1945 def prev(ui, repo, **opts):
1941 1946 """print the name of the previous patch"""
1942 1947 q = repo.mq
1943 1948 l = len(q.applied)
1944 1949 if l == 1:
1945 1950 ui.write(_("only one patch applied\n"))
1946 1951 return 1
1947 1952 if not l:
1948 1953 ui.write(_("no patches applied\n"))
1949 1954 return 1
1950 1955 return q.qseries(repo, start=l - 2, length=1, status='A',
1951 1956 summary=opts.get('summary'))
1952 1957
1953 1958 def setupheaderopts(ui, opts):
1954 1959 if not opts.get('user') and opts.get('currentuser'):
1955 1960 opts['user'] = ui.username()
1956 1961 if not opts.get('date') and opts.get('currentdate'):
1957 1962 opts['date'] = "%d %d" % util.makedate()
1958 1963
1959 1964 def new(ui, repo, patch, *args, **opts):
1960 1965 """create a new patch
1961 1966
1962 1967 qnew creates a new patch on top of the currently-applied patch (if
1963 1968 any). The patch will be initialized with any outstanding changes
1964 1969 in the working directory. You may also use -I/--include,
1965 1970 -X/--exclude, and/or a list of files after the patch name to add
1966 1971 only changes to matching files to the new patch, leaving the rest
1967 1972 as uncommitted modifications.
1968 1973
1969 1974 -u/--user and -d/--date can be used to set the (given) user and
1970 1975 date, respectively. -U/--currentuser and -D/--currentdate set user
1971 1976 to current user and date to current date.
1972 1977
1973 1978 -e/--edit, -m/--message or -l/--logfile set the patch header as
1974 1979 well as the commit message. If none is specified, the header is
1975 1980 empty and the commit message is '[mq]: PATCH'.
1976 1981
1977 1982 Use the -g/--git option to keep the patch in the git extended diff
1978 1983 format. Read the diffs help topic for more information on why this
1979 1984 is important for preserving permission changes and copy/rename
1980 1985 information.
1981 1986 """
1982 1987 msg = cmdutil.logmessage(opts)
1983 1988 def getmsg():
1984 1989 return ui.edit(msg, ui.username())
1985 1990 q = repo.mq
1986 1991 opts['msg'] = msg
1987 1992 if opts.get('edit'):
1988 1993 opts['msg'] = getmsg
1989 1994 else:
1990 1995 opts['msg'] = msg
1991 1996 setupheaderopts(ui, opts)
1992 1997 q.new(repo, patch, *args, **opts)
1993 1998 q.save_dirty()
1994 1999 return 0
1995 2000
1996 2001 def refresh(ui, repo, *pats, **opts):
1997 2002 """update the current patch
1998 2003
1999 2004 If any file patterns are provided, the refreshed patch will
2000 2005 contain only the modifications that match those patterns; the
2001 2006 remaining modifications will remain in the working directory.
2002 2007
2003 2008 If -s/--short is specified, files currently included in the patch
2004 2009 will be refreshed just like matched files and remain in the patch.
2005 2010
2006 2011 hg add/remove/copy/rename work as usual, though you might want to
2007 2012 use git-style patches (-g/--git or [diff] git=1) to track copies
2008 2013 and renames. See the diffs help topic for more information on the
2009 2014 git diff format.
2010 2015 """
2011 2016 q = repo.mq
2012 2017 message = cmdutil.logmessage(opts)
2013 2018 if opts['edit']:
2014 2019 if not q.applied:
2015 2020 ui.write(_("no patches applied\n"))
2016 2021 return 1
2017 2022 if message:
2018 2023 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2019 2024 patch = q.applied[-1].name
2020 2025 ph = patchheader(q.join(patch), q.plainmode)
2021 2026 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2022 2027 setupheaderopts(ui, opts)
2023 2028 ret = q.refresh(repo, pats, msg=message, **opts)
2024 2029 q.save_dirty()
2025 2030 return ret
2026 2031
2027 2032 def diff(ui, repo, *pats, **opts):
2028 2033 """diff of the current patch and subsequent modifications
2029 2034
2030 2035 Shows a diff which includes the current patch as well as any
2031 2036 changes which have been made in the working directory since the
2032 2037 last refresh (thus showing what the current patch would become
2033 2038 after a qrefresh).
2034 2039
2035 2040 Use :hg:`diff` if you only want to see the changes made since the
2036 2041 last qrefresh, or :hg:`export qtip` if you want to see changes
2037 2042 made by the current patch without including changes made since the
2038 2043 qrefresh.
2039 2044 """
2040 2045 repo.mq.diff(repo, pats, opts)
2041 2046 return 0
2042 2047
2043 2048 def fold(ui, repo, *files, **opts):
2044 2049 """fold the named patches into the current patch
2045 2050
2046 2051 Patches must not yet be applied. Each patch will be successively
2047 2052 applied to the current patch in the order given. If all the
2048 2053 patches apply successfully, the current patch will be refreshed
2049 2054 with the new cumulative patch, and the folded patches will be
2050 2055 deleted. With -k/--keep, the folded patch files will not be
2051 2056 removed afterwards.
2052 2057
2053 2058 The header for each folded patch will be concatenated with the
2054 2059 current patch header, separated by a line of '* * *'."""
2055 2060
2056 2061 q = repo.mq
2057 2062
2058 2063 if not files:
2059 2064 raise util.Abort(_('qfold requires at least one patch name'))
2060 2065 if not q.check_toppatch(repo)[0]:
2061 2066 raise util.Abort(_('No patches applied'))
2062 2067 q.check_localchanges(repo)
2063 2068
2064 2069 message = cmdutil.logmessage(opts)
2065 2070 if opts['edit']:
2066 2071 if message:
2067 2072 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2068 2073
2069 2074 parent = q.lookup('qtip')
2070 2075 patches = []
2071 2076 messages = []
2072 2077 for f in files:
2073 2078 p = q.lookup(f)
2074 2079 if p in patches or p == parent:
2075 2080 ui.warn(_('Skipping already folded patch %s') % p)
2076 2081 if q.isapplied(p):
2077 2082 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2078 2083 patches.append(p)
2079 2084
2080 2085 for p in patches:
2081 2086 if not message:
2082 2087 ph = patchheader(q.join(p), q.plainmode)
2083 2088 if ph.message:
2084 2089 messages.append(ph.message)
2085 2090 pf = q.join(p)
2086 2091 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2087 2092 if not patchsuccess:
2088 2093 raise util.Abort(_('Error folding patch %s') % p)
2089 2094 patch.updatedir(ui, repo, files)
2090 2095
2091 2096 if not message:
2092 2097 ph = patchheader(q.join(parent), q.plainmode)
2093 2098 message, user = ph.message, ph.user
2094 2099 for msg in messages:
2095 2100 message.append('* * *')
2096 2101 message.extend(msg)
2097 2102 message = '\n'.join(message)
2098 2103
2099 2104 if opts['edit']:
2100 2105 message = ui.edit(message, user or ui.username())
2101 2106
2102 2107 diffopts = q.patchopts(q.diffopts(), *patches)
2103 2108 q.refresh(repo, msg=message, git=diffopts.git)
2104 2109 q.delete(repo, patches, opts)
2105 2110 q.save_dirty()
2106 2111
2107 2112 def goto(ui, repo, patch, **opts):
2108 2113 '''push or pop patches until named patch is at top of stack'''
2109 2114 q = repo.mq
2110 2115 patch = q.lookup(patch)
2111 2116 if q.isapplied(patch):
2112 2117 ret = q.pop(repo, patch, force=opts['force'])
2113 2118 else:
2114 2119 ret = q.push(repo, patch, force=opts['force'])
2115 2120 q.save_dirty()
2116 2121 return ret
2117 2122
2118 2123 def guard(ui, repo, *args, **opts):
2119 2124 '''set or print guards for a patch
2120 2125
2121 2126 Guards control whether a patch can be pushed. A patch with no
2122 2127 guards is always pushed. A patch with a positive guard ("+foo") is
2123 2128 pushed only if the qselect command has activated it. A patch with
2124 2129 a negative guard ("-foo") is never pushed if the qselect command
2125 2130 has activated it.
2126 2131
2127 2132 With no arguments, print the currently active guards.
2128 2133 With arguments, set guards for the named patch.
2129 2134 NOTE: Specifying negative guards now requires '--'.
2130 2135
2131 2136 To set guards on another patch::
2132 2137
2133 2138 hg qguard other.patch -- +2.6.17 -stable
2134 2139 '''
2135 2140 def status(idx):
2136 2141 guards = q.series_guards[idx] or ['unguarded']
2137 2142 ui.write('%s: ' % ui.label(q.series[idx], 'qguard.patch'))
2138 2143 for i, guard in enumerate(guards):
2139 2144 if guard.startswith('+'):
2140 2145 ui.write(guard, label='qguard.positive')
2141 2146 elif guard.startswith('-'):
2142 2147 ui.write(guard, label='qguard.negative')
2143 2148 else:
2144 2149 ui.write(guard, label='qguard.unguarded')
2145 2150 if i != len(guards) - 1:
2146 2151 ui.write(' ')
2147 2152 ui.write('\n')
2148 2153 q = repo.mq
2149 2154 patch = None
2150 2155 args = list(args)
2151 2156 if opts['list']:
2152 2157 if args or opts['none']:
2153 2158 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2154 2159 for i in xrange(len(q.series)):
2155 2160 status(i)
2156 2161 return
2157 2162 if not args or args[0][0:1] in '-+':
2158 2163 if not q.applied:
2159 2164 raise util.Abort(_('no patches applied'))
2160 2165 patch = q.applied[-1].name
2161 2166 if patch is None and args[0][0:1] not in '-+':
2162 2167 patch = args.pop(0)
2163 2168 if patch is None:
2164 2169 raise util.Abort(_('no patch to work with'))
2165 2170 if args or opts['none']:
2166 2171 idx = q.find_series(patch)
2167 2172 if idx is None:
2168 2173 raise util.Abort(_('no patch named %s') % patch)
2169 2174 q.set_guards(idx, args)
2170 2175 q.save_dirty()
2171 2176 else:
2172 2177 status(q.series.index(q.lookup(patch)))
2173 2178
2174 2179 def header(ui, repo, patch=None):
2175 2180 """print the header of the topmost or specified patch"""
2176 2181 q = repo.mq
2177 2182
2178 2183 if patch:
2179 2184 patch = q.lookup(patch)
2180 2185 else:
2181 2186 if not q.applied:
2182 2187 ui.write(_('no patches applied\n'))
2183 2188 return 1
2184 2189 patch = q.lookup('qtip')
2185 2190 ph = patchheader(q.join(patch), q.plainmode)
2186 2191
2187 2192 ui.write('\n'.join(ph.message) + '\n')
2188 2193
2189 2194 def lastsavename(path):
2190 2195 (directory, base) = os.path.split(path)
2191 2196 names = os.listdir(directory)
2192 2197 namere = re.compile("%s.([0-9]+)" % base)
2193 2198 maxindex = None
2194 2199 maxname = None
2195 2200 for f in names:
2196 2201 m = namere.match(f)
2197 2202 if m:
2198 2203 index = int(m.group(1))
2199 2204 if maxindex is None or index > maxindex:
2200 2205 maxindex = index
2201 2206 maxname = f
2202 2207 if maxname:
2203 2208 return (os.path.join(directory, maxname), maxindex)
2204 2209 return (None, None)
2205 2210
2206 2211 def savename(path):
2207 2212 (last, index) = lastsavename(path)
2208 2213 if last is None:
2209 2214 index = 0
2210 2215 newpath = path + ".%d" % (index + 1)
2211 2216 return newpath
2212 2217
2213 2218 def push(ui, repo, patch=None, **opts):
2214 2219 """push the next patch onto the stack
2215 2220
2216 2221 When -f/--force is applied, all local changes in patched files
2217 2222 will be lost.
2218 2223 """
2219 2224 q = repo.mq
2220 2225 mergeq = None
2221 2226
2222 2227 if opts['merge']:
2223 2228 if opts['name']:
2224 2229 newpath = repo.join(opts['name'])
2225 2230 else:
2226 2231 newpath, i = lastsavename(q.path)
2227 2232 if not newpath:
2228 2233 ui.warn(_("no saved queues found, please use -n\n"))
2229 2234 return 1
2230 2235 mergeq = queue(ui, repo.join(""), newpath)
2231 2236 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2232 2237 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2233 2238 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'))
2234 2239 return ret
2235 2240
2236 2241 def pop(ui, repo, patch=None, **opts):
2237 2242 """pop the current patch off the stack
2238 2243
2239 2244 By default, pops off the top of the patch stack. If given a patch
2240 2245 name, keeps popping off patches until the named patch is at the
2241 2246 top of the stack.
2242 2247 """
2243 2248 localupdate = True
2244 2249 if opts['name']:
2245 2250 q = queue(ui, repo.join(""), repo.join(opts['name']))
2246 2251 ui.warn(_('using patch queue: %s\n') % q.path)
2247 2252 localupdate = False
2248 2253 else:
2249 2254 q = repo.mq
2250 2255 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2251 2256 all=opts['all'])
2252 2257 q.save_dirty()
2253 2258 return ret
2254 2259
2255 2260 def rename(ui, repo, patch, name=None, **opts):
2256 2261 """rename a patch
2257 2262
2258 2263 With one argument, renames the current patch to PATCH1.
2259 2264 With two arguments, renames PATCH1 to PATCH2."""
2260 2265
2261 2266 q = repo.mq
2262 2267
2263 2268 if not name:
2264 2269 name = patch
2265 2270 patch = None
2266 2271
2267 2272 if patch:
2268 2273 patch = q.lookup(patch)
2269 2274 else:
2270 2275 if not q.applied:
2271 2276 ui.write(_('no patches applied\n'))
2272 2277 return
2273 2278 patch = q.lookup('qtip')
2274 2279 absdest = q.join(name)
2275 2280 if os.path.isdir(absdest):
2276 2281 name = normname(os.path.join(name, os.path.basename(patch)))
2277 2282 absdest = q.join(name)
2278 2283 if os.path.exists(absdest):
2279 2284 raise util.Abort(_('%s already exists') % absdest)
2280 2285
2281 2286 if name in q.series:
2282 2287 raise util.Abort(
2283 2288 _('A patch named %s already exists in the series file') % name)
2284 2289
2285 2290 ui.note(_('renaming %s to %s\n') % (patch, name))
2286 2291 i = q.find_series(patch)
2287 2292 guards = q.guard_re.findall(q.full_series[i])
2288 2293 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2289 2294 q.parse_series()
2290 2295 q.series_dirty = 1
2291 2296
2292 2297 info = q.isapplied(patch)
2293 2298 if info:
2294 2299 q.applied[info[0]] = statusentry(info[1], name)
2295 2300 q.applied_dirty = 1
2296 2301
2297 2302 util.rename(q.join(patch), absdest)
2298 2303 r = q.qrepo()
2299 2304 if r:
2300 2305 wlock = r.wlock()
2301 2306 try:
2302 2307 if r.dirstate[patch] == 'a':
2303 2308 r.dirstate.forget(patch)
2304 2309 r.dirstate.add(name)
2305 2310 else:
2306 2311 if r.dirstate[name] == 'r':
2307 2312 r.undelete([name])
2308 2313 r.copy(patch, name)
2309 2314 r.remove([patch], False)
2310 2315 finally:
2311 2316 wlock.release()
2312 2317
2313 2318 q.save_dirty()
2314 2319
2315 2320 def restore(ui, repo, rev, **opts):
2316 2321 """restore the queue state saved by a revision (DEPRECATED)
2317 2322
2318 2323 This command is deprecated, use rebase --mq instead."""
2319 2324 rev = repo.lookup(rev)
2320 2325 q = repo.mq
2321 2326 q.restore(repo, rev, delete=opts['delete'],
2322 2327 qupdate=opts['update'])
2323 2328 q.save_dirty()
2324 2329 return 0
2325 2330
2326 2331 def save(ui, repo, **opts):
2327 2332 """save current queue state (DEPRECATED)
2328 2333
2329 2334 This command is deprecated, use rebase --mq instead."""
2330 2335 q = repo.mq
2331 2336 message = cmdutil.logmessage(opts)
2332 2337 ret = q.save(repo, msg=message)
2333 2338 if ret:
2334 2339 return ret
2335 2340 q.save_dirty()
2336 2341 if opts['copy']:
2337 2342 path = q.path
2338 2343 if opts['name']:
2339 2344 newpath = os.path.join(q.basepath, opts['name'])
2340 2345 if os.path.exists(newpath):
2341 2346 if not os.path.isdir(newpath):
2342 2347 raise util.Abort(_('destination %s exists and is not '
2343 2348 'a directory') % newpath)
2344 2349 if not opts['force']:
2345 2350 raise util.Abort(_('destination %s exists, '
2346 2351 'use -f to force') % newpath)
2347 2352 else:
2348 2353 newpath = savename(path)
2349 2354 ui.warn(_("copy %s to %s\n") % (path, newpath))
2350 2355 util.copyfiles(path, newpath)
2351 2356 if opts['empty']:
2352 2357 try:
2353 2358 os.unlink(q.join(q.status_path))
2354 2359 except:
2355 2360 pass
2356 2361 return 0
2357 2362
2358 2363 def strip(ui, repo, rev, **opts):
2359 2364 """strip a changeset and all its descendants from the repository
2360 2365
2361 2366 The strip command removes all changesets whose local revision
2362 2367 number is greater than or equal to REV, and then restores any
2363 2368 changesets that are not descendants of REV. If the working
2364 2369 directory has uncommitted changes, the operation is aborted unless
2365 2370 the --force flag is supplied.
2366 2371
2367 2372 If a parent of the working directory is stripped, then the working
2368 2373 directory will automatically be updated to the most recent
2369 2374 available ancestor of the stripped parent after the operation
2370 2375 completes.
2371 2376
2372 2377 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2373 2378 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2374 2379 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2375 2380 where BUNDLE is the bundle file created by the strip. Note that
2376 2381 the local revision numbers will in general be different after the
2377 2382 restore.
2378 2383
2379 2384 Use the --nobackup option to discard the backup bundle once the
2380 2385 operation completes.
2381 2386 """
2382 2387 backup = 'all'
2383 2388 if opts['backup']:
2384 2389 backup = 'strip'
2385 2390 elif opts['nobackup']:
2386 2391 backup = 'none'
2387 2392
2388 2393 rev = repo.lookup(rev)
2389 2394 p = repo.dirstate.parents()
2390 2395 cl = repo.changelog
2391 2396 update = True
2392 2397 if p[0] == nullid:
2393 2398 update = False
2394 2399 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2395 2400 update = False
2396 2401 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2397 2402 update = False
2398 2403
2399 2404 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2400 2405 return 0
2401 2406
2402 2407 def select(ui, repo, *args, **opts):
2403 2408 '''set or print guarded patches to push
2404 2409
2405 2410 Use the qguard command to set or print guards on patch, then use
2406 2411 qselect to tell mq which guards to use. A patch will be pushed if
2407 2412 it has no guards or any positive guards match the currently
2408 2413 selected guard, but will not be pushed if any negative guards
2409 2414 match the current guard. For example::
2410 2415
2411 2416 qguard foo.patch -stable (negative guard)
2412 2417 qguard bar.patch +stable (positive guard)
2413 2418 qselect stable
2414 2419
2415 2420 This activates the "stable" guard. mq will skip foo.patch (because
2416 2421 it has a negative match) but push bar.patch (because it has a
2417 2422 positive match).
2418 2423
2419 2424 With no arguments, prints the currently active guards.
2420 2425 With one argument, sets the active guard.
2421 2426
2422 2427 Use -n/--none to deactivate guards (no other arguments needed).
2423 2428 When no guards are active, patches with positive guards are
2424 2429 skipped and patches with negative guards are pushed.
2425 2430
2426 2431 qselect can change the guards on applied patches. It does not pop
2427 2432 guarded patches by default. Use --pop to pop back to the last
2428 2433 applied patch that is not guarded. Use --reapply (which implies
2429 2434 --pop) to push back to the current patch afterwards, but skip
2430 2435 guarded patches.
2431 2436
2432 2437 Use -s/--series to print a list of all guards in the series file
2433 2438 (no other arguments needed). Use -v for more information.'''
2434 2439
2435 2440 q = repo.mq
2436 2441 guards = q.active()
2437 2442 if args or opts['none']:
2438 2443 old_unapplied = q.unapplied(repo)
2439 2444 old_guarded = [i for i in xrange(len(q.applied)) if
2440 2445 not q.pushable(i)[0]]
2441 2446 q.set_active(args)
2442 2447 q.save_dirty()
2443 2448 if not args:
2444 2449 ui.status(_('guards deactivated\n'))
2445 2450 if not opts['pop'] and not opts['reapply']:
2446 2451 unapplied = q.unapplied(repo)
2447 2452 guarded = [i for i in xrange(len(q.applied))
2448 2453 if not q.pushable(i)[0]]
2449 2454 if len(unapplied) != len(old_unapplied):
2450 2455 ui.status(_('number of unguarded, unapplied patches has '
2451 2456 'changed from %d to %d\n') %
2452 2457 (len(old_unapplied), len(unapplied)))
2453 2458 if len(guarded) != len(old_guarded):
2454 2459 ui.status(_('number of guarded, applied patches has changed '
2455 2460 'from %d to %d\n') %
2456 2461 (len(old_guarded), len(guarded)))
2457 2462 elif opts['series']:
2458 2463 guards = {}
2459 2464 noguards = 0
2460 2465 for gs in q.series_guards:
2461 2466 if not gs:
2462 2467 noguards += 1
2463 2468 for g in gs:
2464 2469 guards.setdefault(g, 0)
2465 2470 guards[g] += 1
2466 2471 if ui.verbose:
2467 2472 guards['NONE'] = noguards
2468 2473 guards = guards.items()
2469 2474 guards.sort(key=lambda x: x[0][1:])
2470 2475 if guards:
2471 2476 ui.note(_('guards in series file:\n'))
2472 2477 for guard, count in guards:
2473 2478 ui.note('%2d ' % count)
2474 2479 ui.write(guard, '\n')
2475 2480 else:
2476 2481 ui.note(_('no guards in series file\n'))
2477 2482 else:
2478 2483 if guards:
2479 2484 ui.note(_('active guards:\n'))
2480 2485 for g in guards:
2481 2486 ui.write(g, '\n')
2482 2487 else:
2483 2488 ui.write(_('no active guards\n'))
2484 2489 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2485 2490 popped = False
2486 2491 if opts['pop'] or opts['reapply']:
2487 2492 for i in xrange(len(q.applied)):
2488 2493 pushable, reason = q.pushable(i)
2489 2494 if not pushable:
2490 2495 ui.status(_('popping guarded patches\n'))
2491 2496 popped = True
2492 2497 if i == 0:
2493 2498 q.pop(repo, all=True)
2494 2499 else:
2495 2500 q.pop(repo, i - 1)
2496 2501 break
2497 2502 if popped:
2498 2503 try:
2499 2504 if reapply:
2500 2505 ui.status(_('reapplying unguarded patches\n'))
2501 2506 q.push(repo, reapply)
2502 2507 finally:
2503 2508 q.save_dirty()
2504 2509
2505 2510 def finish(ui, repo, *revrange, **opts):
2506 2511 """move applied patches into repository history
2507 2512
2508 2513 Finishes the specified revisions (corresponding to applied
2509 2514 patches) by moving them out of mq control into regular repository
2510 2515 history.
2511 2516
2512 2517 Accepts a revision range or the -a/--applied option. If --applied
2513 2518 is specified, all applied mq revisions are removed from mq
2514 2519 control. Otherwise, the given revisions must be at the base of the
2515 2520 stack of applied patches.
2516 2521
2517 2522 This can be especially useful if your changes have been applied to
2518 2523 an upstream repository, or if you are about to push your changes
2519 2524 to upstream.
2520 2525 """
2521 2526 if not opts['applied'] and not revrange:
2522 2527 raise util.Abort(_('no revisions specified'))
2523 2528 elif opts['applied']:
2524 2529 revrange = ('qbase:qtip',) + revrange
2525 2530
2526 2531 q = repo.mq
2527 2532 if not q.applied:
2528 2533 ui.status(_('no patches applied\n'))
2529 2534 return 0
2530 2535
2531 2536 revs = cmdutil.revrange(repo, revrange)
2532 2537 q.finish(repo, revs)
2533 2538 q.save_dirty()
2534 2539 return 0
2535 2540
2541 def qqueue(ui, repo, name=None, **opts):
2542 '''manage multiple patch queues
2543
2544 Supports switching between different patch queues, as well as creating
2545 new patch queues and deleting existing ones.
2546
2547 Omitting a queue name or specifying -l/--list will show you the registered
2548 queues - by default the "normal" patches queue is registered. The currently
2549 active queue will be marked with "(active)".
2550
2551 To create a new queue, use -c/--create. The queue is automatically made
2552 active, except in the case where there are applied patches from the
2553 currently active queue in the repository. Then the queue will only be
2554 created and switching will fail.
2555
2556 To delete an existing queue, use --delete. You cannot delete the currently
2557 active queue.
2558 '''
2559
2560 q = repo.mq
2561
2562 _defaultqueue = 'patches'
2563 _allqueues = '.queues'
2564 _activequeue = '.queue'
2565
2566 def _getcurrent():
2567 return os.path.basename(q.path)
2568
2569 def _noqueues():
2570 try:
2571 fh = repo.opener(_allqueues, 'r')
2572 fh.close()
2573 except IOError:
2574 return True
2575
2576 return False
2577
2578 def _getqueues():
2579 current = _getcurrent()
2580
2581 try:
2582 fh = repo.opener(_allqueues, 'r')
2583 queues = [queue.strip() for queue in fh if queue.strip()]
2584 if current not in queues:
2585 queues.append(current)
2586 except IOError:
2587 queues = [_defaultqueue]
2588
2589 return sorted(queues)
2590
2591 def _setactive(name):
2592 if q.applied:
2593 raise util.Abort(_('patches applied - cannot set new queue active'))
2594
2595 fh = repo.opener(_activequeue, 'w')
2596 fh.write(name)
2597 fh.close()
2598
2599 def _addqueue(name):
2600 fh = repo.opener(_allqueues, 'a')
2601 fh.write('%s\n' % (name,))
2602 fh.close()
2603
2604 if not name or opts.get('list'):
2605 current = _getcurrent()
2606 for queue in _getqueues():
2607 ui.write('%s' % (queue,))
2608 if queue == current:
2609 ui.write(_(' (active)\n'))
2610 else:
2611 ui.write('\n')
2612 return
2613
2614 existing = _getqueues()
2615
2616 if name not in existing and opts.get('delete'):
2617 raise util.Abort(_('cannot delete queue that does not exist'))
2618 elif name not in existing and not opts.get('create'):
2619 raise util.Abort(_('use --create to create a new queue'))
2620
2621 if opts.get('create'):
2622 if _noqueues():
2623 _addqueue(_defaultqueue)
2624 _addqueue(name)
2625 _setactive(name)
2626 elif opts.get('delete'):
2627 current = _getcurrent()
2628
2629 if name == current:
2630 raise util.Abort(_('cannot delete currently active queue'))
2631
2632 fh = repo.opener('.queues.new', 'w')
2633 for queue in existing:
2634 if queue == name:
2635 continue
2636 fh.write('%s\n' % (queue,))
2637 fh.close()
2638 util.rename(repo.join('.queues.new'), repo.join(_allqueues))
2639 else:
2640 _setactive(name)
2641
2536 2642 def reposetup(ui, repo):
2537 2643 class mqrepo(repo.__class__):
2538 2644 @util.propertycache
2539 2645 def mq(self):
2540 2646 return queue(self.ui, self.join(""))
2541 2647
2542 2648 def abort_if_wdir_patched(self, errmsg, force=False):
2543 2649 if self.mq.applied and not force:
2544 2650 parent = self.dirstate.parents()[0]
2545 2651 if parent in [s.node for s in self.mq.applied]:
2546 2652 raise util.Abort(errmsg)
2547 2653
2548 2654 def commit(self, text="", user=None, date=None, match=None,
2549 2655 force=False, editor=False, extra={}):
2550 2656 self.abort_if_wdir_patched(
2551 2657 _('cannot commit over an applied mq patch'),
2552 2658 force)
2553 2659
2554 2660 return super(mqrepo, self).commit(text, user, date, match, force,
2555 2661 editor, extra)
2556 2662
2557 2663 def push(self, remote, force=False, revs=None, newbranch=False):
2558 2664 if self.mq.applied and not force and not revs:
2559 2665 raise util.Abort(_('source has mq patches applied'))
2560 2666 return super(mqrepo, self).push(remote, force, revs, newbranch)
2561 2667
2562 2668 def _findtags(self):
2563 2669 '''augment tags from base class with patch tags'''
2564 2670 result = super(mqrepo, self)._findtags()
2565 2671
2566 2672 q = self.mq
2567 2673 if not q.applied:
2568 2674 return result
2569 2675
2570 2676 mqtags = [(patch.node, patch.name) for patch in q.applied]
2571 2677
2572 2678 if mqtags[-1][0] not in self.changelog.nodemap:
2573 2679 self.ui.warn(_('mq status file refers to unknown node %s\n')
2574 2680 % short(mqtags[-1][0]))
2575 2681 return result
2576 2682
2577 2683 mqtags.append((mqtags[-1][0], 'qtip'))
2578 2684 mqtags.append((mqtags[0][0], 'qbase'))
2579 2685 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2580 2686 tags = result[0]
2581 2687 for patch in mqtags:
2582 2688 if patch[1] in tags:
2583 2689 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2584 2690 % patch[1])
2585 2691 else:
2586 2692 tags[patch[1]] = patch[0]
2587 2693
2588 2694 return result
2589 2695
2590 2696 def _branchtags(self, partial, lrev):
2591 2697 q = self.mq
2592 2698 if not q.applied:
2593 2699 return super(mqrepo, self)._branchtags(partial, lrev)
2594 2700
2595 2701 cl = self.changelog
2596 2702 qbasenode = q.applied[0].node
2597 2703 if qbasenode not in cl.nodemap:
2598 2704 self.ui.warn(_('mq status file refers to unknown node %s\n')
2599 2705 % short(qbasenode))
2600 2706 return super(mqrepo, self)._branchtags(partial, lrev)
2601 2707
2602 2708 qbase = cl.rev(qbasenode)
2603 2709 start = lrev + 1
2604 2710 if start < qbase:
2605 2711 # update the cache (excluding the patches) and save it
2606 2712 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2607 2713 self._updatebranchcache(partial, ctxgen)
2608 2714 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2609 2715 start = qbase
2610 2716 # if start = qbase, the cache is as updated as it should be.
2611 2717 # if start > qbase, the cache includes (part of) the patches.
2612 2718 # we might as well use it, but we won't save it.
2613 2719
2614 2720 # update the cache up to the tip
2615 2721 ctxgen = (self[r] for r in xrange(start, len(cl)))
2616 2722 self._updatebranchcache(partial, ctxgen)
2617 2723
2618 2724 return partial
2619 2725
2620 2726 if repo.local():
2621 2727 repo.__class__ = mqrepo
2622 2728
2623 2729 def mqimport(orig, ui, repo, *args, **kwargs):
2624 2730 if (hasattr(repo, 'abort_if_wdir_patched')
2625 2731 and not kwargs.get('no_commit', False)):
2626 2732 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2627 2733 kwargs.get('force'))
2628 2734 return orig(ui, repo, *args, **kwargs)
2629 2735
2630 2736 def mqinit(orig, ui, *args, **kwargs):
2631 2737 mq = kwargs.pop('mq', None)
2632 2738
2633 2739 if not mq:
2634 2740 return orig(ui, *args, **kwargs)
2635 2741
2636 2742 if args:
2637 2743 repopath = args[0]
2638 2744 if not hg.islocal(repopath):
2639 2745 raise util.Abort(_('only a local queue repository '
2640 2746 'may be initialized'))
2641 2747 else:
2642 2748 repopath = cmdutil.findrepo(os.getcwd())
2643 2749 if not repopath:
2644 2750 raise util.Abort(_('There is no Mercurial repository here '
2645 2751 '(.hg not found)'))
2646 2752 repo = hg.repository(ui, repopath)
2647 2753 return qinit(ui, repo, True)
2648 2754
2649 2755 def mqcommand(orig, ui, repo, *args, **kwargs):
2650 2756 """Add --mq option to operate on patch repository instead of main"""
2651 2757
2652 2758 # some commands do not like getting unknown options
2653 2759 mq = kwargs.pop('mq', None)
2654 2760
2655 2761 if not mq:
2656 2762 return orig(ui, repo, *args, **kwargs)
2657 2763
2658 2764 q = repo.mq
2659 2765 r = q.qrepo()
2660 2766 if not r:
2661 2767 raise util.Abort(_('no queue repository'))
2662 2768 return orig(r.ui, r, *args, **kwargs)
2663 2769
2664 2770 def summary(orig, ui, repo, *args, **kwargs):
2665 2771 r = orig(ui, repo, *args, **kwargs)
2666 2772 q = repo.mq
2667 2773 m = []
2668 2774 a, u = len(q.applied), len(q.unapplied(repo))
2669 2775 if a:
2670 2776 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
2671 2777 if u:
2672 2778 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
2673 2779 if m:
2674 2780 ui.write("mq: %s\n" % ', '.join(m))
2675 2781 else:
2676 2782 ui.note(_("mq: (empty queue)\n"))
2677 2783 return r
2678 2784
2679 2785 def uisetup(ui):
2680 2786 mqopt = [('', 'mq', None, _("operate on patch repository"))]
2681 2787
2682 2788 extensions.wrapcommand(commands.table, 'import', mqimport)
2683 2789 extensions.wrapcommand(commands.table, 'summary', summary)
2684 2790
2685 2791 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
2686 2792 entry[1].extend(mqopt)
2687 2793
2688 2794 norepo = commands.norepo.split(" ")
2689 2795 for cmd in commands.table.keys():
2690 2796 cmd = cmdutil.parsealiases(cmd)[0]
2691 2797 if cmd in norepo:
2692 2798 continue
2693 2799 entry = extensions.wrapcommand(commands.table, cmd, mqcommand)
2694 2800 entry[1].extend(mqopt)
2695 2801
2696 2802 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2697 2803
2698 2804 cmdtable = {
2699 2805 "qapplied":
2700 2806 (applied,
2701 2807 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2702 2808 _('hg qapplied [-1] [-s] [PATCH]')),
2703 2809 "qclone":
2704 2810 (clone,
2705 2811 [('', 'pull', None, _('use pull protocol to copy metadata')),
2706 2812 ('U', 'noupdate', None, _('do not update the new working directories')),
2707 2813 ('', 'uncompressed', None,
2708 2814 _('use uncompressed transfer (fast over LAN)')),
2709 2815 ('p', 'patches', '', _('location of source patch repository')),
2710 2816 ] + commands.remoteopts,
2711 2817 _('hg qclone [OPTION]... SOURCE [DEST]')),
2712 2818 "qcommit|qci":
2713 2819 (commit,
2714 2820 commands.table["^commit|ci"][1],
2715 2821 _('hg qcommit [OPTION]... [FILE]...')),
2716 2822 "^qdiff":
2717 2823 (diff,
2718 2824 commands.diffopts + commands.diffopts2 + commands.walkopts,
2719 2825 _('hg qdiff [OPTION]... [FILE]...')),
2720 2826 "qdelete|qremove|qrm":
2721 2827 (delete,
2722 2828 [('k', 'keep', None, _('keep patch file')),
2723 2829 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2724 2830 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2725 2831 'qfold':
2726 2832 (fold,
2727 2833 [('e', 'edit', None, _('edit patch header')),
2728 2834 ('k', 'keep', None, _('keep folded patch files')),
2729 2835 ] + commands.commitopts,
2730 2836 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2731 2837 'qgoto':
2732 2838 (goto,
2733 2839 [('f', 'force', None, _('overwrite any local changes'))],
2734 2840 _('hg qgoto [OPTION]... PATCH')),
2735 2841 'qguard':
2736 2842 (guard,
2737 2843 [('l', 'list', None, _('list all patches and guards')),
2738 2844 ('n', 'none', None, _('drop all guards'))],
2739 2845 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
2740 2846 'qheader': (header, [], _('hg qheader [PATCH]')),
2741 2847 "qimport":
2742 2848 (qimport,
2743 2849 [('e', 'existing', None, _('import file in patch directory')),
2744 2850 ('n', 'name', '', _('name of patch file')),
2745 2851 ('f', 'force', None, _('overwrite existing files')),
2746 2852 ('r', 'rev', [], _('place existing revisions under mq control')),
2747 2853 ('g', 'git', None, _('use git extended diff format')),
2748 2854 ('P', 'push', None, _('qpush after importing'))],
2749 2855 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2750 2856 "^qinit":
2751 2857 (init,
2752 2858 [('c', 'create-repo', None, _('create queue repository'))],
2753 2859 _('hg qinit [-c]')),
2754 2860 "^qnew":
2755 2861 (new,
2756 2862 [('e', 'edit', None, _('edit commit message')),
2757 2863 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2758 2864 ('g', 'git', None, _('use git extended diff format')),
2759 2865 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2760 2866 ('u', 'user', '', _('add "From: <given user>" to patch')),
2761 2867 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2762 2868 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2763 2869 ] + commands.walkopts + commands.commitopts,
2764 2870 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
2765 2871 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2766 2872 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2767 2873 "^qpop":
2768 2874 (pop,
2769 2875 [('a', 'all', None, _('pop all patches')),
2770 2876 ('n', 'name', '', _('queue name to pop (DEPRECATED)')),
2771 2877 ('f', 'force', None, _('forget any local changes to patched files'))],
2772 2878 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2773 2879 "^qpush":
2774 2880 (push,
2775 2881 [('f', 'force', None, _('apply if the patch has rejects')),
2776 2882 ('l', 'list', None, _('list patch name in commit text')),
2777 2883 ('a', 'all', None, _('apply all patches')),
2778 2884 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2779 2885 ('n', 'name', '', _('merge queue name (DEPRECATED)')),
2780 2886 ('', 'move', None, _('reorder patch series and apply only the patch'))],
2781 2887 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [--move] [PATCH | INDEX]')),
2782 2888 "^qrefresh":
2783 2889 (refresh,
2784 2890 [('e', 'edit', None, _('edit commit message')),
2785 2891 ('g', 'git', None, _('use git extended diff format')),
2786 2892 ('s', 'short', None,
2787 2893 _('refresh only files already in the patch and specified files')),
2788 2894 ('U', 'currentuser', None,
2789 2895 _('add/update author field in patch with current user')),
2790 2896 ('u', 'user', '',
2791 2897 _('add/update author field in patch with given user')),
2792 2898 ('D', 'currentdate', None,
2793 2899 _('add/update date field in patch with current date')),
2794 2900 ('d', 'date', '',
2795 2901 _('add/update date field in patch with given date'))
2796 2902 ] + commands.walkopts + commands.commitopts,
2797 2903 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2798 2904 'qrename|qmv':
2799 2905 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2800 2906 "qrestore":
2801 2907 (restore,
2802 2908 [('d', 'delete', None, _('delete save entry')),
2803 2909 ('u', 'update', None, _('update queue working directory'))],
2804 2910 _('hg qrestore [-d] [-u] REV')),
2805 2911 "qsave":
2806 2912 (save,
2807 2913 [('c', 'copy', None, _('copy patch directory')),
2808 2914 ('n', 'name', '', _('copy directory name')),
2809 2915 ('e', 'empty', None, _('clear queue status file')),
2810 2916 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2811 2917 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2812 2918 "qselect":
2813 2919 (select,
2814 2920 [('n', 'none', None, _('disable all guards')),
2815 2921 ('s', 'series', None, _('list all guards in series file')),
2816 2922 ('', 'pop', None, _('pop to before first guarded applied patch')),
2817 2923 ('', 'reapply', None, _('pop, then reapply patches'))],
2818 2924 _('hg qselect [OPTION]... [GUARD]...')),
2819 2925 "qseries":
2820 2926 (series,
2821 2927 [('m', 'missing', None, _('print patches not in series')),
2822 2928 ] + seriesopts,
2823 2929 _('hg qseries [-ms]')),
2824 2930 "strip":
2825 2931 (strip,
2826 2932 [('f', 'force', None, _('force removal of changesets even if the '
2827 2933 'working directory has uncommitted changes')),
2828 2934 ('b', 'backup', None, _('bundle only changesets with local revision'
2829 2935 ' number greater than REV which are not'
2830 2936 ' descendants of REV (DEPRECATED)')),
2831 2937 ('n', 'nobackup', None, _('no backups'))],
2832 2938 _('hg strip [-f] [-n] REV')),
2833 2939 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2834 2940 "qunapplied":
2835 2941 (unapplied,
2836 2942 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2837 2943 _('hg qunapplied [-1] [-s] [PATCH]')),
2838 2944 "qfinish":
2839 2945 (finish,
2840 2946 [('a', 'applied', None, _('finish all applied changesets'))],
2841 2947 _('hg qfinish [-a] [REV]...')),
2948 'qqueue':
2949 (qqueue,
2950 [
2951 ('l', 'list', False, _('list all available queues')),
2952 ('c', 'create', False, _('create new queue')),
2953 ('', 'delete', False, _('delete reference to queue')),
2954 ],
2955 _('[OPTION] [QUEUE]')),
2842 2956 }
2843 2957
2844 2958 colortable = {'qguard.negative': 'red',
2845 2959 'qguard.positive': 'yellow',
2846 2960 'qguard.unguarded': 'green',
2847 2961 'qseries.applied': 'blue bold underline',
2848 2962 'qseries.guarded': 'black bold',
2849 2963 'qseries.missing': 'red bold',
2850 2964 'qseries.unapplied': 'black bold'}
@@ -1,635 +1,636 b''
1 1 % help
2 2 mq extension - manage a stack of patches
3 3
4 4 This extension lets you work with a stack of patches in a Mercurial
5 5 repository. It manages two stacks of patches - all known patches, and applied
6 6 patches (subset of known patches).
7 7
8 8 Known patches are represented as patch files in the .hg/patches directory.
9 9 Applied patches are both patch files and changesets.
10 10
11 11 Common tasks (use "hg help command" for more details):
12 12
13 13 create new patch qnew
14 14 import existing patch qimport
15 15
16 16 print patch series qseries
17 17 print applied patches qapplied
18 18
19 19 add known patch to applied stack qpush
20 20 remove patch from applied stack qpop
21 21 refresh contents of top applied patch qrefresh
22 22
23 23 By default, mq will automatically use git patches when required to avoid
24 24 losing file mode changes, copy records, binary files or empty files creations
25 25 or deletions. This behaviour can be configured with:
26 26
27 27 [mq]
28 28 git = auto/keep/yes/no
29 29
30 30 If set to 'keep', mq will obey the [diff] section configuration while
31 31 preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq
32 32 will override the [diff] section and always generate git or regular patches,
33 33 possibly losing data in the second case.
34 34
35 35 list of commands:
36 36
37 37 qapplied print the patches already applied
38 38 qclone clone main and patch repository at same time
39 39 qdelete remove patches from queue
40 40 qdiff diff of the current patch and subsequent modifications
41 41 qfinish move applied patches into repository history
42 42 qfold fold the named patches into the current patch
43 43 qgoto push or pop patches until named patch is at top of stack
44 44 qguard set or print guards for a patch
45 45 qheader print the header of the topmost or specified patch
46 46 qimport import a patch
47 47 qnew create a new patch
48 48 qnext print the name of the next patch
49 49 qpop pop the current patch off the stack
50 50 qprev print the name of the previous patch
51 51 qpush push the next patch onto the stack
52 qqueue manage multiple patch queues
52 53 qrefresh update the current patch
53 54 qrename rename a patch
54 55 qselect set or print guarded patches to push
55 56 qseries print the entire series file
56 57 qtop print the name of the current patch
57 58 qunapplied print the patches not yet applied
58 59 strip strip a changeset and all its descendants from the repository
59 60
60 61 use "hg -v help mq" to show aliases and global options
61 62 adding a
62 63 updating to branch default
63 64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 65 adding b/z
65 66 % qinit
66 67 % -R qinit
67 68 % qinit -c
68 69 A .hgignore
69 70 A series
70 71 % qinit; qinit -c
71 72 .hgignore:
72 73 ^\.hg
73 74 ^\.mq
74 75 syntax: glob
75 76 status
76 77 guards
77 78 series:
78 79 abort: repository already exists!
79 80 % qinit; <stuff>; qinit -c
80 81 adding .hg/patches/A
81 82 adding .hg/patches/B
82 83 A .hgignore
83 84 A A
84 85 A B
85 86 A series
86 87 .hgignore:
87 88 status
88 89 bleh
89 90 series:
90 91 A
91 92 B
92 93 % init --mq without repo
93 94 abort: There is no Mercurial repository here (.hg not found)
94 95 % init --mq with repo path
95 96 ok
96 97 % init --mq with nonexistent directory
97 98 abort: repository nonexistentdir not found!
98 99 % init --mq with bundle (non "local")
99 100 abort: only a local queue repository may be initialized
100 101 % qrefresh
101 102 foo bar
102 103
103 104 diff -r xa
104 105 --- a/a
105 106 +++ b/a
106 107 @@ -1,1 +1,2 @@
107 108 a
108 109 +a
109 110 % empty qrefresh
110 111 revision:
111 112 patch:
112 113 foo bar
113 114
114 115 working dir diff:
115 116 --- a/a
116 117 +++ b/a
117 118 @@ -1,1 +1,2 @@
118 119 a
119 120 +a
120 121 % qpop
121 122 popping test.patch
122 123 patch queue now empty
123 124 % qpush with dump of tag cache
124 125 .hg/tags.cache (pre qpush):
125 126 1
126 127
127 128 applying test.patch
128 129 now at: test.patch
129 130 .hg/tags.cache (post qpush):
130 131 2
131 132
132 133 % pop/push outside repo
133 134 popping test.patch
134 135 patch queue now empty
135 136 applying test.patch
136 137 now at: test.patch
137 138 % qrefresh in subdir
138 139 % pop/push -a in subdir
139 140 popping test2.patch
140 141 popping test.patch
141 142 patch queue now empty
142 143 applying test.patch
143 144 applying test2.patch
144 145 now at: test2.patch
145 146 % qseries
146 147 test.patch
147 148 test2.patch
148 149 0 A test.patch: f...
149 150 1 A test2.patch:
150 151 popping test2.patch
151 152 now at: test.patch
152 153 0 A test.patch: foo bar
153 154 1 U test2.patch:
154 155 mq: 1 applied, 1 unapplied
155 156 applying test2.patch
156 157 now at: test2.patch
157 158 mq: 2 applied
158 159 % qapplied
159 160 test.patch
160 161 test2.patch
161 162 % qtop
162 163 test2.patch
163 164 % prev
164 165 test.patch
165 166 % next
166 167 all patches applied
167 168 popping test2.patch
168 169 now at: test.patch
169 170 % commit should fail
170 171 abort: cannot commit over an applied mq patch
171 172 % push should fail
172 173 pushing to ../../k
173 174 abort: source has mq patches applied
174 175 % import should fail
175 176 abort: cannot import over an applied patch
176 177 % import --no-commit should succeed
177 178 applying ../../import.diff
178 179 M a
179 180 % qunapplied
180 181 test2.patch
181 182 % qpush/qpop with index
182 183 applying test2.patch
183 184 now at: test2.patch
184 185 popping test2.patch
185 186 popping test1b.patch
186 187 now at: test.patch
187 188 applying test1b.patch
188 189 now at: test1b.patch
189 190 applying test2.patch
190 191 now at: test2.patch
191 192 popping test2.patch
192 193 now at: test1b.patch
193 194 popping test1b.patch
194 195 now at: test.patch
195 196 applying test1b.patch
196 197 applying test2.patch
197 198 now at: test2.patch
198 199 % qpush --move
199 200 popping test2.patch
200 201 popping test1b.patch
201 202 popping test.patch
202 203 patch queue now empty
203 204 applying test2.patch
204 205 now at: test2.patch
205 206 applying test1b.patch
206 207 now at: test1b.patch
207 208 applying test.patch
208 209 now at: test.patch
209 210 0 A test2.patch
210 211 1 A test1b.patch
211 212 2 A test.patch
212 213 popping test.patch
213 214 popping test1b.patch
214 215 popping test2.patch
215 216 patch queue now empty
216 217 applying test.patch
217 218 now at: test.patch
218 219 applying test1b.patch
219 220 now at: test1b.patch
220 221 abort: patch bogus not in series
221 222 abort: cannot push to a previous patch: test.patch
222 223 applying test2.patch
223 224 now at: test2.patch
224 225 % pop, qapplied, qunapplied
225 226 0 A test.patch
226 227 1 A test1b.patch
227 228 2 A test2.patch
228 229 % qapplied -1 test.patch
229 230 only one patch applied
230 231 % qapplied -1 test1b.patch
231 232 test.patch
232 233 % qapplied -1 test2.patch
233 234 test1b.patch
234 235 % qapplied -1
235 236 test1b.patch
236 237 % qapplied
237 238 test.patch
238 239 test1b.patch
239 240 test2.patch
240 241 % qapplied test1b.patch
241 242 test.patch
242 243 test1b.patch
243 244 % qunapplied -1
244 245 all patches applied
245 246 % qunapplied
246 247 % popping
247 248 popping test2.patch
248 249 now at: test1b.patch
249 250 % qunapplied -1
250 251 test2.patch
251 252 % qunapplied
252 253 test2.patch
253 254 % qunapplied test2.patch
254 255 % qunapplied -1 test2.patch
255 256 all patches applied
256 257 % popping -a
257 258 popping test1b.patch
258 259 popping test.patch
259 260 patch queue now empty
260 261 % qapplied
261 262 % qapplied -1
262 263 no patches applied
263 264 applying test.patch
264 265 now at: test.patch
265 266 % push should succeed
266 267 popping test.patch
267 268 patch queue now empty
268 269 pushing to ../../k
269 270 searching for changes
270 271 adding changesets
271 272 adding manifests
272 273 adding file changes
273 274 added 1 changesets with 1 changes to 1 files
274 275 % qpush/qpop error codes
275 276 applying test.patch
276 277 applying test1b.patch
277 278 applying test2.patch
278 279 now at: test2.patch
279 280 % pops all patches and succeeds
280 281 popping test2.patch
281 282 popping test1b.patch
282 283 popping test.patch
283 284 patch queue now empty
284 285 qpop -a succeeds
285 286 % does nothing and succeeds
286 287 no patches applied
287 288 qpop -a succeeds
288 289 % fails - nothing else to pop
289 290 no patches applied
290 291 qpop fails
291 292 % pushes a patch and succeeds
292 293 applying test.patch
293 294 now at: test.patch
294 295 qpush succeeds
295 296 % pops a patch and succeeds
296 297 popping test.patch
297 298 patch queue now empty
298 299 qpop succeeds
299 300 % pushes up to test1b.patch and succeeds
300 301 applying test.patch
301 302 applying test1b.patch
302 303 now at: test1b.patch
303 304 qpush test1b.patch succeeds
304 305 % does nothing and succeeds
305 306 qpush: test1b.patch is already at the top
306 307 qpush test1b.patch succeeds
307 308 % does nothing and succeeds
308 309 qpop: test1b.patch is already at the top
309 310 qpop test1b.patch succeeds
310 311 % fails - can't push to this patch
311 312 abort: cannot push to a previous patch: test.patch
312 313 qpush test.patch fails
313 314 % fails - can't pop to this patch
314 315 abort: patch test2.patch is not applied
315 316 qpop test2.patch fails
316 317 % pops up to test.patch and succeeds
317 318 popping test1b.patch
318 319 now at: test.patch
319 320 qpop test.patch succeeds
320 321 % pushes all patches and succeeds
321 322 applying test1b.patch
322 323 applying test2.patch
323 324 now at: test2.patch
324 325 qpush -a succeeds
325 326 % does nothing and succeeds
326 327 all patches are currently applied
327 328 qpush -a succeeds
328 329 % fails - nothing else to push
329 330 patch series already fully applied
330 331 qpush fails
331 332 % does nothing and succeeds
332 333 qpush: test2.patch is already at the top
333 334 qpush test2.patch succeeds
334 335 % strip
335 336 adding x
336 337 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
337 338 saved backup bundle to
338 339 adding changesets
339 340 adding manifests
340 341 adding file changes
341 342 added 1 changesets with 1 changes to 1 files
342 343 (run 'hg update' to get a working copy)
343 344 % strip with local changes, should complain
344 345 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
345 346 abort: local changes found
346 347 % --force strip with local changes
347 348 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
348 349 saved backup bundle to
349 350 % cd b; hg qrefresh
350 351 adding a
351 352 foo
352 353
353 354 diff -r cb9a9f314b8b a
354 355 --- a/a
355 356 +++ b/a
356 357 @@ -1,1 +1,2 @@
357 358 a
358 359 +a
359 360 diff -r cb9a9f314b8b b/f
360 361 --- /dev/null
361 362 +++ b/b/f
362 363 @@ -0,0 +1,1 @@
363 364 +f
364 365 % hg qrefresh .
365 366 foo
366 367
367 368 diff -r cb9a9f314b8b b/f
368 369 --- /dev/null
369 370 +++ b/b/f
370 371 @@ -0,0 +1,1 @@
371 372 +f
372 373 M a
373 374 % qpush failure
374 375 popping bar
375 376 popping foo
376 377 patch queue now empty
377 378 applying foo
378 379 applying bar
379 380 file foo already exists
380 381 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
381 382 patch failed, unable to continue (try -v)
382 383 patch failed, rejects left in working dir
383 384 errors during apply, please fix and refresh bar
384 385 ? foo
385 386 ? foo.rej
386 387 % mq tags
387 388 0 qparent
388 389 1 foo qbase
389 390 2 bar qtip tip
390 391 % bad node in status
391 392 popping bar
392 393 now at: foo
393 394 changeset: 0:cb9a9f314b8b
394 395 mq status file refers to unknown node
395 396 tag: tip
396 397 user: test
397 398 date: Thu Jan 01 00:00:00 1970 +0000
398 399 summary: a
399 400
400 401 mq status file refers to unknown node
401 402 default 0:cb9a9f314b8b
402 403 abort: trying to pop unknown node
403 404 new file
404 405
405 406 diff --git a/new b/new
406 407 new file mode 100755
407 408 --- /dev/null
408 409 +++ b/new
409 410 @@ -0,0 +1,1 @@
410 411 +foo
411 412 copy file
412 413
413 414 diff --git a/new b/copy
414 415 copy from new
415 416 copy to copy
416 417 popping copy
417 418 now at: new
418 419 applying copy
419 420 now at: copy
420 421 diff --git a/new b/copy
421 422 copy from new
422 423 copy to copy
423 424 diff --git a/new b/copy
424 425 copy from new
425 426 copy to copy
426 427 % test file addition in slow path
427 428 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
428 429 created new head
429 430 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
430 431 diff --git a/bar b/bar
431 432 new file mode 100644
432 433 --- /dev/null
433 434 +++ b/bar
434 435 @@ -0,0 +1,1 @@
435 436 +bar
436 437 diff --git a/foo b/baz
437 438 rename from foo
438 439 rename to baz
439 440 2 baz (foo)
440 441 diff --git a/bar b/bar
441 442 new file mode 100644
442 443 --- /dev/null
443 444 +++ b/bar
444 445 @@ -0,0 +1,1 @@
445 446 +bar
446 447 diff --git a/foo b/baz
447 448 rename from foo
448 449 rename to baz
449 450 2 baz (foo)
450 451 diff --git a/bar b/bar
451 452 diff --git a/foo b/baz
452 453 % test file move chains in the slow path
453 454 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
454 455 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
455 456 diff --git a/foo b/bleh
456 457 rename from foo
457 458 rename to bleh
458 459 diff --git a/quux b/quux
459 460 new file mode 100644
460 461 --- /dev/null
461 462 +++ b/quux
462 463 @@ -0,0 +1,1 @@
463 464 +bar
464 465 3 bleh (foo)
465 466 diff --git a/foo b/barney
466 467 rename from foo
467 468 rename to barney
468 469 diff --git a/fred b/fred
469 470 new file mode 100644
470 471 --- /dev/null
471 472 +++ b/fred
472 473 @@ -0,0 +1,1 @@
473 474 +bar
474 475 3 barney (foo)
475 476 % refresh omitting an added file
476 477 C newfile
477 478 A newfile
478 479 popping baz
479 480 now at: bar
480 481 % create a git patch
481 482 diff --git a/alexander b/alexander
482 483 % create a git binary patch
483 484 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
484 485 diff --git a/bucephalus b/bucephalus
485 486 % check binary patches can be popped and pushed
486 487 popping addbucephalus
487 488 now at: addalexander
488 489 applying addbucephalus
489 490 now at: addbucephalus
490 491 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
491 492 % strip again
492 493 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
493 494 created new head
494 495 merging foo
495 496 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
496 497 (branch merge, don't forget to commit)
497 498 changeset: 3:99615015637b
498 499 tag: tip
499 500 parent: 2:20cbbe65cff7
500 501 parent: 1:d2871fc282d4
501 502 user: test
502 503 date: Thu Jan 01 00:00:00 1970 +0000
503 504 summary: merge
504 505
505 506 changeset: 2:20cbbe65cff7
506 507 parent: 0:53245c60e682
507 508 user: test
508 509 date: Thu Jan 01 00:00:00 1970 +0000
509 510 summary: change foo 2
510 511
511 512 changeset: 1:d2871fc282d4
512 513 user: test
513 514 date: Thu Jan 01 00:00:00 1970 +0000
514 515 summary: change foo 1
515 516
516 517 changeset: 0:53245c60e682
517 518 user: test
518 519 date: Thu Jan 01 00:00:00 1970 +0000
519 520 summary: add foo
520 521
521 522 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
522 523 saved backup bundle to
523 524 changeset: 1:20cbbe65cff7
524 525 tag: tip
525 526 user: test
526 527 date: Thu Jan 01 00:00:00 1970 +0000
527 528 summary: change foo 2
528 529
529 530 changeset: 0:53245c60e682
530 531 user: test
531 532 date: Thu Jan 01 00:00:00 1970 +0000
532 533 summary: add foo
533 534
534 535 % qclone
535 536 abort: versioned patch repository not found (see init --mq)
536 537 adding .hg/patches/patch1
537 538 main repo:
538 539 rev 1: change foo
539 540 rev 0: add foo
540 541 patch repo:
541 542 rev 0: checkpoint
542 543 updating to branch default
543 544 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
544 545 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
545 546 main repo:
546 547 rev 0: add foo
547 548 patch repo:
548 549 rev 0: checkpoint
549 550 popping patch1
550 551 patch queue now empty
551 552 main repo:
552 553 rev 0: add foo
553 554 patch repo:
554 555 rev 0: checkpoint
555 556 updating to branch default
556 557 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
557 558 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 559 main repo:
559 560 rev 0: add foo
560 561 patch repo:
561 562 rev 0: checkpoint
562 563 % test applying on an empty file (issue 1033)
563 564 adding a
564 565 popping changea
565 566 patch queue now empty
566 567 applying changea
567 568 now at: changea
568 569 % test qpush with --force, issue1087
569 570 adding bye.txt
570 571 adding hello.txt
571 572 popping empty
572 573 patch queue now empty
573 574 % qpush should fail, local changes
574 575 abort: local changes found, refresh first
575 576 % apply force, should not discard changes with empty patch
576 577 applying empty
577 578 patch empty is empty
578 579 now at: empty
579 580 diff -r bf5fc3f07a0a hello.txt
580 581 --- a/hello.txt
581 582 +++ b/hello.txt
582 583 @@ -1,1 +1,2 @@
583 584 hello
584 585 +world
585 586 diff -r 9ecee4f634e3 hello.txt
586 587 --- a/hello.txt
587 588 +++ b/hello.txt
588 589 @@ -1,1 +1,2 @@
589 590 hello
590 591 +world
591 592 changeset: 1:bf5fc3f07a0a
592 593 tag: empty
593 594 tag: qbase
594 595 tag: qtip
595 596 tag: tip
596 597 user: test
597 598 date: Thu Jan 01 00:00:00 1970 +0000
598 599 summary: imported patch empty
599 600
600 601
601 602 popping empty
602 603 patch queue now empty
603 604 % qpush should fail, local changes
604 605 abort: local changes found, refresh first
605 606 % apply force, should discard changes in hello, but not bye
606 607 applying empty
607 608 now at: empty
608 609 M bye.txt
609 610 diff -r ba252371dbc1 bye.txt
610 611 --- a/bye.txt
611 612 +++ b/bye.txt
612 613 @@ -1,1 +1,2 @@
613 614 bye
614 615 +universe
615 616 diff -r 9ecee4f634e3 bye.txt
616 617 --- a/bye.txt
617 618 +++ b/bye.txt
618 619 @@ -1,1 +1,2 @@
619 620 bye
620 621 +universe
621 622 diff -r 9ecee4f634e3 hello.txt
622 623 --- a/hello.txt
623 624 +++ b/hello.txt
624 625 @@ -1,1 +1,3 @@
625 626 hello
626 627 +world
627 628 +universe
628 629 % test popping revisions not in working dir ancestry
629 630 0 A empty
630 631 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
631 632 popping empty
632 633 patch queue now empty
633 634 % test popping must remove files added in subdirectories first
634 635 popping rename-dir
635 636 patch queue now empty
General Comments 0
You need to be logged in to leave comments. Login now