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