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