##// END OF EJS Templates
mq: qdiff: support all diffopts
Jason Orendorff -
r6668:034f4449 default
parent child Browse files
Show More
@@ -1,2463 +1,2458 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
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 59 class queue:
60 60 def __init__(self, ui, path, patchdir=None):
61 61 self.basepath = path
62 62 self.path = patchdir or os.path.join(path, "patches")
63 63 self.opener = util.opener(self.path)
64 64 self.ui = ui
65 65 self.applied = []
66 66 self.full_series = []
67 67 self.applied_dirty = 0
68 68 self.series_dirty = 0
69 69 self.series_path = "series"
70 70 self.status_path = "status"
71 71 self.guards_path = "guards"
72 72 self.active_guards = None
73 73 self.guards_dirty = False
74 74 self._diffopts = None
75 75
76 76 if os.path.exists(self.join(self.series_path)):
77 77 self.full_series = self.opener(self.series_path).read().splitlines()
78 78 self.parse_series()
79 79
80 80 if os.path.exists(self.join(self.status_path)):
81 81 lines = self.opener(self.status_path).read().splitlines()
82 82 self.applied = [statusentry(l) for l in lines]
83 83
84 84 def diffopts(self):
85 85 if self._diffopts is None:
86 86 self._diffopts = patch.diffopts(self.ui)
87 87 return self._diffopts
88 88
89 89 def join(self, *p):
90 90 return os.path.join(self.path, *p)
91 91
92 92 def find_series(self, patch):
93 93 pre = re.compile("(\s*)([^#]+)")
94 94 index = 0
95 95 for l in self.full_series:
96 96 m = pre.match(l)
97 97 if m:
98 98 s = m.group(2)
99 99 s = s.rstrip()
100 100 if s == patch:
101 101 return index
102 102 index += 1
103 103 return None
104 104
105 105 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
106 106
107 107 def parse_series(self):
108 108 self.series = []
109 109 self.series_guards = []
110 110 for l in self.full_series:
111 111 h = l.find('#')
112 112 if h == -1:
113 113 patch = l
114 114 comment = ''
115 115 elif h == 0:
116 116 continue
117 117 else:
118 118 patch = l[:h]
119 119 comment = l[h:]
120 120 patch = patch.strip()
121 121 if patch:
122 122 if patch in self.series:
123 123 raise util.Abort(_('%s appears more than once in %s') %
124 124 (patch, self.join(self.series_path)))
125 125 self.series.append(patch)
126 126 self.series_guards.append(self.guard_re.findall(comment))
127 127
128 128 def check_guard(self, guard):
129 129 if not guard:
130 130 return _('guard cannot be an empty string')
131 131 bad_chars = '# \t\r\n\f'
132 132 first = guard[0]
133 133 for c in '-+':
134 134 if first == c:
135 135 return (_('guard %r starts with invalid character: %r') %
136 136 (guard, c))
137 137 for c in bad_chars:
138 138 if c in guard:
139 139 return _('invalid character in guard %r: %r') % (guard, c)
140 140
141 141 def set_active(self, guards):
142 142 for guard in guards:
143 143 bad = self.check_guard(guard)
144 144 if bad:
145 145 raise util.Abort(bad)
146 146 guards = dict.fromkeys(guards).keys()
147 147 guards.sort()
148 148 self.ui.debug('active guards: %s\n' % ' '.join(guards))
149 149 self.active_guards = guards
150 150 self.guards_dirty = True
151 151
152 152 def active(self):
153 153 if self.active_guards is None:
154 154 self.active_guards = []
155 155 try:
156 156 guards = self.opener(self.guards_path).read().split()
157 157 except IOError, err:
158 158 if err.errno != errno.ENOENT: raise
159 159 guards = []
160 160 for i, guard in enumerate(guards):
161 161 bad = self.check_guard(guard)
162 162 if bad:
163 163 self.ui.warn('%s:%d: %s\n' %
164 164 (self.join(self.guards_path), i + 1, bad))
165 165 else:
166 166 self.active_guards.append(guard)
167 167 return self.active_guards
168 168
169 169 def set_guards(self, idx, guards):
170 170 for g in guards:
171 171 if len(g) < 2:
172 172 raise util.Abort(_('guard %r too short') % g)
173 173 if g[0] not in '-+':
174 174 raise util.Abort(_('guard %r starts with invalid char') % g)
175 175 bad = self.check_guard(g[1:])
176 176 if bad:
177 177 raise util.Abort(bad)
178 178 drop = self.guard_re.sub('', self.full_series[idx])
179 179 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
180 180 self.parse_series()
181 181 self.series_dirty = True
182 182
183 183 def pushable(self, idx):
184 184 if isinstance(idx, str):
185 185 idx = self.series.index(idx)
186 186 patchguards = self.series_guards[idx]
187 187 if not patchguards:
188 188 return True, None
189 189 default = False
190 190 guards = self.active()
191 191 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
192 192 if exactneg:
193 193 return False, exactneg[0]
194 194 pos = [g for g in patchguards if g[0] == '+']
195 195 exactpos = [g for g in pos if g[1:] in guards]
196 196 if pos:
197 197 if exactpos:
198 198 return True, exactpos[0]
199 199 return False, pos
200 200 return True, ''
201 201
202 202 def explain_pushable(self, idx, all_patches=False):
203 203 write = all_patches and self.ui.write or self.ui.warn
204 204 if all_patches or self.ui.verbose:
205 205 if isinstance(idx, str):
206 206 idx = self.series.index(idx)
207 207 pushable, why = self.pushable(idx)
208 208 if all_patches and pushable:
209 209 if why is None:
210 210 write(_('allowing %s - no guards in effect\n') %
211 211 self.series[idx])
212 212 else:
213 213 if not why:
214 214 write(_('allowing %s - no matching negative guards\n') %
215 215 self.series[idx])
216 216 else:
217 217 write(_('allowing %s - guarded by %r\n') %
218 218 (self.series[idx], why))
219 219 if not pushable:
220 220 if why:
221 221 write(_('skipping %s - guarded by %r\n') %
222 222 (self.series[idx], why))
223 223 else:
224 224 write(_('skipping %s - no matching guards\n') %
225 225 self.series[idx])
226 226
227 227 def save_dirty(self):
228 228 def write_list(items, path):
229 229 fp = self.opener(path, 'w')
230 230 for i in items:
231 231 fp.write("%s\n" % i)
232 232 fp.close()
233 233 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
234 234 if self.series_dirty: write_list(self.full_series, self.series_path)
235 235 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
236 236
237 237 def readheaders(self, patch):
238 238 def eatdiff(lines):
239 239 while lines:
240 240 l = lines[-1]
241 241 if (l.startswith("diff -") or
242 242 l.startswith("Index:") or
243 243 l.startswith("===========")):
244 244 del lines[-1]
245 245 else:
246 246 break
247 247 def eatempty(lines):
248 248 while lines:
249 249 l = lines[-1]
250 250 if re.match('\s*$', l):
251 251 del lines[-1]
252 252 else:
253 253 break
254 254
255 255 pf = self.join(patch)
256 256 message = []
257 257 comments = []
258 258 user = None
259 259 date = None
260 260 format = None
261 261 subject = None
262 262 diffstart = 0
263 263
264 264 for line in file(pf):
265 265 line = line.rstrip()
266 266 if line.startswith('diff --git'):
267 267 diffstart = 2
268 268 break
269 269 if diffstart:
270 270 if line.startswith('+++ '):
271 271 diffstart = 2
272 272 break
273 273 if line.startswith("--- "):
274 274 diffstart = 1
275 275 continue
276 276 elif format == "hgpatch":
277 277 # parse values when importing the result of an hg export
278 278 if line.startswith("# User "):
279 279 user = line[7:]
280 280 elif line.startswith("# Date "):
281 281 date = line[7:]
282 282 elif not line.startswith("# ") and line:
283 283 message.append(line)
284 284 format = None
285 285 elif line == '# HG changeset patch':
286 286 format = "hgpatch"
287 287 elif (format != "tagdone" and (line.startswith("Subject: ") or
288 288 line.startswith("subject: "))):
289 289 subject = line[9:]
290 290 format = "tag"
291 291 elif (format != "tagdone" and (line.startswith("From: ") or
292 292 line.startswith("from: "))):
293 293 user = line[6:]
294 294 format = "tag"
295 295 elif format == "tag" and line == "":
296 296 # when looking for tags (subject: from: etc) they
297 297 # end once you find a blank line in the source
298 298 format = "tagdone"
299 299 elif message or line:
300 300 message.append(line)
301 301 comments.append(line)
302 302
303 303 eatdiff(message)
304 304 eatdiff(comments)
305 305 eatempty(message)
306 306 eatempty(comments)
307 307
308 308 # make sure message isn't empty
309 309 if format and format.startswith("tag") and subject:
310 310 message.insert(0, "")
311 311 message.insert(0, subject)
312 312 return (message, comments, user, date, diffstart > 1)
313 313
314 314 def removeundo(self, repo):
315 315 undo = repo.sjoin('undo')
316 316 if not os.path.exists(undo):
317 317 return
318 318 try:
319 319 os.unlink(undo)
320 320 except OSError, inst:
321 321 self.ui.warn('error removing undo: %s\n' % str(inst))
322 322
323 323 def printdiff(self, repo, node1, node2=None, files=None,
324 324 fp=None, changes=None, opts={}):
325 325 m = cmdutil.match(repo, files, opts)
326 326 patch.diff(repo, node1, node2, m, fp, changes, self.diffopts())
327 327
328 328 def mergeone(self, repo, mergeq, head, patch, rev):
329 329 # first try just applying the patch
330 330 (err, n) = self.apply(repo, [ patch ], update_status=False,
331 331 strict=True, merge=rev)
332 332
333 333 if err == 0:
334 334 return (err, n)
335 335
336 336 if n is None:
337 337 raise util.Abort(_("apply failed for patch %s") % patch)
338 338
339 339 self.ui.warn("patch didn't work out, merging %s\n" % patch)
340 340
341 341 # apply failed, strip away that rev and merge.
342 342 hg.clean(repo, head)
343 343 self.strip(repo, n, update=False, backup='strip')
344 344
345 345 ctx = repo.changectx(rev)
346 346 ret = hg.merge(repo, rev)
347 347 if ret:
348 348 raise util.Abort(_("update returned %d") % ret)
349 349 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
350 350 if n == None:
351 351 raise util.Abort(_("repo commit failed"))
352 352 try:
353 353 message, comments, user, date, patchfound = mergeq.readheaders(patch)
354 354 except:
355 355 raise util.Abort(_("unable to read %s") % patch)
356 356
357 357 patchf = self.opener(patch, "w")
358 358 if comments:
359 359 comments = "\n".join(comments) + '\n\n'
360 360 patchf.write(comments)
361 361 self.printdiff(repo, head, n, fp=patchf)
362 362 patchf.close()
363 363 self.removeundo(repo)
364 364 return (0, n)
365 365
366 366 def qparents(self, repo, rev=None):
367 367 if rev is None:
368 368 (p1, p2) = repo.dirstate.parents()
369 369 if p2 == revlog.nullid:
370 370 return p1
371 371 if len(self.applied) == 0:
372 372 return None
373 373 return revlog.bin(self.applied[-1].rev)
374 374 pp = repo.changelog.parents(rev)
375 375 if pp[1] != revlog.nullid:
376 376 arevs = [ x.rev for x in self.applied ]
377 377 p0 = revlog.hex(pp[0])
378 378 p1 = revlog.hex(pp[1])
379 379 if p0 in arevs:
380 380 return pp[0]
381 381 if p1 in arevs:
382 382 return pp[1]
383 383 return pp[0]
384 384
385 385 def mergepatch(self, repo, mergeq, series):
386 386 if len(self.applied) == 0:
387 387 # each of the patches merged in will have two parents. This
388 388 # can confuse the qrefresh, qdiff, and strip code because it
389 389 # needs to know which parent is actually in the patch queue.
390 390 # so, we insert a merge marker with only one parent. This way
391 391 # the first patch in the queue is never a merge patch
392 392 #
393 393 pname = ".hg.patches.merge.marker"
394 394 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
395 395 self.removeundo(repo)
396 396 self.applied.append(statusentry(revlog.hex(n), pname))
397 397 self.applied_dirty = 1
398 398
399 399 head = self.qparents(repo)
400 400
401 401 for patch in series:
402 402 patch = mergeq.lookup(patch, strict=True)
403 403 if not patch:
404 404 self.ui.warn("patch %s does not exist\n" % patch)
405 405 return (1, None)
406 406 pushable, reason = self.pushable(patch)
407 407 if not pushable:
408 408 self.explain_pushable(patch, all_patches=True)
409 409 continue
410 410 info = mergeq.isapplied(patch)
411 411 if not info:
412 412 self.ui.warn("patch %s is not applied\n" % patch)
413 413 return (1, None)
414 414 rev = revlog.bin(info[1])
415 415 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
416 416 if head:
417 417 self.applied.append(statusentry(revlog.hex(head), patch))
418 418 self.applied_dirty = 1
419 419 if err:
420 420 return (err, head)
421 421 self.save_dirty()
422 422 return (0, head)
423 423
424 424 def patch(self, repo, patchfile):
425 425 '''Apply patchfile to the working directory.
426 426 patchfile: file name of patch'''
427 427 files = {}
428 428 try:
429 429 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
430 430 files=files)
431 431 except Exception, inst:
432 432 self.ui.note(str(inst) + '\n')
433 433 if not self.ui.verbose:
434 434 self.ui.warn("patch failed, unable to continue (try -v)\n")
435 435 return (False, files, False)
436 436
437 437 return (True, files, fuzz)
438 438
439 439 def apply(self, repo, series, list=False, update_status=True,
440 440 strict=False, patchdir=None, merge=None, all_files={}):
441 441 wlock = lock = tr = None
442 442 try:
443 443 wlock = repo.wlock()
444 444 lock = repo.lock()
445 445 tr = repo.transaction()
446 446 try:
447 447 ret = self._apply(repo, series, list, update_status,
448 448 strict, patchdir, merge, all_files=all_files)
449 449 tr.close()
450 450 self.save_dirty()
451 451 return ret
452 452 except:
453 453 try:
454 454 tr.abort()
455 455 finally:
456 456 repo.invalidate()
457 457 repo.dirstate.invalidate()
458 458 raise
459 459 finally:
460 460 del tr, lock, wlock
461 461 self.removeundo(repo)
462 462
463 463 def _apply(self, repo, series, list=False, update_status=True,
464 464 strict=False, patchdir=None, merge=None, all_files={}):
465 465 # TODO unify with commands.py
466 466 if not patchdir:
467 467 patchdir = self.path
468 468 err = 0
469 469 n = None
470 470 for patchname in series:
471 471 pushable, reason = self.pushable(patchname)
472 472 if not pushable:
473 473 self.explain_pushable(patchname, all_patches=True)
474 474 continue
475 475 self.ui.warn("applying %s\n" % patchname)
476 476 pf = os.path.join(patchdir, patchname)
477 477
478 478 try:
479 479 message, comments, user, date, patchfound = self.readheaders(patchname)
480 480 except:
481 481 self.ui.warn("Unable to read %s\n" % patchname)
482 482 err = 1
483 483 break
484 484
485 485 if not message:
486 486 message = "imported patch %s\n" % patchname
487 487 else:
488 488 if list:
489 489 message.append("\nimported patch %s" % patchname)
490 490 message = '\n'.join(message)
491 491
492 492 (patcherr, files, fuzz) = self.patch(repo, pf)
493 493 all_files.update(files)
494 494 patcherr = not patcherr
495 495
496 496 if merge and files:
497 497 # Mark as removed/merged and update dirstate parent info
498 498 removed = []
499 499 merged = []
500 500 for f in files:
501 501 if os.path.exists(repo.wjoin(f)):
502 502 merged.append(f)
503 503 else:
504 504 removed.append(f)
505 505 for f in removed:
506 506 repo.dirstate.remove(f)
507 507 for f in merged:
508 508 repo.dirstate.merge(f)
509 509 p1, p2 = repo.dirstate.parents()
510 510 repo.dirstate.setparents(p1, merge)
511 511
512 512 files = patch.updatedir(self.ui, repo, files)
513 513 match = cmdutil.matchfiles(repo, files or [])
514 514 n = repo.commit(files, message, user, date, match=match,
515 515 force=True)
516 516
517 517 if n == None:
518 518 raise util.Abort(_("repo commit failed"))
519 519
520 520 if update_status:
521 521 self.applied.append(statusentry(revlog.hex(n), patchname))
522 522
523 523 if patcherr:
524 524 if not patchfound:
525 525 self.ui.warn("patch %s is empty\n" % patchname)
526 526 err = 0
527 527 else:
528 528 self.ui.warn("patch failed, rejects left in working dir\n")
529 529 err = 1
530 530 break
531 531
532 532 if fuzz and strict:
533 533 self.ui.warn("fuzz found when applying patch, stopping\n")
534 534 err = 1
535 535 break
536 536 return (err, n)
537 537
538 538 def _clean_series(self, patches):
539 539 indices = [self.find_series(p) for p in patches]
540 540 indices.sort()
541 541 for i in indices[-1::-1]:
542 542 del self.full_series[i]
543 543 self.parse_series()
544 544 self.series_dirty = 1
545 545
546 546 def finish(self, repo, revs):
547 547 revs.sort()
548 548 firstrev = repo.changelog.rev(revlog.bin(self.applied[0].rev))
549 549 appliedbase = 0
550 550 patches = []
551 551 for rev in revs:
552 552 if rev < firstrev:
553 553 raise util.Abort(_('revision %d is not managed') % rev)
554 554 base = revlog.bin(self.applied[appliedbase].rev)
555 555 node = repo.changelog.node(rev)
556 556 if node != base:
557 557 raise util.Abort(_('cannot delete revision %d above '
558 558 'applied patches') % rev)
559 559 patches.append(self.applied[appliedbase].name)
560 560 appliedbase += 1
561 561
562 562 r = self.qrepo()
563 563 if r:
564 564 r.remove(patches, True)
565 565 else:
566 566 for p in patches:
567 567 os.unlink(self.join(p))
568 568
569 569 del self.applied[:appliedbase]
570 570 self.applied_dirty = 1
571 571 self._clean_series(patches)
572 572
573 573 def delete(self, repo, patches, opts):
574 574 if not patches and not opts.get('rev'):
575 575 raise util.Abort(_('qdelete requires at least one revision or '
576 576 'patch name'))
577 577
578 578 realpatches = []
579 579 for patch in patches:
580 580 patch = self.lookup(patch, strict=True)
581 581 info = self.isapplied(patch)
582 582 if info:
583 583 raise util.Abort(_("cannot delete applied patch %s") % patch)
584 584 if patch not in self.series:
585 585 raise util.Abort(_("patch %s not in series file") % patch)
586 586 realpatches.append(patch)
587 587
588 588 appliedbase = 0
589 589 if opts.get('rev'):
590 590 if not self.applied:
591 591 raise util.Abort(_('no patches applied'))
592 592 revs = cmdutil.revrange(repo, opts['rev'])
593 593 if len(revs) > 1 and revs[0] > revs[1]:
594 594 revs.reverse()
595 595 for rev in revs:
596 596 if appliedbase >= len(self.applied):
597 597 raise util.Abort(_("revision %d is not managed") % rev)
598 598
599 599 base = revlog.bin(self.applied[appliedbase].rev)
600 600 node = repo.changelog.node(rev)
601 601 if node != base:
602 602 raise util.Abort(_("cannot delete revision %d above "
603 603 "applied patches") % rev)
604 604 realpatches.append(self.applied[appliedbase].name)
605 605 appliedbase += 1
606 606
607 607 if not opts.get('keep'):
608 608 r = self.qrepo()
609 609 if r:
610 610 r.remove(realpatches, True)
611 611 else:
612 612 for p in realpatches:
613 613 os.unlink(self.join(p))
614 614
615 615 if appliedbase:
616 616 del self.applied[:appliedbase]
617 617 self.applied_dirty = 1
618 618 self._clean_series(realpatches)
619 619
620 620 def check_toppatch(self, repo):
621 621 if len(self.applied) > 0:
622 622 top = revlog.bin(self.applied[-1].rev)
623 623 pp = repo.dirstate.parents()
624 624 if top not in pp:
625 625 raise util.Abort(_("working directory revision is not qtip"))
626 626 return top
627 627 return None
628 628 def check_localchanges(self, repo, force=False, refresh=True):
629 629 m, a, r, d = repo.status()[:4]
630 630 if m or a or r or d:
631 631 if not force:
632 632 if refresh:
633 633 raise util.Abort(_("local changes found, refresh first"))
634 634 else:
635 635 raise util.Abort(_("local changes found"))
636 636 return m, a, r, d
637 637
638 638 _reserved = ('series', 'status', 'guards')
639 639 def check_reserved_name(self, name):
640 640 if (name in self._reserved or name.startswith('.hg')
641 641 or name.startswith('.mq')):
642 642 raise util.Abort(_('"%s" cannot be used as the name of a patch')
643 643 % name)
644 644
645 645 def new(self, repo, patch, *pats, **opts):
646 646 msg = opts.get('msg')
647 647 force = opts.get('force')
648 648 user = opts.get('user')
649 649 date = opts.get('date')
650 650 if date:
651 651 date = util.parsedate(date)
652 652 self.check_reserved_name(patch)
653 653 if os.path.exists(self.join(patch)):
654 654 raise util.Abort(_('patch "%s" already exists') % patch)
655 655 if opts.get('include') or opts.get('exclude') or pats:
656 656 match = cmdutil.match(repo, pats, opts)
657 657 m, a, r, d = repo.status(match=match)[:4]
658 658 else:
659 659 m, a, r, d = self.check_localchanges(repo, force)
660 660 match = cmdutil.match(repo, m + a + r)
661 661 commitfiles = m + a + r
662 662 self.check_toppatch(repo)
663 663 wlock = repo.wlock()
664 664 try:
665 665 insert = self.full_series_end()
666 666 commitmsg = msg and msg or ("[mq]: %s" % patch)
667 667 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
668 668 if n == None:
669 669 raise util.Abort(_("repo commit failed"))
670 670 self.full_series[insert:insert] = [patch]
671 671 self.applied.append(statusentry(revlog.hex(n), patch))
672 672 self.parse_series()
673 673 self.series_dirty = 1
674 674 self.applied_dirty = 1
675 675 p = self.opener(patch, "w")
676 676 if date:
677 677 p.write("# HG changeset patch\n")
678 678 if user:
679 679 p.write("# User " + user + "\n")
680 680 p.write("# Date %d %d\n" % date)
681 681 p.write("\n")
682 682 elif user:
683 683 p.write("From: " + user + "\n")
684 684 p.write("\n")
685 685 if msg:
686 686 msg = msg + "\n"
687 687 p.write(msg)
688 688 p.close()
689 689 wlock = None
690 690 r = self.qrepo()
691 691 if r: r.add([patch])
692 692 if commitfiles:
693 693 self.refresh(repo, short=True, git=opts.get('git'))
694 694 self.removeundo(repo)
695 695 finally:
696 696 del wlock
697 697
698 698 def strip(self, repo, rev, update=True, backup="all", force=None):
699 699 wlock = lock = None
700 700 try:
701 701 wlock = repo.wlock()
702 702 lock = repo.lock()
703 703
704 704 if update:
705 705 self.check_localchanges(repo, force=force, refresh=False)
706 706 urev = self.qparents(repo, rev)
707 707 hg.clean(repo, urev)
708 708 repo.dirstate.write()
709 709
710 710 self.removeundo(repo)
711 711 repair.strip(self.ui, repo, rev, backup)
712 712 # strip may have unbundled a set of backed up revisions after
713 713 # the actual strip
714 714 self.removeundo(repo)
715 715 finally:
716 716 del lock, wlock
717 717
718 718 def isapplied(self, patch):
719 719 """returns (index, rev, patch)"""
720 720 for i in xrange(len(self.applied)):
721 721 a = self.applied[i]
722 722 if a.name == patch:
723 723 return (i, a.rev, a.name)
724 724 return None
725 725
726 726 # if the exact patch name does not exist, we try a few
727 727 # variations. If strict is passed, we try only #1
728 728 #
729 729 # 1) a number to indicate an offset in the series file
730 730 # 2) a unique substring of the patch name was given
731 731 # 3) patchname[-+]num to indicate an offset in the series file
732 732 def lookup(self, patch, strict=False):
733 733 patch = patch and str(patch)
734 734
735 735 def partial_name(s):
736 736 if s in self.series:
737 737 return s
738 738 matches = [x for x in self.series if s in x]
739 739 if len(matches) > 1:
740 740 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
741 741 for m in matches:
742 742 self.ui.warn(' %s\n' % m)
743 743 return None
744 744 if matches:
745 745 return matches[0]
746 746 if len(self.series) > 0 and len(self.applied) > 0:
747 747 if s == 'qtip':
748 748 return self.series[self.series_end(True)-1]
749 749 if s == 'qbase':
750 750 return self.series[0]
751 751 return None
752 752 if patch == None:
753 753 return None
754 754
755 755 # we don't want to return a partial match until we make
756 756 # sure the file name passed in does not exist (checked below)
757 757 res = partial_name(patch)
758 758 if res and res == patch:
759 759 return res
760 760
761 761 if not os.path.isfile(self.join(patch)):
762 762 try:
763 763 sno = int(patch)
764 764 except(ValueError, OverflowError):
765 765 pass
766 766 else:
767 767 if sno < len(self.series):
768 768 return self.series[sno]
769 769 if not strict:
770 770 # return any partial match made above
771 771 if res:
772 772 return res
773 773 minus = patch.rfind('-')
774 774 if minus >= 0:
775 775 res = partial_name(patch[:minus])
776 776 if res:
777 777 i = self.series.index(res)
778 778 try:
779 779 off = int(patch[minus+1:] or 1)
780 780 except(ValueError, OverflowError):
781 781 pass
782 782 else:
783 783 if i - off >= 0:
784 784 return self.series[i - off]
785 785 plus = patch.rfind('+')
786 786 if plus >= 0:
787 787 res = partial_name(patch[:plus])
788 788 if res:
789 789 i = self.series.index(res)
790 790 try:
791 791 off = int(patch[plus+1:] or 1)
792 792 except(ValueError, OverflowError):
793 793 pass
794 794 else:
795 795 if i + off < len(self.series):
796 796 return self.series[i + off]
797 797 raise util.Abort(_("patch %s not in series") % patch)
798 798
799 799 def push(self, repo, patch=None, force=False, list=False,
800 800 mergeq=None):
801 801 wlock = repo.wlock()
802 802 if repo.dirstate.parents()[0] != repo.changelog.tip():
803 803 self.ui.status(_("(working directory not at tip)\n"))
804 804
805 805 try:
806 806 patch = self.lookup(patch)
807 807 # Suppose our series file is: A B C and the current 'top'
808 808 # patch is B. qpush C should be performed (moving forward)
809 809 # qpush B is a NOP (no change) qpush A is an error (can't
810 810 # go backwards with qpush)
811 811 if patch:
812 812 info = self.isapplied(patch)
813 813 if info:
814 814 if info[0] < len(self.applied) - 1:
815 815 raise util.Abort(
816 816 _("cannot push to a previous patch: %s") % patch)
817 817 if info[0] < len(self.series) - 1:
818 818 self.ui.warn(
819 819 _('qpush: %s is already at the top\n') % patch)
820 820 else:
821 821 self.ui.warn(_('all patches are currently applied\n'))
822 822 return
823 823
824 824 # Following the above example, starting at 'top' of B:
825 825 # qpush should be performed (pushes C), but a subsequent
826 826 # qpush without an argument is an error (nothing to
827 827 # apply). This allows a loop of "...while hg qpush..." to
828 828 # work as it detects an error when done
829 829 if self.series_end() == len(self.series):
830 830 self.ui.warn(_('patch series already fully applied\n'))
831 831 return 1
832 832 if not force:
833 833 self.check_localchanges(repo)
834 834
835 835 self.applied_dirty = 1;
836 836 start = self.series_end()
837 837 if start > 0:
838 838 self.check_toppatch(repo)
839 839 if not patch:
840 840 patch = self.series[start]
841 841 end = start + 1
842 842 else:
843 843 end = self.series.index(patch, start) + 1
844 844 s = self.series[start:end]
845 845 all_files = {}
846 846 try:
847 847 if mergeq:
848 848 ret = self.mergepatch(repo, mergeq, s)
849 849 else:
850 850 ret = self.apply(repo, s, list, all_files=all_files)
851 851 except:
852 852 self.ui.warn(_('cleaning up working directory...'))
853 853 node = repo.dirstate.parents()[0]
854 854 hg.revert(repo, node, None)
855 855 unknown = repo.status()[4]
856 856 # only remove unknown files that we know we touched or
857 857 # created while patching
858 858 for f in unknown:
859 859 if f in all_files:
860 860 util.unlink(repo.wjoin(f))
861 861 self.ui.warn(_('done\n'))
862 862 raise
863 863 top = self.applied[-1].name
864 864 if ret[0]:
865 865 self.ui.write(
866 866 "Errors during apply, please fix and refresh %s\n" % top)
867 867 else:
868 868 self.ui.write("Now at: %s\n" % top)
869 869 return ret[0]
870 870 finally:
871 871 del wlock
872 872
873 873 def pop(self, repo, patch=None, force=False, update=True, all=False):
874 874 def getfile(f, rev, flags):
875 875 t = repo.file(f).read(rev)
876 876 repo.wwrite(f, t, flags)
877 877
878 878 wlock = repo.wlock()
879 879 try:
880 880 if patch:
881 881 # index, rev, patch
882 882 info = self.isapplied(patch)
883 883 if not info:
884 884 patch = self.lookup(patch)
885 885 info = self.isapplied(patch)
886 886 if not info:
887 887 raise util.Abort(_("patch %s is not applied") % patch)
888 888
889 889 if len(self.applied) == 0:
890 890 # Allow qpop -a to work repeatedly,
891 891 # but not qpop without an argument
892 892 self.ui.warn(_("no patches applied\n"))
893 893 return not all
894 894
895 895 if not update:
896 896 parents = repo.dirstate.parents()
897 897 rr = [ revlog.bin(x.rev) for x in self.applied ]
898 898 for p in parents:
899 899 if p in rr:
900 900 self.ui.warn("qpop: forcing dirstate update\n")
901 901 update = True
902 902
903 903 if not force and update:
904 904 self.check_localchanges(repo)
905 905
906 906 self.applied_dirty = 1;
907 907 end = len(self.applied)
908 908 if not patch:
909 909 if all:
910 910 popi = 0
911 911 else:
912 912 popi = len(self.applied) - 1
913 913 else:
914 914 popi = info[0] + 1
915 915 if popi >= end:
916 916 self.ui.warn("qpop: %s is already at the top\n" % patch)
917 917 return
918 918 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
919 919
920 920 start = info[0]
921 921 rev = revlog.bin(info[1])
922 922
923 923 if update:
924 924 top = self.check_toppatch(repo)
925 925
926 926 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
927 927 raise util.Abort("popping would remove a revision not "
928 928 "managed by this patch queue")
929 929
930 930 # we know there are no local changes, so we can make a simplified
931 931 # form of hg.update.
932 932 if update:
933 933 qp = self.qparents(repo, rev)
934 934 changes = repo.changelog.read(qp)
935 935 mmap = repo.manifest.read(changes[0])
936 936 m, a, r, d, u = repo.status(qp, top)[:5]
937 937 if d:
938 938 raise util.Abort("deletions found between repo revs")
939 939 for f in m:
940 940 getfile(f, mmap[f], mmap.flags(f))
941 941 for f in r:
942 942 getfile(f, mmap[f], mmap.flags(f))
943 943 for f in m + r:
944 944 repo.dirstate.normal(f)
945 945 for f in a:
946 946 try:
947 947 os.unlink(repo.wjoin(f))
948 948 except OSError, e:
949 949 if e.errno != errno.ENOENT:
950 950 raise
951 951 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
952 952 except: pass
953 953 repo.dirstate.forget(f)
954 954 repo.dirstate.setparents(qp, revlog.nullid)
955 955 del self.applied[start:end]
956 956 self.strip(repo, rev, update=False, backup='strip')
957 957 if len(self.applied):
958 958 self.ui.write("Now at: %s\n" % self.applied[-1].name)
959 959 else:
960 960 self.ui.write("Patch queue now empty\n")
961 961 finally:
962 962 del wlock
963 963
964 964 def diff(self, repo, pats, opts):
965 965 top = self.check_toppatch(repo)
966 966 if not top:
967 967 self.ui.write("No patches applied\n")
968 968 return
969 969 qp = self.qparents(repo, top)
970 if opts.get('git'):
971 self.diffopts().git = True
972 if opts.get('unified') is not None:
973 self.diffopts().context = opts['unified']
970 self._diffopts = patch.diffopts(self.ui, opts)
974 971 self.printdiff(repo, qp, files=pats, opts=opts)
975 972
976 973 def refresh(self, repo, pats=None, **opts):
977 974 if len(self.applied) == 0:
978 975 self.ui.write("No patches applied\n")
979 976 return 1
980 977 newdate = opts.get('date')
981 978 if newdate:
982 979 newdate = '%d %d' % util.parsedate(newdate)
983 980 wlock = repo.wlock()
984 981 try:
985 982 self.check_toppatch(repo)
986 983 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
987 984 top = revlog.bin(top)
988 985 if repo.changelog.heads(top) != [top]:
989 986 raise util.Abort("cannot refresh a revision with children")
990 987 cparents = repo.changelog.parents(top)
991 988 patchparent = self.qparents(repo, top)
992 989 message, comments, user, date, patchfound = self.readheaders(patchfn)
993 990
994 991 patchf = self.opener(patchfn, 'r+')
995 992
996 993 # if the patch was a git patch, refresh it as a git patch
997 994 for line in patchf:
998 995 if line.startswith('diff --git'):
999 996 self.diffopts().git = True
1000 997 break
1001 998
1002 999 msg = opts.get('msg', '').rstrip()
1003 1000 if msg and comments:
1004 1001 # Remove existing message, keeping the rest of the comments
1005 1002 # fields.
1006 1003 # If comments contains 'subject: ', message will prepend
1007 1004 # the field and a blank line.
1008 1005 if message:
1009 1006 subj = 'subject: ' + message[0].lower()
1010 1007 for i in xrange(len(comments)):
1011 1008 if subj == comments[i].lower():
1012 1009 del comments[i]
1013 1010 message = message[2:]
1014 1011 break
1015 1012 ci = 0
1016 1013 for mi in xrange(len(message)):
1017 1014 while message[mi] != comments[ci]:
1018 1015 ci += 1
1019 1016 del comments[ci]
1020 1017
1021 1018 def setheaderfield(comments, prefixes, new):
1022 1019 # Update all references to a field in the patch header.
1023 1020 # If none found, add it email style.
1024 1021 res = False
1025 1022 for prefix in prefixes:
1026 1023 for i in xrange(len(comments)):
1027 1024 if comments[i].startswith(prefix):
1028 1025 comments[i] = prefix + new
1029 1026 res = True
1030 1027 break
1031 1028 return res
1032 1029
1033 1030 newuser = opts.get('user')
1034 1031 if newuser:
1035 1032 if not setheaderfield(comments, ['From: ', '# User '], newuser):
1036 1033 try:
1037 1034 patchheaderat = comments.index('# HG changeset patch')
1038 1035 comments.insert(patchheaderat + 1,'# User ' + newuser)
1039 1036 except ValueError:
1040 1037 comments = ['From: ' + newuser, ''] + comments
1041 1038 user = newuser
1042 1039
1043 1040 if newdate:
1044 1041 if setheaderfield(comments, ['# Date '], newdate):
1045 1042 date = newdate
1046 1043
1047 1044 if msg:
1048 1045 comments.append(msg)
1049 1046
1050 1047 patchf.seek(0)
1051 1048 patchf.truncate()
1052 1049
1053 1050 if comments:
1054 1051 comments = "\n".join(comments) + '\n\n'
1055 1052 patchf.write(comments)
1056 1053
1057 1054 if opts.get('git'):
1058 1055 self.diffopts().git = True
1059 1056 matchfn = cmdutil.match(repo, pats, opts)
1060 1057 tip = repo.changelog.tip()
1061 1058 if top == tip:
1062 1059 # if the top of our patch queue is also the tip, there is an
1063 1060 # optimization here. We update the dirstate in place and strip
1064 1061 # off the tip commit. Then just commit the current directory
1065 1062 # tree. We can also send repo.commit the list of files
1066 1063 # changed to speed up the diff
1067 1064 #
1068 1065 # in short mode, we only diff the files included in the
1069 1066 # patch already
1070 1067 #
1071 1068 # this should really read:
1072 1069 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
1073 1070 # but we do it backwards to take advantage of manifest/chlog
1074 1071 # caching against the next repo.status call
1075 1072 #
1076 1073 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
1077 1074 changes = repo.changelog.read(tip)
1078 1075 man = repo.manifest.read(changes[0])
1079 1076 aaa = aa[:]
1080 1077 if opts.get('short'):
1081 1078 match = cmdutil.matchfiles(repo, mm + aa + dd)
1082 1079 else:
1083 1080 match = cmdutil.matchall(repo)
1084 1081 m, a, r, d, u = repo.status(match=match)[:5]
1085 1082
1086 1083 # we might end up with files that were added between
1087 1084 # tip and the dirstate parent, but then changed in the
1088 1085 # local dirstate. in this case, we want them to only
1089 1086 # show up in the added section
1090 1087 for x in m:
1091 1088 if x not in aa:
1092 1089 mm.append(x)
1093 1090 # we might end up with files added by the local dirstate that
1094 1091 # were deleted by the patch. In this case, they should only
1095 1092 # show up in the changed section.
1096 1093 for x in a:
1097 1094 if x in dd:
1098 1095 del dd[dd.index(x)]
1099 1096 mm.append(x)
1100 1097 else:
1101 1098 aa.append(x)
1102 1099 # make sure any files deleted in the local dirstate
1103 1100 # are not in the add or change column of the patch
1104 1101 forget = []
1105 1102 for x in d + r:
1106 1103 if x in aa:
1107 1104 del aa[aa.index(x)]
1108 1105 forget.append(x)
1109 1106 continue
1110 1107 elif x in mm:
1111 1108 del mm[mm.index(x)]
1112 1109 dd.append(x)
1113 1110
1114 1111 m = util.unique(mm)
1115 1112 r = util.unique(dd)
1116 1113 a = util.unique(aa)
1117 1114 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1118 1115 match = cmdutil.matchfiles(repo, util.unique(c[0] + c[1] + c[2]))
1119 1116 patch.diff(repo, patchparent, match=match,
1120 1117 fp=patchf, changes=c, opts=self.diffopts())
1121 1118 patchf.close()
1122 1119
1123 1120 repo.dirstate.setparents(*cparents)
1124 1121 copies = {}
1125 1122 for dst in a:
1126 1123 src = repo.dirstate.copied(dst)
1127 1124 if src is not None:
1128 1125 copies.setdefault(src, []).append(dst)
1129 1126 repo.dirstate.add(dst)
1130 1127 # remember the copies between patchparent and tip
1131 1128 # this may be slow, so don't do it if we're not tracking copies
1132 1129 if self.diffopts().git:
1133 1130 for dst in aaa:
1134 1131 f = repo.file(dst)
1135 1132 src = f.renamed(man[dst])
1136 1133 if src:
1137 1134 copies[src[0]] = copies.get(dst, [])
1138 1135 if dst in a:
1139 1136 copies[src[0]].append(dst)
1140 1137 # we can't copy a file created by the patch itself
1141 1138 if dst in copies:
1142 1139 del copies[dst]
1143 1140 for src, dsts in copies.iteritems():
1144 1141 for dst in dsts:
1145 1142 repo.dirstate.copy(src, dst)
1146 1143 for f in r:
1147 1144 repo.dirstate.remove(f)
1148 1145 # if the patch excludes a modified file, mark that
1149 1146 # file with mtime=0 so status can see it.
1150 1147 mm = []
1151 1148 for i in xrange(len(m)-1, -1, -1):
1152 1149 if not matchfn(m[i]):
1153 1150 mm.append(m[i])
1154 1151 del m[i]
1155 1152 for f in m:
1156 1153 repo.dirstate.normal(f)
1157 1154 for f in mm:
1158 1155 repo.dirstate.normallookup(f)
1159 1156 for f in forget:
1160 1157 repo.dirstate.forget(f)
1161 1158
1162 1159 if not msg:
1163 1160 if not message:
1164 1161 message = "[mq]: %s\n" % patchfn
1165 1162 else:
1166 1163 message = "\n".join(message)
1167 1164 else:
1168 1165 message = msg
1169 1166
1170 1167 if not user:
1171 1168 user = changes[1]
1172 1169
1173 1170 self.applied.pop()
1174 1171 self.applied_dirty = 1
1175 1172 self.strip(repo, top, update=False,
1176 1173 backup='strip')
1177 1174 n = repo.commit(match.files(), message, user, date, match=match,
1178 1175 force=1)
1179 1176 self.applied.append(statusentry(revlog.hex(n), patchfn))
1180 1177 self.removeundo(repo)
1181 1178 else:
1182 1179 self.printdiff(repo, patchparent, fp=patchf)
1183 1180 patchf.close()
1184 1181 added = repo.status()[1]
1185 1182 for a in added:
1186 1183 f = repo.wjoin(a)
1187 1184 try:
1188 1185 os.unlink(f)
1189 1186 except OSError, e:
1190 1187 if e.errno != errno.ENOENT:
1191 1188 raise
1192 1189 try: os.removedirs(os.path.dirname(f))
1193 1190 except: pass
1194 1191 # forget the file copies in the dirstate
1195 1192 # push should readd the files later on
1196 1193 repo.dirstate.forget(a)
1197 1194 self.pop(repo, force=True)
1198 1195 self.push(repo, force=True)
1199 1196 finally:
1200 1197 del wlock
1201 1198
1202 1199 def init(self, repo, create=False):
1203 1200 if not create and os.path.isdir(self.path):
1204 1201 raise util.Abort(_("patch queue directory already exists"))
1205 1202 try:
1206 1203 os.mkdir(self.path)
1207 1204 except OSError, inst:
1208 1205 if inst.errno != errno.EEXIST or not create:
1209 1206 raise
1210 1207 if create:
1211 1208 return self.qrepo(create=True)
1212 1209
1213 1210 def unapplied(self, repo, patch=None):
1214 1211 if patch and patch not in self.series:
1215 1212 raise util.Abort(_("patch %s is not in series file") % patch)
1216 1213 if not patch:
1217 1214 start = self.series_end()
1218 1215 else:
1219 1216 start = self.series.index(patch) + 1
1220 1217 unapplied = []
1221 1218 for i in xrange(start, len(self.series)):
1222 1219 pushable, reason = self.pushable(i)
1223 1220 if pushable:
1224 1221 unapplied.append((i, self.series[i]))
1225 1222 self.explain_pushable(i)
1226 1223 return unapplied
1227 1224
1228 1225 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1229 1226 summary=False):
1230 1227 def displayname(patchname):
1231 1228 if summary:
1232 1229 msg = self.readheaders(patchname)[0]
1233 1230 msg = msg and ': ' + msg[0] or ': '
1234 1231 else:
1235 1232 msg = ''
1236 1233 return '%s%s' % (patchname, msg)
1237 1234
1238 1235 applied = dict.fromkeys([p.name for p in self.applied])
1239 1236 if length is None:
1240 1237 length = len(self.series) - start
1241 1238 if not missing:
1242 1239 for i in xrange(start, start+length):
1243 1240 patch = self.series[i]
1244 1241 if patch in applied:
1245 1242 stat = 'A'
1246 1243 elif self.pushable(i)[0]:
1247 1244 stat = 'U'
1248 1245 else:
1249 1246 stat = 'G'
1250 1247 pfx = ''
1251 1248 if self.ui.verbose:
1252 1249 pfx = '%d %s ' % (i, stat)
1253 1250 elif status and status != stat:
1254 1251 continue
1255 1252 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1256 1253 else:
1257 1254 msng_list = []
1258 1255 for root, dirs, files in os.walk(self.path):
1259 1256 d = root[len(self.path) + 1:]
1260 1257 for f in files:
1261 1258 fl = os.path.join(d, f)
1262 1259 if (fl not in self.series and
1263 1260 fl not in (self.status_path, self.series_path,
1264 1261 self.guards_path)
1265 1262 and not fl.startswith('.')):
1266 1263 msng_list.append(fl)
1267 1264 msng_list.sort()
1268 1265 for x in msng_list:
1269 1266 pfx = self.ui.verbose and ('D ') or ''
1270 1267 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1271 1268
1272 1269 def issaveline(self, l):
1273 1270 if l.name == '.hg.patches.save.line':
1274 1271 return True
1275 1272
1276 1273 def qrepo(self, create=False):
1277 1274 if create or os.path.isdir(self.join(".hg")):
1278 1275 return hg.repository(self.ui, path=self.path, create=create)
1279 1276
1280 1277 def restore(self, repo, rev, delete=None, qupdate=None):
1281 1278 c = repo.changelog.read(rev)
1282 1279 desc = c[4].strip()
1283 1280 lines = desc.splitlines()
1284 1281 i = 0
1285 1282 datastart = None
1286 1283 series = []
1287 1284 applied = []
1288 1285 qpp = None
1289 1286 for i in xrange(0, len(lines)):
1290 1287 if lines[i] == 'Patch Data:':
1291 1288 datastart = i + 1
1292 1289 elif lines[i].startswith('Dirstate:'):
1293 1290 l = lines[i].rstrip()
1294 1291 l = l[10:].split(' ')
1295 1292 qpp = [ bin(x) for x in l ]
1296 1293 elif datastart != None:
1297 1294 l = lines[i].rstrip()
1298 1295 se = statusentry(l)
1299 1296 file_ = se.name
1300 1297 if se.rev:
1301 1298 applied.append(se)
1302 1299 else:
1303 1300 series.append(file_)
1304 1301 if datastart == None:
1305 1302 self.ui.warn("No saved patch data found\n")
1306 1303 return 1
1307 1304 self.ui.warn("restoring status: %s\n" % lines[0])
1308 1305 self.full_series = series
1309 1306 self.applied = applied
1310 1307 self.parse_series()
1311 1308 self.series_dirty = 1
1312 1309 self.applied_dirty = 1
1313 1310 heads = repo.changelog.heads()
1314 1311 if delete:
1315 1312 if rev not in heads:
1316 1313 self.ui.warn("save entry has children, leaving it alone\n")
1317 1314 else:
1318 1315 self.ui.warn("removing save entry %s\n" % short(rev))
1319 1316 pp = repo.dirstate.parents()
1320 1317 if rev in pp:
1321 1318 update = True
1322 1319 else:
1323 1320 update = False
1324 1321 self.strip(repo, rev, update=update, backup='strip')
1325 1322 if qpp:
1326 1323 self.ui.warn("saved queue repository parents: %s %s\n" %
1327 1324 (short(qpp[0]), short(qpp[1])))
1328 1325 if qupdate:
1329 1326 self.ui.status(_("queue directory updating\n"))
1330 1327 r = self.qrepo()
1331 1328 if not r:
1332 1329 self.ui.warn("Unable to load queue repository\n")
1333 1330 return 1
1334 1331 hg.clean(r, qpp[0])
1335 1332
1336 1333 def save(self, repo, msg=None):
1337 1334 if len(self.applied) == 0:
1338 1335 self.ui.warn("save: no patches applied, exiting\n")
1339 1336 return 1
1340 1337 if self.issaveline(self.applied[-1]):
1341 1338 self.ui.warn("status is already saved\n")
1342 1339 return 1
1343 1340
1344 1341 ar = [ ':' + x for x in self.full_series ]
1345 1342 if not msg:
1346 1343 msg = "hg patches saved state"
1347 1344 else:
1348 1345 msg = "hg patches: " + msg.rstrip('\r\n')
1349 1346 r = self.qrepo()
1350 1347 if r:
1351 1348 pp = r.dirstate.parents()
1352 1349 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1353 1350 msg += "\n\nPatch Data:\n"
1354 1351 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1355 1352 "\n".join(ar) + '\n' or "")
1356 1353 n = repo.commit(None, text, user=None, force=1)
1357 1354 if not n:
1358 1355 self.ui.warn("repo commit failed\n")
1359 1356 return 1
1360 1357 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1361 1358 self.applied_dirty = 1
1362 1359 self.removeundo(repo)
1363 1360
1364 1361 def full_series_end(self):
1365 1362 if len(self.applied) > 0:
1366 1363 p = self.applied[-1].name
1367 1364 end = self.find_series(p)
1368 1365 if end == None:
1369 1366 return len(self.full_series)
1370 1367 return end + 1
1371 1368 return 0
1372 1369
1373 1370 def series_end(self, all_patches=False):
1374 1371 """If all_patches is False, return the index of the next pushable patch
1375 1372 in the series, or the series length. If all_patches is True, return the
1376 1373 index of the first patch past the last applied one.
1377 1374 """
1378 1375 end = 0
1379 1376 def next(start):
1380 1377 if all_patches:
1381 1378 return start
1382 1379 i = start
1383 1380 while i < len(self.series):
1384 1381 p, reason = self.pushable(i)
1385 1382 if p:
1386 1383 break
1387 1384 self.explain_pushable(i)
1388 1385 i += 1
1389 1386 return i
1390 1387 if len(self.applied) > 0:
1391 1388 p = self.applied[-1].name
1392 1389 try:
1393 1390 end = self.series.index(p)
1394 1391 except ValueError:
1395 1392 return 0
1396 1393 return next(end + 1)
1397 1394 return next(end)
1398 1395
1399 1396 def appliedname(self, index):
1400 1397 pname = self.applied[index].name
1401 1398 if not self.ui.verbose:
1402 1399 p = pname
1403 1400 else:
1404 1401 p = str(self.series.index(pname)) + " " + pname
1405 1402 return p
1406 1403
1407 1404 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1408 1405 force=None, git=False):
1409 1406 def checkseries(patchname):
1410 1407 if patchname in self.series:
1411 1408 raise util.Abort(_('patch %s is already in the series file')
1412 1409 % patchname)
1413 1410 def checkfile(patchname):
1414 1411 if not force and os.path.exists(self.join(patchname)):
1415 1412 raise util.Abort(_('patch "%s" already exists')
1416 1413 % patchname)
1417 1414
1418 1415 if rev:
1419 1416 if files:
1420 1417 raise util.Abort(_('option "-r" not valid when importing '
1421 1418 'files'))
1422 1419 rev = cmdutil.revrange(repo, rev)
1423 1420 rev.sort(lambda x, y: cmp(y, x))
1424 1421 if (len(files) > 1 or len(rev) > 1) and patchname:
1425 1422 raise util.Abort(_('option "-n" not valid when importing multiple '
1426 1423 'patches'))
1427 1424 i = 0
1428 1425 added = []
1429 1426 if rev:
1430 1427 # If mq patches are applied, we can only import revisions
1431 1428 # that form a linear path to qbase.
1432 1429 # Otherwise, they should form a linear path to a head.
1433 1430 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1434 1431 if len(heads) > 1:
1435 1432 raise util.Abort(_('revision %d is the root of more than one '
1436 1433 'branch') % rev[-1])
1437 1434 if self.applied:
1438 1435 base = revlog.hex(repo.changelog.node(rev[0]))
1439 1436 if base in [n.rev for n in self.applied]:
1440 1437 raise util.Abort(_('revision %d is already managed')
1441 1438 % rev[0])
1442 1439 if heads != [revlog.bin(self.applied[-1].rev)]:
1443 1440 raise util.Abort(_('revision %d is not the parent of '
1444 1441 'the queue') % rev[0])
1445 1442 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1446 1443 lastparent = repo.changelog.parentrevs(base)[0]
1447 1444 else:
1448 1445 if heads != [repo.changelog.node(rev[0])]:
1449 1446 raise util.Abort(_('revision %d has unmanaged children')
1450 1447 % rev[0])
1451 1448 lastparent = None
1452 1449
1453 1450 if git:
1454 1451 self.diffopts().git = True
1455 1452
1456 1453 for r in rev:
1457 1454 p1, p2 = repo.changelog.parentrevs(r)
1458 1455 n = repo.changelog.node(r)
1459 1456 if p2 != revlog.nullrev:
1460 1457 raise util.Abort(_('cannot import merge revision %d') % r)
1461 1458 if lastparent and lastparent != r:
1462 1459 raise util.Abort(_('revision %d is not the parent of %d')
1463 1460 % (r, lastparent))
1464 1461 lastparent = p1
1465 1462
1466 1463 if not patchname:
1467 1464 patchname = normname('%d.diff' % r)
1468 1465 self.check_reserved_name(patchname)
1469 1466 checkseries(patchname)
1470 1467 checkfile(patchname)
1471 1468 self.full_series.insert(0, patchname)
1472 1469
1473 1470 patchf = self.opener(patchname, "w")
1474 1471 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1475 1472 patchf.close()
1476 1473
1477 1474 se = statusentry(revlog.hex(n), patchname)
1478 1475 self.applied.insert(0, se)
1479 1476
1480 1477 added.append(patchname)
1481 1478 patchname = None
1482 1479 self.parse_series()
1483 1480 self.applied_dirty = 1
1484 1481
1485 1482 for filename in files:
1486 1483 if existing:
1487 1484 if filename == '-':
1488 1485 raise util.Abort(_('-e is incompatible with import from -'))
1489 1486 if not patchname:
1490 1487 patchname = normname(filename)
1491 1488 self.check_reserved_name(patchname)
1492 1489 if not os.path.isfile(self.join(patchname)):
1493 1490 raise util.Abort(_("patch %s does not exist") % patchname)
1494 1491 else:
1495 1492 try:
1496 1493 if filename == '-':
1497 1494 if not patchname:
1498 1495 raise util.Abort(_('need --name to import a patch from -'))
1499 1496 text = sys.stdin.read()
1500 1497 else:
1501 1498 text = file(filename, 'rb').read()
1502 1499 except IOError:
1503 1500 raise util.Abort(_("unable to read %s") % patchname)
1504 1501 if not patchname:
1505 1502 patchname = normname(os.path.basename(filename))
1506 1503 self.check_reserved_name(patchname)
1507 1504 checkfile(patchname)
1508 1505 patchf = self.opener(patchname, "w")
1509 1506 patchf.write(text)
1510 1507 checkseries(patchname)
1511 1508 index = self.full_series_end() + i
1512 1509 self.full_series[index:index] = [patchname]
1513 1510 self.parse_series()
1514 1511 self.ui.warn("adding %s to series file\n" % patchname)
1515 1512 i += 1
1516 1513 added.append(patchname)
1517 1514 patchname = None
1518 1515 self.series_dirty = 1
1519 1516 qrepo = self.qrepo()
1520 1517 if qrepo:
1521 1518 qrepo.add(added)
1522 1519
1523 1520 def delete(ui, repo, *patches, **opts):
1524 1521 """remove patches from queue
1525 1522
1526 1523 The patches must not be applied, unless they are arguments to
1527 1524 the --rev parameter. At least one patch or revision is required.
1528 1525
1529 1526 With --rev, mq will stop managing the named revisions (converting
1530 1527 them to regular mercurial changesets). The qfinish command should be
1531 1528 used as an alternative for qdel -r, as the latter option is deprecated.
1532 1529
1533 1530 With --keep, the patch files are preserved in the patch directory."""
1534 1531 q = repo.mq
1535 1532 q.delete(repo, patches, opts)
1536 1533 q.save_dirty()
1537 1534 return 0
1538 1535
1539 1536 def applied(ui, repo, patch=None, **opts):
1540 1537 """print the patches already applied"""
1541 1538 q = repo.mq
1542 1539 if patch:
1543 1540 if patch not in q.series:
1544 1541 raise util.Abort(_("patch %s is not in series file") % patch)
1545 1542 end = q.series.index(patch) + 1
1546 1543 else:
1547 1544 end = q.series_end(True)
1548 1545 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1549 1546
1550 1547 def unapplied(ui, repo, patch=None, **opts):
1551 1548 """print the patches not yet applied"""
1552 1549 q = repo.mq
1553 1550 if patch:
1554 1551 if patch not in q.series:
1555 1552 raise util.Abort(_("patch %s is not in series file") % patch)
1556 1553 start = q.series.index(patch) + 1
1557 1554 else:
1558 1555 start = q.series_end(True)
1559 1556 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1560 1557
1561 1558 def qimport(ui, repo, *filename, **opts):
1562 1559 """import a patch
1563 1560
1564 1561 The patch is inserted into the series after the last applied patch.
1565 1562 If no patches have been applied, qimport prepends the patch
1566 1563 to the series.
1567 1564
1568 1565 The patch will have the same name as its source file unless you
1569 1566 give it a new one with --name.
1570 1567
1571 1568 You can register an existing patch inside the patch directory
1572 1569 with the --existing flag.
1573 1570
1574 1571 With --force, an existing patch of the same name will be overwritten.
1575 1572
1576 1573 An existing changeset may be placed under mq control with --rev
1577 1574 (e.g. qimport --rev tip -n patch will place tip under mq control).
1578 1575 With --git, patches imported with --rev will use the git diff
1579 1576 format.
1580 1577 """
1581 1578 q = repo.mq
1582 1579 q.qimport(repo, filename, patchname=opts['name'],
1583 1580 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1584 1581 git=opts['git'])
1585 1582 q.save_dirty()
1586 1583 return 0
1587 1584
1588 1585 def init(ui, repo, **opts):
1589 1586 """init a new queue repository
1590 1587
1591 1588 The queue repository is unversioned by default. If -c is
1592 1589 specified, qinit will create a separate nested repository
1593 1590 for patches (qinit -c may also be run later to convert
1594 1591 an unversioned patch repository into a versioned one).
1595 1592 You can use qcommit to commit changes to this queue repository."""
1596 1593 q = repo.mq
1597 1594 r = q.init(repo, create=opts['create_repo'])
1598 1595 q.save_dirty()
1599 1596 if r:
1600 1597 if not os.path.exists(r.wjoin('.hgignore')):
1601 1598 fp = r.wopener('.hgignore', 'w')
1602 1599 fp.write('^\\.hg\n')
1603 1600 fp.write('^\\.mq\n')
1604 1601 fp.write('syntax: glob\n')
1605 1602 fp.write('status\n')
1606 1603 fp.write('guards\n')
1607 1604 fp.close()
1608 1605 if not os.path.exists(r.wjoin('series')):
1609 1606 r.wopener('series', 'w').close()
1610 1607 r.add(['.hgignore', 'series'])
1611 1608 commands.add(ui, r)
1612 1609 return 0
1613 1610
1614 1611 def clone(ui, source, dest=None, **opts):
1615 1612 '''clone main and patch repository at same time
1616 1613
1617 1614 If source is local, destination will have no patches applied. If
1618 1615 source is remote, this command can not check if patches are
1619 1616 applied in source, so cannot guarantee that patches are not
1620 1617 applied in destination. If you clone remote repository, be sure
1621 1618 before that it has no patches applied.
1622 1619
1623 1620 Source patch repository is looked for in <src>/.hg/patches by
1624 1621 default. Use -p <url> to change.
1625 1622
1626 1623 The patch directory must be a nested mercurial repository, as
1627 1624 would be created by qinit -c.
1628 1625 '''
1629 1626 def patchdir(repo):
1630 1627 url = repo.url()
1631 1628 if url.endswith('/'):
1632 1629 url = url[:-1]
1633 1630 return url + '/.hg/patches'
1634 1631 cmdutil.setremoteconfig(ui, opts)
1635 1632 if dest is None:
1636 1633 dest = hg.defaultdest(source)
1637 1634 sr = hg.repository(ui, ui.expandpath(source))
1638 1635 patchespath = opts['patches'] or patchdir(sr)
1639 1636 try:
1640 1637 pr = hg.repository(ui, patchespath)
1641 1638 except RepoError:
1642 1639 raise util.Abort(_('versioned patch repository not found'
1643 1640 ' (see qinit -c)'))
1644 1641 qbase, destrev = None, None
1645 1642 if sr.local():
1646 1643 if sr.mq.applied:
1647 1644 qbase = revlog.bin(sr.mq.applied[0].rev)
1648 1645 if not hg.islocal(dest):
1649 1646 heads = dict.fromkeys(sr.heads())
1650 1647 for h in sr.heads(qbase):
1651 1648 del heads[h]
1652 1649 destrev = heads.keys()
1653 1650 destrev.append(sr.changelog.parents(qbase)[0])
1654 1651 elif sr.capable('lookup'):
1655 1652 try:
1656 1653 qbase = sr.lookup('qbase')
1657 1654 except RepoError:
1658 1655 pass
1659 1656 ui.note(_('cloning main repo\n'))
1660 1657 sr, dr = hg.clone(ui, sr.url(), dest,
1661 1658 pull=opts['pull'],
1662 1659 rev=destrev,
1663 1660 update=False,
1664 1661 stream=opts['uncompressed'])
1665 1662 ui.note(_('cloning patch repo\n'))
1666 1663 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1667 1664 pull=opts['pull'], update=not opts['noupdate'],
1668 1665 stream=opts['uncompressed'])
1669 1666 if dr.local():
1670 1667 if qbase:
1671 1668 ui.note(_('stripping applied patches from destination repo\n'))
1672 1669 dr.mq.strip(dr, qbase, update=False, backup=None)
1673 1670 if not opts['noupdate']:
1674 1671 ui.note(_('updating destination repo\n'))
1675 1672 hg.update(dr, dr.changelog.tip())
1676 1673
1677 1674 def commit(ui, repo, *pats, **opts):
1678 1675 """commit changes in the queue repository"""
1679 1676 q = repo.mq
1680 1677 r = q.qrepo()
1681 1678 if not r: raise util.Abort('no queue repository')
1682 1679 commands.commit(r.ui, r, *pats, **opts)
1683 1680
1684 1681 def series(ui, repo, **opts):
1685 1682 """print the entire series file"""
1686 1683 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1687 1684 return 0
1688 1685
1689 1686 def top(ui, repo, **opts):
1690 1687 """print the name of the current patch"""
1691 1688 q = repo.mq
1692 1689 t = q.applied and q.series_end(True) or 0
1693 1690 if t:
1694 1691 return q.qseries(repo, start=t-1, length=1, status='A',
1695 1692 summary=opts.get('summary'))
1696 1693 else:
1697 1694 ui.write("No patches applied\n")
1698 1695 return 1
1699 1696
1700 1697 def next(ui, repo, **opts):
1701 1698 """print the name of the next patch"""
1702 1699 q = repo.mq
1703 1700 end = q.series_end()
1704 1701 if end == len(q.series):
1705 1702 ui.write("All patches applied\n")
1706 1703 return 1
1707 1704 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1708 1705
1709 1706 def prev(ui, repo, **opts):
1710 1707 """print the name of the previous patch"""
1711 1708 q = repo.mq
1712 1709 l = len(q.applied)
1713 1710 if l == 1:
1714 1711 ui.write("Only one patch applied\n")
1715 1712 return 1
1716 1713 if not l:
1717 1714 ui.write("No patches applied\n")
1718 1715 return 1
1719 1716 return q.qseries(repo, start=l-2, length=1, status='A',
1720 1717 summary=opts.get('summary'))
1721 1718
1722 1719 def setupheaderopts(ui, opts):
1723 1720 def do(opt,val):
1724 1721 if not opts[opt] and opts['current' + opt]:
1725 1722 opts[opt] = val
1726 1723 do('user', ui.username())
1727 1724 do('date', "%d %d" % util.makedate())
1728 1725
1729 1726 def new(ui, repo, patch, *args, **opts):
1730 1727 """create a new patch
1731 1728
1732 1729 qnew creates a new patch on top of the currently-applied patch
1733 1730 (if any). It will refuse to run if there are any outstanding
1734 1731 changes unless -f is specified, in which case the patch will
1735 1732 be initialised with them. You may also use -I, -X, and/or a list of
1736 1733 files after the patch name to add only changes to matching files
1737 1734 to the new patch, leaving the rest as uncommitted modifications.
1738 1735
1739 1736 -e, -m or -l set the patch header as well as the commit message.
1740 1737 If none is specified, the patch header is empty and the
1741 1738 commit message is '[mq]: PATCH'"""
1742 1739 q = repo.mq
1743 1740 message = cmdutil.logmessage(opts)
1744 1741 if opts['edit']:
1745 1742 message = ui.edit(message, ui.username())
1746 1743 opts['msg'] = message
1747 1744 setupheaderopts(ui, opts)
1748 1745 q.new(repo, patch, *args, **opts)
1749 1746 q.save_dirty()
1750 1747 return 0
1751 1748
1752 1749 def refresh(ui, repo, *pats, **opts):
1753 1750 """update the current patch
1754 1751
1755 1752 If any file patterns are provided, the refreshed patch will contain only
1756 1753 the modifications that match those patterns; the remaining modifications
1757 1754 will remain in the working directory.
1758 1755
1759 1756 hg add/remove/copy/rename work as usual, though you might want to use
1760 1757 git-style patches (--git or [diff] git=1) to track copies and renames.
1761 1758 """
1762 1759 q = repo.mq
1763 1760 message = cmdutil.logmessage(opts)
1764 1761 if opts['edit']:
1765 1762 if not q.applied:
1766 1763 ui.write(_("No patches applied\n"))
1767 1764 return 1
1768 1765 if message:
1769 1766 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1770 1767 patch = q.applied[-1].name
1771 1768 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1772 1769 message = ui.edit('\n'.join(message), user or ui.username())
1773 1770 setupheaderopts(ui, opts)
1774 1771 ret = q.refresh(repo, pats, msg=message, **opts)
1775 1772 q.save_dirty()
1776 1773 return ret
1777 1774
1778 1775 def diff(ui, repo, *pats, **opts):
1779 1776 """diff of the current patch and subsequent modifications
1780 1777
1781 1778 Shows a diff which includes the current patch as well as any changes which
1782 1779 have been made in the working directory since the last refresh (thus
1783 1780 showing what the current patch would become after a qrefresh).
1784 1781
1785 1782 Use 'hg diff' if you only want to see the changes made since the last
1786 1783 qrefresh, or 'hg export qtip' if you want to see changes made by the
1787 1784 current patch without including changes made since the qrefresh.
1788 1785 """
1789 1786 repo.mq.diff(repo, pats, opts)
1790 1787 return 0
1791 1788
1792 1789 def fold(ui, repo, *files, **opts):
1793 1790 """fold the named patches into the current patch
1794 1791
1795 1792 Patches must not yet be applied. Each patch will be successively
1796 1793 applied to the current patch in the order given. If all the
1797 1794 patches apply successfully, the current patch will be refreshed
1798 1795 with the new cumulative patch, and the folded patches will
1799 1796 be deleted. With -k/--keep, the folded patch files will not
1800 1797 be removed afterwards.
1801 1798
1802 1799 The header for each folded patch will be concatenated with
1803 1800 the current patch header, separated by a line of '* * *'."""
1804 1801
1805 1802 q = repo.mq
1806 1803
1807 1804 if not files:
1808 1805 raise util.Abort(_('qfold requires at least one patch name'))
1809 1806 if not q.check_toppatch(repo):
1810 1807 raise util.Abort(_('No patches applied'))
1811 1808
1812 1809 message = cmdutil.logmessage(opts)
1813 1810 if opts['edit']:
1814 1811 if message:
1815 1812 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1816 1813
1817 1814 parent = q.lookup('qtip')
1818 1815 patches = []
1819 1816 messages = []
1820 1817 for f in files:
1821 1818 p = q.lookup(f)
1822 1819 if p in patches or p == parent:
1823 1820 ui.warn(_('Skipping already folded patch %s') % p)
1824 1821 if q.isapplied(p):
1825 1822 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1826 1823 patches.append(p)
1827 1824
1828 1825 for p in patches:
1829 1826 if not message:
1830 1827 messages.append(q.readheaders(p)[0])
1831 1828 pf = q.join(p)
1832 1829 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1833 1830 if not patchsuccess:
1834 1831 raise util.Abort(_('Error folding patch %s') % p)
1835 1832 patch.updatedir(ui, repo, files)
1836 1833
1837 1834 if not message:
1838 1835 message, comments, user = q.readheaders(parent)[0:3]
1839 1836 for msg in messages:
1840 1837 message.append('* * *')
1841 1838 message.extend(msg)
1842 1839 message = '\n'.join(message)
1843 1840
1844 1841 if opts['edit']:
1845 1842 message = ui.edit(message, user or ui.username())
1846 1843
1847 1844 q.refresh(repo, msg=message)
1848 1845 q.delete(repo, patches, opts)
1849 1846 q.save_dirty()
1850 1847
1851 1848 def goto(ui, repo, patch, **opts):
1852 1849 '''push or pop patches until named patch is at top of stack'''
1853 1850 q = repo.mq
1854 1851 patch = q.lookup(patch)
1855 1852 if q.isapplied(patch):
1856 1853 ret = q.pop(repo, patch, force=opts['force'])
1857 1854 else:
1858 1855 ret = q.push(repo, patch, force=opts['force'])
1859 1856 q.save_dirty()
1860 1857 return ret
1861 1858
1862 1859 def guard(ui, repo, *args, **opts):
1863 1860 '''set or print guards for a patch
1864 1861
1865 1862 Guards control whether a patch can be pushed. A patch with no
1866 1863 guards is always pushed. A patch with a positive guard ("+foo") is
1867 1864 pushed only if the qselect command has activated it. A patch with
1868 1865 a negative guard ("-foo") is never pushed if the qselect command
1869 1866 has activated it.
1870 1867
1871 1868 With no arguments, print the currently active guards.
1872 1869 With arguments, set guards for the named patch.
1873 1870
1874 1871 To set a negative guard "-foo" on topmost patch ("--" is needed so
1875 1872 hg will not interpret "-foo" as an option):
1876 1873 hg qguard -- -foo
1877 1874
1878 1875 To set guards on another patch:
1879 1876 hg qguard other.patch +2.6.17 -stable
1880 1877 '''
1881 1878 def status(idx):
1882 1879 guards = q.series_guards[idx] or ['unguarded']
1883 1880 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1884 1881 q = repo.mq
1885 1882 patch = None
1886 1883 args = list(args)
1887 1884 if opts['list']:
1888 1885 if args or opts['none']:
1889 1886 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1890 1887 for i in xrange(len(q.series)):
1891 1888 status(i)
1892 1889 return
1893 1890 if not args or args[0][0:1] in '-+':
1894 1891 if not q.applied:
1895 1892 raise util.Abort(_('no patches applied'))
1896 1893 patch = q.applied[-1].name
1897 1894 if patch is None and args[0][0:1] not in '-+':
1898 1895 patch = args.pop(0)
1899 1896 if patch is None:
1900 1897 raise util.Abort(_('no patch to work with'))
1901 1898 if args or opts['none']:
1902 1899 idx = q.find_series(patch)
1903 1900 if idx is None:
1904 1901 raise util.Abort(_('no patch named %s') % patch)
1905 1902 q.set_guards(idx, args)
1906 1903 q.save_dirty()
1907 1904 else:
1908 1905 status(q.series.index(q.lookup(patch)))
1909 1906
1910 1907 def header(ui, repo, patch=None):
1911 1908 """Print the header of the topmost or specified patch"""
1912 1909 q = repo.mq
1913 1910
1914 1911 if patch:
1915 1912 patch = q.lookup(patch)
1916 1913 else:
1917 1914 if not q.applied:
1918 1915 ui.write('No patches applied\n')
1919 1916 return 1
1920 1917 patch = q.lookup('qtip')
1921 1918 message = repo.mq.readheaders(patch)[0]
1922 1919
1923 1920 ui.write('\n'.join(message) + '\n')
1924 1921
1925 1922 def lastsavename(path):
1926 1923 (directory, base) = os.path.split(path)
1927 1924 names = os.listdir(directory)
1928 1925 namere = re.compile("%s.([0-9]+)" % base)
1929 1926 maxindex = None
1930 1927 maxname = None
1931 1928 for f in names:
1932 1929 m = namere.match(f)
1933 1930 if m:
1934 1931 index = int(m.group(1))
1935 1932 if maxindex == None or index > maxindex:
1936 1933 maxindex = index
1937 1934 maxname = f
1938 1935 if maxname:
1939 1936 return (os.path.join(directory, maxname), maxindex)
1940 1937 return (None, None)
1941 1938
1942 1939 def savename(path):
1943 1940 (last, index) = lastsavename(path)
1944 1941 if last is None:
1945 1942 index = 0
1946 1943 newpath = path + ".%d" % (index + 1)
1947 1944 return newpath
1948 1945
1949 1946 def push(ui, repo, patch=None, **opts):
1950 1947 """push the next patch onto the stack
1951 1948
1952 1949 When --force is applied, all local changes in patched files will be lost.
1953 1950 """
1954 1951 q = repo.mq
1955 1952 mergeq = None
1956 1953
1957 1954 if opts['all']:
1958 1955 if not q.series:
1959 1956 ui.warn(_('no patches in series\n'))
1960 1957 return 0
1961 1958 patch = q.series[-1]
1962 1959 if opts['merge']:
1963 1960 if opts['name']:
1964 1961 newpath = repo.join(opts['name'])
1965 1962 else:
1966 1963 newpath, i = lastsavename(q.path)
1967 1964 if not newpath:
1968 1965 ui.warn("no saved queues found, please use -n\n")
1969 1966 return 1
1970 1967 mergeq = queue(ui, repo.join(""), newpath)
1971 1968 ui.warn("merging with queue at: %s\n" % mergeq.path)
1972 1969 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1973 1970 mergeq=mergeq)
1974 1971 return ret
1975 1972
1976 1973 def pop(ui, repo, patch=None, **opts):
1977 1974 """pop the current patch off the stack
1978 1975
1979 1976 By default, pops off the top of the patch stack. If given a patch name,
1980 1977 keeps popping off patches until the named patch is at the top of the stack.
1981 1978 """
1982 1979 localupdate = True
1983 1980 if opts['name']:
1984 1981 q = queue(ui, repo.join(""), repo.join(opts['name']))
1985 1982 ui.warn('using patch queue: %s\n' % q.path)
1986 1983 localupdate = False
1987 1984 else:
1988 1985 q = repo.mq
1989 1986 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1990 1987 all=opts['all'])
1991 1988 q.save_dirty()
1992 1989 return ret
1993 1990
1994 1991 def rename(ui, repo, patch, name=None, **opts):
1995 1992 """rename a patch
1996 1993
1997 1994 With one argument, renames the current patch to PATCH1.
1998 1995 With two arguments, renames PATCH1 to PATCH2."""
1999 1996
2000 1997 q = repo.mq
2001 1998
2002 1999 if not name:
2003 2000 name = patch
2004 2001 patch = None
2005 2002
2006 2003 if patch:
2007 2004 patch = q.lookup(patch)
2008 2005 else:
2009 2006 if not q.applied:
2010 2007 ui.write(_('No patches applied\n'))
2011 2008 return
2012 2009 patch = q.lookup('qtip')
2013 2010 absdest = q.join(name)
2014 2011 if os.path.isdir(absdest):
2015 2012 name = normname(os.path.join(name, os.path.basename(patch)))
2016 2013 absdest = q.join(name)
2017 2014 if os.path.exists(absdest):
2018 2015 raise util.Abort(_('%s already exists') % absdest)
2019 2016
2020 2017 if name in q.series:
2021 2018 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2022 2019
2023 2020 if ui.verbose:
2024 2021 ui.write('Renaming %s to %s\n' % (patch, name))
2025 2022 i = q.find_series(patch)
2026 2023 guards = q.guard_re.findall(q.full_series[i])
2027 2024 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2028 2025 q.parse_series()
2029 2026 q.series_dirty = 1
2030 2027
2031 2028 info = q.isapplied(patch)
2032 2029 if info:
2033 2030 q.applied[info[0]] = statusentry(info[1], name)
2034 2031 q.applied_dirty = 1
2035 2032
2036 2033 util.rename(q.join(patch), absdest)
2037 2034 r = q.qrepo()
2038 2035 if r:
2039 2036 wlock = r.wlock()
2040 2037 try:
2041 2038 if r.dirstate[patch] == 'a':
2042 2039 r.dirstate.forget(patch)
2043 2040 r.dirstate.add(name)
2044 2041 else:
2045 2042 if r.dirstate[name] == 'r':
2046 2043 r.undelete([name])
2047 2044 r.copy(patch, name)
2048 2045 r.remove([patch], False)
2049 2046 finally:
2050 2047 del wlock
2051 2048
2052 2049 q.save_dirty()
2053 2050
2054 2051 def restore(ui, repo, rev, **opts):
2055 2052 """restore the queue state saved by a rev"""
2056 2053 rev = repo.lookup(rev)
2057 2054 q = repo.mq
2058 2055 q.restore(repo, rev, delete=opts['delete'],
2059 2056 qupdate=opts['update'])
2060 2057 q.save_dirty()
2061 2058 return 0
2062 2059
2063 2060 def save(ui, repo, **opts):
2064 2061 """save current queue state"""
2065 2062 q = repo.mq
2066 2063 message = cmdutil.logmessage(opts)
2067 2064 ret = q.save(repo, msg=message)
2068 2065 if ret:
2069 2066 return ret
2070 2067 q.save_dirty()
2071 2068 if opts['copy']:
2072 2069 path = q.path
2073 2070 if opts['name']:
2074 2071 newpath = os.path.join(q.basepath, opts['name'])
2075 2072 if os.path.exists(newpath):
2076 2073 if not os.path.isdir(newpath):
2077 2074 raise util.Abort(_('destination %s exists and is not '
2078 2075 'a directory') % newpath)
2079 2076 if not opts['force']:
2080 2077 raise util.Abort(_('destination %s exists, '
2081 2078 'use -f to force') % newpath)
2082 2079 else:
2083 2080 newpath = savename(path)
2084 2081 ui.warn("copy %s to %s\n" % (path, newpath))
2085 2082 util.copyfiles(path, newpath)
2086 2083 if opts['empty']:
2087 2084 try:
2088 2085 os.unlink(q.join(q.status_path))
2089 2086 except:
2090 2087 pass
2091 2088 return 0
2092 2089
2093 2090 def strip(ui, repo, rev, **opts):
2094 2091 """strip a revision and all its descendants from the repository
2095 2092
2096 2093 If one of the working dir's parent revisions is stripped, the working
2097 2094 directory will be updated to the parent of the stripped revision.
2098 2095 """
2099 2096 backup = 'all'
2100 2097 if opts['backup']:
2101 2098 backup = 'strip'
2102 2099 elif opts['nobackup']:
2103 2100 backup = 'none'
2104 2101
2105 2102 rev = repo.lookup(rev)
2106 2103 p = repo.dirstate.parents()
2107 2104 cl = repo.changelog
2108 2105 update = True
2109 2106 if p[0] == revlog.nullid:
2110 2107 update = False
2111 2108 elif p[1] == revlog.nullid and rev != cl.ancestor(p[0], rev):
2112 2109 update = False
2113 2110 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2114 2111 update = False
2115 2112
2116 2113 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2117 2114 return 0
2118 2115
2119 2116 def select(ui, repo, *args, **opts):
2120 2117 '''set or print guarded patches to push
2121 2118
2122 2119 Use the qguard command to set or print guards on patch, then use
2123 2120 qselect to tell mq which guards to use. A patch will be pushed if it
2124 2121 has no guards or any positive guards match the currently selected guard,
2125 2122 but will not be pushed if any negative guards match the current guard.
2126 2123 For example:
2127 2124
2128 2125 qguard foo.patch -stable (negative guard)
2129 2126 qguard bar.patch +stable (positive guard)
2130 2127 qselect stable
2131 2128
2132 2129 This activates the "stable" guard. mq will skip foo.patch (because
2133 2130 it has a negative match) but push bar.patch (because it
2134 2131 has a positive match).
2135 2132
2136 2133 With no arguments, prints the currently active guards.
2137 2134 With one argument, sets the active guard.
2138 2135
2139 2136 Use -n/--none to deactivate guards (no other arguments needed).
2140 2137 When no guards are active, patches with positive guards are skipped
2141 2138 and patches with negative guards are pushed.
2142 2139
2143 2140 qselect can change the guards on applied patches. It does not pop
2144 2141 guarded patches by default. Use --pop to pop back to the last applied
2145 2142 patch that is not guarded. Use --reapply (which implies --pop) to push
2146 2143 back to the current patch afterwards, but skip guarded patches.
2147 2144
2148 2145 Use -s/--series to print a list of all guards in the series file (no
2149 2146 other arguments needed). Use -v for more information.'''
2150 2147
2151 2148 q = repo.mq
2152 2149 guards = q.active()
2153 2150 if args or opts['none']:
2154 2151 old_unapplied = q.unapplied(repo)
2155 2152 old_guarded = [i for i in xrange(len(q.applied)) if
2156 2153 not q.pushable(i)[0]]
2157 2154 q.set_active(args)
2158 2155 q.save_dirty()
2159 2156 if not args:
2160 2157 ui.status(_('guards deactivated\n'))
2161 2158 if not opts['pop'] and not opts['reapply']:
2162 2159 unapplied = q.unapplied(repo)
2163 2160 guarded = [i for i in xrange(len(q.applied))
2164 2161 if not q.pushable(i)[0]]
2165 2162 if len(unapplied) != len(old_unapplied):
2166 2163 ui.status(_('number of unguarded, unapplied patches has '
2167 2164 'changed from %d to %d\n') %
2168 2165 (len(old_unapplied), len(unapplied)))
2169 2166 if len(guarded) != len(old_guarded):
2170 2167 ui.status(_('number of guarded, applied patches has changed '
2171 2168 'from %d to %d\n') %
2172 2169 (len(old_guarded), len(guarded)))
2173 2170 elif opts['series']:
2174 2171 guards = {}
2175 2172 noguards = 0
2176 2173 for gs in q.series_guards:
2177 2174 if not gs:
2178 2175 noguards += 1
2179 2176 for g in gs:
2180 2177 guards.setdefault(g, 0)
2181 2178 guards[g] += 1
2182 2179 if ui.verbose:
2183 2180 guards['NONE'] = noguards
2184 2181 guards = guards.items()
2185 2182 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2186 2183 if guards:
2187 2184 ui.note(_('guards in series file:\n'))
2188 2185 for guard, count in guards:
2189 2186 ui.note('%2d ' % count)
2190 2187 ui.write(guard, '\n')
2191 2188 else:
2192 2189 ui.note(_('no guards in series file\n'))
2193 2190 else:
2194 2191 if guards:
2195 2192 ui.note(_('active guards:\n'))
2196 2193 for g in guards:
2197 2194 ui.write(g, '\n')
2198 2195 else:
2199 2196 ui.write(_('no active guards\n'))
2200 2197 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2201 2198 popped = False
2202 2199 if opts['pop'] or opts['reapply']:
2203 2200 for i in xrange(len(q.applied)):
2204 2201 pushable, reason = q.pushable(i)
2205 2202 if not pushable:
2206 2203 ui.status(_('popping guarded patches\n'))
2207 2204 popped = True
2208 2205 if i == 0:
2209 2206 q.pop(repo, all=True)
2210 2207 else:
2211 2208 q.pop(repo, i-1)
2212 2209 break
2213 2210 if popped:
2214 2211 try:
2215 2212 if reapply:
2216 2213 ui.status(_('reapplying unguarded patches\n'))
2217 2214 q.push(repo, reapply)
2218 2215 finally:
2219 2216 q.save_dirty()
2220 2217
2221 2218 def finish(ui, repo, *revrange, **opts):
2222 2219 """move applied patches into repository history
2223 2220
2224 2221 Finishes the specified revisions (corresponding to applied patches) by
2225 2222 moving them out of mq control into regular repository history.
2226 2223
2227 2224 Accepts a revision range or the --all option. If --all is specified, all
2228 2225 applied mq revisions are removed from mq control. Otherwise, the given
2229 2226 revisions must be at the base of the stack of applied patches.
2230 2227
2231 2228 This can be especially useful if your changes have been applied to an
2232 2229 upstream repository, or if you are about to push your changes to upstream.
2233 2230 """
2234 2231 if not opts['applied'] and not revrange:
2235 2232 raise util.Abort(_('no revisions specified'))
2236 2233 elif opts['applied']:
2237 2234 revrange = ('qbase:qtip',) + revrange
2238 2235
2239 2236 q = repo.mq
2240 2237 if not q.applied:
2241 2238 ui.status(_('no patches applied\n'))
2242 2239 return 0
2243 2240
2244 2241 revs = cmdutil.revrange(repo, revrange)
2245 2242 q.finish(repo, revs)
2246 2243 q.save_dirty()
2247 2244 return 0
2248 2245
2249 2246 def reposetup(ui, repo):
2250 2247 class mqrepo(repo.__class__):
2251 2248 def abort_if_wdir_patched(self, errmsg, force=False):
2252 2249 if self.mq.applied and not force:
2253 2250 parent = revlog.hex(self.dirstate.parents()[0])
2254 2251 if parent in [s.rev for s in self.mq.applied]:
2255 2252 raise util.Abort(errmsg)
2256 2253
2257 2254 def commit(self, *args, **opts):
2258 2255 if len(args) >= 6:
2259 2256 force = args[5]
2260 2257 else:
2261 2258 force = opts.get('force')
2262 2259 self.abort_if_wdir_patched(
2263 2260 _('cannot commit over an applied mq patch'),
2264 2261 force)
2265 2262
2266 2263 return super(mqrepo, self).commit(*args, **opts)
2267 2264
2268 2265 def push(self, remote, force=False, revs=None):
2269 2266 if self.mq.applied and not force and not revs:
2270 2267 raise util.Abort(_('source has mq patches applied'))
2271 2268 return super(mqrepo, self).push(remote, force, revs)
2272 2269
2273 2270 def tags(self):
2274 2271 if self.tagscache:
2275 2272 return self.tagscache
2276 2273
2277 2274 tagscache = super(mqrepo, self).tags()
2278 2275
2279 2276 q = self.mq
2280 2277 if not q.applied:
2281 2278 return tagscache
2282 2279
2283 2280 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2284 2281
2285 2282 if mqtags[-1][0] not in self.changelog.nodemap:
2286 2283 self.ui.warn('mq status file refers to unknown node %s\n'
2287 2284 % revlog.short(mqtags[-1][0]))
2288 2285 return tagscache
2289 2286
2290 2287 mqtags.append((mqtags[-1][0], 'qtip'))
2291 2288 mqtags.append((mqtags[0][0], 'qbase'))
2292 2289 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2293 2290 for patch in mqtags:
2294 2291 if patch[1] in tagscache:
2295 2292 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2296 2293 else:
2297 2294 tagscache[patch[1]] = patch[0]
2298 2295
2299 2296 return tagscache
2300 2297
2301 2298 def _branchtags(self, partial, lrev):
2302 2299 q = self.mq
2303 2300 if not q.applied:
2304 2301 return super(mqrepo, self)._branchtags(partial, lrev)
2305 2302
2306 2303 cl = self.changelog
2307 2304 qbasenode = revlog.bin(q.applied[0].rev)
2308 2305 if qbasenode not in cl.nodemap:
2309 2306 self.ui.warn('mq status file refers to unknown node %s\n'
2310 2307 % revlog.short(qbasenode))
2311 2308 return super(mqrepo, self)._branchtags(partial, lrev)
2312 2309
2313 2310 qbase = cl.rev(qbasenode)
2314 2311 start = lrev + 1
2315 2312 if start < qbase:
2316 2313 # update the cache (excluding the patches) and save it
2317 2314 self._updatebranchcache(partial, lrev+1, qbase)
2318 2315 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2319 2316 start = qbase
2320 2317 # if start = qbase, the cache is as updated as it should be.
2321 2318 # if start > qbase, the cache includes (part of) the patches.
2322 2319 # we might as well use it, but we won't save it.
2323 2320
2324 2321 # update the cache up to the tip
2325 2322 self._updatebranchcache(partial, start, cl.count())
2326 2323
2327 2324 return partial
2328 2325
2329 2326 if repo.local():
2330 2327 repo.__class__ = mqrepo
2331 2328 repo.mq = queue(ui, repo.join(""))
2332 2329
2333 2330 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2334 2331
2335 2332 headeropts = [
2336 2333 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2337 2334 ('u', 'user', '', _('add "From: <given user>" to patch')),
2338 2335 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2339 2336 ('d', 'date', '', _('add "Date: <given date>" to patch'))]
2340 2337
2341 2338 cmdtable = {
2342 2339 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2343 2340 "qclone":
2344 2341 (clone,
2345 2342 [('', 'pull', None, _('use pull protocol to copy metadata')),
2346 2343 ('U', 'noupdate', None, _('do not update the new working directories')),
2347 2344 ('', 'uncompressed', None,
2348 2345 _('use uncompressed transfer (fast over LAN)')),
2349 2346 ('p', 'patches', '', _('location of source patch repo')),
2350 2347 ] + commands.remoteopts,
2351 2348 _('hg qclone [OPTION]... SOURCE [DEST]')),
2352 2349 "qcommit|qci":
2353 2350 (commit,
2354 2351 commands.table["^commit|ci"][1],
2355 2352 _('hg qcommit [OPTION]... [FILE]...')),
2356 2353 "^qdiff":
2357 2354 (diff,
2358 [('g', 'git', None, _('use git extended diff format')),
2359 ('U', 'unified', 3, _('number of lines of context to show')),
2360 ] + commands.walkopts,
2361 _('hg qdiff [-I] [-X] [-U NUM] [-g] [FILE]...')),
2355 commands.diffopts + commands.diffopts2 + commands.walkopts,
2356 _('hg qdiff [OPTION]... [FILE]...')),
2362 2357 "qdelete|qremove|qrm":
2363 2358 (delete,
2364 2359 [('k', 'keep', None, _('keep patch file')),
2365 2360 ('r', 'rev', [], _('stop managing a revision'))],
2366 2361 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2367 2362 'qfold':
2368 2363 (fold,
2369 2364 [('e', 'edit', None, _('edit patch header')),
2370 2365 ('k', 'keep', None, _('keep folded patch files')),
2371 2366 ] + commands.commitopts,
2372 2367 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2373 2368 'qgoto':
2374 2369 (goto,
2375 2370 [('f', 'force', None, _('overwrite any local changes'))],
2376 2371 _('hg qgoto [OPTION]... PATCH')),
2377 2372 'qguard':
2378 2373 (guard,
2379 2374 [('l', 'list', None, _('list all patches and guards')),
2380 2375 ('n', 'none', None, _('drop all guards'))],
2381 2376 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2382 2377 'qheader': (header, [], _('hg qheader [PATCH]')),
2383 2378 "^qimport":
2384 2379 (qimport,
2385 2380 [('e', 'existing', None, 'import file in patch dir'),
2386 2381 ('n', 'name', '', 'patch file name'),
2387 2382 ('f', 'force', None, 'overwrite existing files'),
2388 2383 ('r', 'rev', [], 'place existing revisions under mq control'),
2389 2384 ('g', 'git', None, _('use git extended diff format'))],
2390 2385 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2391 2386 "^qinit":
2392 2387 (init,
2393 2388 [('c', 'create-repo', None, 'create queue repository')],
2394 2389 _('hg qinit [-c]')),
2395 2390 "qnew":
2396 2391 (new,
2397 2392 [('e', 'edit', None, _('edit commit message')),
2398 2393 ('f', 'force', None, _('import uncommitted changes into patch')),
2399 2394 ('g', 'git', None, _('use git extended diff format')),
2400 2395 ] + commands.walkopts + commands.commitopts + headeropts,
2401 2396 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2402 2397 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2403 2398 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2404 2399 "^qpop":
2405 2400 (pop,
2406 2401 [('a', 'all', None, _('pop all patches')),
2407 2402 ('n', 'name', '', _('queue name to pop')),
2408 2403 ('f', 'force', None, _('forget any local changes'))],
2409 2404 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2410 2405 "^qpush":
2411 2406 (push,
2412 2407 [('f', 'force', None, _('apply if the patch has rejects')),
2413 2408 ('l', 'list', None, _('list patch name in commit text')),
2414 2409 ('a', 'all', None, _('apply all patches')),
2415 2410 ('m', 'merge', None, _('merge from another queue')),
2416 2411 ('n', 'name', '', _('merge queue name'))],
2417 2412 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2418 2413 "^qrefresh":
2419 2414 (refresh,
2420 2415 [('e', 'edit', None, _('edit commit message')),
2421 2416 ('g', 'git', None, _('use git extended diff format')),
2422 2417 ('s', 'short', None, _('refresh only files already in the patch')),
2423 2418 ] + commands.walkopts + commands.commitopts + headeropts,
2424 2419 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2425 2420 'qrename|qmv':
2426 2421 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2427 2422 "qrestore":
2428 2423 (restore,
2429 2424 [('d', 'delete', None, _('delete save entry')),
2430 2425 ('u', 'update', None, _('update queue working dir'))],
2431 2426 _('hg qrestore [-d] [-u] REV')),
2432 2427 "qsave":
2433 2428 (save,
2434 2429 [('c', 'copy', None, _('copy patch directory')),
2435 2430 ('n', 'name', '', _('copy directory name')),
2436 2431 ('e', 'empty', None, _('clear queue status file')),
2437 2432 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2438 2433 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2439 2434 "qselect":
2440 2435 (select,
2441 2436 [('n', 'none', None, _('disable all guards')),
2442 2437 ('s', 'series', None, _('list all guards in series file')),
2443 2438 ('', 'pop', None, _('pop to before first guarded applied patch')),
2444 2439 ('', 'reapply', None, _('pop, then reapply patches'))],
2445 2440 _('hg qselect [OPTION]... [GUARD]...')),
2446 2441 "qseries":
2447 2442 (series,
2448 2443 [('m', 'missing', None, _('print patches not in series')),
2449 2444 ] + seriesopts,
2450 2445 _('hg qseries [-ms]')),
2451 2446 "^strip":
2452 2447 (strip,
2453 2448 [('f', 'force', None, _('force removal with local changes')),
2454 2449 ('b', 'backup', None, _('bundle unrelated changesets')),
2455 2450 ('n', 'nobackup', None, _('no backups'))],
2456 2451 _('hg strip [-f] [-b] [-n] REV')),
2457 2452 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2458 2453 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2459 2454 "qfinish":
2460 2455 (finish,
2461 2456 [('a', 'applied', None, _('finish all applied changesets'))],
2462 2457 _('hg qfinish [-a] [REV...]')),
2463 2458 }
@@ -1,3330 +1,3332 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from node import hex, nullid, nullrev, short
9 9 from repo import RepoError, NoCapability
10 10 from i18n import _
11 11 import os, re, sys, urllib
12 12 import hg, util, revlog, bundlerepo, extensions, copies
13 13 import difflib, patch, time, help, mdiff, tempfile
14 14 import version, socket
15 15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16 16 import merge as merge_
17 17
18 18 # Commands start here, listed alphabetically
19 19
20 20 def add(ui, repo, *pats, **opts):
21 21 """add the specified files on the next commit
22 22
23 23 Schedule files to be version controlled and added to the repository.
24 24
25 25 The files will be added to the repository at the next commit. To
26 26 undo an add before that, see hg revert.
27 27
28 28 If no names are given, add all files in the repository.
29 29 """
30 30
31 31 rejected = None
32 32 exacts = {}
33 33 names = []
34 34 m = cmdutil.match(repo, pats, opts)
35 35 m.bad = lambda x,y: True
36 36 for abs in repo.walk(m):
37 37 if m.exact(abs):
38 38 if ui.verbose:
39 39 ui.status(_('adding %s\n') % m.rel(abs))
40 40 names.append(abs)
41 41 exacts[abs] = 1
42 42 elif abs not in repo.dirstate:
43 43 ui.status(_('adding %s\n') % m.rel(abs))
44 44 names.append(abs)
45 45 if not opts.get('dry_run'):
46 46 rejected = repo.add(names)
47 47 rejected = [p for p in rejected if p in exacts]
48 48 return rejected and 1 or 0
49 49
50 50 def addremove(ui, repo, *pats, **opts):
51 51 """add all new files, delete all missing files
52 52
53 53 Add all new files and remove all missing files from the repository.
54 54
55 55 New files are ignored if they match any of the patterns in .hgignore. As
56 56 with add, these changes take effect at the next commit.
57 57
58 58 Use the -s option to detect renamed files. With a parameter > 0,
59 59 this compares every removed file with every added file and records
60 60 those similar enough as renames. This option takes a percentage
61 61 between 0 (disabled) and 100 (files must be identical) as its
62 62 parameter. Detecting renamed files this way can be expensive.
63 63 """
64 64 try:
65 65 sim = float(opts.get('similarity') or 0)
66 66 except ValueError:
67 67 raise util.Abort(_('similarity must be a number'))
68 68 if sim < 0 or sim > 100:
69 69 raise util.Abort(_('similarity must be between 0 and 100'))
70 70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
71 71
72 72 def annotate(ui, repo, *pats, **opts):
73 73 """show changeset information per file line
74 74
75 75 List changes in files, showing the revision id responsible for each line
76 76
77 77 This command is useful to discover who did a change or when a change took
78 78 place.
79 79
80 80 Without the -a option, annotate will avoid processing files it
81 81 detects as binary. With -a, annotate will generate an annotation
82 82 anyway, probably with undesirable results.
83 83 """
84 84 datefunc = ui.quiet and util.shortdate or util.datestr
85 85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
86 86
87 87 if not pats:
88 88 raise util.Abort(_('at least one file name or pattern required'))
89 89
90 90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
91 91 ('number', lambda x: str(x[0].rev())),
92 92 ('changeset', lambda x: short(x[0].node())),
93 93 ('date', getdate),
94 94 ('follow', lambda x: x[0].path()),
95 95 ]
96 96
97 97 if (not opts['user'] and not opts['changeset'] and not opts['date']
98 98 and not opts['follow']):
99 99 opts['number'] = 1
100 100
101 101 linenumber = opts.get('line_number') is not None
102 102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
103 103 raise util.Abort(_('at least one of -n/-c is required for -l'))
104 104
105 105 funcmap = [func for op, func in opmap if opts.get(op)]
106 106 if linenumber:
107 107 lastfunc = funcmap[-1]
108 108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
109 109
110 110 ctx = repo.changectx(opts['rev'])
111 111
112 112 m = cmdutil.match(repo, pats, opts)
113 113 for abs in repo.walk(m, ctx.node()):
114 114 fctx = ctx.filectx(abs)
115 115 if not opts['text'] and util.binary(fctx.data()):
116 116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
117 117 continue
118 118
119 119 lines = fctx.annotate(follow=opts.get('follow'),
120 120 linenumber=linenumber)
121 121 pieces = []
122 122
123 123 for f in funcmap:
124 124 l = [f(n) for n, dummy in lines]
125 125 if l:
126 126 m = max(map(len, l))
127 127 pieces.append(["%*s" % (m, x) for x in l])
128 128
129 129 if pieces:
130 130 for p, l in zip(zip(*pieces), lines):
131 131 ui.write("%s: %s" % (" ".join(p), l[1]))
132 132
133 133 def archive(ui, repo, dest, **opts):
134 134 '''create unversioned archive of a repository revision
135 135
136 136 By default, the revision used is the parent of the working
137 137 directory; use "-r" to specify a different revision.
138 138
139 139 To specify the type of archive to create, use "-t". Valid
140 140 types are:
141 141
142 142 "files" (default): a directory full of files
143 143 "tar": tar archive, uncompressed
144 144 "tbz2": tar archive, compressed using bzip2
145 145 "tgz": tar archive, compressed using gzip
146 146 "uzip": zip archive, uncompressed
147 147 "zip": zip archive, compressed using deflate
148 148
149 149 The exact name of the destination archive or directory is given
150 150 using a format string; see "hg help export" for details.
151 151
152 152 Each member added to an archive file has a directory prefix
153 153 prepended. Use "-p" to specify a format string for the prefix.
154 154 The default is the basename of the archive, with suffixes removed.
155 155 '''
156 156
157 157 ctx = repo.changectx(opts['rev'])
158 158 if not ctx:
159 159 raise util.Abort(_('repository has no revisions'))
160 160 node = ctx.node()
161 161 dest = cmdutil.make_filename(repo, dest, node)
162 162 if os.path.realpath(dest) == repo.root:
163 163 raise util.Abort(_('repository root cannot be destination'))
164 164 matchfn = cmdutil.match(repo, [], opts)
165 165 kind = opts.get('type') or 'files'
166 166 prefix = opts['prefix']
167 167 if dest == '-':
168 168 if kind == 'files':
169 169 raise util.Abort(_('cannot archive plain files to stdout'))
170 170 dest = sys.stdout
171 171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
172 172 prefix = cmdutil.make_filename(repo, prefix, node)
173 173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
174 174 matchfn, prefix)
175 175
176 176 def backout(ui, repo, node=None, rev=None, **opts):
177 177 '''reverse effect of earlier changeset
178 178
179 179 Commit the backed out changes as a new changeset. The new
180 180 changeset is a child of the backed out changeset.
181 181
182 182 If you back out a changeset other than the tip, a new head is
183 183 created. This head will be the new tip and you should merge this
184 184 backout changeset with another head (current one by default).
185 185
186 186 The --merge option remembers the parent of the working directory
187 187 before starting the backout, then merges the new head with that
188 188 changeset afterwards. This saves you from doing the merge by
189 189 hand. The result of this merge is not committed, as for a normal
190 190 merge.
191 191
192 192 See 'hg help dates' for a list of formats valid for -d/--date.
193 193 '''
194 194 if rev and node:
195 195 raise util.Abort(_("please specify just one revision"))
196 196
197 197 if not rev:
198 198 rev = node
199 199
200 200 if not rev:
201 201 raise util.Abort(_("please specify a revision to backout"))
202 202
203 203 date = opts.get('date')
204 204 if date:
205 205 opts['date'] = util.parsedate(date)
206 206
207 207 cmdutil.bail_if_changed(repo)
208 208 node = repo.lookup(rev)
209 209
210 210 op1, op2 = repo.dirstate.parents()
211 211 a = repo.changelog.ancestor(op1, node)
212 212 if a != node:
213 213 raise util.Abort(_('cannot back out change on a different branch'))
214 214
215 215 p1, p2 = repo.changelog.parents(node)
216 216 if p1 == nullid:
217 217 raise util.Abort(_('cannot back out a change with no parents'))
218 218 if p2 != nullid:
219 219 if not opts['parent']:
220 220 raise util.Abort(_('cannot back out a merge changeset without '
221 221 '--parent'))
222 222 p = repo.lookup(opts['parent'])
223 223 if p not in (p1, p2):
224 224 raise util.Abort(_('%s is not a parent of %s') %
225 225 (short(p), short(node)))
226 226 parent = p
227 227 else:
228 228 if opts['parent']:
229 229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
230 230 parent = p1
231 231
232 232 # the backout should appear on the same branch
233 233 branch = repo.dirstate.branch()
234 234 hg.clean(repo, node, show_stats=False)
235 235 repo.dirstate.setbranch(branch)
236 236 revert_opts = opts.copy()
237 237 revert_opts['date'] = None
238 238 revert_opts['all'] = True
239 239 revert_opts['rev'] = hex(parent)
240 240 revert_opts['no_backup'] = None
241 241 revert(ui, repo, **revert_opts)
242 242 commit_opts = opts.copy()
243 243 commit_opts['addremove'] = False
244 244 if not commit_opts['message'] and not commit_opts['logfile']:
245 245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
246 246 commit_opts['force_editor'] = True
247 247 commit(ui, repo, **commit_opts)
248 248 def nice(node):
249 249 return '%d:%s' % (repo.changelog.rev(node), short(node))
250 250 ui.status(_('changeset %s backs out changeset %s\n') %
251 251 (nice(repo.changelog.tip()), nice(node)))
252 252 if op1 != node:
253 253 hg.clean(repo, op1, show_stats=False)
254 254 if opts['merge']:
255 255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
256 256 hg.merge(repo, hex(repo.changelog.tip()))
257 257 else:
258 258 ui.status(_('the backout changeset is a new head - '
259 259 'do not forget to merge\n'))
260 260 ui.status(_('(use "backout --merge" '
261 261 'if you want to auto-merge)\n'))
262 262
263 263 def bisect(ui, repo, rev=None, extra=None,
264 264 reset=None, good=None, bad=None, skip=None, noupdate=None):
265 265 """subdivision search of changesets
266 266
267 267 This command helps to find changesets which introduce problems.
268 268 To use, mark the earliest changeset you know exhibits the problem
269 269 as bad, then mark the latest changeset which is free from the
270 270 problem as good. Bisect will update your working directory to a
271 271 revision for testing. Once you have performed tests, mark the
272 272 working directory as bad or good and bisect will either update to
273 273 another candidate changeset or announce that it has found the bad
274 274 revision.
275 275 """
276 276 # backward compatibility
277 277 if rev in "good bad reset init".split():
278 278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
279 279 cmd, rev, extra = rev, extra, None
280 280 if cmd == "good":
281 281 good = True
282 282 elif cmd == "bad":
283 283 bad = True
284 284 else:
285 285 reset = True
286 286 elif extra or good + bad + skip + reset > 1:
287 287 raise util.Abort("Incompatible arguments")
288 288
289 289 if reset:
290 290 p = repo.join("bisect.state")
291 291 if os.path.exists(p):
292 292 os.unlink(p)
293 293 return
294 294
295 295 # load state
296 296 state = {'good': [], 'bad': [], 'skip': []}
297 297 if os.path.exists(repo.join("bisect.state")):
298 298 for l in repo.opener("bisect.state"):
299 299 kind, node = l[:-1].split()
300 300 node = repo.lookup(node)
301 301 if kind not in state:
302 302 raise util.Abort(_("unknown bisect kind %s") % kind)
303 303 state[kind].append(node)
304 304
305 305 # update state
306 306 node = repo.lookup(rev or '.')
307 307 if good:
308 308 state['good'].append(node)
309 309 elif bad:
310 310 state['bad'].append(node)
311 311 elif skip:
312 312 state['skip'].append(node)
313 313
314 314 # save state
315 315 f = repo.opener("bisect.state", "w", atomictemp=True)
316 316 wlock = repo.wlock()
317 317 try:
318 318 for kind in state:
319 319 for node in state[kind]:
320 320 f.write("%s %s\n" % (kind, hex(node)))
321 321 f.rename()
322 322 finally:
323 323 del wlock
324 324
325 325 if not state['good'] or not state['bad']:
326 326 return
327 327
328 328 # actually bisect
329 329 node, changesets, good = hbisect.bisect(repo.changelog, state)
330 330 if changesets == 0:
331 331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
332 332 displayer = cmdutil.show_changeset(ui, repo, {})
333 333 displayer.show(changenode=node)
334 334 elif node is not None:
335 335 # compute the approximate number of remaining tests
336 336 tests, size = 0, 2
337 337 while size <= changesets:
338 338 tests, size = tests + 1, size * 2
339 339 rev = repo.changelog.rev(node)
340 340 ui.write(_("Testing changeset %s:%s "
341 341 "(%s changesets remaining, ~%s tests)\n")
342 342 % (rev, short(node), changesets, tests))
343 343 if not noupdate:
344 344 cmdutil.bail_if_changed(repo)
345 345 return hg.clean(repo, node)
346 346
347 347 def branch(ui, repo, label=None, **opts):
348 348 """set or show the current branch name
349 349
350 350 With no argument, show the current branch name. With one argument,
351 351 set the working directory branch name (the branch does not exist in
352 352 the repository until the next commit).
353 353
354 354 Unless --force is specified, branch will not let you set a
355 355 branch name that shadows an existing branch.
356 356
357 357 Use the command 'hg update' to switch to an existing branch.
358 358 """
359 359
360 360 if label:
361 361 if not opts.get('force') and label in repo.branchtags():
362 362 if label not in [p.branch() for p in repo.workingctx().parents()]:
363 363 raise util.Abort(_('a branch of the same name already exists'
364 364 ' (use --force to override)'))
365 365 repo.dirstate.setbranch(util.fromlocal(label))
366 366 ui.status(_('marked working directory as branch %s\n') % label)
367 367 else:
368 368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
369 369
370 370 def branches(ui, repo, active=False):
371 371 """list repository named branches
372 372
373 373 List the repository's named branches, indicating which ones are
374 374 inactive. If active is specified, only show active branches.
375 375
376 376 A branch is considered active if it contains repository heads.
377 377
378 378 Use the command 'hg update' to switch to an existing branch.
379 379 """
380 380 hexfunc = ui.debugflag and hex or short
381 381 activebranches = [util.tolocal(repo.changectx(n).branch())
382 382 for n in repo.heads()]
383 383 branches = [(tag in activebranches, repo.changelog.rev(node), tag)
384 384 for tag, node in repo.branchtags().items()]
385 385 branches.sort(reverse=True)
386 386
387 387 for isactive, node, tag in branches:
388 388 if (not active) or isactive:
389 389 if ui.quiet:
390 390 ui.write("%s\n" % tag)
391 391 else:
392 392 rev = str(node).rjust(32 - util.locallen(tag))
393 393 isinactive = ((not isactive) and " (inactive)") or ''
394 394 data = tag, rev, hexfunc(repo.lookup(node)), isinactive
395 395 ui.write("%s%s:%s%s\n" % data)
396 396
397 397 def bundle(ui, repo, fname, dest=None, **opts):
398 398 """create a changegroup file
399 399
400 400 Generate a compressed changegroup file collecting changesets not
401 401 found in the other repository.
402 402
403 403 If no destination repository is specified the destination is
404 404 assumed to have all the nodes specified by one or more --base
405 405 parameters. To create a bundle containing all changesets, use
406 406 --all (or --base null). To change the compression method applied,
407 407 use the -t option (by default, bundles are compressed using bz2).
408 408
409 409 The bundle file can then be transferred using conventional means and
410 410 applied to another repository with the unbundle or pull command.
411 411 This is useful when direct push and pull are not available or when
412 412 exporting an entire repository is undesirable.
413 413
414 414 Applying bundles preserves all changeset contents including
415 415 permissions, copy/rename information, and revision history.
416 416 """
417 417 revs = opts.get('rev') or None
418 418 if revs:
419 419 revs = [repo.lookup(rev) for rev in revs]
420 420 if opts.get('all'):
421 421 base = ['null']
422 422 else:
423 423 base = opts.get('base')
424 424 if base:
425 425 if dest:
426 426 raise util.Abort(_("--base is incompatible with specifiying "
427 427 "a destination"))
428 428 base = [repo.lookup(rev) for rev in base]
429 429 # create the right base
430 430 # XXX: nodesbetween / changegroup* should be "fixed" instead
431 431 o = []
432 432 has = {nullid: None}
433 433 for n in base:
434 434 has.update(repo.changelog.reachable(n))
435 435 if revs:
436 436 visit = list(revs)
437 437 else:
438 438 visit = repo.changelog.heads()
439 439 seen = {}
440 440 while visit:
441 441 n = visit.pop(0)
442 442 parents = [p for p in repo.changelog.parents(n) if p not in has]
443 443 if len(parents) == 0:
444 444 o.insert(0, n)
445 445 else:
446 446 for p in parents:
447 447 if p not in seen:
448 448 seen[p] = 1
449 449 visit.append(p)
450 450 else:
451 451 cmdutil.setremoteconfig(ui, opts)
452 452 dest, revs, checkout = hg.parseurl(
453 453 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
454 454 other = hg.repository(ui, dest)
455 455 o = repo.findoutgoing(other, force=opts['force'])
456 456
457 457 if revs:
458 458 cg = repo.changegroupsubset(o, revs, 'bundle')
459 459 else:
460 460 cg = repo.changegroup(o, 'bundle')
461 461
462 462 bundletype = opts.get('type', 'bzip2').lower()
463 463 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
464 464 bundletype = btypes.get(bundletype)
465 465 if bundletype not in changegroup.bundletypes:
466 466 raise util.Abort(_('unknown bundle type specified with --type'))
467 467
468 468 changegroup.writebundle(cg, fname, bundletype)
469 469
470 470 def cat(ui, repo, file1, *pats, **opts):
471 471 """output the current or given revision of files
472 472
473 473 Print the specified files as they were at the given revision.
474 474 If no revision is given, the parent of the working directory is used,
475 475 or tip if no revision is checked out.
476 476
477 477 Output may be to a file, in which case the name of the file is
478 478 given using a format string. The formatting rules are the same as
479 479 for the export command, with the following additions:
480 480
481 481 %s basename of file being printed
482 482 %d dirname of file being printed, or '.' if in repo root
483 483 %p root-relative path name of file being printed
484 484 """
485 485 ctx = repo.changectx(opts['rev'])
486 486 err = 1
487 487 m = cmdutil.match(repo, (file1,) + pats, opts)
488 488 for abs in repo.walk(m, ctx.node()):
489 489 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
490 490 data = ctx.filectx(abs).data()
491 491 if opts.get('decode'):
492 492 data = repo.wwritedata(abs, data)
493 493 fp.write(data)
494 494 err = 0
495 495 return err
496 496
497 497 def clone(ui, source, dest=None, **opts):
498 498 """make a copy of an existing repository
499 499
500 500 Create a copy of an existing repository in a new directory.
501 501
502 502 If no destination directory name is specified, it defaults to the
503 503 basename of the source.
504 504
505 505 The location of the source is added to the new repository's
506 506 .hg/hgrc file, as the default to be used for future pulls.
507 507
508 508 For efficiency, hardlinks are used for cloning whenever the source
509 509 and destination are on the same filesystem (note this applies only
510 510 to the repository data, not to the checked out files). Some
511 511 filesystems, such as AFS, implement hardlinking incorrectly, but
512 512 do not report errors. In these cases, use the --pull option to
513 513 avoid hardlinking.
514 514
515 515 In some cases, you can clone repositories and checked out files
516 516 using full hardlinks with
517 517
518 518 $ cp -al REPO REPOCLONE
519 519
520 520 This is the fastest way to clone, but it is not always safe. The
521 521 operation is not atomic (making sure REPO is not modified during
522 522 the operation is up to you) and you have to make sure your editor
523 523 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
524 524 this is not compatible with certain extensions that place their
525 525 metadata under the .hg directory, such as mq.
526 526
527 527 If you use the -r option to clone up to a specific revision, no
528 528 subsequent revisions will be present in the cloned repository.
529 529 This option implies --pull, even on local repositories.
530 530
531 531 If the -U option is used, the new clone will contain only a repository
532 532 (.hg) and no working copy (the working copy parent is the null revision).
533 533
534 534 See pull for valid source format details.
535 535
536 536 It is possible to specify an ssh:// URL as the destination, but no
537 537 .hg/hgrc and working directory will be created on the remote side.
538 538 Look at the help text for the pull command for important details
539 539 about ssh:// URLs.
540 540 """
541 541 cmdutil.setremoteconfig(ui, opts)
542 542 hg.clone(ui, source, dest,
543 543 pull=opts['pull'],
544 544 stream=opts['uncompressed'],
545 545 rev=opts['rev'],
546 546 update=not opts['noupdate'])
547 547
548 548 def commit(ui, repo, *pats, **opts):
549 549 """commit the specified files or all outstanding changes
550 550
551 551 Commit changes to the given files into the repository.
552 552
553 553 If a list of files is omitted, all changes reported by "hg status"
554 554 will be committed.
555 555
556 556 If you are committing the result of a merge, do not provide any
557 557 file names or -I/-X filters.
558 558
559 559 If no commit message is specified, the configured editor is started to
560 560 enter a message.
561 561
562 562 See 'hg help dates' for a list of formats valid for -d/--date.
563 563 """
564 564 def commitfunc(ui, repo, message, match, opts):
565 565 return repo.commit(match.files(), message, opts['user'], opts['date'],
566 566 match, force_editor=opts.get('force_editor'))
567 567
568 568 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
569 569 if not node:
570 570 return
571 571 cl = repo.changelog
572 572 rev = cl.rev(node)
573 573 parents = cl.parentrevs(rev)
574 574 if rev - 1 in parents:
575 575 # one of the parents was the old tip
576 576 return
577 577 if (parents == (nullrev, nullrev) or
578 578 len(cl.heads(cl.node(parents[0]))) > 1 and
579 579 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
580 580 ui.status(_('created new head\n'))
581 581
582 582 def copy(ui, repo, *pats, **opts):
583 583 """mark files as copied for the next commit
584 584
585 585 Mark dest as having copies of source files. If dest is a
586 586 directory, copies are put in that directory. If dest is a file,
587 587 there can only be one source.
588 588
589 589 By default, this command copies the contents of files as they
590 590 stand in the working directory. If invoked with --after, the
591 591 operation is recorded, but no copying is performed.
592 592
593 593 This command takes effect in the next commit. To undo a copy
594 594 before that, see hg revert.
595 595 """
596 596 wlock = repo.wlock(False)
597 597 try:
598 598 return cmdutil.copy(ui, repo, pats, opts)
599 599 finally:
600 600 del wlock
601 601
602 602 def debugancestor(ui, repo, *args):
603 603 """find the ancestor revision of two revisions in a given index"""
604 604 if len(args) == 3:
605 605 index, rev1, rev2 = args
606 606 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
607 607 lookup = r.lookup
608 608 elif len(args) == 2:
609 609 if not repo:
610 610 raise util.Abort(_("There is no Mercurial repository here "
611 611 "(.hg not found)"))
612 612 rev1, rev2 = args
613 613 r = repo.changelog
614 614 lookup = repo.lookup
615 615 else:
616 616 raise util.Abort(_('either two or three arguments required'))
617 617 a = r.ancestor(lookup(rev1), lookup(rev2))
618 618 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
619 619
620 620 def debugcomplete(ui, cmd='', **opts):
621 621 """returns the completion list associated with the given command"""
622 622
623 623 if opts['options']:
624 624 options = []
625 625 otables = [globalopts]
626 626 if cmd:
627 627 aliases, entry = cmdutil.findcmd(ui, cmd, table)
628 628 otables.append(entry[1])
629 629 for t in otables:
630 630 for o in t:
631 631 if o[0]:
632 632 options.append('-%s' % o[0])
633 633 options.append('--%s' % o[1])
634 634 ui.write("%s\n" % "\n".join(options))
635 635 return
636 636
637 637 clist = cmdutil.findpossible(ui, cmd, table).keys()
638 638 clist.sort()
639 639 ui.write("%s\n" % "\n".join(clist))
640 640
641 641 def debugfsinfo(ui, path = "."):
642 642 file('.debugfsinfo', 'w').write('')
643 643 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
644 644 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
645 645 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
646 646 and 'yes' or 'no'))
647 647 os.unlink('.debugfsinfo')
648 648
649 649 def debugrebuildstate(ui, repo, rev=""):
650 650 """rebuild the dirstate as it would look like for the given revision"""
651 651 if rev == "":
652 652 rev = repo.changelog.tip()
653 653 ctx = repo.changectx(rev)
654 654 files = ctx.manifest()
655 655 wlock = repo.wlock()
656 656 try:
657 657 repo.dirstate.rebuild(rev, files)
658 658 finally:
659 659 del wlock
660 660
661 661 def debugcheckstate(ui, repo):
662 662 """validate the correctness of the current dirstate"""
663 663 parent1, parent2 = repo.dirstate.parents()
664 664 m1 = repo.changectx(parent1).manifest()
665 665 m2 = repo.changectx(parent2).manifest()
666 666 errors = 0
667 667 for f in repo.dirstate:
668 668 state = repo.dirstate[f]
669 669 if state in "nr" and f not in m1:
670 670 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
671 671 errors += 1
672 672 if state in "a" and f in m1:
673 673 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
674 674 errors += 1
675 675 if state in "m" and f not in m1 and f not in m2:
676 676 ui.warn(_("%s in state %s, but not in either manifest\n") %
677 677 (f, state))
678 678 errors += 1
679 679 for f in m1:
680 680 state = repo.dirstate[f]
681 681 if state not in "nrm":
682 682 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
683 683 errors += 1
684 684 if errors:
685 685 error = _(".hg/dirstate inconsistent with current parent's manifest")
686 686 raise util.Abort(error)
687 687
688 688 def showconfig(ui, repo, *values, **opts):
689 689 """show combined config settings from all hgrc files
690 690
691 691 With no args, print names and values of all config items.
692 692
693 693 With one arg of the form section.name, print just the value of
694 694 that config item.
695 695
696 696 With multiple args, print names and values of all config items
697 697 with matching section names."""
698 698
699 699 untrusted = bool(opts.get('untrusted'))
700 700 if values:
701 701 if len([v for v in values if '.' in v]) > 1:
702 702 raise util.Abort(_('only one config item permitted'))
703 703 for section, name, value in ui.walkconfig(untrusted=untrusted):
704 704 sectname = section + '.' + name
705 705 if values:
706 706 for v in values:
707 707 if v == section:
708 708 ui.write('%s=%s\n' % (sectname, value))
709 709 elif v == sectname:
710 710 ui.write(value, '\n')
711 711 else:
712 712 ui.write('%s=%s\n' % (sectname, value))
713 713
714 714 def debugsetparents(ui, repo, rev1, rev2=None):
715 715 """manually set the parents of the current working directory
716 716
717 717 This is useful for writing repository conversion tools, but should
718 718 be used with care.
719 719 """
720 720
721 721 if not rev2:
722 722 rev2 = hex(nullid)
723 723
724 724 wlock = repo.wlock()
725 725 try:
726 726 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
727 727 finally:
728 728 del wlock
729 729
730 730 def debugstate(ui, repo, nodates=None):
731 731 """show the contents of the current dirstate"""
732 732 k = repo.dirstate._map.items()
733 733 k.sort()
734 734 timestr = ""
735 735 showdate = not nodates
736 736 for file_, ent in k:
737 737 if showdate:
738 738 if ent[3] == -1:
739 739 # Pad or slice to locale representation
740 740 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
741 741 timestr = 'unset'
742 742 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
743 743 else:
744 744 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
745 745 if ent[1] & 020000:
746 746 mode = 'lnk'
747 747 else:
748 748 mode = '%3o' % (ent[1] & 0777)
749 749 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
750 750 for f in repo.dirstate.copies():
751 751 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
752 752
753 753 def debugdata(ui, file_, rev):
754 754 """dump the contents of a data file revision"""
755 755 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
756 756 try:
757 757 ui.write(r.revision(r.lookup(rev)))
758 758 except KeyError:
759 759 raise util.Abort(_('invalid revision identifier %s') % rev)
760 760
761 761 def debugdate(ui, date, range=None, **opts):
762 762 """parse and display a date"""
763 763 if opts["extended"]:
764 764 d = util.parsedate(date, util.extendeddateformats)
765 765 else:
766 766 d = util.parsedate(date)
767 767 ui.write("internal: %s %s\n" % d)
768 768 ui.write("standard: %s\n" % util.datestr(d))
769 769 if range:
770 770 m = util.matchdate(range)
771 771 ui.write("match: %s\n" % m(d[0]))
772 772
773 773 def debugindex(ui, file_):
774 774 """dump the contents of an index file"""
775 775 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
776 776 ui.write(" rev offset length base linkrev" +
777 777 " nodeid p1 p2\n")
778 778 for i in xrange(r.count()):
779 779 node = r.node(i)
780 780 try:
781 781 pp = r.parents(node)
782 782 except:
783 783 pp = [nullid, nullid]
784 784 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
785 785 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
786 786 short(node), short(pp[0]), short(pp[1])))
787 787
788 788 def debugindexdot(ui, file_):
789 789 """dump an index DAG as a .dot file"""
790 790 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
791 791 ui.write("digraph G {\n")
792 792 for i in xrange(r.count()):
793 793 node = r.node(i)
794 794 pp = r.parents(node)
795 795 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
796 796 if pp[1] != nullid:
797 797 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
798 798 ui.write("}\n")
799 799
800 800 def debuginstall(ui):
801 801 '''test Mercurial installation'''
802 802
803 803 def writetemp(contents):
804 804 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
805 805 f = os.fdopen(fd, "wb")
806 806 f.write(contents)
807 807 f.close()
808 808 return name
809 809
810 810 problems = 0
811 811
812 812 # encoding
813 813 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
814 814 try:
815 815 util.fromlocal("test")
816 816 except util.Abort, inst:
817 817 ui.write(" %s\n" % inst)
818 818 ui.write(_(" (check that your locale is properly set)\n"))
819 819 problems += 1
820 820
821 821 # compiled modules
822 822 ui.status(_("Checking extensions...\n"))
823 823 try:
824 824 import bdiff, mpatch, base85
825 825 except Exception, inst:
826 826 ui.write(" %s\n" % inst)
827 827 ui.write(_(" One or more extensions could not be found"))
828 828 ui.write(_(" (check that you compiled the extensions)\n"))
829 829 problems += 1
830 830
831 831 # templates
832 832 ui.status(_("Checking templates...\n"))
833 833 try:
834 834 import templater
835 835 t = templater.templater(templater.templatepath("map-cmdline.default"))
836 836 except Exception, inst:
837 837 ui.write(" %s\n" % inst)
838 838 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
839 839 problems += 1
840 840
841 841 # patch
842 842 ui.status(_("Checking patch...\n"))
843 843 patchproblems = 0
844 844 a = "1\n2\n3\n4\n"
845 845 b = "1\n2\n3\ninsert\n4\n"
846 846 fa = writetemp(a)
847 847 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
848 848 os.path.basename(fa))
849 849 fd = writetemp(d)
850 850
851 851 files = {}
852 852 try:
853 853 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
854 854 except util.Abort, e:
855 855 ui.write(_(" patch call failed:\n"))
856 856 ui.write(" " + str(e) + "\n")
857 857 patchproblems += 1
858 858 else:
859 859 if list(files) != [os.path.basename(fa)]:
860 860 ui.write(_(" unexpected patch output!\n"))
861 861 patchproblems += 1
862 862 a = file(fa).read()
863 863 if a != b:
864 864 ui.write(_(" patch test failed!\n"))
865 865 patchproblems += 1
866 866
867 867 if patchproblems:
868 868 if ui.config('ui', 'patch'):
869 869 ui.write(_(" (Current patch tool may be incompatible with patch,"
870 870 " or misconfigured. Please check your .hgrc file)\n"))
871 871 else:
872 872 ui.write(_(" Internal patcher failure, please report this error"
873 873 " to http://www.selenic.com/mercurial/bts\n"))
874 874 problems += patchproblems
875 875
876 876 os.unlink(fa)
877 877 os.unlink(fd)
878 878
879 879 # editor
880 880 ui.status(_("Checking commit editor...\n"))
881 881 editor = ui.geteditor()
882 882 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
883 883 if not cmdpath:
884 884 if editor == 'vi':
885 885 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
886 886 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
887 887 else:
888 888 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
889 889 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
890 890 problems += 1
891 891
892 892 # check username
893 893 ui.status(_("Checking username...\n"))
894 894 user = os.environ.get("HGUSER")
895 895 if user is None:
896 896 user = ui.config("ui", "username")
897 897 if user is None:
898 898 user = os.environ.get("EMAIL")
899 899 if not user:
900 900 ui.warn(" ")
901 901 ui.username()
902 902 ui.write(_(" (specify a username in your .hgrc file)\n"))
903 903
904 904 if not problems:
905 905 ui.status(_("No problems detected\n"))
906 906 else:
907 907 ui.write(_("%s problems detected,"
908 908 " please check your install!\n") % problems)
909 909
910 910 return problems
911 911
912 912 def debugrename(ui, repo, file1, *pats, **opts):
913 913 """dump rename information"""
914 914
915 915 ctx = repo.changectx(opts.get('rev', 'tip'))
916 916 m = cmdutil.match(repo, (file1,) + pats, opts)
917 917 for abs in repo.walk(m, ctx.node()):
918 918 fctx = ctx.filectx(abs)
919 919 o = fctx.filelog().renamed(fctx.filenode())
920 920 rel = m.rel(abs)
921 921 if o:
922 922 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
923 923 else:
924 924 ui.write(_("%s not renamed\n") % rel)
925 925
926 926 def debugwalk(ui, repo, *pats, **opts):
927 927 """show how files match on given patterns"""
928 928 m = cmdutil.match(repo, pats, opts)
929 929 items = list(repo.walk(m))
930 930 if not items:
931 931 return
932 932 fmt = 'f %%-%ds %%-%ds %%s' % (
933 933 max([len(abs) for abs in items]),
934 934 max([len(m.rel(abs)) for abs in items]))
935 935 for abs in items:
936 936 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
937 937 ui.write("%s\n" % line.rstrip())
938 938
939 939 def diff(ui, repo, *pats, **opts):
940 940 """diff repository (or selected files)
941 941
942 942 Show differences between revisions for the specified files.
943 943
944 944 Differences between files are shown using the unified diff format.
945 945
946 946 NOTE: diff may generate unexpected results for merges, as it will
947 947 default to comparing against the working directory's first parent
948 948 changeset if no revisions are specified.
949 949
950 950 When two revision arguments are given, then changes are shown
951 951 between those revisions. If only one revision is specified then
952 952 that revision is compared to the working directory, and, when no
953 953 revisions are specified, the working directory files are compared
954 954 to its parent.
955 955
956 956 Without the -a option, diff will avoid generating diffs of files
957 957 it detects as binary. With -a, diff will generate a diff anyway,
958 958 probably with undesirable results.
959 959 """
960 960 node1, node2 = cmdutil.revpair(repo, opts['rev'])
961 961
962 962 m = cmdutil.match(repo, pats, opts)
963 963 patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
964 964
965 965 def export(ui, repo, *changesets, **opts):
966 966 """dump the header and diffs for one or more changesets
967 967
968 968 Print the changeset header and diffs for one or more revisions.
969 969
970 970 The information shown in the changeset header is: author,
971 971 changeset hash, parent(s) and commit comment.
972 972
973 973 NOTE: export may generate unexpected diff output for merge changesets,
974 974 as it will compare the merge changeset against its first parent only.
975 975
976 976 Output may be to a file, in which case the name of the file is
977 977 given using a format string. The formatting rules are as follows:
978 978
979 979 %% literal "%" character
980 980 %H changeset hash (40 bytes of hexadecimal)
981 981 %N number of patches being generated
982 982 %R changeset revision number
983 983 %b basename of the exporting repository
984 984 %h short-form changeset hash (12 bytes of hexadecimal)
985 985 %n zero-padded sequence number, starting at 1
986 986 %r zero-padded changeset revision number
987 987
988 988 Without the -a option, export will avoid generating diffs of files
989 989 it detects as binary. With -a, export will generate a diff anyway,
990 990 probably with undesirable results.
991 991
992 992 With the --switch-parent option, the diff will be against the second
993 993 parent. It can be useful to review a merge.
994 994 """
995 995 if not changesets:
996 996 raise util.Abort(_("export requires at least one changeset"))
997 997 revs = cmdutil.revrange(repo, changesets)
998 998 if len(revs) > 1:
999 999 ui.note(_('exporting patches:\n'))
1000 1000 else:
1001 1001 ui.note(_('exporting patch:\n'))
1002 1002 patch.export(repo, revs, template=opts['output'],
1003 1003 switch_parent=opts['switch_parent'],
1004 1004 opts=patch.diffopts(ui, opts))
1005 1005
1006 1006 def grep(ui, repo, pattern, *pats, **opts):
1007 1007 """search for a pattern in specified files and revisions
1008 1008
1009 1009 Search revisions of files for a regular expression.
1010 1010
1011 1011 This command behaves differently than Unix grep. It only accepts
1012 1012 Python/Perl regexps. It searches repository history, not the
1013 1013 working directory. It always prints the revision number in which
1014 1014 a match appears.
1015 1015
1016 1016 By default, grep only prints output for the first revision of a
1017 1017 file in which it finds a match. To get it to print every revision
1018 1018 that contains a change in match status ("-" for a match that
1019 1019 becomes a non-match, or "+" for a non-match that becomes a match),
1020 1020 use the --all flag.
1021 1021 """
1022 1022 reflags = 0
1023 1023 if opts['ignore_case']:
1024 1024 reflags |= re.I
1025 1025 try:
1026 1026 regexp = re.compile(pattern, reflags)
1027 1027 except Exception, inst:
1028 1028 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1029 1029 return None
1030 1030 sep, eol = ':', '\n'
1031 1031 if opts['print0']:
1032 1032 sep = eol = '\0'
1033 1033
1034 1034 fcache = {}
1035 1035 def getfile(fn):
1036 1036 if fn not in fcache:
1037 1037 fcache[fn] = repo.file(fn)
1038 1038 return fcache[fn]
1039 1039
1040 1040 def matchlines(body):
1041 1041 begin = 0
1042 1042 linenum = 0
1043 1043 while True:
1044 1044 match = regexp.search(body, begin)
1045 1045 if not match:
1046 1046 break
1047 1047 mstart, mend = match.span()
1048 1048 linenum += body.count('\n', begin, mstart) + 1
1049 1049 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1050 1050 lend = body.find('\n', mend)
1051 1051 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1052 1052 begin = lend + 1
1053 1053
1054 1054 class linestate(object):
1055 1055 def __init__(self, line, linenum, colstart, colend):
1056 1056 self.line = line
1057 1057 self.linenum = linenum
1058 1058 self.colstart = colstart
1059 1059 self.colend = colend
1060 1060
1061 1061 def __hash__(self):
1062 1062 return hash((self.linenum, self.line))
1063 1063
1064 1064 def __eq__(self, other):
1065 1065 return self.line == other.line
1066 1066
1067 1067 matches = {}
1068 1068 copies = {}
1069 1069 def grepbody(fn, rev, body):
1070 1070 matches[rev].setdefault(fn, [])
1071 1071 m = matches[rev][fn]
1072 1072 for lnum, cstart, cend, line in matchlines(body):
1073 1073 s = linestate(line, lnum, cstart, cend)
1074 1074 m.append(s)
1075 1075
1076 1076 def difflinestates(a, b):
1077 1077 sm = difflib.SequenceMatcher(None, a, b)
1078 1078 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1079 1079 if tag == 'insert':
1080 1080 for i in xrange(blo, bhi):
1081 1081 yield ('+', b[i])
1082 1082 elif tag == 'delete':
1083 1083 for i in xrange(alo, ahi):
1084 1084 yield ('-', a[i])
1085 1085 elif tag == 'replace':
1086 1086 for i in xrange(alo, ahi):
1087 1087 yield ('-', a[i])
1088 1088 for i in xrange(blo, bhi):
1089 1089 yield ('+', b[i])
1090 1090
1091 1091 prev = {}
1092 1092 def display(fn, rev, states, prevstates):
1093 1093 datefunc = ui.quiet and util.shortdate or util.datestr
1094 1094 found = False
1095 1095 filerevmatches = {}
1096 1096 r = prev.get(fn, -1)
1097 1097 if opts['all']:
1098 1098 iter = difflinestates(states, prevstates)
1099 1099 else:
1100 1100 iter = [('', l) for l in prevstates]
1101 1101 for change, l in iter:
1102 1102 cols = [fn, str(r)]
1103 1103 if opts['line_number']:
1104 1104 cols.append(str(l.linenum))
1105 1105 if opts['all']:
1106 1106 cols.append(change)
1107 1107 if opts['user']:
1108 1108 cols.append(ui.shortuser(get(r)[1]))
1109 1109 if opts.get('date'):
1110 1110 cols.append(datefunc(get(r)[2]))
1111 1111 if opts['files_with_matches']:
1112 1112 c = (fn, r)
1113 1113 if c in filerevmatches:
1114 1114 continue
1115 1115 filerevmatches[c] = 1
1116 1116 else:
1117 1117 cols.append(l.line)
1118 1118 ui.write(sep.join(cols), eol)
1119 1119 found = True
1120 1120 return found
1121 1121
1122 1122 fstate = {}
1123 1123 skip = {}
1124 1124 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1125 1125 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1126 1126 found = False
1127 1127 follow = opts.get('follow')
1128 1128 for st, rev, fns in changeiter:
1129 1129 if st == 'window':
1130 1130 matches.clear()
1131 1131 elif st == 'add':
1132 1132 ctx = repo.changectx(rev)
1133 1133 matches[rev] = {}
1134 1134 for fn in fns:
1135 1135 if fn in skip:
1136 1136 continue
1137 1137 try:
1138 1138 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1139 1139 fstate.setdefault(fn, [])
1140 1140 if follow:
1141 1141 copied = getfile(fn).renamed(ctx.filenode(fn))
1142 1142 if copied:
1143 1143 copies.setdefault(rev, {})[fn] = copied[0]
1144 1144 except revlog.LookupError:
1145 1145 pass
1146 1146 elif st == 'iter':
1147 1147 states = matches[rev].items()
1148 1148 states.sort()
1149 1149 for fn, m in states:
1150 1150 copy = copies.get(rev, {}).get(fn)
1151 1151 if fn in skip:
1152 1152 if copy:
1153 1153 skip[copy] = True
1154 1154 continue
1155 1155 if fn in prev or fstate[fn]:
1156 1156 r = display(fn, rev, m, fstate[fn])
1157 1157 found = found or r
1158 1158 if r and not opts['all']:
1159 1159 skip[fn] = True
1160 1160 if copy:
1161 1161 skip[copy] = True
1162 1162 fstate[fn] = m
1163 1163 if copy:
1164 1164 fstate[copy] = m
1165 1165 prev[fn] = rev
1166 1166
1167 1167 fstate = fstate.items()
1168 1168 fstate.sort()
1169 1169 for fn, state in fstate:
1170 1170 if fn in skip:
1171 1171 continue
1172 1172 if fn not in copies.get(prev[fn], {}):
1173 1173 found = display(fn, rev, {}, state) or found
1174 1174 return (not found and 1) or 0
1175 1175
1176 1176 def heads(ui, repo, *branchrevs, **opts):
1177 1177 """show current repository heads or show branch heads
1178 1178
1179 1179 With no arguments, show all repository head changesets.
1180 1180
1181 1181 If branch or revisions names are given this will show the heads of
1182 1182 the specified branches or the branches those revisions are tagged
1183 1183 with.
1184 1184
1185 1185 Repository "heads" are changesets that don't have child
1186 1186 changesets. They are where development generally takes place and
1187 1187 are the usual targets for update and merge operations.
1188 1188
1189 1189 Branch heads are changesets that have a given branch tag, but have
1190 1190 no child changesets with that tag. They are usually where
1191 1191 development on the given branch takes place.
1192 1192 """
1193 1193 if opts['rev']:
1194 1194 start = repo.lookup(opts['rev'])
1195 1195 else:
1196 1196 start = None
1197 1197 if not branchrevs:
1198 1198 # Assume we're looking repo-wide heads if no revs were specified.
1199 1199 heads = repo.heads(start)
1200 1200 else:
1201 1201 heads = []
1202 1202 visitedset = util.set()
1203 1203 for branchrev in branchrevs:
1204 1204 branch = repo.changectx(branchrev).branch()
1205 1205 if branch in visitedset:
1206 1206 continue
1207 1207 visitedset.add(branch)
1208 1208 bheads = repo.branchheads(branch, start)
1209 1209 if not bheads:
1210 1210 if branch != branchrev:
1211 1211 ui.warn(_("no changes on branch %s containing %s are "
1212 1212 "reachable from %s\n")
1213 1213 % (branch, branchrev, opts['rev']))
1214 1214 else:
1215 1215 ui.warn(_("no changes on branch %s are reachable from %s\n")
1216 1216 % (branch, opts['rev']))
1217 1217 heads.extend(bheads)
1218 1218 if not heads:
1219 1219 return 1
1220 1220 displayer = cmdutil.show_changeset(ui, repo, opts)
1221 1221 for n in heads:
1222 1222 displayer.show(changenode=n)
1223 1223
1224 1224 def help_(ui, name=None, with_version=False):
1225 1225 """show help for a command, extension, or list of commands
1226 1226
1227 1227 With no arguments, print a list of commands and short help.
1228 1228
1229 1229 Given a command name, print help for that command.
1230 1230
1231 1231 Given an extension name, print help for that extension, and the
1232 1232 commands it provides."""
1233 1233 option_lists = []
1234 1234
1235 1235 def addglobalopts(aliases):
1236 1236 if ui.verbose:
1237 1237 option_lists.append((_("global options:"), globalopts))
1238 1238 if name == 'shortlist':
1239 1239 option_lists.append((_('use "hg help" for the full list '
1240 1240 'of commands'), ()))
1241 1241 else:
1242 1242 if name == 'shortlist':
1243 1243 msg = _('use "hg help" for the full list of commands '
1244 1244 'or "hg -v" for details')
1245 1245 elif aliases:
1246 1246 msg = _('use "hg -v help%s" to show aliases and '
1247 1247 'global options') % (name and " " + name or "")
1248 1248 else:
1249 1249 msg = _('use "hg -v help %s" to show global options') % name
1250 1250 option_lists.append((msg, ()))
1251 1251
1252 1252 def helpcmd(name):
1253 1253 if with_version:
1254 1254 version_(ui)
1255 1255 ui.write('\n')
1256 1256
1257 1257 try:
1258 1258 aliases, i = cmdutil.findcmd(ui, name, table)
1259 1259 except cmdutil.AmbiguousCommand, inst:
1260 1260 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1261 1261 helplist(_('list of commands:\n\n'), select)
1262 1262 return
1263 1263
1264 1264 # synopsis
1265 1265 ui.write("%s\n" % i[2])
1266 1266
1267 1267 # aliases
1268 1268 if not ui.quiet and len(aliases) > 1:
1269 1269 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1270 1270
1271 1271 # description
1272 1272 doc = i[0].__doc__
1273 1273 if not doc:
1274 1274 doc = _("(No help text available)")
1275 1275 if ui.quiet:
1276 1276 doc = doc.splitlines(0)[0]
1277 1277 ui.write("\n%s\n" % doc.rstrip())
1278 1278
1279 1279 if not ui.quiet:
1280 1280 # options
1281 1281 if i[1]:
1282 1282 option_lists.append((_("options:\n"), i[1]))
1283 1283
1284 1284 addglobalopts(False)
1285 1285
1286 1286 def helplist(header, select=None):
1287 1287 h = {}
1288 1288 cmds = {}
1289 1289 for c, e in table.items():
1290 1290 f = c.split("|", 1)[0]
1291 1291 if select and not select(f):
1292 1292 continue
1293 1293 if name == "shortlist" and not f.startswith("^"):
1294 1294 continue
1295 1295 f = f.lstrip("^")
1296 1296 if not ui.debugflag and f.startswith("debug"):
1297 1297 continue
1298 1298 doc = e[0].__doc__
1299 1299 if not doc:
1300 1300 doc = _("(No help text available)")
1301 1301 h[f] = doc.splitlines(0)[0].rstrip()
1302 1302 cmds[f] = c.lstrip("^")
1303 1303
1304 1304 if not h:
1305 1305 ui.status(_('no commands defined\n'))
1306 1306 return
1307 1307
1308 1308 ui.status(header)
1309 1309 fns = h.keys()
1310 1310 fns.sort()
1311 1311 m = max(map(len, fns))
1312 1312 for f in fns:
1313 1313 if ui.verbose:
1314 1314 commands = cmds[f].replace("|",", ")
1315 1315 ui.write(" %s:\n %s\n"%(commands, h[f]))
1316 1316 else:
1317 1317 ui.write(' %-*s %s\n' % (m, f, h[f]))
1318 1318
1319 1319 if not ui.quiet:
1320 1320 addglobalopts(True)
1321 1321
1322 1322 def helptopic(name):
1323 1323 v = None
1324 1324 for i, d in help.helptable:
1325 1325 l = i.split('|')
1326 1326 if name in l:
1327 1327 v = i
1328 1328 header = l[-1]
1329 1329 doc = d
1330 1330 if not v:
1331 1331 raise cmdutil.UnknownCommand(name)
1332 1332
1333 1333 # description
1334 1334 if not doc:
1335 1335 doc = _("(No help text available)")
1336 1336 if callable(doc):
1337 1337 doc = doc()
1338 1338
1339 1339 ui.write("%s\n" % header)
1340 1340 ui.write("%s\n" % doc.rstrip())
1341 1341
1342 1342 def helpext(name):
1343 1343 try:
1344 1344 mod = extensions.find(name)
1345 1345 except KeyError:
1346 1346 raise cmdutil.UnknownCommand(name)
1347 1347
1348 1348 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1349 1349 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1350 1350 for d in doc[1:]:
1351 1351 ui.write(d, '\n')
1352 1352
1353 1353 ui.status('\n')
1354 1354
1355 1355 try:
1356 1356 ct = mod.cmdtable
1357 1357 except AttributeError:
1358 1358 ct = {}
1359 1359
1360 1360 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1361 1361 helplist(_('list of commands:\n\n'), modcmds.has_key)
1362 1362
1363 1363 if name and name != 'shortlist':
1364 1364 i = None
1365 1365 for f in (helpcmd, helptopic, helpext):
1366 1366 try:
1367 1367 f(name)
1368 1368 i = None
1369 1369 break
1370 1370 except cmdutil.UnknownCommand, inst:
1371 1371 i = inst
1372 1372 if i:
1373 1373 raise i
1374 1374
1375 1375 else:
1376 1376 # program name
1377 1377 if ui.verbose or with_version:
1378 1378 version_(ui)
1379 1379 else:
1380 1380 ui.status(_("Mercurial Distributed SCM\n"))
1381 1381 ui.status('\n')
1382 1382
1383 1383 # list of commands
1384 1384 if name == "shortlist":
1385 1385 header = _('basic commands:\n\n')
1386 1386 else:
1387 1387 header = _('list of commands:\n\n')
1388 1388
1389 1389 helplist(header)
1390 1390
1391 1391 # list all option lists
1392 1392 opt_output = []
1393 1393 for title, options in option_lists:
1394 1394 opt_output.append(("\n%s" % title, None))
1395 1395 for shortopt, longopt, default, desc in options:
1396 1396 if "DEPRECATED" in desc and not ui.verbose: continue
1397 1397 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1398 1398 longopt and " --%s" % longopt),
1399 1399 "%s%s" % (desc,
1400 1400 default
1401 1401 and _(" (default: %s)") % default
1402 1402 or "")))
1403 1403
1404 1404 if ui.verbose:
1405 1405 ui.write(_("\nspecial help topics:\n"))
1406 1406 topics = []
1407 1407 for i, d in help.helptable:
1408 1408 l = i.split('|')
1409 1409 topics.append((", ".join(l[:-1]), l[-1]))
1410 1410 topics_len = max([len(s[0]) for s in topics])
1411 1411 for t, desc in topics:
1412 1412 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1413 1413
1414 1414 if opt_output:
1415 1415 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1416 1416 for first, second in opt_output:
1417 1417 if second:
1418 1418 ui.write(" %-*s %s\n" % (opts_len, first, second))
1419 1419 else:
1420 1420 ui.write("%s\n" % first)
1421 1421
1422 1422 def identify(ui, repo, source=None,
1423 1423 rev=None, num=None, id=None, branch=None, tags=None):
1424 1424 """identify the working copy or specified revision
1425 1425
1426 1426 With no revision, print a summary of the current state of the repo.
1427 1427
1428 1428 With a path, do a lookup in another repository.
1429 1429
1430 1430 This summary identifies the repository state using one or two parent
1431 1431 hash identifiers, followed by a "+" if there are uncommitted changes
1432 1432 in the working directory, a list of tags for this revision and a branch
1433 1433 name for non-default branches.
1434 1434 """
1435 1435
1436 1436 if not repo and not source:
1437 1437 raise util.Abort(_("There is no Mercurial repository here "
1438 1438 "(.hg not found)"))
1439 1439
1440 1440 hexfunc = ui.debugflag and hex or short
1441 1441 default = not (num or id or branch or tags)
1442 1442 output = []
1443 1443
1444 1444 if source:
1445 1445 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1446 1446 srepo = hg.repository(ui, source)
1447 1447 if not rev and revs:
1448 1448 rev = revs[0]
1449 1449 if not rev:
1450 1450 rev = "tip"
1451 1451 if num or branch or tags:
1452 1452 raise util.Abort(
1453 1453 "can't query remote revision number, branch, or tags")
1454 1454 output = [hexfunc(srepo.lookup(rev))]
1455 1455 elif not rev:
1456 1456 ctx = repo.workingctx()
1457 1457 parents = ctx.parents()
1458 1458 changed = False
1459 1459 if default or id or num:
1460 1460 changed = ctx.files() + ctx.deleted()
1461 1461 if default or id:
1462 1462 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1463 1463 (changed) and "+" or "")]
1464 1464 if num:
1465 1465 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1466 1466 (changed) and "+" or ""))
1467 1467 else:
1468 1468 ctx = repo.changectx(rev)
1469 1469 if default or id:
1470 1470 output = [hexfunc(ctx.node())]
1471 1471 if num:
1472 1472 output.append(str(ctx.rev()))
1473 1473
1474 1474 if not source and default and not ui.quiet:
1475 1475 b = util.tolocal(ctx.branch())
1476 1476 if b != 'default':
1477 1477 output.append("(%s)" % b)
1478 1478
1479 1479 # multiple tags for a single parent separated by '/'
1480 1480 t = "/".join(ctx.tags())
1481 1481 if t:
1482 1482 output.append(t)
1483 1483
1484 1484 if branch:
1485 1485 output.append(util.tolocal(ctx.branch()))
1486 1486
1487 1487 if tags:
1488 1488 output.extend(ctx.tags())
1489 1489
1490 1490 ui.write("%s\n" % ' '.join(output))
1491 1491
1492 1492 def import_(ui, repo, patch1, *patches, **opts):
1493 1493 """import an ordered set of patches
1494 1494
1495 1495 Import a list of patches and commit them individually.
1496 1496
1497 1497 If there are outstanding changes in the working directory, import
1498 1498 will abort unless given the -f flag.
1499 1499
1500 1500 You can import a patch straight from a mail message. Even patches
1501 1501 as attachments work (body part must be type text/plain or
1502 1502 text/x-patch to be used). From and Subject headers of email
1503 1503 message are used as default committer and commit message. All
1504 1504 text/plain body parts before first diff are added to commit
1505 1505 message.
1506 1506
1507 1507 If the imported patch was generated by hg export, user and description
1508 1508 from patch override values from message headers and body. Values
1509 1509 given on command line with -m and -u override these.
1510 1510
1511 1511 If --exact is specified, import will set the working directory
1512 1512 to the parent of each patch before applying it, and will abort
1513 1513 if the resulting changeset has a different ID than the one
1514 1514 recorded in the patch. This may happen due to character set
1515 1515 problems or other deficiencies in the text patch format.
1516 1516
1517 1517 To read a patch from standard input, use patch name "-".
1518 1518 See 'hg help dates' for a list of formats valid for -d/--date.
1519 1519 """
1520 1520 patches = (patch1,) + patches
1521 1521
1522 1522 date = opts.get('date')
1523 1523 if date:
1524 1524 opts['date'] = util.parsedate(date)
1525 1525
1526 1526 if opts.get('exact') or not opts['force']:
1527 1527 cmdutil.bail_if_changed(repo)
1528 1528
1529 1529 d = opts["base"]
1530 1530 strip = opts["strip"]
1531 1531 wlock = lock = None
1532 1532 try:
1533 1533 wlock = repo.wlock()
1534 1534 lock = repo.lock()
1535 1535 for p in patches:
1536 1536 pf = os.path.join(d, p)
1537 1537
1538 1538 if pf == '-':
1539 1539 ui.status(_("applying patch from stdin\n"))
1540 1540 data = patch.extract(ui, sys.stdin)
1541 1541 else:
1542 1542 ui.status(_("applying %s\n") % p)
1543 1543 if os.path.exists(pf):
1544 1544 data = patch.extract(ui, file(pf, 'rb'))
1545 1545 else:
1546 1546 data = patch.extract(ui, urllib.urlopen(pf))
1547 1547 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1548 1548
1549 1549 if tmpname is None:
1550 1550 raise util.Abort(_('no diffs found'))
1551 1551
1552 1552 try:
1553 1553 cmdline_message = cmdutil.logmessage(opts)
1554 1554 if cmdline_message:
1555 1555 # pickup the cmdline msg
1556 1556 message = cmdline_message
1557 1557 elif message:
1558 1558 # pickup the patch msg
1559 1559 message = message.strip()
1560 1560 else:
1561 1561 # launch the editor
1562 1562 message = None
1563 1563 ui.debug(_('message:\n%s\n') % message)
1564 1564
1565 1565 wp = repo.workingctx().parents()
1566 1566 if opts.get('exact'):
1567 1567 if not nodeid or not p1:
1568 1568 raise util.Abort(_('not a mercurial patch'))
1569 1569 p1 = repo.lookup(p1)
1570 1570 p2 = repo.lookup(p2 or hex(nullid))
1571 1571
1572 1572 if p1 != wp[0].node():
1573 1573 hg.clean(repo, p1)
1574 1574 repo.dirstate.setparents(p1, p2)
1575 1575 elif p2:
1576 1576 try:
1577 1577 p1 = repo.lookup(p1)
1578 1578 p2 = repo.lookup(p2)
1579 1579 if p1 == wp[0].node():
1580 1580 repo.dirstate.setparents(p1, p2)
1581 1581 except RepoError:
1582 1582 pass
1583 1583 if opts.get('exact') or opts.get('import_branch'):
1584 1584 repo.dirstate.setbranch(branch or 'default')
1585 1585
1586 1586 files = {}
1587 1587 try:
1588 1588 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1589 1589 files=files)
1590 1590 finally:
1591 1591 files = patch.updatedir(ui, repo, files)
1592 1592 if not opts.get('no_commit'):
1593 1593 n = repo.commit(files, message, opts.get('user') or user,
1594 1594 opts.get('date') or date)
1595 1595 if opts.get('exact'):
1596 1596 if hex(n) != nodeid:
1597 1597 repo.rollback()
1598 1598 raise util.Abort(_('patch is damaged'
1599 1599 ' or loses information'))
1600 1600 # Force a dirstate write so that the next transaction
1601 1601 # backups an up-do-date file.
1602 1602 repo.dirstate.write()
1603 1603 finally:
1604 1604 os.unlink(tmpname)
1605 1605 finally:
1606 1606 del lock, wlock
1607 1607
1608 1608 def incoming(ui, repo, source="default", **opts):
1609 1609 """show new changesets found in source
1610 1610
1611 1611 Show new changesets found in the specified path/URL or the default
1612 1612 pull location. These are the changesets that would be pulled if a pull
1613 1613 was requested.
1614 1614
1615 1615 For remote repository, using --bundle avoids downloading the changesets
1616 1616 twice if the incoming is followed by a pull.
1617 1617
1618 1618 See pull for valid source format details.
1619 1619 """
1620 1620 limit = cmdutil.loglimit(opts)
1621 1621 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1622 1622 cmdutil.setremoteconfig(ui, opts)
1623 1623
1624 1624 other = hg.repository(ui, source)
1625 1625 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1626 1626 if revs:
1627 1627 revs = [other.lookup(rev) for rev in revs]
1628 1628 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1629 1629 if not incoming:
1630 1630 try:
1631 1631 os.unlink(opts["bundle"])
1632 1632 except:
1633 1633 pass
1634 1634 ui.status(_("no changes found\n"))
1635 1635 return 1
1636 1636
1637 1637 cleanup = None
1638 1638 try:
1639 1639 fname = opts["bundle"]
1640 1640 if fname or not other.local():
1641 1641 # create a bundle (uncompressed if other repo is not local)
1642 1642 if revs is None:
1643 1643 cg = other.changegroup(incoming, "incoming")
1644 1644 else:
1645 1645 cg = other.changegroupsubset(incoming, revs, 'incoming')
1646 1646 bundletype = other.local() and "HG10BZ" or "HG10UN"
1647 1647 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1648 1648 # keep written bundle?
1649 1649 if opts["bundle"]:
1650 1650 cleanup = None
1651 1651 if not other.local():
1652 1652 # use the created uncompressed bundlerepo
1653 1653 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1654 1654
1655 1655 o = other.changelog.nodesbetween(incoming, revs)[0]
1656 1656 if opts['newest_first']:
1657 1657 o.reverse()
1658 1658 displayer = cmdutil.show_changeset(ui, other, opts)
1659 1659 count = 0
1660 1660 for n in o:
1661 1661 if count >= limit:
1662 1662 break
1663 1663 parents = [p for p in other.changelog.parents(n) if p != nullid]
1664 1664 if opts['no_merges'] and len(parents) == 2:
1665 1665 continue
1666 1666 count += 1
1667 1667 displayer.show(changenode=n)
1668 1668 finally:
1669 1669 if hasattr(other, 'close'):
1670 1670 other.close()
1671 1671 if cleanup:
1672 1672 os.unlink(cleanup)
1673 1673
1674 1674 def init(ui, dest=".", **opts):
1675 1675 """create a new repository in the given directory
1676 1676
1677 1677 Initialize a new repository in the given directory. If the given
1678 1678 directory does not exist, it is created.
1679 1679
1680 1680 If no directory is given, the current directory is used.
1681 1681
1682 1682 It is possible to specify an ssh:// URL as the destination.
1683 1683 Look at the help text for the pull command for important details
1684 1684 about ssh:// URLs.
1685 1685 """
1686 1686 cmdutil.setremoteconfig(ui, opts)
1687 1687 hg.repository(ui, dest, create=1)
1688 1688
1689 1689 def locate(ui, repo, *pats, **opts):
1690 1690 """locate files matching specific patterns
1691 1691
1692 1692 Print all files under Mercurial control whose names match the
1693 1693 given patterns.
1694 1694
1695 1695 This command searches the entire repository by default. To search
1696 1696 just the current directory and its subdirectories, use
1697 1697 "--include .".
1698 1698
1699 1699 If no patterns are given to match, this command prints all file
1700 1700 names.
1701 1701
1702 1702 If you want to feed the output of this command into the "xargs"
1703 1703 command, use the "-0" option to both this command and "xargs".
1704 1704 This will avoid the problem of "xargs" treating single filenames
1705 1705 that contain white space as multiple filenames.
1706 1706 """
1707 1707 end = opts['print0'] and '\0' or '\n'
1708 1708 rev = opts['rev']
1709 1709 if rev:
1710 1710 node = repo.lookup(rev)
1711 1711 else:
1712 1712 node = None
1713 1713
1714 1714 ret = 1
1715 1715 m = cmdutil.match(repo, pats, opts, default='relglob')
1716 1716 m.bad = lambda x,y: False
1717 1717 for abs in repo.walk(m, node):
1718 1718 if not node and abs not in repo.dirstate:
1719 1719 continue
1720 1720 if opts['fullpath']:
1721 1721 ui.write(os.path.join(repo.root, abs), end)
1722 1722 else:
1723 1723 ui.write(((pats and m.rel(abs)) or abs), end)
1724 1724 ret = 0
1725 1725
1726 1726 return ret
1727 1727
1728 1728 def log(ui, repo, *pats, **opts):
1729 1729 """show revision history of entire repository or files
1730 1730
1731 1731 Print the revision history of the specified files or the entire
1732 1732 project.
1733 1733
1734 1734 File history is shown without following rename or copy history of
1735 1735 files. Use -f/--follow with a file name to follow history across
1736 1736 renames and copies. --follow without a file name will only show
1737 1737 ancestors or descendants of the starting revision. --follow-first
1738 1738 only follows the first parent of merge revisions.
1739 1739
1740 1740 If no revision range is specified, the default is tip:0 unless
1741 1741 --follow is set, in which case the working directory parent is
1742 1742 used as the starting revision.
1743 1743
1744 1744 See 'hg help dates' for a list of formats valid for -d/--date.
1745 1745
1746 1746 By default this command outputs: changeset id and hash, tags,
1747 1747 non-trivial parents, user, date and time, and a summary for each
1748 1748 commit. When the -v/--verbose switch is used, the list of changed
1749 1749 files and full commit message is shown.
1750 1750
1751 1751 NOTE: log -p may generate unexpected diff output for merge
1752 1752 changesets, as it will compare the merge changeset against its
1753 1753 first parent only. Also, the files: list will only reflect files
1754 1754 that are different from BOTH parents.
1755 1755
1756 1756 """
1757 1757
1758 1758 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1759 1759 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1760 1760
1761 1761 limit = cmdutil.loglimit(opts)
1762 1762 count = 0
1763 1763
1764 1764 if opts['copies'] and opts['rev']:
1765 1765 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1766 1766 else:
1767 1767 endrev = repo.changelog.count()
1768 1768 rcache = {}
1769 1769 ncache = {}
1770 1770 def getrenamed(fn, rev):
1771 1771 '''looks up all renames for a file (up to endrev) the first
1772 1772 time the file is given. It indexes on the changerev and only
1773 1773 parses the manifest if linkrev != changerev.
1774 1774 Returns rename info for fn at changerev rev.'''
1775 1775 if fn not in rcache:
1776 1776 rcache[fn] = {}
1777 1777 ncache[fn] = {}
1778 1778 fl = repo.file(fn)
1779 1779 for i in xrange(fl.count()):
1780 1780 node = fl.node(i)
1781 1781 lr = fl.linkrev(node)
1782 1782 renamed = fl.renamed(node)
1783 1783 rcache[fn][lr] = renamed
1784 1784 if renamed:
1785 1785 ncache[fn][node] = renamed
1786 1786 if lr >= endrev:
1787 1787 break
1788 1788 if rev in rcache[fn]:
1789 1789 return rcache[fn][rev]
1790 1790
1791 1791 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1792 1792 # filectx logic.
1793 1793
1794 1794 try:
1795 1795 return repo.changectx(rev).filectx(fn).renamed()
1796 1796 except revlog.LookupError:
1797 1797 pass
1798 1798 return None
1799 1799
1800 1800 df = False
1801 1801 if opts["date"]:
1802 1802 df = util.matchdate(opts["date"])
1803 1803
1804 1804 only_branches = opts['only_branch']
1805 1805
1806 1806 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1807 1807 for st, rev, fns in changeiter:
1808 1808 if st == 'add':
1809 1809 changenode = repo.changelog.node(rev)
1810 1810 parents = [p for p in repo.changelog.parentrevs(rev)
1811 1811 if p != nullrev]
1812 1812 if opts['no_merges'] and len(parents) == 2:
1813 1813 continue
1814 1814 if opts['only_merges'] and len(parents) != 2:
1815 1815 continue
1816 1816
1817 1817 if only_branches:
1818 1818 revbranch = get(rev)[5]['branch']
1819 1819 if revbranch not in only_branches:
1820 1820 continue
1821 1821
1822 1822 if df:
1823 1823 changes = get(rev)
1824 1824 if not df(changes[2][0]):
1825 1825 continue
1826 1826
1827 1827 if opts['keyword']:
1828 1828 changes = get(rev)
1829 1829 miss = 0
1830 1830 for k in [kw.lower() for kw in opts['keyword']]:
1831 1831 if not (k in changes[1].lower() or
1832 1832 k in changes[4].lower() or
1833 1833 k in " ".join(changes[3]).lower()):
1834 1834 miss = 1
1835 1835 break
1836 1836 if miss:
1837 1837 continue
1838 1838
1839 1839 copies = []
1840 1840 if opts.get('copies') and rev:
1841 1841 for fn in get(rev)[3]:
1842 1842 rename = getrenamed(fn, rev)
1843 1843 if rename:
1844 1844 copies.append((fn, rename[0]))
1845 1845 displayer.show(rev, changenode, copies=copies)
1846 1846 elif st == 'iter':
1847 1847 if count == limit: break
1848 1848 if displayer.flush(rev):
1849 1849 count += 1
1850 1850
1851 1851 def manifest(ui, repo, node=None, rev=None):
1852 1852 """output the current or given revision of the project manifest
1853 1853
1854 1854 Print a list of version controlled files for the given revision.
1855 1855 If no revision is given, the parent of the working directory is used,
1856 1856 or tip if no revision is checked out.
1857 1857
1858 1858 The manifest is the list of files being version controlled. If no revision
1859 1859 is given then the first parent of the working directory is used.
1860 1860
1861 1861 With -v flag, print file permissions, symlink and executable bits. With
1862 1862 --debug flag, print file revision hashes.
1863 1863 """
1864 1864
1865 1865 if rev and node:
1866 1866 raise util.Abort(_("please specify just one revision"))
1867 1867
1868 1868 if not node:
1869 1869 node = rev
1870 1870
1871 1871 m = repo.changectx(node).manifest()
1872 1872 files = m.keys()
1873 1873 files.sort()
1874 1874
1875 1875 for f in files:
1876 1876 if ui.debugflag:
1877 1877 ui.write("%40s " % hex(m[f]))
1878 1878 if ui.verbose:
1879 1879 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1880 1880 perm = m.execf(f) and "755" or "644"
1881 1881 ui.write("%3s %1s " % (perm, type))
1882 1882 ui.write("%s\n" % f)
1883 1883
1884 1884 def merge(ui, repo, node=None, force=None, rev=None):
1885 1885 """merge working directory with another revision
1886 1886
1887 1887 Merge the contents of the current working directory and the
1888 1888 requested revision. Files that changed between either parent are
1889 1889 marked as changed for the next commit and a commit must be
1890 1890 performed before any further updates are allowed.
1891 1891
1892 1892 If no revision is specified, the working directory's parent is a
1893 1893 head revision, and the repository contains exactly one other head,
1894 1894 the other head is merged with by default. Otherwise, an explicit
1895 1895 revision to merge with must be provided.
1896 1896 """
1897 1897
1898 1898 if rev and node:
1899 1899 raise util.Abort(_("please specify just one revision"))
1900 1900 if not node:
1901 1901 node = rev
1902 1902
1903 1903 if not node:
1904 1904 heads = repo.heads()
1905 1905 if len(heads) > 2:
1906 1906 raise util.Abort(_('repo has %d heads - '
1907 1907 'please merge with an explicit rev') %
1908 1908 len(heads))
1909 1909 parent = repo.dirstate.parents()[0]
1910 1910 if len(heads) == 1:
1911 1911 msg = _('there is nothing to merge')
1912 1912 if parent != repo.lookup(repo.workingctx().branch()):
1913 1913 msg = _('%s - use "hg update" instead') % msg
1914 1914 raise util.Abort(msg)
1915 1915
1916 1916 if parent not in heads:
1917 1917 raise util.Abort(_('working dir not at a head rev - '
1918 1918 'use "hg update" or merge with an explicit rev'))
1919 1919 node = parent == heads[0] and heads[-1] or heads[0]
1920 1920 return hg.merge(repo, node, force=force)
1921 1921
1922 1922 def outgoing(ui, repo, dest=None, **opts):
1923 1923 """show changesets not found in destination
1924 1924
1925 1925 Show changesets not found in the specified destination repository or
1926 1926 the default push location. These are the changesets that would be pushed
1927 1927 if a push was requested.
1928 1928
1929 1929 See pull for valid destination format details.
1930 1930 """
1931 1931 limit = cmdutil.loglimit(opts)
1932 1932 dest, revs, checkout = hg.parseurl(
1933 1933 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1934 1934 cmdutil.setremoteconfig(ui, opts)
1935 1935 if revs:
1936 1936 revs = [repo.lookup(rev) for rev in revs]
1937 1937
1938 1938 other = hg.repository(ui, dest)
1939 1939 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1940 1940 o = repo.findoutgoing(other, force=opts['force'])
1941 1941 if not o:
1942 1942 ui.status(_("no changes found\n"))
1943 1943 return 1
1944 1944 o = repo.changelog.nodesbetween(o, revs)[0]
1945 1945 if opts['newest_first']:
1946 1946 o.reverse()
1947 1947 displayer = cmdutil.show_changeset(ui, repo, opts)
1948 1948 count = 0
1949 1949 for n in o:
1950 1950 if count >= limit:
1951 1951 break
1952 1952 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1953 1953 if opts['no_merges'] and len(parents) == 2:
1954 1954 continue
1955 1955 count += 1
1956 1956 displayer.show(changenode=n)
1957 1957
1958 1958 def parents(ui, repo, file_=None, **opts):
1959 1959 """show the parents of the working dir or revision
1960 1960
1961 1961 Print the working directory's parent revisions. If a
1962 1962 revision is given via --rev, the parent of that revision
1963 1963 will be printed. If a file argument is given, revision in
1964 1964 which the file was last changed (before the working directory
1965 1965 revision or the argument to --rev if given) is printed.
1966 1966 """
1967 1967 rev = opts.get('rev')
1968 1968 if rev:
1969 1969 ctx = repo.changectx(rev)
1970 1970 else:
1971 1971 ctx = repo.workingctx()
1972 1972
1973 1973 if file_:
1974 1974 m = cmdutil.match(repo, (file_,), opts)
1975 1975 if m.anypats() or len(m.files()) != 1:
1976 1976 raise util.Abort(_('can only specify an explicit file name'))
1977 1977 file_ = m.files()[0]
1978 1978 filenodes = []
1979 1979 for cp in ctx.parents():
1980 1980 if not cp:
1981 1981 continue
1982 1982 try:
1983 1983 filenodes.append(cp.filenode(file_))
1984 1984 except revlog.LookupError:
1985 1985 pass
1986 1986 if not filenodes:
1987 1987 raise util.Abort(_("'%s' not found in manifest!") % file_)
1988 1988 fl = repo.file(file_)
1989 1989 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1990 1990 else:
1991 1991 p = [cp.node() for cp in ctx.parents()]
1992 1992
1993 1993 displayer = cmdutil.show_changeset(ui, repo, opts)
1994 1994 for n in p:
1995 1995 if n != nullid:
1996 1996 displayer.show(changenode=n)
1997 1997
1998 1998 def paths(ui, repo, search=None):
1999 1999 """show definition of symbolic path names
2000 2000
2001 2001 Show definition of symbolic path name NAME. If no name is given, show
2002 2002 definition of available names.
2003 2003
2004 2004 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2005 2005 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2006 2006 """
2007 2007 if search:
2008 2008 for name, path in ui.configitems("paths"):
2009 2009 if name == search:
2010 2010 ui.write("%s\n" % util.hidepassword(path))
2011 2011 return
2012 2012 ui.warn(_("not found!\n"))
2013 2013 return 1
2014 2014 else:
2015 2015 for name, path in ui.configitems("paths"):
2016 2016 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2017 2017
2018 2018 def postincoming(ui, repo, modheads, optupdate, checkout):
2019 2019 if modheads == 0:
2020 2020 return
2021 2021 if optupdate:
2022 2022 if modheads <= 1 or checkout:
2023 2023 return hg.update(repo, checkout)
2024 2024 else:
2025 2025 ui.status(_("not updating, since new heads added\n"))
2026 2026 if modheads > 1:
2027 2027 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2028 2028 else:
2029 2029 ui.status(_("(run 'hg update' to get a working copy)\n"))
2030 2030
2031 2031 def pull(ui, repo, source="default", **opts):
2032 2032 """pull changes from the specified source
2033 2033
2034 2034 Pull changes from a remote repository to a local one.
2035 2035
2036 2036 This finds all changes from the repository at the specified path
2037 2037 or URL and adds them to the local repository. By default, this
2038 2038 does not update the copy of the project in the working directory.
2039 2039
2040 2040 Valid URLs are of the form:
2041 2041
2042 2042 local/filesystem/path (or file://local/filesystem/path)
2043 2043 http://[user@]host[:port]/[path]
2044 2044 https://[user@]host[:port]/[path]
2045 2045 ssh://[user@]host[:port]/[path]
2046 2046 static-http://host[:port]/[path]
2047 2047
2048 2048 Paths in the local filesystem can either point to Mercurial
2049 2049 repositories or to bundle files (as created by 'hg bundle' or
2050 2050 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2051 2051 allows access to a Mercurial repository where you simply use a web
2052 2052 server to publish the .hg directory as static content.
2053 2053
2054 2054 An optional identifier after # indicates a particular branch, tag,
2055 2055 or changeset to pull.
2056 2056
2057 2057 Some notes about using SSH with Mercurial:
2058 2058 - SSH requires an accessible shell account on the destination machine
2059 2059 and a copy of hg in the remote path or specified with as remotecmd.
2060 2060 - path is relative to the remote user's home directory by default.
2061 2061 Use an extra slash at the start of a path to specify an absolute path:
2062 2062 ssh://example.com//tmp/repository
2063 2063 - Mercurial doesn't use its own compression via SSH; the right thing
2064 2064 to do is to configure it in your ~/.ssh/config, e.g.:
2065 2065 Host *.mylocalnetwork.example.com
2066 2066 Compression no
2067 2067 Host *
2068 2068 Compression yes
2069 2069 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2070 2070 with the --ssh command line option.
2071 2071 """
2072 2072 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2073 2073 cmdutil.setremoteconfig(ui, opts)
2074 2074
2075 2075 other = hg.repository(ui, source)
2076 2076 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2077 2077 if revs:
2078 2078 try:
2079 2079 revs = [other.lookup(rev) for rev in revs]
2080 2080 except NoCapability:
2081 2081 error = _("Other repository doesn't support revision lookup, "
2082 2082 "so a rev cannot be specified.")
2083 2083 raise util.Abort(error)
2084 2084
2085 2085 modheads = repo.pull(other, heads=revs, force=opts['force'])
2086 2086 return postincoming(ui, repo, modheads, opts['update'], checkout)
2087 2087
2088 2088 def push(ui, repo, dest=None, **opts):
2089 2089 """push changes to the specified destination
2090 2090
2091 2091 Push changes from the local repository to the given destination.
2092 2092
2093 2093 This is the symmetrical operation for pull. It helps to move
2094 2094 changes from the current repository to a different one. If the
2095 2095 destination is local this is identical to a pull in that directory
2096 2096 from the current one.
2097 2097
2098 2098 By default, push will refuse to run if it detects the result would
2099 2099 increase the number of remote heads. This generally indicates the
2100 2100 the client has forgotten to pull and merge before pushing.
2101 2101
2102 2102 Valid URLs are of the form:
2103 2103
2104 2104 local/filesystem/path (or file://local/filesystem/path)
2105 2105 ssh://[user@]host[:port]/[path]
2106 2106 http://[user@]host[:port]/[path]
2107 2107 https://[user@]host[:port]/[path]
2108 2108
2109 2109 An optional identifier after # indicates a particular branch, tag,
2110 2110 or changeset to push. If -r is used, the named changeset and all its
2111 2111 ancestors will be pushed to the remote repository.
2112 2112
2113 2113 Look at the help text for the pull command for important details
2114 2114 about ssh:// URLs.
2115 2115
2116 2116 Pushing to http:// and https:// URLs is only possible, if this
2117 2117 feature is explicitly enabled on the remote Mercurial server.
2118 2118 """
2119 2119 dest, revs, checkout = hg.parseurl(
2120 2120 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2121 2121 cmdutil.setremoteconfig(ui, opts)
2122 2122
2123 2123 other = hg.repository(ui, dest)
2124 2124 ui.status('pushing to %s\n' % util.hidepassword(dest))
2125 2125 if revs:
2126 2126 revs = [repo.lookup(rev) for rev in revs]
2127 2127 r = repo.push(other, opts['force'], revs=revs)
2128 2128 return r == 0
2129 2129
2130 2130 def rawcommit(ui, repo, *pats, **opts):
2131 2131 """raw commit interface (DEPRECATED)
2132 2132
2133 2133 (DEPRECATED)
2134 2134 Lowlevel commit, for use in helper scripts.
2135 2135
2136 2136 This command is not intended to be used by normal users, as it is
2137 2137 primarily useful for importing from other SCMs.
2138 2138
2139 2139 This command is now deprecated and will be removed in a future
2140 2140 release, please use debugsetparents and commit instead.
2141 2141 """
2142 2142
2143 2143 ui.warn(_("(the rawcommit command is deprecated)\n"))
2144 2144
2145 2145 message = cmdutil.logmessage(opts)
2146 2146
2147 2147 files = cmdutil.match(repo, pats, opts).files()
2148 2148 if opts['files']:
2149 2149 files += open(opts['files']).read().splitlines()
2150 2150
2151 2151 parents = [repo.lookup(p) for p in opts['parent']]
2152 2152
2153 2153 try:
2154 2154 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2155 2155 except ValueError, inst:
2156 2156 raise util.Abort(str(inst))
2157 2157
2158 2158 def recover(ui, repo):
2159 2159 """roll back an interrupted transaction
2160 2160
2161 2161 Recover from an interrupted commit or pull.
2162 2162
2163 2163 This command tries to fix the repository status after an interrupted
2164 2164 operation. It should only be necessary when Mercurial suggests it.
2165 2165 """
2166 2166 if repo.recover():
2167 2167 return hg.verify(repo)
2168 2168 return 1
2169 2169
2170 2170 def remove(ui, repo, *pats, **opts):
2171 2171 """remove the specified files on the next commit
2172 2172
2173 2173 Schedule the indicated files for removal from the repository.
2174 2174
2175 2175 This only removes files from the current branch, not from the entire
2176 2176 project history. -A can be used to remove only files that have already
2177 2177 been deleted, -f can be used to force deletion, and -Af can be used
2178 2178 to remove files from the next revision without deleting them.
2179 2179
2180 2180 The following table details the behavior of remove for different file
2181 2181 states (columns) and option combinations (rows). The file states are
2182 2182 Added, Clean, Modified and Missing (as reported by hg status). The
2183 2183 actions are Warn, Remove (from branch) and Delete (from disk).
2184 2184
2185 2185 A C M !
2186 2186 none W RD W R
2187 2187 -f R RD RD R
2188 2188 -A W W W R
2189 2189 -Af R R R R
2190 2190
2191 2191 This command schedules the files to be removed at the next commit.
2192 2192 To undo a remove before that, see hg revert.
2193 2193 """
2194 2194
2195 2195 after, force = opts.get('after'), opts.get('force')
2196 2196 if not pats and not after:
2197 2197 raise util.Abort(_('no files specified'))
2198 2198
2199 2199 m = cmdutil.match(repo, pats, opts)
2200 2200 mardu = map(dict.fromkeys, repo.status(match=m))[:5]
2201 2201 modified, added, removed, deleted, unknown = mardu
2202 2202
2203 2203 remove, forget = [], []
2204 2204 for abs in repo.walk(m):
2205 2205
2206 2206 reason = None
2207 2207 if abs in removed or abs in unknown:
2208 2208 continue
2209 2209
2210 2210 # last column
2211 2211 elif abs in deleted:
2212 2212 remove.append(abs)
2213 2213
2214 2214 # rest of the third row
2215 2215 elif after and not force:
2216 2216 reason = _('still exists (use -f to force removal)')
2217 2217
2218 2218 # rest of the first column
2219 2219 elif abs in added:
2220 2220 if not force:
2221 2221 reason = _('has been marked for add (use -f to force removal)')
2222 2222 else:
2223 2223 forget.append(abs)
2224 2224
2225 2225 # rest of the third column
2226 2226 elif abs in modified:
2227 2227 if not force:
2228 2228 reason = _('is modified (use -f to force removal)')
2229 2229 else:
2230 2230 remove.append(abs)
2231 2231
2232 2232 # rest of the second column
2233 2233 elif not reason:
2234 2234 remove.append(abs)
2235 2235
2236 2236 if reason:
2237 2237 ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
2238 2238 elif ui.verbose or not m.exact(abs):
2239 2239 ui.status(_('removing %s\n') % m.rel(abs))
2240 2240
2241 2241 repo.forget(forget)
2242 2242 repo.remove(remove, unlink=not after)
2243 2243
2244 2244 def rename(ui, repo, *pats, **opts):
2245 2245 """rename files; equivalent of copy + remove
2246 2246
2247 2247 Mark dest as copies of sources; mark sources for deletion. If
2248 2248 dest is a directory, copies are put in that directory. If dest is
2249 2249 a file, there can only be one source.
2250 2250
2251 2251 By default, this command copies the contents of files as they
2252 2252 stand in the working directory. If invoked with --after, the
2253 2253 operation is recorded, but no copying is performed.
2254 2254
2255 2255 This command takes effect in the next commit. To undo a rename
2256 2256 before that, see hg revert.
2257 2257 """
2258 2258 wlock = repo.wlock(False)
2259 2259 try:
2260 2260 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2261 2261 finally:
2262 2262 del wlock
2263 2263
2264 2264 def resolve(ui, repo, *pats, **opts):
2265 2265 """resolve file merges from a branch merge or update
2266 2266
2267 2267 This command will attempt to resolve unresolved merges from the
2268 2268 last update or merge command. This will use the local file
2269 2269 revision preserved at the last update or merge to cleanly retry
2270 2270 the file merge attempt. With no file or options specified, this
2271 2271 command will attempt to resolve all unresolved files.
2272 2272
2273 2273 The codes used to show the status of files are:
2274 2274 U = unresolved
2275 2275 R = resolved
2276 2276 """
2277 2277
2278 2278 if len([x for x in opts if opts[x]]) > 1:
2279 2279 raise util.Abort(_("too many options specified"))
2280 2280
2281 2281 ms = merge_.mergestate(repo)
2282 2282 m = cmdutil.match(repo, pats, opts)
2283 2283
2284 2284 for f in ms:
2285 2285 if m(f):
2286 2286 if opts.get("list"):
2287 2287 ui.write("%s %s\n" % (ms[f].upper(), f))
2288 2288 elif opts.get("mark"):
2289 2289 ms.mark(f, "r")
2290 2290 elif opts.get("unmark"):
2291 2291 ms.mark(f, "u")
2292 2292 else:
2293 2293 wctx = repo.workingctx()
2294 2294 mctx = wctx.parents()[-1]
2295 2295 ms.resolve(f, wctx, mctx)
2296 2296
2297 2297 def revert(ui, repo, *pats, **opts):
2298 2298 """restore individual files or dirs to an earlier state
2299 2299
2300 2300 (use update -r to check out earlier revisions, revert does not
2301 2301 change the working dir parents)
2302 2302
2303 2303 With no revision specified, revert the named files or directories
2304 2304 to the contents they had in the parent of the working directory.
2305 2305 This restores the contents of the affected files to an unmodified
2306 2306 state and unschedules adds, removes, copies, and renames. If the
2307 2307 working directory has two parents, you must explicitly specify the
2308 2308 revision to revert to.
2309 2309
2310 2310 Using the -r option, revert the given files or directories to their
2311 2311 contents as of a specific revision. This can be helpful to "roll
2312 2312 back" some or all of an earlier change.
2313 2313 See 'hg help dates' for a list of formats valid for -d/--date.
2314 2314
2315 2315 Revert modifies the working directory. It does not commit any
2316 2316 changes, or change the parent of the working directory. If you
2317 2317 revert to a revision other than the parent of the working
2318 2318 directory, the reverted files will thus appear modified
2319 2319 afterwards.
2320 2320
2321 2321 If a file has been deleted, it is restored. If the executable
2322 2322 mode of a file was changed, it is reset.
2323 2323
2324 2324 If names are given, all files matching the names are reverted.
2325 2325 If no arguments are given, no files are reverted.
2326 2326
2327 2327 Modified files are saved with a .orig suffix before reverting.
2328 2328 To disable these backups, use --no-backup.
2329 2329 """
2330 2330
2331 2331 if opts["date"]:
2332 2332 if opts["rev"]:
2333 2333 raise util.Abort(_("you can't specify a revision and a date"))
2334 2334 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2335 2335
2336 2336 if not pats and not opts['all']:
2337 2337 raise util.Abort(_('no files or directories specified; '
2338 2338 'use --all to revert the whole repo'))
2339 2339
2340 2340 parent, p2 = repo.dirstate.parents()
2341 2341 if not opts['rev'] and p2 != nullid:
2342 2342 raise util.Abort(_('uncommitted merge - please provide a '
2343 2343 'specific revision'))
2344 2344 ctx = repo.changectx(opts['rev'])
2345 2345 node = ctx.node()
2346 2346 mf = ctx.manifest()
2347 2347 if node == parent:
2348 2348 pmf = mf
2349 2349 else:
2350 2350 pmf = None
2351 2351
2352 2352 # need all matching names in dirstate and manifest of target rev,
2353 2353 # so have to walk both. do not print errors if files exist in one
2354 2354 # but not other.
2355 2355
2356 2356 names = {}
2357 2357
2358 2358 wlock = repo.wlock()
2359 2359 try:
2360 2360 # walk dirstate.
2361 2361 files = []
2362 2362
2363 2363 m = cmdutil.match(repo, pats, opts)
2364 2364 m.bad = lambda x,y: False
2365 2365 for abs in repo.walk(m):
2366 2366 names[abs] = m.rel(abs), m.exact(abs)
2367 2367
2368 2368 # walk target manifest.
2369 2369
2370 2370 def badfn(path, msg):
2371 2371 if path in names:
2372 2372 return False
2373 2373 path_ = path + '/'
2374 2374 for f in names:
2375 2375 if f.startswith(path_):
2376 2376 return False
2377 2377 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2378 2378 return False
2379 2379
2380 2380 m = cmdutil.match(repo, pats, opts)
2381 2381 m.bad = badfn
2382 2382 for abs in repo.walk(m, node=node):
2383 2383 if abs not in names:
2384 2384 names[abs] = m.rel(abs), m.exact(abs)
2385 2385
2386 2386 m = cmdutil.matchfiles(repo, names)
2387 2387 changes = repo.status(match=m)[:4]
2388 2388 modified, added, removed, deleted = map(dict.fromkeys, changes)
2389 2389
2390 2390 # if f is a rename, also revert the source
2391 2391 cwd = repo.getcwd()
2392 2392 for f in added:
2393 2393 src = repo.dirstate.copied(f)
2394 2394 if src and src not in names and repo.dirstate[src] == 'r':
2395 2395 removed[src] = None
2396 2396 names[src] = (repo.pathto(src, cwd), True)
2397 2397
2398 2398 def removeforget(abs):
2399 2399 if repo.dirstate[abs] == 'a':
2400 2400 return _('forgetting %s\n')
2401 2401 return _('removing %s\n')
2402 2402
2403 2403 revert = ([], _('reverting %s\n'))
2404 2404 add = ([], _('adding %s\n'))
2405 2405 remove = ([], removeforget)
2406 2406 undelete = ([], _('undeleting %s\n'))
2407 2407
2408 2408 disptable = (
2409 2409 # dispatch table:
2410 2410 # file state
2411 2411 # action if in target manifest
2412 2412 # action if not in target manifest
2413 2413 # make backup if in target manifest
2414 2414 # make backup if not in target manifest
2415 2415 (modified, revert, remove, True, True),
2416 2416 (added, revert, remove, True, False),
2417 2417 (removed, undelete, None, False, False),
2418 2418 (deleted, revert, remove, False, False),
2419 2419 )
2420 2420
2421 2421 entries = names.items()
2422 2422 entries.sort()
2423 2423
2424 2424 for abs, (rel, exact) in entries:
2425 2425 mfentry = mf.get(abs)
2426 2426 target = repo.wjoin(abs)
2427 2427 def handle(xlist, dobackup):
2428 2428 xlist[0].append(abs)
2429 2429 if dobackup and not opts['no_backup'] and util.lexists(target):
2430 2430 bakname = "%s.orig" % rel
2431 2431 ui.note(_('saving current version of %s as %s\n') %
2432 2432 (rel, bakname))
2433 2433 if not opts.get('dry_run'):
2434 2434 util.copyfile(target, bakname)
2435 2435 if ui.verbose or not exact:
2436 2436 msg = xlist[1]
2437 2437 if not isinstance(msg, basestring):
2438 2438 msg = msg(abs)
2439 2439 ui.status(msg % rel)
2440 2440 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2441 2441 if abs not in table: continue
2442 2442 # file has changed in dirstate
2443 2443 if mfentry:
2444 2444 handle(hitlist, backuphit)
2445 2445 elif misslist is not None:
2446 2446 handle(misslist, backupmiss)
2447 2447 break
2448 2448 else:
2449 2449 if abs not in repo.dirstate:
2450 2450 if mfentry:
2451 2451 handle(add, True)
2452 2452 elif exact:
2453 2453 ui.warn(_('file not managed: %s\n') % rel)
2454 2454 continue
2455 2455 # file has not changed in dirstate
2456 2456 if node == parent:
2457 2457 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2458 2458 continue
2459 2459 if pmf is None:
2460 2460 # only need parent manifest in this unlikely case,
2461 2461 # so do not read by default
2462 2462 pmf = repo.changectx(parent).manifest()
2463 2463 if abs in pmf:
2464 2464 if mfentry:
2465 2465 # if version of file is same in parent and target
2466 2466 # manifests, do nothing
2467 2467 if (pmf[abs] != mfentry or
2468 2468 pmf.flags(abs) != mf.flags(abs)):
2469 2469 handle(revert, False)
2470 2470 else:
2471 2471 handle(remove, False)
2472 2472
2473 2473 if not opts.get('dry_run'):
2474 2474 def checkout(f):
2475 2475 fc = ctx[f]
2476 2476 repo.wwrite(f, fc.data(), fc.fileflags())
2477 2477
2478 2478 audit_path = util.path_auditor(repo.root)
2479 2479 for f in remove[0]:
2480 2480 if repo.dirstate[f] == 'a':
2481 2481 repo.dirstate.forget(f)
2482 2482 continue
2483 2483 audit_path(f)
2484 2484 try:
2485 2485 util.unlink(repo.wjoin(f))
2486 2486 except OSError:
2487 2487 pass
2488 2488 repo.dirstate.remove(f)
2489 2489
2490 2490 normal = None
2491 2491 if node == parent:
2492 2492 # We're reverting to our parent. If possible, we'd like status
2493 2493 # to report the file as clean. We have to use normallookup for
2494 2494 # merges to avoid losing information about merged/dirty files.
2495 2495 if p2 != nullid:
2496 2496 normal = repo.dirstate.normallookup
2497 2497 else:
2498 2498 normal = repo.dirstate.normal
2499 2499 for f in revert[0]:
2500 2500 checkout(f)
2501 2501 if normal:
2502 2502 normal(f)
2503 2503
2504 2504 for f in add[0]:
2505 2505 checkout(f)
2506 2506 repo.dirstate.add(f)
2507 2507
2508 2508 normal = repo.dirstate.normallookup
2509 2509 if node == parent and p2 == nullid:
2510 2510 normal = repo.dirstate.normal
2511 2511 for f in undelete[0]:
2512 2512 checkout(f)
2513 2513 normal(f)
2514 2514
2515 2515 finally:
2516 2516 del wlock
2517 2517
2518 2518 def rollback(ui, repo):
2519 2519 """roll back the last transaction
2520 2520
2521 2521 This command should be used with care. There is only one level of
2522 2522 rollback, and there is no way to undo a rollback. It will also
2523 2523 restore the dirstate at the time of the last transaction, losing
2524 2524 any dirstate changes since that time.
2525 2525
2526 2526 Transactions are used to encapsulate the effects of all commands
2527 2527 that create new changesets or propagate existing changesets into a
2528 2528 repository. For example, the following commands are transactional,
2529 2529 and their effects can be rolled back:
2530 2530
2531 2531 commit
2532 2532 import
2533 2533 pull
2534 2534 push (with this repository as destination)
2535 2535 unbundle
2536 2536
2537 2537 This command is not intended for use on public repositories. Once
2538 2538 changes are visible for pull by other users, rolling a transaction
2539 2539 back locally is ineffective (someone else may already have pulled
2540 2540 the changes). Furthermore, a race is possible with readers of the
2541 2541 repository; for example an in-progress pull from the repository
2542 2542 may fail if a rollback is performed.
2543 2543 """
2544 2544 repo.rollback()
2545 2545
2546 2546 def root(ui, repo):
2547 2547 """print the root (top) of the current working dir
2548 2548
2549 2549 Print the root directory of the current repository.
2550 2550 """
2551 2551 ui.write(repo.root + "\n")
2552 2552
2553 2553 def serve(ui, repo, **opts):
2554 2554 """export the repository via HTTP
2555 2555
2556 2556 Start a local HTTP repository browser and pull server.
2557 2557
2558 2558 By default, the server logs accesses to stdout and errors to
2559 2559 stderr. Use the "-A" and "-E" options to log to files.
2560 2560 """
2561 2561
2562 2562 if opts["stdio"]:
2563 2563 if repo is None:
2564 2564 raise RepoError(_("There is no Mercurial repository here"
2565 2565 " (.hg not found)"))
2566 2566 s = sshserver.sshserver(ui, repo)
2567 2567 s.serve_forever()
2568 2568
2569 2569 parentui = ui.parentui or ui
2570 2570 optlist = ("name templates style address port prefix ipv6"
2571 2571 " accesslog errorlog webdir_conf certificate")
2572 2572 for o in optlist.split():
2573 2573 if opts[o]:
2574 2574 parentui.setconfig("web", o, str(opts[o]))
2575 2575 if (repo is not None) and (repo.ui != parentui):
2576 2576 repo.ui.setconfig("web", o, str(opts[o]))
2577 2577
2578 2578 if repo is None and not ui.config("web", "webdir_conf"):
2579 2579 raise RepoError(_("There is no Mercurial repository here"
2580 2580 " (.hg not found)"))
2581 2581
2582 2582 class service:
2583 2583 def init(self):
2584 2584 util.set_signal_handler()
2585 2585 self.httpd = hgweb.server.create_server(parentui, repo)
2586 2586
2587 2587 if not ui.verbose: return
2588 2588
2589 2589 if self.httpd.prefix:
2590 2590 prefix = self.httpd.prefix.strip('/') + '/'
2591 2591 else:
2592 2592 prefix = ''
2593 2593
2594 2594 port = ':%d' % self.httpd.port
2595 2595 if port == ':80':
2596 2596 port = ''
2597 2597
2598 2598 bindaddr = self.httpd.addr
2599 2599 if bindaddr == '0.0.0.0':
2600 2600 bindaddr = '*'
2601 2601 elif ':' in bindaddr: # IPv6
2602 2602 bindaddr = '[%s]' % bindaddr
2603 2603
2604 2604 fqaddr = self.httpd.fqaddr
2605 2605 if ':' in fqaddr:
2606 2606 fqaddr = '[%s]' % fqaddr
2607 2607 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2608 2608 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2609 2609
2610 2610 def run(self):
2611 2611 self.httpd.serve_forever()
2612 2612
2613 2613 service = service()
2614 2614
2615 2615 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2616 2616
2617 2617 def status(ui, repo, *pats, **opts):
2618 2618 """show changed files in the working directory
2619 2619
2620 2620 Show status of files in the repository. If names are given, only
2621 2621 files that match are shown. Files that are clean or ignored or
2622 2622 source of a copy/move operation, are not listed unless -c (clean),
2623 2623 -i (ignored), -C (copies) or -A is given. Unless options described
2624 2624 with "show only ..." are given, the options -mardu are used.
2625 2625
2626 2626 Option -q/--quiet hides untracked (unknown and ignored) files
2627 2627 unless explicitly requested with -u/--unknown or -i/-ignored.
2628 2628
2629 2629 NOTE: status may appear to disagree with diff if permissions have
2630 2630 changed or a merge has occurred. The standard diff format does not
2631 2631 report permission changes and diff only reports changes relative
2632 2632 to one merge parent.
2633 2633
2634 2634 If one revision is given, it is used as the base revision.
2635 2635 If two revisions are given, the difference between them is shown.
2636 2636
2637 2637 The codes used to show the status of files are:
2638 2638 M = modified
2639 2639 A = added
2640 2640 R = removed
2641 2641 C = clean
2642 2642 ! = deleted, but still tracked
2643 2643 ? = not tracked
2644 2644 I = ignored
2645 2645 = the previous added file was copied from here
2646 2646 """
2647 2647
2648 2648 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2649 2649 cwd = (pats and repo.getcwd()) or ''
2650 2650 end = opts['print0'] and '\0' or '\n'
2651 2651 copy = {}
2652 2652 states = 'modified added removed deleted unknown ignored clean'.split()
2653 2653 show = [k for k in states if opts[k]]
2654 2654 if opts['all']:
2655 2655 show += ui.quiet and (states[:4] + ['clean']) or states
2656 2656 if not show:
2657 2657 show = ui.quiet and states[:4] or states[:5]
2658 2658
2659 2659 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2660 2660 'ignored' in show, 'clean' in show, 'unknown' in show)
2661 2661 changestates = zip(states, 'MAR!?IC', stat)
2662 2662
2663 2663 if (opts['all'] or opts['copies']) and not opts['no_status']:
2664 2664 ctxn = repo.changectx(nullid)
2665 2665 ctx1 = repo.changectx(node1)
2666 2666 ctx2 = repo.changectx(node2)
2667 2667 added = stat[1]
2668 2668 if node2 is None:
2669 2669 added = stat[0] + stat[1] # merged?
2670 2670 ctx2 = repo.workingctx()
2671 2671 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].items():
2672 2672 if k in added:
2673 2673 copy[k] = v
2674 2674 elif v in added:
2675 2675 copy[v] = k
2676 2676
2677 2677 for state, char, files in changestates:
2678 2678 if state in show:
2679 2679 format = "%s %%s%s" % (char, end)
2680 2680 if opts['no_status']:
2681 2681 format = "%%s%s" % end
2682 2682
2683 2683 for f in files:
2684 2684 ui.write(format % repo.pathto(f, cwd))
2685 2685 if f in copy:
2686 2686 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2687 2687
2688 2688 def tag(ui, repo, name1, *names, **opts):
2689 2689 """add one or more tags for the current or given revision
2690 2690
2691 2691 Name a particular revision using <name>.
2692 2692
2693 2693 Tags are used to name particular revisions of the repository and are
2694 2694 very useful to compare different revisions, to go back to significant
2695 2695 earlier versions or to mark branch points as releases, etc.
2696 2696
2697 2697 If no revision is given, the parent of the working directory is used,
2698 2698 or tip if no revision is checked out.
2699 2699
2700 2700 To facilitate version control, distribution, and merging of tags,
2701 2701 they are stored as a file named ".hgtags" which is managed
2702 2702 similarly to other project files and can be hand-edited if
2703 2703 necessary. The file '.hg/localtags' is used for local tags (not
2704 2704 shared among repositories).
2705 2705
2706 2706 See 'hg help dates' for a list of formats valid for -d/--date.
2707 2707 """
2708 2708
2709 2709 rev_ = None
2710 2710 names = (name1,) + names
2711 2711 if len(names) != len(dict.fromkeys(names)):
2712 2712 raise util.Abort(_('tag names must be unique'))
2713 2713 for n in names:
2714 2714 if n in ['tip', '.', 'null']:
2715 2715 raise util.Abort(_('the name \'%s\' is reserved') % n)
2716 2716 if opts['rev'] and opts['remove']:
2717 2717 raise util.Abort(_("--rev and --remove are incompatible"))
2718 2718 if opts['rev']:
2719 2719 rev_ = opts['rev']
2720 2720 message = opts['message']
2721 2721 if opts['remove']:
2722 2722 expectedtype = opts['local'] and 'local' or 'global'
2723 2723 for n in names:
2724 2724 if not repo.tagtype(n):
2725 2725 raise util.Abort(_('tag \'%s\' does not exist') % n)
2726 2726 if repo.tagtype(n) != expectedtype:
2727 2727 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2728 2728 (n, expectedtype))
2729 2729 rev_ = nullid
2730 2730 if not message:
2731 2731 message = _('Removed tag %s') % ', '.join(names)
2732 2732 elif not opts['force']:
2733 2733 for n in names:
2734 2734 if n in repo.tags():
2735 2735 raise util.Abort(_('tag \'%s\' already exists '
2736 2736 '(use -f to force)') % n)
2737 2737 if not rev_ and repo.dirstate.parents()[1] != nullid:
2738 2738 raise util.Abort(_('uncommitted merge - please provide a '
2739 2739 'specific revision'))
2740 2740 r = repo.changectx(rev_).node()
2741 2741
2742 2742 if not message:
2743 2743 message = (_('Added tag %s for changeset %s') %
2744 2744 (', '.join(names), short(r)))
2745 2745
2746 2746 date = opts.get('date')
2747 2747 if date:
2748 2748 date = util.parsedate(date)
2749 2749
2750 2750 repo.tag(names, r, message, opts['local'], opts['user'], date)
2751 2751
2752 2752 def tags(ui, repo):
2753 2753 """list repository tags
2754 2754
2755 2755 List the repository tags.
2756 2756
2757 2757 This lists both regular and local tags. When the -v/--verbose switch
2758 2758 is used, a third column "local" is printed for local tags.
2759 2759 """
2760 2760
2761 2761 l = repo.tagslist()
2762 2762 l.reverse()
2763 2763 hexfunc = ui.debugflag and hex or short
2764 2764 tagtype = ""
2765 2765
2766 2766 for t, n in l:
2767 2767 if ui.quiet:
2768 2768 ui.write("%s\n" % t)
2769 2769 continue
2770 2770
2771 2771 try:
2772 2772 hn = hexfunc(n)
2773 2773 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2774 2774 except revlog.LookupError:
2775 2775 r = " ?:%s" % hn
2776 2776 else:
2777 2777 spaces = " " * (30 - util.locallen(t))
2778 2778 if ui.verbose:
2779 2779 if repo.tagtype(t) == 'local':
2780 2780 tagtype = " local"
2781 2781 else:
2782 2782 tagtype = ""
2783 2783 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2784 2784
2785 2785 def tip(ui, repo, **opts):
2786 2786 """show the tip revision
2787 2787
2788 2788 The tip revision (usually just called the tip) is the most
2789 2789 recently added changeset in the repository, the most recently
2790 2790 changed head.
2791 2791
2792 2792 If you have just made a commit, that commit will be the tip. If
2793 2793 you have just pulled changes from another repository, the tip of
2794 2794 that repository becomes the current tip. The "tip" tag is special
2795 2795 and cannot be renamed or assigned to a different changeset.
2796 2796 """
2797 2797 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2798 2798
2799 2799 def unbundle(ui, repo, fname1, *fnames, **opts):
2800 2800 """apply one or more changegroup files
2801 2801
2802 2802 Apply one or more compressed changegroup files generated by the
2803 2803 bundle command.
2804 2804 """
2805 2805 fnames = (fname1,) + fnames
2806 2806
2807 2807 lock = None
2808 2808 try:
2809 2809 lock = repo.lock()
2810 2810 for fname in fnames:
2811 2811 if os.path.exists(fname):
2812 2812 f = open(fname, "rb")
2813 2813 else:
2814 2814 f = urllib.urlopen(fname)
2815 2815 gen = changegroup.readbundle(f, fname)
2816 2816 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2817 2817 finally:
2818 2818 del lock
2819 2819
2820 2820 return postincoming(ui, repo, modheads, opts['update'], None)
2821 2821
2822 2822 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2823 2823 """update working directory
2824 2824
2825 2825 Update the working directory to the specified revision, or the
2826 2826 tip of the current branch if none is specified.
2827 2827
2828 2828 If the requested revision is a descendant of the working
2829 2829 directory, any outstanding changes in the working directory will
2830 2830 be merged into the result. If it is not directly descended but is
2831 2831 on the same named branch, update aborts with a suggestion to use
2832 2832 merge or update -C instead.
2833 2833
2834 2834 If the requested revision is on a different named branch and the
2835 2835 working directory is clean, update quietly switches branches.
2836 2836
2837 2837 See 'hg help dates' for a list of formats valid for --date.
2838 2838 """
2839 2839 if rev and node:
2840 2840 raise util.Abort(_("please specify just one revision"))
2841 2841
2842 2842 if not rev:
2843 2843 rev = node
2844 2844
2845 2845 if date:
2846 2846 if rev:
2847 2847 raise util.Abort(_("you can't specify a revision and a date"))
2848 2848 rev = cmdutil.finddate(ui, repo, date)
2849 2849
2850 2850 if clean:
2851 2851 return hg.clean(repo, rev)
2852 2852 else:
2853 2853 return hg.update(repo, rev)
2854 2854
2855 2855 def verify(ui, repo):
2856 2856 """verify the integrity of the repository
2857 2857
2858 2858 Verify the integrity of the current repository.
2859 2859
2860 2860 This will perform an extensive check of the repository's
2861 2861 integrity, validating the hashes and checksums of each entry in
2862 2862 the changelog, manifest, and tracked files, as well as the
2863 2863 integrity of their crosslinks and indices.
2864 2864 """
2865 2865 return hg.verify(repo)
2866 2866
2867 2867 def version_(ui):
2868 2868 """output version and copyright information"""
2869 2869 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2870 2870 % version.get_version())
2871 2871 ui.status(_(
2872 2872 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2873 2873 "This is free software; see the source for copying conditions. "
2874 2874 "There is NO\nwarranty; "
2875 2875 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2876 2876 ))
2877 2877
2878 2878 # Command options and aliases are listed here, alphabetically
2879 2879
2880 2880 globalopts = [
2881 2881 ('R', 'repository', '',
2882 2882 _('repository root directory or symbolic path name')),
2883 2883 ('', 'cwd', '', _('change working directory')),
2884 2884 ('y', 'noninteractive', None,
2885 2885 _('do not prompt, assume \'yes\' for any required answers')),
2886 2886 ('q', 'quiet', None, _('suppress output')),
2887 2887 ('v', 'verbose', None, _('enable additional output')),
2888 2888 ('', 'config', [], _('set/override config option')),
2889 2889 ('', 'debug', None, _('enable debugging output')),
2890 2890 ('', 'debugger', None, _('start debugger')),
2891 2891 ('', 'encoding', util._encoding, _('set the charset encoding')),
2892 2892 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2893 2893 ('', 'lsprof', None, _('print improved command execution profile')),
2894 2894 ('', 'traceback', None, _('print traceback on exception')),
2895 2895 ('', 'time', None, _('time how long the command takes')),
2896 2896 ('', 'profile', None, _('print command execution profile')),
2897 2897 ('', 'version', None, _('output version information and exit')),
2898 2898 ('h', 'help', None, _('display help and exit')),
2899 2899 ]
2900 2900
2901 2901 dryrunopts = [('n', 'dry-run', None,
2902 2902 _('do not perform actions, just print output'))]
2903 2903
2904 2904 remoteopts = [
2905 2905 ('e', 'ssh', '', _('specify ssh command to use')),
2906 2906 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2907 2907 ]
2908 2908
2909 2909 walkopts = [
2910 2910 ('I', 'include', [], _('include names matching the given patterns')),
2911 2911 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2912 2912 ]
2913 2913
2914 2914 commitopts = [
2915 2915 ('m', 'message', '', _('use <text> as commit message')),
2916 2916 ('l', 'logfile', '', _('read commit message from <file>')),
2917 2917 ]
2918 2918
2919 2919 commitopts2 = [
2920 2920 ('d', 'date', '', _('record datecode as commit date')),
2921 2921 ('u', 'user', '', _('record user as committer')),
2922 2922 ]
2923 2923
2924 2924 templateopts = [
2925 2925 ('', 'style', '', _('display using template map file')),
2926 2926 ('', 'template', '', _('display with template')),
2927 2927 ]
2928 2928
2929 2929 logopts = [
2930 2930 ('p', 'patch', None, _('show patch')),
2931 2931 ('l', 'limit', '', _('limit number of changes displayed')),
2932 2932 ('M', 'no-merges', None, _('do not show merges')),
2933 2933 ] + templateopts
2934 2934
2935 diffopts = [
2936 ('a', 'text', None, _('treat all files as text')),
2937 ('g', 'git', None, _('use git extended diff format')),
2938 ('', 'nodates', None, _("don't include dates in diff headers"))
2939 ]
2940
2941 diffopts2 = [
2942 ('p', 'show-function', None, _('show which function each change is in')),
2943 ('w', 'ignore-all-space', None,
2944 _('ignore white space when comparing lines')),
2945 ('b', 'ignore-space-change', None,
2946 _('ignore changes in the amount of white space')),
2947 ('B', 'ignore-blank-lines', None,
2948 _('ignore changes whose lines are all blank')),
2949 ('U', 'unified', '', _('number of lines of context to show'))
2950 ]
2951
2935 2952 table = {
2936 2953 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2937 2954 "addremove":
2938 2955 (addremove,
2939 2956 [('s', 'similarity', '',
2940 2957 _('guess renamed files by similarity (0<=s<=100)')),
2941 2958 ] + walkopts + dryrunopts,
2942 2959 _('hg addremove [OPTION]... [FILE]...')),
2943 2960 "^annotate|blame":
2944 2961 (annotate,
2945 2962 [('r', 'rev', '', _('annotate the specified revision')),
2946 2963 ('f', 'follow', None, _('follow file copies and renames')),
2947 2964 ('a', 'text', None, _('treat all files as text')),
2948 2965 ('u', 'user', None, _('list the author (long with -v)')),
2949 2966 ('d', 'date', None, _('list the date (short with -q)')),
2950 2967 ('n', 'number', None, _('list the revision number (default)')),
2951 2968 ('c', 'changeset', None, _('list the changeset')),
2952 2969 ('l', 'line-number', None,
2953 2970 _('show line number at the first appearance'))
2954 2971 ] + walkopts,
2955 2972 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2956 2973 "archive":
2957 2974 (archive,
2958 2975 [('', 'no-decode', None, _('do not pass files through decoders')),
2959 2976 ('p', 'prefix', '', _('directory prefix for files in archive')),
2960 2977 ('r', 'rev', '', _('revision to distribute')),
2961 2978 ('t', 'type', '', _('type of distribution to create')),
2962 2979 ] + walkopts,
2963 2980 _('hg archive [OPTION]... DEST')),
2964 2981 "backout":
2965 2982 (backout,
2966 2983 [('', 'merge', None,
2967 2984 _('merge with old dirstate parent after backout')),
2968 2985 ('', 'parent', '', _('parent to choose when backing out merge')),
2969 2986 ('r', 'rev', '', _('revision to backout')),
2970 2987 ] + walkopts + commitopts + commitopts2,
2971 2988 _('hg backout [OPTION]... [-r] REV')),
2972 2989 "bisect":
2973 2990 (bisect,
2974 2991 [('r', 'reset', False, _('reset bisect state')),
2975 2992 ('g', 'good', False, _('mark changeset good')),
2976 2993 ('b', 'bad', False, _('mark changeset bad')),
2977 2994 ('s', 'skip', False, _('skip testing changeset')),
2978 2995 ('U', 'noupdate', False, _('do not update to target'))],
2979 2996 _("hg bisect [-gbsr] [REV]")),
2980 2997 "branch":
2981 2998 (branch,
2982 2999 [('f', 'force', None,
2983 3000 _('set branch name even if it shadows an existing branch'))],
2984 3001 _('hg branch [-f] [NAME]')),
2985 3002 "branches":
2986 3003 (branches,
2987 3004 [('a', 'active', False,
2988 3005 _('show only branches that have unmerged heads'))],
2989 3006 _('hg branches [-a]')),
2990 3007 "bundle":
2991 3008 (bundle,
2992 3009 [('f', 'force', None,
2993 3010 _('run even when remote repository is unrelated')),
2994 3011 ('r', 'rev', [],
2995 3012 _('a changeset up to which you would like to bundle')),
2996 3013 ('', 'base', [],
2997 3014 _('a base changeset to specify instead of a destination')),
2998 3015 ('a', 'all', None, _('bundle all changesets in the repository')),
2999 3016 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3000 3017 ] + remoteopts,
3001 3018 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3002 3019 "cat":
3003 3020 (cat,
3004 3021 [('o', 'output', '', _('print output to file with formatted name')),
3005 3022 ('r', 'rev', '', _('print the given revision')),
3006 3023 ('', 'decode', None, _('apply any matching decode filter')),
3007 3024 ] + walkopts,
3008 3025 _('hg cat [OPTION]... FILE...')),
3009 3026 "^clone":
3010 3027 (clone,
3011 3028 [('U', 'noupdate', None,
3012 3029 _('the clone will only contain a repository (no working copy)')),
3013 3030 ('r', 'rev', [],
3014 3031 _('a changeset you would like to have after cloning')),
3015 3032 ('', 'pull', None, _('use pull protocol to copy metadata')),
3016 3033 ('', 'uncompressed', None,
3017 3034 _('use uncompressed transfer (fast over LAN)')),
3018 3035 ] + remoteopts,
3019 3036 _('hg clone [OPTION]... SOURCE [DEST]')),
3020 3037 "^commit|ci":
3021 3038 (commit,
3022 3039 [('A', 'addremove', None,
3023 3040 _('mark new/missing files as added/removed before committing')),
3024 3041 ] + walkopts + commitopts + commitopts2,
3025 3042 _('hg commit [OPTION]... [FILE]...')),
3026 3043 "copy|cp":
3027 3044 (copy,
3028 3045 [('A', 'after', None, _('record a copy that has already occurred')),
3029 3046 ('f', 'force', None,
3030 3047 _('forcibly copy over an existing managed file')),
3031 3048 ] + walkopts + dryrunopts,
3032 3049 _('hg copy [OPTION]... [SOURCE]... DEST')),
3033 3050 "debugancestor": (debugancestor, [],
3034 3051 _('hg debugancestor [INDEX] REV1 REV2')),
3035 3052 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3036 3053 "debugcomplete":
3037 3054 (debugcomplete,
3038 3055 [('o', 'options', None, _('show the command options'))],
3039 3056 _('hg debugcomplete [-o] CMD')),
3040 3057 "debugdate":
3041 3058 (debugdate,
3042 3059 [('e', 'extended', None, _('try extended date formats'))],
3043 3060 _('hg debugdate [-e] DATE [RANGE]')),
3044 3061 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3045 3062 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3046 3063 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3047 3064 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3048 3065 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3049 3066 "debugrawcommit|rawcommit":
3050 3067 (rawcommit,
3051 3068 [('p', 'parent', [], _('parent')),
3052 3069 ('F', 'files', '', _('file list'))
3053 3070 ] + commitopts + commitopts2,
3054 3071 _('hg debugrawcommit [OPTION]... [FILE]...')),
3055 3072 "debugrebuildstate":
3056 3073 (debugrebuildstate,
3057 3074 [('r', 'rev', '', _('revision to rebuild to'))],
3058 3075 _('hg debugrebuildstate [-r REV] [REV]')),
3059 3076 "debugrename":
3060 3077 (debugrename,
3061 3078 [('r', 'rev', '', _('revision to debug'))],
3062 3079 _('hg debugrename [-r REV] FILE')),
3063 3080 "debugsetparents":
3064 3081 (debugsetparents,
3065 3082 [],
3066 3083 _('hg debugsetparents REV1 [REV2]')),
3067 3084 "debugstate":
3068 3085 (debugstate,
3069 3086 [('', 'nodates', None, _('do not display the saved mtime'))],
3070 3087 _('hg debugstate [OPTS]')),
3071 3088 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3072 3089 "^diff":
3073 3090 (diff,
3074 [('r', 'rev', [], _('revision')),
3075 ('a', 'text', None, _('treat all files as text')),
3076 ('p', 'show-function', None,
3077 _('show which function each change is in')),
3078 ('g', 'git', None, _('use git extended diff format')),
3079 ('', 'nodates', None, _("don't include dates in diff headers")),
3080 ('w', 'ignore-all-space', None,
3081 _('ignore white space when comparing lines')),
3082 ('b', 'ignore-space-change', None,
3083 _('ignore changes in the amount of white space')),
3084 ('B', 'ignore-blank-lines', None,
3085 _('ignore changes whose lines are all blank')),
3086 ('U', 'unified', '',
3087 _('number of lines of context to show'))
3088 ] + walkopts,
3091 [('r', 'rev', [], _('revision'))
3092 ] + diffopts + diffopts2 + walkopts,
3089 3093 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3090 3094 "^export":
3091 3095 (export,
3092 3096 [('o', 'output', '', _('print output to file with formatted name')),
3093 ('a', 'text', None, _('treat all files as text')),
3094 ('g', 'git', None, _('use git extended diff format')),
3095 ('', 'nodates', None, _("don't include dates in diff headers")),
3096 ('', 'switch-parent', None, _('diff against the second parent'))],
3097 ('', 'switch-parent', None, _('diff against the second parent'))
3098 ] + diffopts,
3097 3099 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3098 3100 "grep":
3099 3101 (grep,
3100 3102 [('0', 'print0', None, _('end fields with NUL')),
3101 3103 ('', 'all', None, _('print all revisions that match')),
3102 3104 ('f', 'follow', None,
3103 3105 _('follow changeset history, or file history across copies and renames')),
3104 3106 ('i', 'ignore-case', None, _('ignore case when matching')),
3105 3107 ('l', 'files-with-matches', None,
3106 3108 _('print only filenames and revs that match')),
3107 3109 ('n', 'line-number', None, _('print matching line numbers')),
3108 3110 ('r', 'rev', [], _('search in given revision range')),
3109 3111 ('u', 'user', None, _('list the author (long with -v)')),
3110 3112 ('d', 'date', None, _('list the date (short with -q)')),
3111 3113 ] + walkopts,
3112 3114 _('hg grep [OPTION]... PATTERN [FILE]...')),
3113 3115 "heads":
3114 3116 (heads,
3115 3117 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3116 3118 ] + templateopts,
3117 3119 _('hg heads [-r REV] [REV]...')),
3118 3120 "help": (help_, [], _('hg help [COMMAND]')),
3119 3121 "identify|id":
3120 3122 (identify,
3121 3123 [('r', 'rev', '', _('identify the specified rev')),
3122 3124 ('n', 'num', None, _('show local revision number')),
3123 3125 ('i', 'id', None, _('show global revision id')),
3124 3126 ('b', 'branch', None, _('show branch')),
3125 3127 ('t', 'tags', None, _('show tags'))],
3126 3128 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3127 3129 "import|patch":
3128 3130 (import_,
3129 3131 [('p', 'strip', 1,
3130 3132 _('directory strip option for patch. This has the same\n'
3131 3133 'meaning as the corresponding patch option')),
3132 3134 ('b', 'base', '', _('base path')),
3133 3135 ('f', 'force', None,
3134 3136 _('skip check for outstanding uncommitted changes')),
3135 3137 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3136 3138 ('', 'exact', None,
3137 3139 _('apply patch to the nodes from which it was generated')),
3138 3140 ('', 'import-branch', None,
3139 3141 _('Use any branch information in patch (implied by --exact)'))] +
3140 3142 commitopts + commitopts2,
3141 3143 _('hg import [OPTION]... PATCH...')),
3142 3144 "incoming|in":
3143 3145 (incoming,
3144 3146 [('f', 'force', None,
3145 3147 _('run even when remote repository is unrelated')),
3146 3148 ('n', 'newest-first', None, _('show newest record first')),
3147 3149 ('', 'bundle', '', _('file to store the bundles into')),
3148 3150 ('r', 'rev', [],
3149 3151 _('a specific revision up to which you would like to pull')),
3150 3152 ] + logopts + remoteopts,
3151 3153 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3152 3154 ' [--bundle FILENAME] [SOURCE]')),
3153 3155 "^init":
3154 3156 (init,
3155 3157 remoteopts,
3156 3158 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3157 3159 "locate":
3158 3160 (locate,
3159 3161 [('r', 'rev', '', _('search the repository as it stood at rev')),
3160 3162 ('0', 'print0', None,
3161 3163 _('end filenames with NUL, for use with xargs')),
3162 3164 ('f', 'fullpath', None,
3163 3165 _('print complete paths from the filesystem root')),
3164 3166 ] + walkopts,
3165 3167 _('hg locate [OPTION]... [PATTERN]...')),
3166 3168 "^log|history":
3167 3169 (log,
3168 3170 [('f', 'follow', None,
3169 3171 _('follow changeset history, or file history across copies and renames')),
3170 3172 ('', 'follow-first', None,
3171 3173 _('only follow the first parent of merge changesets')),
3172 3174 ('d', 'date', '', _('show revs matching date spec')),
3173 3175 ('C', 'copies', None, _('show copied files')),
3174 3176 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3175 3177 ('r', 'rev', [], _('show the specified revision or range')),
3176 3178 ('', 'removed', None, _('include revs where files were removed')),
3177 3179 ('m', 'only-merges', None, _('show only merges')),
3178 3180 ('b', 'only-branch', [],
3179 3181 _('show only changesets within the given named branch')),
3180 3182 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3181 3183 ] + logopts + walkopts,
3182 3184 _('hg log [OPTION]... [FILE]')),
3183 3185 "manifest":
3184 3186 (manifest,
3185 3187 [('r', 'rev', '', _('revision to display'))],
3186 3188 _('hg manifest [-r REV]')),
3187 3189 "^merge":
3188 3190 (merge,
3189 3191 [('f', 'force', None, _('force a merge with outstanding changes')),
3190 3192 ('r', 'rev', '', _('revision to merge')),
3191 3193 ],
3192 3194 _('hg merge [-f] [[-r] REV]')),
3193 3195 "outgoing|out":
3194 3196 (outgoing,
3195 3197 [('f', 'force', None,
3196 3198 _('run even when remote repository is unrelated')),
3197 3199 ('r', 'rev', [],
3198 3200 _('a specific revision up to which you would like to push')),
3199 3201 ('n', 'newest-first', None, _('show newest record first')),
3200 3202 ] + logopts + remoteopts,
3201 3203 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3202 3204 "^parents":
3203 3205 (parents,
3204 3206 [('r', 'rev', '', _('show parents from the specified rev')),
3205 3207 ] + templateopts,
3206 3208 _('hg parents [-r REV] [FILE]')),
3207 3209 "paths": (paths, [], _('hg paths [NAME]')),
3208 3210 "^pull":
3209 3211 (pull,
3210 3212 [('u', 'update', None,
3211 3213 _('update to new tip if changesets were pulled')),
3212 3214 ('f', 'force', None,
3213 3215 _('run even when remote repository is unrelated')),
3214 3216 ('r', 'rev', [],
3215 3217 _('a specific revision up to which you would like to pull')),
3216 3218 ] + remoteopts,
3217 3219 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3218 3220 "^push":
3219 3221 (push,
3220 3222 [('f', 'force', None, _('force push')),
3221 3223 ('r', 'rev', [],
3222 3224 _('a specific revision up to which you would like to push')),
3223 3225 ] + remoteopts,
3224 3226 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3225 3227 "recover": (recover, [], _('hg recover')),
3226 3228 "^remove|rm":
3227 3229 (remove,
3228 3230 [('A', 'after', None, _('record delete for missing files')),
3229 3231 ('f', 'force', None,
3230 3232 _('remove (and delete) file even if added or modified')),
3231 3233 ] + walkopts,
3232 3234 _('hg remove [OPTION]... FILE...')),
3233 3235 "rename|mv":
3234 3236 (rename,
3235 3237 [('A', 'after', None, _('record a rename that has already occurred')),
3236 3238 ('f', 'force', None,
3237 3239 _('forcibly copy over an existing managed file')),
3238 3240 ] + walkopts + dryrunopts,
3239 3241 _('hg rename [OPTION]... SOURCE... DEST')),
3240 3242 "resolve":
3241 3243 (resolve,
3242 3244 [('l', 'list', None, _('list state of files needing merge')),
3243 3245 ('m', 'mark', None, _('mark files as resolved')),
3244 3246 ('u', 'unmark', None, _('unmark files as resolved'))],
3245 3247 ('hg resolve [OPTION] [FILES...]')),
3246 3248 "revert":
3247 3249 (revert,
3248 3250 [('a', 'all', None, _('revert all changes when no arguments given')),
3249 3251 ('d', 'date', '', _('tipmost revision matching date')),
3250 3252 ('r', 'rev', '', _('revision to revert to')),
3251 3253 ('', 'no-backup', None, _('do not save backup copies of files')),
3252 3254 ] + walkopts + dryrunopts,
3253 3255 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3254 3256 "rollback": (rollback, [], _('hg rollback')),
3255 3257 "root": (root, [], _('hg root')),
3256 3258 "^serve":
3257 3259 (serve,
3258 3260 [('A', 'accesslog', '', _('name of access log file to write to')),
3259 3261 ('d', 'daemon', None, _('run server in background')),
3260 3262 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3261 3263 ('E', 'errorlog', '', _('name of error log file to write to')),
3262 3264 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3263 3265 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3264 3266 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3265 3267 ('n', 'name', '',
3266 3268 _('name to show in web pages (default: working dir)')),
3267 3269 ('', 'webdir-conf', '', _('name of the webdir config file'
3268 3270 ' (serve more than one repo)')),
3269 3271 ('', 'pid-file', '', _('name of file to write process ID to')),
3270 3272 ('', 'stdio', None, _('for remote clients')),
3271 3273 ('t', 'templates', '', _('web templates to use')),
3272 3274 ('', 'style', '', _('template style to use')),
3273 3275 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3274 3276 ('', 'certificate', '', _('SSL certificate file'))],
3275 3277 _('hg serve [OPTION]...')),
3276 3278 "showconfig|debugconfig":
3277 3279 (showconfig,
3278 3280 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3279 3281 _('hg showconfig [-u] [NAME]...')),
3280 3282 "^status|st":
3281 3283 (status,
3282 3284 [('A', 'all', None, _('show status of all files')),
3283 3285 ('m', 'modified', None, _('show only modified files')),
3284 3286 ('a', 'added', None, _('show only added files')),
3285 3287 ('r', 'removed', None, _('show only removed files')),
3286 3288 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3287 3289 ('c', 'clean', None, _('show only files without changes')),
3288 3290 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3289 3291 ('i', 'ignored', None, _('show only ignored files')),
3290 3292 ('n', 'no-status', None, _('hide status prefix')),
3291 3293 ('C', 'copies', None, _('show source of copied files')),
3292 3294 ('0', 'print0', None,
3293 3295 _('end filenames with NUL, for use with xargs')),
3294 3296 ('', 'rev', [], _('show difference from revision')),
3295 3297 ] + walkopts,
3296 3298 _('hg status [OPTION]... [FILE]...')),
3297 3299 "tag":
3298 3300 (tag,
3299 3301 [('f', 'force', None, _('replace existing tag')),
3300 3302 ('l', 'local', None, _('make the tag local')),
3301 3303 ('r', 'rev', '', _('revision to tag')),
3302 3304 ('', 'remove', None, _('remove a tag')),
3303 3305 # -l/--local is already there, commitopts cannot be used
3304 3306 ('m', 'message', '', _('use <text> as commit message')),
3305 3307 ] + commitopts2,
3306 3308 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3307 3309 "tags": (tags, [], _('hg tags')),
3308 3310 "tip":
3309 3311 (tip,
3310 3312 [('p', 'patch', None, _('show patch')),
3311 3313 ] + templateopts,
3312 3314 _('hg tip [-p]')),
3313 3315 "unbundle":
3314 3316 (unbundle,
3315 3317 [('u', 'update', None,
3316 3318 _('update to new tip if changesets were unbundled'))],
3317 3319 _('hg unbundle [-u] FILE...')),
3318 3320 "^update|up|checkout|co":
3319 3321 (update,
3320 3322 [('C', 'clean', None, _('overwrite locally modified files')),
3321 3323 ('d', 'date', '', _('tipmost revision matching date')),
3322 3324 ('r', 'rev', '', _('revision'))],
3323 3325 _('hg update [-C] [-d DATE] [[-r] REV]')),
3324 3326 "verify": (verify, [], _('hg verify')),
3325 3327 "version": (version_, [], _('hg version')),
3326 3328 }
3327 3329
3328 3330 norepo = ("clone init version help debugcomplete debugdata"
3329 3331 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3330 3332 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,314 +1,314 b''
1 1 Mercurial Distributed SCM
2 2
3 3 basic commands:
4 4
5 5 add add the specified files on the next commit
6 6 annotate show changeset information per file line
7 7 clone make a copy of an existing repository
8 8 commit commit the specified files or all outstanding changes
9 9 diff diff repository (or selected files)
10 10 export dump the header and diffs for one or more changesets
11 11 init create a new repository in the given directory
12 12 log show revision history of entire repository or files
13 13 merge merge working directory with another revision
14 14 parents show the parents of the working dir or revision
15 15 pull pull changes from the specified source
16 16 push push changes to the specified destination
17 17 remove remove the specified files on the next commit
18 18 serve export the repository via HTTP
19 19 status show changed files in the working directory
20 20 update update working directory
21 21
22 22 use "hg help" for the full list of commands or "hg -v" for details
23 23 add add the specified files on the next commit
24 24 annotate show changeset information per file line
25 25 clone make a copy of an existing repository
26 26 commit commit the specified files or all outstanding changes
27 27 diff diff repository (or selected files)
28 28 export dump the header and diffs for one or more changesets
29 29 init create a new repository in the given directory
30 30 log show revision history of entire repository or files
31 31 merge merge working directory with another revision
32 32 parents show the parents of the working dir or revision
33 33 pull pull changes from the specified source
34 34 push push changes to the specified destination
35 35 remove remove the specified files on the next commit
36 36 serve export the repository via HTTP
37 37 status show changed files in the working directory
38 38 update update working directory
39 39 Mercurial Distributed SCM
40 40
41 41 list of commands:
42 42
43 43 add add the specified files on the next commit
44 44 addremove add all new files, delete all missing files
45 45 annotate show changeset information per file line
46 46 archive create unversioned archive of a repository revision
47 47 backout reverse effect of earlier changeset
48 48 bisect subdivision search of changesets
49 49 branch set or show the current branch name
50 50 branches list repository named branches
51 51 bundle create a changegroup file
52 52 cat output the current or given revision of files
53 53 clone make a copy of an existing repository
54 54 commit commit the specified files or all outstanding changes
55 55 copy mark files as copied for the next commit
56 56 diff diff repository (or selected files)
57 57 export dump the header and diffs for one or more changesets
58 58 grep search for a pattern in specified files and revisions
59 59 heads show current repository heads or show branch heads
60 60 help show help for a command, extension, or list of commands
61 61 identify identify the working copy or specified revision
62 62 import import an ordered set of patches
63 63 incoming show new changesets found in source
64 64 init create a new repository in the given directory
65 65 locate locate files matching specific patterns
66 66 log show revision history of entire repository or files
67 67 manifest output the current or given revision of the project manifest
68 68 merge merge working directory with another revision
69 69 outgoing show changesets not found in destination
70 70 parents show the parents of the working dir or revision
71 71 paths show definition of symbolic path names
72 72 pull pull changes from the specified source
73 73 push push changes to the specified destination
74 74 recover roll back an interrupted transaction
75 75 remove remove the specified files on the next commit
76 76 rename rename files; equivalent of copy + remove
77 77 resolve resolve file merges from a branch merge or update
78 78 revert restore individual files or dirs to an earlier state
79 79 rollback roll back the last transaction
80 80 root print the root (top) of the current working dir
81 81 serve export the repository via HTTP
82 82 showconfig show combined config settings from all hgrc files
83 83 status show changed files in the working directory
84 84 tag add one or more tags for the current or given revision
85 85 tags list repository tags
86 86 tip show the tip revision
87 87 unbundle apply one or more changegroup files
88 88 update update working directory
89 89 verify verify the integrity of the repository
90 90 version output version and copyright information
91 91
92 92 use "hg -v help" to show aliases and global options
93 93 add add the specified files on the next commit
94 94 addremove add all new files, delete all missing files
95 95 annotate show changeset information per file line
96 96 archive create unversioned archive of a repository revision
97 97 backout reverse effect of earlier changeset
98 98 bisect subdivision search of changesets
99 99 branch set or show the current branch name
100 100 branches list repository named branches
101 101 bundle create a changegroup file
102 102 cat output the current or given revision of files
103 103 clone make a copy of an existing repository
104 104 commit commit the specified files or all outstanding changes
105 105 copy mark files as copied for the next commit
106 106 diff diff repository (or selected files)
107 107 export dump the header and diffs for one or more changesets
108 108 grep search for a pattern in specified files and revisions
109 109 heads show current repository heads or show branch heads
110 110 help show help for a command, extension, or list of commands
111 111 identify identify the working copy or specified revision
112 112 import import an ordered set of patches
113 113 incoming show new changesets found in source
114 114 init create a new repository in the given directory
115 115 locate locate files matching specific patterns
116 116 log show revision history of entire repository or files
117 117 manifest output the current or given revision of the project manifest
118 118 merge merge working directory with another revision
119 119 outgoing show changesets not found in destination
120 120 parents show the parents of the working dir or revision
121 121 paths show definition of symbolic path names
122 122 pull pull changes from the specified source
123 123 push push changes to the specified destination
124 124 recover roll back an interrupted transaction
125 125 remove remove the specified files on the next commit
126 126 rename rename files; equivalent of copy + remove
127 127 resolve resolve file merges from a branch merge or update
128 128 revert restore individual files or dirs to an earlier state
129 129 rollback roll back the last transaction
130 130 root print the root (top) of the current working dir
131 131 serve export the repository via HTTP
132 132 showconfig show combined config settings from all hgrc files
133 133 status show changed files in the working directory
134 134 tag add one or more tags for the current or given revision
135 135 tags list repository tags
136 136 tip show the tip revision
137 137 unbundle apply one or more changegroup files
138 138 update update working directory
139 139 verify verify the integrity of the repository
140 140 version output version and copyright information
141 141 hg add [OPTION]... [FILE]...
142 142
143 143 add the specified files on the next commit
144 144
145 145 Schedule files to be version controlled and added to the repository.
146 146
147 147 The files will be added to the repository at the next commit. To
148 148 undo an add before that, see hg revert.
149 149
150 150 If no names are given, add all files in the repository.
151 151
152 152 options:
153 153
154 154 -I --include include names matching the given patterns
155 155 -X --exclude exclude names matching the given patterns
156 156 -n --dry-run do not perform actions, just print output
157 157
158 158 use "hg -v help add" to show global options
159 159 hg add: option --skjdfks not recognized
160 160 hg add [OPTION]... [FILE]...
161 161
162 162 add the specified files on the next commit
163 163
164 164 Schedule files to be version controlled and added to the repository.
165 165
166 166 The files will be added to the repository at the next commit. To
167 167 undo an add before that, see hg revert.
168 168
169 169 If no names are given, add all files in the repository.
170 170
171 171 options:
172 172
173 173 -I --include include names matching the given patterns
174 174 -X --exclude exclude names matching the given patterns
175 175 -n --dry-run do not perform actions, just print output
176 176
177 177 use "hg -v help add" to show global options
178 178 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
179 179
180 180 diff repository (or selected files)
181 181
182 182 Show differences between revisions for the specified files.
183 183
184 184 Differences between files are shown using the unified diff format.
185 185
186 186 NOTE: diff may generate unexpected results for merges, as it will
187 187 default to comparing against the working directory's first parent
188 188 changeset if no revisions are specified.
189 189
190 190 When two revision arguments are given, then changes are shown
191 191 between those revisions. If only one revision is specified then
192 192 that revision is compared to the working directory, and, when no
193 193 revisions are specified, the working directory files are compared
194 194 to its parent.
195 195
196 196 Without the -a option, diff will avoid generating diffs of files
197 197 it detects as binary. With -a, diff will generate a diff anyway,
198 198 probably with undesirable results.
199 199
200 200 options:
201 201
202 202 -r --rev revision
203 203 -a --text treat all files as text
204 -p --show-function show which function each change is in
205 204 -g --git use git extended diff format
206 205 --nodates don't include dates in diff headers
206 -p --show-function show which function each change is in
207 207 -w --ignore-all-space ignore white space when comparing lines
208 208 -b --ignore-space-change ignore changes in the amount of white space
209 209 -B --ignore-blank-lines ignore changes whose lines are all blank
210 210 -U --unified number of lines of context to show
211 211 -I --include include names matching the given patterns
212 212 -X --exclude exclude names matching the given patterns
213 213
214 214 use "hg -v help diff" to show global options
215 215 hg status [OPTION]... [FILE]...
216 216
217 217 aliases: st
218 218
219 219 show changed files in the working directory
220 220
221 221 Show status of files in the repository. If names are given, only
222 222 files that match are shown. Files that are clean or ignored or
223 223 source of a copy/move operation, are not listed unless -c (clean),
224 224 -i (ignored), -C (copies) or -A is given. Unless options described
225 225 with "show only ..." are given, the options -mardu are used.
226 226
227 227 Option -q/--quiet hides untracked (unknown and ignored) files
228 228 unless explicitly requested with -u/--unknown or -i/-ignored.
229 229
230 230 NOTE: status may appear to disagree with diff if permissions have
231 231 changed or a merge has occurred. The standard diff format does not
232 232 report permission changes and diff only reports changes relative
233 233 to one merge parent.
234 234
235 235 If one revision is given, it is used as the base revision.
236 236 If two revisions are given, the difference between them is shown.
237 237
238 238 The codes used to show the status of files are:
239 239 M = modified
240 240 A = added
241 241 R = removed
242 242 C = clean
243 243 ! = deleted, but still tracked
244 244 ? = not tracked
245 245 I = ignored
246 246 = the previous added file was copied from here
247 247
248 248 options:
249 249
250 250 -A --all show status of all files
251 251 -m --modified show only modified files
252 252 -a --added show only added files
253 253 -r --removed show only removed files
254 254 -d --deleted show only deleted (but tracked) files
255 255 -c --clean show only files without changes
256 256 -u --unknown show only unknown (not tracked) files
257 257 -i --ignored show only ignored files
258 258 -n --no-status hide status prefix
259 259 -C --copies show source of copied files
260 260 -0 --print0 end filenames with NUL, for use with xargs
261 261 --rev show difference from revision
262 262 -I --include include names matching the given patterns
263 263 -X --exclude exclude names matching the given patterns
264 264
265 265 use "hg -v help status" to show global options
266 266 hg status [OPTION]... [FILE]...
267 267
268 268 show changed files in the working directory
269 269 hg: unknown command 'foo'
270 270 Mercurial Distributed SCM
271 271
272 272 basic commands:
273 273
274 274 add add the specified files on the next commit
275 275 annotate show changeset information per file line
276 276 clone make a copy of an existing repository
277 277 commit commit the specified files or all outstanding changes
278 278 diff diff repository (or selected files)
279 279 export dump the header and diffs for one or more changesets
280 280 init create a new repository in the given directory
281 281 log show revision history of entire repository or files
282 282 merge merge working directory with another revision
283 283 parents show the parents of the working dir or revision
284 284 pull pull changes from the specified source
285 285 push push changes to the specified destination
286 286 remove remove the specified files on the next commit
287 287 serve export the repository via HTTP
288 288 status show changed files in the working directory
289 289 update update working directory
290 290
291 291 use "hg help" for the full list of commands or "hg -v" for details
292 292 hg: unknown command 'skjdfks'
293 293 Mercurial Distributed SCM
294 294
295 295 basic commands:
296 296
297 297 add add the specified files on the next commit
298 298 annotate show changeset information per file line
299 299 clone make a copy of an existing repository
300 300 commit commit the specified files or all outstanding changes
301 301 diff diff repository (or selected files)
302 302 export dump the header and diffs for one or more changesets
303 303 init create a new repository in the given directory
304 304 log show revision history of entire repository or files
305 305 merge merge working directory with another revision
306 306 parents show the parents of the working dir or revision
307 307 pull pull changes from the specified source
308 308 push push changes to the specified destination
309 309 remove remove the specified files on the next commit
310 310 serve export the repository via HTTP
311 311 status show changed files in the working directory
312 312 update update working directory
313 313
314 314 use "hg help" for the full list of commands or "hg -v" for details
@@ -1,27 +1,59 b''
1 1 #!/bin/sh
2 2
3 3 echo "[extensions]" >> $HGRCPATH
4 4 echo "mq=" >> $HGRCPATH
5 5
6 6 echo % init
7 7 hg init a
8 8 cd a
9 9
10 10 echo % commit
11 11 echo 'base' > base
12 12 hg ci -Ambase -d '1 0'
13 13
14 14 echo % qnew mqbase
15 15 hg qnew -mmqbase mqbase
16 16
17 17 echo % qrefresh
18 18 echo 'patched' > base
19 19 hg qrefresh
20 20
21 21 echo % qdiff
22 22 hg qdiff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
23 23 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
24 24
25 25 echo % qdiff dirname
26 26 hg qdiff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
27 27 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
28
29 echo % qdiff filename
30 hg qdiff --nodates base
31
32 echo % revert
33 hg revert -a
34
35 echo % qpop
36 hg qpop
37
38 echo % qdelete mqbase
39 hg qdelete mqbase
40
41 echo % commit 2
42 printf '1\n2\n3\n4\nhello world\ngoodbye world\n7\n8\n9\n' > lines
43 hg ci -Amlines -d '2 0'
44
45 echo % qnew 2
46 hg qnew -mmqbase2 mqbase2
47 printf '\n\n1\n2\n3\n4\nhello world\n goodbye world\n7\n8\n9\n' > lines
48
49 echo % qdiff -U 1
50 hg qdiff --nodates -U 1
51
52 echo % qdiff -b
53 hg qdiff --nodates -b
54
55 echo % qdiff -U 1 -B
56 hg qdiff --nodates -U 1 -B
57
58 echo qdiff -w
59 hg qdiff --nodates -w
@@ -1,19 +1,87 b''
1 1 % init
2 2 % commit
3 3 adding base
4 4 % qnew mqbase
5 5 % qrefresh
6 6 % qdiff
7 7 diff -r 67e992f2c4f3 base
8 8 --- a/base
9 9 +++ b/base
10 10 @@ -1,1 +1,1 @@
11 11 -base
12 12 +patched
13 13 % qdiff dirname
14 14 diff -r 67e992f2c4f3 base
15 15 --- a/base
16 16 +++ b/base
17 17 @@ -1,1 +1,1 @@
18 18 -base
19 19 +patched
20 % qdiff filename
21 diff -r 67e992f2c4f3 base
22 --- a/base
23 +++ b/base
24 @@ -1,1 +1,1 @@
25 -base
26 +patched
27 % revert
28 % qpop
29 Patch queue now empty
30 % qdelete mqbase
31 % commit 2
32 adding lines
33 % qnew 2
34 % qdiff -U 1
35 diff -r 35fb829491c1 lines
36 --- a/lines
37 +++ b/lines
38 @@ -1,1 +1,3 @@
39 +
40 +
41 1
42 @@ -4,4 +6,4 @@
43 4
44 -hello world
45 -goodbye world
46 +hello world
47 + goodbye world
48 7
49 % qdiff -b
50 diff -r 35fb829491c1 lines
51 --- a/lines
52 +++ b/lines
53 @@ -1,9 +1,11 @@
54 +
55 +
56 1
57 2
58 3
59 4
60 -hello world
61 -goodbye world
62 +hello world
63 + goodbye world
64 7
65 8
66 9
67 % qdiff -U 1 -B
68 diff -r 35fb829491c1 lines
69 --- a/lines
70 +++ b/lines
71 @@ -4,4 +6,4 @@
72 4
73 -hello world
74 -goodbye world
75 +hello world
76 + goodbye world
77 7
78 qdiff -w
79 diff -r 35fb829491c1 lines
80 --- a/lines
81 +++ b/lines
82 @@ -1,3 +1,5 @@
83 +
84 +
85 1
86 2
87 3
General Comments 0
You need to be logged in to leave comments. Login now