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