##// END OF EJS Templates
i18n: wrap false positives for translation detection
Matt Mackall -
r17956:a08775ec default
parent child Browse files
Show More
@@ -1,853 +1,853
1 1 # Mercurial built-in replacement for cvsps.
2 2 #
3 3 # Copyright 2008, Frank Kingswood <frank@kingswood-consulting.co.uk>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import os
9 9 import re
10 10 import cPickle as pickle
11 11 from mercurial import util
12 12 from mercurial.i18n import _
13 13 from mercurial import hook
14 14 from mercurial import util
15 15
16 16 class logentry(object):
17 17 '''Class logentry has the following attributes:
18 18 .author - author name as CVS knows it
19 19 .branch - name of branch this revision is on
20 20 .branches - revision tuple of branches starting at this revision
21 21 .comment - commit message
22 22 .date - the commit date as a (time, tz) tuple
23 23 .dead - true if file revision is dead
24 24 .file - Name of file
25 25 .lines - a tuple (+lines, -lines) or None
26 26 .parent - Previous revision of this entry
27 27 .rcs - name of file as returned from CVS
28 28 .revision - revision number as tuple
29 29 .tags - list of tags on the file
30 30 .synthetic - is this a synthetic "file ... added on ..." revision?
31 31 .mergepoint- the branch that has been merged from
32 32 (if present in rlog output)
33 33 .branchpoints- the branches that start at the current entry
34 34 '''
35 35 def __init__(self, **entries):
36 36 self.synthetic = False
37 37 self.__dict__.update(entries)
38 38
39 39 def __repr__(self):
40 40 return "<%s at 0x%x: %s %s>" % (self.__class__.__name__,
41 41 id(self),
42 42 self.file,
43 43 ".".join(map(str, self.revision)))
44 44
45 45 class logerror(Exception):
46 46 pass
47 47
48 48 def getrepopath(cvspath):
49 49 """Return the repository path from a CVS path.
50 50
51 51 >>> getrepopath('/foo/bar')
52 52 '/foo/bar'
53 53 >>> getrepopath('c:/foo/bar')
54 54 'c:/foo/bar'
55 55 >>> getrepopath(':pserver:10/foo/bar')
56 56 '/foo/bar'
57 57 >>> getrepopath(':pserver:10c:/foo/bar')
58 58 '/foo/bar'
59 59 >>> getrepopath(':pserver:/foo/bar')
60 60 '/foo/bar'
61 61 >>> getrepopath(':pserver:c:/foo/bar')
62 62 'c:/foo/bar'
63 63 >>> getrepopath(':pserver:truc@foo.bar:/foo/bar')
64 64 '/foo/bar'
65 65 >>> getrepopath(':pserver:truc@foo.bar:c:/foo/bar')
66 66 'c:/foo/bar'
67 67 """
68 68 # According to CVS manual, CVS paths are expressed like:
69 69 # [:method:][[user][:password]@]hostname[:[port]]/path/to/repository
70 70 #
71 71 # Unfortunately, Windows absolute paths start with a drive letter
72 72 # like 'c:' making it harder to parse. Here we assume that drive
73 73 # letters are only one character long and any CVS component before
74 74 # the repository path is at least 2 characters long, and use this
75 75 # to disambiguate.
76 76 parts = cvspath.split(':')
77 77 if len(parts) == 1:
78 78 return parts[0]
79 79 # Here there is an ambiguous case if we have a port number
80 80 # immediately followed by a Windows driver letter. We assume this
81 81 # never happens and decide it must be CVS path component,
82 82 # therefore ignoring it.
83 83 if len(parts[-2]) > 1:
84 84 return parts[-1].lstrip('0123456789')
85 85 return parts[-2] + ':' + parts[-1]
86 86
87 87 def createlog(ui, directory=None, root="", rlog=True, cache=None):
88 88 '''Collect the CVS rlog'''
89 89
90 90 # Because we store many duplicate commit log messages, reusing strings
91 91 # saves a lot of memory and pickle storage space.
92 92 _scache = {}
93 93 def scache(s):
94 94 "return a shared version of a string"
95 95 return _scache.setdefault(s, s)
96 96
97 97 ui.status(_('collecting CVS rlog\n'))
98 98
99 99 log = [] # list of logentry objects containing the CVS state
100 100
101 101 # patterns to match in CVS (r)log output, by state of use
102 102 re_00 = re.compile('RCS file: (.+)$')
103 103 re_01 = re.compile('cvs \\[r?log aborted\\]: (.+)$')
104 104 re_02 = re.compile('cvs (r?log|server): (.+)\n$')
105 105 re_03 = re.compile("(Cannot access.+CVSROOT)|"
106 106 "(can't create temporary directory.+)$")
107 107 re_10 = re.compile('Working file: (.+)$')
108 108 re_20 = re.compile('symbolic names:')
109 109 re_30 = re.compile('\t(.+): ([\\d.]+)$')
110 110 re_31 = re.compile('----------------------------$')
111 111 re_32 = re.compile('======================================='
112 112 '======================================$')
113 113 re_50 = re.compile('revision ([\\d.]+)(\s+locked by:\s+.+;)?$')
114 114 re_60 = re.compile(r'date:\s+(.+);\s+author:\s+(.+);\s+state:\s+(.+?);'
115 115 r'(\s+lines:\s+(\+\d+)?\s+(-\d+)?;)?'
116 116 r'(.*mergepoint:\s+([^;]+);)?')
117 117 re_70 = re.compile('branches: (.+);$')
118 118
119 119 file_added_re = re.compile(r'file [^/]+ was (initially )?added on branch')
120 120
121 121 prefix = '' # leading path to strip of what we get from CVS
122 122
123 123 if directory is None:
124 124 # Current working directory
125 125
126 126 # Get the real directory in the repository
127 127 try:
128 128 prefix = open(os.path.join('CVS','Repository')).read().strip()
129 129 directory = prefix
130 130 if prefix == ".":
131 131 prefix = ""
132 132 except IOError:
133 133 raise logerror(_('not a CVS sandbox'))
134 134
135 135 if prefix and not prefix.endswith(os.sep):
136 136 prefix += os.sep
137 137
138 138 # Use the Root file in the sandbox, if it exists
139 139 try:
140 140 root = open(os.path.join('CVS','Root')).read().strip()
141 141 except IOError:
142 142 pass
143 143
144 144 if not root:
145 145 root = os.environ.get('CVSROOT', '')
146 146
147 147 # read log cache if one exists
148 148 oldlog = []
149 149 date = None
150 150
151 151 if cache:
152 152 cachedir = os.path.expanduser('~/.hg.cvsps')
153 153 if not os.path.exists(cachedir):
154 154 os.mkdir(cachedir)
155 155
156 156 # The cvsps cache pickle needs a uniquified name, based on the
157 157 # repository location. The address may have all sort of nasties
158 158 # in it, slashes, colons and such. So here we take just the
159 159 # alphanumeric characters, concatenated in a way that does not
160 160 # mix up the various components, so that
161 161 # :pserver:user@server:/path
162 162 # and
163 163 # /pserver/user/server/path
164 164 # are mapped to different cache file names.
165 165 cachefile = root.split(":") + [directory, "cache"]
166 166 cachefile = ['-'.join(re.findall(r'\w+', s)) for s in cachefile if s]
167 167 cachefile = os.path.join(cachedir,
168 168 '.'.join([s for s in cachefile if s]))
169 169
170 170 if cache == 'update':
171 171 try:
172 172 ui.note(_('reading cvs log cache %s\n') % cachefile)
173 173 oldlog = pickle.load(open(cachefile))
174 174 ui.note(_('cache has %d log entries\n') % len(oldlog))
175 175 except Exception, e:
176 176 ui.note(_('error reading cache: %r\n') % e)
177 177
178 178 if oldlog:
179 179 date = oldlog[-1].date # last commit date as a (time,tz) tuple
180 180 date = util.datestr(date, '%Y/%m/%d %H:%M:%S %1%2')
181 181
182 182 # build the CVS commandline
183 183 cmd = ['cvs', '-q']
184 184 if root:
185 185 cmd.append('-d%s' % root)
186 186 p = util.normpath(getrepopath(root))
187 187 if not p.endswith('/'):
188 188 p += '/'
189 189 if prefix:
190 190 # looks like normpath replaces "" by "."
191 191 prefix = p + util.normpath(prefix)
192 192 else:
193 193 prefix = p
194 194 cmd.append(['log', 'rlog'][rlog])
195 195 if date:
196 196 # no space between option and date string
197 197 cmd.append('-d>%s' % date)
198 198 cmd.append(directory)
199 199
200 200 # state machine begins here
201 201 tags = {} # dictionary of revisions on current file with their tags
202 202 branchmap = {} # mapping between branch names and revision numbers
203 203 state = 0
204 204 store = False # set when a new record can be appended
205 205
206 206 cmd = [util.shellquote(arg) for arg in cmd]
207 207 ui.note(_("running %s\n") % (' '.join(cmd)))
208 208 ui.debug("prefix=%r directory=%r root=%r\n" % (prefix, directory, root))
209 209
210 210 pfp = util.popen(' '.join(cmd))
211 211 peek = pfp.readline()
212 212 while True:
213 213 line = peek
214 214 if line == '':
215 215 break
216 216 peek = pfp.readline()
217 217 if line.endswith('\n'):
218 218 line = line[:-1]
219 219 #ui.debug('state=%d line=%r\n' % (state, line))
220 220
221 221 if state == 0:
222 222 # initial state, consume input until we see 'RCS file'
223 223 match = re_00.match(line)
224 224 if match:
225 225 rcs = match.group(1)
226 226 tags = {}
227 227 if rlog:
228 228 filename = util.normpath(rcs[:-2])
229 229 if filename.startswith(prefix):
230 230 filename = filename[len(prefix):]
231 231 if filename.startswith('/'):
232 232 filename = filename[1:]
233 233 if filename.startswith('Attic/'):
234 234 filename = filename[6:]
235 235 else:
236 236 filename = filename.replace('/Attic/', '/')
237 237 state = 2
238 238 continue
239 239 state = 1
240 240 continue
241 241 match = re_01.match(line)
242 242 if match:
243 243 raise logerror(match.group(1))
244 244 match = re_02.match(line)
245 245 if match:
246 246 raise logerror(match.group(2))
247 247 if re_03.match(line):
248 248 raise logerror(line)
249 249
250 250 elif state == 1:
251 251 # expect 'Working file' (only when using log instead of rlog)
252 252 match = re_10.match(line)
253 253 assert match, _('RCS file must be followed by working file')
254 254 filename = util.normpath(match.group(1))
255 255 state = 2
256 256
257 257 elif state == 2:
258 258 # expect 'symbolic names'
259 259 if re_20.match(line):
260 260 branchmap = {}
261 261 state = 3
262 262
263 263 elif state == 3:
264 264 # read the symbolic names and store as tags
265 265 match = re_30.match(line)
266 266 if match:
267 267 rev = [int(x) for x in match.group(2).split('.')]
268 268
269 269 # Convert magic branch number to an odd-numbered one
270 270 revn = len(rev)
271 271 if revn > 3 and (revn % 2) == 0 and rev[-2] == 0:
272 272 rev = rev[:-2] + rev[-1:]
273 273 rev = tuple(rev)
274 274
275 275 if rev not in tags:
276 276 tags[rev] = []
277 277 tags[rev].append(match.group(1))
278 278 branchmap[match.group(1)] = match.group(2)
279 279
280 280 elif re_31.match(line):
281 281 state = 5
282 282 elif re_32.match(line):
283 283 state = 0
284 284
285 285 elif state == 4:
286 286 # expecting '------' separator before first revision
287 287 if re_31.match(line):
288 288 state = 5
289 289 else:
290 290 assert not re_32.match(line), _('must have at least '
291 291 'some revisions')
292 292
293 293 elif state == 5:
294 294 # expecting revision number and possibly (ignored) lock indication
295 295 # we create the logentry here from values stored in states 0 to 4,
296 296 # as this state is re-entered for subsequent revisions of a file.
297 297 match = re_50.match(line)
298 298 assert match, _('expected revision number')
299 299 e = logentry(rcs=scache(rcs), file=scache(filename),
300 300 revision=tuple([int(x) for x in match.group(1).split('.')]),
301 301 branches=[], parent=None)
302 302 state = 6
303 303
304 304 elif state == 6:
305 305 # expecting date, author, state, lines changed
306 306 match = re_60.match(line)
307 307 assert match, _('revision must be followed by date line')
308 308 d = match.group(1)
309 309 if d[2] == '/':
310 310 # Y2K
311 311 d = '19' + d
312 312
313 313 if len(d.split()) != 3:
314 314 # cvs log dates always in GMT
315 315 d = d + ' UTC'
316 316 e.date = util.parsedate(d, ['%y/%m/%d %H:%M:%S',
317 317 '%Y/%m/%d %H:%M:%S',
318 318 '%Y-%m-%d %H:%M:%S'])
319 319 e.author = scache(match.group(2))
320 320 e.dead = match.group(3).lower() == 'dead'
321 321
322 322 if match.group(5):
323 323 if match.group(6):
324 324 e.lines = (int(match.group(5)), int(match.group(6)))
325 325 else:
326 326 e.lines = (int(match.group(5)), 0)
327 327 elif match.group(6):
328 328 e.lines = (0, int(match.group(6)))
329 329 else:
330 330 e.lines = None
331 331
332 332 if match.group(7): # cvsnt mergepoint
333 333 myrev = match.group(8).split('.')
334 334 if len(myrev) == 2: # head
335 335 e.mergepoint = 'HEAD'
336 336 else:
337 337 myrev = '.'.join(myrev[:-2] + ['0', myrev[-2]])
338 338 branches = [b for b in branchmap if branchmap[b] == myrev]
339 339 assert len(branches) == 1, ('unknown branch: %s'
340 340 % e.mergepoint)
341 341 e.mergepoint = branches[0]
342 342 else:
343 343 e.mergepoint = None
344 344 e.comment = []
345 345 state = 7
346 346
347 347 elif state == 7:
348 348 # read the revision numbers of branches that start at this revision
349 349 # or store the commit log message otherwise
350 350 m = re_70.match(line)
351 351 if m:
352 352 e.branches = [tuple([int(y) for y in x.strip().split('.')])
353 353 for x in m.group(1).split(';')]
354 354 state = 8
355 355 elif re_31.match(line) and re_50.match(peek):
356 356 state = 5
357 357 store = True
358 358 elif re_32.match(line):
359 359 state = 0
360 360 store = True
361 361 else:
362 362 e.comment.append(line)
363 363
364 364 elif state == 8:
365 365 # store commit log message
366 366 if re_31.match(line):
367 367 cpeek = peek
368 368 if cpeek.endswith('\n'):
369 369 cpeek = cpeek[:-1]
370 370 if re_50.match(cpeek):
371 371 state = 5
372 372 store = True
373 373 else:
374 374 e.comment.append(line)
375 375 elif re_32.match(line):
376 376 state = 0
377 377 store = True
378 378 else:
379 379 e.comment.append(line)
380 380
381 381 # When a file is added on a branch B1, CVS creates a synthetic
382 382 # dead trunk revision 1.1 so that the branch has a root.
383 383 # Likewise, if you merge such a file to a later branch B2 (one
384 384 # that already existed when the file was added on B1), CVS
385 385 # creates a synthetic dead revision 1.1.x.1 on B2. Don't drop
386 386 # these revisions now, but mark them synthetic so
387 387 # createchangeset() can take care of them.
388 388 if (store and
389 389 e.dead and
390 390 e.revision[-1] == 1 and # 1.1 or 1.1.x.1
391 391 len(e.comment) == 1 and
392 392 file_added_re.match(e.comment[0])):
393 393 ui.debug('found synthetic revision in %s: %r\n'
394 394 % (e.rcs, e.comment[0]))
395 395 e.synthetic = True
396 396
397 397 if store:
398 398 # clean up the results and save in the log.
399 399 store = False
400 400 e.tags = sorted([scache(x) for x in tags.get(e.revision, [])])
401 401 e.comment = scache('\n'.join(e.comment))
402 402
403 403 revn = len(e.revision)
404 404 if revn > 3 and (revn % 2) == 0:
405 405 e.branch = tags.get(e.revision[:-1], [None])[0]
406 406 else:
407 407 e.branch = None
408 408
409 409 # find the branches starting from this revision
410 410 branchpoints = set()
411 411 for branch, revision in branchmap.iteritems():
412 412 revparts = tuple([int(i) for i in revision.split('.')])
413 413 if len(revparts) < 2: # bad tags
414 414 continue
415 415 if revparts[-2] == 0 and revparts[-1] % 2 == 0:
416 416 # normal branch
417 417 if revparts[:-2] == e.revision:
418 418 branchpoints.add(branch)
419 419 elif revparts == (1, 1, 1): # vendor branch
420 420 if revparts in e.branches:
421 421 branchpoints.add(branch)
422 422 e.branchpoints = branchpoints
423 423
424 424 log.append(e)
425 425
426 426 if len(log) % 100 == 0:
427 427 ui.status(util.ellipsis('%d %s' % (len(log), e.file), 80)+'\n')
428 428
429 429 log.sort(key=lambda x: (x.rcs, x.revision))
430 430
431 431 # find parent revisions of individual files
432 432 versions = {}
433 433 for e in log:
434 434 branch = e.revision[:-1]
435 435 p = versions.get((e.rcs, branch), None)
436 436 if p is None:
437 437 p = e.revision[:-2]
438 438 e.parent = p
439 439 versions[(e.rcs, branch)] = e.revision
440 440
441 441 # update the log cache
442 442 if cache:
443 443 if log:
444 444 # join up the old and new logs
445 445 log.sort(key=lambda x: x.date)
446 446
447 447 if oldlog and oldlog[-1].date >= log[0].date:
448 448 raise logerror(_('log cache overlaps with new log entries,'
449 449 ' re-run without cache.'))
450 450
451 451 log = oldlog + log
452 452
453 453 # write the new cachefile
454 454 ui.note(_('writing cvs log cache %s\n') % cachefile)
455 455 pickle.dump(log, open(cachefile, 'w'))
456 456 else:
457 457 log = oldlog
458 458
459 459 ui.status(_('%d log entries\n') % len(log))
460 460
461 461 hook.hook(ui, None, "cvslog", True, log=log)
462 462
463 463 return log
464 464
465 465
466 466 class changeset(object):
467 467 '''Class changeset has the following attributes:
468 468 .id - integer identifying this changeset (list index)
469 469 .author - author name as CVS knows it
470 470 .branch - name of branch this changeset is on, or None
471 471 .comment - commit message
472 472 .date - the commit date as a (time,tz) tuple
473 473 .entries - list of logentry objects in this changeset
474 474 .parents - list of one or two parent changesets
475 475 .tags - list of tags on this changeset
476 476 .synthetic - from synthetic revision "file ... added on branch ..."
477 477 .mergepoint- the branch that has been merged from
478 478 (if present in rlog output)
479 479 .branchpoints- the branches that start at the current entry
480 480 '''
481 481 def __init__(self, **entries):
482 482 self.synthetic = False
483 483 self.__dict__.update(entries)
484 484
485 485 def __repr__(self):
486 486 return "<%s at 0x%x: %s>" % (self.__class__.__name__,
487 487 id(self),
488 488 getattr(self, 'id', "(no id)"))
489 489
490 490 def createchangeset(ui, log, fuzz=60, mergefrom=None, mergeto=None):
491 491 '''Convert log into changesets.'''
492 492
493 493 ui.status(_('creating changesets\n'))
494 494
495 495 # Merge changesets
496 496
497 497 log.sort(key=lambda x: (x.comment, x.author, x.branch, x.date))
498 498
499 499 changesets = []
500 500 files = set()
501 501 c = None
502 502 for i, e in enumerate(log):
503 503
504 504 # Check if log entry belongs to the current changeset or not.
505 505
506 506 # Since CVS is file-centric, two different file revisions with
507 507 # different branchpoints should be treated as belonging to two
508 508 # different changesets (and the ordering is important and not
509 509 # honoured by cvsps at this point).
510 510 #
511 511 # Consider the following case:
512 512 # foo 1.1 branchpoints: [MYBRANCH]
513 513 # bar 1.1 branchpoints: [MYBRANCH, MYBRANCH2]
514 514 #
515 515 # Here foo is part only of MYBRANCH, but not MYBRANCH2, e.g. a
516 516 # later version of foo may be in MYBRANCH2, so foo should be the
517 517 # first changeset and bar the next and MYBRANCH and MYBRANCH2
518 518 # should both start off of the bar changeset. No provisions are
519 519 # made to ensure that this is, in fact, what happens.
520 520 if not (c and
521 521 e.comment == c.comment and
522 522 e.author == c.author and
523 523 e.branch == c.branch and
524 524 (not util.safehasattr(e, 'branchpoints') or
525 525 not util.safehasattr (c, 'branchpoints') or
526 526 e.branchpoints == c.branchpoints) and
527 527 ((c.date[0] + c.date[1]) <=
528 528 (e.date[0] + e.date[1]) <=
529 529 (c.date[0] + c.date[1]) + fuzz) and
530 530 e.file not in files):
531 531 c = changeset(comment=e.comment, author=e.author,
532 532 branch=e.branch, date=e.date, entries=[],
533 533 mergepoint=getattr(e, 'mergepoint', None),
534 534 branchpoints=getattr(e, 'branchpoints', set()))
535 535 changesets.append(c)
536 536 files = set()
537 537 if len(changesets) % 100 == 0:
538 538 t = '%d %s' % (len(changesets), repr(e.comment)[1:-1])
539 539 ui.status(util.ellipsis(t, 80) + '\n')
540 540
541 541 c.entries.append(e)
542 542 files.add(e.file)
543 543 c.date = e.date # changeset date is date of latest commit in it
544 544
545 545 # Mark synthetic changesets
546 546
547 547 for c in changesets:
548 548 # Synthetic revisions always get their own changeset, because
549 549 # the log message includes the filename. E.g. if you add file3
550 550 # and file4 on a branch, you get four log entries and three
551 551 # changesets:
552 552 # "File file3 was added on branch ..." (synthetic, 1 entry)
553 553 # "File file4 was added on branch ..." (synthetic, 1 entry)
554 554 # "Add file3 and file4 to fix ..." (real, 2 entries)
555 555 # Hence the check for 1 entry here.
556 556 c.synthetic = len(c.entries) == 1 and c.entries[0].synthetic
557 557
558 558 # Sort files in each changeset
559 559
560 560 def entitycompare(l, r):
561 561 'Mimic cvsps sorting order'
562 562 l = l.file.split('/')
563 563 r = r.file.split('/')
564 564 nl = len(l)
565 565 nr = len(r)
566 566 n = min(nl, nr)
567 567 for i in range(n):
568 568 if i + 1 == nl and nl < nr:
569 569 return -1
570 570 elif i + 1 == nr and nl > nr:
571 571 return +1
572 572 elif l[i] < r[i]:
573 573 return -1
574 574 elif l[i] > r[i]:
575 575 return +1
576 576 return 0
577 577
578 578 for c in changesets:
579 579 c.entries.sort(entitycompare)
580 580
581 581 # Sort changesets by date
582 582
583 583 def cscmp(l, r):
584 584 d = sum(l.date) - sum(r.date)
585 585 if d:
586 586 return d
587 587
588 588 # detect vendor branches and initial commits on a branch
589 589 le = {}
590 590 for e in l.entries:
591 591 le[e.rcs] = e.revision
592 592 re = {}
593 593 for e in r.entries:
594 594 re[e.rcs] = e.revision
595 595
596 596 d = 0
597 597 for e in l.entries:
598 598 if re.get(e.rcs, None) == e.parent:
599 599 assert not d
600 600 d = 1
601 601 break
602 602
603 603 for e in r.entries:
604 604 if le.get(e.rcs, None) == e.parent:
605 605 assert not d
606 606 d = -1
607 607 break
608 608
609 609 return d
610 610
611 611 changesets.sort(cscmp)
612 612
613 613 # Collect tags
614 614
615 615 globaltags = {}
616 616 for c in changesets:
617 617 for e in c.entries:
618 618 for tag in e.tags:
619 619 # remember which is the latest changeset to have this tag
620 620 globaltags[tag] = c
621 621
622 622 for c in changesets:
623 623 tags = set()
624 624 for e in c.entries:
625 625 tags.update(e.tags)
626 626 # remember tags only if this is the latest changeset to have it
627 627 c.tags = sorted(tag for tag in tags if globaltags[tag] is c)
628 628
629 629 # Find parent changesets, handle {{mergetobranch BRANCHNAME}}
630 630 # by inserting dummy changesets with two parents, and handle
631 631 # {{mergefrombranch BRANCHNAME}} by setting two parents.
632 632
633 633 if mergeto is None:
634 634 mergeto = r'{{mergetobranch ([-\w]+)}}'
635 635 if mergeto:
636 636 mergeto = re.compile(mergeto)
637 637
638 638 if mergefrom is None:
639 639 mergefrom = r'{{mergefrombranch ([-\w]+)}}'
640 640 if mergefrom:
641 641 mergefrom = re.compile(mergefrom)
642 642
643 643 versions = {} # changeset index where we saw any particular file version
644 644 branches = {} # changeset index where we saw a branch
645 645 n = len(changesets)
646 646 i = 0
647 647 while i < n:
648 648 c = changesets[i]
649 649
650 650 for f in c.entries:
651 651 versions[(f.rcs, f.revision)] = i
652 652
653 653 p = None
654 654 if c.branch in branches:
655 655 p = branches[c.branch]
656 656 else:
657 657 # first changeset on a new branch
658 658 # the parent is a changeset with the branch in its
659 659 # branchpoints such that it is the latest possible
660 660 # commit without any intervening, unrelated commits.
661 661
662 662 for candidate in xrange(i):
663 663 if c.branch not in changesets[candidate].branchpoints:
664 664 if p is not None:
665 665 break
666 666 continue
667 667 p = candidate
668 668
669 669 c.parents = []
670 670 if p is not None:
671 671 p = changesets[p]
672 672
673 673 # Ensure no changeset has a synthetic changeset as a parent.
674 674 while p.synthetic:
675 675 assert len(p.parents) <= 1, \
676 676 _('synthetic changeset cannot have multiple parents')
677 677 if p.parents:
678 678 p = p.parents[0]
679 679 else:
680 680 p = None
681 681 break
682 682
683 683 if p is not None:
684 684 c.parents.append(p)
685 685
686 686 if c.mergepoint:
687 687 if c.mergepoint == 'HEAD':
688 688 c.mergepoint = None
689 689 c.parents.append(changesets[branches[c.mergepoint]])
690 690
691 691 if mergefrom:
692 692 m = mergefrom.search(c.comment)
693 693 if m:
694 694 m = m.group(1)
695 695 if m == 'HEAD':
696 696 m = None
697 697 try:
698 698 candidate = changesets[branches[m]]
699 699 except KeyError:
700 700 ui.warn(_("warning: CVS commit message references "
701 701 "non-existent branch %r:\n%s\n")
702 702 % (m, c.comment))
703 703 if m in branches and c.branch != m and not candidate.synthetic:
704 704 c.parents.append(candidate)
705 705
706 706 if mergeto:
707 707 m = mergeto.search(c.comment)
708 708 if m:
709 709 if m.groups():
710 710 m = m.group(1)
711 711 if m == 'HEAD':
712 712 m = None
713 713 else:
714 714 m = None # if no group found then merge to HEAD
715 715 if m in branches and c.branch != m:
716 716 # insert empty changeset for merge
717 717 cc = changeset(
718 718 author=c.author, branch=m, date=c.date,
719 719 comment='convert-repo: CVS merge from branch %s'
720 720 % c.branch,
721 721 entries=[], tags=[],
722 722 parents=[changesets[branches[m]], c])
723 723 changesets.insert(i + 1, cc)
724 724 branches[m] = i + 1
725 725
726 726 # adjust our loop counters now we have inserted a new entry
727 727 n += 1
728 728 i += 2
729 729 continue
730 730
731 731 branches[c.branch] = i
732 732 i += 1
733 733
734 734 # Drop synthetic changesets (safe now that we have ensured no other
735 735 # changesets can have them as parents).
736 736 i = 0
737 737 while i < len(changesets):
738 738 if changesets[i].synthetic:
739 739 del changesets[i]
740 740 else:
741 741 i += 1
742 742
743 743 # Number changesets
744 744
745 745 for i, c in enumerate(changesets):
746 746 c.id = i + 1
747 747
748 748 ui.status(_('%d changeset entries\n') % len(changesets))
749 749
750 750 hook.hook(ui, None, "cvschangesets", True, changesets=changesets)
751 751
752 752 return changesets
753 753
754 754
755 755 def debugcvsps(ui, *args, **opts):
756 756 '''Read CVS rlog for current directory or named path in
757 757 repository, and convert the log to changesets based on matching
758 758 commit log entries and dates.
759 759 '''
760 760 if opts["new_cache"]:
761 761 cache = "write"
762 762 elif opts["update_cache"]:
763 763 cache = "update"
764 764 else:
765 765 cache = None
766 766
767 767 revisions = opts["revisions"]
768 768
769 769 try:
770 770 if args:
771 771 log = []
772 772 for d in args:
773 773 log += createlog(ui, d, root=opts["root"], cache=cache)
774 774 else:
775 775 log = createlog(ui, root=opts["root"], cache=cache)
776 776 except logerror, e:
777 777 ui.write("%r\n"%e)
778 778 return
779 779
780 780 changesets = createchangeset(ui, log, opts["fuzz"])
781 781 del log
782 782
783 783 # Print changesets (optionally filtered)
784 784
785 785 off = len(revisions)
786 786 branches = {} # latest version number in each branch
787 787 ancestors = {} # parent branch
788 788 for cs in changesets:
789 789
790 790 if opts["ancestors"]:
791 791 if cs.branch not in branches and cs.parents and cs.parents[0].id:
792 792 ancestors[cs.branch] = (changesets[cs.parents[0].id - 1].branch,
793 793 cs.parents[0].id)
794 794 branches[cs.branch] = cs.id
795 795
796 796 # limit by branches
797 797 if opts["branches"] and (cs.branch or 'HEAD') not in opts["branches"]:
798 798 continue
799 799
800 800 if not off:
801 801 # Note: trailing spaces on several lines here are needed to have
802 802 # bug-for-bug compatibility with cvsps.
803 803 ui.write('---------------------\n')
804 ui.write('PatchSet %d \n' % cs.id)
805 ui.write('Date: %s\n' % util.datestr(cs.date,
806 '%Y/%m/%d %H:%M:%S %1%2'))
807 ui.write('Author: %s\n' % cs.author)
808 ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
809 ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
810 ','.join(cs.tags) or '(none)'))
804 ui.write(('PatchSet %d \n' % cs.id))
805 ui.write(('Date: %s\n' % util.datestr(cs.date,
806 '%Y/%m/%d %H:%M:%S %1%2')))
807 ui.write(('Author: %s\n' % cs.author))
808 ui.write(('Branch: %s\n' % (cs.branch or 'HEAD')))
809 ui.write(('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
810 ','.join(cs.tags) or '(none)')))
811 811 branchpoints = getattr(cs, 'branchpoints', None)
812 812 if branchpoints:
813 ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
813 ui.write(('Branchpoints: %s \n' % ', '.join(branchpoints)))
814 814 if opts["parents"] and cs.parents:
815 815 if len(cs.parents) > 1:
816 ui.write('Parents: %s\n' %
817 (','.join([str(p.id) for p in cs.parents])))
816 ui.write(('Parents: %s\n' %
817 (','.join([str(p.id) for p in cs.parents]))))
818 818 else:
819 ui.write('Parent: %d\n' % cs.parents[0].id)
819 ui.write(('Parent: %d\n' % cs.parents[0].id))
820 820
821 821 if opts["ancestors"]:
822 822 b = cs.branch
823 823 r = []
824 824 while b:
825 825 b, c = ancestors[b]
826 826 r.append('%s:%d:%d' % (b or "HEAD", c, branches[b]))
827 827 if r:
828 ui.write('Ancestors: %s\n' % (','.join(r)))
828 ui.write(('Ancestors: %s\n' % (','.join(r))))
829 829
830 ui.write('Log:\n')
830 ui.write(('Log:\n'))
831 831 ui.write('%s\n\n' % cs.comment)
832 ui.write('Members: \n')
832 ui.write(('Members: \n'))
833 833 for f in cs.entries:
834 834 fn = f.file
835 835 if fn.startswith(opts["prefix"]):
836 836 fn = fn[len(opts["prefix"]):]
837 837 ui.write('\t%s:%s->%s%s \n' % (
838 838 fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL',
839 839 '.'.join([str(x) for x in f.revision]),
840 840 ['', '(DEAD)'][f.dead]))
841 841 ui.write('\n')
842 842
843 843 # have we seen the start tag?
844 844 if revisions and off:
845 845 if revisions[0] == str(cs.id) or \
846 846 revisions[0] in cs.tags:
847 847 off = False
848 848
849 849 # see if we reached the end tag
850 850 if len(revisions) > 1 and not off:
851 851 if revisions[1] == str(cs.id) or \
852 852 revisions[1] in cs.tags:
853 853 break
@@ -1,354 +1,354
1 1 # Minimal support for git commands on an hg repository
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''browse the repository in a graphical way
9 9
10 10 The hgk extension allows browsing the history of a repository in a
11 11 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not
12 12 distributed with Mercurial.)
13 13
14 14 hgk consists of two parts: a Tcl script that does the displaying and
15 15 querying of information, and an extension to Mercurial named hgk.py,
16 16 which provides hooks for hgk to get information. hgk can be found in
17 17 the contrib directory, and the extension is shipped in the hgext
18 18 repository, and needs to be enabled.
19 19
20 20 The :hg:`view` command will launch the hgk Tcl script. For this command
21 21 to work, hgk must be in your search path. Alternately, you can specify
22 22 the path to hgk in your configuration file::
23 23
24 24 [hgk]
25 25 path=/location/of/hgk
26 26
27 27 hgk can make use of the extdiff extension to visualize revisions.
28 28 Assuming you had already configured extdiff vdiff command, just add::
29 29
30 30 [hgk]
31 31 vdiff=vdiff
32 32
33 33 Revisions context menu will now display additional entries to fire
34 34 vdiff on hovered and selected revisions.
35 35 '''
36 36
37 37 import os
38 38 from mercurial import commands, util, patch, revlog, scmutil
39 39 from mercurial.node import nullid, nullrev, short
40 40 from mercurial.i18n import _
41 41
42 42 testedwith = 'internal'
43 43
44 44 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
45 45 """diff trees from two commits"""
46 46 def __difftree(repo, node1, node2, files=[]):
47 47 assert node2 is not None
48 48 mmap = repo[node1].manifest()
49 49 mmap2 = repo[node2].manifest()
50 50 m = scmutil.match(repo[node1], files)
51 51 modified, added, removed = repo.status(node1, node2, m)[:3]
52 52 empty = short(nullid)
53 53
54 54 for f in modified:
55 55 # TODO get file permissions
56 56 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
57 57 (short(mmap[f]), short(mmap2[f]), f, f))
58 58 for f in added:
59 59 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
60 60 (empty, short(mmap2[f]), f, f))
61 61 for f in removed:
62 62 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
63 63 (short(mmap[f]), empty, f, f))
64 64 ##
65 65
66 66 while True:
67 67 if opts['stdin']:
68 68 try:
69 69 line = raw_input().split(' ')
70 70 node1 = line[0]
71 71 if len(line) > 1:
72 72 node2 = line[1]
73 73 else:
74 74 node2 = None
75 75 except EOFError:
76 76 break
77 77 node1 = repo.lookup(node1)
78 78 if node2:
79 79 node2 = repo.lookup(node2)
80 80 else:
81 81 node2 = node1
82 82 node1 = repo.changelog.parents(node1)[0]
83 83 if opts['patch']:
84 84 if opts['pretty']:
85 85 catcommit(ui, repo, node2, "")
86 86 m = scmutil.match(repo[node1], files)
87 87 chunks = patch.diff(repo, node1, node2, match=m,
88 88 opts=patch.diffopts(ui, {'git': True}))
89 89 for chunk in chunks:
90 90 ui.write(chunk)
91 91 else:
92 92 __difftree(repo, node1, node2, files=files)
93 93 if not opts['stdin']:
94 94 break
95 95
96 96 def catcommit(ui, repo, n, prefix, ctx=None):
97 97 nlprefix = '\n' + prefix
98 98 if ctx is None:
99 99 ctx = repo[n]
100 100 # use ctx.node() instead ??
101 ui.write("tree %s\n" % short(ctx.changeset()[0]))
101 ui.write(("tree %s\n" % short(ctx.changeset()[0])))
102 102 for p in ctx.parents():
103 ui.write("parent %s\n" % p)
103 ui.write(("parent %s\n" % p))
104 104
105 105 date = ctx.date()
106 106 description = ctx.description().replace("\0", "")
107 107 lines = description.splitlines()
108 108 if lines and lines[-1].startswith('committer:'):
109 109 committer = lines[-1].split(': ')[1].rstrip()
110 110 else:
111 111 committer = ctx.user()
112 112
113 ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
114 ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
115 ui.write("revision %d\n" % ctx.rev())
116 ui.write("branch %s\n\n" % ctx.branch())
113 ui.write(("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1])))
114 ui.write(("committer %s %s %s\n" % (committer, int(date[0]), date[1])))
115 ui.write(("revision %d\n" % ctx.rev()))
116 ui.write(("branch %s\n\n" % ctx.branch()))
117 117
118 118 if prefix != "":
119 119 ui.write("%s%s\n" % (prefix,
120 120 description.replace('\n', nlprefix).strip()))
121 121 else:
122 122 ui.write(description + "\n")
123 123 if prefix:
124 124 ui.write('\0')
125 125
126 126 def base(ui, repo, node1, node2):
127 127 """output common ancestor information"""
128 128 node1 = repo.lookup(node1)
129 129 node2 = repo.lookup(node2)
130 130 n = repo.changelog.ancestor(node1, node2)
131 131 ui.write(short(n) + "\n")
132 132
133 133 def catfile(ui, repo, type=None, r=None, **opts):
134 134 """cat a specific revision"""
135 135 # in stdin mode, every line except the commit is prefixed with two
136 136 # spaces. This way the our caller can find the commit without magic
137 137 # strings
138 138 #
139 139 prefix = ""
140 140 if opts['stdin']:
141 141 try:
142 142 (type, r) = raw_input().split(' ')
143 143 prefix = " "
144 144 except EOFError:
145 145 return
146 146
147 147 else:
148 148 if not type or not r:
149 149 ui.warn(_("cat-file: type or revision not supplied\n"))
150 150 commands.help_(ui, 'cat-file')
151 151
152 152 while r:
153 153 if type != "commit":
154 154 ui.warn(_("aborting hg cat-file only understands commits\n"))
155 155 return 1
156 156 n = repo.lookup(r)
157 157 catcommit(ui, repo, n, prefix)
158 158 if opts['stdin']:
159 159 try:
160 160 (type, r) = raw_input().split(' ')
161 161 except EOFError:
162 162 break
163 163 else:
164 164 break
165 165
166 166 # git rev-tree is a confusing thing. You can supply a number of
167 167 # commit sha1s on the command line, and it walks the commit history
168 168 # telling you which commits are reachable from the supplied ones via
169 169 # a bitmask based on arg position.
170 170 # you can specify a commit to stop at by starting the sha1 with ^
171 171 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
172 172 def chlogwalk():
173 173 count = len(repo)
174 174 i = count
175 175 l = [0] * 100
176 176 chunk = 100
177 177 while True:
178 178 if chunk > i:
179 179 chunk = i
180 180 i = 0
181 181 else:
182 182 i -= chunk
183 183
184 184 for x in xrange(chunk):
185 185 if i + x >= count:
186 186 l[chunk - x:] = [0] * (chunk - x)
187 187 break
188 188 if full is not None:
189 189 l[x] = repo[i + x]
190 190 l[x].changeset() # force reading
191 191 else:
192 192 l[x] = 1
193 193 for x in xrange(chunk - 1, -1, -1):
194 194 if l[x] != 0:
195 195 yield (i + x, full is not None and l[x] or None)
196 196 if i == 0:
197 197 break
198 198
199 199 # calculate and return the reachability bitmask for sha
200 200 def is_reachable(ar, reachable, sha):
201 201 if len(ar) == 0:
202 202 return 1
203 203 mask = 0
204 204 for i in xrange(len(ar)):
205 205 if sha in reachable[i]:
206 206 mask |= 1 << i
207 207
208 208 return mask
209 209
210 210 reachable = []
211 211 stop_sha1 = []
212 212 want_sha1 = []
213 213 count = 0
214 214
215 215 # figure out which commits they are asking for and which ones they
216 216 # want us to stop on
217 217 for i, arg in enumerate(args):
218 218 if arg.startswith('^'):
219 219 s = repo.lookup(arg[1:])
220 220 stop_sha1.append(s)
221 221 want_sha1.append(s)
222 222 elif arg != 'HEAD':
223 223 want_sha1.append(repo.lookup(arg))
224 224
225 225 # calculate the graph for the supplied commits
226 226 for i, n in enumerate(want_sha1):
227 227 reachable.append(set())
228 228 visit = [n]
229 229 reachable[i].add(n)
230 230 while visit:
231 231 n = visit.pop(0)
232 232 if n in stop_sha1:
233 233 continue
234 234 for p in repo.changelog.parents(n):
235 235 if p not in reachable[i]:
236 236 reachable[i].add(p)
237 237 visit.append(p)
238 238 if p in stop_sha1:
239 239 continue
240 240
241 241 # walk the repository looking for commits that are in our
242 242 # reachability graph
243 243 for i, ctx in chlogwalk():
244 244 n = repo.changelog.node(i)
245 245 mask = is_reachable(want_sha1, reachable, n)
246 246 if mask:
247 247 parentstr = ""
248 248 if parents:
249 249 pp = repo.changelog.parents(n)
250 250 if pp[0] != nullid:
251 251 parentstr += " " + short(pp[0])
252 252 if pp[1] != nullid:
253 253 parentstr += " " + short(pp[1])
254 254 if not full:
255 255 ui.write("%s%s\n" % (short(n), parentstr))
256 256 elif full == "commit":
257 257 ui.write("%s%s\n" % (short(n), parentstr))
258 258 catcommit(ui, repo, n, ' ', ctx)
259 259 else:
260 260 (p1, p2) = repo.changelog.parents(n)
261 261 (h, h1, h2) = map(short, (n, p1, p2))
262 262 (i1, i2) = map(repo.changelog.rev, (p1, p2))
263 263
264 264 date = ctx.date()[0]
265 265 ui.write("%s %s:%s" % (date, h, mask))
266 266 mask = is_reachable(want_sha1, reachable, p1)
267 267 if i1 != nullrev and mask > 0:
268 268 ui.write("%s:%s " % (h1, mask)),
269 269 mask = is_reachable(want_sha1, reachable, p2)
270 270 if i2 != nullrev and mask > 0:
271 271 ui.write("%s:%s " % (h2, mask))
272 272 ui.write("\n")
273 273 if maxnr and count >= maxnr:
274 274 break
275 275 count += 1
276 276
277 277 def revparse(ui, repo, *revs, **opts):
278 278 """parse given revisions"""
279 279 def revstr(rev):
280 280 if rev == 'HEAD':
281 281 rev = 'tip'
282 282 return revlog.hex(repo.lookup(rev))
283 283
284 284 for r in revs:
285 285 revrange = r.split(':', 1)
286 286 ui.write('%s\n' % revstr(revrange[0]))
287 287 if len(revrange) == 2:
288 288 ui.write('^%s\n' % revstr(revrange[1]))
289 289
290 290 # git rev-list tries to order things by date, and has the ability to stop
291 291 # at a given commit without walking the whole repo. TODO add the stop
292 292 # parameter
293 293 def revlist(ui, repo, *revs, **opts):
294 294 """print revisions"""
295 295 if opts['header']:
296 296 full = "commit"
297 297 else:
298 298 full = None
299 299 copy = [x for x in revs]
300 300 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
301 301
302 302 def config(ui, repo, **opts):
303 303 """print extension options"""
304 304 def writeopt(name, value):
305 ui.write('k=%s\nv=%s\n' % (name, value))
305 ui.write(('k=%s\nv=%s\n' % (name, value)))
306 306
307 307 writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
308 308
309 309
310 310 def view(ui, repo, *etc, **opts):
311 311 "start interactive history viewer"
312 312 os.chdir(repo.root)
313 313 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
314 314 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
315 315 ui.debug("running %s\n" % cmd)
316 316 util.system(cmd)
317 317
318 318 cmdtable = {
319 319 "^view":
320 320 (view,
321 321 [('l', 'limit', '',
322 322 _('limit number of changes displayed'), _('NUM'))],
323 323 _('hg view [-l LIMIT] [REVRANGE]')),
324 324 "debug-diff-tree":
325 325 (difftree,
326 326 [('p', 'patch', None, _('generate patch')),
327 327 ('r', 'recursive', None, _('recursive')),
328 328 ('P', 'pretty', None, _('pretty')),
329 329 ('s', 'stdin', None, _('stdin')),
330 330 ('C', 'copy', None, _('detect copies')),
331 331 ('S', 'search', "", _('search'))],
332 332 _('hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...')),
333 333 "debug-cat-file":
334 334 (catfile,
335 335 [('s', 'stdin', None, _('stdin'))],
336 336 _('hg debug-cat-file [OPTION]... TYPE FILE')),
337 337 "debug-config":
338 338 (config, [], _('hg debug-config')),
339 339 "debug-merge-base":
340 340 (base, [], _('hg debug-merge-base REV REV')),
341 341 "debug-rev-parse":
342 342 (revparse,
343 343 [('', 'default', '', _('ignored'))],
344 344 _('hg debug-rev-parse REV')),
345 345 "debug-rev-list":
346 346 (revlist,
347 347 [('H', 'header', None, _('header')),
348 348 ('t', 'topo-order', None, _('topo-order')),
349 349 ('p', 'parents', None, _('parents')),
350 350 ('n', 'max-count', 0, _('max-count'))],
351 351 _('hg debug-rev-list [OPTION]... REV...')),
352 352 }
353 353
354 354 commands.inferrepo += " debug-diff-tree debug-cat-file"
@@ -1,555 +1,555
1 1 # patchbomb.py - sending Mercurial changesets as patch emails
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to send changesets as (a series of) patch emails
9 9
10 10 The series is started off with a "[PATCH 0 of N]" introduction, which
11 11 describes the series as a whole.
12 12
13 13 Each patch email has a Subject line of "[PATCH M of N] ...", using the
14 14 first line of the changeset description as the subject text. The
15 15 message contains two or three body parts:
16 16
17 17 - The changeset description.
18 18 - [Optional] The result of running diffstat on the patch.
19 19 - The patch itself, as generated by :hg:`export`.
20 20
21 21 Each message refers to the first in the series using the In-Reply-To
22 22 and References headers, so they will show up as a sequence in threaded
23 23 mail and news readers, and in mail archives.
24 24
25 25 To configure other defaults, add a section like this to your
26 26 configuration file::
27 27
28 28 [email]
29 29 from = My Name <my@email>
30 30 to = recipient1, recipient2, ...
31 31 cc = cc1, cc2, ...
32 32 bcc = bcc1, bcc2, ...
33 33 reply-to = address1, address2, ...
34 34
35 35 Use ``[patchbomb]`` as configuration section name if you need to
36 36 override global ``[email]`` address settings.
37 37
38 38 Then you can use the :hg:`email` command to mail a series of
39 39 changesets as a patchbomb.
40 40
41 41 You can also either configure the method option in the email section
42 42 to be a sendmail compatible mailer or fill out the [smtp] section so
43 43 that the patchbomb extension can automatically send patchbombs
44 44 directly from the commandline. See the [email] and [smtp] sections in
45 45 hgrc(5) for details.
46 46 '''
47 47
48 48 import os, errno, socket, tempfile, cStringIO
49 49 import email.MIMEMultipart, email.MIMEBase
50 50 import email.Utils, email.Encoders, email.Generator
51 51 from mercurial import cmdutil, commands, hg, mail, patch, util
52 52 from mercurial import scmutil
53 53 from mercurial.i18n import _
54 54 from mercurial.node import bin
55 55
56 56 cmdtable = {}
57 57 command = cmdutil.command(cmdtable)
58 58 testedwith = 'internal'
59 59
60 60 def prompt(ui, prompt, default=None, rest=':'):
61 61 if default:
62 62 prompt += ' [%s]' % default
63 63 return ui.prompt(prompt + rest, default)
64 64
65 65 def introwanted(opts, number):
66 66 '''is an introductory message apparently wanted?'''
67 67 return number > 1 or opts.get('intro') or opts.get('desc')
68 68
69 69 def makepatch(ui, repo, patchlines, opts, _charsets, idx, total, numbered,
70 70 patchname=None):
71 71
72 72 desc = []
73 73 node = None
74 74 body = ''
75 75
76 76 for line in patchlines:
77 77 if line.startswith('#'):
78 78 if line.startswith('# Node ID'):
79 79 node = line.split()[-1]
80 80 continue
81 81 if line.startswith('diff -r') or line.startswith('diff --git'):
82 82 break
83 83 desc.append(line)
84 84
85 85 if not patchname and not node:
86 86 raise ValueError
87 87
88 88 if opts.get('attach') and not opts.get('body'):
89 89 body = ('\n'.join(desc[1:]).strip() or
90 90 'Patch subject is complete summary.')
91 91 body += '\n\n\n'
92 92
93 93 if opts.get('plain'):
94 94 while patchlines and patchlines[0].startswith('# '):
95 95 patchlines.pop(0)
96 96 if patchlines:
97 97 patchlines.pop(0)
98 98 while patchlines and not patchlines[0].strip():
99 99 patchlines.pop(0)
100 100
101 101 ds = patch.diffstat(patchlines, git=opts.get('git'))
102 102 if opts.get('diffstat'):
103 103 body += ds + '\n\n'
104 104
105 105 addattachment = opts.get('attach') or opts.get('inline')
106 106 if not addattachment or opts.get('body'):
107 107 body += '\n'.join(patchlines)
108 108
109 109 if addattachment:
110 110 msg = email.MIMEMultipart.MIMEMultipart()
111 111 if body:
112 112 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
113 113 p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch',
114 114 opts.get('test'))
115 115 binnode = bin(node)
116 116 # if node is mq patch, it will have the patch file's name as a tag
117 117 if not patchname:
118 118 patchtags = [t for t in repo.nodetags(binnode)
119 119 if t.endswith('.patch') or t.endswith('.diff')]
120 120 if patchtags:
121 121 patchname = patchtags[0]
122 122 elif total > 1:
123 123 patchname = cmdutil.makefilename(repo, '%b-%n.patch',
124 124 binnode, seqno=idx,
125 125 total=total)
126 126 else:
127 127 patchname = cmdutil.makefilename(repo, '%b.patch', binnode)
128 128 disposition = 'inline'
129 129 if opts.get('attach'):
130 130 disposition = 'attachment'
131 131 p['Content-Disposition'] = disposition + '; filename=' + patchname
132 132 msg.attach(p)
133 133 else:
134 134 msg = mail.mimetextpatch(body, display=opts.get('test'))
135 135
136 136 flag = ' '.join(opts.get('flag'))
137 137 if flag:
138 138 flag = ' ' + flag
139 139
140 140 subj = desc[0].strip().rstrip('. ')
141 141 if not numbered:
142 142 subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
143 143 else:
144 144 tlen = len(str(total))
145 145 subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj)
146 146 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
147 147 msg['X-Mercurial-Node'] = node
148 148 return msg, subj, ds
149 149
150 150 emailopts = [
151 151 ('', 'body', None, _('send patches as inline message text (default)')),
152 152 ('a', 'attach', None, _('send patches as attachments')),
153 153 ('i', 'inline', None, _('send patches as inline attachments')),
154 154 ('', 'bcc', [], _('email addresses of blind carbon copy recipients')),
155 155 ('c', 'cc', [], _('email addresses of copy recipients')),
156 156 ('', 'confirm', None, _('ask for confirmation before sending')),
157 157 ('d', 'diffstat', None, _('add diffstat output to messages')),
158 158 ('', 'date', '', _('use the given date as the sending date')),
159 159 ('', 'desc', '', _('use the given file as the series description')),
160 160 ('f', 'from', '', _('email address of sender')),
161 161 ('n', 'test', None, _('print messages that would be sent')),
162 162 ('m', 'mbox', '', _('write messages to mbox file instead of sending them')),
163 163 ('', 'reply-to', [], _('email addresses replies should be sent to')),
164 164 ('s', 'subject', '', _('subject of first message (intro or single patch)')),
165 165 ('', 'in-reply-to', '', _('message identifier to reply to')),
166 166 ('', 'flag', [], _('flags to add in subject prefixes')),
167 167 ('t', 'to', [], _('email addresses of recipients'))]
168 168
169 169 @command('email',
170 170 [('g', 'git', None, _('use git extended diff format')),
171 171 ('', 'plain', None, _('omit hg patch header')),
172 172 ('o', 'outgoing', None,
173 173 _('send changes not found in the target repository')),
174 174 ('b', 'bundle', None, _('send changes not in target as a binary bundle')),
175 175 ('', 'bundlename', 'bundle',
176 176 _('name of the bundle attachment file'), _('NAME')),
177 177 ('r', 'rev', [], _('a revision to send'), _('REV')),
178 178 ('', 'force', None, _('run even when remote repository is unrelated '
179 179 '(with -b/--bundle)')),
180 180 ('', 'base', [], _('a base changeset to specify instead of a destination '
181 181 '(with -b/--bundle)'), _('REV')),
182 182 ('', 'intro', None, _('send an introduction email for a single patch')),
183 183 ] + emailopts + commands.remoteopts,
184 184 _('hg email [OPTION]... [DEST]...'))
185 185 def patchbomb(ui, repo, *revs, **opts):
186 186 '''send changesets by email
187 187
188 188 By default, diffs are sent in the format generated by
189 189 :hg:`export`, one per message. The series starts with a "[PATCH 0
190 190 of N]" introduction, which describes the series as a whole.
191 191
192 192 Each patch email has a Subject line of "[PATCH M of N] ...", using
193 193 the first line of the changeset description as the subject text.
194 194 The message contains two or three parts. First, the changeset
195 195 description.
196 196
197 197 With the -d/--diffstat option, if the diffstat program is
198 198 installed, the result of running diffstat on the patch is inserted.
199 199
200 200 Finally, the patch itself, as generated by :hg:`export`.
201 201
202 202 With the -d/--diffstat or --confirm options, you will be presented
203 203 with a final summary of all messages and asked for confirmation before
204 204 the messages are sent.
205 205
206 206 By default the patch is included as text in the email body for
207 207 easy reviewing. Using the -a/--attach option will instead create
208 208 an attachment for the patch. With -i/--inline an inline attachment
209 209 will be created. You can include a patch both as text in the email
210 210 body and as a regular or an inline attachment by combining the
211 211 -a/--attach or -i/--inline with the --body option.
212 212
213 213 With -o/--outgoing, emails will be generated for patches not found
214 214 in the destination repository (or only those which are ancestors
215 215 of the specified revisions if any are provided)
216 216
217 217 With -b/--bundle, changesets are selected as for --outgoing, but a
218 218 single email containing a binary Mercurial bundle as an attachment
219 219 will be sent.
220 220
221 221 With -m/--mbox, instead of previewing each patchbomb message in a
222 222 pager or sending the messages directly, it will create a UNIX
223 223 mailbox file with the patch emails. This mailbox file can be
224 224 previewed with any mail user agent which supports UNIX mbox
225 225 files.
226 226
227 227 With -n/--test, all steps will run, but mail will not be sent.
228 228 You will be prompted for an email recipient address, a subject and
229 229 an introductory message describing the patches of your patchbomb.
230 230 Then when all is done, patchbomb messages are displayed. If the
231 231 PAGER environment variable is set, your pager will be fired up once
232 232 for each patchbomb message, so you can verify everything is alright.
233 233
234 234 In case email sending fails, you will find a backup of your series
235 235 introductory message in ``.hg/last-email.txt``.
236 236
237 237 Examples::
238 238
239 239 hg email -r 3000 # send patch 3000 only
240 240 hg email -r 3000 -r 3001 # send patches 3000 and 3001
241 241 hg email -r 3000:3005 # send patches 3000 through 3005
242 242 hg email 3000 # send patch 3000 (deprecated)
243 243
244 244 hg email -o # send all patches not in default
245 245 hg email -o DEST # send all patches not in DEST
246 246 hg email -o -r 3000 # send all ancestors of 3000 not in default
247 247 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
248 248
249 249 hg email -b # send bundle of all patches not in default
250 250 hg email -b DEST # send bundle of all patches not in DEST
251 251 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
252 252 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
253 253
254 254 hg email -o -m mbox && # generate an mbox file...
255 255 mutt -R -f mbox # ... and view it with mutt
256 256 hg email -o -m mbox && # generate an mbox file ...
257 257 formail -s sendmail \\ # ... and use formail to send from the mbox
258 258 -bm -t < mbox # ... using sendmail
259 259
260 260 Before using this command, you will need to enable email in your
261 261 hgrc. See the [email] section in hgrc(5) for details.
262 262 '''
263 263
264 264 _charsets = mail._charsets(ui)
265 265
266 266 bundle = opts.get('bundle')
267 267 date = opts.get('date')
268 268 mbox = opts.get('mbox')
269 269 outgoing = opts.get('outgoing')
270 270 rev = opts.get('rev')
271 271 # internal option used by pbranches
272 272 patches = opts.get('patches')
273 273
274 274 def getoutgoing(dest, revs):
275 275 '''Return the revisions present locally but not in dest'''
276 276 url = ui.expandpath(dest or 'default-push', dest or 'default')
277 277 url = hg.parseurl(url)[0]
278 278 ui.status(_('comparing with %s\n') % util.hidepassword(url))
279 279
280 280 revs = [r for r in scmutil.revrange(repo, revs) if r >= 0]
281 281 if not revs:
282 282 revs = [len(repo) - 1]
283 283 revs = repo.revs('outgoing(%s) and ::%ld', dest or '', revs)
284 284 if not revs:
285 285 ui.status(_("no changes found\n"))
286 286 return []
287 287 return [str(r) for r in revs]
288 288
289 289 def getpatches(revs):
290 290 for r in scmutil.revrange(repo, revs):
291 291 output = cStringIO.StringIO()
292 292 cmdutil.export(repo, [r], fp=output,
293 293 opts=patch.diffopts(ui, opts))
294 294 yield output.getvalue().split('\n')
295 295
296 296 def getbundle(dest):
297 297 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
298 298 tmpfn = os.path.join(tmpdir, 'bundle')
299 299 try:
300 300 commands.bundle(ui, repo, tmpfn, dest, **opts)
301 301 fp = open(tmpfn, 'rb')
302 302 data = fp.read()
303 303 fp.close()
304 304 return data
305 305 finally:
306 306 try:
307 307 os.unlink(tmpfn)
308 308 except OSError:
309 309 pass
310 310 os.rmdir(tmpdir)
311 311
312 312 if not (opts.get('test') or mbox):
313 313 # really sending
314 314 mail.validateconfig(ui)
315 315
316 316 if not (revs or rev or outgoing or bundle or patches):
317 317 raise util.Abort(_('specify at least one changeset with -r or -o'))
318 318
319 319 if outgoing and bundle:
320 320 raise util.Abort(_("--outgoing mode always on with --bundle;"
321 321 " do not re-specify --outgoing"))
322 322
323 323 if outgoing or bundle:
324 324 if len(revs) > 1:
325 325 raise util.Abort(_("too many destinations"))
326 326 dest = revs and revs[0] or None
327 327 revs = []
328 328
329 329 if rev:
330 330 if revs:
331 331 raise util.Abort(_('use only one form to specify the revision'))
332 332 revs = rev
333 333
334 334 if outgoing:
335 335 revs = getoutgoing(dest, rev)
336 336 if bundle:
337 337 opts['revs'] = revs
338 338
339 339 # start
340 340 if date:
341 341 start_time = util.parsedate(date)
342 342 else:
343 343 start_time = util.makedate()
344 344
345 345 def genmsgid(id):
346 346 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
347 347
348 348 def getdescription(body, sender):
349 349 if opts.get('desc'):
350 350 body = open(opts.get('desc')).read()
351 351 else:
352 352 ui.write(_('\nWrite the introductory message for the '
353 353 'patch series.\n\n'))
354 354 body = ui.edit(body, sender)
355 355 # Save series description in case sendmail fails
356 356 msgfile = repo.opener('last-email.txt', 'wb')
357 357 msgfile.write(body)
358 358 msgfile.close()
359 359 return body
360 360
361 361 def getpatchmsgs(patches, patchnames=None):
362 362 msgs = []
363 363
364 364 ui.write(_('this patch series consists of %d patches.\n\n')
365 365 % len(patches))
366 366
367 367 # build the intro message, or skip it if the user declines
368 368 if introwanted(opts, len(patches)):
369 369 msg = makeintro(patches)
370 370 if msg:
371 371 msgs.append(msg)
372 372
373 373 # are we going to send more than one message?
374 374 numbered = len(msgs) + len(patches) > 1
375 375
376 376 # now generate the actual patch messages
377 377 name = None
378 378 for i, p in enumerate(patches):
379 379 if patchnames:
380 380 name = patchnames[i]
381 381 msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
382 382 len(patches), numbered, name)
383 383 msgs.append(msg)
384 384
385 385 return msgs
386 386
387 387 def makeintro(patches):
388 388 tlen = len(str(len(patches)))
389 389
390 390 flag = opts.get('flag') or ''
391 391 if flag:
392 392 flag = ' ' + ' '.join(flag)
393 393 prefix = '[PATCH %0*d of %d%s]' % (tlen, 0, len(patches), flag)
394 394
395 395 subj = (opts.get('subject') or
396 396 prompt(ui, '(optional) Subject: ', rest=prefix, default=''))
397 397 if not subj:
398 398 return None # skip intro if the user doesn't bother
399 399
400 400 subj = prefix + ' ' + subj
401 401
402 402 body = ''
403 403 if opts.get('diffstat'):
404 404 # generate a cumulative diffstat of the whole patch series
405 405 diffstat = patch.diffstat(sum(patches, []))
406 406 body = '\n' + diffstat
407 407 else:
408 408 diffstat = None
409 409
410 410 body = getdescription(body, sender)
411 411 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
412 412 msg['Subject'] = mail.headencode(ui, subj, _charsets,
413 413 opts.get('test'))
414 414 return (msg, subj, diffstat)
415 415
416 416 def getbundlemsgs(bundle):
417 417 subj = (opts.get('subject')
418 418 or prompt(ui, 'Subject:', 'A bundle for your repository'))
419 419
420 420 body = getdescription('', sender)
421 421 msg = email.MIMEMultipart.MIMEMultipart()
422 422 if body:
423 423 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
424 424 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
425 425 datapart.set_payload(bundle)
426 426 bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
427 427 datapart.add_header('Content-Disposition', 'attachment',
428 428 filename=bundlename)
429 429 email.Encoders.encode_base64(datapart)
430 430 msg.attach(datapart)
431 431 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
432 432 return [(msg, subj, None)]
433 433
434 434 sender = (opts.get('from') or ui.config('email', 'from') or
435 435 ui.config('patchbomb', 'from') or
436 436 prompt(ui, 'From', ui.username()))
437 437
438 438 if patches:
439 439 msgs = getpatchmsgs(patches, opts.get('patchnames'))
440 440 elif bundle:
441 441 msgs = getbundlemsgs(getbundle(dest))
442 442 else:
443 443 msgs = getpatchmsgs(list(getpatches(revs)))
444 444
445 445 showaddrs = []
446 446
447 447 def getaddrs(header, ask=False, default=None):
448 448 configkey = header.lower()
449 449 opt = header.replace('-', '_').lower()
450 450 addrs = opts.get(opt)
451 451 if addrs:
452 452 showaddrs.append('%s: %s' % (header, ', '.join(addrs)))
453 453 return mail.addrlistencode(ui, addrs, _charsets, opts.get('test'))
454 454
455 455 # not on the command line: fallback to config and then maybe ask
456 456 addr = (ui.config('email', configkey) or
457 457 ui.config('patchbomb', configkey) or
458 458 '')
459 459 if not addr and ask:
460 460 addr = prompt(ui, header, default=default)
461 461 if addr:
462 462 showaddrs.append('%s: %s' % (header, addr))
463 463 return mail.addrlistencode(ui, [addr], _charsets, opts.get('test'))
464 464 else:
465 465 return default
466 466
467 467 to = getaddrs('To', ask=True)
468 468 if not to:
469 469 # we can get here in non-interactive mode
470 470 raise util.Abort(_('no recipient addresses provided'))
471 471 cc = getaddrs('Cc', ask=True, default='') or []
472 472 bcc = getaddrs('Bcc') or []
473 473 replyto = getaddrs('Reply-To')
474 474
475 475 if opts.get('diffstat') or opts.get('confirm'):
476 476 ui.write(_('\nFinal summary:\n\n'))
477 ui.write('From: %s\n' % sender)
477 ui.write(('From: %s\n' % sender))
478 478 for addr in showaddrs:
479 479 ui.write('%s\n' % addr)
480 480 for m, subj, ds in msgs:
481 ui.write('Subject: %s\n' % subj)
481 ui.write(('Subject: %s\n' % subj))
482 482 if ds:
483 483 ui.write(ds)
484 484 ui.write('\n')
485 485 if ui.promptchoice(_('are you sure you want to send (yn)?'),
486 486 (_('&Yes'), _('&No'))):
487 487 raise util.Abort(_('patchbomb canceled'))
488 488
489 489 ui.write('\n')
490 490
491 491 parent = opts.get('in_reply_to') or None
492 492 # angle brackets may be omitted, they're not semantically part of the msg-id
493 493 if parent is not None:
494 494 if not parent.startswith('<'):
495 495 parent = '<' + parent
496 496 if not parent.endswith('>'):
497 497 parent += '>'
498 498
499 499 sender_addr = email.Utils.parseaddr(sender)[1]
500 500 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
501 501 sendmail = None
502 502 for i, (m, subj, ds) in enumerate(msgs):
503 503 try:
504 504 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
505 505 except TypeError:
506 506 m['Message-Id'] = genmsgid('patchbomb')
507 507 if parent:
508 508 m['In-Reply-To'] = parent
509 509 m['References'] = parent
510 510 if not parent or 'X-Mercurial-Node' not in m:
511 511 parent = m['Message-Id']
512 512
513 513 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
514 514 m['Date'] = email.Utils.formatdate(start_time[0], localtime=True)
515 515
516 516 start_time = (start_time[0] + 1, start_time[1])
517 517 m['From'] = sender
518 518 m['To'] = ', '.join(to)
519 519 if cc:
520 520 m['Cc'] = ', '.join(cc)
521 521 if bcc:
522 522 m['Bcc'] = ', '.join(bcc)
523 523 if replyto:
524 524 m['Reply-To'] = ', '.join(replyto)
525 525 if opts.get('test'):
526 526 ui.status(_('displaying '), subj, ' ...\n')
527 527 ui.flush()
528 528 if 'PAGER' in os.environ and not ui.plain():
529 529 fp = util.popen(os.environ['PAGER'], 'w')
530 530 else:
531 531 fp = ui
532 532 generator = email.Generator.Generator(fp, mangle_from_=False)
533 533 try:
534 534 generator.flatten(m, 0)
535 535 fp.write('\n')
536 536 except IOError, inst:
537 537 if inst.errno != errno.EPIPE:
538 538 raise
539 539 if fp is not ui:
540 540 fp.close()
541 541 else:
542 542 if not sendmail:
543 543 sendmail = mail.connect(ui, mbox=mbox)
544 544 ui.status(_('sending '), subj, ' ...\n')
545 545 ui.progress(_('sending'), i, item=subj, total=len(msgs))
546 546 if not mbox:
547 547 # Exim does not remove the Bcc field
548 548 del m['Bcc']
549 549 fp = cStringIO.StringIO()
550 550 generator = email.Generator.Generator(fp, mangle_from_=False)
551 551 generator.flatten(m, 0)
552 552 sendmail(sender_addr, to + bcc + cc, fp.getvalue())
553 553
554 554 ui.progress(_('writing'), None)
555 555 ui.progress(_('sending'), None)
@@ -1,5975 +1,5977
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 of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, difflib, time, tempfile, errno
12 12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, hbisect
15 15 import sshserver, hgweb, hgweb.server, commandserver
16 16 import merge as mergemod
17 17 import minirst, revset, fileset
18 18 import dagparser, context, simplemerge, graphmod
19 19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
20 20 import phases, obsolete
21 21
22 22 table = {}
23 23
24 24 command = cmdutil.command(table)
25 25
26 26 # common command options
27 27
28 28 globalopts = [
29 29 ('R', 'repository', '',
30 30 _('repository root directory or name of overlay bundle file'),
31 31 _('REPO')),
32 32 ('', 'cwd', '',
33 33 _('change working directory'), _('DIR')),
34 34 ('y', 'noninteractive', None,
35 35 _('do not prompt, automatically pick the first choice for all prompts')),
36 36 ('q', 'quiet', None, _('suppress output')),
37 37 ('v', 'verbose', None, _('enable additional output')),
38 38 ('', 'config', [],
39 39 _('set/override config option (use \'section.name=value\')'),
40 40 _('CONFIG')),
41 41 ('', 'debug', None, _('enable debugging output')),
42 42 ('', 'debugger', None, _('start debugger')),
43 43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 44 _('ENCODE')),
45 45 ('', 'encodingmode', encoding.encodingmode,
46 46 _('set the charset encoding mode'), _('MODE')),
47 47 ('', 'traceback', None, _('always print a traceback on exception')),
48 48 ('', 'time', None, _('time how long the command takes')),
49 49 ('', 'profile', None, _('print command execution profile')),
50 50 ('', 'version', None, _('output version information and exit')),
51 51 ('h', 'help', None, _('display help and exit')),
52 52 ]
53 53
54 54 dryrunopts = [('n', 'dry-run', None,
55 55 _('do not perform actions, just print output'))]
56 56
57 57 remoteopts = [
58 58 ('e', 'ssh', '',
59 59 _('specify ssh command to use'), _('CMD')),
60 60 ('', 'remotecmd', '',
61 61 _('specify hg command to run on the remote side'), _('CMD')),
62 62 ('', 'insecure', None,
63 63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 64 ]
65 65
66 66 walkopts = [
67 67 ('I', 'include', [],
68 68 _('include names matching the given patterns'), _('PATTERN')),
69 69 ('X', 'exclude', [],
70 70 _('exclude names matching the given patterns'), _('PATTERN')),
71 71 ]
72 72
73 73 commitopts = [
74 74 ('m', 'message', '',
75 75 _('use text as commit message'), _('TEXT')),
76 76 ('l', 'logfile', '',
77 77 _('read commit message from file'), _('FILE')),
78 78 ]
79 79
80 80 commitopts2 = [
81 81 ('d', 'date', '',
82 82 _('record the specified date as commit date'), _('DATE')),
83 83 ('u', 'user', '',
84 84 _('record the specified user as committer'), _('USER')),
85 85 ]
86 86
87 87 templateopts = [
88 88 ('', 'style', '',
89 89 _('display using template map file'), _('STYLE')),
90 90 ('', 'template', '',
91 91 _('display with template'), _('TEMPLATE')),
92 92 ]
93 93
94 94 logopts = [
95 95 ('p', 'patch', None, _('show patch')),
96 96 ('g', 'git', None, _('use git extended diff format')),
97 97 ('l', 'limit', '',
98 98 _('limit number of changes displayed'), _('NUM')),
99 99 ('M', 'no-merges', None, _('do not show merges')),
100 100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 101 ('G', 'graph', None, _("show the revision DAG")),
102 102 ] + templateopts
103 103
104 104 diffopts = [
105 105 ('a', 'text', None, _('treat all files as text')),
106 106 ('g', 'git', None, _('use git extended diff format')),
107 107 ('', 'nodates', None, _('omit dates from diff headers'))
108 108 ]
109 109
110 110 diffwsopts = [
111 111 ('w', 'ignore-all-space', None,
112 112 _('ignore white space when comparing lines')),
113 113 ('b', 'ignore-space-change', None,
114 114 _('ignore changes in the amount of white space')),
115 115 ('B', 'ignore-blank-lines', None,
116 116 _('ignore changes whose lines are all blank')),
117 117 ]
118 118
119 119 diffopts2 = [
120 120 ('p', 'show-function', None, _('show which function each change is in')),
121 121 ('', 'reverse', None, _('produce a diff that undoes the changes')),
122 122 ] + diffwsopts + [
123 123 ('U', 'unified', '',
124 124 _('number of lines of context to show'), _('NUM')),
125 125 ('', 'stat', None, _('output diffstat-style summary of changes')),
126 126 ]
127 127
128 128 mergetoolopts = [
129 129 ('t', 'tool', '', _('specify merge tool')),
130 130 ]
131 131
132 132 similarityopts = [
133 133 ('s', 'similarity', '',
134 134 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
135 135 ]
136 136
137 137 subrepoopts = [
138 138 ('S', 'subrepos', None,
139 139 _('recurse into subrepositories'))
140 140 ]
141 141
142 142 # Commands start here, listed alphabetically
143 143
144 144 @command('^add',
145 145 walkopts + subrepoopts + dryrunopts,
146 146 _('[OPTION]... [FILE]...'))
147 147 def add(ui, repo, *pats, **opts):
148 148 """add the specified files on the next commit
149 149
150 150 Schedule files to be version controlled and added to the
151 151 repository.
152 152
153 153 The files will be added to the repository at the next commit. To
154 154 undo an add before that, see :hg:`forget`.
155 155
156 156 If no names are given, add all files to the repository.
157 157
158 158 .. container:: verbose
159 159
160 160 An example showing how new (unknown) files are added
161 161 automatically by :hg:`add`::
162 162
163 163 $ ls
164 164 foo.c
165 165 $ hg status
166 166 ? foo.c
167 167 $ hg add
168 168 adding foo.c
169 169 $ hg status
170 170 A foo.c
171 171
172 172 Returns 0 if all files are successfully added.
173 173 """
174 174
175 175 m = scmutil.match(repo[None], pats, opts)
176 176 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
177 177 opts.get('subrepos'), prefix="", explicitonly=False)
178 178 return rejected and 1 or 0
179 179
180 180 @command('addremove',
181 181 similarityopts + walkopts + dryrunopts,
182 182 _('[OPTION]... [FILE]...'))
183 183 def addremove(ui, repo, *pats, **opts):
184 184 """add all new files, delete all missing files
185 185
186 186 Add all new files and remove all missing files from the
187 187 repository.
188 188
189 189 New files are ignored if they match any of the patterns in
190 190 ``.hgignore``. As with add, these changes take effect at the next
191 191 commit.
192 192
193 193 Use the -s/--similarity option to detect renamed files. This
194 194 option takes a percentage between 0 (disabled) and 100 (files must
195 195 be identical) as its parameter. With a parameter greater than 0,
196 196 this compares every removed file with every added file and records
197 197 those similar enough as renames. Detecting renamed files this way
198 198 can be expensive. After using this option, :hg:`status -C` can be
199 199 used to check which files were identified as moved or renamed. If
200 200 not specified, -s/--similarity defaults to 100 and only renames of
201 201 identical files are detected.
202 202
203 203 Returns 0 if all files are successfully added.
204 204 """
205 205 try:
206 206 sim = float(opts.get('similarity') or 100)
207 207 except ValueError:
208 208 raise util.Abort(_('similarity must be a number'))
209 209 if sim < 0 or sim > 100:
210 210 raise util.Abort(_('similarity must be between 0 and 100'))
211 211 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
212 212
213 213 @command('^annotate|blame',
214 214 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
215 215 ('', 'follow', None,
216 216 _('follow copies/renames and list the filename (DEPRECATED)')),
217 217 ('', 'no-follow', None, _("don't follow copies and renames")),
218 218 ('a', 'text', None, _('treat all files as text')),
219 219 ('u', 'user', None, _('list the author (long with -v)')),
220 220 ('f', 'file', None, _('list the filename')),
221 221 ('d', 'date', None, _('list the date (short with -q)')),
222 222 ('n', 'number', None, _('list the revision number (default)')),
223 223 ('c', 'changeset', None, _('list the changeset')),
224 224 ('l', 'line-number', None, _('show line number at the first appearance'))
225 225 ] + diffwsopts + walkopts,
226 226 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
227 227 def annotate(ui, repo, *pats, **opts):
228 228 """show changeset information by line for each file
229 229
230 230 List changes in files, showing the revision id responsible for
231 231 each line
232 232
233 233 This command is useful for discovering when a change was made and
234 234 by whom.
235 235
236 236 Without the -a/--text option, annotate will avoid processing files
237 237 it detects as binary. With -a, annotate will annotate the file
238 238 anyway, although the results will probably be neither useful
239 239 nor desirable.
240 240
241 241 Returns 0 on success.
242 242 """
243 243 if opts.get('follow'):
244 244 # --follow is deprecated and now just an alias for -f/--file
245 245 # to mimic the behavior of Mercurial before version 1.5
246 246 opts['file'] = True
247 247
248 248 datefunc = ui.quiet and util.shortdate or util.datestr
249 249 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
250 250
251 251 if not pats:
252 252 raise util.Abort(_('at least one filename or pattern is required'))
253 253
254 254 hexfn = ui.debugflag and hex or short
255 255
256 256 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
257 257 ('number', ' ', lambda x: str(x[0].rev())),
258 258 ('changeset', ' ', lambda x: hexfn(x[0].node())),
259 259 ('date', ' ', getdate),
260 260 ('file', ' ', lambda x: x[0].path()),
261 261 ('line_number', ':', lambda x: str(x[1])),
262 262 ]
263 263
264 264 if (not opts.get('user') and not opts.get('changeset')
265 265 and not opts.get('date') and not opts.get('file')):
266 266 opts['number'] = True
267 267
268 268 linenumber = opts.get('line_number') is not None
269 269 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
270 270 raise util.Abort(_('at least one of -n/-c is required for -l'))
271 271
272 272 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
273 273 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
274 274
275 275 def bad(x, y):
276 276 raise util.Abort("%s: %s" % (x, y))
277 277
278 278 ctx = scmutil.revsingle(repo, opts.get('rev'))
279 279 m = scmutil.match(ctx, pats, opts)
280 280 m.bad = bad
281 281 follow = not opts.get('no_follow')
282 282 diffopts = patch.diffopts(ui, opts, section='annotate')
283 283 for abs in ctx.walk(m):
284 284 fctx = ctx[abs]
285 285 if not opts.get('text') and util.binary(fctx.data()):
286 286 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
287 287 continue
288 288
289 289 lines = fctx.annotate(follow=follow, linenumber=linenumber,
290 290 diffopts=diffopts)
291 291 pieces = []
292 292
293 293 for f, sep in funcmap:
294 294 l = [f(n) for n, dummy in lines]
295 295 if l:
296 296 sized = [(x, encoding.colwidth(x)) for x in l]
297 297 ml = max([w for x, w in sized])
298 298 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
299 299 for x, w in sized])
300 300
301 301 if pieces:
302 302 for p, l in zip(zip(*pieces), lines):
303 303 ui.write("%s: %s" % ("".join(p), l[1]))
304 304
305 305 if lines and not lines[-1][1].endswith('\n'):
306 306 ui.write('\n')
307 307
308 308 @command('archive',
309 309 [('', 'no-decode', None, _('do not pass files through decoders')),
310 310 ('p', 'prefix', '', _('directory prefix for files in archive'),
311 311 _('PREFIX')),
312 312 ('r', 'rev', '', _('revision to distribute'), _('REV')),
313 313 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
314 314 ] + subrepoopts + walkopts,
315 315 _('[OPTION]... DEST'))
316 316 def archive(ui, repo, dest, **opts):
317 317 '''create an unversioned archive of a repository revision
318 318
319 319 By default, the revision used is the parent of the working
320 320 directory; use -r/--rev to specify a different revision.
321 321
322 322 The archive type is automatically detected based on file
323 323 extension (or override using -t/--type).
324 324
325 325 .. container:: verbose
326 326
327 327 Examples:
328 328
329 329 - create a zip file containing the 1.0 release::
330 330
331 331 hg archive -r 1.0 project-1.0.zip
332 332
333 333 - create a tarball excluding .hg files::
334 334
335 335 hg archive project.tar.gz -X ".hg*"
336 336
337 337 Valid types are:
338 338
339 339 :``files``: a directory full of files (default)
340 340 :``tar``: tar archive, uncompressed
341 341 :``tbz2``: tar archive, compressed using bzip2
342 342 :``tgz``: tar archive, compressed using gzip
343 343 :``uzip``: zip archive, uncompressed
344 344 :``zip``: zip archive, compressed using deflate
345 345
346 346 The exact name of the destination archive or directory is given
347 347 using a format string; see :hg:`help export` for details.
348 348
349 349 Each member added to an archive file has a directory prefix
350 350 prepended. Use -p/--prefix to specify a format string for the
351 351 prefix. The default is the basename of the archive, with suffixes
352 352 removed.
353 353
354 354 Returns 0 on success.
355 355 '''
356 356
357 357 ctx = scmutil.revsingle(repo, opts.get('rev'))
358 358 if not ctx:
359 359 raise util.Abort(_('no working directory: please specify a revision'))
360 360 node = ctx.node()
361 361 dest = cmdutil.makefilename(repo, dest, node)
362 362 if os.path.realpath(dest) == repo.root:
363 363 raise util.Abort(_('repository root cannot be destination'))
364 364
365 365 kind = opts.get('type') or archival.guesskind(dest) or 'files'
366 366 prefix = opts.get('prefix')
367 367
368 368 if dest == '-':
369 369 if kind == 'files':
370 370 raise util.Abort(_('cannot archive plain files to stdout'))
371 371 dest = cmdutil.makefileobj(repo, dest)
372 372 if not prefix:
373 373 prefix = os.path.basename(repo.root) + '-%h'
374 374
375 375 prefix = cmdutil.makefilename(repo, prefix, node)
376 376 matchfn = scmutil.match(ctx, [], opts)
377 377 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
378 378 matchfn, prefix, subrepos=opts.get('subrepos'))
379 379
380 380 @command('backout',
381 381 [('', 'merge', None, _('merge with old dirstate parent after backout')),
382 382 ('', 'parent', '',
383 383 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
384 384 ('r', 'rev', '', _('revision to backout'), _('REV')),
385 385 ] + mergetoolopts + walkopts + commitopts + commitopts2,
386 386 _('[OPTION]... [-r] REV'))
387 387 def backout(ui, repo, node=None, rev=None, **opts):
388 388 '''reverse effect of earlier changeset
389 389
390 390 Prepare a new changeset with the effect of REV undone in the
391 391 current working directory.
392 392
393 393 If REV is the parent of the working directory, then this new changeset
394 394 is committed automatically. Otherwise, hg needs to merge the
395 395 changes and the merged result is left uncommitted.
396 396
397 397 .. note::
398 398 backout cannot be used to fix either an unwanted or
399 399 incorrect merge.
400 400
401 401 .. container:: verbose
402 402
403 403 By default, the pending changeset will have one parent,
404 404 maintaining a linear history. With --merge, the pending
405 405 changeset will instead have two parents: the old parent of the
406 406 working directory and a new child of REV that simply undoes REV.
407 407
408 408 Before version 1.7, the behavior without --merge was equivalent
409 409 to specifying --merge followed by :hg:`update --clean .` to
410 410 cancel the merge and leave the child of REV as a head to be
411 411 merged separately.
412 412
413 413 See :hg:`help dates` for a list of formats valid for -d/--date.
414 414
415 415 Returns 0 on success.
416 416 '''
417 417 if rev and node:
418 418 raise util.Abort(_("please specify just one revision"))
419 419
420 420 if not rev:
421 421 rev = node
422 422
423 423 if not rev:
424 424 raise util.Abort(_("please specify a revision to backout"))
425 425
426 426 date = opts.get('date')
427 427 if date:
428 428 opts['date'] = util.parsedate(date)
429 429
430 430 cmdutil.bailifchanged(repo)
431 431 node = scmutil.revsingle(repo, rev).node()
432 432
433 433 op1, op2 = repo.dirstate.parents()
434 434 a = repo.changelog.ancestor(op1, node)
435 435 if a != node:
436 436 raise util.Abort(_('cannot backout change on a different branch'))
437 437
438 438 p1, p2 = repo.changelog.parents(node)
439 439 if p1 == nullid:
440 440 raise util.Abort(_('cannot backout a change with no parents'))
441 441 if p2 != nullid:
442 442 if not opts.get('parent'):
443 443 raise util.Abort(_('cannot backout a merge changeset'))
444 444 p = repo.lookup(opts['parent'])
445 445 if p not in (p1, p2):
446 446 raise util.Abort(_('%s is not a parent of %s') %
447 447 (short(p), short(node)))
448 448 parent = p
449 449 else:
450 450 if opts.get('parent'):
451 451 raise util.Abort(_('cannot use --parent on non-merge changeset'))
452 452 parent = p1
453 453
454 454 # the backout should appear on the same branch
455 455 wlock = repo.wlock()
456 456 try:
457 457 branch = repo.dirstate.branch()
458 458 hg.clean(repo, node, show_stats=False)
459 459 repo.dirstate.setbranch(branch)
460 460 revert_opts = opts.copy()
461 461 revert_opts['date'] = None
462 462 revert_opts['all'] = True
463 463 revert_opts['rev'] = hex(parent)
464 464 revert_opts['no_backup'] = None
465 465 revert(ui, repo, **revert_opts)
466 466 if not opts.get('merge') and op1 != node:
467 467 try:
468 468 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
469 469 return hg.update(repo, op1)
470 470 finally:
471 471 ui.setconfig('ui', 'forcemerge', '')
472 472
473 473 commit_opts = opts.copy()
474 474 commit_opts['addremove'] = False
475 475 if not commit_opts['message'] and not commit_opts['logfile']:
476 476 # we don't translate commit messages
477 477 commit_opts['message'] = "Backed out changeset %s" % short(node)
478 478 commit_opts['force_editor'] = True
479 479 commit(ui, repo, **commit_opts)
480 480 def nice(node):
481 481 return '%d:%s' % (repo.changelog.rev(node), short(node))
482 482 ui.status(_('changeset %s backs out changeset %s\n') %
483 483 (nice(repo.changelog.tip()), nice(node)))
484 484 if opts.get('merge') and op1 != node:
485 485 hg.clean(repo, op1, show_stats=False)
486 486 ui.status(_('merging with changeset %s\n')
487 487 % nice(repo.changelog.tip()))
488 488 try:
489 489 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
490 490 return hg.merge(repo, hex(repo.changelog.tip()))
491 491 finally:
492 492 ui.setconfig('ui', 'forcemerge', '')
493 493 finally:
494 494 wlock.release()
495 495 return 0
496 496
497 497 @command('bisect',
498 498 [('r', 'reset', False, _('reset bisect state')),
499 499 ('g', 'good', False, _('mark changeset good')),
500 500 ('b', 'bad', False, _('mark changeset bad')),
501 501 ('s', 'skip', False, _('skip testing changeset')),
502 502 ('e', 'extend', False, _('extend the bisect range')),
503 503 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
504 504 ('U', 'noupdate', False, _('do not update to target'))],
505 505 _("[-gbsr] [-U] [-c CMD] [REV]"))
506 506 def bisect(ui, repo, rev=None, extra=None, command=None,
507 507 reset=None, good=None, bad=None, skip=None, extend=None,
508 508 noupdate=None):
509 509 """subdivision search of changesets
510 510
511 511 This command helps to find changesets which introduce problems. To
512 512 use, mark the earliest changeset you know exhibits the problem as
513 513 bad, then mark the latest changeset which is free from the problem
514 514 as good. Bisect will update your working directory to a revision
515 515 for testing (unless the -U/--noupdate option is specified). Once
516 516 you have performed tests, mark the working directory as good or
517 517 bad, and bisect will either update to another candidate changeset
518 518 or announce that it has found the bad revision.
519 519
520 520 As a shortcut, you can also use the revision argument to mark a
521 521 revision as good or bad without checking it out first.
522 522
523 523 If you supply a command, it will be used for automatic bisection.
524 524 The environment variable HG_NODE will contain the ID of the
525 525 changeset being tested. The exit status of the command will be
526 526 used to mark revisions as good or bad: status 0 means good, 125
527 527 means to skip the revision, 127 (command not found) will abort the
528 528 bisection, and any other non-zero exit status means the revision
529 529 is bad.
530 530
531 531 .. container:: verbose
532 532
533 533 Some examples:
534 534
535 535 - start a bisection with known bad revision 12, and good revision 34::
536 536
537 537 hg bisect --bad 34
538 538 hg bisect --good 12
539 539
540 540 - advance the current bisection by marking current revision as good or
541 541 bad::
542 542
543 543 hg bisect --good
544 544 hg bisect --bad
545 545
546 546 - mark the current revision, or a known revision, to be skipped (e.g. if
547 547 that revision is not usable because of another issue)::
548 548
549 549 hg bisect --skip
550 550 hg bisect --skip 23
551 551
552 552 - forget the current bisection::
553 553
554 554 hg bisect --reset
555 555
556 556 - use 'make && make tests' to automatically find the first broken
557 557 revision::
558 558
559 559 hg bisect --reset
560 560 hg bisect --bad 34
561 561 hg bisect --good 12
562 562 hg bisect --command 'make && make tests'
563 563
564 564 - see all changesets whose states are already known in the current
565 565 bisection::
566 566
567 567 hg log -r "bisect(pruned)"
568 568
569 569 - see the changeset currently being bisected (especially useful
570 570 if running with -U/--noupdate)::
571 571
572 572 hg log -r "bisect(current)"
573 573
574 574 - see all changesets that took part in the current bisection::
575 575
576 576 hg log -r "bisect(range)"
577 577
578 578 - with the graphlog extension, you can even get a nice graph::
579 579
580 580 hg log --graph -r "bisect(range)"
581 581
582 582 See :hg:`help revsets` for more about the `bisect()` keyword.
583 583
584 584 Returns 0 on success.
585 585 """
586 586 def extendbisectrange(nodes, good):
587 587 # bisect is incomplete when it ends on a merge node and
588 588 # one of the parent was not checked.
589 589 parents = repo[nodes[0]].parents()
590 590 if len(parents) > 1:
591 591 side = good and state['bad'] or state['good']
592 592 num = len(set(i.node() for i in parents) & set(side))
593 593 if num == 1:
594 594 return parents[0].ancestor(parents[1])
595 595 return None
596 596
597 597 def print_result(nodes, good):
598 598 displayer = cmdutil.show_changeset(ui, repo, {})
599 599 if len(nodes) == 1:
600 600 # narrowed it down to a single revision
601 601 if good:
602 602 ui.write(_("The first good revision is:\n"))
603 603 else:
604 604 ui.write(_("The first bad revision is:\n"))
605 605 displayer.show(repo[nodes[0]])
606 606 extendnode = extendbisectrange(nodes, good)
607 607 if extendnode is not None:
608 608 ui.write(_('Not all ancestors of this changeset have been'
609 609 ' checked.\nUse bisect --extend to continue the '
610 610 'bisection from\nthe common ancestor, %s.\n')
611 611 % extendnode)
612 612 else:
613 613 # multiple possible revisions
614 614 if good:
615 615 ui.write(_("Due to skipped revisions, the first "
616 616 "good revision could be any of:\n"))
617 617 else:
618 618 ui.write(_("Due to skipped revisions, the first "
619 619 "bad revision could be any of:\n"))
620 620 for n in nodes:
621 621 displayer.show(repo[n])
622 622 displayer.close()
623 623
624 624 def check_state(state, interactive=True):
625 625 if not state['good'] or not state['bad']:
626 626 if (good or bad or skip or reset) and interactive:
627 627 return
628 628 if not state['good']:
629 629 raise util.Abort(_('cannot bisect (no known good revisions)'))
630 630 else:
631 631 raise util.Abort(_('cannot bisect (no known bad revisions)'))
632 632 return True
633 633
634 634 # backward compatibility
635 635 if rev in "good bad reset init".split():
636 636 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
637 637 cmd, rev, extra = rev, extra, None
638 638 if cmd == "good":
639 639 good = True
640 640 elif cmd == "bad":
641 641 bad = True
642 642 else:
643 643 reset = True
644 644 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
645 645 raise util.Abort(_('incompatible arguments'))
646 646
647 647 if reset:
648 648 p = repo.join("bisect.state")
649 649 if os.path.exists(p):
650 650 os.unlink(p)
651 651 return
652 652
653 653 state = hbisect.load_state(repo)
654 654
655 655 if command:
656 656 changesets = 1
657 657 try:
658 658 node = state['current'][0]
659 659 except LookupError:
660 660 if noupdate:
661 661 raise util.Abort(_('current bisect revision is unknown - '
662 662 'start a new bisect to fix'))
663 663 node, p2 = repo.dirstate.parents()
664 664 if p2 != nullid:
665 665 raise util.Abort(_('current bisect revision is a merge'))
666 666 try:
667 667 while changesets:
668 668 # update state
669 669 state['current'] = [node]
670 670 hbisect.save_state(repo, state)
671 671 status = util.system(command,
672 672 environ={'HG_NODE': hex(node)},
673 673 out=ui.fout)
674 674 if status == 125:
675 675 transition = "skip"
676 676 elif status == 0:
677 677 transition = "good"
678 678 # status < 0 means process was killed
679 679 elif status == 127:
680 680 raise util.Abort(_("failed to execute %s") % command)
681 681 elif status < 0:
682 682 raise util.Abort(_("%s killed") % command)
683 683 else:
684 684 transition = "bad"
685 685 ctx = scmutil.revsingle(repo, rev, node)
686 686 rev = None # clear for future iterations
687 687 state[transition].append(ctx.node())
688 688 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
689 689 check_state(state, interactive=False)
690 690 # bisect
691 691 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
692 692 # update to next check
693 693 node = nodes[0]
694 694 if not noupdate:
695 695 cmdutil.bailifchanged(repo)
696 696 hg.clean(repo, node, show_stats=False)
697 697 finally:
698 698 state['current'] = [node]
699 699 hbisect.save_state(repo, state)
700 700 print_result(nodes, good)
701 701 return
702 702
703 703 # update state
704 704
705 705 if rev:
706 706 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
707 707 else:
708 708 nodes = [repo.lookup('.')]
709 709
710 710 if good or bad or skip:
711 711 if good:
712 712 state['good'] += nodes
713 713 elif bad:
714 714 state['bad'] += nodes
715 715 elif skip:
716 716 state['skip'] += nodes
717 717 hbisect.save_state(repo, state)
718 718
719 719 if not check_state(state):
720 720 return
721 721
722 722 # actually bisect
723 723 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
724 724 if extend:
725 725 if not changesets:
726 726 extendnode = extendbisectrange(nodes, good)
727 727 if extendnode is not None:
728 728 ui.write(_("Extending search to changeset %d:%s\n"
729 729 % (extendnode.rev(), extendnode)))
730 730 state['current'] = [extendnode.node()]
731 731 hbisect.save_state(repo, state)
732 732 if noupdate:
733 733 return
734 734 cmdutil.bailifchanged(repo)
735 735 return hg.clean(repo, extendnode.node())
736 736 raise util.Abort(_("nothing to extend"))
737 737
738 738 if changesets == 0:
739 739 print_result(nodes, good)
740 740 else:
741 741 assert len(nodes) == 1 # only a single node can be tested next
742 742 node = nodes[0]
743 743 # compute the approximate number of remaining tests
744 744 tests, size = 0, 2
745 745 while size <= changesets:
746 746 tests, size = tests + 1, size * 2
747 747 rev = repo.changelog.rev(node)
748 748 ui.write(_("Testing changeset %d:%s "
749 749 "(%d changesets remaining, ~%d tests)\n")
750 750 % (rev, short(node), changesets, tests))
751 751 state['current'] = [node]
752 752 hbisect.save_state(repo, state)
753 753 if not noupdate:
754 754 cmdutil.bailifchanged(repo)
755 755 return hg.clean(repo, node)
756 756
757 757 @command('bookmarks',
758 758 [('f', 'force', False, _('force')),
759 759 ('r', 'rev', '', _('revision'), _('REV')),
760 760 ('d', 'delete', False, _('delete a given bookmark')),
761 761 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
762 762 ('i', 'inactive', False, _('mark a bookmark inactive'))],
763 763 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
764 764 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
765 765 rename=None, inactive=False):
766 766 '''track a line of development with movable markers
767 767
768 768 Bookmarks are pointers to certain commits that move when committing.
769 769 Bookmarks are local. They can be renamed, copied and deleted. It is
770 770 possible to use :hg:`merge NAME` to merge from a given bookmark, and
771 771 :hg:`update NAME` to update to a given bookmark.
772 772
773 773 You can use :hg:`bookmark NAME` to set a bookmark on the working
774 774 directory's parent revision with the given name. If you specify
775 775 a revision using -r REV (where REV may be an existing bookmark),
776 776 the bookmark is assigned to that revision.
777 777
778 778 Bookmarks can be pushed and pulled between repositories (see :hg:`help
779 779 push` and :hg:`help pull`). This requires both the local and remote
780 780 repositories to support bookmarks. For versions prior to 1.8, this means
781 781 the bookmarks extension must be enabled.
782 782
783 783 With -i/--inactive, the new bookmark will not be made the active
784 784 bookmark. If -r/--rev is given, the new bookmark will not be made
785 785 active even if -i/--inactive is not given. If no NAME is given, the
786 786 current active bookmark will be marked inactive.
787 787 '''
788 788 hexfn = ui.debugflag and hex or short
789 789 marks = repo._bookmarks
790 790 cur = repo.changectx('.').node()
791 791
792 792 def checkformat(mark):
793 793 mark = mark.strip()
794 794 if not mark:
795 795 raise util.Abort(_("bookmark names cannot consist entirely of "
796 796 "whitespace"))
797 797 scmutil.checknewlabel(repo, mark, 'bookmark')
798 798 return mark
799 799
800 800 def checkconflict(repo, mark, force=False):
801 801 if mark in marks and not force:
802 802 raise util.Abort(_("bookmark '%s' already exists "
803 803 "(use -f to force)") % mark)
804 804 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
805 805 and not force):
806 806 raise util.Abort(
807 807 _("a bookmark cannot have the name of an existing branch"))
808 808
809 809 if delete and rename:
810 810 raise util.Abort(_("--delete and --rename are incompatible"))
811 811 if delete and rev:
812 812 raise util.Abort(_("--rev is incompatible with --delete"))
813 813 if rename and rev:
814 814 raise util.Abort(_("--rev is incompatible with --rename"))
815 815 if mark is None and (delete or rev):
816 816 raise util.Abort(_("bookmark name required"))
817 817
818 818 if delete:
819 819 if mark not in marks:
820 820 raise util.Abort(_("bookmark '%s' does not exist") % mark)
821 821 if mark == repo._bookmarkcurrent:
822 822 bookmarks.setcurrent(repo, None)
823 823 del marks[mark]
824 824 marks.write()
825 825
826 826 elif rename:
827 827 if mark is None:
828 828 raise util.Abort(_("new bookmark name required"))
829 829 mark = checkformat(mark)
830 830 if rename not in marks:
831 831 raise util.Abort(_("bookmark '%s' does not exist") % rename)
832 832 checkconflict(repo, mark, force)
833 833 marks[mark] = marks[rename]
834 834 if repo._bookmarkcurrent == rename and not inactive:
835 835 bookmarks.setcurrent(repo, mark)
836 836 del marks[rename]
837 837 marks.write()
838 838
839 839 elif mark is not None:
840 840 mark = checkformat(mark)
841 841 if inactive and mark == repo._bookmarkcurrent:
842 842 bookmarks.setcurrent(repo, None)
843 843 return
844 844 checkconflict(repo, mark, force)
845 845 if rev:
846 846 marks[mark] = scmutil.revsingle(repo, rev).node()
847 847 else:
848 848 marks[mark] = cur
849 849 if not inactive and cur == marks[mark]:
850 850 bookmarks.setcurrent(repo, mark)
851 851 marks.write()
852 852
853 853 # Same message whether trying to deactivate the current bookmark (-i
854 854 # with no NAME) or listing bookmarks
855 855 elif len(marks) == 0:
856 856 ui.status(_("no bookmarks set\n"))
857 857
858 858 elif inactive:
859 859 if not repo._bookmarkcurrent:
860 860 ui.status(_("no active bookmark\n"))
861 861 else:
862 862 bookmarks.setcurrent(repo, None)
863 863
864 864 else: # show bookmarks
865 865 for bmark, n in sorted(marks.iteritems()):
866 866 current = repo._bookmarkcurrent
867 867 if bmark == current and n == cur:
868 868 prefix, label = '*', 'bookmarks.current'
869 869 else:
870 870 prefix, label = ' ', ''
871 871
872 872 if ui.quiet:
873 873 ui.write("%s\n" % bmark, label=label)
874 874 else:
875 875 ui.write(" %s %-25s %d:%s\n" % (
876 876 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
877 877 label=label)
878 878
879 879 @command('branch',
880 880 [('f', 'force', None,
881 881 _('set branch name even if it shadows an existing branch')),
882 882 ('C', 'clean', None, _('reset branch name to parent branch name'))],
883 883 _('[-fC] [NAME]'))
884 884 def branch(ui, repo, label=None, **opts):
885 885 """set or show the current branch name
886 886
887 887 .. note::
888 888 Branch names are permanent and global. Use :hg:`bookmark` to create a
889 889 light-weight bookmark instead. See :hg:`help glossary` for more
890 890 information about named branches and bookmarks.
891 891
892 892 With no argument, show the current branch name. With one argument,
893 893 set the working directory branch name (the branch will not exist
894 894 in the repository until the next commit). Standard practice
895 895 recommends that primary development take place on the 'default'
896 896 branch.
897 897
898 898 Unless -f/--force is specified, branch will not let you set a
899 899 branch name that already exists, even if it's inactive.
900 900
901 901 Use -C/--clean to reset the working directory branch to that of
902 902 the parent of the working directory, negating a previous branch
903 903 change.
904 904
905 905 Use the command :hg:`update` to switch to an existing branch. Use
906 906 :hg:`commit --close-branch` to mark this branch as closed.
907 907
908 908 Returns 0 on success.
909 909 """
910 910 if not opts.get('clean') and not label:
911 911 ui.write("%s\n" % repo.dirstate.branch())
912 912 return
913 913
914 914 wlock = repo.wlock()
915 915 try:
916 916 if opts.get('clean'):
917 917 label = repo[None].p1().branch()
918 918 repo.dirstate.setbranch(label)
919 919 ui.status(_('reset working directory to branch %s\n') % label)
920 920 elif label:
921 921 if not opts.get('force') and label in repo.branchmap():
922 922 if label not in [p.branch() for p in repo.parents()]:
923 923 raise util.Abort(_('a branch of the same name already'
924 924 ' exists'),
925 925 # i18n: "it" refers to an existing branch
926 926 hint=_("use 'hg update' to switch to it"))
927 927 repo.dirstate.setbranch(label)
928 928 ui.status(_('marked working directory as branch %s\n') % label)
929 929 ui.status(_('(branches are permanent and global, '
930 930 'did you want a bookmark?)\n'))
931 931 finally:
932 932 wlock.release()
933 933
934 934 @command('branches',
935 935 [('a', 'active', False, _('show only branches that have unmerged heads')),
936 936 ('c', 'closed', False, _('show normal and closed branches'))],
937 937 _('[-ac]'))
938 938 def branches(ui, repo, active=False, closed=False):
939 939 """list repository named branches
940 940
941 941 List the repository's named branches, indicating which ones are
942 942 inactive. If -c/--closed is specified, also list branches which have
943 943 been marked closed (see :hg:`commit --close-branch`).
944 944
945 945 If -a/--active is specified, only show active branches. A branch
946 946 is considered active if it contains repository heads.
947 947
948 948 Use the command :hg:`update` to switch to an existing branch.
949 949
950 950 Returns 0.
951 951 """
952 952
953 953 hexfunc = ui.debugflag and hex or short
954 954
955 955 activebranches = set([repo[n].branch() for n in repo.heads()])
956 956 branches = []
957 957 for tag, heads in repo.branchmap().iteritems():
958 958 for h in reversed(heads):
959 959 ctx = repo[h]
960 960 isopen = not ctx.closesbranch()
961 961 if isopen:
962 962 tip = ctx
963 963 break
964 964 else:
965 965 tip = repo[heads[-1]]
966 966 isactive = tag in activebranches and isopen
967 967 branches.append((tip, isactive, isopen))
968 968 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
969 969 reverse=True)
970 970
971 971 for ctx, isactive, isopen in branches:
972 972 if (not active) or isactive:
973 973 if isactive:
974 974 label = 'branches.active'
975 975 notice = ''
976 976 elif not isopen:
977 977 if not closed:
978 978 continue
979 979 label = 'branches.closed'
980 980 notice = _(' (closed)')
981 981 else:
982 982 label = 'branches.inactive'
983 983 notice = _(' (inactive)')
984 984 if ctx.branch() == repo.dirstate.branch():
985 985 label = 'branches.current'
986 986 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
987 987 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
988 988 'log.changeset changeset.%s' % ctx.phasestr())
989 989 tag = ui.label(ctx.branch(), label)
990 990 if ui.quiet:
991 991 ui.write("%s\n" % tag)
992 992 else:
993 993 ui.write("%s %s%s\n" % (tag, rev, notice))
994 994
995 995 @command('bundle',
996 996 [('f', 'force', None, _('run even when the destination is unrelated')),
997 997 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
998 998 _('REV')),
999 999 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1000 1000 _('BRANCH')),
1001 1001 ('', 'base', [],
1002 1002 _('a base changeset assumed to be available at the destination'),
1003 1003 _('REV')),
1004 1004 ('a', 'all', None, _('bundle all changesets in the repository')),
1005 1005 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1006 1006 ] + remoteopts,
1007 1007 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1008 1008 def bundle(ui, repo, fname, dest=None, **opts):
1009 1009 """create a changegroup file
1010 1010
1011 1011 Generate a compressed changegroup file collecting changesets not
1012 1012 known to be in another repository.
1013 1013
1014 1014 If you omit the destination repository, then hg assumes the
1015 1015 destination will have all the nodes you specify with --base
1016 1016 parameters. To create a bundle containing all changesets, use
1017 1017 -a/--all (or --base null).
1018 1018
1019 1019 You can change compression method with the -t/--type option.
1020 1020 The available compression methods are: none, bzip2, and
1021 1021 gzip (by default, bundles are compressed using bzip2).
1022 1022
1023 1023 The bundle file can then be transferred using conventional means
1024 1024 and applied to another repository with the unbundle or pull
1025 1025 command. This is useful when direct push and pull are not
1026 1026 available or when exporting an entire repository is undesirable.
1027 1027
1028 1028 Applying bundles preserves all changeset contents including
1029 1029 permissions, copy/rename information, and revision history.
1030 1030
1031 1031 Returns 0 on success, 1 if no changes found.
1032 1032 """
1033 1033 revs = None
1034 1034 if 'rev' in opts:
1035 1035 revs = scmutil.revrange(repo, opts['rev'])
1036 1036
1037 1037 bundletype = opts.get('type', 'bzip2').lower()
1038 1038 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1039 1039 bundletype = btypes.get(bundletype)
1040 1040 if bundletype not in changegroup.bundletypes:
1041 1041 raise util.Abort(_('unknown bundle type specified with --type'))
1042 1042
1043 1043 if opts.get('all'):
1044 1044 base = ['null']
1045 1045 else:
1046 1046 base = scmutil.revrange(repo, opts.get('base'))
1047 1047 if base:
1048 1048 if dest:
1049 1049 raise util.Abort(_("--base is incompatible with specifying "
1050 1050 "a destination"))
1051 1051 common = [repo.lookup(rev) for rev in base]
1052 1052 heads = revs and map(repo.lookup, revs) or revs
1053 1053 cg = repo.getbundle('bundle', heads=heads, common=common)
1054 1054 outgoing = None
1055 1055 else:
1056 1056 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1057 1057 dest, branches = hg.parseurl(dest, opts.get('branch'))
1058 1058 other = hg.peer(repo, opts, dest)
1059 1059 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1060 1060 heads = revs and map(repo.lookup, revs) or revs
1061 1061 outgoing = discovery.findcommonoutgoing(repo, other,
1062 1062 onlyheads=heads,
1063 1063 force=opts.get('force'),
1064 1064 portable=True)
1065 1065 cg = repo.getlocalbundle('bundle', outgoing)
1066 1066 if not cg:
1067 1067 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1068 1068 return 1
1069 1069
1070 1070 changegroup.writebundle(cg, fname, bundletype)
1071 1071
1072 1072 @command('cat',
1073 1073 [('o', 'output', '',
1074 1074 _('print output to file with formatted name'), _('FORMAT')),
1075 1075 ('r', 'rev', '', _('print the given revision'), _('REV')),
1076 1076 ('', 'decode', None, _('apply any matching decode filter')),
1077 1077 ] + walkopts,
1078 1078 _('[OPTION]... FILE...'))
1079 1079 def cat(ui, repo, file1, *pats, **opts):
1080 1080 """output the current or given revision of files
1081 1081
1082 1082 Print the specified files as they were at the given revision. If
1083 1083 no revision is given, the parent of the working directory is used,
1084 1084 or tip if no revision is checked out.
1085 1085
1086 1086 Output may be to a file, in which case the name of the file is
1087 1087 given using a format string. The formatting rules are the same as
1088 1088 for the export command, with the following additions:
1089 1089
1090 1090 :``%s``: basename of file being printed
1091 1091 :``%d``: dirname of file being printed, or '.' if in repository root
1092 1092 :``%p``: root-relative path name of file being printed
1093 1093
1094 1094 Returns 0 on success.
1095 1095 """
1096 1096 ctx = scmutil.revsingle(repo, opts.get('rev'))
1097 1097 err = 1
1098 1098 m = scmutil.match(ctx, (file1,) + pats, opts)
1099 1099 for abs in ctx.walk(m):
1100 1100 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1101 1101 pathname=abs)
1102 1102 data = ctx[abs].data()
1103 1103 if opts.get('decode'):
1104 1104 data = repo.wwritedata(abs, data)
1105 1105 fp.write(data)
1106 1106 fp.close()
1107 1107 err = 0
1108 1108 return err
1109 1109
1110 1110 @command('^clone',
1111 1111 [('U', 'noupdate', None,
1112 1112 _('the clone will include an empty working copy (only a repository)')),
1113 1113 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1114 1114 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1115 1115 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1116 1116 ('', 'pull', None, _('use pull protocol to copy metadata')),
1117 1117 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1118 1118 ] + remoteopts,
1119 1119 _('[OPTION]... SOURCE [DEST]'))
1120 1120 def clone(ui, source, dest=None, **opts):
1121 1121 """make a copy of an existing repository
1122 1122
1123 1123 Create a copy of an existing repository in a new directory.
1124 1124
1125 1125 If no destination directory name is specified, it defaults to the
1126 1126 basename of the source.
1127 1127
1128 1128 The location of the source is added to the new repository's
1129 1129 ``.hg/hgrc`` file, as the default to be used for future pulls.
1130 1130
1131 1131 Only local paths and ``ssh://`` URLs are supported as
1132 1132 destinations. For ``ssh://`` destinations, no working directory or
1133 1133 ``.hg/hgrc`` will be created on the remote side.
1134 1134
1135 1135 To pull only a subset of changesets, specify one or more revisions
1136 1136 identifiers with -r/--rev or branches with -b/--branch. The
1137 1137 resulting clone will contain only the specified changesets and
1138 1138 their ancestors. These options (or 'clone src#rev dest') imply
1139 1139 --pull, even for local source repositories. Note that specifying a
1140 1140 tag will include the tagged changeset but not the changeset
1141 1141 containing the tag.
1142 1142
1143 1143 To check out a particular version, use -u/--update, or
1144 1144 -U/--noupdate to create a clone with no working directory.
1145 1145
1146 1146 .. container:: verbose
1147 1147
1148 1148 For efficiency, hardlinks are used for cloning whenever the
1149 1149 source and destination are on the same filesystem (note this
1150 1150 applies only to the repository data, not to the working
1151 1151 directory). Some filesystems, such as AFS, implement hardlinking
1152 1152 incorrectly, but do not report errors. In these cases, use the
1153 1153 --pull option to avoid hardlinking.
1154 1154
1155 1155 In some cases, you can clone repositories and the working
1156 1156 directory using full hardlinks with ::
1157 1157
1158 1158 $ cp -al REPO REPOCLONE
1159 1159
1160 1160 This is the fastest way to clone, but it is not always safe. The
1161 1161 operation is not atomic (making sure REPO is not modified during
1162 1162 the operation is up to you) and you have to make sure your
1163 1163 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1164 1164 so). Also, this is not compatible with certain extensions that
1165 1165 place their metadata under the .hg directory, such as mq.
1166 1166
1167 1167 Mercurial will update the working directory to the first applicable
1168 1168 revision from this list:
1169 1169
1170 1170 a) null if -U or the source repository has no changesets
1171 1171 b) if -u . and the source repository is local, the first parent of
1172 1172 the source repository's working directory
1173 1173 c) the changeset specified with -u (if a branch name, this means the
1174 1174 latest head of that branch)
1175 1175 d) the changeset specified with -r
1176 1176 e) the tipmost head specified with -b
1177 1177 f) the tipmost head specified with the url#branch source syntax
1178 1178 g) the tipmost head of the default branch
1179 1179 h) tip
1180 1180
1181 1181 Examples:
1182 1182
1183 1183 - clone a remote repository to a new directory named hg/::
1184 1184
1185 1185 hg clone http://selenic.com/hg
1186 1186
1187 1187 - create a lightweight local clone::
1188 1188
1189 1189 hg clone project/ project-feature/
1190 1190
1191 1191 - clone from an absolute path on an ssh server (note double-slash)::
1192 1192
1193 1193 hg clone ssh://user@server//home/projects/alpha/
1194 1194
1195 1195 - do a high-speed clone over a LAN while checking out a
1196 1196 specified version::
1197 1197
1198 1198 hg clone --uncompressed http://server/repo -u 1.5
1199 1199
1200 1200 - create a repository without changesets after a particular revision::
1201 1201
1202 1202 hg clone -r 04e544 experimental/ good/
1203 1203
1204 1204 - clone (and track) a particular named branch::
1205 1205
1206 1206 hg clone http://selenic.com/hg#stable
1207 1207
1208 1208 See :hg:`help urls` for details on specifying URLs.
1209 1209
1210 1210 Returns 0 on success.
1211 1211 """
1212 1212 if opts.get('noupdate') and opts.get('updaterev'):
1213 1213 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1214 1214
1215 1215 r = hg.clone(ui, opts, source, dest,
1216 1216 pull=opts.get('pull'),
1217 1217 stream=opts.get('uncompressed'),
1218 1218 rev=opts.get('rev'),
1219 1219 update=opts.get('updaterev') or not opts.get('noupdate'),
1220 1220 branch=opts.get('branch'))
1221 1221
1222 1222 return r is None
1223 1223
1224 1224 @command('^commit|ci',
1225 1225 [('A', 'addremove', None,
1226 1226 _('mark new/missing files as added/removed before committing')),
1227 1227 ('', 'close-branch', None,
1228 1228 _('mark a branch as closed, hiding it from the branch list')),
1229 1229 ('', 'amend', None, _('amend the parent of the working dir')),
1230 1230 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1231 1231 _('[OPTION]... [FILE]...'))
1232 1232 def commit(ui, repo, *pats, **opts):
1233 1233 """commit the specified files or all outstanding changes
1234 1234
1235 1235 Commit changes to the given files into the repository. Unlike a
1236 1236 centralized SCM, this operation is a local operation. See
1237 1237 :hg:`push` for a way to actively distribute your changes.
1238 1238
1239 1239 If a list of files is omitted, all changes reported by :hg:`status`
1240 1240 will be committed.
1241 1241
1242 1242 If you are committing the result of a merge, do not provide any
1243 1243 filenames or -I/-X filters.
1244 1244
1245 1245 If no commit message is specified, Mercurial starts your
1246 1246 configured editor where you can enter a message. In case your
1247 1247 commit fails, you will find a backup of your message in
1248 1248 ``.hg/last-message.txt``.
1249 1249
1250 1250 The --amend flag can be used to amend the parent of the
1251 1251 working directory with a new commit that contains the changes
1252 1252 in the parent in addition to those currently reported by :hg:`status`,
1253 1253 if there are any. The old commit is stored in a backup bundle in
1254 1254 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1255 1255 on how to restore it).
1256 1256
1257 1257 Message, user and date are taken from the amended commit unless
1258 1258 specified. When a message isn't specified on the command line,
1259 1259 the editor will open with the message of the amended commit.
1260 1260
1261 1261 It is not possible to amend public changesets (see :hg:`help phases`)
1262 1262 or changesets that have children.
1263 1263
1264 1264 See :hg:`help dates` for a list of formats valid for -d/--date.
1265 1265
1266 1266 Returns 0 on success, 1 if nothing changed.
1267 1267 """
1268 1268 if opts.get('subrepos'):
1269 1269 # Let --subrepos on the command line override config setting.
1270 1270 ui.setconfig('ui', 'commitsubrepos', True)
1271 1271
1272 1272 extra = {}
1273 1273 if opts.get('close_branch'):
1274 1274 if repo['.'].node() not in repo.branchheads():
1275 1275 # The topo heads set is included in the branch heads set of the
1276 1276 # current branch, so it's sufficient to test branchheads
1277 1277 raise util.Abort(_('can only close branch heads'))
1278 1278 extra['close'] = 1
1279 1279
1280 1280 branch = repo[None].branch()
1281 1281 bheads = repo.branchheads(branch)
1282 1282
1283 1283 if opts.get('amend'):
1284 1284 if ui.configbool('ui', 'commitsubrepos'):
1285 1285 raise util.Abort(_('cannot amend recursively'))
1286 1286
1287 1287 old = repo['.']
1288 1288 if old.phase() == phases.public:
1289 1289 raise util.Abort(_('cannot amend public changesets'))
1290 1290 if len(old.parents()) > 1:
1291 1291 raise util.Abort(_('cannot amend merge changesets'))
1292 1292 if len(repo[None].parents()) > 1:
1293 1293 raise util.Abort(_('cannot amend while merging'))
1294 1294 if old.children():
1295 1295 raise util.Abort(_('cannot amend changeset with children'))
1296 1296
1297 1297 e = cmdutil.commiteditor
1298 1298 if opts.get('force_editor'):
1299 1299 e = cmdutil.commitforceeditor
1300 1300
1301 1301 def commitfunc(ui, repo, message, match, opts):
1302 1302 editor = e
1303 1303 # message contains text from -m or -l, if it's empty,
1304 1304 # open the editor with the old message
1305 1305 if not message:
1306 1306 message = old.description()
1307 1307 editor = cmdutil.commitforceeditor
1308 1308 return repo.commit(message,
1309 1309 opts.get('user') or old.user(),
1310 1310 opts.get('date') or old.date(),
1311 1311 match,
1312 1312 editor=editor,
1313 1313 extra=extra)
1314 1314
1315 1315 current = repo._bookmarkcurrent
1316 1316 marks = old.bookmarks()
1317 1317 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1318 1318 if node == old.node():
1319 1319 ui.status(_("nothing changed\n"))
1320 1320 return 1
1321 1321 elif marks:
1322 1322 ui.debug('moving bookmarks %r from %s to %s\n' %
1323 1323 (marks, old.hex(), hex(node)))
1324 1324 newmarks = repo._bookmarks
1325 1325 for bm in marks:
1326 1326 newmarks[bm] = node
1327 1327 if bm == current:
1328 1328 bookmarks.setcurrent(repo, bm)
1329 1329 newmarks.write()
1330 1330 else:
1331 1331 e = cmdutil.commiteditor
1332 1332 if opts.get('force_editor'):
1333 1333 e = cmdutil.commitforceeditor
1334 1334
1335 1335 def commitfunc(ui, repo, message, match, opts):
1336 1336 return repo.commit(message, opts.get('user'), opts.get('date'),
1337 1337 match, editor=e, extra=extra)
1338 1338
1339 1339 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1340 1340
1341 1341 if not node:
1342 1342 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1343 1343 if stat[3]:
1344 1344 ui.status(_("nothing changed (%d missing files, see "
1345 1345 "'hg status')\n") % len(stat[3]))
1346 1346 else:
1347 1347 ui.status(_("nothing changed\n"))
1348 1348 return 1
1349 1349
1350 1350 ctx = repo[node]
1351 1351 parents = ctx.parents()
1352 1352
1353 1353 if (not opts.get('amend') and bheads and node not in bheads and not
1354 1354 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1355 1355 ui.status(_('created new head\n'))
1356 1356 # The message is not printed for initial roots. For the other
1357 1357 # changesets, it is printed in the following situations:
1358 1358 #
1359 1359 # Par column: for the 2 parents with ...
1360 1360 # N: null or no parent
1361 1361 # B: parent is on another named branch
1362 1362 # C: parent is a regular non head changeset
1363 1363 # H: parent was a branch head of the current branch
1364 1364 # Msg column: whether we print "created new head" message
1365 1365 # In the following, it is assumed that there already exists some
1366 1366 # initial branch heads of the current branch, otherwise nothing is
1367 1367 # printed anyway.
1368 1368 #
1369 1369 # Par Msg Comment
1370 1370 # N N y additional topo root
1371 1371 #
1372 1372 # B N y additional branch root
1373 1373 # C N y additional topo head
1374 1374 # H N n usual case
1375 1375 #
1376 1376 # B B y weird additional branch root
1377 1377 # C B y branch merge
1378 1378 # H B n merge with named branch
1379 1379 #
1380 1380 # C C y additional head from merge
1381 1381 # C H n merge with a head
1382 1382 #
1383 1383 # H H n head merge: head count decreases
1384 1384
1385 1385 if not opts.get('close_branch'):
1386 1386 for r in parents:
1387 1387 if r.closesbranch() and r.branch() == branch:
1388 1388 ui.status(_('reopening closed branch head %d\n') % r)
1389 1389
1390 1390 if ui.debugflag:
1391 1391 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1392 1392 elif ui.verbose:
1393 1393 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1394 1394
1395 1395 @command('copy|cp',
1396 1396 [('A', 'after', None, _('record a copy that has already occurred')),
1397 1397 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1398 1398 ] + walkopts + dryrunopts,
1399 1399 _('[OPTION]... [SOURCE]... DEST'))
1400 1400 def copy(ui, repo, *pats, **opts):
1401 1401 """mark files as copied for the next commit
1402 1402
1403 1403 Mark dest as having copies of source files. If dest is a
1404 1404 directory, copies are put in that directory. If dest is a file,
1405 1405 the source must be a single file.
1406 1406
1407 1407 By default, this command copies the contents of files as they
1408 1408 exist in the working directory. If invoked with -A/--after, the
1409 1409 operation is recorded, but no copying is performed.
1410 1410
1411 1411 This command takes effect with the next commit. To undo a copy
1412 1412 before that, see :hg:`revert`.
1413 1413
1414 1414 Returns 0 on success, 1 if errors are encountered.
1415 1415 """
1416 1416 wlock = repo.wlock(False)
1417 1417 try:
1418 1418 return cmdutil.copy(ui, repo, pats, opts)
1419 1419 finally:
1420 1420 wlock.release()
1421 1421
1422 1422 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1423 1423 def debugancestor(ui, repo, *args):
1424 1424 """find the ancestor revision of two revisions in a given index"""
1425 1425 if len(args) == 3:
1426 1426 index, rev1, rev2 = args
1427 1427 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1428 1428 lookup = r.lookup
1429 1429 elif len(args) == 2:
1430 1430 if not repo:
1431 1431 raise util.Abort(_("there is no Mercurial repository here "
1432 1432 "(.hg not found)"))
1433 1433 rev1, rev2 = args
1434 1434 r = repo.changelog
1435 1435 lookup = repo.lookup
1436 1436 else:
1437 1437 raise util.Abort(_('either two or three arguments required'))
1438 1438 a = r.ancestor(lookup(rev1), lookup(rev2))
1439 1439 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1440 1440
1441 1441 @command('debugbuilddag',
1442 1442 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1443 1443 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1444 1444 ('n', 'new-file', None, _('add new file at each rev'))],
1445 1445 _('[OPTION]... [TEXT]'))
1446 1446 def debugbuilddag(ui, repo, text=None,
1447 1447 mergeable_file=False,
1448 1448 overwritten_file=False,
1449 1449 new_file=False):
1450 1450 """builds a repo with a given DAG from scratch in the current empty repo
1451 1451
1452 1452 The description of the DAG is read from stdin if not given on the
1453 1453 command line.
1454 1454
1455 1455 Elements:
1456 1456
1457 1457 - "+n" is a linear run of n nodes based on the current default parent
1458 1458 - "." is a single node based on the current default parent
1459 1459 - "$" resets the default parent to null (implied at the start);
1460 1460 otherwise the default parent is always the last node created
1461 1461 - "<p" sets the default parent to the backref p
1462 1462 - "*p" is a fork at parent p, which is a backref
1463 1463 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1464 1464 - "/p2" is a merge of the preceding node and p2
1465 1465 - ":tag" defines a local tag for the preceding node
1466 1466 - "@branch" sets the named branch for subsequent nodes
1467 1467 - "#...\\n" is a comment up to the end of the line
1468 1468
1469 1469 Whitespace between the above elements is ignored.
1470 1470
1471 1471 A backref is either
1472 1472
1473 1473 - a number n, which references the node curr-n, where curr is the current
1474 1474 node, or
1475 1475 - the name of a local tag you placed earlier using ":tag", or
1476 1476 - empty to denote the default parent.
1477 1477
1478 1478 All string valued-elements are either strictly alphanumeric, or must
1479 1479 be enclosed in double quotes ("..."), with "\\" as escape character.
1480 1480 """
1481 1481
1482 1482 if text is None:
1483 1483 ui.status(_("reading DAG from stdin\n"))
1484 1484 text = ui.fin.read()
1485 1485
1486 1486 cl = repo.changelog
1487 1487 if len(cl) > 0:
1488 1488 raise util.Abort(_('repository is not empty'))
1489 1489
1490 1490 # determine number of revs in DAG
1491 1491 total = 0
1492 1492 for type, data in dagparser.parsedag(text):
1493 1493 if type == 'n':
1494 1494 total += 1
1495 1495
1496 1496 if mergeable_file:
1497 1497 linesperrev = 2
1498 1498 # make a file with k lines per rev
1499 1499 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1500 1500 initialmergedlines.append("")
1501 1501
1502 1502 tags = []
1503 1503
1504 1504 lock = tr = None
1505 1505 try:
1506 1506 lock = repo.lock()
1507 1507 tr = repo.transaction("builddag")
1508 1508
1509 1509 at = -1
1510 1510 atbranch = 'default'
1511 1511 nodeids = []
1512 1512 id = 0
1513 1513 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1514 1514 for type, data in dagparser.parsedag(text):
1515 1515 if type == 'n':
1516 ui.note('node %s\n' % str(data))
1516 ui.note(('node %s\n' % str(data)))
1517 1517 id, ps = data
1518 1518
1519 1519 files = []
1520 1520 fctxs = {}
1521 1521
1522 1522 p2 = None
1523 1523 if mergeable_file:
1524 1524 fn = "mf"
1525 1525 p1 = repo[ps[0]]
1526 1526 if len(ps) > 1:
1527 1527 p2 = repo[ps[1]]
1528 1528 pa = p1.ancestor(p2)
1529 1529 base, local, other = [x[fn].data() for x in pa, p1, p2]
1530 1530 m3 = simplemerge.Merge3Text(base, local, other)
1531 1531 ml = [l.strip() for l in m3.merge_lines()]
1532 1532 ml.append("")
1533 1533 elif at > 0:
1534 1534 ml = p1[fn].data().split("\n")
1535 1535 else:
1536 1536 ml = initialmergedlines
1537 1537 ml[id * linesperrev] += " r%i" % id
1538 1538 mergedtext = "\n".join(ml)
1539 1539 files.append(fn)
1540 1540 fctxs[fn] = context.memfilectx(fn, mergedtext)
1541 1541
1542 1542 if overwritten_file:
1543 1543 fn = "of"
1544 1544 files.append(fn)
1545 1545 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1546 1546
1547 1547 if new_file:
1548 1548 fn = "nf%i" % id
1549 1549 files.append(fn)
1550 1550 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1551 1551 if len(ps) > 1:
1552 1552 if not p2:
1553 1553 p2 = repo[ps[1]]
1554 1554 for fn in p2:
1555 1555 if fn.startswith("nf"):
1556 1556 files.append(fn)
1557 1557 fctxs[fn] = p2[fn]
1558 1558
1559 1559 def fctxfn(repo, cx, path):
1560 1560 return fctxs.get(path)
1561 1561
1562 1562 if len(ps) == 0 or ps[0] < 0:
1563 1563 pars = [None, None]
1564 1564 elif len(ps) == 1:
1565 1565 pars = [nodeids[ps[0]], None]
1566 1566 else:
1567 1567 pars = [nodeids[p] for p in ps]
1568 1568 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1569 1569 date=(id, 0),
1570 1570 user="debugbuilddag",
1571 1571 extra={'branch': atbranch})
1572 1572 nodeid = repo.commitctx(cx)
1573 1573 nodeids.append(nodeid)
1574 1574 at = id
1575 1575 elif type == 'l':
1576 1576 id, name = data
1577 ui.note('tag %s\n' % name)
1577 ui.note(('tag %s\n' % name))
1578 1578 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1579 1579 elif type == 'a':
1580 ui.note('branch %s\n' % data)
1580 ui.note(('branch %s\n' % data))
1581 1581 atbranch = data
1582 1582 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1583 1583 tr.close()
1584 1584
1585 1585 if tags:
1586 1586 repo.opener.write("localtags", "".join(tags))
1587 1587 finally:
1588 1588 ui.progress(_('building'), None)
1589 1589 release(tr, lock)
1590 1590
1591 1591 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1592 1592 def debugbundle(ui, bundlepath, all=None, **opts):
1593 1593 """lists the contents of a bundle"""
1594 1594 f = hg.openpath(ui, bundlepath)
1595 1595 try:
1596 1596 gen = changegroup.readbundle(f, bundlepath)
1597 1597 if all:
1598 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1598 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1599 1599
1600 1600 def showchunks(named):
1601 1601 ui.write("\n%s\n" % named)
1602 1602 chain = None
1603 1603 while True:
1604 1604 chunkdata = gen.deltachunk(chain)
1605 1605 if not chunkdata:
1606 1606 break
1607 1607 node = chunkdata['node']
1608 1608 p1 = chunkdata['p1']
1609 1609 p2 = chunkdata['p2']
1610 1610 cs = chunkdata['cs']
1611 1611 deltabase = chunkdata['deltabase']
1612 1612 delta = chunkdata['delta']
1613 1613 ui.write("%s %s %s %s %s %s\n" %
1614 1614 (hex(node), hex(p1), hex(p2),
1615 1615 hex(cs), hex(deltabase), len(delta)))
1616 1616 chain = node
1617 1617
1618 1618 chunkdata = gen.changelogheader()
1619 1619 showchunks("changelog")
1620 1620 chunkdata = gen.manifestheader()
1621 1621 showchunks("manifest")
1622 1622 while True:
1623 1623 chunkdata = gen.filelogheader()
1624 1624 if not chunkdata:
1625 1625 break
1626 1626 fname = chunkdata['filename']
1627 1627 showchunks(fname)
1628 1628 else:
1629 1629 chunkdata = gen.changelogheader()
1630 1630 chain = None
1631 1631 while True:
1632 1632 chunkdata = gen.deltachunk(chain)
1633 1633 if not chunkdata:
1634 1634 break
1635 1635 node = chunkdata['node']
1636 1636 ui.write("%s\n" % hex(node))
1637 1637 chain = node
1638 1638 finally:
1639 1639 f.close()
1640 1640
1641 1641 @command('debugcheckstate', [], '')
1642 1642 def debugcheckstate(ui, repo):
1643 1643 """validate the correctness of the current dirstate"""
1644 1644 parent1, parent2 = repo.dirstate.parents()
1645 1645 m1 = repo[parent1].manifest()
1646 1646 m2 = repo[parent2].manifest()
1647 1647 errors = 0
1648 1648 for f in repo.dirstate:
1649 1649 state = repo.dirstate[f]
1650 1650 if state in "nr" and f not in m1:
1651 1651 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1652 1652 errors += 1
1653 1653 if state in "a" and f in m1:
1654 1654 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1655 1655 errors += 1
1656 1656 if state in "m" and f not in m1 and f not in m2:
1657 1657 ui.warn(_("%s in state %s, but not in either manifest\n") %
1658 1658 (f, state))
1659 1659 errors += 1
1660 1660 for f in m1:
1661 1661 state = repo.dirstate[f]
1662 1662 if state not in "nrm":
1663 1663 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1664 1664 errors += 1
1665 1665 if errors:
1666 1666 error = _(".hg/dirstate inconsistent with current parent's manifest")
1667 1667 raise util.Abort(error)
1668 1668
1669 1669 @command('debugcommands', [], _('[COMMAND]'))
1670 1670 def debugcommands(ui, cmd='', *args):
1671 1671 """list all available commands and options"""
1672 1672 for cmd, vals in sorted(table.iteritems()):
1673 1673 cmd = cmd.split('|')[0].strip('^')
1674 1674 opts = ', '.join([i[1] for i in vals[1]])
1675 1675 ui.write('%s: %s\n' % (cmd, opts))
1676 1676
1677 1677 @command('debugcomplete',
1678 1678 [('o', 'options', None, _('show the command options'))],
1679 1679 _('[-o] CMD'))
1680 1680 def debugcomplete(ui, cmd='', **opts):
1681 1681 """returns the completion list associated with the given command"""
1682 1682
1683 1683 if opts.get('options'):
1684 1684 options = []
1685 1685 otables = [globalopts]
1686 1686 if cmd:
1687 1687 aliases, entry = cmdutil.findcmd(cmd, table, False)
1688 1688 otables.append(entry[1])
1689 1689 for t in otables:
1690 1690 for o in t:
1691 1691 if "(DEPRECATED)" in o[3]:
1692 1692 continue
1693 1693 if o[0]:
1694 1694 options.append('-%s' % o[0])
1695 1695 options.append('--%s' % o[1])
1696 1696 ui.write("%s\n" % "\n".join(options))
1697 1697 return
1698 1698
1699 1699 cmdlist = cmdutil.findpossible(cmd, table)
1700 1700 if ui.verbose:
1701 1701 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1702 1702 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1703 1703
1704 1704 @command('debugdag',
1705 1705 [('t', 'tags', None, _('use tags as labels')),
1706 1706 ('b', 'branches', None, _('annotate with branch names')),
1707 1707 ('', 'dots', None, _('use dots for runs')),
1708 1708 ('s', 'spaces', None, _('separate elements by spaces'))],
1709 1709 _('[OPTION]... [FILE [REV]...]'))
1710 1710 def debugdag(ui, repo, file_=None, *revs, **opts):
1711 1711 """format the changelog or an index DAG as a concise textual description
1712 1712
1713 1713 If you pass a revlog index, the revlog's DAG is emitted. If you list
1714 1714 revision numbers, they get labeled in the output as rN.
1715 1715
1716 1716 Otherwise, the changelog DAG of the current repo is emitted.
1717 1717 """
1718 1718 spaces = opts.get('spaces')
1719 1719 dots = opts.get('dots')
1720 1720 if file_:
1721 1721 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1722 1722 revs = set((int(r) for r in revs))
1723 1723 def events():
1724 1724 for r in rlog:
1725 1725 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1726 1726 if p != -1)))
1727 1727 if r in revs:
1728 1728 yield 'l', (r, "r%i" % r)
1729 1729 elif repo:
1730 1730 cl = repo.changelog
1731 1731 tags = opts.get('tags')
1732 1732 branches = opts.get('branches')
1733 1733 if tags:
1734 1734 labels = {}
1735 1735 for l, n in repo.tags().items():
1736 1736 labels.setdefault(cl.rev(n), []).append(l)
1737 1737 def events():
1738 1738 b = "default"
1739 1739 for r in cl:
1740 1740 if branches:
1741 1741 newb = cl.read(cl.node(r))[5]['branch']
1742 1742 if newb != b:
1743 1743 yield 'a', newb
1744 1744 b = newb
1745 1745 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1746 1746 if p != -1)))
1747 1747 if tags:
1748 1748 ls = labels.get(r)
1749 1749 if ls:
1750 1750 for l in ls:
1751 1751 yield 'l', (r, l)
1752 1752 else:
1753 1753 raise util.Abort(_('need repo for changelog dag'))
1754 1754
1755 1755 for line in dagparser.dagtextlines(events(),
1756 1756 addspaces=spaces,
1757 1757 wraplabels=True,
1758 1758 wrapannotations=True,
1759 1759 wrapnonlinear=dots,
1760 1760 usedots=dots,
1761 1761 maxlinewidth=70):
1762 1762 ui.write(line)
1763 1763 ui.write("\n")
1764 1764
1765 1765 @command('debugdata',
1766 1766 [('c', 'changelog', False, _('open changelog')),
1767 1767 ('m', 'manifest', False, _('open manifest'))],
1768 1768 _('-c|-m|FILE REV'))
1769 1769 def debugdata(ui, repo, file_, rev = None, **opts):
1770 1770 """dump the contents of a data file revision"""
1771 1771 if opts.get('changelog') or opts.get('manifest'):
1772 1772 file_, rev = None, file_
1773 1773 elif rev is None:
1774 1774 raise error.CommandError('debugdata', _('invalid arguments'))
1775 1775 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1776 1776 try:
1777 1777 ui.write(r.revision(r.lookup(rev)))
1778 1778 except KeyError:
1779 1779 raise util.Abort(_('invalid revision identifier %s') % rev)
1780 1780
1781 1781 @command('debugdate',
1782 1782 [('e', 'extended', None, _('try extended date formats'))],
1783 1783 _('[-e] DATE [RANGE]'))
1784 1784 def debugdate(ui, date, range=None, **opts):
1785 1785 """parse and display a date"""
1786 1786 if opts["extended"]:
1787 1787 d = util.parsedate(date, util.extendeddateformats)
1788 1788 else:
1789 1789 d = util.parsedate(date)
1790 ui.write("internal: %s %s\n" % d)
1791 ui.write("standard: %s\n" % util.datestr(d))
1790 ui.write(("internal: %s %s\n") % d)
1791 ui.write(("standard: %s\n") % util.datestr(d))
1792 1792 if range:
1793 1793 m = util.matchdate(range)
1794 ui.write("match: %s\n" % m(d[0]))
1794 ui.write(("match: %s\n") % m(d[0]))
1795 1795
1796 1796 @command('debugdiscovery',
1797 1797 [('', 'old', None, _('use old-style discovery')),
1798 1798 ('', 'nonheads', None,
1799 1799 _('use old-style discovery with non-heads included')),
1800 1800 ] + remoteopts,
1801 1801 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1802 1802 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1803 1803 """runs the changeset discovery protocol in isolation"""
1804 1804 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1805 1805 opts.get('branch'))
1806 1806 remote = hg.peer(repo, opts, remoteurl)
1807 1807 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1808 1808
1809 1809 # make sure tests are repeatable
1810 1810 random.seed(12323)
1811 1811
1812 1812 def doit(localheads, remoteheads, remote=remote):
1813 1813 if opts.get('old'):
1814 1814 if localheads:
1815 1815 raise util.Abort('cannot use localheads with old style '
1816 1816 'discovery')
1817 1817 if not util.safehasattr(remote, 'branches'):
1818 1818 # enable in-client legacy support
1819 1819 remote = localrepo.locallegacypeer(remote.local())
1820 1820 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1821 1821 force=True)
1822 1822 common = set(common)
1823 1823 if not opts.get('nonheads'):
1824 ui.write("unpruned common: %s\n" % " ".join([short(n)
1824 ui.write(("unpruned common: %s\n") % " ".join([short(n)
1825 1825 for n in common]))
1826 1826 dag = dagutil.revlogdag(repo.changelog)
1827 1827 all = dag.ancestorset(dag.internalizeall(common))
1828 1828 common = dag.externalizeall(dag.headsetofconnecteds(all))
1829 1829 else:
1830 1830 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1831 1831 common = set(common)
1832 1832 rheads = set(hds)
1833 1833 lheads = set(repo.heads())
1834 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1834 ui.write(("common heads: %s\n") % " ".join([short(n) for n in common]))
1835 1835 if lheads <= common:
1836 ui.write("local is subset\n")
1836 ui.write(("local is subset\n"))
1837 1837 elif rheads <= common:
1838 ui.write("remote is subset\n")
1838 ui.write(("remote is subset\n"))
1839 1839
1840 1840 serverlogs = opts.get('serverlog')
1841 1841 if serverlogs:
1842 1842 for filename in serverlogs:
1843 1843 logfile = open(filename, 'r')
1844 1844 try:
1845 1845 line = logfile.readline()
1846 1846 while line:
1847 1847 parts = line.strip().split(';')
1848 1848 op = parts[1]
1849 1849 if op == 'cg':
1850 1850 pass
1851 1851 elif op == 'cgss':
1852 1852 doit(parts[2].split(' '), parts[3].split(' '))
1853 1853 elif op == 'unb':
1854 1854 doit(parts[3].split(' '), parts[2].split(' '))
1855 1855 line = logfile.readline()
1856 1856 finally:
1857 1857 logfile.close()
1858 1858
1859 1859 else:
1860 1860 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1861 1861 opts.get('remote_head'))
1862 1862 localrevs = opts.get('local_head')
1863 1863 doit(localrevs, remoterevs)
1864 1864
1865 1865 @command('debugfileset',
1866 1866 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1867 1867 _('[-r REV] FILESPEC'))
1868 1868 def debugfileset(ui, repo, expr, **opts):
1869 1869 '''parse and apply a fileset specification'''
1870 1870 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1871 1871 if ui.verbose:
1872 1872 tree = fileset.parse(expr)[0]
1873 1873 ui.note(tree, "\n")
1874 1874
1875 1875 for f in fileset.getfileset(ctx, expr):
1876 1876 ui.write("%s\n" % f)
1877 1877
1878 1878 @command('debugfsinfo', [], _('[PATH]'))
1879 1879 def debugfsinfo(ui, path = "."):
1880 1880 """show information detected about current filesystem"""
1881 1881 util.writefile('.debugfsinfo', '')
1882 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1883 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1884 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1882 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
1883 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
1884 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
1885 1885 and 'yes' or 'no'))
1886 1886 os.unlink('.debugfsinfo')
1887 1887
1888 1888 @command('debuggetbundle',
1889 1889 [('H', 'head', [], _('id of head node'), _('ID')),
1890 1890 ('C', 'common', [], _('id of common node'), _('ID')),
1891 1891 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1892 1892 _('REPO FILE [-H|-C ID]...'))
1893 1893 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1894 1894 """retrieves a bundle from a repo
1895 1895
1896 1896 Every ID must be a full-length hex node id string. Saves the bundle to the
1897 1897 given file.
1898 1898 """
1899 1899 repo = hg.peer(ui, opts, repopath)
1900 1900 if not repo.capable('getbundle'):
1901 1901 raise util.Abort("getbundle() not supported by target repository")
1902 1902 args = {}
1903 1903 if common:
1904 1904 args['common'] = [bin(s) for s in common]
1905 1905 if head:
1906 1906 args['heads'] = [bin(s) for s in head]
1907 1907 bundle = repo.getbundle('debug', **args)
1908 1908
1909 1909 bundletype = opts.get('type', 'bzip2').lower()
1910 1910 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1911 1911 bundletype = btypes.get(bundletype)
1912 1912 if bundletype not in changegroup.bundletypes:
1913 1913 raise util.Abort(_('unknown bundle type specified with --type'))
1914 1914 changegroup.writebundle(bundle, bundlepath, bundletype)
1915 1915
1916 1916 @command('debugignore', [], '')
1917 1917 def debugignore(ui, repo, *values, **opts):
1918 1918 """display the combined ignore pattern"""
1919 1919 ignore = repo.dirstate._ignore
1920 1920 includepat = getattr(ignore, 'includepat', None)
1921 1921 if includepat is not None:
1922 1922 ui.write("%s\n" % includepat)
1923 1923 else:
1924 1924 raise util.Abort(_("no ignore patterns found"))
1925 1925
1926 1926 @command('debugindex',
1927 1927 [('c', 'changelog', False, _('open changelog')),
1928 1928 ('m', 'manifest', False, _('open manifest')),
1929 1929 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1930 1930 _('[-f FORMAT] -c|-m|FILE'))
1931 1931 def debugindex(ui, repo, file_ = None, **opts):
1932 1932 """dump the contents of an index file"""
1933 1933 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1934 1934 format = opts.get('format', 0)
1935 1935 if format not in (0, 1):
1936 1936 raise util.Abort(_("unknown format %d") % format)
1937 1937
1938 1938 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1939 1939 if generaldelta:
1940 1940 basehdr = ' delta'
1941 1941 else:
1942 1942 basehdr = ' base'
1943 1943
1944 1944 if format == 0:
1945 1945 ui.write(" rev offset length " + basehdr + " linkrev"
1946 1946 " nodeid p1 p2\n")
1947 1947 elif format == 1:
1948 1948 ui.write(" rev flag offset length"
1949 1949 " size " + basehdr + " link p1 p2"
1950 1950 " nodeid\n")
1951 1951
1952 1952 for i in r:
1953 1953 node = r.node(i)
1954 1954 if generaldelta:
1955 1955 base = r.deltaparent(i)
1956 1956 else:
1957 1957 base = r.chainbase(i)
1958 1958 if format == 0:
1959 1959 try:
1960 1960 pp = r.parents(node)
1961 1961 except Exception:
1962 1962 pp = [nullid, nullid]
1963 1963 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1964 1964 i, r.start(i), r.length(i), base, r.linkrev(i),
1965 1965 short(node), short(pp[0]), short(pp[1])))
1966 1966 elif format == 1:
1967 1967 pr = r.parentrevs(i)
1968 1968 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1969 1969 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1970 1970 base, r.linkrev(i), pr[0], pr[1], short(node)))
1971 1971
1972 1972 @command('debugindexdot', [], _('FILE'))
1973 1973 def debugindexdot(ui, repo, file_):
1974 1974 """dump an index DAG as a graphviz dot file"""
1975 1975 r = None
1976 1976 if repo:
1977 1977 filelog = repo.file(file_)
1978 1978 if len(filelog):
1979 1979 r = filelog
1980 1980 if not r:
1981 1981 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1982 ui.write("digraph G {\n")
1982 ui.write(("digraph G {\n"))
1983 1983 for i in r:
1984 1984 node = r.node(i)
1985 1985 pp = r.parents(node)
1986 1986 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1987 1987 if pp[1] != nullid:
1988 1988 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1989 1989 ui.write("}\n")
1990 1990
1991 1991 @command('debuginstall', [], '')
1992 1992 def debuginstall(ui):
1993 1993 '''test Mercurial installation
1994 1994
1995 1995 Returns 0 on success.
1996 1996 '''
1997 1997
1998 1998 def writetemp(contents):
1999 1999 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2000 2000 f = os.fdopen(fd, "wb")
2001 2001 f.write(contents)
2002 2002 f.close()
2003 2003 return name
2004 2004
2005 2005 problems = 0
2006 2006
2007 2007 # encoding
2008 2008 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2009 2009 try:
2010 2010 encoding.fromlocal("test")
2011 2011 except util.Abort, inst:
2012 2012 ui.write(" %s\n" % inst)
2013 2013 ui.write(_(" (check that your locale is properly set)\n"))
2014 2014 problems += 1
2015 2015
2016 2016 # Python lib
2017 2017 ui.status(_("checking Python lib (%s)...\n")
2018 2018 % os.path.dirname(os.__file__))
2019 2019
2020 2020 # compiled modules
2021 2021 ui.status(_("checking installed modules (%s)...\n")
2022 2022 % os.path.dirname(__file__))
2023 2023 try:
2024 2024 import bdiff, mpatch, base85, osutil
2025 2025 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2026 2026 except Exception, inst:
2027 2027 ui.write(" %s\n" % inst)
2028 2028 ui.write(_(" One or more extensions could not be found"))
2029 2029 ui.write(_(" (check that you compiled the extensions)\n"))
2030 2030 problems += 1
2031 2031
2032 2032 # templates
2033 2033 import templater
2034 2034 p = templater.templatepath()
2035 2035 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2036 2036 try:
2037 2037 templater.templater(templater.templatepath("map-cmdline.default"))
2038 2038 except Exception, inst:
2039 2039 ui.write(" %s\n" % inst)
2040 2040 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2041 2041 problems += 1
2042 2042
2043 2043 # editor
2044 2044 ui.status(_("checking commit editor...\n"))
2045 2045 editor = ui.geteditor()
2046 2046 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2047 2047 if not cmdpath:
2048 2048 if editor == 'vi':
2049 2049 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2050 2050 ui.write(_(" (specify a commit editor in your configuration"
2051 2051 " file)\n"))
2052 2052 else:
2053 2053 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2054 2054 ui.write(_(" (specify a commit editor in your configuration"
2055 2055 " file)\n"))
2056 2056 problems += 1
2057 2057
2058 2058 # check username
2059 2059 ui.status(_("checking username...\n"))
2060 2060 try:
2061 2061 ui.username()
2062 2062 except util.Abort, e:
2063 2063 ui.write(" %s\n" % e)
2064 2064 ui.write(_(" (specify a username in your configuration file)\n"))
2065 2065 problems += 1
2066 2066
2067 2067 if not problems:
2068 2068 ui.status(_("no problems detected\n"))
2069 2069 else:
2070 2070 ui.write(_("%s problems detected,"
2071 2071 " please check your install!\n") % problems)
2072 2072
2073 2073 return problems
2074 2074
2075 2075 @command('debugknown', [], _('REPO ID...'))
2076 2076 def debugknown(ui, repopath, *ids, **opts):
2077 2077 """test whether node ids are known to a repo
2078 2078
2079 2079 Every ID must be a full-length hex node id string. Returns a list of 0s
2080 2080 and 1s indicating unknown/known.
2081 2081 """
2082 2082 repo = hg.peer(ui, opts, repopath)
2083 2083 if not repo.capable('known'):
2084 2084 raise util.Abort("known() not supported by target repository")
2085 2085 flags = repo.known([bin(s) for s in ids])
2086 2086 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2087 2087
2088 2088 @command('debugobsolete',
2089 2089 [('', 'flags', 0, _('markers flag')),
2090 2090 ] + commitopts2,
2091 2091 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2092 2092 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2093 2093 """create arbitrary obsolete marker"""
2094 2094 def parsenodeid(s):
2095 2095 try:
2096 2096 # We do not use revsingle/revrange functions here to accept
2097 2097 # arbitrary node identifiers, possibly not present in the
2098 2098 # local repository.
2099 2099 n = bin(s)
2100 2100 if len(n) != len(nullid):
2101 2101 raise TypeError()
2102 2102 return n
2103 2103 except TypeError:
2104 2104 raise util.Abort('changeset references must be full hexadecimal '
2105 2105 'node identifiers')
2106 2106
2107 2107 if precursor is not None:
2108 2108 metadata = {}
2109 2109 if 'date' in opts:
2110 2110 metadata['date'] = opts['date']
2111 2111 metadata['user'] = opts['user'] or ui.username()
2112 2112 succs = tuple(parsenodeid(succ) for succ in successors)
2113 2113 l = repo.lock()
2114 2114 try:
2115 2115 tr = repo.transaction('debugobsolete')
2116 2116 try:
2117 2117 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2118 2118 opts['flags'], metadata)
2119 2119 tr.close()
2120 2120 finally:
2121 2121 tr.release()
2122 2122 finally:
2123 2123 l.release()
2124 2124 else:
2125 2125 for m in obsolete.allmarkers(repo):
2126 2126 ui.write(hex(m.precnode()))
2127 2127 for repl in m.succnodes():
2128 2128 ui.write(' ')
2129 2129 ui.write(hex(repl))
2130 2130 ui.write(' %X ' % m._data[2])
2131 2131 ui.write(m.metadata())
2132 2132 ui.write('\n')
2133 2133
2134 2134 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2135 2135 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2136 2136 '''access the pushkey key/value protocol
2137 2137
2138 2138 With two args, list the keys in the given namespace.
2139 2139
2140 2140 With five args, set a key to new if it currently is set to old.
2141 2141 Reports success or failure.
2142 2142 '''
2143 2143
2144 2144 target = hg.peer(ui, {}, repopath)
2145 2145 if keyinfo:
2146 2146 key, old, new = keyinfo
2147 2147 r = target.pushkey(namespace, key, old, new)
2148 2148 ui.status(str(r) + '\n')
2149 2149 return not r
2150 2150 else:
2151 2151 for k, v in target.listkeys(namespace).iteritems():
2152 2152 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2153 2153 v.encode('string-escape')))
2154 2154
2155 2155 @command('debugpvec', [], _('A B'))
2156 2156 def debugpvec(ui, repo, a, b=None):
2157 2157 ca = scmutil.revsingle(repo, a)
2158 2158 cb = scmutil.revsingle(repo, b)
2159 2159 pa = pvec.ctxpvec(ca)
2160 2160 pb = pvec.ctxpvec(cb)
2161 2161 if pa == pb:
2162 2162 rel = "="
2163 2163 elif pa > pb:
2164 2164 rel = ">"
2165 2165 elif pa < pb:
2166 2166 rel = "<"
2167 2167 elif pa | pb:
2168 2168 rel = "|"
2169 2169 ui.write(_("a: %s\n") % pa)
2170 2170 ui.write(_("b: %s\n") % pb)
2171 2171 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2172 2172 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2173 2173 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2174 2174 pa.distance(pb), rel))
2175 2175
2176 2176 @command('debugrebuildstate',
2177 2177 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2178 2178 _('[-r REV] [REV]'))
2179 2179 def debugrebuildstate(ui, repo, rev="tip"):
2180 2180 """rebuild the dirstate as it would look like for the given revision"""
2181 2181 ctx = scmutil.revsingle(repo, rev)
2182 2182 wlock = repo.wlock()
2183 2183 try:
2184 2184 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2185 2185 finally:
2186 2186 wlock.release()
2187 2187
2188 2188 @command('debugrename',
2189 2189 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2190 2190 _('[-r REV] FILE'))
2191 2191 def debugrename(ui, repo, file1, *pats, **opts):
2192 2192 """dump rename information"""
2193 2193
2194 2194 ctx = scmutil.revsingle(repo, opts.get('rev'))
2195 2195 m = scmutil.match(ctx, (file1,) + pats, opts)
2196 2196 for abs in ctx.walk(m):
2197 2197 fctx = ctx[abs]
2198 2198 o = fctx.filelog().renamed(fctx.filenode())
2199 2199 rel = m.rel(abs)
2200 2200 if o:
2201 2201 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2202 2202 else:
2203 2203 ui.write(_("%s not renamed\n") % rel)
2204 2204
2205 2205 @command('debugrevlog',
2206 2206 [('c', 'changelog', False, _('open changelog')),
2207 2207 ('m', 'manifest', False, _('open manifest')),
2208 2208 ('d', 'dump', False, _('dump index data'))],
2209 2209 _('-c|-m|FILE'))
2210 2210 def debugrevlog(ui, repo, file_ = None, **opts):
2211 2211 """show data and statistics about a revlog"""
2212 2212 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2213 2213
2214 2214 if opts.get("dump"):
2215 2215 numrevs = len(r)
2216 2216 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2217 2217 " rawsize totalsize compression heads\n")
2218 2218 ts = 0
2219 2219 heads = set()
2220 2220 for rev in xrange(numrevs):
2221 2221 dbase = r.deltaparent(rev)
2222 2222 if dbase == -1:
2223 2223 dbase = rev
2224 2224 cbase = r.chainbase(rev)
2225 2225 p1, p2 = r.parentrevs(rev)
2226 2226 rs = r.rawsize(rev)
2227 2227 ts = ts + rs
2228 2228 heads -= set(r.parentrevs(rev))
2229 2229 heads.add(rev)
2230 2230 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2231 2231 (rev, p1, p2, r.start(rev), r.end(rev),
2232 2232 r.start(dbase), r.start(cbase),
2233 2233 r.start(p1), r.start(p2),
2234 2234 rs, ts, ts / r.end(rev), len(heads)))
2235 2235 return 0
2236 2236
2237 2237 v = r.version
2238 2238 format = v & 0xFFFF
2239 2239 flags = []
2240 2240 gdelta = False
2241 2241 if v & revlog.REVLOGNGINLINEDATA:
2242 2242 flags.append('inline')
2243 2243 if v & revlog.REVLOGGENERALDELTA:
2244 2244 gdelta = True
2245 2245 flags.append('generaldelta')
2246 2246 if not flags:
2247 2247 flags = ['(none)']
2248 2248
2249 2249 nummerges = 0
2250 2250 numfull = 0
2251 2251 numprev = 0
2252 2252 nump1 = 0
2253 2253 nump2 = 0
2254 2254 numother = 0
2255 2255 nump1prev = 0
2256 2256 nump2prev = 0
2257 2257 chainlengths = []
2258 2258
2259 2259 datasize = [None, 0, 0L]
2260 2260 fullsize = [None, 0, 0L]
2261 2261 deltasize = [None, 0, 0L]
2262 2262
2263 2263 def addsize(size, l):
2264 2264 if l[0] is None or size < l[0]:
2265 2265 l[0] = size
2266 2266 if size > l[1]:
2267 2267 l[1] = size
2268 2268 l[2] += size
2269 2269
2270 2270 numrevs = len(r)
2271 2271 for rev in xrange(numrevs):
2272 2272 p1, p2 = r.parentrevs(rev)
2273 2273 delta = r.deltaparent(rev)
2274 2274 if format > 0:
2275 2275 addsize(r.rawsize(rev), datasize)
2276 2276 if p2 != nullrev:
2277 2277 nummerges += 1
2278 2278 size = r.length(rev)
2279 2279 if delta == nullrev:
2280 2280 chainlengths.append(0)
2281 2281 numfull += 1
2282 2282 addsize(size, fullsize)
2283 2283 else:
2284 2284 chainlengths.append(chainlengths[delta] + 1)
2285 2285 addsize(size, deltasize)
2286 2286 if delta == rev - 1:
2287 2287 numprev += 1
2288 2288 if delta == p1:
2289 2289 nump1prev += 1
2290 2290 elif delta == p2:
2291 2291 nump2prev += 1
2292 2292 elif delta == p1:
2293 2293 nump1 += 1
2294 2294 elif delta == p2:
2295 2295 nump2 += 1
2296 2296 elif delta != nullrev:
2297 2297 numother += 1
2298 2298
2299 2299 # Adjust size min value for empty cases
2300 2300 for size in (datasize, fullsize, deltasize):
2301 2301 if size[0] is None:
2302 2302 size[0] = 0
2303 2303
2304 2304 numdeltas = numrevs - numfull
2305 2305 numoprev = numprev - nump1prev - nump2prev
2306 2306 totalrawsize = datasize[2]
2307 2307 datasize[2] /= numrevs
2308 2308 fulltotal = fullsize[2]
2309 2309 fullsize[2] /= numfull
2310 2310 deltatotal = deltasize[2]
2311 2311 if numrevs - numfull > 0:
2312 2312 deltasize[2] /= numrevs - numfull
2313 2313 totalsize = fulltotal + deltatotal
2314 2314 avgchainlen = sum(chainlengths) / numrevs
2315 2315 compratio = totalrawsize / totalsize
2316 2316
2317 2317 basedfmtstr = '%%%dd\n'
2318 2318 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2319 2319
2320 2320 def dfmtstr(max):
2321 2321 return basedfmtstr % len(str(max))
2322 2322 def pcfmtstr(max, padding=0):
2323 2323 return basepcfmtstr % (len(str(max)), ' ' * padding)
2324 2324
2325 2325 def pcfmt(value, total):
2326 2326 return (value, 100 * float(value) / total)
2327 2327
2328 ui.write('format : %d\n' % format)
2329 ui.write('flags : %s\n' % ', '.join(flags))
2328 ui.write(('format : %d\n') % format)
2329 ui.write(('flags : %s\n') % ', '.join(flags))
2330 2330
2331 2331 ui.write('\n')
2332 2332 fmt = pcfmtstr(totalsize)
2333 2333 fmt2 = dfmtstr(totalsize)
2334 ui.write('revisions : ' + fmt2 % numrevs)
2335 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2336 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2337 ui.write('revisions : ' + fmt2 % numrevs)
2338 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2339 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2340 ui.write('revision size : ' + fmt2 % totalsize)
2341 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2342 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2334 ui.write(('revisions : ') + fmt2 % numrevs)
2335 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2336 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2337 ui.write(('revisions : ') + fmt2 % numrevs)
2338 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2339 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2340 ui.write(('revision size : ') + fmt2 % totalsize)
2341 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2342 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2343 2343
2344 2344 ui.write('\n')
2345 2345 fmt = dfmtstr(max(avgchainlen, compratio))
2346 ui.write('avg chain length : ' + fmt % avgchainlen)
2347 ui.write('compression ratio : ' + fmt % compratio)
2346 ui.write(('avg chain length : ') + fmt % avgchainlen)
2347 ui.write(('compression ratio : ') + fmt % compratio)
2348 2348
2349 2349 if format > 0:
2350 2350 ui.write('\n')
2351 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2351 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2352 2352 % tuple(datasize))
2353 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2353 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2354 2354 % tuple(fullsize))
2355 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2355 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2356 2356 % tuple(deltasize))
2357 2357
2358 2358 if numdeltas > 0:
2359 2359 ui.write('\n')
2360 2360 fmt = pcfmtstr(numdeltas)
2361 2361 fmt2 = pcfmtstr(numdeltas, 4)
2362 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2362 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2363 2363 if numprev > 0:
2364 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev,
2364 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2365 2365 numprev))
2366 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev,
2366 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2367 2367 numprev))
2368 ui.write(' other : ' + fmt2 % pcfmt(numoprev,
2368 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2369 2369 numprev))
2370 2370 if gdelta:
2371 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2372 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2373 ui.write('deltas against other : ' + fmt % pcfmt(numother,
2371 ui.write(('deltas against p1 : ')
2372 + fmt % pcfmt(nump1, numdeltas))
2373 ui.write(('deltas against p2 : ')
2374 + fmt % pcfmt(nump2, numdeltas))
2375 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2374 2376 numdeltas))
2375 2377
2376 2378 @command('debugrevspec', [], ('REVSPEC'))
2377 2379 def debugrevspec(ui, repo, expr):
2378 2380 """parse and apply a revision specification
2379 2381
2380 2382 Use --verbose to print the parsed tree before and after aliases
2381 2383 expansion.
2382 2384 """
2383 2385 if ui.verbose:
2384 2386 tree = revset.parse(expr)[0]
2385 2387 ui.note(revset.prettyformat(tree), "\n")
2386 2388 newtree = revset.findaliases(ui, tree)
2387 2389 if newtree != tree:
2388 2390 ui.note(revset.prettyformat(newtree), "\n")
2389 2391 func = revset.match(ui, expr)
2390 2392 for c in func(repo, range(len(repo))):
2391 2393 ui.write("%s\n" % c)
2392 2394
2393 2395 @command('debugsetparents', [], _('REV1 [REV2]'))
2394 2396 def debugsetparents(ui, repo, rev1, rev2=None):
2395 2397 """manually set the parents of the current working directory
2396 2398
2397 2399 This is useful for writing repository conversion tools, but should
2398 2400 be used with care.
2399 2401
2400 2402 Returns 0 on success.
2401 2403 """
2402 2404
2403 2405 r1 = scmutil.revsingle(repo, rev1).node()
2404 2406 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2405 2407
2406 2408 wlock = repo.wlock()
2407 2409 try:
2408 2410 repo.setparents(r1, r2)
2409 2411 finally:
2410 2412 wlock.release()
2411 2413
2412 2414 @command('debugstate',
2413 2415 [('', 'nodates', None, _('do not display the saved mtime')),
2414 2416 ('', 'datesort', None, _('sort by saved mtime'))],
2415 2417 _('[OPTION]...'))
2416 2418 def debugstate(ui, repo, nodates=None, datesort=None):
2417 2419 """show the contents of the current dirstate"""
2418 2420 timestr = ""
2419 2421 showdate = not nodates
2420 2422 if datesort:
2421 2423 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2422 2424 else:
2423 2425 keyfunc = None # sort by filename
2424 2426 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2425 2427 if showdate:
2426 2428 if ent[3] == -1:
2427 2429 # Pad or slice to locale representation
2428 2430 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2429 2431 time.localtime(0)))
2430 2432 timestr = 'unset'
2431 2433 timestr = (timestr[:locale_len] +
2432 2434 ' ' * (locale_len - len(timestr)))
2433 2435 else:
2434 2436 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2435 2437 time.localtime(ent[3]))
2436 2438 if ent[1] & 020000:
2437 2439 mode = 'lnk'
2438 2440 else:
2439 2441 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2440 2442 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2441 2443 for f in repo.dirstate.copies():
2442 2444 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2443 2445
2444 2446 @command('debugsub',
2445 2447 [('r', 'rev', '',
2446 2448 _('revision to check'), _('REV'))],
2447 2449 _('[-r REV] [REV]'))
2448 2450 def debugsub(ui, repo, rev=None):
2449 2451 ctx = scmutil.revsingle(repo, rev, None)
2450 2452 for k, v in sorted(ctx.substate.items()):
2451 ui.write('path %s\n' % k)
2452 ui.write(' source %s\n' % v[0])
2453 ui.write(' revision %s\n' % v[1])
2453 ui.write(('path %s\n') % k)
2454 ui.write((' source %s\n') % v[0])
2455 ui.write((' revision %s\n') % v[1])
2454 2456
2455 2457 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2456 2458 def debugwalk(ui, repo, *pats, **opts):
2457 2459 """show how files match on given patterns"""
2458 2460 m = scmutil.match(repo[None], pats, opts)
2459 2461 items = list(repo.walk(m))
2460 2462 if not items:
2461 2463 return
2462 2464 f = lambda fn: fn
2463 2465 if ui.configbool('ui', 'slash') and os.sep != '/':
2464 2466 f = lambda fn: util.normpath(fn)
2465 2467 fmt = 'f %%-%ds %%-%ds %%s' % (
2466 2468 max([len(abs) for abs in items]),
2467 2469 max([len(m.rel(abs)) for abs in items]))
2468 2470 for abs in items:
2469 2471 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2470 2472 ui.write("%s\n" % line.rstrip())
2471 2473
2472 2474 @command('debugwireargs',
2473 2475 [('', 'three', '', 'three'),
2474 2476 ('', 'four', '', 'four'),
2475 2477 ('', 'five', '', 'five'),
2476 2478 ] + remoteopts,
2477 2479 _('REPO [OPTIONS]... [ONE [TWO]]'))
2478 2480 def debugwireargs(ui, repopath, *vals, **opts):
2479 2481 repo = hg.peer(ui, opts, repopath)
2480 2482 for opt in remoteopts:
2481 2483 del opts[opt[1]]
2482 2484 args = {}
2483 2485 for k, v in opts.iteritems():
2484 2486 if v:
2485 2487 args[k] = v
2486 2488 # run twice to check that we don't mess up the stream for the next command
2487 2489 res1 = repo.debugwireargs(*vals, **args)
2488 2490 res2 = repo.debugwireargs(*vals, **args)
2489 2491 ui.write("%s\n" % res1)
2490 2492 if res1 != res2:
2491 2493 ui.warn("%s\n" % res2)
2492 2494
2493 2495 @command('^diff',
2494 2496 [('r', 'rev', [], _('revision'), _('REV')),
2495 2497 ('c', 'change', '', _('change made by revision'), _('REV'))
2496 2498 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2497 2499 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2498 2500 def diff(ui, repo, *pats, **opts):
2499 2501 """diff repository (or selected files)
2500 2502
2501 2503 Show differences between revisions for the specified files.
2502 2504
2503 2505 Differences between files are shown using the unified diff format.
2504 2506
2505 2507 .. note::
2506 2508 diff may generate unexpected results for merges, as it will
2507 2509 default to comparing against the working directory's first
2508 2510 parent changeset if no revisions are specified.
2509 2511
2510 2512 When two revision arguments are given, then changes are shown
2511 2513 between those revisions. If only one revision is specified then
2512 2514 that revision is compared to the working directory, and, when no
2513 2515 revisions are specified, the working directory files are compared
2514 2516 to its parent.
2515 2517
2516 2518 Alternatively you can specify -c/--change with a revision to see
2517 2519 the changes in that changeset relative to its first parent.
2518 2520
2519 2521 Without the -a/--text option, diff will avoid generating diffs of
2520 2522 files it detects as binary. With -a, diff will generate a diff
2521 2523 anyway, probably with undesirable results.
2522 2524
2523 2525 Use the -g/--git option to generate diffs in the git extended diff
2524 2526 format. For more information, read :hg:`help diffs`.
2525 2527
2526 2528 .. container:: verbose
2527 2529
2528 2530 Examples:
2529 2531
2530 2532 - compare a file in the current working directory to its parent::
2531 2533
2532 2534 hg diff foo.c
2533 2535
2534 2536 - compare two historical versions of a directory, with rename info::
2535 2537
2536 2538 hg diff --git -r 1.0:1.2 lib/
2537 2539
2538 2540 - get change stats relative to the last change on some date::
2539 2541
2540 2542 hg diff --stat -r "date('may 2')"
2541 2543
2542 2544 - diff all newly-added files that contain a keyword::
2543 2545
2544 2546 hg diff "set:added() and grep(GNU)"
2545 2547
2546 2548 - compare a revision and its parents::
2547 2549
2548 2550 hg diff -c 9353 # compare against first parent
2549 2551 hg diff -r 9353^:9353 # same using revset syntax
2550 2552 hg diff -r 9353^2:9353 # compare against the second parent
2551 2553
2552 2554 Returns 0 on success.
2553 2555 """
2554 2556
2555 2557 revs = opts.get('rev')
2556 2558 change = opts.get('change')
2557 2559 stat = opts.get('stat')
2558 2560 reverse = opts.get('reverse')
2559 2561
2560 2562 if revs and change:
2561 2563 msg = _('cannot specify --rev and --change at the same time')
2562 2564 raise util.Abort(msg)
2563 2565 elif change:
2564 2566 node2 = scmutil.revsingle(repo, change, None).node()
2565 2567 node1 = repo[node2].p1().node()
2566 2568 else:
2567 2569 node1, node2 = scmutil.revpair(repo, revs)
2568 2570
2569 2571 if reverse:
2570 2572 node1, node2 = node2, node1
2571 2573
2572 2574 diffopts = patch.diffopts(ui, opts)
2573 2575 m = scmutil.match(repo[node2], pats, opts)
2574 2576 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2575 2577 listsubrepos=opts.get('subrepos'))
2576 2578
2577 2579 @command('^export',
2578 2580 [('o', 'output', '',
2579 2581 _('print output to file with formatted name'), _('FORMAT')),
2580 2582 ('', 'switch-parent', None, _('diff against the second parent')),
2581 2583 ('r', 'rev', [], _('revisions to export'), _('REV')),
2582 2584 ] + diffopts,
2583 2585 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2584 2586 def export(ui, repo, *changesets, **opts):
2585 2587 """dump the header and diffs for one or more changesets
2586 2588
2587 2589 Print the changeset header and diffs for one or more revisions.
2588 2590
2589 2591 The information shown in the changeset header is: author, date,
2590 2592 branch name (if non-default), changeset hash, parent(s) and commit
2591 2593 comment.
2592 2594
2593 2595 .. note::
2594 2596 export may generate unexpected diff output for merge
2595 2597 changesets, as it will compare the merge changeset against its
2596 2598 first parent only.
2597 2599
2598 2600 Output may be to a file, in which case the name of the file is
2599 2601 given using a format string. The formatting rules are as follows:
2600 2602
2601 2603 :``%%``: literal "%" character
2602 2604 :``%H``: changeset hash (40 hexadecimal digits)
2603 2605 :``%N``: number of patches being generated
2604 2606 :``%R``: changeset revision number
2605 2607 :``%b``: basename of the exporting repository
2606 2608 :``%h``: short-form changeset hash (12 hexadecimal digits)
2607 2609 :``%m``: first line of the commit message (only alphanumeric characters)
2608 2610 :``%n``: zero-padded sequence number, starting at 1
2609 2611 :``%r``: zero-padded changeset revision number
2610 2612
2611 2613 Without the -a/--text option, export will avoid generating diffs
2612 2614 of files it detects as binary. With -a, export will generate a
2613 2615 diff anyway, probably with undesirable results.
2614 2616
2615 2617 Use the -g/--git option to generate diffs in the git extended diff
2616 2618 format. See :hg:`help diffs` for more information.
2617 2619
2618 2620 With the --switch-parent option, the diff will be against the
2619 2621 second parent. It can be useful to review a merge.
2620 2622
2621 2623 .. container:: verbose
2622 2624
2623 2625 Examples:
2624 2626
2625 2627 - use export and import to transplant a bugfix to the current
2626 2628 branch::
2627 2629
2628 2630 hg export -r 9353 | hg import -
2629 2631
2630 2632 - export all the changesets between two revisions to a file with
2631 2633 rename information::
2632 2634
2633 2635 hg export --git -r 123:150 > changes.txt
2634 2636
2635 2637 - split outgoing changes into a series of patches with
2636 2638 descriptive names::
2637 2639
2638 2640 hg export -r "outgoing()" -o "%n-%m.patch"
2639 2641
2640 2642 Returns 0 on success.
2641 2643 """
2642 2644 changesets += tuple(opts.get('rev', []))
2643 2645 revs = scmutil.revrange(repo, changesets)
2644 2646 if not revs:
2645 2647 raise util.Abort(_("export requires at least one changeset"))
2646 2648 if len(revs) > 1:
2647 2649 ui.note(_('exporting patches:\n'))
2648 2650 else:
2649 2651 ui.note(_('exporting patch:\n'))
2650 2652 cmdutil.export(repo, revs, template=opts.get('output'),
2651 2653 switch_parent=opts.get('switch_parent'),
2652 2654 opts=patch.diffopts(ui, opts))
2653 2655
2654 2656 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2655 2657 def forget(ui, repo, *pats, **opts):
2656 2658 """forget the specified files on the next commit
2657 2659
2658 2660 Mark the specified files so they will no longer be tracked
2659 2661 after the next commit.
2660 2662
2661 2663 This only removes files from the current branch, not from the
2662 2664 entire project history, and it does not delete them from the
2663 2665 working directory.
2664 2666
2665 2667 To undo a forget before the next commit, see :hg:`add`.
2666 2668
2667 2669 .. container:: verbose
2668 2670
2669 2671 Examples:
2670 2672
2671 2673 - forget newly-added binary files::
2672 2674
2673 2675 hg forget "set:added() and binary()"
2674 2676
2675 2677 - forget files that would be excluded by .hgignore::
2676 2678
2677 2679 hg forget "set:hgignore()"
2678 2680
2679 2681 Returns 0 on success.
2680 2682 """
2681 2683
2682 2684 if not pats:
2683 2685 raise util.Abort(_('no files specified'))
2684 2686
2685 2687 m = scmutil.match(repo[None], pats, opts)
2686 2688 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2687 2689 return rejected and 1 or 0
2688 2690
2689 2691 @command(
2690 2692 'graft',
2691 2693 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2692 2694 ('c', 'continue', False, _('resume interrupted graft')),
2693 2695 ('e', 'edit', False, _('invoke editor on commit messages')),
2694 2696 ('', 'log', None, _('append graft info to log message')),
2695 2697 ('D', 'currentdate', False,
2696 2698 _('record the current date as commit date')),
2697 2699 ('U', 'currentuser', False,
2698 2700 _('record the current user as committer'), _('DATE'))]
2699 2701 + commitopts2 + mergetoolopts + dryrunopts,
2700 2702 _('[OPTION]... [-r] REV...'))
2701 2703 def graft(ui, repo, *revs, **opts):
2702 2704 '''copy changes from other branches onto the current branch
2703 2705
2704 2706 This command uses Mercurial's merge logic to copy individual
2705 2707 changes from other branches without merging branches in the
2706 2708 history graph. This is sometimes known as 'backporting' or
2707 2709 'cherry-picking'. By default, graft will copy user, date, and
2708 2710 description from the source changesets.
2709 2711
2710 2712 Changesets that are ancestors of the current revision, that have
2711 2713 already been grafted, or that are merges will be skipped.
2712 2714
2713 2715 If --log is specified, log messages will have a comment appended
2714 2716 of the form::
2715 2717
2716 2718 (grafted from CHANGESETHASH)
2717 2719
2718 2720 If a graft merge results in conflicts, the graft process is
2719 2721 interrupted so that the current merge can be manually resolved.
2720 2722 Once all conflicts are addressed, the graft process can be
2721 2723 continued with the -c/--continue option.
2722 2724
2723 2725 .. note::
2724 2726 The -c/--continue option does not reapply earlier options.
2725 2727
2726 2728 .. container:: verbose
2727 2729
2728 2730 Examples:
2729 2731
2730 2732 - copy a single change to the stable branch and edit its description::
2731 2733
2732 2734 hg update stable
2733 2735 hg graft --edit 9393
2734 2736
2735 2737 - graft a range of changesets with one exception, updating dates::
2736 2738
2737 2739 hg graft -D "2085::2093 and not 2091"
2738 2740
2739 2741 - continue a graft after resolving conflicts::
2740 2742
2741 2743 hg graft -c
2742 2744
2743 2745 - show the source of a grafted changeset::
2744 2746
2745 2747 hg log --debug -r tip
2746 2748
2747 2749 Returns 0 on successful completion.
2748 2750 '''
2749 2751
2750 2752 revs = list(revs)
2751 2753 revs.extend(opts['rev'])
2752 2754
2753 2755 if not opts.get('user') and opts.get('currentuser'):
2754 2756 opts['user'] = ui.username()
2755 2757 if not opts.get('date') and opts.get('currentdate'):
2756 2758 opts['date'] = "%d %d" % util.makedate()
2757 2759
2758 2760 editor = None
2759 2761 if opts.get('edit'):
2760 2762 editor = cmdutil.commitforceeditor
2761 2763
2762 2764 cont = False
2763 2765 if opts['continue']:
2764 2766 cont = True
2765 2767 if revs:
2766 2768 raise util.Abort(_("can't specify --continue and revisions"))
2767 2769 # read in unfinished revisions
2768 2770 try:
2769 2771 nodes = repo.opener.read('graftstate').splitlines()
2770 2772 revs = [repo[node].rev() for node in nodes]
2771 2773 except IOError, inst:
2772 2774 if inst.errno != errno.ENOENT:
2773 2775 raise
2774 2776 raise util.Abort(_("no graft state found, can't continue"))
2775 2777 else:
2776 2778 cmdutil.bailifchanged(repo)
2777 2779 if not revs:
2778 2780 raise util.Abort(_('no revisions specified'))
2779 2781 revs = scmutil.revrange(repo, revs)
2780 2782
2781 2783 # check for merges
2782 2784 for rev in repo.revs('%ld and merge()', revs):
2783 2785 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2784 2786 revs.remove(rev)
2785 2787 if not revs:
2786 2788 return -1
2787 2789
2788 2790 # check for ancestors of dest branch
2789 2791 for rev in repo.revs('::. and %ld', revs):
2790 2792 ui.warn(_('skipping ancestor revision %s\n') % rev)
2791 2793 revs.remove(rev)
2792 2794 if not revs:
2793 2795 return -1
2794 2796
2795 2797 # analyze revs for earlier grafts
2796 2798 ids = {}
2797 2799 for ctx in repo.set("%ld", revs):
2798 2800 ids[ctx.hex()] = ctx.rev()
2799 2801 n = ctx.extra().get('source')
2800 2802 if n:
2801 2803 ids[n] = ctx.rev()
2802 2804
2803 2805 # check ancestors for earlier grafts
2804 2806 ui.debug('scanning for duplicate grafts\n')
2805 2807 for ctx in repo.set("::. - ::%ld", revs):
2806 2808 n = ctx.extra().get('source')
2807 2809 if n in ids:
2808 2810 r = repo[n].rev()
2809 2811 if r in revs:
2810 2812 ui.warn(_('skipping already grafted revision %s\n') % r)
2811 2813 revs.remove(r)
2812 2814 elif ids[n] in revs:
2813 2815 ui.warn(_('skipping already grafted revision %s '
2814 2816 '(same origin %d)\n') % (ids[n], r))
2815 2817 revs.remove(ids[n])
2816 2818 elif ctx.hex() in ids:
2817 2819 r = ids[ctx.hex()]
2818 2820 ui.warn(_('skipping already grafted revision %s '
2819 2821 '(was grafted from %d)\n') % (r, ctx.rev()))
2820 2822 revs.remove(r)
2821 2823 if not revs:
2822 2824 return -1
2823 2825
2824 2826 wlock = repo.wlock()
2825 2827 try:
2826 2828 for pos, ctx in enumerate(repo.set("%ld", revs)):
2827 2829 current = repo['.']
2828 2830
2829 2831 ui.status(_('grafting revision %s\n') % ctx.rev())
2830 2832 if opts.get('dry_run'):
2831 2833 continue
2832 2834
2833 2835 # we don't merge the first commit when continuing
2834 2836 if not cont:
2835 2837 # perform the graft merge with p1(rev) as 'ancestor'
2836 2838 try:
2837 2839 # ui.forcemerge is an internal variable, do not document
2838 2840 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2839 2841 stats = mergemod.update(repo, ctx.node(), True, True, False,
2840 2842 ctx.p1().node())
2841 2843 finally:
2842 2844 repo.ui.setconfig('ui', 'forcemerge', '')
2843 2845 # report any conflicts
2844 2846 if stats and stats[3] > 0:
2845 2847 # write out state for --continue
2846 2848 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2847 2849 repo.opener.write('graftstate', ''.join(nodelines))
2848 2850 raise util.Abort(
2849 2851 _("unresolved conflicts, can't continue"),
2850 2852 hint=_('use hg resolve and hg graft --continue'))
2851 2853 else:
2852 2854 cont = False
2853 2855
2854 2856 # drop the second merge parent
2855 2857 repo.setparents(current.node(), nullid)
2856 2858 repo.dirstate.write()
2857 2859 # fix up dirstate for copies and renames
2858 2860 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2859 2861
2860 2862 # commit
2861 2863 source = ctx.extra().get('source')
2862 2864 if not source:
2863 2865 source = ctx.hex()
2864 2866 extra = {'source': source}
2865 2867 user = ctx.user()
2866 2868 if opts.get('user'):
2867 2869 user = opts['user']
2868 2870 date = ctx.date()
2869 2871 if opts.get('date'):
2870 2872 date = opts['date']
2871 2873 message = ctx.description()
2872 2874 if opts.get('log'):
2873 2875 message += '\n(grafted from %s)' % ctx.hex()
2874 2876 node = repo.commit(text=message, user=user,
2875 2877 date=date, extra=extra, editor=editor)
2876 2878 if node is None:
2877 2879 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2878 2880 finally:
2879 2881 wlock.release()
2880 2882
2881 2883 # remove state when we complete successfully
2882 2884 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2883 2885 util.unlinkpath(repo.join('graftstate'))
2884 2886
2885 2887 return 0
2886 2888
2887 2889 @command('grep',
2888 2890 [('0', 'print0', None, _('end fields with NUL')),
2889 2891 ('', 'all', None, _('print all revisions that match')),
2890 2892 ('a', 'text', None, _('treat all files as text')),
2891 2893 ('f', 'follow', None,
2892 2894 _('follow changeset history,'
2893 2895 ' or file history across copies and renames')),
2894 2896 ('i', 'ignore-case', None, _('ignore case when matching')),
2895 2897 ('l', 'files-with-matches', None,
2896 2898 _('print only filenames and revisions that match')),
2897 2899 ('n', 'line-number', None, _('print matching line numbers')),
2898 2900 ('r', 'rev', [],
2899 2901 _('only search files changed within revision range'), _('REV')),
2900 2902 ('u', 'user', None, _('list the author (long with -v)')),
2901 2903 ('d', 'date', None, _('list the date (short with -q)')),
2902 2904 ] + walkopts,
2903 2905 _('[OPTION]... PATTERN [FILE]...'))
2904 2906 def grep(ui, repo, pattern, *pats, **opts):
2905 2907 """search for a pattern in specified files and revisions
2906 2908
2907 2909 Search revisions of files for a regular expression.
2908 2910
2909 2911 This command behaves differently than Unix grep. It only accepts
2910 2912 Python/Perl regexps. It searches repository history, not the
2911 2913 working directory. It always prints the revision number in which a
2912 2914 match appears.
2913 2915
2914 2916 By default, grep only prints output for the first revision of a
2915 2917 file in which it finds a match. To get it to print every revision
2916 2918 that contains a change in match status ("-" for a match that
2917 2919 becomes a non-match, or "+" for a non-match that becomes a match),
2918 2920 use the --all flag.
2919 2921
2920 2922 Returns 0 if a match is found, 1 otherwise.
2921 2923 """
2922 2924 reflags = re.M
2923 2925 if opts.get('ignore_case'):
2924 2926 reflags |= re.I
2925 2927 try:
2926 2928 regexp = re.compile(pattern, reflags)
2927 2929 except re.error, inst:
2928 2930 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2929 2931 return 1
2930 2932 sep, eol = ':', '\n'
2931 2933 if opts.get('print0'):
2932 2934 sep = eol = '\0'
2933 2935
2934 2936 getfile = util.lrucachefunc(repo.file)
2935 2937
2936 2938 def matchlines(body):
2937 2939 begin = 0
2938 2940 linenum = 0
2939 2941 while begin < len(body):
2940 2942 match = regexp.search(body, begin)
2941 2943 if not match:
2942 2944 break
2943 2945 mstart, mend = match.span()
2944 2946 linenum += body.count('\n', begin, mstart) + 1
2945 2947 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2946 2948 begin = body.find('\n', mend) + 1 or len(body) + 1
2947 2949 lend = begin - 1
2948 2950 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2949 2951
2950 2952 class linestate(object):
2951 2953 def __init__(self, line, linenum, colstart, colend):
2952 2954 self.line = line
2953 2955 self.linenum = linenum
2954 2956 self.colstart = colstart
2955 2957 self.colend = colend
2956 2958
2957 2959 def __hash__(self):
2958 2960 return hash((self.linenum, self.line))
2959 2961
2960 2962 def __eq__(self, other):
2961 2963 return self.line == other.line
2962 2964
2963 2965 matches = {}
2964 2966 copies = {}
2965 2967 def grepbody(fn, rev, body):
2966 2968 matches[rev].setdefault(fn, [])
2967 2969 m = matches[rev][fn]
2968 2970 for lnum, cstart, cend, line in matchlines(body):
2969 2971 s = linestate(line, lnum, cstart, cend)
2970 2972 m.append(s)
2971 2973
2972 2974 def difflinestates(a, b):
2973 2975 sm = difflib.SequenceMatcher(None, a, b)
2974 2976 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2975 2977 if tag == 'insert':
2976 2978 for i in xrange(blo, bhi):
2977 2979 yield ('+', b[i])
2978 2980 elif tag == 'delete':
2979 2981 for i in xrange(alo, ahi):
2980 2982 yield ('-', a[i])
2981 2983 elif tag == 'replace':
2982 2984 for i in xrange(alo, ahi):
2983 2985 yield ('-', a[i])
2984 2986 for i in xrange(blo, bhi):
2985 2987 yield ('+', b[i])
2986 2988
2987 2989 def display(fn, ctx, pstates, states):
2988 2990 rev = ctx.rev()
2989 2991 datefunc = ui.quiet and util.shortdate or util.datestr
2990 2992 found = False
2991 2993 filerevmatches = {}
2992 2994 def binary():
2993 2995 flog = getfile(fn)
2994 2996 return util.binary(flog.read(ctx.filenode(fn)))
2995 2997
2996 2998 if opts.get('all'):
2997 2999 iter = difflinestates(pstates, states)
2998 3000 else:
2999 3001 iter = [('', l) for l in states]
3000 3002 for change, l in iter:
3001 3003 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3002 3004 before, match, after = None, None, None
3003 3005
3004 3006 if opts.get('line_number'):
3005 3007 cols.append((str(l.linenum), 'grep.linenumber'))
3006 3008 if opts.get('all'):
3007 3009 cols.append((change, 'grep.change'))
3008 3010 if opts.get('user'):
3009 3011 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3010 3012 if opts.get('date'):
3011 3013 cols.append((datefunc(ctx.date()), 'grep.date'))
3012 3014 if opts.get('files_with_matches'):
3013 3015 c = (fn, rev)
3014 3016 if c in filerevmatches:
3015 3017 continue
3016 3018 filerevmatches[c] = 1
3017 3019 else:
3018 3020 before = l.line[:l.colstart]
3019 3021 match = l.line[l.colstart:l.colend]
3020 3022 after = l.line[l.colend:]
3021 3023 for col, label in cols[:-1]:
3022 3024 ui.write(col, label=label)
3023 3025 ui.write(sep, label='grep.sep')
3024 3026 ui.write(cols[-1][0], label=cols[-1][1])
3025 3027 if before is not None:
3026 3028 ui.write(sep, label='grep.sep')
3027 3029 if not opts.get('text') and binary():
3028 3030 ui.write(" Binary file matches")
3029 3031 else:
3030 3032 ui.write(before)
3031 3033 ui.write(match, label='grep.match')
3032 3034 ui.write(after)
3033 3035 ui.write(eol)
3034 3036 found = True
3035 3037 return found
3036 3038
3037 3039 skip = {}
3038 3040 revfiles = {}
3039 3041 matchfn = scmutil.match(repo[None], pats, opts)
3040 3042 found = False
3041 3043 follow = opts.get('follow')
3042 3044
3043 3045 def prep(ctx, fns):
3044 3046 rev = ctx.rev()
3045 3047 pctx = ctx.p1()
3046 3048 parent = pctx.rev()
3047 3049 matches.setdefault(rev, {})
3048 3050 matches.setdefault(parent, {})
3049 3051 files = revfiles.setdefault(rev, [])
3050 3052 for fn in fns:
3051 3053 flog = getfile(fn)
3052 3054 try:
3053 3055 fnode = ctx.filenode(fn)
3054 3056 except error.LookupError:
3055 3057 continue
3056 3058
3057 3059 copied = flog.renamed(fnode)
3058 3060 copy = follow and copied and copied[0]
3059 3061 if copy:
3060 3062 copies.setdefault(rev, {})[fn] = copy
3061 3063 if fn in skip:
3062 3064 if copy:
3063 3065 skip[copy] = True
3064 3066 continue
3065 3067 files.append(fn)
3066 3068
3067 3069 if fn not in matches[rev]:
3068 3070 grepbody(fn, rev, flog.read(fnode))
3069 3071
3070 3072 pfn = copy or fn
3071 3073 if pfn not in matches[parent]:
3072 3074 try:
3073 3075 fnode = pctx.filenode(pfn)
3074 3076 grepbody(pfn, parent, flog.read(fnode))
3075 3077 except error.LookupError:
3076 3078 pass
3077 3079
3078 3080 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3079 3081 rev = ctx.rev()
3080 3082 parent = ctx.p1().rev()
3081 3083 for fn in sorted(revfiles.get(rev, [])):
3082 3084 states = matches[rev][fn]
3083 3085 copy = copies.get(rev, {}).get(fn)
3084 3086 if fn in skip:
3085 3087 if copy:
3086 3088 skip[copy] = True
3087 3089 continue
3088 3090 pstates = matches.get(parent, {}).get(copy or fn, [])
3089 3091 if pstates or states:
3090 3092 r = display(fn, ctx, pstates, states)
3091 3093 found = found or r
3092 3094 if r and not opts.get('all'):
3093 3095 skip[fn] = True
3094 3096 if copy:
3095 3097 skip[copy] = True
3096 3098 del matches[rev]
3097 3099 del revfiles[rev]
3098 3100
3099 3101 return not found
3100 3102
3101 3103 @command('heads',
3102 3104 [('r', 'rev', '',
3103 3105 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3104 3106 ('t', 'topo', False, _('show topological heads only')),
3105 3107 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3106 3108 ('c', 'closed', False, _('show normal and closed branch heads')),
3107 3109 ] + templateopts,
3108 3110 _('[-ct] [-r STARTREV] [REV]...'))
3109 3111 def heads(ui, repo, *branchrevs, **opts):
3110 3112 """show current repository heads or show branch heads
3111 3113
3112 3114 With no arguments, show all repository branch heads.
3113 3115
3114 3116 Repository "heads" are changesets with no child changesets. They are
3115 3117 where development generally takes place and are the usual targets
3116 3118 for update and merge operations. Branch heads are changesets that have
3117 3119 no child changeset on the same branch.
3118 3120
3119 3121 If one or more REVs are given, only branch heads on the branches
3120 3122 associated with the specified changesets are shown. This means
3121 3123 that you can use :hg:`heads foo` to see the heads on a branch
3122 3124 named ``foo``.
3123 3125
3124 3126 If -c/--closed is specified, also show branch heads marked closed
3125 3127 (see :hg:`commit --close-branch`).
3126 3128
3127 3129 If STARTREV is specified, only those heads that are descendants of
3128 3130 STARTREV will be displayed.
3129 3131
3130 3132 If -t/--topo is specified, named branch mechanics will be ignored and only
3131 3133 changesets without children will be shown.
3132 3134
3133 3135 Returns 0 if matching heads are found, 1 if not.
3134 3136 """
3135 3137
3136 3138 start = None
3137 3139 if 'rev' in opts:
3138 3140 start = scmutil.revsingle(repo, opts['rev'], None).node()
3139 3141
3140 3142 if opts.get('topo'):
3141 3143 heads = [repo[h] for h in repo.heads(start)]
3142 3144 else:
3143 3145 heads = []
3144 3146 for branch in repo.branchmap():
3145 3147 heads += repo.branchheads(branch, start, opts.get('closed'))
3146 3148 heads = [repo[h] for h in heads]
3147 3149
3148 3150 if branchrevs:
3149 3151 branches = set(repo[br].branch() for br in branchrevs)
3150 3152 heads = [h for h in heads if h.branch() in branches]
3151 3153
3152 3154 if opts.get('active') and branchrevs:
3153 3155 dagheads = repo.heads(start)
3154 3156 heads = [h for h in heads if h.node() in dagheads]
3155 3157
3156 3158 if branchrevs:
3157 3159 haveheads = set(h.branch() for h in heads)
3158 3160 if branches - haveheads:
3159 3161 headless = ', '.join(b for b in branches - haveheads)
3160 3162 msg = _('no open branch heads found on branches %s')
3161 3163 if opts.get('rev'):
3162 3164 msg += _(' (started at %s)') % opts['rev']
3163 3165 ui.warn((msg + '\n') % headless)
3164 3166
3165 3167 if not heads:
3166 3168 return 1
3167 3169
3168 3170 heads = sorted(heads, key=lambda x: -x.rev())
3169 3171 displayer = cmdutil.show_changeset(ui, repo, opts)
3170 3172 for ctx in heads:
3171 3173 displayer.show(ctx)
3172 3174 displayer.close()
3173 3175
3174 3176 @command('help',
3175 3177 [('e', 'extension', None, _('show only help for extensions')),
3176 3178 ('c', 'command', None, _('show only help for commands')),
3177 3179 ('k', 'keyword', '', _('show topics matching keyword')),
3178 3180 ],
3179 3181 _('[-ec] [TOPIC]'))
3180 3182 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3181 3183 """show help for a given topic or a help overview
3182 3184
3183 3185 With no arguments, print a list of commands with short help messages.
3184 3186
3185 3187 Given a topic, extension, or command name, print help for that
3186 3188 topic.
3187 3189
3188 3190 Returns 0 if successful.
3189 3191 """
3190 3192
3191 3193 textwidth = min(ui.termwidth(), 80) - 2
3192 3194
3193 3195 def helpcmd(name):
3194 3196 try:
3195 3197 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3196 3198 except error.AmbiguousCommand, inst:
3197 3199 # py3k fix: except vars can't be used outside the scope of the
3198 3200 # except block, nor can be used inside a lambda. python issue4617
3199 3201 prefix = inst.args[0]
3200 3202 select = lambda c: c.lstrip('^').startswith(prefix)
3201 3203 rst = helplist(select)
3202 3204 return rst
3203 3205
3204 3206 rst = []
3205 3207
3206 3208 # check if it's an invalid alias and display its error if it is
3207 3209 if getattr(entry[0], 'badalias', False):
3208 3210 if not unknowncmd:
3209 3211 ui.pushbuffer()
3210 3212 entry[0](ui)
3211 3213 rst.append(ui.popbuffer())
3212 3214 return rst
3213 3215
3214 3216 # synopsis
3215 3217 if len(entry) > 2:
3216 3218 if entry[2].startswith('hg'):
3217 3219 rst.append("%s\n" % entry[2])
3218 3220 else:
3219 3221 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
3220 3222 else:
3221 3223 rst.append('hg %s\n' % aliases[0])
3222 3224 # aliases
3223 3225 if full and not ui.quiet and len(aliases) > 1:
3224 3226 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
3225 3227 rst.append('\n')
3226 3228
3227 3229 # description
3228 3230 doc = gettext(entry[0].__doc__)
3229 3231 if not doc:
3230 3232 doc = _("(no help text available)")
3231 3233 if util.safehasattr(entry[0], 'definition'): # aliased command
3232 3234 if entry[0].definition.startswith('!'): # shell alias
3233 3235 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3234 3236 else:
3235 3237 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3236 3238 doc = doc.splitlines(True)
3237 3239 if ui.quiet or not full:
3238 3240 rst.append(doc[0])
3239 3241 else:
3240 3242 rst.extend(doc)
3241 3243 rst.append('\n')
3242 3244
3243 3245 # check if this command shadows a non-trivial (multi-line)
3244 3246 # extension help text
3245 3247 try:
3246 3248 mod = extensions.find(name)
3247 3249 doc = gettext(mod.__doc__) or ''
3248 3250 if '\n' in doc.strip():
3249 3251 msg = _('use "hg help -e %s" to show help for '
3250 3252 'the %s extension') % (name, name)
3251 3253 rst.append('\n%s\n' % msg)
3252 3254 except KeyError:
3253 3255 pass
3254 3256
3255 3257 # options
3256 3258 if not ui.quiet and entry[1]:
3257 3259 rst.append('\n%s\n\n' % _("options:"))
3258 3260 rst.append(help.optrst(entry[1], ui.verbose))
3259 3261
3260 3262 if ui.verbose:
3261 3263 rst.append('\n%s\n\n' % _("global options:"))
3262 3264 rst.append(help.optrst(globalopts, ui.verbose))
3263 3265
3264 3266 if not ui.verbose:
3265 3267 if not full:
3266 3268 rst.append(_('\nuse "hg help %s" to show the full help text\n')
3267 3269 % name)
3268 3270 elif not ui.quiet:
3269 3271 omitted = _('use "hg -v help %s" to show more complete'
3270 3272 ' help and the global options') % name
3271 3273 notomitted = _('use "hg -v help %s" to show'
3272 3274 ' the global options') % name
3273 3275 help.indicateomitted(rst, omitted, notomitted)
3274 3276
3275 3277 return rst
3276 3278
3277 3279
3278 3280 def helplist(select=None):
3279 3281 # list of commands
3280 3282 if name == "shortlist":
3281 3283 header = _('basic commands:\n\n')
3282 3284 else:
3283 3285 header = _('list of commands:\n\n')
3284 3286
3285 3287 h = {}
3286 3288 cmds = {}
3287 3289 for c, e in table.iteritems():
3288 3290 f = c.split("|", 1)[0]
3289 3291 if select and not select(f):
3290 3292 continue
3291 3293 if (not select and name != 'shortlist' and
3292 3294 e[0].__module__ != __name__):
3293 3295 continue
3294 3296 if name == "shortlist" and not f.startswith("^"):
3295 3297 continue
3296 3298 f = f.lstrip("^")
3297 3299 if not ui.debugflag and f.startswith("debug"):
3298 3300 continue
3299 3301 doc = e[0].__doc__
3300 3302 if doc and 'DEPRECATED' in doc and not ui.verbose:
3301 3303 continue
3302 3304 doc = gettext(doc)
3303 3305 if not doc:
3304 3306 doc = _("(no help text available)")
3305 3307 h[f] = doc.splitlines()[0].rstrip()
3306 3308 cmds[f] = c.lstrip("^")
3307 3309
3308 3310 rst = []
3309 3311 if not h:
3310 3312 if not ui.quiet:
3311 3313 rst.append(_('no commands defined\n'))
3312 3314 return rst
3313 3315
3314 3316 if not ui.quiet:
3315 3317 rst.append(header)
3316 3318 fns = sorted(h)
3317 3319 for f in fns:
3318 3320 if ui.verbose:
3319 3321 commands = cmds[f].replace("|",", ")
3320 3322 rst.append(" :%s: %s\n" % (commands, h[f]))
3321 3323 else:
3322 3324 rst.append(' :%s: %s\n' % (f, h[f]))
3323 3325
3324 3326 if not name:
3325 3327 exts = help.listexts(_('enabled extensions:'), extensions.enabled())
3326 3328 if exts:
3327 3329 rst.append('\n')
3328 3330 rst.extend(exts)
3329 3331
3330 3332 rst.append(_("\nadditional help topics:\n\n"))
3331 3333 topics = []
3332 3334 for names, header, doc in help.helptable:
3333 3335 topics.append((names[0], header))
3334 3336 for t, desc in topics:
3335 3337 rst.append(" :%s: %s\n" % (t, desc))
3336 3338
3337 3339 optlist = []
3338 3340 if not ui.quiet:
3339 3341 if ui.verbose:
3340 3342 optlist.append((_("global options:"), globalopts))
3341 3343 if name == 'shortlist':
3342 3344 optlist.append((_('use "hg help" for the full list '
3343 3345 'of commands'), ()))
3344 3346 else:
3345 3347 if name == 'shortlist':
3346 3348 msg = _('use "hg help" for the full list of commands '
3347 3349 'or "hg -v" for details')
3348 3350 elif name and not full:
3349 3351 msg = _('use "hg help %s" to show the full help '
3350 3352 'text') % name
3351 3353 else:
3352 3354 msg = _('use "hg -v help%s" to show builtin aliases and '
3353 3355 'global options') % (name and " " + name or "")
3354 3356 optlist.append((msg, ()))
3355 3357
3356 3358 if optlist:
3357 3359 for title, options in optlist:
3358 3360 rst.append('\n%s\n' % title)
3359 3361 if options:
3360 3362 rst.append('\n%s\n' % help.optrst(options, ui.verbose))
3361 3363 return rst
3362 3364
3363 3365 def helptopic(name):
3364 3366 for names, header, doc in help.helptable:
3365 3367 if name in names:
3366 3368 break
3367 3369 else:
3368 3370 raise error.UnknownCommand(name)
3369 3371
3370 3372 rst = ["%s\n\n" % header]
3371 3373 # description
3372 3374 if not doc:
3373 3375 rst.append(" %s\n" % _("(no help text available)"))
3374 3376 if util.safehasattr(doc, '__call__'):
3375 3377 rst += [" %s\n" % l for l in doc().splitlines()]
3376 3378
3377 3379 if not ui.verbose:
3378 3380 omitted = (_('use "hg help -v %s" to show more complete help') %
3379 3381 name)
3380 3382 help.indicateomitted(rst, omitted)
3381 3383
3382 3384 try:
3383 3385 cmdutil.findcmd(name, table)
3384 3386 rst.append(_('\nuse "hg help -c %s" to see help for '
3385 3387 'the %s command\n') % (name, name))
3386 3388 except error.UnknownCommand:
3387 3389 pass
3388 3390 return rst
3389 3391
3390 3392 def helpext(name):
3391 3393 try:
3392 3394 mod = extensions.find(name)
3393 3395 doc = gettext(mod.__doc__) or _('no help text available')
3394 3396 except KeyError:
3395 3397 mod = None
3396 3398 doc = extensions.disabledext(name)
3397 3399 if not doc:
3398 3400 raise error.UnknownCommand(name)
3399 3401
3400 3402 if '\n' not in doc:
3401 3403 head, tail = doc, ""
3402 3404 else:
3403 3405 head, tail = doc.split('\n', 1)
3404 3406 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
3405 3407 if tail:
3406 3408 rst.extend(tail.splitlines(True))
3407 3409 rst.append('\n')
3408 3410
3409 3411 if not ui.verbose:
3410 3412 omitted = (_('use "hg help -v %s" to show more complete help') %
3411 3413 name)
3412 3414 help.indicateomitted(rst, omitted)
3413 3415
3414 3416 if mod:
3415 3417 try:
3416 3418 ct = mod.cmdtable
3417 3419 except AttributeError:
3418 3420 ct = {}
3419 3421 modcmds = set([c.split('|', 1)[0] for c in ct])
3420 3422 rst.extend(helplist(modcmds.__contains__))
3421 3423 else:
3422 3424 rst.append(_('use "hg help extensions" for information on enabling '
3423 3425 'extensions\n'))
3424 3426 return rst
3425 3427
3426 3428 def helpextcmd(name):
3427 3429 cmd, ext, mod = extensions.disabledcmd(ui, name,
3428 3430 ui.configbool('ui', 'strict'))
3429 3431 doc = gettext(mod.__doc__).splitlines()[0]
3430 3432
3431 3433 rst = help.listexts(_("'%s' is provided by the following "
3432 3434 "extension:") % cmd, {ext: doc}, indent=4)
3433 3435 rst.append('\n')
3434 3436 rst.append(_('use "hg help extensions" for information on enabling '
3435 3437 'extensions\n'))
3436 3438 return rst
3437 3439
3438 3440
3439 3441 rst = []
3440 3442 kw = opts.get('keyword')
3441 3443 if kw:
3442 3444 matches = help.topicmatch(kw)
3443 3445 for t, title in (('topics', _('Topics')),
3444 3446 ('commands', _('Commands')),
3445 3447 ('extensions', _('Extensions')),
3446 3448 ('extensioncommands', _('Extension Commands'))):
3447 3449 if matches[t]:
3448 3450 rst.append('%s:\n\n' % title)
3449 3451 rst.extend(minirst.maketable(sorted(matches[t]), 1))
3450 3452 rst.append('\n')
3451 3453 elif name and name != 'shortlist':
3452 3454 i = None
3453 3455 if unknowncmd:
3454 3456 queries = (helpextcmd,)
3455 3457 elif opts.get('extension'):
3456 3458 queries = (helpext,)
3457 3459 elif opts.get('command'):
3458 3460 queries = (helpcmd,)
3459 3461 else:
3460 3462 queries = (helptopic, helpcmd, helpext, helpextcmd)
3461 3463 for f in queries:
3462 3464 try:
3463 3465 rst = f(name)
3464 3466 i = None
3465 3467 break
3466 3468 except error.UnknownCommand, inst:
3467 3469 i = inst
3468 3470 if i:
3469 3471 raise i
3470 3472 else:
3471 3473 # program name
3472 3474 if not ui.quiet:
3473 3475 rst = [_("Mercurial Distributed SCM\n"), '\n']
3474 3476 rst.extend(helplist())
3475 3477
3476 3478 keep = ui.verbose and ['verbose'] or []
3477 3479 text = ''.join(rst)
3478 3480 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3479 3481 if 'verbose' in pruned:
3480 3482 keep.append('omitted')
3481 3483 else:
3482 3484 keep.append('notomitted')
3483 3485 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3484 3486 ui.write(formatted)
3485 3487
3486 3488
3487 3489 @command('identify|id',
3488 3490 [('r', 'rev', '',
3489 3491 _('identify the specified revision'), _('REV')),
3490 3492 ('n', 'num', None, _('show local revision number')),
3491 3493 ('i', 'id', None, _('show global revision id')),
3492 3494 ('b', 'branch', None, _('show branch')),
3493 3495 ('t', 'tags', None, _('show tags')),
3494 3496 ('B', 'bookmarks', None, _('show bookmarks')),
3495 3497 ] + remoteopts,
3496 3498 _('[-nibtB] [-r REV] [SOURCE]'))
3497 3499 def identify(ui, repo, source=None, rev=None,
3498 3500 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3499 3501 """identify the working copy or specified revision
3500 3502
3501 3503 Print a summary identifying the repository state at REV using one or
3502 3504 two parent hash identifiers, followed by a "+" if the working
3503 3505 directory has uncommitted changes, the branch name (if not default),
3504 3506 a list of tags, and a list of bookmarks.
3505 3507
3506 3508 When REV is not given, print a summary of the current state of the
3507 3509 repository.
3508 3510
3509 3511 Specifying a path to a repository root or Mercurial bundle will
3510 3512 cause lookup to operate on that repository/bundle.
3511 3513
3512 3514 .. container:: verbose
3513 3515
3514 3516 Examples:
3515 3517
3516 3518 - generate a build identifier for the working directory::
3517 3519
3518 3520 hg id --id > build-id.dat
3519 3521
3520 3522 - find the revision corresponding to a tag::
3521 3523
3522 3524 hg id -n -r 1.3
3523 3525
3524 3526 - check the most recent revision of a remote repository::
3525 3527
3526 3528 hg id -r tip http://selenic.com/hg/
3527 3529
3528 3530 Returns 0 if successful.
3529 3531 """
3530 3532
3531 3533 if not repo and not source:
3532 3534 raise util.Abort(_("there is no Mercurial repository here "
3533 3535 "(.hg not found)"))
3534 3536
3535 3537 hexfunc = ui.debugflag and hex or short
3536 3538 default = not (num or id or branch or tags or bookmarks)
3537 3539 output = []
3538 3540 revs = []
3539 3541
3540 3542 if source:
3541 3543 source, branches = hg.parseurl(ui.expandpath(source))
3542 3544 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3543 3545 repo = peer.local()
3544 3546 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3545 3547
3546 3548 if not repo:
3547 3549 if num or branch or tags:
3548 3550 raise util.Abort(
3549 3551 _("can't query remote revision number, branch, or tags"))
3550 3552 if not rev and revs:
3551 3553 rev = revs[0]
3552 3554 if not rev:
3553 3555 rev = "tip"
3554 3556
3555 3557 remoterev = peer.lookup(rev)
3556 3558 if default or id:
3557 3559 output = [hexfunc(remoterev)]
3558 3560
3559 3561 def getbms():
3560 3562 bms = []
3561 3563
3562 3564 if 'bookmarks' in peer.listkeys('namespaces'):
3563 3565 hexremoterev = hex(remoterev)
3564 3566 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3565 3567 if bmr == hexremoterev]
3566 3568
3567 3569 return bms
3568 3570
3569 3571 if bookmarks:
3570 3572 output.extend(getbms())
3571 3573 elif default and not ui.quiet:
3572 3574 # multiple bookmarks for a single parent separated by '/'
3573 3575 bm = '/'.join(getbms())
3574 3576 if bm:
3575 3577 output.append(bm)
3576 3578 else:
3577 3579 if not rev:
3578 3580 ctx = repo[None]
3579 3581 parents = ctx.parents()
3580 3582 changed = ""
3581 3583 if default or id or num:
3582 3584 if (util.any(repo.status())
3583 3585 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3584 3586 changed = '+'
3585 3587 if default or id:
3586 3588 output = ["%s%s" %
3587 3589 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3588 3590 if num:
3589 3591 output.append("%s%s" %
3590 3592 ('+'.join([str(p.rev()) for p in parents]), changed))
3591 3593 else:
3592 3594 ctx = scmutil.revsingle(repo, rev)
3593 3595 if default or id:
3594 3596 output = [hexfunc(ctx.node())]
3595 3597 if num:
3596 3598 output.append(str(ctx.rev()))
3597 3599
3598 3600 if default and not ui.quiet:
3599 3601 b = ctx.branch()
3600 3602 if b != 'default':
3601 3603 output.append("(%s)" % b)
3602 3604
3603 3605 # multiple tags for a single parent separated by '/'
3604 3606 t = '/'.join(ctx.tags())
3605 3607 if t:
3606 3608 output.append(t)
3607 3609
3608 3610 # multiple bookmarks for a single parent separated by '/'
3609 3611 bm = '/'.join(ctx.bookmarks())
3610 3612 if bm:
3611 3613 output.append(bm)
3612 3614 else:
3613 3615 if branch:
3614 3616 output.append(ctx.branch())
3615 3617
3616 3618 if tags:
3617 3619 output.extend(ctx.tags())
3618 3620
3619 3621 if bookmarks:
3620 3622 output.extend(ctx.bookmarks())
3621 3623
3622 3624 ui.write("%s\n" % ' '.join(output))
3623 3625
3624 3626 @command('import|patch',
3625 3627 [('p', 'strip', 1,
3626 3628 _('directory strip option for patch. This has the same '
3627 3629 'meaning as the corresponding patch option'), _('NUM')),
3628 3630 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3629 3631 ('e', 'edit', False, _('invoke editor on commit messages')),
3630 3632 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3631 3633 ('', 'no-commit', None,
3632 3634 _("don't commit, just update the working directory")),
3633 3635 ('', 'bypass', None,
3634 3636 _("apply patch without touching the working directory")),
3635 3637 ('', 'exact', None,
3636 3638 _('apply patch to the nodes from which it was generated')),
3637 3639 ('', 'import-branch', None,
3638 3640 _('use any branch information in patch (implied by --exact)'))] +
3639 3641 commitopts + commitopts2 + similarityopts,
3640 3642 _('[OPTION]... PATCH...'))
3641 3643 def import_(ui, repo, patch1=None, *patches, **opts):
3642 3644 """import an ordered set of patches
3643 3645
3644 3646 Import a list of patches and commit them individually (unless
3645 3647 --no-commit is specified).
3646 3648
3647 3649 If there are outstanding changes in the working directory, import
3648 3650 will abort unless given the -f/--force flag.
3649 3651
3650 3652 You can import a patch straight from a mail message. Even patches
3651 3653 as attachments work (to use the body part, it must have type
3652 3654 text/plain or text/x-patch). From and Subject headers of email
3653 3655 message are used as default committer and commit message. All
3654 3656 text/plain body parts before first diff are added to commit
3655 3657 message.
3656 3658
3657 3659 If the imported patch was generated by :hg:`export`, user and
3658 3660 description from patch override values from message headers and
3659 3661 body. Values given on command line with -m/--message and -u/--user
3660 3662 override these.
3661 3663
3662 3664 If --exact is specified, import will set the working directory to
3663 3665 the parent of each patch before applying it, and will abort if the
3664 3666 resulting changeset has a different ID than the one recorded in
3665 3667 the patch. This may happen due to character set problems or other
3666 3668 deficiencies in the text patch format.
3667 3669
3668 3670 Use --bypass to apply and commit patches directly to the
3669 3671 repository, not touching the working directory. Without --exact,
3670 3672 patches will be applied on top of the working directory parent
3671 3673 revision.
3672 3674
3673 3675 With -s/--similarity, hg will attempt to discover renames and
3674 3676 copies in the patch in the same way as :hg:`addremove`.
3675 3677
3676 3678 To read a patch from standard input, use "-" as the patch name. If
3677 3679 a URL is specified, the patch will be downloaded from it.
3678 3680 See :hg:`help dates` for a list of formats valid for -d/--date.
3679 3681
3680 3682 .. container:: verbose
3681 3683
3682 3684 Examples:
3683 3685
3684 3686 - import a traditional patch from a website and detect renames::
3685 3687
3686 3688 hg import -s 80 http://example.com/bugfix.patch
3687 3689
3688 3690 - import a changeset from an hgweb server::
3689 3691
3690 3692 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3691 3693
3692 3694 - import all the patches in an Unix-style mbox::
3693 3695
3694 3696 hg import incoming-patches.mbox
3695 3697
3696 3698 - attempt to exactly restore an exported changeset (not always
3697 3699 possible)::
3698 3700
3699 3701 hg import --exact proposed-fix.patch
3700 3702
3701 3703 Returns 0 on success.
3702 3704 """
3703 3705
3704 3706 if not patch1:
3705 3707 raise util.Abort(_('need at least one patch to import'))
3706 3708
3707 3709 patches = (patch1,) + patches
3708 3710
3709 3711 date = opts.get('date')
3710 3712 if date:
3711 3713 opts['date'] = util.parsedate(date)
3712 3714
3713 3715 editor = cmdutil.commiteditor
3714 3716 if opts.get('edit'):
3715 3717 editor = cmdutil.commitforceeditor
3716 3718
3717 3719 update = not opts.get('bypass')
3718 3720 if not update and opts.get('no_commit'):
3719 3721 raise util.Abort(_('cannot use --no-commit with --bypass'))
3720 3722 try:
3721 3723 sim = float(opts.get('similarity') or 0)
3722 3724 except ValueError:
3723 3725 raise util.Abort(_('similarity must be a number'))
3724 3726 if sim < 0 or sim > 100:
3725 3727 raise util.Abort(_('similarity must be between 0 and 100'))
3726 3728 if sim and not update:
3727 3729 raise util.Abort(_('cannot use --similarity with --bypass'))
3728 3730
3729 3731 if (opts.get('exact') or not opts.get('force')) and update:
3730 3732 cmdutil.bailifchanged(repo)
3731 3733
3732 3734 base = opts["base"]
3733 3735 strip = opts["strip"]
3734 3736 wlock = lock = tr = None
3735 3737 msgs = []
3736 3738
3737 3739 def checkexact(repo, n, nodeid):
3738 3740 if opts.get('exact') and hex(n) != nodeid:
3739 3741 repo.rollback()
3740 3742 raise util.Abort(_('patch is damaged or loses information'))
3741 3743
3742 3744 def tryone(ui, hunk, parents):
3743 3745 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3744 3746 patch.extract(ui, hunk)
3745 3747
3746 3748 if not tmpname:
3747 3749 return (None, None)
3748 3750 msg = _('applied to working directory')
3749 3751
3750 3752 try:
3751 3753 cmdline_message = cmdutil.logmessage(ui, opts)
3752 3754 if cmdline_message:
3753 3755 # pickup the cmdline msg
3754 3756 message = cmdline_message
3755 3757 elif message:
3756 3758 # pickup the patch msg
3757 3759 message = message.strip()
3758 3760 else:
3759 3761 # launch the editor
3760 3762 message = None
3761 3763 ui.debug('message:\n%s\n' % message)
3762 3764
3763 3765 if len(parents) == 1:
3764 3766 parents.append(repo[nullid])
3765 3767 if opts.get('exact'):
3766 3768 if not nodeid or not p1:
3767 3769 raise util.Abort(_('not a Mercurial patch'))
3768 3770 p1 = repo[p1]
3769 3771 p2 = repo[p2 or nullid]
3770 3772 elif p2:
3771 3773 try:
3772 3774 p1 = repo[p1]
3773 3775 p2 = repo[p2]
3774 3776 # Without any options, consider p2 only if the
3775 3777 # patch is being applied on top of the recorded
3776 3778 # first parent.
3777 3779 if p1 != parents[0]:
3778 3780 p1 = parents[0]
3779 3781 p2 = repo[nullid]
3780 3782 except error.RepoError:
3781 3783 p1, p2 = parents
3782 3784 else:
3783 3785 p1, p2 = parents
3784 3786
3785 3787 n = None
3786 3788 if update:
3787 3789 if p1 != parents[0]:
3788 3790 hg.clean(repo, p1.node())
3789 3791 if p2 != parents[1]:
3790 3792 repo.setparents(p1.node(), p2.node())
3791 3793
3792 3794 if opts.get('exact') or opts.get('import_branch'):
3793 3795 repo.dirstate.setbranch(branch or 'default')
3794 3796
3795 3797 files = set()
3796 3798 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3797 3799 eolmode=None, similarity=sim / 100.0)
3798 3800 files = list(files)
3799 3801 if opts.get('no_commit'):
3800 3802 if message:
3801 3803 msgs.append(message)
3802 3804 else:
3803 3805 if opts.get('exact') or p2:
3804 3806 # If you got here, you either use --force and know what
3805 3807 # you are doing or used --exact or a merge patch while
3806 3808 # being updated to its first parent.
3807 3809 m = None
3808 3810 else:
3809 3811 m = scmutil.matchfiles(repo, files or [])
3810 3812 n = repo.commit(message, opts.get('user') or user,
3811 3813 opts.get('date') or date, match=m,
3812 3814 editor=editor)
3813 3815 checkexact(repo, n, nodeid)
3814 3816 else:
3815 3817 if opts.get('exact') or opts.get('import_branch'):
3816 3818 branch = branch or 'default'
3817 3819 else:
3818 3820 branch = p1.branch()
3819 3821 store = patch.filestore()
3820 3822 try:
3821 3823 files = set()
3822 3824 try:
3823 3825 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3824 3826 files, eolmode=None)
3825 3827 except patch.PatchError, e:
3826 3828 raise util.Abort(str(e))
3827 3829 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3828 3830 message,
3829 3831 opts.get('user') or user,
3830 3832 opts.get('date') or date,
3831 3833 branch, files, store,
3832 3834 editor=cmdutil.commiteditor)
3833 3835 repo.savecommitmessage(memctx.description())
3834 3836 n = memctx.commit()
3835 3837 checkexact(repo, n, nodeid)
3836 3838 finally:
3837 3839 store.close()
3838 3840 if n:
3839 3841 # i18n: refers to a short changeset id
3840 3842 msg = _('created %s') % short(n)
3841 3843 return (msg, n)
3842 3844 finally:
3843 3845 os.unlink(tmpname)
3844 3846
3845 3847 try:
3846 3848 try:
3847 3849 wlock = repo.wlock()
3848 3850 if not opts.get('no_commit'):
3849 3851 lock = repo.lock()
3850 3852 tr = repo.transaction('import')
3851 3853 parents = repo.parents()
3852 3854 for patchurl in patches:
3853 3855 if patchurl == '-':
3854 3856 ui.status(_('applying patch from stdin\n'))
3855 3857 patchfile = ui.fin
3856 3858 patchurl = 'stdin' # for error message
3857 3859 else:
3858 3860 patchurl = os.path.join(base, patchurl)
3859 3861 ui.status(_('applying %s\n') % patchurl)
3860 3862 patchfile = hg.openpath(ui, patchurl)
3861 3863
3862 3864 haspatch = False
3863 3865 for hunk in patch.split(patchfile):
3864 3866 (msg, node) = tryone(ui, hunk, parents)
3865 3867 if msg:
3866 3868 haspatch = True
3867 3869 ui.note(msg + '\n')
3868 3870 if update or opts.get('exact'):
3869 3871 parents = repo.parents()
3870 3872 else:
3871 3873 parents = [repo[node]]
3872 3874
3873 3875 if not haspatch:
3874 3876 raise util.Abort(_('%s: no diffs found') % patchurl)
3875 3877
3876 3878 if tr:
3877 3879 tr.close()
3878 3880 if msgs:
3879 3881 repo.savecommitmessage('\n* * *\n'.join(msgs))
3880 3882 except: # re-raises
3881 3883 # wlock.release() indirectly calls dirstate.write(): since
3882 3884 # we're crashing, we do not want to change the working dir
3883 3885 # parent after all, so make sure it writes nothing
3884 3886 repo.dirstate.invalidate()
3885 3887 raise
3886 3888 finally:
3887 3889 if tr:
3888 3890 tr.release()
3889 3891 release(lock, wlock)
3890 3892
3891 3893 @command('incoming|in',
3892 3894 [('f', 'force', None,
3893 3895 _('run even if remote repository is unrelated')),
3894 3896 ('n', 'newest-first', None, _('show newest record first')),
3895 3897 ('', 'bundle', '',
3896 3898 _('file to store the bundles into'), _('FILE')),
3897 3899 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3898 3900 ('B', 'bookmarks', False, _("compare bookmarks")),
3899 3901 ('b', 'branch', [],
3900 3902 _('a specific branch you would like to pull'), _('BRANCH')),
3901 3903 ] + logopts + remoteopts + subrepoopts,
3902 3904 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3903 3905 def incoming(ui, repo, source="default", **opts):
3904 3906 """show new changesets found in source
3905 3907
3906 3908 Show new changesets found in the specified path/URL or the default
3907 3909 pull location. These are the changesets that would have been pulled
3908 3910 if a pull at the time you issued this command.
3909 3911
3910 3912 For remote repository, using --bundle avoids downloading the
3911 3913 changesets twice if the incoming is followed by a pull.
3912 3914
3913 3915 See pull for valid source format details.
3914 3916
3915 3917 Returns 0 if there are incoming changes, 1 otherwise.
3916 3918 """
3917 3919 if opts.get('graph'):
3918 3920 cmdutil.checkunsupportedgraphflags([], opts)
3919 3921 def display(other, chlist, displayer):
3920 3922 revdag = cmdutil.graphrevs(other, chlist, opts)
3921 3923 showparents = [ctx.node() for ctx in repo[None].parents()]
3922 3924 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3923 3925 graphmod.asciiedges)
3924 3926
3925 3927 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3926 3928 return 0
3927 3929
3928 3930 if opts.get('bundle') and opts.get('subrepos'):
3929 3931 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3930 3932
3931 3933 if opts.get('bookmarks'):
3932 3934 source, branches = hg.parseurl(ui.expandpath(source),
3933 3935 opts.get('branch'))
3934 3936 other = hg.peer(repo, opts, source)
3935 3937 if 'bookmarks' not in other.listkeys('namespaces'):
3936 3938 ui.warn(_("remote doesn't support bookmarks\n"))
3937 3939 return 0
3938 3940 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3939 3941 return bookmarks.diff(ui, repo, other)
3940 3942
3941 3943 repo._subtoppath = ui.expandpath(source)
3942 3944 try:
3943 3945 return hg.incoming(ui, repo, source, opts)
3944 3946 finally:
3945 3947 del repo._subtoppath
3946 3948
3947 3949
3948 3950 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3949 3951 def init(ui, dest=".", **opts):
3950 3952 """create a new repository in the given directory
3951 3953
3952 3954 Initialize a new repository in the given directory. If the given
3953 3955 directory does not exist, it will be created.
3954 3956
3955 3957 If no directory is given, the current directory is used.
3956 3958
3957 3959 It is possible to specify an ``ssh://`` URL as the destination.
3958 3960 See :hg:`help urls` for more information.
3959 3961
3960 3962 Returns 0 on success.
3961 3963 """
3962 3964 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3963 3965
3964 3966 @command('locate',
3965 3967 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3966 3968 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3967 3969 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3968 3970 ] + walkopts,
3969 3971 _('[OPTION]... [PATTERN]...'))
3970 3972 def locate(ui, repo, *pats, **opts):
3971 3973 """locate files matching specific patterns
3972 3974
3973 3975 Print files under Mercurial control in the working directory whose
3974 3976 names match the given patterns.
3975 3977
3976 3978 By default, this command searches all directories in the working
3977 3979 directory. To search just the current directory and its
3978 3980 subdirectories, use "--include .".
3979 3981
3980 3982 If no patterns are given to match, this command prints the names
3981 3983 of all files under Mercurial control in the working directory.
3982 3984
3983 3985 If you want to feed the output of this command into the "xargs"
3984 3986 command, use the -0 option to both this command and "xargs". This
3985 3987 will avoid the problem of "xargs" treating single filenames that
3986 3988 contain whitespace as multiple filenames.
3987 3989
3988 3990 Returns 0 if a match is found, 1 otherwise.
3989 3991 """
3990 3992 end = opts.get('print0') and '\0' or '\n'
3991 3993 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3992 3994
3993 3995 ret = 1
3994 3996 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3995 3997 m.bad = lambda x, y: False
3996 3998 for abs in repo[rev].walk(m):
3997 3999 if not rev and abs not in repo.dirstate:
3998 4000 continue
3999 4001 if opts.get('fullpath'):
4000 4002 ui.write(repo.wjoin(abs), end)
4001 4003 else:
4002 4004 ui.write(((pats and m.rel(abs)) or abs), end)
4003 4005 ret = 0
4004 4006
4005 4007 return ret
4006 4008
4007 4009 @command('^log|history',
4008 4010 [('f', 'follow', None,
4009 4011 _('follow changeset history, or file history across copies and renames')),
4010 4012 ('', 'follow-first', None,
4011 4013 _('only follow the first parent of merge changesets (DEPRECATED)')),
4012 4014 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4013 4015 ('C', 'copies', None, _('show copied files')),
4014 4016 ('k', 'keyword', [],
4015 4017 _('do case-insensitive search for a given text'), _('TEXT')),
4016 4018 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
4017 4019 ('', 'removed', None, _('include revisions where files were removed')),
4018 4020 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4019 4021 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4020 4022 ('', 'only-branch', [],
4021 4023 _('show only changesets within the given named branch (DEPRECATED)'),
4022 4024 _('BRANCH')),
4023 4025 ('b', 'branch', [],
4024 4026 _('show changesets within the given named branch'), _('BRANCH')),
4025 4027 ('P', 'prune', [],
4026 4028 _('do not display revision or any of its ancestors'), _('REV')),
4027 4029 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
4028 4030 ] + logopts + walkopts,
4029 4031 _('[OPTION]... [FILE]'))
4030 4032 def log(ui, repo, *pats, **opts):
4031 4033 """show revision history of entire repository or files
4032 4034
4033 4035 Print the revision history of the specified files or the entire
4034 4036 project.
4035 4037
4036 4038 If no revision range is specified, the default is ``tip:0`` unless
4037 4039 --follow is set, in which case the working directory parent is
4038 4040 used as the starting revision.
4039 4041
4040 4042 File history is shown without following rename or copy history of
4041 4043 files. Use -f/--follow with a filename to follow history across
4042 4044 renames and copies. --follow without a filename will only show
4043 4045 ancestors or descendants of the starting revision.
4044 4046
4045 4047 By default this command prints revision number and changeset id,
4046 4048 tags, non-trivial parents, user, date and time, and a summary for
4047 4049 each commit. When the -v/--verbose switch is used, the list of
4048 4050 changed files and full commit message are shown.
4049 4051
4050 4052 .. note::
4051 4053 log -p/--patch may generate unexpected diff output for merge
4052 4054 changesets, as it will only compare the merge changeset against
4053 4055 its first parent. Also, only files different from BOTH parents
4054 4056 will appear in files:.
4055 4057
4056 4058 .. note::
4057 4059 for performance reasons, log FILE may omit duplicate changes
4058 4060 made on branches and will not show deletions. To see all
4059 4061 changes including duplicates and deletions, use the --removed
4060 4062 switch.
4061 4063
4062 4064 .. container:: verbose
4063 4065
4064 4066 Some examples:
4065 4067
4066 4068 - changesets with full descriptions and file lists::
4067 4069
4068 4070 hg log -v
4069 4071
4070 4072 - changesets ancestral to the working directory::
4071 4073
4072 4074 hg log -f
4073 4075
4074 4076 - last 10 commits on the current branch::
4075 4077
4076 4078 hg log -l 10 -b .
4077 4079
4078 4080 - changesets showing all modifications of a file, including removals::
4079 4081
4080 4082 hg log --removed file.c
4081 4083
4082 4084 - all changesets that touch a directory, with diffs, excluding merges::
4083 4085
4084 4086 hg log -Mp lib/
4085 4087
4086 4088 - all revision numbers that match a keyword::
4087 4089
4088 4090 hg log -k bug --template "{rev}\\n"
4089 4091
4090 4092 - check if a given changeset is included is a tagged release::
4091 4093
4092 4094 hg log -r "a21ccf and ancestor(1.9)"
4093 4095
4094 4096 - find all changesets by some user in a date range::
4095 4097
4096 4098 hg log -k alice -d "may 2008 to jul 2008"
4097 4099
4098 4100 - summary of all changesets after the last tag::
4099 4101
4100 4102 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4101 4103
4102 4104 See :hg:`help dates` for a list of formats valid for -d/--date.
4103 4105
4104 4106 See :hg:`help revisions` and :hg:`help revsets` for more about
4105 4107 specifying revisions.
4106 4108
4107 4109 See :hg:`help templates` for more about pre-packaged styles and
4108 4110 specifying custom templates.
4109 4111
4110 4112 Returns 0 on success.
4111 4113 """
4112 4114 if opts.get('graph'):
4113 4115 return cmdutil.graphlog(ui, repo, *pats, **opts)
4114 4116
4115 4117 matchfn = scmutil.match(repo[None], pats, opts)
4116 4118 limit = cmdutil.loglimit(opts)
4117 4119 count = 0
4118 4120
4119 4121 getrenamed, endrev = None, None
4120 4122 if opts.get('copies'):
4121 4123 if opts.get('rev'):
4122 4124 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
4123 4125 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4124 4126
4125 4127 df = False
4126 4128 if opts.get("date"):
4127 4129 df = util.matchdate(opts["date"])
4128 4130
4129 4131 branches = opts.get('branch', []) + opts.get('only_branch', [])
4130 4132 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4131 4133
4132 4134 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4133 4135 def prep(ctx, fns):
4134 4136 rev = ctx.rev()
4135 4137 parents = [p for p in repo.changelog.parentrevs(rev)
4136 4138 if p != nullrev]
4137 4139 if opts.get('no_merges') and len(parents) == 2:
4138 4140 return
4139 4141 if opts.get('only_merges') and len(parents) != 2:
4140 4142 return
4141 4143 if opts.get('branch') and ctx.branch() not in opts['branch']:
4142 4144 return
4143 4145 if not opts.get('hidden') and ctx.hidden():
4144 4146 return
4145 4147 if df and not df(ctx.date()[0]):
4146 4148 return
4147 4149
4148 4150 lower = encoding.lower
4149 4151 if opts.get('user'):
4150 4152 luser = lower(ctx.user())
4151 4153 for k in [lower(x) for x in opts['user']]:
4152 4154 if (k in luser):
4153 4155 break
4154 4156 else:
4155 4157 return
4156 4158 if opts.get('keyword'):
4157 4159 luser = lower(ctx.user())
4158 4160 ldesc = lower(ctx.description())
4159 4161 lfiles = lower(" ".join(ctx.files()))
4160 4162 for k in [lower(x) for x in opts['keyword']]:
4161 4163 if (k in luser or k in ldesc or k in lfiles):
4162 4164 break
4163 4165 else:
4164 4166 return
4165 4167
4166 4168 copies = None
4167 4169 if getrenamed is not None and rev:
4168 4170 copies = []
4169 4171 for fn in ctx.files():
4170 4172 rename = getrenamed(fn, rev)
4171 4173 if rename:
4172 4174 copies.append((fn, rename[0]))
4173 4175
4174 4176 revmatchfn = None
4175 4177 if opts.get('patch') or opts.get('stat'):
4176 4178 if opts.get('follow') or opts.get('follow_first'):
4177 4179 # note: this might be wrong when following through merges
4178 4180 revmatchfn = scmutil.match(repo[None], fns, default='path')
4179 4181 else:
4180 4182 revmatchfn = matchfn
4181 4183
4182 4184 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4183 4185
4184 4186 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4185 4187 if count == limit:
4186 4188 break
4187 4189 if displayer.flush(ctx.rev()):
4188 4190 count += 1
4189 4191 displayer.close()
4190 4192
4191 4193 @command('manifest',
4192 4194 [('r', 'rev', '', _('revision to display'), _('REV')),
4193 4195 ('', 'all', False, _("list files from all revisions"))],
4194 4196 _('[-r REV]'))
4195 4197 def manifest(ui, repo, node=None, rev=None, **opts):
4196 4198 """output the current or given revision of the project manifest
4197 4199
4198 4200 Print a list of version controlled files for the given revision.
4199 4201 If no revision is given, the first parent of the working directory
4200 4202 is used, or the null revision if no revision is checked out.
4201 4203
4202 4204 With -v, print file permissions, symlink and executable bits.
4203 4205 With --debug, print file revision hashes.
4204 4206
4205 4207 If option --all is specified, the list of all files from all revisions
4206 4208 is printed. This includes deleted and renamed files.
4207 4209
4208 4210 Returns 0 on success.
4209 4211 """
4210 4212
4211 4213 fm = ui.formatter('manifest', opts)
4212 4214
4213 4215 if opts.get('all'):
4214 4216 if rev or node:
4215 4217 raise util.Abort(_("can't specify a revision with --all"))
4216 4218
4217 4219 res = []
4218 4220 prefix = "data/"
4219 4221 suffix = ".i"
4220 4222 plen = len(prefix)
4221 4223 slen = len(suffix)
4222 4224 lock = repo.lock()
4223 4225 try:
4224 4226 for fn, b, size in repo.store.datafiles():
4225 4227 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4226 4228 res.append(fn[plen:-slen])
4227 4229 finally:
4228 4230 lock.release()
4229 4231 for f in res:
4230 4232 fm.startitem()
4231 4233 fm.write("path", '%s\n', f)
4232 4234 fm.end()
4233 4235 return
4234 4236
4235 4237 if rev and node:
4236 4238 raise util.Abort(_("please specify just one revision"))
4237 4239
4238 4240 if not node:
4239 4241 node = rev
4240 4242
4241 4243 char = {'l': '@', 'x': '*', '': ''}
4242 4244 mode = {'l': '644', 'x': '755', '': '644'}
4243 4245 ctx = scmutil.revsingle(repo, node)
4244 4246 mf = ctx.manifest()
4245 4247 for f in ctx:
4246 4248 fm.startitem()
4247 4249 fl = ctx[f].flags()
4248 4250 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4249 4251 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4250 4252 fm.write('path', '%s\n', f)
4251 4253 fm.end()
4252 4254
4253 4255 @command('^merge',
4254 4256 [('f', 'force', None, _('force a merge with outstanding changes')),
4255 4257 ('r', 'rev', '', _('revision to merge'), _('REV')),
4256 4258 ('P', 'preview', None,
4257 4259 _('review revisions to merge (no merge is performed)'))
4258 4260 ] + mergetoolopts,
4259 4261 _('[-P] [-f] [[-r] REV]'))
4260 4262 def merge(ui, repo, node=None, **opts):
4261 4263 """merge working directory with another revision
4262 4264
4263 4265 The current working directory is updated with all changes made in
4264 4266 the requested revision since the last common predecessor revision.
4265 4267
4266 4268 Files that changed between either parent are marked as changed for
4267 4269 the next commit and a commit must be performed before any further
4268 4270 updates to the repository are allowed. The next commit will have
4269 4271 two parents.
4270 4272
4271 4273 ``--tool`` can be used to specify the merge tool used for file
4272 4274 merges. It overrides the HGMERGE environment variable and your
4273 4275 configuration files. See :hg:`help merge-tools` for options.
4274 4276
4275 4277 If no revision is specified, the working directory's parent is a
4276 4278 head revision, and the current branch contains exactly one other
4277 4279 head, the other head is merged with by default. Otherwise, an
4278 4280 explicit revision with which to merge with must be provided.
4279 4281
4280 4282 :hg:`resolve` must be used to resolve unresolved files.
4281 4283
4282 4284 To undo an uncommitted merge, use :hg:`update --clean .` which
4283 4285 will check out a clean copy of the original merge parent, losing
4284 4286 all changes.
4285 4287
4286 4288 Returns 0 on success, 1 if there are unresolved files.
4287 4289 """
4288 4290
4289 4291 if opts.get('rev') and node:
4290 4292 raise util.Abort(_("please specify just one revision"))
4291 4293 if not node:
4292 4294 node = opts.get('rev')
4293 4295
4294 4296 if node:
4295 4297 node = scmutil.revsingle(repo, node).node()
4296 4298
4297 4299 if not node and repo._bookmarkcurrent:
4298 4300 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4299 4301 curhead = repo[repo._bookmarkcurrent]
4300 4302 if len(bmheads) == 2:
4301 4303 if curhead == bmheads[0]:
4302 4304 node = bmheads[1]
4303 4305 else:
4304 4306 node = bmheads[0]
4305 4307 elif len(bmheads) > 2:
4306 4308 raise util.Abort(_("multiple matching bookmarks to merge - "
4307 4309 "please merge with an explicit rev or bookmark"),
4308 4310 hint=_("run 'hg heads' to see all heads"))
4309 4311 elif len(bmheads) <= 1:
4310 4312 raise util.Abort(_("no matching bookmark to merge - "
4311 4313 "please merge with an explicit rev or bookmark"),
4312 4314 hint=_("run 'hg heads' to see all heads"))
4313 4315
4314 4316 if not node and not repo._bookmarkcurrent:
4315 4317 branch = repo[None].branch()
4316 4318 bheads = repo.branchheads(branch)
4317 4319 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4318 4320
4319 4321 if len(nbhs) > 2:
4320 4322 raise util.Abort(_("branch '%s' has %d heads - "
4321 4323 "please merge with an explicit rev")
4322 4324 % (branch, len(bheads)),
4323 4325 hint=_("run 'hg heads .' to see heads"))
4324 4326
4325 4327 parent = repo.dirstate.p1()
4326 4328 if len(nbhs) <= 1:
4327 4329 if len(bheads) > 1:
4328 4330 raise util.Abort(_("heads are bookmarked - "
4329 4331 "please merge with an explicit rev"),
4330 4332 hint=_("run 'hg heads' to see all heads"))
4331 4333 if len(repo.heads()) > 1:
4332 4334 raise util.Abort(_("branch '%s' has one head - "
4333 4335 "please merge with an explicit rev")
4334 4336 % branch,
4335 4337 hint=_("run 'hg heads' to see all heads"))
4336 4338 msg, hint = _('nothing to merge'), None
4337 4339 if parent != repo.lookup(branch):
4338 4340 hint = _("use 'hg update' instead")
4339 4341 raise util.Abort(msg, hint=hint)
4340 4342
4341 4343 if parent not in bheads:
4342 4344 raise util.Abort(_('working directory not at a head revision'),
4343 4345 hint=_("use 'hg update' or merge with an "
4344 4346 "explicit revision"))
4345 4347 if parent == nbhs[0]:
4346 4348 node = nbhs[-1]
4347 4349 else:
4348 4350 node = nbhs[0]
4349 4351
4350 4352 if opts.get('preview'):
4351 4353 # find nodes that are ancestors of p2 but not of p1
4352 4354 p1 = repo.lookup('.')
4353 4355 p2 = repo.lookup(node)
4354 4356 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4355 4357
4356 4358 displayer = cmdutil.show_changeset(ui, repo, opts)
4357 4359 for node in nodes:
4358 4360 displayer.show(repo[node])
4359 4361 displayer.close()
4360 4362 return 0
4361 4363
4362 4364 try:
4363 4365 # ui.forcemerge is an internal variable, do not document
4364 4366 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4365 4367 return hg.merge(repo, node, force=opts.get('force'))
4366 4368 finally:
4367 4369 ui.setconfig('ui', 'forcemerge', '')
4368 4370
4369 4371 @command('outgoing|out',
4370 4372 [('f', 'force', None, _('run even when the destination is unrelated')),
4371 4373 ('r', 'rev', [],
4372 4374 _('a changeset intended to be included in the destination'), _('REV')),
4373 4375 ('n', 'newest-first', None, _('show newest record first')),
4374 4376 ('B', 'bookmarks', False, _('compare bookmarks')),
4375 4377 ('b', 'branch', [], _('a specific branch you would like to push'),
4376 4378 _('BRANCH')),
4377 4379 ] + logopts + remoteopts + subrepoopts,
4378 4380 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4379 4381 def outgoing(ui, repo, dest=None, **opts):
4380 4382 """show changesets not found in the destination
4381 4383
4382 4384 Show changesets not found in the specified destination repository
4383 4385 or the default push location. These are the changesets that would
4384 4386 be pushed if a push was requested.
4385 4387
4386 4388 See pull for details of valid destination formats.
4387 4389
4388 4390 Returns 0 if there are outgoing changes, 1 otherwise.
4389 4391 """
4390 4392 if opts.get('graph'):
4391 4393 cmdutil.checkunsupportedgraphflags([], opts)
4392 4394 o = hg._outgoing(ui, repo, dest, opts)
4393 4395 if o is None:
4394 4396 return
4395 4397
4396 4398 revdag = cmdutil.graphrevs(repo, o, opts)
4397 4399 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4398 4400 showparents = [ctx.node() for ctx in repo[None].parents()]
4399 4401 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4400 4402 graphmod.asciiedges)
4401 4403 return 0
4402 4404
4403 4405 if opts.get('bookmarks'):
4404 4406 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4405 4407 dest, branches = hg.parseurl(dest, opts.get('branch'))
4406 4408 other = hg.peer(repo, opts, dest)
4407 4409 if 'bookmarks' not in other.listkeys('namespaces'):
4408 4410 ui.warn(_("remote doesn't support bookmarks\n"))
4409 4411 return 0
4410 4412 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4411 4413 return bookmarks.diff(ui, other, repo)
4412 4414
4413 4415 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4414 4416 try:
4415 4417 return hg.outgoing(ui, repo, dest, opts)
4416 4418 finally:
4417 4419 del repo._subtoppath
4418 4420
4419 4421 @command('parents',
4420 4422 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4421 4423 ] + templateopts,
4422 4424 _('[-r REV] [FILE]'))
4423 4425 def parents(ui, repo, file_=None, **opts):
4424 4426 """show the parents of the working directory or revision
4425 4427
4426 4428 Print the working directory's parent revisions. If a revision is
4427 4429 given via -r/--rev, the parent of that revision will be printed.
4428 4430 If a file argument is given, the revision in which the file was
4429 4431 last changed (before the working directory revision or the
4430 4432 argument to --rev if given) is printed.
4431 4433
4432 4434 Returns 0 on success.
4433 4435 """
4434 4436
4435 4437 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4436 4438
4437 4439 if file_:
4438 4440 m = scmutil.match(ctx, (file_,), opts)
4439 4441 if m.anypats() or len(m.files()) != 1:
4440 4442 raise util.Abort(_('can only specify an explicit filename'))
4441 4443 file_ = m.files()[0]
4442 4444 filenodes = []
4443 4445 for cp in ctx.parents():
4444 4446 if not cp:
4445 4447 continue
4446 4448 try:
4447 4449 filenodes.append(cp.filenode(file_))
4448 4450 except error.LookupError:
4449 4451 pass
4450 4452 if not filenodes:
4451 4453 raise util.Abort(_("'%s' not found in manifest!") % file_)
4452 4454 fl = repo.file(file_)
4453 4455 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4454 4456 else:
4455 4457 p = [cp.node() for cp in ctx.parents()]
4456 4458
4457 4459 displayer = cmdutil.show_changeset(ui, repo, opts)
4458 4460 for n in p:
4459 4461 if n != nullid:
4460 4462 displayer.show(repo[n])
4461 4463 displayer.close()
4462 4464
4463 4465 @command('paths', [], _('[NAME]'))
4464 4466 def paths(ui, repo, search=None):
4465 4467 """show aliases for remote repositories
4466 4468
4467 4469 Show definition of symbolic path name NAME. If no name is given,
4468 4470 show definition of all available names.
4469 4471
4470 4472 Option -q/--quiet suppresses all output when searching for NAME
4471 4473 and shows only the path names when listing all definitions.
4472 4474
4473 4475 Path names are defined in the [paths] section of your
4474 4476 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4475 4477 repository, ``.hg/hgrc`` is used, too.
4476 4478
4477 4479 The path names ``default`` and ``default-push`` have a special
4478 4480 meaning. When performing a push or pull operation, they are used
4479 4481 as fallbacks if no location is specified on the command-line.
4480 4482 When ``default-push`` is set, it will be used for push and
4481 4483 ``default`` will be used for pull; otherwise ``default`` is used
4482 4484 as the fallback for both. When cloning a repository, the clone
4483 4485 source is written as ``default`` in ``.hg/hgrc``. Note that
4484 4486 ``default`` and ``default-push`` apply to all inbound (e.g.
4485 4487 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4486 4488 :hg:`bundle`) operations.
4487 4489
4488 4490 See :hg:`help urls` for more information.
4489 4491
4490 4492 Returns 0 on success.
4491 4493 """
4492 4494 if search:
4493 4495 for name, path in ui.configitems("paths"):
4494 4496 if name == search:
4495 4497 ui.status("%s\n" % util.hidepassword(path))
4496 4498 return
4497 4499 if not ui.quiet:
4498 4500 ui.warn(_("not found!\n"))
4499 4501 return 1
4500 4502 else:
4501 4503 for name, path in ui.configitems("paths"):
4502 4504 if ui.quiet:
4503 4505 ui.write("%s\n" % name)
4504 4506 else:
4505 4507 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4506 4508
4507 4509 @command('^phase',
4508 4510 [('p', 'public', False, _('set changeset phase to public')),
4509 4511 ('d', 'draft', False, _('set changeset phase to draft')),
4510 4512 ('s', 'secret', False, _('set changeset phase to secret')),
4511 4513 ('f', 'force', False, _('allow to move boundary backward')),
4512 4514 ('r', 'rev', [], _('target revision'), _('REV')),
4513 4515 ],
4514 4516 _('[-p|-d|-s] [-f] [-r] REV...'))
4515 4517 def phase(ui, repo, *revs, **opts):
4516 4518 """set or show the current phase name
4517 4519
4518 4520 With no argument, show the phase name of specified revisions.
4519 4521
4520 4522 With one of -p/--public, -d/--draft or -s/--secret, change the
4521 4523 phase value of the specified revisions.
4522 4524
4523 4525 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4524 4526 lower phase to an higher phase. Phases are ordered as follows::
4525 4527
4526 4528 public < draft < secret
4527 4529
4528 4530 Return 0 on success, 1 if no phases were changed or some could not
4529 4531 be changed.
4530 4532 """
4531 4533 # search for a unique phase argument
4532 4534 targetphase = None
4533 4535 for idx, name in enumerate(phases.phasenames):
4534 4536 if opts[name]:
4535 4537 if targetphase is not None:
4536 4538 raise util.Abort(_('only one phase can be specified'))
4537 4539 targetphase = idx
4538 4540
4539 4541 # look for specified revision
4540 4542 revs = list(revs)
4541 4543 revs.extend(opts['rev'])
4542 4544 if not revs:
4543 4545 raise util.Abort(_('no revisions specified'))
4544 4546
4545 4547 revs = scmutil.revrange(repo, revs)
4546 4548
4547 4549 lock = None
4548 4550 ret = 0
4549 4551 if targetphase is None:
4550 4552 # display
4551 4553 for r in revs:
4552 4554 ctx = repo[r]
4553 4555 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4554 4556 else:
4555 4557 lock = repo.lock()
4556 4558 try:
4557 4559 # set phase
4558 4560 if not revs:
4559 4561 raise util.Abort(_('empty revision set'))
4560 4562 nodes = [repo[r].node() for r in revs]
4561 4563 olddata = repo._phasecache.getphaserevs(repo)[:]
4562 4564 phases.advanceboundary(repo, targetphase, nodes)
4563 4565 if opts['force']:
4564 4566 phases.retractboundary(repo, targetphase, nodes)
4565 4567 finally:
4566 4568 lock.release()
4567 4569 newdata = repo._phasecache.getphaserevs(repo)
4568 4570 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4569 4571 rejected = [n for n in nodes
4570 4572 if newdata[repo[n].rev()] < targetphase]
4571 4573 if rejected:
4572 4574 ui.warn(_('cannot move %i changesets to a more permissive '
4573 4575 'phase, use --force\n') % len(rejected))
4574 4576 ret = 1
4575 4577 if changes:
4576 4578 msg = _('phase changed for %i changesets\n') % changes
4577 4579 if ret:
4578 4580 ui.status(msg)
4579 4581 else:
4580 4582 ui.note(msg)
4581 4583 else:
4582 4584 ui.warn(_('no phases changed\n'))
4583 4585 ret = 1
4584 4586 return ret
4585 4587
4586 4588 def postincoming(ui, repo, modheads, optupdate, checkout):
4587 4589 if modheads == 0:
4588 4590 return
4589 4591 if optupdate:
4590 4592 movemarkfrom = repo['.'].node()
4591 4593 try:
4592 4594 ret = hg.update(repo, checkout)
4593 4595 except util.Abort, inst:
4594 4596 ui.warn(_("not updating: %s\n") % str(inst))
4595 4597 return 0
4596 4598 if not ret and not checkout:
4597 4599 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4598 4600 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4599 4601 return ret
4600 4602 if modheads > 1:
4601 4603 currentbranchheads = len(repo.branchheads())
4602 4604 if currentbranchheads == modheads:
4603 4605 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4604 4606 elif currentbranchheads > 1:
4605 4607 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4606 4608 "merge)\n"))
4607 4609 else:
4608 4610 ui.status(_("(run 'hg heads' to see heads)\n"))
4609 4611 else:
4610 4612 ui.status(_("(run 'hg update' to get a working copy)\n"))
4611 4613
4612 4614 @command('^pull',
4613 4615 [('u', 'update', None,
4614 4616 _('update to new branch head if changesets were pulled')),
4615 4617 ('f', 'force', None, _('run even when remote repository is unrelated')),
4616 4618 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4617 4619 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4618 4620 ('b', 'branch', [], _('a specific branch you would like to pull'),
4619 4621 _('BRANCH')),
4620 4622 ] + remoteopts,
4621 4623 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4622 4624 def pull(ui, repo, source="default", **opts):
4623 4625 """pull changes from the specified source
4624 4626
4625 4627 Pull changes from a remote repository to a local one.
4626 4628
4627 4629 This finds all changes from the repository at the specified path
4628 4630 or URL and adds them to a local repository (the current one unless
4629 4631 -R is specified). By default, this does not update the copy of the
4630 4632 project in the working directory.
4631 4633
4632 4634 Use :hg:`incoming` if you want to see what would have been added
4633 4635 by a pull at the time you issued this command. If you then decide
4634 4636 to add those changes to the repository, you should use :hg:`pull
4635 4637 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4636 4638
4637 4639 If SOURCE is omitted, the 'default' path will be used.
4638 4640 See :hg:`help urls` for more information.
4639 4641
4640 4642 Returns 0 on success, 1 if an update had unresolved files.
4641 4643 """
4642 4644 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4643 4645 other = hg.peer(repo, opts, source)
4644 4646 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4645 4647 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4646 4648
4647 4649 if opts.get('bookmark'):
4648 4650 if not revs:
4649 4651 revs = []
4650 4652 rb = other.listkeys('bookmarks')
4651 4653 for b in opts['bookmark']:
4652 4654 if b not in rb:
4653 4655 raise util.Abort(_('remote bookmark %s not found!') % b)
4654 4656 revs.append(rb[b])
4655 4657
4656 4658 if revs:
4657 4659 try:
4658 4660 revs = [other.lookup(rev) for rev in revs]
4659 4661 except error.CapabilityError:
4660 4662 err = _("other repository doesn't support revision lookup, "
4661 4663 "so a rev cannot be specified.")
4662 4664 raise util.Abort(err)
4663 4665
4664 4666 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4665 4667 bookmarks.updatefromremote(ui, repo, other, source)
4666 4668 if checkout:
4667 4669 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4668 4670 repo._subtoppath = source
4669 4671 try:
4670 4672 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4671 4673
4672 4674 finally:
4673 4675 del repo._subtoppath
4674 4676
4675 4677 # update specified bookmarks
4676 4678 if opts.get('bookmark'):
4677 4679 marks = repo._bookmarks
4678 4680 for b in opts['bookmark']:
4679 4681 # explicit pull overrides local bookmark if any
4680 4682 ui.status(_("importing bookmark %s\n") % b)
4681 4683 marks[b] = repo[rb[b]].node()
4682 4684 marks.write()
4683 4685
4684 4686 return ret
4685 4687
4686 4688 @command('^push',
4687 4689 [('f', 'force', None, _('force push')),
4688 4690 ('r', 'rev', [],
4689 4691 _('a changeset intended to be included in the destination'),
4690 4692 _('REV')),
4691 4693 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4692 4694 ('b', 'branch', [],
4693 4695 _('a specific branch you would like to push'), _('BRANCH')),
4694 4696 ('', 'new-branch', False, _('allow pushing a new branch')),
4695 4697 ] + remoteopts,
4696 4698 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4697 4699 def push(ui, repo, dest=None, **opts):
4698 4700 """push changes to the specified destination
4699 4701
4700 4702 Push changesets from the local repository to the specified
4701 4703 destination.
4702 4704
4703 4705 This operation is symmetrical to pull: it is identical to a pull
4704 4706 in the destination repository from the current one.
4705 4707
4706 4708 By default, push will not allow creation of new heads at the
4707 4709 destination, since multiple heads would make it unclear which head
4708 4710 to use. In this situation, it is recommended to pull and merge
4709 4711 before pushing.
4710 4712
4711 4713 Use --new-branch if you want to allow push to create a new named
4712 4714 branch that is not present at the destination. This allows you to
4713 4715 only create a new branch without forcing other changes.
4714 4716
4715 4717 Use -f/--force to override the default behavior and push all
4716 4718 changesets on all branches.
4717 4719
4718 4720 If -r/--rev is used, the specified revision and all its ancestors
4719 4721 will be pushed to the remote repository.
4720 4722
4721 4723 If -B/--bookmark is used, the specified bookmarked revision, its
4722 4724 ancestors, and the bookmark will be pushed to the remote
4723 4725 repository.
4724 4726
4725 4727 Please see :hg:`help urls` for important details about ``ssh://``
4726 4728 URLs. If DESTINATION is omitted, a default path will be used.
4727 4729
4728 4730 Returns 0 if push was successful, 1 if nothing to push.
4729 4731 """
4730 4732
4731 4733 if opts.get('bookmark'):
4732 4734 for b in opts['bookmark']:
4733 4735 # translate -B options to -r so changesets get pushed
4734 4736 if b in repo._bookmarks:
4735 4737 opts.setdefault('rev', []).append(b)
4736 4738 else:
4737 4739 # if we try to push a deleted bookmark, translate it to null
4738 4740 # this lets simultaneous -r, -b options continue working
4739 4741 opts.setdefault('rev', []).append("null")
4740 4742
4741 4743 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4742 4744 dest, branches = hg.parseurl(dest, opts.get('branch'))
4743 4745 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4744 4746 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4745 4747 other = hg.peer(repo, opts, dest)
4746 4748 if revs:
4747 4749 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4748 4750
4749 4751 repo._subtoppath = dest
4750 4752 try:
4751 4753 # push subrepos depth-first for coherent ordering
4752 4754 c = repo['']
4753 4755 subs = c.substate # only repos that are committed
4754 4756 for s in sorted(subs):
4755 4757 if c.sub(s).push(opts) == 0:
4756 4758 return False
4757 4759 finally:
4758 4760 del repo._subtoppath
4759 4761 result = repo.push(other, opts.get('force'), revs=revs,
4760 4762 newbranch=opts.get('new_branch'))
4761 4763
4762 4764 result = not result
4763 4765
4764 4766 if opts.get('bookmark'):
4765 4767 rb = other.listkeys('bookmarks')
4766 4768 for b in opts['bookmark']:
4767 4769 # explicit push overrides remote bookmark if any
4768 4770 if b in repo._bookmarks:
4769 4771 ui.status(_("exporting bookmark %s\n") % b)
4770 4772 new = repo[b].hex()
4771 4773 elif b in rb:
4772 4774 ui.status(_("deleting remote bookmark %s\n") % b)
4773 4775 new = '' # delete
4774 4776 else:
4775 4777 ui.warn(_('bookmark %s does not exist on the local '
4776 4778 'or remote repository!\n') % b)
4777 4779 return 2
4778 4780 old = rb.get(b, '')
4779 4781 r = other.pushkey('bookmarks', b, old, new)
4780 4782 if not r:
4781 4783 ui.warn(_('updating bookmark %s failed!\n') % b)
4782 4784 if not result:
4783 4785 result = 2
4784 4786
4785 4787 return result
4786 4788
4787 4789 @command('recover', [])
4788 4790 def recover(ui, repo):
4789 4791 """roll back an interrupted transaction
4790 4792
4791 4793 Recover from an interrupted commit or pull.
4792 4794
4793 4795 This command tries to fix the repository status after an
4794 4796 interrupted operation. It should only be necessary when Mercurial
4795 4797 suggests it.
4796 4798
4797 4799 Returns 0 if successful, 1 if nothing to recover or verify fails.
4798 4800 """
4799 4801 if repo.recover():
4800 4802 return hg.verify(repo)
4801 4803 return 1
4802 4804
4803 4805 @command('^remove|rm',
4804 4806 [('A', 'after', None, _('record delete for missing files')),
4805 4807 ('f', 'force', None,
4806 4808 _('remove (and delete) file even if added or modified')),
4807 4809 ] + walkopts,
4808 4810 _('[OPTION]... FILE...'))
4809 4811 def remove(ui, repo, *pats, **opts):
4810 4812 """remove the specified files on the next commit
4811 4813
4812 4814 Schedule the indicated files for removal from the current branch.
4813 4815
4814 4816 This command schedules the files to be removed at the next commit.
4815 4817 To undo a remove before that, see :hg:`revert`. To undo added
4816 4818 files, see :hg:`forget`.
4817 4819
4818 4820 .. container:: verbose
4819 4821
4820 4822 -A/--after can be used to remove only files that have already
4821 4823 been deleted, -f/--force can be used to force deletion, and -Af
4822 4824 can be used to remove files from the next revision without
4823 4825 deleting them from the working directory.
4824 4826
4825 4827 The following table details the behavior of remove for different
4826 4828 file states (columns) and option combinations (rows). The file
4827 4829 states are Added [A], Clean [C], Modified [M] and Missing [!]
4828 4830 (as reported by :hg:`status`). The actions are Warn, Remove
4829 4831 (from branch) and Delete (from disk):
4830 4832
4831 4833 ======= == == == ==
4832 4834 A C M !
4833 4835 ======= == == == ==
4834 4836 none W RD W R
4835 4837 -f R RD RD R
4836 4838 -A W W W R
4837 4839 -Af R R R R
4838 4840 ======= == == == ==
4839 4841
4840 4842 Note that remove never deletes files in Added [A] state from the
4841 4843 working directory, not even if option --force is specified.
4842 4844
4843 4845 Returns 0 on success, 1 if any warnings encountered.
4844 4846 """
4845 4847
4846 4848 ret = 0
4847 4849 after, force = opts.get('after'), opts.get('force')
4848 4850 if not pats and not after:
4849 4851 raise util.Abort(_('no files specified'))
4850 4852
4851 4853 m = scmutil.match(repo[None], pats, opts)
4852 4854 s = repo.status(match=m, clean=True)
4853 4855 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4854 4856
4855 4857 # warn about failure to delete explicit files/dirs
4856 4858 wctx = repo[None]
4857 4859 for f in m.files():
4858 4860 if f in repo.dirstate or f in wctx.dirs():
4859 4861 continue
4860 4862 if os.path.exists(m.rel(f)):
4861 4863 if os.path.isdir(m.rel(f)):
4862 4864 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4863 4865 else:
4864 4866 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4865 4867 # missing files will generate a warning elsewhere
4866 4868 ret = 1
4867 4869
4868 4870 if force:
4869 4871 list = modified + deleted + clean + added
4870 4872 elif after:
4871 4873 list = deleted
4872 4874 for f in modified + added + clean:
4873 4875 ui.warn(_('not removing %s: file still exists (use -f'
4874 4876 ' to force removal)\n') % m.rel(f))
4875 4877 ret = 1
4876 4878 else:
4877 4879 list = deleted + clean
4878 4880 for f in modified:
4879 4881 ui.warn(_('not removing %s: file is modified (use -f'
4880 4882 ' to force removal)\n') % m.rel(f))
4881 4883 ret = 1
4882 4884 for f in added:
4883 4885 ui.warn(_('not removing %s: file has been marked for add'
4884 4886 ' (use forget to undo)\n') % m.rel(f))
4885 4887 ret = 1
4886 4888
4887 4889 for f in sorted(list):
4888 4890 if ui.verbose or not m.exact(f):
4889 4891 ui.status(_('removing %s\n') % m.rel(f))
4890 4892
4891 4893 wlock = repo.wlock()
4892 4894 try:
4893 4895 if not after:
4894 4896 for f in list:
4895 4897 if f in added:
4896 4898 continue # we never unlink added files on remove
4897 4899 try:
4898 4900 util.unlinkpath(repo.wjoin(f))
4899 4901 except OSError, inst:
4900 4902 if inst.errno != errno.ENOENT:
4901 4903 raise
4902 4904 repo[None].forget(list)
4903 4905 finally:
4904 4906 wlock.release()
4905 4907
4906 4908 return ret
4907 4909
4908 4910 @command('rename|move|mv',
4909 4911 [('A', 'after', None, _('record a rename that has already occurred')),
4910 4912 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4911 4913 ] + walkopts + dryrunopts,
4912 4914 _('[OPTION]... SOURCE... DEST'))
4913 4915 def rename(ui, repo, *pats, **opts):
4914 4916 """rename files; equivalent of copy + remove
4915 4917
4916 4918 Mark dest as copies of sources; mark sources for deletion. If dest
4917 4919 is a directory, copies are put in that directory. If dest is a
4918 4920 file, there can only be one source.
4919 4921
4920 4922 By default, this command copies the contents of files as they
4921 4923 exist in the working directory. If invoked with -A/--after, the
4922 4924 operation is recorded, but no copying is performed.
4923 4925
4924 4926 This command takes effect at the next commit. To undo a rename
4925 4927 before that, see :hg:`revert`.
4926 4928
4927 4929 Returns 0 on success, 1 if errors are encountered.
4928 4930 """
4929 4931 wlock = repo.wlock(False)
4930 4932 try:
4931 4933 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4932 4934 finally:
4933 4935 wlock.release()
4934 4936
4935 4937 @command('resolve',
4936 4938 [('a', 'all', None, _('select all unresolved files')),
4937 4939 ('l', 'list', None, _('list state of files needing merge')),
4938 4940 ('m', 'mark', None, _('mark files as resolved')),
4939 4941 ('u', 'unmark', None, _('mark files as unresolved')),
4940 4942 ('n', 'no-status', None, _('hide status prefix'))]
4941 4943 + mergetoolopts + walkopts,
4942 4944 _('[OPTION]... [FILE]...'))
4943 4945 def resolve(ui, repo, *pats, **opts):
4944 4946 """redo merges or set/view the merge status of files
4945 4947
4946 4948 Merges with unresolved conflicts are often the result of
4947 4949 non-interactive merging using the ``internal:merge`` configuration
4948 4950 setting, or a command-line merge tool like ``diff3``. The resolve
4949 4951 command is used to manage the files involved in a merge, after
4950 4952 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4951 4953 working directory must have two parents). See :hg:`help
4952 4954 merge-tools` for information on configuring merge tools.
4953 4955
4954 4956 The resolve command can be used in the following ways:
4955 4957
4956 4958 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4957 4959 files, discarding any previous merge attempts. Re-merging is not
4958 4960 performed for files already marked as resolved. Use ``--all/-a``
4959 4961 to select all unresolved files. ``--tool`` can be used to specify
4960 4962 the merge tool used for the given files. It overrides the HGMERGE
4961 4963 environment variable and your configuration files. Previous file
4962 4964 contents are saved with a ``.orig`` suffix.
4963 4965
4964 4966 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4965 4967 (e.g. after having manually fixed-up the files). The default is
4966 4968 to mark all unresolved files.
4967 4969
4968 4970 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4969 4971 default is to mark all resolved files.
4970 4972
4971 4973 - :hg:`resolve -l`: list files which had or still have conflicts.
4972 4974 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4973 4975
4974 4976 Note that Mercurial will not let you commit files with unresolved
4975 4977 merge conflicts. You must use :hg:`resolve -m ...` before you can
4976 4978 commit after a conflicting merge.
4977 4979
4978 4980 Returns 0 on success, 1 if any files fail a resolve attempt.
4979 4981 """
4980 4982
4981 4983 all, mark, unmark, show, nostatus = \
4982 4984 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4983 4985
4984 4986 if (show and (mark or unmark)) or (mark and unmark):
4985 4987 raise util.Abort(_("too many options specified"))
4986 4988 if pats and all:
4987 4989 raise util.Abort(_("can't specify --all and patterns"))
4988 4990 if not (all or pats or show or mark or unmark):
4989 4991 raise util.Abort(_('no files or directories specified; '
4990 4992 'use --all to remerge all files'))
4991 4993
4992 4994 ms = mergemod.mergestate(repo)
4993 4995 m = scmutil.match(repo[None], pats, opts)
4994 4996 ret = 0
4995 4997
4996 4998 for f in ms:
4997 4999 if m(f):
4998 5000 if show:
4999 5001 if nostatus:
5000 5002 ui.write("%s\n" % f)
5001 5003 else:
5002 5004 ui.write("%s %s\n" % (ms[f].upper(), f),
5003 5005 label='resolve.' +
5004 5006 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5005 5007 elif mark:
5006 5008 ms.mark(f, "r")
5007 5009 elif unmark:
5008 5010 ms.mark(f, "u")
5009 5011 else:
5010 5012 wctx = repo[None]
5011 5013 mctx = wctx.parents()[-1]
5012 5014
5013 5015 # backup pre-resolve (merge uses .orig for its own purposes)
5014 5016 a = repo.wjoin(f)
5015 5017 util.copyfile(a, a + ".resolve")
5016 5018
5017 5019 try:
5018 5020 # resolve file
5019 5021 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
5020 5022 if ms.resolve(f, wctx, mctx):
5021 5023 ret = 1
5022 5024 finally:
5023 5025 ui.setconfig('ui', 'forcemerge', '')
5024 5026 ms.commit()
5025 5027
5026 5028 # replace filemerge's .orig file with our resolve file
5027 5029 util.rename(a + ".resolve", a + ".orig")
5028 5030
5029 5031 ms.commit()
5030 5032 return ret
5031 5033
5032 5034 @command('revert',
5033 5035 [('a', 'all', None, _('revert all changes when no arguments given')),
5034 5036 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5035 5037 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5036 5038 ('C', 'no-backup', None, _('do not save backup copies of files')),
5037 5039 ] + walkopts + dryrunopts,
5038 5040 _('[OPTION]... [-r REV] [NAME]...'))
5039 5041 def revert(ui, repo, *pats, **opts):
5040 5042 """restore files to their checkout state
5041 5043
5042 5044 .. note::
5043 5045
5044 5046 To check out earlier revisions, you should use :hg:`update REV`.
5045 5047 To cancel an uncommitted merge (and lose your changes), use
5046 5048 :hg:`update --clean .`.
5047 5049
5048 5050 With no revision specified, revert the specified files or directories
5049 5051 to the contents they had in the parent of the working directory.
5050 5052 This restores the contents of files to an unmodified
5051 5053 state and unschedules adds, removes, copies, and renames. If the
5052 5054 working directory has two parents, you must explicitly specify a
5053 5055 revision.
5054 5056
5055 5057 Using the -r/--rev or -d/--date options, revert the given files or
5056 5058 directories to their states as of a specific revision. Because
5057 5059 revert does not change the working directory parents, this will
5058 5060 cause these files to appear modified. This can be helpful to "back
5059 5061 out" some or all of an earlier change. See :hg:`backout` for a
5060 5062 related method.
5061 5063
5062 5064 Modified files are saved with a .orig suffix before reverting.
5063 5065 To disable these backups, use --no-backup.
5064 5066
5065 5067 See :hg:`help dates` for a list of formats valid for -d/--date.
5066 5068
5067 5069 Returns 0 on success.
5068 5070 """
5069 5071
5070 5072 if opts.get("date"):
5071 5073 if opts.get("rev"):
5072 5074 raise util.Abort(_("you can't specify a revision and a date"))
5073 5075 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5074 5076
5075 5077 parent, p2 = repo.dirstate.parents()
5076 5078 if not opts.get('rev') and p2 != nullid:
5077 5079 # revert after merge is a trap for new users (issue2915)
5078 5080 raise util.Abort(_('uncommitted merge with no revision specified'),
5079 5081 hint=_('use "hg update" or see "hg help revert"'))
5080 5082
5081 5083 ctx = scmutil.revsingle(repo, opts.get('rev'))
5082 5084
5083 5085 if not pats and not opts.get('all'):
5084 5086 msg = _("no files or directories specified")
5085 5087 if p2 != nullid:
5086 5088 hint = _("uncommitted merge, use --all to discard all changes,"
5087 5089 " or 'hg update -C .' to abort the merge")
5088 5090 raise util.Abort(msg, hint=hint)
5089 5091 dirty = util.any(repo.status())
5090 5092 node = ctx.node()
5091 5093 if node != parent:
5092 5094 if dirty:
5093 5095 hint = _("uncommitted changes, use --all to discard all"
5094 5096 " changes, or 'hg update %s' to update") % ctx.rev()
5095 5097 else:
5096 5098 hint = _("use --all to revert all files,"
5097 5099 " or 'hg update %s' to update") % ctx.rev()
5098 5100 elif dirty:
5099 5101 hint = _("uncommitted changes, use --all to discard all changes")
5100 5102 else:
5101 5103 hint = _("use --all to revert all files")
5102 5104 raise util.Abort(msg, hint=hint)
5103 5105
5104 5106 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5105 5107
5106 5108 @command('rollback', dryrunopts +
5107 5109 [('f', 'force', False, _('ignore safety measures'))])
5108 5110 def rollback(ui, repo, **opts):
5109 5111 """roll back the last transaction (dangerous)
5110 5112
5111 5113 This command should be used with care. There is only one level of
5112 5114 rollback, and there is no way to undo a rollback. It will also
5113 5115 restore the dirstate at the time of the last transaction, losing
5114 5116 any dirstate changes since that time. This command does not alter
5115 5117 the working directory.
5116 5118
5117 5119 Transactions are used to encapsulate the effects of all commands
5118 5120 that create new changesets or propagate existing changesets into a
5119 5121 repository.
5120 5122
5121 5123 .. container:: verbose
5122 5124
5123 5125 For example, the following commands are transactional, and their
5124 5126 effects can be rolled back:
5125 5127
5126 5128 - commit
5127 5129 - import
5128 5130 - pull
5129 5131 - push (with this repository as the destination)
5130 5132 - unbundle
5131 5133
5132 5134 To avoid permanent data loss, rollback will refuse to rollback a
5133 5135 commit transaction if it isn't checked out. Use --force to
5134 5136 override this protection.
5135 5137
5136 5138 This command is not intended for use on public repositories. Once
5137 5139 changes are visible for pull by other users, rolling a transaction
5138 5140 back locally is ineffective (someone else may already have pulled
5139 5141 the changes). Furthermore, a race is possible with readers of the
5140 5142 repository; for example an in-progress pull from the repository
5141 5143 may fail if a rollback is performed.
5142 5144
5143 5145 Returns 0 on success, 1 if no rollback data is available.
5144 5146 """
5145 5147 return repo.rollback(dryrun=opts.get('dry_run'),
5146 5148 force=opts.get('force'))
5147 5149
5148 5150 @command('root', [])
5149 5151 def root(ui, repo):
5150 5152 """print the root (top) of the current working directory
5151 5153
5152 5154 Print the root directory of the current repository.
5153 5155
5154 5156 Returns 0 on success.
5155 5157 """
5156 5158 ui.write(repo.root + "\n")
5157 5159
5158 5160 @command('^serve',
5159 5161 [('A', 'accesslog', '', _('name of access log file to write to'),
5160 5162 _('FILE')),
5161 5163 ('d', 'daemon', None, _('run server in background')),
5162 5164 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5163 5165 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5164 5166 # use string type, then we can check if something was passed
5165 5167 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5166 5168 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5167 5169 _('ADDR')),
5168 5170 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5169 5171 _('PREFIX')),
5170 5172 ('n', 'name', '',
5171 5173 _('name to show in web pages (default: working directory)'), _('NAME')),
5172 5174 ('', 'web-conf', '',
5173 5175 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5174 5176 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5175 5177 _('FILE')),
5176 5178 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5177 5179 ('', 'stdio', None, _('for remote clients')),
5178 5180 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5179 5181 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5180 5182 ('', 'style', '', _('template style to use'), _('STYLE')),
5181 5183 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5182 5184 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5183 5185 _('[OPTION]...'))
5184 5186 def serve(ui, repo, **opts):
5185 5187 """start stand-alone webserver
5186 5188
5187 5189 Start a local HTTP repository browser and pull server. You can use
5188 5190 this for ad-hoc sharing and browsing of repositories. It is
5189 5191 recommended to use a real web server to serve a repository for
5190 5192 longer periods of time.
5191 5193
5192 5194 Please note that the server does not implement access control.
5193 5195 This means that, by default, anybody can read from the server and
5194 5196 nobody can write to it by default. Set the ``web.allow_push``
5195 5197 option to ``*`` to allow everybody to push to the server. You
5196 5198 should use a real web server if you need to authenticate users.
5197 5199
5198 5200 By default, the server logs accesses to stdout and errors to
5199 5201 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5200 5202 files.
5201 5203
5202 5204 To have the server choose a free port number to listen on, specify
5203 5205 a port number of 0; in this case, the server will print the port
5204 5206 number it uses.
5205 5207
5206 5208 Returns 0 on success.
5207 5209 """
5208 5210
5209 5211 if opts["stdio"] and opts["cmdserver"]:
5210 5212 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5211 5213
5212 5214 def checkrepo():
5213 5215 if repo is None:
5214 5216 raise error.RepoError(_("there is no Mercurial repository here"
5215 5217 " (.hg not found)"))
5216 5218
5217 5219 if opts["stdio"]:
5218 5220 checkrepo()
5219 5221 s = sshserver.sshserver(ui, repo)
5220 5222 s.serve_forever()
5221 5223
5222 5224 if opts["cmdserver"]:
5223 5225 checkrepo()
5224 5226 s = commandserver.server(ui, repo, opts["cmdserver"])
5225 5227 return s.serve()
5226 5228
5227 5229 # this way we can check if something was given in the command-line
5228 5230 if opts.get('port'):
5229 5231 opts['port'] = util.getport(opts.get('port'))
5230 5232
5231 5233 baseui = repo and repo.baseui or ui
5232 5234 optlist = ("name templates style address port prefix ipv6"
5233 5235 " accesslog errorlog certificate encoding")
5234 5236 for o in optlist.split():
5235 5237 val = opts.get(o, '')
5236 5238 if val in (None, ''): # should check against default options instead
5237 5239 continue
5238 5240 baseui.setconfig("web", o, val)
5239 5241 if repo and repo.ui != baseui:
5240 5242 repo.ui.setconfig("web", o, val)
5241 5243
5242 5244 o = opts.get('web_conf') or opts.get('webdir_conf')
5243 5245 if not o:
5244 5246 if not repo:
5245 5247 raise error.RepoError(_("there is no Mercurial repository"
5246 5248 " here (.hg not found)"))
5247 5249 o = repo.root
5248 5250
5249 5251 app = hgweb.hgweb(o, baseui=ui)
5250 5252
5251 5253 class service(object):
5252 5254 def init(self):
5253 5255 util.setsignalhandler()
5254 5256 self.httpd = hgweb.server.create_server(ui, app)
5255 5257
5256 5258 if opts['port'] and not ui.verbose:
5257 5259 return
5258 5260
5259 5261 if self.httpd.prefix:
5260 5262 prefix = self.httpd.prefix.strip('/') + '/'
5261 5263 else:
5262 5264 prefix = ''
5263 5265
5264 5266 port = ':%d' % self.httpd.port
5265 5267 if port == ':80':
5266 5268 port = ''
5267 5269
5268 5270 bindaddr = self.httpd.addr
5269 5271 if bindaddr == '0.0.0.0':
5270 5272 bindaddr = '*'
5271 5273 elif ':' in bindaddr: # IPv6
5272 5274 bindaddr = '[%s]' % bindaddr
5273 5275
5274 5276 fqaddr = self.httpd.fqaddr
5275 5277 if ':' in fqaddr:
5276 5278 fqaddr = '[%s]' % fqaddr
5277 5279 if opts['port']:
5278 5280 write = ui.status
5279 5281 else:
5280 5282 write = ui.write
5281 5283 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5282 5284 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5283 5285
5284 5286 def run(self):
5285 5287 self.httpd.serve_forever()
5286 5288
5287 5289 service = service()
5288 5290
5289 5291 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5290 5292
5291 5293 @command('showconfig|debugconfig',
5292 5294 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5293 5295 _('[-u] [NAME]...'))
5294 5296 def showconfig(ui, repo, *values, **opts):
5295 5297 """show combined config settings from all hgrc files
5296 5298
5297 5299 With no arguments, print names and values of all config items.
5298 5300
5299 5301 With one argument of the form section.name, print just the value
5300 5302 of that config item.
5301 5303
5302 5304 With multiple arguments, print names and values of all config
5303 5305 items with matching section names.
5304 5306
5305 5307 With --debug, the source (filename and line number) is printed
5306 5308 for each config item.
5307 5309
5308 5310 Returns 0 on success.
5309 5311 """
5310 5312
5311 5313 for f in scmutil.rcpath():
5312 5314 ui.debug('read config from: %s\n' % f)
5313 5315 untrusted = bool(opts.get('untrusted'))
5314 5316 if values:
5315 5317 sections = [v for v in values if '.' not in v]
5316 5318 items = [v for v in values if '.' in v]
5317 5319 if len(items) > 1 or items and sections:
5318 5320 raise util.Abort(_('only one config item permitted'))
5319 5321 for section, name, value in ui.walkconfig(untrusted=untrusted):
5320 5322 value = str(value).replace('\n', '\\n')
5321 5323 sectname = section + '.' + name
5322 5324 if values:
5323 5325 for v in values:
5324 5326 if v == section:
5325 5327 ui.debug('%s: ' %
5326 5328 ui.configsource(section, name, untrusted))
5327 5329 ui.write('%s=%s\n' % (sectname, value))
5328 5330 elif v == sectname:
5329 5331 ui.debug('%s: ' %
5330 5332 ui.configsource(section, name, untrusted))
5331 5333 ui.write(value, '\n')
5332 5334 else:
5333 5335 ui.debug('%s: ' %
5334 5336 ui.configsource(section, name, untrusted))
5335 5337 ui.write('%s=%s\n' % (sectname, value))
5336 5338
5337 5339 @command('^status|st',
5338 5340 [('A', 'all', None, _('show status of all files')),
5339 5341 ('m', 'modified', None, _('show only modified files')),
5340 5342 ('a', 'added', None, _('show only added files')),
5341 5343 ('r', 'removed', None, _('show only removed files')),
5342 5344 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5343 5345 ('c', 'clean', None, _('show only files without changes')),
5344 5346 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5345 5347 ('i', 'ignored', None, _('show only ignored files')),
5346 5348 ('n', 'no-status', None, _('hide status prefix')),
5347 5349 ('C', 'copies', None, _('show source of copied files')),
5348 5350 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5349 5351 ('', 'rev', [], _('show difference from revision'), _('REV')),
5350 5352 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5351 5353 ] + walkopts + subrepoopts,
5352 5354 _('[OPTION]... [FILE]...'))
5353 5355 def status(ui, repo, *pats, **opts):
5354 5356 """show changed files in the working directory
5355 5357
5356 5358 Show status of files in the repository. If names are given, only
5357 5359 files that match are shown. Files that are clean or ignored or
5358 5360 the source of a copy/move operation, are not listed unless
5359 5361 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5360 5362 Unless options described with "show only ..." are given, the
5361 5363 options -mardu are used.
5362 5364
5363 5365 Option -q/--quiet hides untracked (unknown and ignored) files
5364 5366 unless explicitly requested with -u/--unknown or -i/--ignored.
5365 5367
5366 5368 .. note::
5367 5369 status may appear to disagree with diff if permissions have
5368 5370 changed or a merge has occurred. The standard diff format does
5369 5371 not report permission changes and diff only reports changes
5370 5372 relative to one merge parent.
5371 5373
5372 5374 If one revision is given, it is used as the base revision.
5373 5375 If two revisions are given, the differences between them are
5374 5376 shown. The --change option can also be used as a shortcut to list
5375 5377 the changed files of a revision from its first parent.
5376 5378
5377 5379 The codes used to show the status of files are::
5378 5380
5379 5381 M = modified
5380 5382 A = added
5381 5383 R = removed
5382 5384 C = clean
5383 5385 ! = missing (deleted by non-hg command, but still tracked)
5384 5386 ? = not tracked
5385 5387 I = ignored
5386 5388 = origin of the previous file listed as A (added)
5387 5389
5388 5390 .. container:: verbose
5389 5391
5390 5392 Examples:
5391 5393
5392 5394 - show changes in the working directory relative to a
5393 5395 changeset::
5394 5396
5395 5397 hg status --rev 9353
5396 5398
5397 5399 - show all changes including copies in an existing changeset::
5398 5400
5399 5401 hg status --copies --change 9353
5400 5402
5401 5403 - get a NUL separated list of added files, suitable for xargs::
5402 5404
5403 5405 hg status -an0
5404 5406
5405 5407 Returns 0 on success.
5406 5408 """
5407 5409
5408 5410 revs = opts.get('rev')
5409 5411 change = opts.get('change')
5410 5412
5411 5413 if revs and change:
5412 5414 msg = _('cannot specify --rev and --change at the same time')
5413 5415 raise util.Abort(msg)
5414 5416 elif change:
5415 5417 node2 = scmutil.revsingle(repo, change, None).node()
5416 5418 node1 = repo[node2].p1().node()
5417 5419 else:
5418 5420 node1, node2 = scmutil.revpair(repo, revs)
5419 5421
5420 5422 cwd = (pats and repo.getcwd()) or ''
5421 5423 end = opts.get('print0') and '\0' or '\n'
5422 5424 copy = {}
5423 5425 states = 'modified added removed deleted unknown ignored clean'.split()
5424 5426 show = [k for k in states if opts.get(k)]
5425 5427 if opts.get('all'):
5426 5428 show += ui.quiet and (states[:4] + ['clean']) or states
5427 5429 if not show:
5428 5430 show = ui.quiet and states[:4] or states[:5]
5429 5431
5430 5432 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5431 5433 'ignored' in show, 'clean' in show, 'unknown' in show,
5432 5434 opts.get('subrepos'))
5433 5435 changestates = zip(states, 'MAR!?IC', stat)
5434 5436
5435 5437 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5436 5438 copy = copies.pathcopies(repo[node1], repo[node2])
5437 5439
5438 5440 fm = ui.formatter('status', opts)
5439 5441 fmt = '%s' + end
5440 5442 showchar = not opts.get('no_status')
5441 5443
5442 5444 for state, char, files in changestates:
5443 5445 if state in show:
5444 5446 label = 'status.' + state
5445 5447 for f in files:
5446 5448 fm.startitem()
5447 5449 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5448 5450 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5449 5451 if f in copy:
5450 5452 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5451 5453 label='status.copied')
5452 5454 fm.end()
5453 5455
5454 5456 @command('^summary|sum',
5455 5457 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5456 5458 def summary(ui, repo, **opts):
5457 5459 """summarize working directory state
5458 5460
5459 5461 This generates a brief summary of the working directory state,
5460 5462 including parents, branch, commit status, and available updates.
5461 5463
5462 5464 With the --remote option, this will check the default paths for
5463 5465 incoming and outgoing changes. This can be time-consuming.
5464 5466
5465 5467 Returns 0 on success.
5466 5468 """
5467 5469
5468 5470 ctx = repo[None]
5469 5471 parents = ctx.parents()
5470 5472 pnode = parents[0].node()
5471 5473 marks = []
5472 5474
5473 5475 for p in parents:
5474 5476 # label with log.changeset (instead of log.parent) since this
5475 5477 # shows a working directory parent *changeset*:
5476 5478 # i18n: column positioning for "hg summary"
5477 5479 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5478 5480 label='log.changeset changeset.%s' % p.phasestr())
5479 5481 ui.write(' '.join(p.tags()), label='log.tag')
5480 5482 if p.bookmarks():
5481 5483 marks.extend(p.bookmarks())
5482 5484 if p.rev() == -1:
5483 5485 if not len(repo):
5484 5486 ui.write(_(' (empty repository)'))
5485 5487 else:
5486 5488 ui.write(_(' (no revision checked out)'))
5487 5489 ui.write('\n')
5488 5490 if p.description():
5489 5491 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5490 5492 label='log.summary')
5491 5493
5492 5494 branch = ctx.branch()
5493 5495 bheads = repo.branchheads(branch)
5494 5496 # i18n: column positioning for "hg summary"
5495 5497 m = _('branch: %s\n') % branch
5496 5498 if branch != 'default':
5497 5499 ui.write(m, label='log.branch')
5498 5500 else:
5499 5501 ui.status(m, label='log.branch')
5500 5502
5501 5503 if marks:
5502 5504 current = repo._bookmarkcurrent
5503 5505 # i18n: column positioning for "hg summary"
5504 5506 ui.write(_('bookmarks:'), label='log.bookmark')
5505 5507 if current is not None:
5506 5508 try:
5507 5509 marks.remove(current)
5508 5510 ui.write(' *' + current, label='bookmarks.current')
5509 5511 except ValueError:
5510 5512 # current bookmark not in parent ctx marks
5511 5513 pass
5512 5514 for m in marks:
5513 5515 ui.write(' ' + m, label='log.bookmark')
5514 5516 ui.write('\n', label='log.bookmark')
5515 5517
5516 5518 st = list(repo.status(unknown=True))[:6]
5517 5519
5518 5520 c = repo.dirstate.copies()
5519 5521 copied, renamed = [], []
5520 5522 for d, s in c.iteritems():
5521 5523 if s in st[2]:
5522 5524 st[2].remove(s)
5523 5525 renamed.append(d)
5524 5526 else:
5525 5527 copied.append(d)
5526 5528 if d in st[1]:
5527 5529 st[1].remove(d)
5528 5530 st.insert(3, renamed)
5529 5531 st.insert(4, copied)
5530 5532
5531 5533 ms = mergemod.mergestate(repo)
5532 5534 st.append([f for f in ms if ms[f] == 'u'])
5533 5535
5534 5536 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5535 5537 st.append(subs)
5536 5538
5537 5539 labels = [ui.label(_('%d modified'), 'status.modified'),
5538 5540 ui.label(_('%d added'), 'status.added'),
5539 5541 ui.label(_('%d removed'), 'status.removed'),
5540 5542 ui.label(_('%d renamed'), 'status.copied'),
5541 5543 ui.label(_('%d copied'), 'status.copied'),
5542 5544 ui.label(_('%d deleted'), 'status.deleted'),
5543 5545 ui.label(_('%d unknown'), 'status.unknown'),
5544 5546 ui.label(_('%d ignored'), 'status.ignored'),
5545 5547 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5546 5548 ui.label(_('%d subrepos'), 'status.modified')]
5547 5549 t = []
5548 5550 for s, l in zip(st, labels):
5549 5551 if s:
5550 5552 t.append(l % len(s))
5551 5553
5552 5554 t = ', '.join(t)
5553 5555 cleanworkdir = False
5554 5556
5555 5557 if len(parents) > 1:
5556 5558 t += _(' (merge)')
5557 5559 elif branch != parents[0].branch():
5558 5560 t += _(' (new branch)')
5559 5561 elif (parents[0].closesbranch() and
5560 5562 pnode in repo.branchheads(branch, closed=True)):
5561 5563 t += _(' (head closed)')
5562 5564 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5563 5565 t += _(' (clean)')
5564 5566 cleanworkdir = True
5565 5567 elif pnode not in bheads:
5566 5568 t += _(' (new branch head)')
5567 5569
5568 5570 if cleanworkdir:
5569 5571 # i18n: column positioning for "hg summary"
5570 5572 ui.status(_('commit: %s\n') % t.strip())
5571 5573 else:
5572 5574 # i18n: column positioning for "hg summary"
5573 5575 ui.write(_('commit: %s\n') % t.strip())
5574 5576
5575 5577 # all ancestors of branch heads - all ancestors of parent = new csets
5576 5578 new = [0] * len(repo)
5577 5579 cl = repo.changelog
5578 5580 for a in [cl.rev(n) for n in bheads]:
5579 5581 new[a] = 1
5580 5582 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5581 5583 new[a] = 1
5582 5584 for a in [p.rev() for p in parents]:
5583 5585 if a >= 0:
5584 5586 new[a] = 0
5585 5587 for a in cl.ancestors([p.rev() for p in parents]):
5586 5588 new[a] = 0
5587 5589 new = sum(new)
5588 5590
5589 5591 if new == 0:
5590 5592 # i18n: column positioning for "hg summary"
5591 5593 ui.status(_('update: (current)\n'))
5592 5594 elif pnode not in bheads:
5593 5595 # i18n: column positioning for "hg summary"
5594 5596 ui.write(_('update: %d new changesets (update)\n') % new)
5595 5597 else:
5596 5598 # i18n: column positioning for "hg summary"
5597 5599 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5598 5600 (new, len(bheads)))
5599 5601
5600 5602 if opts.get('remote'):
5601 5603 t = []
5602 5604 source, branches = hg.parseurl(ui.expandpath('default'))
5603 5605 other = hg.peer(repo, {}, source)
5604 5606 revs, checkout = hg.addbranchrevs(repo, other, branches,
5605 5607 opts.get('rev'))
5606 5608 ui.debug('comparing with %s\n' % util.hidepassword(source))
5607 5609 repo.ui.pushbuffer()
5608 5610 commoninc = discovery.findcommonincoming(repo, other)
5609 5611 _common, incoming, _rheads = commoninc
5610 5612 repo.ui.popbuffer()
5611 5613 if incoming:
5612 5614 t.append(_('1 or more incoming'))
5613 5615
5614 5616 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5615 5617 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5616 5618 if source != dest:
5617 5619 other = hg.peer(repo, {}, dest)
5618 5620 commoninc = None
5619 5621 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5620 5622 repo.ui.pushbuffer()
5621 5623 outgoing = discovery.findcommonoutgoing(repo, other,
5622 5624 commoninc=commoninc)
5623 5625 repo.ui.popbuffer()
5624 5626 o = outgoing.missing
5625 5627 if o:
5626 5628 t.append(_('%d outgoing') % len(o))
5627 5629 if 'bookmarks' in other.listkeys('namespaces'):
5628 5630 lmarks = repo.listkeys('bookmarks')
5629 5631 rmarks = other.listkeys('bookmarks')
5630 5632 diff = set(rmarks) - set(lmarks)
5631 5633 if len(diff) > 0:
5632 5634 t.append(_('%d incoming bookmarks') % len(diff))
5633 5635 diff = set(lmarks) - set(rmarks)
5634 5636 if len(diff) > 0:
5635 5637 t.append(_('%d outgoing bookmarks') % len(diff))
5636 5638
5637 5639 if t:
5638 5640 # i18n: column positioning for "hg summary"
5639 5641 ui.write(_('remote: %s\n') % (', '.join(t)))
5640 5642 else:
5641 5643 # i18n: column positioning for "hg summary"
5642 5644 ui.status(_('remote: (synced)\n'))
5643 5645
5644 5646 @command('tag',
5645 5647 [('f', 'force', None, _('force tag')),
5646 5648 ('l', 'local', None, _('make the tag local')),
5647 5649 ('r', 'rev', '', _('revision to tag'), _('REV')),
5648 5650 ('', 'remove', None, _('remove a tag')),
5649 5651 # -l/--local is already there, commitopts cannot be used
5650 5652 ('e', 'edit', None, _('edit commit message')),
5651 5653 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5652 5654 ] + commitopts2,
5653 5655 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5654 5656 def tag(ui, repo, name1, *names, **opts):
5655 5657 """add one or more tags for the current or given revision
5656 5658
5657 5659 Name a particular revision using <name>.
5658 5660
5659 5661 Tags are used to name particular revisions of the repository and are
5660 5662 very useful to compare different revisions, to go back to significant
5661 5663 earlier versions or to mark branch points as releases, etc. Changing
5662 5664 an existing tag is normally disallowed; use -f/--force to override.
5663 5665
5664 5666 If no revision is given, the parent of the working directory is
5665 5667 used, or tip if no revision is checked out.
5666 5668
5667 5669 To facilitate version control, distribution, and merging of tags,
5668 5670 they are stored as a file named ".hgtags" which is managed similarly
5669 5671 to other project files and can be hand-edited if necessary. This
5670 5672 also means that tagging creates a new commit. The file
5671 5673 ".hg/localtags" is used for local tags (not shared among
5672 5674 repositories).
5673 5675
5674 5676 Tag commits are usually made at the head of a branch. If the parent
5675 5677 of the working directory is not a branch head, :hg:`tag` aborts; use
5676 5678 -f/--force to force the tag commit to be based on a non-head
5677 5679 changeset.
5678 5680
5679 5681 See :hg:`help dates` for a list of formats valid for -d/--date.
5680 5682
5681 5683 Since tag names have priority over branch names during revision
5682 5684 lookup, using an existing branch name as a tag name is discouraged.
5683 5685
5684 5686 Returns 0 on success.
5685 5687 """
5686 5688 wlock = lock = None
5687 5689 try:
5688 5690 wlock = repo.wlock()
5689 5691 lock = repo.lock()
5690 5692 rev_ = "."
5691 5693 names = [t.strip() for t in (name1,) + names]
5692 5694 if len(names) != len(set(names)):
5693 5695 raise util.Abort(_('tag names must be unique'))
5694 5696 for n in names:
5695 5697 scmutil.checknewlabel(repo, n, 'tag')
5696 5698 if not n:
5697 5699 raise util.Abort(_('tag names cannot consist entirely of '
5698 5700 'whitespace'))
5699 5701 if opts.get('rev') and opts.get('remove'):
5700 5702 raise util.Abort(_("--rev and --remove are incompatible"))
5701 5703 if opts.get('rev'):
5702 5704 rev_ = opts['rev']
5703 5705 message = opts.get('message')
5704 5706 if opts.get('remove'):
5705 5707 expectedtype = opts.get('local') and 'local' or 'global'
5706 5708 for n in names:
5707 5709 if not repo.tagtype(n):
5708 5710 raise util.Abort(_("tag '%s' does not exist") % n)
5709 5711 if repo.tagtype(n) != expectedtype:
5710 5712 if expectedtype == 'global':
5711 5713 raise util.Abort(_("tag '%s' is not a global tag") % n)
5712 5714 else:
5713 5715 raise util.Abort(_("tag '%s' is not a local tag") % n)
5714 5716 rev_ = nullid
5715 5717 if not message:
5716 5718 # we don't translate commit messages
5717 5719 message = 'Removed tag %s' % ', '.join(names)
5718 5720 elif not opts.get('force'):
5719 5721 for n in names:
5720 5722 if n in repo.tags():
5721 5723 raise util.Abort(_("tag '%s' already exists "
5722 5724 "(use -f to force)") % n)
5723 5725 if not opts.get('local'):
5724 5726 p1, p2 = repo.dirstate.parents()
5725 5727 if p2 != nullid:
5726 5728 raise util.Abort(_('uncommitted merge'))
5727 5729 bheads = repo.branchheads()
5728 5730 if not opts.get('force') and bheads and p1 not in bheads:
5729 5731 raise util.Abort(_('not at a branch head (use -f to force)'))
5730 5732 r = scmutil.revsingle(repo, rev_).node()
5731 5733
5732 5734 if not message:
5733 5735 # we don't translate commit messages
5734 5736 message = ('Added tag %s for changeset %s' %
5735 5737 (', '.join(names), short(r)))
5736 5738
5737 5739 date = opts.get('date')
5738 5740 if date:
5739 5741 date = util.parsedate(date)
5740 5742
5741 5743 if opts.get('edit'):
5742 5744 message = ui.edit(message, ui.username())
5743 5745
5744 5746 # don't allow tagging the null rev
5745 5747 if (not opts.get('remove') and
5746 5748 scmutil.revsingle(repo, rev_).rev() == nullrev):
5747 5749 raise util.Abort(_("null revision specified"))
5748 5750
5749 5751 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5750 5752 finally:
5751 5753 release(lock, wlock)
5752 5754
5753 5755 @command('tags', [], '')
5754 5756 def tags(ui, repo, **opts):
5755 5757 """list repository tags
5756 5758
5757 5759 This lists both regular and local tags. When the -v/--verbose
5758 5760 switch is used, a third column "local" is printed for local tags.
5759 5761
5760 5762 Returns 0 on success.
5761 5763 """
5762 5764
5763 5765 fm = ui.formatter('tags', opts)
5764 5766 hexfunc = ui.debugflag and hex or short
5765 5767 tagtype = ""
5766 5768
5767 5769 for t, n in reversed(repo.tagslist()):
5768 5770 hn = hexfunc(n)
5769 5771 label = 'tags.normal'
5770 5772 tagtype = ''
5771 5773 if repo.tagtype(t) == 'local':
5772 5774 label = 'tags.local'
5773 5775 tagtype = 'local'
5774 5776
5775 5777 fm.startitem()
5776 5778 fm.write('tag', '%s', t, label=label)
5777 5779 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5778 5780 fm.condwrite(not ui.quiet, 'rev id', fmt,
5779 5781 repo.changelog.rev(n), hn, label=label)
5780 5782 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5781 5783 tagtype, label=label)
5782 5784 fm.plain('\n')
5783 5785 fm.end()
5784 5786
5785 5787 @command('tip',
5786 5788 [('p', 'patch', None, _('show patch')),
5787 5789 ('g', 'git', None, _('use git extended diff format')),
5788 5790 ] + templateopts,
5789 5791 _('[-p] [-g]'))
5790 5792 def tip(ui, repo, **opts):
5791 5793 """show the tip revision
5792 5794
5793 5795 The tip revision (usually just called the tip) is the changeset
5794 5796 most recently added to the repository (and therefore the most
5795 5797 recently changed head).
5796 5798
5797 5799 If you have just made a commit, that commit will be the tip. If
5798 5800 you have just pulled changes from another repository, the tip of
5799 5801 that repository becomes the current tip. The "tip" tag is special
5800 5802 and cannot be renamed or assigned to a different changeset.
5801 5803
5802 5804 Returns 0 on success.
5803 5805 """
5804 5806 displayer = cmdutil.show_changeset(ui, repo, opts)
5805 5807 displayer.show(repo[len(repo) - 1])
5806 5808 displayer.close()
5807 5809
5808 5810 @command('unbundle',
5809 5811 [('u', 'update', None,
5810 5812 _('update to new branch head if changesets were unbundled'))],
5811 5813 _('[-u] FILE...'))
5812 5814 def unbundle(ui, repo, fname1, *fnames, **opts):
5813 5815 """apply one or more changegroup files
5814 5816
5815 5817 Apply one or more compressed changegroup files generated by the
5816 5818 bundle command.
5817 5819
5818 5820 Returns 0 on success, 1 if an update has unresolved files.
5819 5821 """
5820 5822 fnames = (fname1,) + fnames
5821 5823
5822 5824 lock = repo.lock()
5823 5825 wc = repo['.']
5824 5826 try:
5825 5827 for fname in fnames:
5826 5828 f = hg.openpath(ui, fname)
5827 5829 gen = changegroup.readbundle(f, fname)
5828 5830 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5829 5831 finally:
5830 5832 lock.release()
5831 5833 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5832 5834 return postincoming(ui, repo, modheads, opts.get('update'), None)
5833 5835
5834 5836 @command('^update|up|checkout|co',
5835 5837 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5836 5838 ('c', 'check', None,
5837 5839 _('update across branches if no uncommitted changes')),
5838 5840 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5839 5841 ('r', 'rev', '', _('revision'), _('REV'))],
5840 5842 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5841 5843 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5842 5844 """update working directory (or switch revisions)
5843 5845
5844 5846 Update the repository's working directory to the specified
5845 5847 changeset. If no changeset is specified, update to the tip of the
5846 5848 current named branch and move the current bookmark (see :hg:`help
5847 5849 bookmarks`).
5848 5850
5849 5851 Update sets the working directory's parent revision to the specified
5850 5852 changeset (see :hg:`help parents`).
5851 5853
5852 5854 If the changeset is not a descendant or ancestor of the working
5853 5855 directory's parent, the update is aborted. With the -c/--check
5854 5856 option, the working directory is checked for uncommitted changes; if
5855 5857 none are found, the working directory is updated to the specified
5856 5858 changeset.
5857 5859
5858 5860 .. container:: verbose
5859 5861
5860 5862 The following rules apply when the working directory contains
5861 5863 uncommitted changes:
5862 5864
5863 5865 1. If neither -c/--check nor -C/--clean is specified, and if
5864 5866 the requested changeset is an ancestor or descendant of
5865 5867 the working directory's parent, the uncommitted changes
5866 5868 are merged into the requested changeset and the merged
5867 5869 result is left uncommitted. If the requested changeset is
5868 5870 not an ancestor or descendant (that is, it is on another
5869 5871 branch), the update is aborted and the uncommitted changes
5870 5872 are preserved.
5871 5873
5872 5874 2. With the -c/--check option, the update is aborted and the
5873 5875 uncommitted changes are preserved.
5874 5876
5875 5877 3. With the -C/--clean option, uncommitted changes are discarded and
5876 5878 the working directory is updated to the requested changeset.
5877 5879
5878 5880 To cancel an uncommitted merge (and lose your changes), use
5879 5881 :hg:`update --clean .`.
5880 5882
5881 5883 Use null as the changeset to remove the working directory (like
5882 5884 :hg:`clone -U`).
5883 5885
5884 5886 If you want to revert just one file to an older revision, use
5885 5887 :hg:`revert [-r REV] NAME`.
5886 5888
5887 5889 See :hg:`help dates` for a list of formats valid for -d/--date.
5888 5890
5889 5891 Returns 0 on success, 1 if there are unresolved files.
5890 5892 """
5891 5893 if rev and node:
5892 5894 raise util.Abort(_("please specify just one revision"))
5893 5895
5894 5896 if rev is None or rev == '':
5895 5897 rev = node
5896 5898
5897 5899 # with no argument, we also move the current bookmark, if any
5898 5900 movemarkfrom = None
5899 5901 if rev is None:
5900 5902 movemarkfrom = repo['.'].node()
5901 5903
5902 5904 # if we defined a bookmark, we have to remember the original bookmark name
5903 5905 brev = rev
5904 5906 rev = scmutil.revsingle(repo, rev, rev).rev()
5905 5907
5906 5908 if check and clean:
5907 5909 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5908 5910
5909 5911 if date:
5910 5912 if rev is not None:
5911 5913 raise util.Abort(_("you can't specify a revision and a date"))
5912 5914 rev = cmdutil.finddate(ui, repo, date)
5913 5915
5914 5916 if check:
5915 5917 c = repo[None]
5916 5918 if c.dirty(merge=False, branch=False, missing=True):
5917 5919 raise util.Abort(_("uncommitted local changes"))
5918 5920 if rev is None:
5919 5921 rev = repo[repo[None].branch()].rev()
5920 5922 mergemod._checkunknown(repo, repo[None], repo[rev])
5921 5923
5922 5924 if clean:
5923 5925 ret = hg.clean(repo, rev)
5924 5926 else:
5925 5927 ret = hg.update(repo, rev)
5926 5928
5927 5929 if not ret and movemarkfrom:
5928 5930 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5929 5931 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5930 5932 elif brev in repo._bookmarks:
5931 5933 bookmarks.setcurrent(repo, brev)
5932 5934 elif brev:
5933 5935 bookmarks.unsetcurrent(repo)
5934 5936
5935 5937 return ret
5936 5938
5937 5939 @command('verify', [])
5938 5940 def verify(ui, repo):
5939 5941 """verify the integrity of the repository
5940 5942
5941 5943 Verify the integrity of the current repository.
5942 5944
5943 5945 This will perform an extensive check of the repository's
5944 5946 integrity, validating the hashes and checksums of each entry in
5945 5947 the changelog, manifest, and tracked files, as well as the
5946 5948 integrity of their crosslinks and indices.
5947 5949
5948 5950 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5949 5951 for more information about recovery from corruption of the
5950 5952 repository.
5951 5953
5952 5954 Returns 0 on success, 1 if errors are encountered.
5953 5955 """
5954 5956 return hg.verify(repo)
5955 5957
5956 5958 @command('version', [])
5957 5959 def version_(ui):
5958 5960 """output version and copyright information"""
5959 5961 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5960 5962 % util.version())
5961 5963 ui.status(_(
5962 5964 "(see http://mercurial.selenic.com for more information)\n"
5963 5965 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5964 5966 "This is free software; see the source for copying conditions. "
5965 5967 "There is NO\nwarranty; "
5966 5968 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5967 5969 ))
5968 5970
5969 5971 norepo = ("clone init version help debugcommands debugcomplete"
5970 5972 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5971 5973 " debugknown debuggetbundle debugbundle")
5972 5974 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5973 5975 " debugdata debugindex debugindexdot debugrevlog")
5974 5976 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5975 5977 " remove resolve status debugwalk")
@@ -1,46 +1,46
1 1 # Extension dedicated to test patch.diff() upgrade modes
2 2 #
3 3 #
4 4 from mercurial import scmutil, patch, util
5 5
6 6 def autodiff(ui, repo, *pats, **opts):
7 7 diffopts = patch.diffopts(ui, opts)
8 8 git = opts.get('git', 'no')
9 9 brokenfiles = set()
10 10 losedatafn = None
11 11 if git in ('yes', 'no'):
12 12 diffopts.git = git == 'yes'
13 13 diffopts.upgrade = False
14 14 elif git == 'auto':
15 15 diffopts.git = False
16 16 diffopts.upgrade = True
17 17 elif git == 'warn':
18 18 diffopts.git = False
19 19 diffopts.upgrade = True
20 20 def losedatafn(fn=None, **kwargs):
21 21 brokenfiles.add(fn)
22 22 return True
23 23 elif git == 'abort':
24 24 diffopts.git = False
25 25 diffopts.upgrade = True
26 26 def losedatafn(fn=None, **kwargs):
27 27 raise util.Abort('losing data for %s' % fn)
28 28 else:
29 29 raise util.Abort('--git must be yes, no or auto')
30 30
31 31 node1, node2 = scmutil.revpair(repo, [])
32 32 m = scmutil.match(repo[node2], pats, opts)
33 33 it = patch.diff(repo, node1, node2, match=m, opts=diffopts,
34 34 losedatafn=losedatafn)
35 35 for chunk in it:
36 36 ui.write(chunk)
37 37 for fn in sorted(brokenfiles):
38 ui.write('data lost for: %s\n' % fn)
38 ui.write(('data lost for: %s\n' % fn))
39 39
40 40 cmdtable = {
41 41 "autodiff":
42 42 (autodiff,
43 43 [('', 'git', '', 'git upgrade mode (yes/no/auto/warn/abort)'),
44 44 ],
45 45 '[OPTION]... [FILE]...'),
46 46 }
@@ -1,167 +1,10
1 1 $ check_code="$TESTDIR"/../contrib/check-code.py
2 2 $ cd "$TESTDIR"/..
3 3 $ if hg identify -q > /dev/null; then :
4 4 > else
5 5 > echo "skipped: not a Mercurial working dir" >&2
6 6 > exit 80
7 7 > fi
8 8 $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
9 9
10 10 $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
11 hgext/convert/cvsps.py:0:
12 > ui.write('Ancestors: %s\n' % (','.join(r)))
13 warning: unwrapped ui message
14 hgext/convert/cvsps.py:0:
15 > ui.write('Parent: %d\n' % cs.parents[0].id)
16 warning: unwrapped ui message
17 hgext/convert/cvsps.py:0:
18 > ui.write('Parents: %s\n' %
19 warning: unwrapped ui message
20 hgext/convert/cvsps.py:0:
21 > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
22 warning: unwrapped ui message
23 hgext/convert/cvsps.py:0:
24 > ui.write('Author: %s\n' % cs.author)
25 warning: unwrapped ui message
26 hgext/convert/cvsps.py:0:
27 > ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
28 warning: unwrapped ui message
29 hgext/convert/cvsps.py:0:
30 > ui.write('Date: %s\n' % util.datestr(cs.date,
31 warning: unwrapped ui message
32 hgext/convert/cvsps.py:0:
33 > ui.write('Log:\n')
34 warning: unwrapped ui message
35 hgext/convert/cvsps.py:0:
36 > ui.write('Members: \n')
37 warning: unwrapped ui message
38 hgext/convert/cvsps.py:0:
39 > ui.write('PatchSet %d \n' % cs.id)
40 warning: unwrapped ui message
41 hgext/convert/cvsps.py:0:
42 > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
43 warning: unwrapped ui message
44 hgext/hgk.py:0:
45 > ui.write("parent %s\n" % p)
46 warning: unwrapped ui message
47 hgext/hgk.py:0:
48 > ui.write('k=%s\nv=%s\n' % (name, value))
49 warning: unwrapped ui message
50 hgext/hgk.py:0:
51 > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
52 warning: unwrapped ui message
53 hgext/hgk.py:0:
54 > ui.write("branch %s\n\n" % ctx.branch())
55 warning: unwrapped ui message
56 hgext/hgk.py:0:
57 > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
58 warning: unwrapped ui message
59 hgext/hgk.py:0:
60 > ui.write("revision %d\n" % ctx.rev())
61 warning: unwrapped ui message
62 hgext/hgk.py:0:
63 > ui.write("tree %s\n" % short(ctx.changeset()[0]))
64 warning: unwrapped ui message
65 hgext/patchbomb.py:0:
66 > ui.write('Subject: %s\n' % subj)
67 warning: unwrapped ui message
68 hgext/patchbomb.py:0:
69 > ui.write('From: %s\n' % sender)
70 warning: unwrapped ui message
71 mercurial/commands.py:0:
72 > ui.note('branch %s\n' % data)
73 warning: unwrapped ui message
74 mercurial/commands.py:0:
75 > ui.note('node %s\n' % str(data))
76 warning: unwrapped ui message
77 mercurial/commands.py:0:
78 > ui.note('tag %s\n' % name)
79 warning: unwrapped ui message
80 mercurial/commands.py:0:
81 > ui.write("unpruned common: %s\n" % " ".join([short(n)
82 warning: unwrapped ui message
83 mercurial/commands.py:0:
84 > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
85 warning: unwrapped ui message
86 mercurial/commands.py:0:
87 > ui.write("local is subset\n")
88 warning: unwrapped ui message
89 mercurial/commands.py:0:
90 > ui.write("remote is subset\n")
91 warning: unwrapped ui message
92 mercurial/commands.py:0:
93 > ui.write('deltas against other : ' + fmt % pcfmt(numother,
94 warning: unwrapped ui message
95 mercurial/commands.py:0:
96 > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
97 warning: unwrapped ui message
98 mercurial/commands.py:0:
99 > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
100 warning: unwrapped ui message
101 mercurial/commands.py:0:
102 > ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
103 warning: unwrapped ui message
104 mercurial/commands.py:0:
105 > ui.write("match: %s\n" % m(d[0]))
106 warning: unwrapped ui message
107 mercurial/commands.py:0:
108 > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
109 warning: unwrapped ui message
110 mercurial/commands.py:0:
111 > ui.write('path %s\n' % k)
112 warning: unwrapped ui message
113 mercurial/commands.py:0:
114 > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
115 warning: unwrapped ui message
116 mercurial/commands.py:0:
117 > ui.write("digraph G {\n")
118 warning: unwrapped ui message
119 mercurial/commands.py:0:
120 > ui.write("internal: %s %s\n" % d)
121 warning: unwrapped ui message
122 mercurial/commands.py:0:
123 > ui.write("standard: %s\n" % util.datestr(d))
124 warning: unwrapped ui message
125 mercurial/commands.py:0:
126 > ui.write('avg chain length : ' + fmt % avgchainlen)
127 warning: unwrapped ui message
128 mercurial/commands.py:0:
129 > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
130 warning: unwrapped ui message
131 mercurial/commands.py:0:
132 > ui.write('compression ratio : ' + fmt % compratio)
133 warning: unwrapped ui message
134 mercurial/commands.py:0:
135 > ui.write('delta size (min/max/avg) : %d / %d / %d\n'
136 warning: unwrapped ui message
137 mercurial/commands.py:0:
138 > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
139 warning: unwrapped ui message
140 mercurial/commands.py:0:
141 > ui.write('flags : %s\n' % ', '.join(flags))
142 warning: unwrapped ui message
143 mercurial/commands.py:0:
144 > ui.write('format : %d\n' % format)
145 warning: unwrapped ui message
146 mercurial/commands.py:0:
147 > ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
148 warning: unwrapped ui message
149 mercurial/commands.py:0:
150 > ui.write('revision size : ' + fmt2 % totalsize)
151 warning: unwrapped ui message
152 mercurial/commands.py:0:
153 > ui.write('revisions : ' + fmt2 % numrevs)
154 warning: unwrapped ui message
155 warning: unwrapped ui message
156 mercurial/commands.py:0:
157 > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
158 warning: unwrapped ui message
159 tests/autodiff.py:0:
160 > ui.write('data lost for: %s\n' % fn)
161 warning: unwrapped ui message
162 tests/test-ui-color.py:0:
163 > testui.warn('warning\n')
164 warning: unwrapped ui message
165 tests/test-ui-color.py:0:
166 > testui.write('buffered\n')
167 warning: unwrapped ui message
@@ -1,33 +1,33
1 1 import os, sys
2 2 from hgext import color
3 3 from mercurial import dispatch, ui
4 4
5 5 # ensure errors aren't buffered
6 6 testui = color.colorui()
7 7 testui.pushbuffer()
8 testui.write('buffered\n')
9 testui.warn('warning\n')
8 testui.write(('buffered\n'))
9 testui.warn(('warning\n'))
10 10 testui.write_err('error\n')
11 11 print repr(testui.popbuffer())
12 12
13 13 # test dispatch.dispatch with the same ui object
14 14 hgrc = open(os.environ["HGRCPATH"], 'w')
15 15 hgrc.write('[extensions]\n')
16 16 hgrc.write('color=\n')
17 17 hgrc.close()
18 18
19 19 ui_ = ui.ui()
20 20 ui_.setconfig('ui', 'formatted', 'True')
21 21
22 22 # we're not interested in the output, so write that to devnull
23 23 ui_.fout = open(os.devnull, 'w')
24 24
25 25 # call some arbitrary command just so we go through
26 26 # color's wrapped _runcommand twice.
27 27 def runcmd():
28 28 dispatch.dispatch(dispatch.request(['version', '-q'], ui_))
29 29
30 30 runcmd()
31 31 print "colored? " + str(issubclass(ui_.__class__, color.colorui))
32 32 runcmd()
33 33 print "colored? " + str(issubclass(ui_.__class__, color.colorui))
General Comments 0
You need to be logged in to leave comments. Login now