##// END OF EJS Templates
rename util.unlink to unlinkpath
Adrian Buehlmann -
r13235:6bf39d88 default
parent child Browse files
Show More
@@ -1,3253 +1,3253 b''
1 1 # mq.py - patch queues for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''manage a stack of patches
9 9
10 10 This extension lets you work with a stack of patches in a Mercurial
11 11 repository. It manages two stacks of patches - all known patches, and
12 12 applied patches (subset of known patches).
13 13
14 14 Known patches are represented as patch files in the .hg/patches
15 15 directory. Applied patches are both patch files and changesets.
16 16
17 17 Common tasks (use :hg:`help command` for more details)::
18 18
19 19 create new patch qnew
20 20 import existing patch qimport
21 21
22 22 print patch series qseries
23 23 print applied patches qapplied
24 24
25 25 add known patch to applied stack qpush
26 26 remove patch from applied stack qpop
27 27 refresh contents of top applied patch qrefresh
28 28
29 29 By default, mq will automatically use git patches when required to
30 30 avoid losing file mode changes, copy records, binary files or empty
31 31 files creations or deletions. This behaviour can be configured with::
32 32
33 33 [mq]
34 34 git = auto/keep/yes/no
35 35
36 36 If set to 'keep', mq will obey the [diff] section configuration while
37 37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 38 'no', mq will override the [diff] section and always generate git or
39 39 regular patches, possibly losing data in the second case.
40 40
41 41 You will by default be managing a patch queue named "patches". You can
42 42 create other, independent patch queues with the :hg:`qqueue` command.
43 43 '''
44 44
45 45 from mercurial.i18n import _
46 46 from mercurial.node import bin, hex, short, nullid, nullrev
47 47 from mercurial.lock import release
48 48 from mercurial import commands, cmdutil, hg, patch, util
49 49 from mercurial import repair, extensions, url, error
50 50 import os, sys, re, errno, shutil
51 51
52 52 commands.norepo += " qclone"
53 53
54 54 # Patch names looks like unix-file names.
55 55 # They must be joinable with queue directory and result in the patch path.
56 56 normname = util.normpath
57 57
58 58 class statusentry(object):
59 59 def __init__(self, node, name):
60 60 self.node, self.name = node, name
61 61 def __repr__(self):
62 62 return hex(self.node) + ':' + self.name
63 63
64 64 class patchheader(object):
65 65 def __init__(self, pf, plainmode=False):
66 66 def eatdiff(lines):
67 67 while lines:
68 68 l = lines[-1]
69 69 if (l.startswith("diff -") or
70 70 l.startswith("Index:") or
71 71 l.startswith("===========")):
72 72 del lines[-1]
73 73 else:
74 74 break
75 75 def eatempty(lines):
76 76 while lines:
77 77 if not lines[-1].strip():
78 78 del lines[-1]
79 79 else:
80 80 break
81 81
82 82 message = []
83 83 comments = []
84 84 user = None
85 85 date = None
86 86 parent = None
87 87 format = None
88 88 subject = None
89 89 branch = None
90 90 nodeid = None
91 91 diffstart = 0
92 92
93 93 for line in file(pf):
94 94 line = line.rstrip()
95 95 if (line.startswith('diff --git')
96 96 or (diffstart and line.startswith('+++ '))):
97 97 diffstart = 2
98 98 break
99 99 diffstart = 0 # reset
100 100 if line.startswith("--- "):
101 101 diffstart = 1
102 102 continue
103 103 elif format == "hgpatch":
104 104 # parse values when importing the result of an hg export
105 105 if line.startswith("# User "):
106 106 user = line[7:]
107 107 elif line.startswith("# Date "):
108 108 date = line[7:]
109 109 elif line.startswith("# Parent "):
110 110 parent = line[9:]
111 111 elif line.startswith("# Branch "):
112 112 branch = line[9:]
113 113 elif line.startswith("# Node ID "):
114 114 nodeid = line[10:]
115 115 elif not line.startswith("# ") and line:
116 116 message.append(line)
117 117 format = None
118 118 elif line == '# HG changeset patch':
119 119 message = []
120 120 format = "hgpatch"
121 121 elif (format != "tagdone" and (line.startswith("Subject: ") or
122 122 line.startswith("subject: "))):
123 123 subject = line[9:]
124 124 format = "tag"
125 125 elif (format != "tagdone" and (line.startswith("From: ") or
126 126 line.startswith("from: "))):
127 127 user = line[6:]
128 128 format = "tag"
129 129 elif (format != "tagdone" and (line.startswith("Date: ") or
130 130 line.startswith("date: "))):
131 131 date = line[6:]
132 132 format = "tag"
133 133 elif format == "tag" and line == "":
134 134 # when looking for tags (subject: from: etc) they
135 135 # end once you find a blank line in the source
136 136 format = "tagdone"
137 137 elif message or line:
138 138 message.append(line)
139 139 comments.append(line)
140 140
141 141 eatdiff(message)
142 142 eatdiff(comments)
143 143 # Remember the exact starting line of the patch diffs before consuming
144 144 # empty lines, for external use by TortoiseHg and others
145 145 self.diffstartline = len(comments)
146 146 eatempty(message)
147 147 eatempty(comments)
148 148
149 149 # make sure message isn't empty
150 150 if format and format.startswith("tag") and subject:
151 151 message.insert(0, "")
152 152 message.insert(0, subject)
153 153
154 154 self.message = message
155 155 self.comments = comments
156 156 self.user = user
157 157 self.date = date
158 158 self.parent = parent
159 159 # nodeid and branch are for external use by TortoiseHg and others
160 160 self.nodeid = nodeid
161 161 self.branch = branch
162 162 self.haspatch = diffstart > 1
163 163 self.plainmode = plainmode
164 164
165 165 def setuser(self, user):
166 166 if not self.updateheader(['From: ', '# User '], user):
167 167 try:
168 168 patchheaderat = self.comments.index('# HG changeset patch')
169 169 self.comments.insert(patchheaderat + 1, '# User ' + user)
170 170 except ValueError:
171 171 if self.plainmode or self._hasheader(['Date: ']):
172 172 self.comments = ['From: ' + user] + self.comments
173 173 else:
174 174 tmp = ['# HG changeset patch', '# User ' + user, '']
175 175 self.comments = tmp + self.comments
176 176 self.user = user
177 177
178 178 def setdate(self, date):
179 179 if not self.updateheader(['Date: ', '# Date '], date):
180 180 try:
181 181 patchheaderat = self.comments.index('# HG changeset patch')
182 182 self.comments.insert(patchheaderat + 1, '# Date ' + date)
183 183 except ValueError:
184 184 if self.plainmode or self._hasheader(['From: ']):
185 185 self.comments = ['Date: ' + date] + self.comments
186 186 else:
187 187 tmp = ['# HG changeset patch', '# Date ' + date, '']
188 188 self.comments = tmp + self.comments
189 189 self.date = date
190 190
191 191 def setparent(self, parent):
192 192 if not self.updateheader(['# Parent '], parent):
193 193 try:
194 194 patchheaderat = self.comments.index('# HG changeset patch')
195 195 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
196 196 except ValueError:
197 197 pass
198 198 self.parent = parent
199 199
200 200 def setmessage(self, message):
201 201 if self.comments:
202 202 self._delmsg()
203 203 self.message = [message]
204 204 self.comments += self.message
205 205
206 206 def updateheader(self, prefixes, new):
207 207 '''Update all references to a field in the patch header.
208 208 Return whether the field is present.'''
209 209 res = False
210 210 for prefix in prefixes:
211 211 for i in xrange(len(self.comments)):
212 212 if self.comments[i].startswith(prefix):
213 213 self.comments[i] = prefix + new
214 214 res = True
215 215 break
216 216 return res
217 217
218 218 def _hasheader(self, prefixes):
219 219 '''Check if a header starts with any of the given prefixes.'''
220 220 for prefix in prefixes:
221 221 for comment in self.comments:
222 222 if comment.startswith(prefix):
223 223 return True
224 224 return False
225 225
226 226 def __str__(self):
227 227 if not self.comments:
228 228 return ''
229 229 return '\n'.join(self.comments) + '\n\n'
230 230
231 231 def _delmsg(self):
232 232 '''Remove existing message, keeping the rest of the comments fields.
233 233 If comments contains 'subject: ', message will prepend
234 234 the field and a blank line.'''
235 235 if self.message:
236 236 subj = 'subject: ' + self.message[0].lower()
237 237 for i in xrange(len(self.comments)):
238 238 if subj == self.comments[i].lower():
239 239 del self.comments[i]
240 240 self.message = self.message[2:]
241 241 break
242 242 ci = 0
243 243 for mi in self.message:
244 244 while mi != self.comments[ci]:
245 245 ci += 1
246 246 del self.comments[ci]
247 247
248 248 class queue(object):
249 249 def __init__(self, ui, path, patchdir=None):
250 250 self.basepath = path
251 251 try:
252 252 fh = open(os.path.join(path, 'patches.queue'))
253 253 cur = fh.read().rstrip()
254 254 if not cur:
255 255 curpath = os.path.join(path, 'patches')
256 256 else:
257 257 curpath = os.path.join(path, 'patches-' + cur)
258 258 except IOError:
259 259 curpath = os.path.join(path, 'patches')
260 260 self.path = patchdir or curpath
261 261 self.opener = util.opener(self.path)
262 262 self.ui = ui
263 263 self.applied_dirty = 0
264 264 self.series_dirty = 0
265 265 self.added = []
266 266 self.series_path = "series"
267 267 self.status_path = "status"
268 268 self.guards_path = "guards"
269 269 self.active_guards = None
270 270 self.guards_dirty = False
271 271 # Handle mq.git as a bool with extended values
272 272 try:
273 273 gitmode = ui.configbool('mq', 'git', None)
274 274 if gitmode is None:
275 275 raise error.ConfigError()
276 276 self.gitmode = gitmode and 'yes' or 'no'
277 277 except error.ConfigError:
278 278 self.gitmode = ui.config('mq', 'git', 'auto').lower()
279 279 self.plainmode = ui.configbool('mq', 'plain', False)
280 280
281 281 @util.propertycache
282 282 def applied(self):
283 283 if os.path.exists(self.join(self.status_path)):
284 284 def parse(l):
285 285 n, name = l.split(':', 1)
286 286 return statusentry(bin(n), name)
287 287 lines = self.opener(self.status_path).read().splitlines()
288 288 return [parse(l) for l in lines]
289 289 return []
290 290
291 291 @util.propertycache
292 292 def full_series(self):
293 293 if os.path.exists(self.join(self.series_path)):
294 294 return self.opener(self.series_path).read().splitlines()
295 295 return []
296 296
297 297 @util.propertycache
298 298 def series(self):
299 299 self.parse_series()
300 300 return self.series
301 301
302 302 @util.propertycache
303 303 def series_guards(self):
304 304 self.parse_series()
305 305 return self.series_guards
306 306
307 307 def invalidate(self):
308 308 for a in 'applied full_series series series_guards'.split():
309 309 if a in self.__dict__:
310 310 delattr(self, a)
311 311 self.applied_dirty = 0
312 312 self.series_dirty = 0
313 313 self.guards_dirty = False
314 314 self.active_guards = None
315 315
316 316 def diffopts(self, opts={}, patchfn=None):
317 317 diffopts = patch.diffopts(self.ui, opts)
318 318 if self.gitmode == 'auto':
319 319 diffopts.upgrade = True
320 320 elif self.gitmode == 'keep':
321 321 pass
322 322 elif self.gitmode in ('yes', 'no'):
323 323 diffopts.git = self.gitmode == 'yes'
324 324 else:
325 325 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
326 326 ' got %s') % self.gitmode)
327 327 if patchfn:
328 328 diffopts = self.patchopts(diffopts, patchfn)
329 329 return diffopts
330 330
331 331 def patchopts(self, diffopts, *patches):
332 332 """Return a copy of input diff options with git set to true if
333 333 referenced patch is a git patch and should be preserved as such.
334 334 """
335 335 diffopts = diffopts.copy()
336 336 if not diffopts.git and self.gitmode == 'keep':
337 337 for patchfn in patches:
338 338 patchf = self.opener(patchfn, 'r')
339 339 # if the patch was a git patch, refresh it as a git patch
340 340 for line in patchf:
341 341 if line.startswith('diff --git'):
342 342 diffopts.git = True
343 343 break
344 344 patchf.close()
345 345 return diffopts
346 346
347 347 def join(self, *p):
348 348 return os.path.join(self.path, *p)
349 349
350 350 def find_series(self, patch):
351 351 def matchpatch(l):
352 352 l = l.split('#', 1)[0]
353 353 return l.strip() == patch
354 354 for index, l in enumerate(self.full_series):
355 355 if matchpatch(l):
356 356 return index
357 357 return None
358 358
359 359 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
360 360
361 361 def parse_series(self):
362 362 self.series = []
363 363 self.series_guards = []
364 364 for l in self.full_series:
365 365 h = l.find('#')
366 366 if h == -1:
367 367 patch = l
368 368 comment = ''
369 369 elif h == 0:
370 370 continue
371 371 else:
372 372 patch = l[:h]
373 373 comment = l[h:]
374 374 patch = patch.strip()
375 375 if patch:
376 376 if patch in self.series:
377 377 raise util.Abort(_('%s appears more than once in %s') %
378 378 (patch, self.join(self.series_path)))
379 379 self.series.append(patch)
380 380 self.series_guards.append(self.guard_re.findall(comment))
381 381
382 382 def check_guard(self, guard):
383 383 if not guard:
384 384 return _('guard cannot be an empty string')
385 385 bad_chars = '# \t\r\n\f'
386 386 first = guard[0]
387 387 if first in '-+':
388 388 return (_('guard %r starts with invalid character: %r') %
389 389 (guard, first))
390 390 for c in bad_chars:
391 391 if c in guard:
392 392 return _('invalid character in guard %r: %r') % (guard, c)
393 393
394 394 def set_active(self, guards):
395 395 for guard in guards:
396 396 bad = self.check_guard(guard)
397 397 if bad:
398 398 raise util.Abort(bad)
399 399 guards = sorted(set(guards))
400 400 self.ui.debug('active guards: %s\n' % ' '.join(guards))
401 401 self.active_guards = guards
402 402 self.guards_dirty = True
403 403
404 404 def active(self):
405 405 if self.active_guards is None:
406 406 self.active_guards = []
407 407 try:
408 408 guards = self.opener(self.guards_path).read().split()
409 409 except IOError, err:
410 410 if err.errno != errno.ENOENT:
411 411 raise
412 412 guards = []
413 413 for i, guard in enumerate(guards):
414 414 bad = self.check_guard(guard)
415 415 if bad:
416 416 self.ui.warn('%s:%d: %s\n' %
417 417 (self.join(self.guards_path), i + 1, bad))
418 418 else:
419 419 self.active_guards.append(guard)
420 420 return self.active_guards
421 421
422 422 def set_guards(self, idx, guards):
423 423 for g in guards:
424 424 if len(g) < 2:
425 425 raise util.Abort(_('guard %r too short') % g)
426 426 if g[0] not in '-+':
427 427 raise util.Abort(_('guard %r starts with invalid char') % g)
428 428 bad = self.check_guard(g[1:])
429 429 if bad:
430 430 raise util.Abort(bad)
431 431 drop = self.guard_re.sub('', self.full_series[idx])
432 432 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
433 433 self.parse_series()
434 434 self.series_dirty = True
435 435
436 436 def pushable(self, idx):
437 437 if isinstance(idx, str):
438 438 idx = self.series.index(idx)
439 439 patchguards = self.series_guards[idx]
440 440 if not patchguards:
441 441 return True, None
442 442 guards = self.active()
443 443 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
444 444 if exactneg:
445 445 return False, exactneg[0]
446 446 pos = [g for g in patchguards if g[0] == '+']
447 447 exactpos = [g for g in pos if g[1:] in guards]
448 448 if pos:
449 449 if exactpos:
450 450 return True, exactpos[0]
451 451 return False, pos
452 452 return True, ''
453 453
454 454 def explain_pushable(self, idx, all_patches=False):
455 455 write = all_patches and self.ui.write or self.ui.warn
456 456 if all_patches or self.ui.verbose:
457 457 if isinstance(idx, str):
458 458 idx = self.series.index(idx)
459 459 pushable, why = self.pushable(idx)
460 460 if all_patches and pushable:
461 461 if why is None:
462 462 write(_('allowing %s - no guards in effect\n') %
463 463 self.series[idx])
464 464 else:
465 465 if not why:
466 466 write(_('allowing %s - no matching negative guards\n') %
467 467 self.series[idx])
468 468 else:
469 469 write(_('allowing %s - guarded by %r\n') %
470 470 (self.series[idx], why))
471 471 if not pushable:
472 472 if why:
473 473 write(_('skipping %s - guarded by %r\n') %
474 474 (self.series[idx], why))
475 475 else:
476 476 write(_('skipping %s - no matching guards\n') %
477 477 self.series[idx])
478 478
479 479 def save_dirty(self):
480 480 def write_list(items, path):
481 481 fp = self.opener(path, 'w')
482 482 for i in items:
483 483 fp.write("%s\n" % i)
484 484 fp.close()
485 485 if self.applied_dirty:
486 486 write_list(map(str, self.applied), self.status_path)
487 487 if self.series_dirty:
488 488 write_list(self.full_series, self.series_path)
489 489 if self.guards_dirty:
490 490 write_list(self.active_guards, self.guards_path)
491 491 if self.added:
492 492 qrepo = self.qrepo()
493 493 if qrepo:
494 494 qrepo[None].add(f for f in self.added if f not in qrepo[None])
495 495 self.added = []
496 496
497 497 def removeundo(self, repo):
498 498 undo = repo.sjoin('undo')
499 499 if not os.path.exists(undo):
500 500 return
501 501 try:
502 502 os.unlink(undo)
503 503 except OSError, inst:
504 504 self.ui.warn(_('error removing undo: %s\n') % str(inst))
505 505
506 506 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
507 507 fp=None, changes=None, opts={}):
508 508 stat = opts.get('stat')
509 509 m = cmdutil.match(repo, files, opts)
510 510 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
511 511 changes, stat, fp)
512 512
513 513 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
514 514 # first try just applying the patch
515 515 (err, n) = self.apply(repo, [patch], update_status=False,
516 516 strict=True, merge=rev)
517 517
518 518 if err == 0:
519 519 return (err, n)
520 520
521 521 if n is None:
522 522 raise util.Abort(_("apply failed for patch %s") % patch)
523 523
524 524 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
525 525
526 526 # apply failed, strip away that rev and merge.
527 527 hg.clean(repo, head)
528 528 self.strip(repo, [n], update=False, backup='strip')
529 529
530 530 ctx = repo[rev]
531 531 ret = hg.merge(repo, rev)
532 532 if ret:
533 533 raise util.Abort(_("update returned %d") % ret)
534 534 n = repo.commit(ctx.description(), ctx.user(), force=True)
535 535 if n is None:
536 536 raise util.Abort(_("repo commit failed"))
537 537 try:
538 538 ph = patchheader(mergeq.join(patch), self.plainmode)
539 539 except:
540 540 raise util.Abort(_("unable to read %s") % patch)
541 541
542 542 diffopts = self.patchopts(diffopts, patch)
543 543 patchf = self.opener(patch, "w")
544 544 comments = str(ph)
545 545 if comments:
546 546 patchf.write(comments)
547 547 self.printdiff(repo, diffopts, head, n, fp=patchf)
548 548 patchf.close()
549 549 self.removeundo(repo)
550 550 return (0, n)
551 551
552 552 def qparents(self, repo, rev=None):
553 553 if rev is None:
554 554 (p1, p2) = repo.dirstate.parents()
555 555 if p2 == nullid:
556 556 return p1
557 557 if not self.applied:
558 558 return None
559 559 return self.applied[-1].node
560 560 p1, p2 = repo.changelog.parents(rev)
561 561 if p2 != nullid and p2 in [x.node for x in self.applied]:
562 562 return p2
563 563 return p1
564 564
565 565 def mergepatch(self, repo, mergeq, series, diffopts):
566 566 if not self.applied:
567 567 # each of the patches merged in will have two parents. This
568 568 # can confuse the qrefresh, qdiff, and strip code because it
569 569 # needs to know which parent is actually in the patch queue.
570 570 # so, we insert a merge marker with only one parent. This way
571 571 # the first patch in the queue is never a merge patch
572 572 #
573 573 pname = ".hg.patches.merge.marker"
574 574 n = repo.commit('[mq]: merge marker', force=True)
575 575 self.removeundo(repo)
576 576 self.applied.append(statusentry(n, pname))
577 577 self.applied_dirty = 1
578 578
579 579 head = self.qparents(repo)
580 580
581 581 for patch in series:
582 582 patch = mergeq.lookup(patch, strict=True)
583 583 if not patch:
584 584 self.ui.warn(_("patch %s does not exist\n") % patch)
585 585 return (1, None)
586 586 pushable, reason = self.pushable(patch)
587 587 if not pushable:
588 588 self.explain_pushable(patch, all_patches=True)
589 589 continue
590 590 info = mergeq.isapplied(patch)
591 591 if not info:
592 592 self.ui.warn(_("patch %s is not applied\n") % patch)
593 593 return (1, None)
594 594 rev = info[1]
595 595 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
596 596 if head:
597 597 self.applied.append(statusentry(head, patch))
598 598 self.applied_dirty = 1
599 599 if err:
600 600 return (err, head)
601 601 self.save_dirty()
602 602 return (0, head)
603 603
604 604 def patch(self, repo, patchfile):
605 605 '''Apply patchfile to the working directory.
606 606 patchfile: name of patch file'''
607 607 files = {}
608 608 try:
609 609 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
610 610 files=files, eolmode=None)
611 611 except Exception, inst:
612 612 self.ui.note(str(inst) + '\n')
613 613 if not self.ui.verbose:
614 614 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
615 615 return (False, files, False)
616 616
617 617 return (True, files, fuzz)
618 618
619 619 def apply(self, repo, series, list=False, update_status=True,
620 620 strict=False, patchdir=None, merge=None, all_files=None):
621 621 wlock = lock = tr = None
622 622 try:
623 623 wlock = repo.wlock()
624 624 lock = repo.lock()
625 625 tr = repo.transaction("qpush")
626 626 try:
627 627 ret = self._apply(repo, series, list, update_status,
628 628 strict, patchdir, merge, all_files=all_files)
629 629 tr.close()
630 630 self.save_dirty()
631 631 return ret
632 632 except:
633 633 try:
634 634 tr.abort()
635 635 finally:
636 636 repo.invalidate()
637 637 repo.dirstate.invalidate()
638 638 raise
639 639 finally:
640 640 release(tr, lock, wlock)
641 641 self.removeundo(repo)
642 642
643 643 def _apply(self, repo, series, list=False, update_status=True,
644 644 strict=False, patchdir=None, merge=None, all_files=None):
645 645 '''returns (error, hash)
646 646 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
647 647 # TODO unify with commands.py
648 648 if not patchdir:
649 649 patchdir = self.path
650 650 err = 0
651 651 n = None
652 652 for patchname in series:
653 653 pushable, reason = self.pushable(patchname)
654 654 if not pushable:
655 655 self.explain_pushable(patchname, all_patches=True)
656 656 continue
657 657 self.ui.status(_("applying %s\n") % patchname)
658 658 pf = os.path.join(patchdir, patchname)
659 659
660 660 try:
661 661 ph = patchheader(self.join(patchname), self.plainmode)
662 662 except:
663 663 self.ui.warn(_("unable to read %s\n") % patchname)
664 664 err = 1
665 665 break
666 666
667 667 message = ph.message
668 668 if not message:
669 669 # The commit message should not be translated
670 670 message = "imported patch %s\n" % patchname
671 671 else:
672 672 if list:
673 673 # The commit message should not be translated
674 674 message.append("\nimported patch %s" % patchname)
675 675 message = '\n'.join(message)
676 676
677 677 if ph.haspatch:
678 678 (patcherr, files, fuzz) = self.patch(repo, pf)
679 679 if all_files is not None:
680 680 all_files.update(files)
681 681 patcherr = not patcherr
682 682 else:
683 683 self.ui.warn(_("patch %s is empty\n") % patchname)
684 684 patcherr, files, fuzz = 0, [], 0
685 685
686 686 if merge and files:
687 687 # Mark as removed/merged and update dirstate parent info
688 688 removed = []
689 689 merged = []
690 690 for f in files:
691 691 if os.path.lexists(repo.wjoin(f)):
692 692 merged.append(f)
693 693 else:
694 694 removed.append(f)
695 695 for f in removed:
696 696 repo.dirstate.remove(f)
697 697 for f in merged:
698 698 repo.dirstate.merge(f)
699 699 p1, p2 = repo.dirstate.parents()
700 700 repo.dirstate.setparents(p1, merge)
701 701
702 702 files = cmdutil.updatedir(self.ui, repo, files)
703 703 match = cmdutil.matchfiles(repo, files or [])
704 704 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
705 705
706 706 if n is None:
707 707 raise util.Abort(_("repository commit failed"))
708 708
709 709 if update_status:
710 710 self.applied.append(statusentry(n, patchname))
711 711
712 712 if patcherr:
713 713 self.ui.warn(_("patch failed, rejects left in working dir\n"))
714 714 err = 2
715 715 break
716 716
717 717 if fuzz and strict:
718 718 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
719 719 err = 3
720 720 break
721 721 return (err, n)
722 722
723 723 def _cleanup(self, patches, numrevs, keep=False):
724 724 if not keep:
725 725 r = self.qrepo()
726 726 if r:
727 727 r[None].remove(patches, True)
728 728 else:
729 729 for p in patches:
730 730 os.unlink(self.join(p))
731 731
732 732 if numrevs:
733 733 del self.applied[:numrevs]
734 734 self.applied_dirty = 1
735 735
736 736 for i in sorted([self.find_series(p) for p in patches], reverse=True):
737 737 del self.full_series[i]
738 738 self.parse_series()
739 739 self.series_dirty = 1
740 740
741 741 def _revpatches(self, repo, revs):
742 742 firstrev = repo[self.applied[0].node].rev()
743 743 patches = []
744 744 for i, rev in enumerate(revs):
745 745
746 746 if rev < firstrev:
747 747 raise util.Abort(_('revision %d is not managed') % rev)
748 748
749 749 ctx = repo[rev]
750 750 base = self.applied[i].node
751 751 if ctx.node() != base:
752 752 msg = _('cannot delete revision %d above applied patches')
753 753 raise util.Abort(msg % rev)
754 754
755 755 patch = self.applied[i].name
756 756 for fmt in ('[mq]: %s', 'imported patch %s'):
757 757 if ctx.description() == fmt % patch:
758 758 msg = _('patch %s finalized without changeset message\n')
759 759 repo.ui.status(msg % patch)
760 760 break
761 761
762 762 patches.append(patch)
763 763 return patches
764 764
765 765 def finish(self, repo, revs):
766 766 patches = self._revpatches(repo, sorted(revs))
767 767 self._cleanup(patches, len(patches))
768 768
769 769 def delete(self, repo, patches, opts):
770 770 if not patches and not opts.get('rev'):
771 771 raise util.Abort(_('qdelete requires at least one revision or '
772 772 'patch name'))
773 773
774 774 realpatches = []
775 775 for patch in patches:
776 776 patch = self.lookup(patch, strict=True)
777 777 info = self.isapplied(patch)
778 778 if info:
779 779 raise util.Abort(_("cannot delete applied patch %s") % patch)
780 780 if patch not in self.series:
781 781 raise util.Abort(_("patch %s not in series file") % patch)
782 782 if patch not in realpatches:
783 783 realpatches.append(patch)
784 784
785 785 numrevs = 0
786 786 if opts.get('rev'):
787 787 if not self.applied:
788 788 raise util.Abort(_('no patches applied'))
789 789 revs = cmdutil.revrange(repo, opts.get('rev'))
790 790 if len(revs) > 1 and revs[0] > revs[1]:
791 791 revs.reverse()
792 792 revpatches = self._revpatches(repo, revs)
793 793 realpatches += revpatches
794 794 numrevs = len(revpatches)
795 795
796 796 self._cleanup(realpatches, numrevs, opts.get('keep'))
797 797
798 798 def check_toppatch(self, repo):
799 799 if self.applied:
800 800 top = self.applied[-1].node
801 801 patch = self.applied[-1].name
802 802 pp = repo.dirstate.parents()
803 803 if top not in pp:
804 804 raise util.Abort(_("working directory revision is not qtip"))
805 805 return top, patch
806 806 return None, None
807 807
808 808 def check_substate(self, repo):
809 809 '''return list of subrepos at a different revision than substate.
810 810 Abort if any subrepos have uncommitted changes.'''
811 811 inclsubs = []
812 812 wctx = repo[None]
813 813 for s in wctx.substate:
814 814 if wctx.sub(s).dirty(True):
815 815 raise util.Abort(
816 816 _("uncommitted changes in subrepository %s") % s)
817 817 elif wctx.sub(s).dirty():
818 818 inclsubs.append(s)
819 819 return inclsubs
820 820
821 821 def check_localchanges(self, repo, force=False, refresh=True):
822 822 m, a, r, d = repo.status()[:4]
823 823 if (m or a or r or d) and not force:
824 824 if refresh:
825 825 raise util.Abort(_("local changes found, refresh first"))
826 826 else:
827 827 raise util.Abort(_("local changes found"))
828 828 return m, a, r, d
829 829
830 830 _reserved = ('series', 'status', 'guards')
831 831 def check_reserved_name(self, name):
832 832 if (name in self._reserved or name.startswith('.hg')
833 833 or name.startswith('.mq') or '#' in name or ':' in name):
834 834 raise util.Abort(_('"%s" cannot be used as the name of a patch')
835 835 % name)
836 836
837 837 def new(self, repo, patchfn, *pats, **opts):
838 838 """options:
839 839 msg: a string or a no-argument function returning a string
840 840 """
841 841 msg = opts.get('msg')
842 842 user = opts.get('user')
843 843 date = opts.get('date')
844 844 if date:
845 845 date = util.parsedate(date)
846 846 diffopts = self.diffopts({'git': opts.get('git')})
847 847 self.check_reserved_name(patchfn)
848 848 if os.path.exists(self.join(patchfn)):
849 849 if os.path.isdir(self.join(patchfn)):
850 850 raise util.Abort(_('"%s" already exists as a directory')
851 851 % patchfn)
852 852 else:
853 853 raise util.Abort(_('patch "%s" already exists') % patchfn)
854 854
855 855 inclsubs = self.check_substate(repo)
856 856 if inclsubs:
857 857 inclsubs.append('.hgsubstate')
858 858 if opts.get('include') or opts.get('exclude') or pats:
859 859 if inclsubs:
860 860 pats = list(pats or []) + inclsubs
861 861 match = cmdutil.match(repo, pats, opts)
862 862 # detect missing files in pats
863 863 def badfn(f, msg):
864 864 if f != '.hgsubstate': # .hgsubstate is auto-created
865 865 raise util.Abort('%s: %s' % (f, msg))
866 866 match.bad = badfn
867 867 m, a, r, d = repo.status(match=match)[:4]
868 868 else:
869 869 m, a, r, d = self.check_localchanges(repo, force=True)
870 870 match = cmdutil.matchfiles(repo, m + a + r + inclsubs)
871 871 if len(repo[None].parents()) > 1:
872 872 raise util.Abort(_('cannot manage merge changesets'))
873 873 commitfiles = m + a + r
874 874 self.check_toppatch(repo)
875 875 insert = self.full_series_end()
876 876 wlock = repo.wlock()
877 877 try:
878 878 try:
879 879 # if patch file write fails, abort early
880 880 p = self.opener(patchfn, "w")
881 881 except IOError, e:
882 882 raise util.Abort(_('cannot write patch "%s": %s')
883 883 % (patchfn, e.strerror))
884 884 try:
885 885 if self.plainmode:
886 886 if user:
887 887 p.write("From: " + user + "\n")
888 888 if not date:
889 889 p.write("\n")
890 890 if date:
891 891 p.write("Date: %d %d\n\n" % date)
892 892 else:
893 893 p.write("# HG changeset patch\n")
894 894 p.write("# Parent "
895 895 + hex(repo[None].parents()[0].node()) + "\n")
896 896 if user:
897 897 p.write("# User " + user + "\n")
898 898 if date:
899 899 p.write("# Date %s %s\n\n" % date)
900 900 if hasattr(msg, '__call__'):
901 901 msg = msg()
902 902 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
903 903 n = repo.commit(commitmsg, user, date, match=match, force=True)
904 904 if n is None:
905 905 raise util.Abort(_("repo commit failed"))
906 906 try:
907 907 self.full_series[insert:insert] = [patchfn]
908 908 self.applied.append(statusentry(n, patchfn))
909 909 self.parse_series()
910 910 self.series_dirty = 1
911 911 self.applied_dirty = 1
912 912 if msg:
913 913 msg = msg + "\n\n"
914 914 p.write(msg)
915 915 if commitfiles:
916 916 parent = self.qparents(repo, n)
917 917 chunks = patch.diff(repo, node1=parent, node2=n,
918 918 match=match, opts=diffopts)
919 919 for chunk in chunks:
920 920 p.write(chunk)
921 921 p.close()
922 922 wlock.release()
923 923 wlock = None
924 924 r = self.qrepo()
925 925 if r:
926 926 r[None].add([patchfn])
927 927 except:
928 928 repo.rollback()
929 929 raise
930 930 except Exception:
931 931 patchpath = self.join(patchfn)
932 932 try:
933 933 os.unlink(patchpath)
934 934 except:
935 935 self.ui.warn(_('error unlinking %s\n') % patchpath)
936 936 raise
937 937 self.removeundo(repo)
938 938 finally:
939 939 release(wlock)
940 940
941 941 def strip(self, repo, revs, update=True, backup="all", force=None):
942 942 wlock = lock = None
943 943 try:
944 944 wlock = repo.wlock()
945 945 lock = repo.lock()
946 946
947 947 if update:
948 948 self.check_localchanges(repo, force=force, refresh=False)
949 949 urev = self.qparents(repo, revs[0])
950 950 hg.clean(repo, urev)
951 951 repo.dirstate.write()
952 952
953 953 self.removeundo(repo)
954 954 for rev in revs:
955 955 repair.strip(self.ui, repo, rev, backup)
956 956 # strip may have unbundled a set of backed up revisions after
957 957 # the actual strip
958 958 self.removeundo(repo)
959 959 finally:
960 960 release(lock, wlock)
961 961
962 962 def isapplied(self, patch):
963 963 """returns (index, rev, patch)"""
964 964 for i, a in enumerate(self.applied):
965 965 if a.name == patch:
966 966 return (i, a.node, a.name)
967 967 return None
968 968
969 969 # if the exact patch name does not exist, we try a few
970 970 # variations. If strict is passed, we try only #1
971 971 #
972 972 # 1) a number to indicate an offset in the series file
973 973 # 2) a unique substring of the patch name was given
974 974 # 3) patchname[-+]num to indicate an offset in the series file
975 975 def lookup(self, patch, strict=False):
976 976 patch = patch and str(patch)
977 977
978 978 def partial_name(s):
979 979 if s in self.series:
980 980 return s
981 981 matches = [x for x in self.series if s in x]
982 982 if len(matches) > 1:
983 983 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
984 984 for m in matches:
985 985 self.ui.warn(' %s\n' % m)
986 986 return None
987 987 if matches:
988 988 return matches[0]
989 989 if self.series and self.applied:
990 990 if s == 'qtip':
991 991 return self.series[self.series_end(True)-1]
992 992 if s == 'qbase':
993 993 return self.series[0]
994 994 return None
995 995
996 996 if patch is None:
997 997 return None
998 998 if patch in self.series:
999 999 return patch
1000 1000
1001 1001 if not os.path.isfile(self.join(patch)):
1002 1002 try:
1003 1003 sno = int(patch)
1004 1004 except (ValueError, OverflowError):
1005 1005 pass
1006 1006 else:
1007 1007 if -len(self.series) <= sno < len(self.series):
1008 1008 return self.series[sno]
1009 1009
1010 1010 if not strict:
1011 1011 res = partial_name(patch)
1012 1012 if res:
1013 1013 return res
1014 1014 minus = patch.rfind('-')
1015 1015 if minus >= 0:
1016 1016 res = partial_name(patch[:minus])
1017 1017 if res:
1018 1018 i = self.series.index(res)
1019 1019 try:
1020 1020 off = int(patch[minus + 1:] or 1)
1021 1021 except (ValueError, OverflowError):
1022 1022 pass
1023 1023 else:
1024 1024 if i - off >= 0:
1025 1025 return self.series[i - off]
1026 1026 plus = patch.rfind('+')
1027 1027 if plus >= 0:
1028 1028 res = partial_name(patch[:plus])
1029 1029 if res:
1030 1030 i = self.series.index(res)
1031 1031 try:
1032 1032 off = int(patch[plus + 1:] or 1)
1033 1033 except (ValueError, OverflowError):
1034 1034 pass
1035 1035 else:
1036 1036 if i + off < len(self.series):
1037 1037 return self.series[i + off]
1038 1038 raise util.Abort(_("patch %s not in series") % patch)
1039 1039
1040 1040 def push(self, repo, patch=None, force=False, list=False,
1041 1041 mergeq=None, all=False, move=False, exact=False):
1042 1042 diffopts = self.diffopts()
1043 1043 wlock = repo.wlock()
1044 1044 try:
1045 1045 heads = []
1046 1046 for b, ls in repo.branchmap().iteritems():
1047 1047 heads += ls
1048 1048 if not heads:
1049 1049 heads = [nullid]
1050 1050 if repo.dirstate.parents()[0] not in heads and not exact:
1051 1051 self.ui.status(_("(working directory not at a head)\n"))
1052 1052
1053 1053 if not self.series:
1054 1054 self.ui.warn(_('no patches in series\n'))
1055 1055 return 0
1056 1056
1057 1057 patch = self.lookup(patch)
1058 1058 # Suppose our series file is: A B C and the current 'top'
1059 1059 # patch is B. qpush C should be performed (moving forward)
1060 1060 # qpush B is a NOP (no change) qpush A is an error (can't
1061 1061 # go backwards with qpush)
1062 1062 if patch:
1063 1063 info = self.isapplied(patch)
1064 1064 if info:
1065 1065 if info[0] < len(self.applied) - 1:
1066 1066 raise util.Abort(
1067 1067 _("cannot push to a previous patch: %s") % patch)
1068 1068 self.ui.warn(
1069 1069 _('qpush: %s is already at the top\n') % patch)
1070 1070 return 0
1071 1071 pushable, reason = self.pushable(patch)
1072 1072 if not pushable:
1073 1073 if reason:
1074 1074 reason = _('guarded by %r') % reason
1075 1075 else:
1076 1076 reason = _('no matching guards')
1077 1077 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1078 1078 return 1
1079 1079 elif all:
1080 1080 patch = self.series[-1]
1081 1081 if self.isapplied(patch):
1082 1082 self.ui.warn(_('all patches are currently applied\n'))
1083 1083 return 0
1084 1084
1085 1085 # Following the above example, starting at 'top' of B:
1086 1086 # qpush should be performed (pushes C), but a subsequent
1087 1087 # qpush without an argument is an error (nothing to
1088 1088 # apply). This allows a loop of "...while hg qpush..." to
1089 1089 # work as it detects an error when done
1090 1090 start = self.series_end()
1091 1091 if start == len(self.series):
1092 1092 self.ui.warn(_('patch series already fully applied\n'))
1093 1093 return 1
1094 1094 if not force:
1095 1095 self.check_localchanges(repo)
1096 1096
1097 1097 if exact:
1098 1098 if move:
1099 1099 raise util.Abort(_("cannot use --exact and --move together"))
1100 1100 if self.applied:
1101 1101 raise util.Abort(_("cannot push --exact with applied patches"))
1102 1102 root = self.series[start]
1103 1103 target = patchheader(self.join(root), self.plainmode).parent
1104 1104 if not target:
1105 1105 raise util.Abort(_("%s does not have a parent recorded" % root))
1106 1106 if not repo[target] == repo['.']:
1107 1107 hg.update(repo, target)
1108 1108
1109 1109 if move:
1110 1110 if not patch:
1111 1111 raise util.Abort(_("please specify the patch to move"))
1112 1112 for i, rpn in enumerate(self.full_series[start:]):
1113 1113 # strip markers for patch guards
1114 1114 if self.guard_re.split(rpn, 1)[0] == patch:
1115 1115 break
1116 1116 index = start + i
1117 1117 assert index < len(self.full_series)
1118 1118 fullpatch = self.full_series[index]
1119 1119 del self.full_series[index]
1120 1120 self.full_series.insert(start, fullpatch)
1121 1121 self.parse_series()
1122 1122 self.series_dirty = 1
1123 1123
1124 1124 self.applied_dirty = 1
1125 1125 if start > 0:
1126 1126 self.check_toppatch(repo)
1127 1127 if not patch:
1128 1128 patch = self.series[start]
1129 1129 end = start + 1
1130 1130 else:
1131 1131 end = self.series.index(patch, start) + 1
1132 1132
1133 1133 s = self.series[start:end]
1134 1134 all_files = set()
1135 1135 try:
1136 1136 if mergeq:
1137 1137 ret = self.mergepatch(repo, mergeq, s, diffopts)
1138 1138 else:
1139 1139 ret = self.apply(repo, s, list, all_files=all_files)
1140 1140 except:
1141 1141 self.ui.warn(_('cleaning up working directory...'))
1142 1142 node = repo.dirstate.parents()[0]
1143 1143 hg.revert(repo, node, None)
1144 1144 # only remove unknown files that we know we touched or
1145 1145 # created while patching
1146 1146 for f in all_files:
1147 1147 if f not in repo.dirstate:
1148 1148 try:
1149 util.unlink(repo.wjoin(f))
1149 util.unlinkpath(repo.wjoin(f))
1150 1150 except OSError, inst:
1151 1151 if inst.errno != errno.ENOENT:
1152 1152 raise
1153 1153 self.ui.warn(_('done\n'))
1154 1154 raise
1155 1155
1156 1156 if not self.applied:
1157 1157 return ret[0]
1158 1158 top = self.applied[-1].name
1159 1159 if ret[0] and ret[0] > 1:
1160 1160 msg = _("errors during apply, please fix and refresh %s\n")
1161 1161 self.ui.write(msg % top)
1162 1162 else:
1163 1163 self.ui.write(_("now at: %s\n") % top)
1164 1164 return ret[0]
1165 1165
1166 1166 finally:
1167 1167 wlock.release()
1168 1168
1169 1169 def pop(self, repo, patch=None, force=False, update=True, all=False):
1170 1170 wlock = repo.wlock()
1171 1171 try:
1172 1172 if patch:
1173 1173 # index, rev, patch
1174 1174 info = self.isapplied(patch)
1175 1175 if not info:
1176 1176 patch = self.lookup(patch)
1177 1177 info = self.isapplied(patch)
1178 1178 if not info:
1179 1179 raise util.Abort(_("patch %s is not applied") % patch)
1180 1180
1181 1181 if not self.applied:
1182 1182 # Allow qpop -a to work repeatedly,
1183 1183 # but not qpop without an argument
1184 1184 self.ui.warn(_("no patches applied\n"))
1185 1185 return not all
1186 1186
1187 1187 if all:
1188 1188 start = 0
1189 1189 elif patch:
1190 1190 start = info[0] + 1
1191 1191 else:
1192 1192 start = len(self.applied) - 1
1193 1193
1194 1194 if start >= len(self.applied):
1195 1195 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1196 1196 return
1197 1197
1198 1198 if not update:
1199 1199 parents = repo.dirstate.parents()
1200 1200 rr = [x.node for x in self.applied]
1201 1201 for p in parents:
1202 1202 if p in rr:
1203 1203 self.ui.warn(_("qpop: forcing dirstate update\n"))
1204 1204 update = True
1205 1205 else:
1206 1206 parents = [p.node() for p in repo[None].parents()]
1207 1207 needupdate = False
1208 1208 for entry in self.applied[start:]:
1209 1209 if entry.node in parents:
1210 1210 needupdate = True
1211 1211 break
1212 1212 update = needupdate
1213 1213
1214 1214 if not force and update:
1215 1215 self.check_localchanges(repo)
1216 1216
1217 1217 self.applied_dirty = 1
1218 1218 end = len(self.applied)
1219 1219 rev = self.applied[start].node
1220 1220 if update:
1221 1221 top = self.check_toppatch(repo)[0]
1222 1222
1223 1223 try:
1224 1224 heads = repo.changelog.heads(rev)
1225 1225 except error.LookupError:
1226 1226 node = short(rev)
1227 1227 raise util.Abort(_('trying to pop unknown node %s') % node)
1228 1228
1229 1229 if heads != [self.applied[-1].node]:
1230 1230 raise util.Abort(_("popping would remove a revision not "
1231 1231 "managed by this patch queue"))
1232 1232
1233 1233 # we know there are no local changes, so we can make a simplified
1234 1234 # form of hg.update.
1235 1235 if update:
1236 1236 qp = self.qparents(repo, rev)
1237 1237 ctx = repo[qp]
1238 1238 m, a, r, d = repo.status(qp, top)[:4]
1239 1239 if d:
1240 1240 raise util.Abort(_("deletions found between repo revs"))
1241 1241 for f in a:
1242 1242 try:
1243 util.unlink(repo.wjoin(f))
1243 util.unlinkpath(repo.wjoin(f))
1244 1244 except OSError, e:
1245 1245 if e.errno != errno.ENOENT:
1246 1246 raise
1247 1247 repo.dirstate.forget(f)
1248 1248 for f in m + r:
1249 1249 fctx = ctx[f]
1250 1250 repo.wwrite(f, fctx.data(), fctx.flags())
1251 1251 repo.dirstate.normal(f)
1252 1252 repo.dirstate.setparents(qp, nullid)
1253 1253 for patch in reversed(self.applied[start:end]):
1254 1254 self.ui.status(_("popping %s\n") % patch.name)
1255 1255 del self.applied[start:end]
1256 1256 self.strip(repo, [rev], update=False, backup='strip')
1257 1257 if self.applied:
1258 1258 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1259 1259 else:
1260 1260 self.ui.write(_("patch queue now empty\n"))
1261 1261 finally:
1262 1262 wlock.release()
1263 1263
1264 1264 def diff(self, repo, pats, opts):
1265 1265 top, patch = self.check_toppatch(repo)
1266 1266 if not top:
1267 1267 self.ui.write(_("no patches applied\n"))
1268 1268 return
1269 1269 qp = self.qparents(repo, top)
1270 1270 if opts.get('reverse'):
1271 1271 node1, node2 = None, qp
1272 1272 else:
1273 1273 node1, node2 = qp, None
1274 1274 diffopts = self.diffopts(opts, patch)
1275 1275 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1276 1276
1277 1277 def refresh(self, repo, pats=None, **opts):
1278 1278 if not self.applied:
1279 1279 self.ui.write(_("no patches applied\n"))
1280 1280 return 1
1281 1281 msg = opts.get('msg', '').rstrip()
1282 1282 newuser = opts.get('user')
1283 1283 newdate = opts.get('date')
1284 1284 if newdate:
1285 1285 newdate = '%d %d' % util.parsedate(newdate)
1286 1286 wlock = repo.wlock()
1287 1287
1288 1288 try:
1289 1289 self.check_toppatch(repo)
1290 1290 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1291 1291 if repo.changelog.heads(top) != [top]:
1292 1292 raise util.Abort(_("cannot refresh a revision with children"))
1293 1293
1294 1294 inclsubs = self.check_substate(repo)
1295 1295
1296 1296 cparents = repo.changelog.parents(top)
1297 1297 patchparent = self.qparents(repo, top)
1298 1298 ph = patchheader(self.join(patchfn), self.plainmode)
1299 1299 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1300 1300 if msg:
1301 1301 ph.setmessage(msg)
1302 1302 if newuser:
1303 1303 ph.setuser(newuser)
1304 1304 if newdate:
1305 1305 ph.setdate(newdate)
1306 1306 ph.setparent(hex(patchparent))
1307 1307
1308 1308 # only commit new patch when write is complete
1309 1309 patchf = self.opener(patchfn, 'w', atomictemp=True)
1310 1310
1311 1311 comments = str(ph)
1312 1312 if comments:
1313 1313 patchf.write(comments)
1314 1314
1315 1315 # update the dirstate in place, strip off the qtip commit
1316 1316 # and then commit.
1317 1317 #
1318 1318 # this should really read:
1319 1319 # mm, dd, aa = repo.status(top, patchparent)[:3]
1320 1320 # but we do it backwards to take advantage of manifest/chlog
1321 1321 # caching against the next repo.status call
1322 1322 mm, aa, dd = repo.status(patchparent, top)[:3]
1323 1323 changes = repo.changelog.read(top)
1324 1324 man = repo.manifest.read(changes[0])
1325 1325 aaa = aa[:]
1326 1326 matchfn = cmdutil.match(repo, pats, opts)
1327 1327 # in short mode, we only diff the files included in the
1328 1328 # patch already plus specified files
1329 1329 if opts.get('short'):
1330 1330 # if amending a patch, we start with existing
1331 1331 # files plus specified files - unfiltered
1332 1332 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1333 1333 # filter with inc/exl options
1334 1334 matchfn = cmdutil.match(repo, opts=opts)
1335 1335 else:
1336 1336 match = cmdutil.matchall(repo)
1337 1337 m, a, r, d = repo.status(match=match)[:4]
1338 1338 mm = set(mm)
1339 1339 aa = set(aa)
1340 1340 dd = set(dd)
1341 1341
1342 1342 # we might end up with files that were added between
1343 1343 # qtip and the dirstate parent, but then changed in the
1344 1344 # local dirstate. in this case, we want them to only
1345 1345 # show up in the added section
1346 1346 for x in m:
1347 1347 if x not in aa:
1348 1348 mm.add(x)
1349 1349 # we might end up with files added by the local dirstate that
1350 1350 # were deleted by the patch. In this case, they should only
1351 1351 # show up in the changed section.
1352 1352 for x in a:
1353 1353 if x in dd:
1354 1354 dd.remove(x)
1355 1355 mm.add(x)
1356 1356 else:
1357 1357 aa.add(x)
1358 1358 # make sure any files deleted in the local dirstate
1359 1359 # are not in the add or change column of the patch
1360 1360 forget = []
1361 1361 for x in d + r:
1362 1362 if x in aa:
1363 1363 aa.remove(x)
1364 1364 forget.append(x)
1365 1365 continue
1366 1366 else:
1367 1367 mm.discard(x)
1368 1368 dd.add(x)
1369 1369
1370 1370 m = list(mm)
1371 1371 r = list(dd)
1372 1372 a = list(aa)
1373 1373 c = [filter(matchfn, l) for l in (m, a, r)]
1374 1374 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1375 1375 chunks = patch.diff(repo, patchparent, match=match,
1376 1376 changes=c, opts=diffopts)
1377 1377 for chunk in chunks:
1378 1378 patchf.write(chunk)
1379 1379
1380 1380 try:
1381 1381 if diffopts.git or diffopts.upgrade:
1382 1382 copies = {}
1383 1383 for dst in a:
1384 1384 src = repo.dirstate.copied(dst)
1385 1385 # during qfold, the source file for copies may
1386 1386 # be removed. Treat this as a simple add.
1387 1387 if src is not None and src in repo.dirstate:
1388 1388 copies.setdefault(src, []).append(dst)
1389 1389 repo.dirstate.add(dst)
1390 1390 # remember the copies between patchparent and qtip
1391 1391 for dst in aaa:
1392 1392 f = repo.file(dst)
1393 1393 src = f.renamed(man[dst])
1394 1394 if src:
1395 1395 copies.setdefault(src[0], []).extend(
1396 1396 copies.get(dst, []))
1397 1397 if dst in a:
1398 1398 copies[src[0]].append(dst)
1399 1399 # we can't copy a file created by the patch itself
1400 1400 if dst in copies:
1401 1401 del copies[dst]
1402 1402 for src, dsts in copies.iteritems():
1403 1403 for dst in dsts:
1404 1404 repo.dirstate.copy(src, dst)
1405 1405 else:
1406 1406 for dst in a:
1407 1407 repo.dirstate.add(dst)
1408 1408 # Drop useless copy information
1409 1409 for f in list(repo.dirstate.copies()):
1410 1410 repo.dirstate.copy(None, f)
1411 1411 for f in r:
1412 1412 repo.dirstate.remove(f)
1413 1413 # if the patch excludes a modified file, mark that
1414 1414 # file with mtime=0 so status can see it.
1415 1415 mm = []
1416 1416 for i in xrange(len(m)-1, -1, -1):
1417 1417 if not matchfn(m[i]):
1418 1418 mm.append(m[i])
1419 1419 del m[i]
1420 1420 for f in m:
1421 1421 repo.dirstate.normal(f)
1422 1422 for f in mm:
1423 1423 repo.dirstate.normallookup(f)
1424 1424 for f in forget:
1425 1425 repo.dirstate.forget(f)
1426 1426
1427 1427 if not msg:
1428 1428 if not ph.message:
1429 1429 message = "[mq]: %s\n" % patchfn
1430 1430 else:
1431 1431 message = "\n".join(ph.message)
1432 1432 else:
1433 1433 message = msg
1434 1434
1435 1435 user = ph.user or changes[1]
1436 1436
1437 1437 # assumes strip can roll itself back if interrupted
1438 1438 repo.dirstate.setparents(*cparents)
1439 1439 self.applied.pop()
1440 1440 self.applied_dirty = 1
1441 1441 self.strip(repo, [top], update=False,
1442 1442 backup='strip')
1443 1443 except:
1444 1444 repo.dirstate.invalidate()
1445 1445 raise
1446 1446
1447 1447 try:
1448 1448 # might be nice to attempt to roll back strip after this
1449 1449 patchf.rename()
1450 1450 n = repo.commit(message, user, ph.date, match=match,
1451 1451 force=True)
1452 1452 self.applied.append(statusentry(n, patchfn))
1453 1453 except:
1454 1454 ctx = repo[cparents[0]]
1455 1455 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1456 1456 self.save_dirty()
1457 1457 self.ui.warn(_('refresh interrupted while patch was popped! '
1458 1458 '(revert --all, qpush to recover)\n'))
1459 1459 raise
1460 1460 finally:
1461 1461 wlock.release()
1462 1462 self.removeundo(repo)
1463 1463
1464 1464 def init(self, repo, create=False):
1465 1465 if not create and os.path.isdir(self.path):
1466 1466 raise util.Abort(_("patch queue directory already exists"))
1467 1467 try:
1468 1468 os.mkdir(self.path)
1469 1469 except OSError, inst:
1470 1470 if inst.errno != errno.EEXIST or not create:
1471 1471 raise
1472 1472 if create:
1473 1473 return self.qrepo(create=True)
1474 1474
1475 1475 def unapplied(self, repo, patch=None):
1476 1476 if patch and patch not in self.series:
1477 1477 raise util.Abort(_("patch %s is not in series file") % patch)
1478 1478 if not patch:
1479 1479 start = self.series_end()
1480 1480 else:
1481 1481 start = self.series.index(patch) + 1
1482 1482 unapplied = []
1483 1483 for i in xrange(start, len(self.series)):
1484 1484 pushable, reason = self.pushable(i)
1485 1485 if pushable:
1486 1486 unapplied.append((i, self.series[i]))
1487 1487 self.explain_pushable(i)
1488 1488 return unapplied
1489 1489
1490 1490 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1491 1491 summary=False):
1492 1492 def displayname(pfx, patchname, state):
1493 1493 if pfx:
1494 1494 self.ui.write(pfx)
1495 1495 if summary:
1496 1496 ph = patchheader(self.join(patchname), self.plainmode)
1497 1497 msg = ph.message and ph.message[0] or ''
1498 1498 if self.ui.formatted():
1499 1499 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1500 1500 if width > 0:
1501 1501 msg = util.ellipsis(msg, width)
1502 1502 else:
1503 1503 msg = ''
1504 1504 self.ui.write(patchname, label='qseries.' + state)
1505 1505 self.ui.write(': ')
1506 1506 self.ui.write(msg, label='qseries.message.' + state)
1507 1507 else:
1508 1508 self.ui.write(patchname, label='qseries.' + state)
1509 1509 self.ui.write('\n')
1510 1510
1511 1511 applied = set([p.name for p in self.applied])
1512 1512 if length is None:
1513 1513 length = len(self.series) - start
1514 1514 if not missing:
1515 1515 if self.ui.verbose:
1516 1516 idxwidth = len(str(start + length - 1))
1517 1517 for i in xrange(start, start + length):
1518 1518 patch = self.series[i]
1519 1519 if patch in applied:
1520 1520 char, state = 'A', 'applied'
1521 1521 elif self.pushable(i)[0]:
1522 1522 char, state = 'U', 'unapplied'
1523 1523 else:
1524 1524 char, state = 'G', 'guarded'
1525 1525 pfx = ''
1526 1526 if self.ui.verbose:
1527 1527 pfx = '%*d %s ' % (idxwidth, i, char)
1528 1528 elif status and status != char:
1529 1529 continue
1530 1530 displayname(pfx, patch, state)
1531 1531 else:
1532 1532 msng_list = []
1533 1533 for root, dirs, files in os.walk(self.path):
1534 1534 d = root[len(self.path) + 1:]
1535 1535 for f in files:
1536 1536 fl = os.path.join(d, f)
1537 1537 if (fl not in self.series and
1538 1538 fl not in (self.status_path, self.series_path,
1539 1539 self.guards_path)
1540 1540 and not fl.startswith('.')):
1541 1541 msng_list.append(fl)
1542 1542 for x in sorted(msng_list):
1543 1543 pfx = self.ui.verbose and ('D ') or ''
1544 1544 displayname(pfx, x, 'missing')
1545 1545
1546 1546 def issaveline(self, l):
1547 1547 if l.name == '.hg.patches.save.line':
1548 1548 return True
1549 1549
1550 1550 def qrepo(self, create=False):
1551 1551 ui = self.ui.copy()
1552 1552 ui.setconfig('paths', 'default', '', overlay=False)
1553 1553 ui.setconfig('paths', 'default-push', '', overlay=False)
1554 1554 if create or os.path.isdir(self.join(".hg")):
1555 1555 return hg.repository(ui, path=self.path, create=create)
1556 1556
1557 1557 def restore(self, repo, rev, delete=None, qupdate=None):
1558 1558 desc = repo[rev].description().strip()
1559 1559 lines = desc.splitlines()
1560 1560 i = 0
1561 1561 datastart = None
1562 1562 series = []
1563 1563 applied = []
1564 1564 qpp = None
1565 1565 for i, line in enumerate(lines):
1566 1566 if line == 'Patch Data:':
1567 1567 datastart = i + 1
1568 1568 elif line.startswith('Dirstate:'):
1569 1569 l = line.rstrip()
1570 1570 l = l[10:].split(' ')
1571 1571 qpp = [bin(x) for x in l]
1572 1572 elif datastart is not None:
1573 1573 l = line.rstrip()
1574 1574 n, name = l.split(':', 1)
1575 1575 if n:
1576 1576 applied.append(statusentry(bin(n), name))
1577 1577 else:
1578 1578 series.append(l)
1579 1579 if datastart is None:
1580 1580 self.ui.warn(_("No saved patch data found\n"))
1581 1581 return 1
1582 1582 self.ui.warn(_("restoring status: %s\n") % lines[0])
1583 1583 self.full_series = series
1584 1584 self.applied = applied
1585 1585 self.parse_series()
1586 1586 self.series_dirty = 1
1587 1587 self.applied_dirty = 1
1588 1588 heads = repo.changelog.heads()
1589 1589 if delete:
1590 1590 if rev not in heads:
1591 1591 self.ui.warn(_("save entry has children, leaving it alone\n"))
1592 1592 else:
1593 1593 self.ui.warn(_("removing save entry %s\n") % short(rev))
1594 1594 pp = repo.dirstate.parents()
1595 1595 if rev in pp:
1596 1596 update = True
1597 1597 else:
1598 1598 update = False
1599 1599 self.strip(repo, [rev], update=update, backup='strip')
1600 1600 if qpp:
1601 1601 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1602 1602 (short(qpp[0]), short(qpp[1])))
1603 1603 if qupdate:
1604 1604 self.ui.status(_("updating queue directory\n"))
1605 1605 r = self.qrepo()
1606 1606 if not r:
1607 1607 self.ui.warn(_("Unable to load queue repository\n"))
1608 1608 return 1
1609 1609 hg.clean(r, qpp[0])
1610 1610
1611 1611 def save(self, repo, msg=None):
1612 1612 if not self.applied:
1613 1613 self.ui.warn(_("save: no patches applied, exiting\n"))
1614 1614 return 1
1615 1615 if self.issaveline(self.applied[-1]):
1616 1616 self.ui.warn(_("status is already saved\n"))
1617 1617 return 1
1618 1618
1619 1619 if not msg:
1620 1620 msg = _("hg patches saved state")
1621 1621 else:
1622 1622 msg = "hg patches: " + msg.rstrip('\r\n')
1623 1623 r = self.qrepo()
1624 1624 if r:
1625 1625 pp = r.dirstate.parents()
1626 1626 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1627 1627 msg += "\n\nPatch Data:\n"
1628 1628 msg += ''.join('%s\n' % x for x in self.applied)
1629 1629 msg += ''.join(':%s\n' % x for x in self.full_series)
1630 1630 n = repo.commit(msg, force=True)
1631 1631 if not n:
1632 1632 self.ui.warn(_("repo commit failed\n"))
1633 1633 return 1
1634 1634 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1635 1635 self.applied_dirty = 1
1636 1636 self.removeundo(repo)
1637 1637
1638 1638 def full_series_end(self):
1639 1639 if self.applied:
1640 1640 p = self.applied[-1].name
1641 1641 end = self.find_series(p)
1642 1642 if end is None:
1643 1643 return len(self.full_series)
1644 1644 return end + 1
1645 1645 return 0
1646 1646
1647 1647 def series_end(self, all_patches=False):
1648 1648 """If all_patches is False, return the index of the next pushable patch
1649 1649 in the series, or the series length. If all_patches is True, return the
1650 1650 index of the first patch past the last applied one.
1651 1651 """
1652 1652 end = 0
1653 1653 def next(start):
1654 1654 if all_patches or start >= len(self.series):
1655 1655 return start
1656 1656 for i in xrange(start, len(self.series)):
1657 1657 p, reason = self.pushable(i)
1658 1658 if p:
1659 1659 break
1660 1660 self.explain_pushable(i)
1661 1661 return i
1662 1662 if self.applied:
1663 1663 p = self.applied[-1].name
1664 1664 try:
1665 1665 end = self.series.index(p)
1666 1666 except ValueError:
1667 1667 return 0
1668 1668 return next(end + 1)
1669 1669 return next(end)
1670 1670
1671 1671 def appliedname(self, index):
1672 1672 pname = self.applied[index].name
1673 1673 if not self.ui.verbose:
1674 1674 p = pname
1675 1675 else:
1676 1676 p = str(self.series.index(pname)) + " " + pname
1677 1677 return p
1678 1678
1679 1679 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1680 1680 force=None, git=False):
1681 1681 def checkseries(patchname):
1682 1682 if patchname in self.series:
1683 1683 raise util.Abort(_('patch %s is already in the series file')
1684 1684 % patchname)
1685 1685 def checkfile(patchname):
1686 1686 if not force and os.path.exists(self.join(patchname)):
1687 1687 raise util.Abort(_('patch "%s" already exists')
1688 1688 % patchname)
1689 1689
1690 1690 if rev:
1691 1691 if files:
1692 1692 raise util.Abort(_('option "-r" not valid when importing '
1693 1693 'files'))
1694 1694 rev = cmdutil.revrange(repo, rev)
1695 1695 rev.sort(reverse=True)
1696 1696 if (len(files) > 1 or len(rev) > 1) and patchname:
1697 1697 raise util.Abort(_('option "-n" not valid when importing multiple '
1698 1698 'patches'))
1699 1699 if rev:
1700 1700 # If mq patches are applied, we can only import revisions
1701 1701 # that form a linear path to qbase.
1702 1702 # Otherwise, they should form a linear path to a head.
1703 1703 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1704 1704 if len(heads) > 1:
1705 1705 raise util.Abort(_('revision %d is the root of more than one '
1706 1706 'branch') % rev[-1])
1707 1707 if self.applied:
1708 1708 base = repo.changelog.node(rev[0])
1709 1709 if base in [n.node for n in self.applied]:
1710 1710 raise util.Abort(_('revision %d is already managed')
1711 1711 % rev[0])
1712 1712 if heads != [self.applied[-1].node]:
1713 1713 raise util.Abort(_('revision %d is not the parent of '
1714 1714 'the queue') % rev[0])
1715 1715 base = repo.changelog.rev(self.applied[0].node)
1716 1716 lastparent = repo.changelog.parentrevs(base)[0]
1717 1717 else:
1718 1718 if heads != [repo.changelog.node(rev[0])]:
1719 1719 raise util.Abort(_('revision %d has unmanaged children')
1720 1720 % rev[0])
1721 1721 lastparent = None
1722 1722
1723 1723 diffopts = self.diffopts({'git': git})
1724 1724 for r in rev:
1725 1725 p1, p2 = repo.changelog.parentrevs(r)
1726 1726 n = repo.changelog.node(r)
1727 1727 if p2 != nullrev:
1728 1728 raise util.Abort(_('cannot import merge revision %d') % r)
1729 1729 if lastparent and lastparent != r:
1730 1730 raise util.Abort(_('revision %d is not the parent of %d')
1731 1731 % (r, lastparent))
1732 1732 lastparent = p1
1733 1733
1734 1734 if not patchname:
1735 1735 patchname = normname('%d.diff' % r)
1736 1736 self.check_reserved_name(patchname)
1737 1737 checkseries(patchname)
1738 1738 checkfile(patchname)
1739 1739 self.full_series.insert(0, patchname)
1740 1740
1741 1741 patchf = self.opener(patchname, "w")
1742 1742 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1743 1743 patchf.close()
1744 1744
1745 1745 se = statusentry(n, patchname)
1746 1746 self.applied.insert(0, se)
1747 1747
1748 1748 self.added.append(patchname)
1749 1749 patchname = None
1750 1750 self.parse_series()
1751 1751 self.applied_dirty = 1
1752 1752 self.series_dirty = True
1753 1753
1754 1754 for i, filename in enumerate(files):
1755 1755 if existing:
1756 1756 if filename == '-':
1757 1757 raise util.Abort(_('-e is incompatible with import from -'))
1758 1758 filename = normname(filename)
1759 1759 self.check_reserved_name(filename)
1760 1760 originpath = self.join(filename)
1761 1761 if not os.path.isfile(originpath):
1762 1762 raise util.Abort(_("patch %s does not exist") % filename)
1763 1763
1764 1764 if patchname:
1765 1765 self.check_reserved_name(patchname)
1766 1766 checkfile(patchname)
1767 1767
1768 1768 self.ui.write(_('renaming %s to %s\n')
1769 1769 % (filename, patchname))
1770 1770 util.rename(originpath, self.join(patchname))
1771 1771 else:
1772 1772 patchname = filename
1773 1773
1774 1774 else:
1775 1775 try:
1776 1776 if filename == '-':
1777 1777 if not patchname:
1778 1778 raise util.Abort(
1779 1779 _('need --name to import a patch from -'))
1780 1780 text = sys.stdin.read()
1781 1781 else:
1782 1782 text = url.open(self.ui, filename).read()
1783 1783 except (OSError, IOError):
1784 1784 raise util.Abort(_("unable to read file %s") % filename)
1785 1785 if not patchname:
1786 1786 patchname = normname(os.path.basename(filename))
1787 1787 self.check_reserved_name(patchname)
1788 1788 checkfile(patchname)
1789 1789 patchf = self.opener(patchname, "w")
1790 1790 patchf.write(text)
1791 1791 if not force:
1792 1792 checkseries(patchname)
1793 1793 if patchname not in self.series:
1794 1794 index = self.full_series_end() + i
1795 1795 self.full_series[index:index] = [patchname]
1796 1796 self.parse_series()
1797 1797 self.series_dirty = True
1798 1798 self.ui.warn(_("adding %s to series file\n") % patchname)
1799 1799 self.added.append(patchname)
1800 1800 patchname = None
1801 1801
1802 1802 def delete(ui, repo, *patches, **opts):
1803 1803 """remove patches from queue
1804 1804
1805 1805 The patches must not be applied, and at least one patch is required. With
1806 1806 -k/--keep, the patch files are preserved in the patch directory.
1807 1807
1808 1808 To stop managing a patch and move it into permanent history,
1809 1809 use the :hg:`qfinish` command."""
1810 1810 q = repo.mq
1811 1811 q.delete(repo, patches, opts)
1812 1812 q.save_dirty()
1813 1813 return 0
1814 1814
1815 1815 def applied(ui, repo, patch=None, **opts):
1816 1816 """print the patches already applied
1817 1817
1818 1818 Returns 0 on success."""
1819 1819
1820 1820 q = repo.mq
1821 1821
1822 1822 if patch:
1823 1823 if patch not in q.series:
1824 1824 raise util.Abort(_("patch %s is not in series file") % patch)
1825 1825 end = q.series.index(patch) + 1
1826 1826 else:
1827 1827 end = q.series_end(True)
1828 1828
1829 1829 if opts.get('last') and not end:
1830 1830 ui.write(_("no patches applied\n"))
1831 1831 return 1
1832 1832 elif opts.get('last') and end == 1:
1833 1833 ui.write(_("only one patch applied\n"))
1834 1834 return 1
1835 1835 elif opts.get('last'):
1836 1836 start = end - 2
1837 1837 end = 1
1838 1838 else:
1839 1839 start = 0
1840 1840
1841 1841 q.qseries(repo, length=end, start=start, status='A',
1842 1842 summary=opts.get('summary'))
1843 1843
1844 1844
1845 1845 def unapplied(ui, repo, patch=None, **opts):
1846 1846 """print the patches not yet applied
1847 1847
1848 1848 Returns 0 on success."""
1849 1849
1850 1850 q = repo.mq
1851 1851 if patch:
1852 1852 if patch not in q.series:
1853 1853 raise util.Abort(_("patch %s is not in series file") % patch)
1854 1854 start = q.series.index(patch) + 1
1855 1855 else:
1856 1856 start = q.series_end(True)
1857 1857
1858 1858 if start == len(q.series) and opts.get('first'):
1859 1859 ui.write(_("all patches applied\n"))
1860 1860 return 1
1861 1861
1862 1862 length = opts.get('first') and 1 or None
1863 1863 q.qseries(repo, start=start, length=length, status='U',
1864 1864 summary=opts.get('summary'))
1865 1865
1866 1866 def qimport(ui, repo, *filename, **opts):
1867 1867 """import a patch
1868 1868
1869 1869 The patch is inserted into the series after the last applied
1870 1870 patch. If no patches have been applied, qimport prepends the patch
1871 1871 to the series.
1872 1872
1873 1873 The patch will have the same name as its source file unless you
1874 1874 give it a new one with -n/--name.
1875 1875
1876 1876 You can register an existing patch inside the patch directory with
1877 1877 the -e/--existing flag.
1878 1878
1879 1879 With -f/--force, an existing patch of the same name will be
1880 1880 overwritten.
1881 1881
1882 1882 An existing changeset may be placed under mq control with -r/--rev
1883 1883 (e.g. qimport --rev tip -n patch will place tip under mq control).
1884 1884 With -g/--git, patches imported with --rev will use the git diff
1885 1885 format. See the diffs help topic for information on why this is
1886 1886 important for preserving rename/copy information and permission
1887 1887 changes.
1888 1888
1889 1889 To import a patch from standard input, pass - as the patch file.
1890 1890 When importing from standard input, a patch name must be specified
1891 1891 using the --name flag.
1892 1892
1893 1893 To import an existing patch while renaming it::
1894 1894
1895 1895 hg qimport -e existing-patch -n new-name
1896 1896
1897 1897 Returns 0 if import succeeded.
1898 1898 """
1899 1899 q = repo.mq
1900 1900 try:
1901 1901 q.qimport(repo, filename, patchname=opts.get('name'),
1902 1902 existing=opts.get('existing'), force=opts.get('force'),
1903 1903 rev=opts.get('rev'), git=opts.get('git'))
1904 1904 finally:
1905 1905 q.save_dirty()
1906 1906
1907 1907 if opts.get('push') and not opts.get('rev'):
1908 1908 return q.push(repo, None)
1909 1909 return 0
1910 1910
1911 1911 def qinit(ui, repo, create):
1912 1912 """initialize a new queue repository
1913 1913
1914 1914 This command also creates a series file for ordering patches, and
1915 1915 an mq-specific .hgignore file in the queue repository, to exclude
1916 1916 the status and guards files (these contain mostly transient state).
1917 1917
1918 1918 Returns 0 if initialization succeeded."""
1919 1919 q = repo.mq
1920 1920 r = q.init(repo, create)
1921 1921 q.save_dirty()
1922 1922 if r:
1923 1923 if not os.path.exists(r.wjoin('.hgignore')):
1924 1924 fp = r.wopener('.hgignore', 'w')
1925 1925 fp.write('^\\.hg\n')
1926 1926 fp.write('^\\.mq\n')
1927 1927 fp.write('syntax: glob\n')
1928 1928 fp.write('status\n')
1929 1929 fp.write('guards\n')
1930 1930 fp.close()
1931 1931 if not os.path.exists(r.wjoin('series')):
1932 1932 r.wopener('series', 'w').close()
1933 1933 r[None].add(['.hgignore', 'series'])
1934 1934 commands.add(ui, r)
1935 1935 return 0
1936 1936
1937 1937 def init(ui, repo, **opts):
1938 1938 """init a new queue repository (DEPRECATED)
1939 1939
1940 1940 The queue repository is unversioned by default. If
1941 1941 -c/--create-repo is specified, qinit will create a separate nested
1942 1942 repository for patches (qinit -c may also be run later to convert
1943 1943 an unversioned patch repository into a versioned one). You can use
1944 1944 qcommit to commit changes to this queue repository.
1945 1945
1946 1946 This command is deprecated. Without -c, it's implied by other relevant
1947 1947 commands. With -c, use :hg:`init --mq` instead."""
1948 1948 return qinit(ui, repo, create=opts.get('create_repo'))
1949 1949
1950 1950 def clone(ui, source, dest=None, **opts):
1951 1951 '''clone main and patch repository at same time
1952 1952
1953 1953 If source is local, destination will have no patches applied. If
1954 1954 source is remote, this command can not check if patches are
1955 1955 applied in source, so cannot guarantee that patches are not
1956 1956 applied in destination. If you clone remote repository, be sure
1957 1957 before that it has no patches applied.
1958 1958
1959 1959 Source patch repository is looked for in <src>/.hg/patches by
1960 1960 default. Use -p <url> to change.
1961 1961
1962 1962 The patch directory must be a nested Mercurial repository, as
1963 1963 would be created by :hg:`init --mq`.
1964 1964
1965 1965 Return 0 on success.
1966 1966 '''
1967 1967 def patchdir(repo):
1968 1968 url = repo.url()
1969 1969 if url.endswith('/'):
1970 1970 url = url[:-1]
1971 1971 return url + '/.hg/patches'
1972 1972 if dest is None:
1973 1973 dest = hg.defaultdest(source)
1974 1974 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1975 1975 if opts.get('patches'):
1976 1976 patchespath = ui.expandpath(opts.get('patches'))
1977 1977 else:
1978 1978 patchespath = patchdir(sr)
1979 1979 try:
1980 1980 hg.repository(ui, patchespath)
1981 1981 except error.RepoError:
1982 1982 raise util.Abort(_('versioned patch repository not found'
1983 1983 ' (see init --mq)'))
1984 1984 qbase, destrev = None, None
1985 1985 if sr.local():
1986 1986 if sr.mq.applied:
1987 1987 qbase = sr.mq.applied[0].node
1988 1988 if not hg.islocal(dest):
1989 1989 heads = set(sr.heads())
1990 1990 destrev = list(heads.difference(sr.heads(qbase)))
1991 1991 destrev.append(sr.changelog.parents(qbase)[0])
1992 1992 elif sr.capable('lookup'):
1993 1993 try:
1994 1994 qbase = sr.lookup('qbase')
1995 1995 except error.RepoError:
1996 1996 pass
1997 1997 ui.note(_('cloning main repository\n'))
1998 1998 sr, dr = hg.clone(ui, sr.url(), dest,
1999 1999 pull=opts.get('pull'),
2000 2000 rev=destrev,
2001 2001 update=False,
2002 2002 stream=opts.get('uncompressed'))
2003 2003 ui.note(_('cloning patch repository\n'))
2004 2004 hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
2005 2005 pull=opts.get('pull'), update=not opts.get('noupdate'),
2006 2006 stream=opts.get('uncompressed'))
2007 2007 if dr.local():
2008 2008 if qbase:
2009 2009 ui.note(_('stripping applied patches from destination '
2010 2010 'repository\n'))
2011 2011 dr.mq.strip(dr, [qbase], update=False, backup=None)
2012 2012 if not opts.get('noupdate'):
2013 2013 ui.note(_('updating destination repository\n'))
2014 2014 hg.update(dr, dr.changelog.tip())
2015 2015
2016 2016 def commit(ui, repo, *pats, **opts):
2017 2017 """commit changes in the queue repository (DEPRECATED)
2018 2018
2019 2019 This command is deprecated; use :hg:`commit --mq` instead."""
2020 2020 q = repo.mq
2021 2021 r = q.qrepo()
2022 2022 if not r:
2023 2023 raise util.Abort('no queue repository')
2024 2024 commands.commit(r.ui, r, *pats, **opts)
2025 2025
2026 2026 def series(ui, repo, **opts):
2027 2027 """print the entire series file
2028 2028
2029 2029 Returns 0 on success."""
2030 2030 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2031 2031 return 0
2032 2032
2033 2033 def top(ui, repo, **opts):
2034 2034 """print the name of the current patch
2035 2035
2036 2036 Returns 0 on success."""
2037 2037 q = repo.mq
2038 2038 t = q.applied and q.series_end(True) or 0
2039 2039 if t:
2040 2040 q.qseries(repo, start=t - 1, length=1, status='A',
2041 2041 summary=opts.get('summary'))
2042 2042 else:
2043 2043 ui.write(_("no patches applied\n"))
2044 2044 return 1
2045 2045
2046 2046 def next(ui, repo, **opts):
2047 2047 """print the name of the next patch
2048 2048
2049 2049 Returns 0 on success."""
2050 2050 q = repo.mq
2051 2051 end = q.series_end()
2052 2052 if end == len(q.series):
2053 2053 ui.write(_("all patches applied\n"))
2054 2054 return 1
2055 2055 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2056 2056
2057 2057 def prev(ui, repo, **opts):
2058 2058 """print the name of the previous patch
2059 2059
2060 2060 Returns 0 on success."""
2061 2061 q = repo.mq
2062 2062 l = len(q.applied)
2063 2063 if l == 1:
2064 2064 ui.write(_("only one patch applied\n"))
2065 2065 return 1
2066 2066 if not l:
2067 2067 ui.write(_("no patches applied\n"))
2068 2068 return 1
2069 2069 q.qseries(repo, start=l - 2, length=1, status='A',
2070 2070 summary=opts.get('summary'))
2071 2071
2072 2072 def setupheaderopts(ui, opts):
2073 2073 if not opts.get('user') and opts.get('currentuser'):
2074 2074 opts['user'] = ui.username()
2075 2075 if not opts.get('date') and opts.get('currentdate'):
2076 2076 opts['date'] = "%d %d" % util.makedate()
2077 2077
2078 2078 def new(ui, repo, patch, *args, **opts):
2079 2079 """create a new patch
2080 2080
2081 2081 qnew creates a new patch on top of the currently-applied patch (if
2082 2082 any). The patch will be initialized with any outstanding changes
2083 2083 in the working directory. You may also use -I/--include,
2084 2084 -X/--exclude, and/or a list of files after the patch name to add
2085 2085 only changes to matching files to the new patch, leaving the rest
2086 2086 as uncommitted modifications.
2087 2087
2088 2088 -u/--user and -d/--date can be used to set the (given) user and
2089 2089 date, respectively. -U/--currentuser and -D/--currentdate set user
2090 2090 to current user and date to current date.
2091 2091
2092 2092 -e/--edit, -m/--message or -l/--logfile set the patch header as
2093 2093 well as the commit message. If none is specified, the header is
2094 2094 empty and the commit message is '[mq]: PATCH'.
2095 2095
2096 2096 Use the -g/--git option to keep the patch in the git extended diff
2097 2097 format. Read the diffs help topic for more information on why this
2098 2098 is important for preserving permission changes and copy/rename
2099 2099 information.
2100 2100
2101 2101 Returns 0 on successful creation of a new patch.
2102 2102 """
2103 2103 msg = cmdutil.logmessage(opts)
2104 2104 def getmsg():
2105 2105 return ui.edit(msg, opts.get('user') or ui.username())
2106 2106 q = repo.mq
2107 2107 opts['msg'] = msg
2108 2108 if opts.get('edit'):
2109 2109 opts['msg'] = getmsg
2110 2110 else:
2111 2111 opts['msg'] = msg
2112 2112 setupheaderopts(ui, opts)
2113 2113 q.new(repo, patch, *args, **opts)
2114 2114 q.save_dirty()
2115 2115 return 0
2116 2116
2117 2117 def refresh(ui, repo, *pats, **opts):
2118 2118 """update the current patch
2119 2119
2120 2120 If any file patterns are provided, the refreshed patch will
2121 2121 contain only the modifications that match those patterns; the
2122 2122 remaining modifications will remain in the working directory.
2123 2123
2124 2124 If -s/--short is specified, files currently included in the patch
2125 2125 will be refreshed just like matched files and remain in the patch.
2126 2126
2127 2127 If -e/--edit is specified, Mercurial will start your configured editor for
2128 2128 you to enter a message. In case qrefresh fails, you will find a backup of
2129 2129 your message in ``.hg/last-message.txt``.
2130 2130
2131 2131 hg add/remove/copy/rename work as usual, though you might want to
2132 2132 use git-style patches (-g/--git or [diff] git=1) to track copies
2133 2133 and renames. See the diffs help topic for more information on the
2134 2134 git diff format.
2135 2135
2136 2136 Returns 0 on success.
2137 2137 """
2138 2138 q = repo.mq
2139 2139 message = cmdutil.logmessage(opts)
2140 2140 if opts.get('edit'):
2141 2141 if not q.applied:
2142 2142 ui.write(_("no patches applied\n"))
2143 2143 return 1
2144 2144 if message:
2145 2145 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2146 2146 patch = q.applied[-1].name
2147 2147 ph = patchheader(q.join(patch), q.plainmode)
2148 2148 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2149 2149 # We don't want to lose the patch message if qrefresh fails (issue2062)
2150 2150 msgfile = repo.opener('last-message.txt', 'wb')
2151 2151 msgfile.write(message)
2152 2152 msgfile.close()
2153 2153 setupheaderopts(ui, opts)
2154 2154 ret = q.refresh(repo, pats, msg=message, **opts)
2155 2155 q.save_dirty()
2156 2156 return ret
2157 2157
2158 2158 def diff(ui, repo, *pats, **opts):
2159 2159 """diff of the current patch and subsequent modifications
2160 2160
2161 2161 Shows a diff which includes the current patch as well as any
2162 2162 changes which have been made in the working directory since the
2163 2163 last refresh (thus showing what the current patch would become
2164 2164 after a qrefresh).
2165 2165
2166 2166 Use :hg:`diff` if you only want to see the changes made since the
2167 2167 last qrefresh, or :hg:`export qtip` if you want to see changes
2168 2168 made by the current patch without including changes made since the
2169 2169 qrefresh.
2170 2170
2171 2171 Returns 0 on success.
2172 2172 """
2173 2173 repo.mq.diff(repo, pats, opts)
2174 2174 return 0
2175 2175
2176 2176 def fold(ui, repo, *files, **opts):
2177 2177 """fold the named patches into the current patch
2178 2178
2179 2179 Patches must not yet be applied. Each patch will be successively
2180 2180 applied to the current patch in the order given. If all the
2181 2181 patches apply successfully, the current patch will be refreshed
2182 2182 with the new cumulative patch, and the folded patches will be
2183 2183 deleted. With -k/--keep, the folded patch files will not be
2184 2184 removed afterwards.
2185 2185
2186 2186 The header for each folded patch will be concatenated with the
2187 2187 current patch header, separated by a line of ``* * *``.
2188 2188
2189 2189 Returns 0 on success."""
2190 2190
2191 2191 q = repo.mq
2192 2192
2193 2193 if not files:
2194 2194 raise util.Abort(_('qfold requires at least one patch name'))
2195 2195 if not q.check_toppatch(repo)[0]:
2196 2196 raise util.Abort(_('no patches applied'))
2197 2197 q.check_localchanges(repo)
2198 2198
2199 2199 message = cmdutil.logmessage(opts)
2200 2200 if opts.get('edit'):
2201 2201 if message:
2202 2202 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2203 2203
2204 2204 parent = q.lookup('qtip')
2205 2205 patches = []
2206 2206 messages = []
2207 2207 for f in files:
2208 2208 p = q.lookup(f)
2209 2209 if p in patches or p == parent:
2210 2210 ui.warn(_('Skipping already folded patch %s\n') % p)
2211 2211 if q.isapplied(p):
2212 2212 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2213 2213 patches.append(p)
2214 2214
2215 2215 for p in patches:
2216 2216 if not message:
2217 2217 ph = patchheader(q.join(p), q.plainmode)
2218 2218 if ph.message:
2219 2219 messages.append(ph.message)
2220 2220 pf = q.join(p)
2221 2221 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2222 2222 if not patchsuccess:
2223 2223 raise util.Abort(_('error folding patch %s') % p)
2224 2224 cmdutil.updatedir(ui, repo, files)
2225 2225
2226 2226 if not message:
2227 2227 ph = patchheader(q.join(parent), q.plainmode)
2228 2228 message, user = ph.message, ph.user
2229 2229 for msg in messages:
2230 2230 message.append('* * *')
2231 2231 message.extend(msg)
2232 2232 message = '\n'.join(message)
2233 2233
2234 2234 if opts.get('edit'):
2235 2235 message = ui.edit(message, user or ui.username())
2236 2236
2237 2237 diffopts = q.patchopts(q.diffopts(), *patches)
2238 2238 q.refresh(repo, msg=message, git=diffopts.git)
2239 2239 q.delete(repo, patches, opts)
2240 2240 q.save_dirty()
2241 2241
2242 2242 def goto(ui, repo, patch, **opts):
2243 2243 '''push or pop patches until named patch is at top of stack
2244 2244
2245 2245 Returns 0 on success.'''
2246 2246 q = repo.mq
2247 2247 patch = q.lookup(patch)
2248 2248 if q.isapplied(patch):
2249 2249 ret = q.pop(repo, patch, force=opts.get('force'))
2250 2250 else:
2251 2251 ret = q.push(repo, patch, force=opts.get('force'))
2252 2252 q.save_dirty()
2253 2253 return ret
2254 2254
2255 2255 def guard(ui, repo, *args, **opts):
2256 2256 '''set or print guards for a patch
2257 2257
2258 2258 Guards control whether a patch can be pushed. A patch with no
2259 2259 guards is always pushed. A patch with a positive guard ("+foo") is
2260 2260 pushed only if the :hg:`qselect` command has activated it. A patch with
2261 2261 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2262 2262 has activated it.
2263 2263
2264 2264 With no arguments, print the currently active guards.
2265 2265 With arguments, set guards for the named patch.
2266 2266
2267 2267 .. note::
2268 2268 Specifying negative guards now requires '--'.
2269 2269
2270 2270 To set guards on another patch::
2271 2271
2272 2272 hg qguard other.patch -- +2.6.17 -stable
2273 2273
2274 2274 Returns 0 on success.
2275 2275 '''
2276 2276 def status(idx):
2277 2277 guards = q.series_guards[idx] or ['unguarded']
2278 2278 if q.series[idx] in applied:
2279 2279 state = 'applied'
2280 2280 elif q.pushable(idx)[0]:
2281 2281 state = 'unapplied'
2282 2282 else:
2283 2283 state = 'guarded'
2284 2284 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2285 2285 ui.write('%s: ' % ui.label(q.series[idx], label))
2286 2286
2287 2287 for i, guard in enumerate(guards):
2288 2288 if guard.startswith('+'):
2289 2289 ui.write(guard, label='qguard.positive')
2290 2290 elif guard.startswith('-'):
2291 2291 ui.write(guard, label='qguard.negative')
2292 2292 else:
2293 2293 ui.write(guard, label='qguard.unguarded')
2294 2294 if i != len(guards) - 1:
2295 2295 ui.write(' ')
2296 2296 ui.write('\n')
2297 2297 q = repo.mq
2298 2298 applied = set(p.name for p in q.applied)
2299 2299 patch = None
2300 2300 args = list(args)
2301 2301 if opts.get('list'):
2302 2302 if args or opts.get('none'):
2303 2303 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2304 2304 for i in xrange(len(q.series)):
2305 2305 status(i)
2306 2306 return
2307 2307 if not args or args[0][0:1] in '-+':
2308 2308 if not q.applied:
2309 2309 raise util.Abort(_('no patches applied'))
2310 2310 patch = q.applied[-1].name
2311 2311 if patch is None and args[0][0:1] not in '-+':
2312 2312 patch = args.pop(0)
2313 2313 if patch is None:
2314 2314 raise util.Abort(_('no patch to work with'))
2315 2315 if args or opts.get('none'):
2316 2316 idx = q.find_series(patch)
2317 2317 if idx is None:
2318 2318 raise util.Abort(_('no patch named %s') % patch)
2319 2319 q.set_guards(idx, args)
2320 2320 q.save_dirty()
2321 2321 else:
2322 2322 status(q.series.index(q.lookup(patch)))
2323 2323
2324 2324 def header(ui, repo, patch=None):
2325 2325 """print the header of the topmost or specified patch
2326 2326
2327 2327 Returns 0 on success."""
2328 2328 q = repo.mq
2329 2329
2330 2330 if patch:
2331 2331 patch = q.lookup(patch)
2332 2332 else:
2333 2333 if not q.applied:
2334 2334 ui.write(_('no patches applied\n'))
2335 2335 return 1
2336 2336 patch = q.lookup('qtip')
2337 2337 ph = patchheader(q.join(patch), q.plainmode)
2338 2338
2339 2339 ui.write('\n'.join(ph.message) + '\n')
2340 2340
2341 2341 def lastsavename(path):
2342 2342 (directory, base) = os.path.split(path)
2343 2343 names = os.listdir(directory)
2344 2344 namere = re.compile("%s.([0-9]+)" % base)
2345 2345 maxindex = None
2346 2346 maxname = None
2347 2347 for f in names:
2348 2348 m = namere.match(f)
2349 2349 if m:
2350 2350 index = int(m.group(1))
2351 2351 if maxindex is None or index > maxindex:
2352 2352 maxindex = index
2353 2353 maxname = f
2354 2354 if maxname:
2355 2355 return (os.path.join(directory, maxname), maxindex)
2356 2356 return (None, None)
2357 2357
2358 2358 def savename(path):
2359 2359 (last, index) = lastsavename(path)
2360 2360 if last is None:
2361 2361 index = 0
2362 2362 newpath = path + ".%d" % (index + 1)
2363 2363 return newpath
2364 2364
2365 2365 def push(ui, repo, patch=None, **opts):
2366 2366 """push the next patch onto the stack
2367 2367
2368 2368 When -f/--force is applied, all local changes in patched files
2369 2369 will be lost.
2370 2370
2371 2371 Return 0 on succces.
2372 2372 """
2373 2373 q = repo.mq
2374 2374 mergeq = None
2375 2375
2376 2376 if opts.get('merge'):
2377 2377 if opts.get('name'):
2378 2378 newpath = repo.join(opts.get('name'))
2379 2379 else:
2380 2380 newpath, i = lastsavename(q.path)
2381 2381 if not newpath:
2382 2382 ui.warn(_("no saved queues found, please use -n\n"))
2383 2383 return 1
2384 2384 mergeq = queue(ui, repo.join(""), newpath)
2385 2385 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2386 2386 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2387 2387 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2388 2388 exact=opts.get('exact'))
2389 2389 return ret
2390 2390
2391 2391 def pop(ui, repo, patch=None, **opts):
2392 2392 """pop the current patch off the stack
2393 2393
2394 2394 By default, pops off the top of the patch stack. If given a patch
2395 2395 name, keeps popping off patches until the named patch is at the
2396 2396 top of the stack.
2397 2397
2398 2398 Return 0 on success.
2399 2399 """
2400 2400 localupdate = True
2401 2401 if opts.get('name'):
2402 2402 q = queue(ui, repo.join(""), repo.join(opts.get('name')))
2403 2403 ui.warn(_('using patch queue: %s\n') % q.path)
2404 2404 localupdate = False
2405 2405 else:
2406 2406 q = repo.mq
2407 2407 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2408 2408 all=opts.get('all'))
2409 2409 q.save_dirty()
2410 2410 return ret
2411 2411
2412 2412 def rename(ui, repo, patch, name=None, **opts):
2413 2413 """rename a patch
2414 2414
2415 2415 With one argument, renames the current patch to PATCH1.
2416 2416 With two arguments, renames PATCH1 to PATCH2.
2417 2417
2418 2418 Returns 0 on success."""
2419 2419
2420 2420 q = repo.mq
2421 2421
2422 2422 if not name:
2423 2423 name = patch
2424 2424 patch = None
2425 2425
2426 2426 if patch:
2427 2427 patch = q.lookup(patch)
2428 2428 else:
2429 2429 if not q.applied:
2430 2430 ui.write(_('no patches applied\n'))
2431 2431 return
2432 2432 patch = q.lookup('qtip')
2433 2433 absdest = q.join(name)
2434 2434 if os.path.isdir(absdest):
2435 2435 name = normname(os.path.join(name, os.path.basename(patch)))
2436 2436 absdest = q.join(name)
2437 2437 if os.path.exists(absdest):
2438 2438 raise util.Abort(_('%s already exists') % absdest)
2439 2439
2440 2440 if name in q.series:
2441 2441 raise util.Abort(
2442 2442 _('A patch named %s already exists in the series file') % name)
2443 2443
2444 2444 ui.note(_('renaming %s to %s\n') % (patch, name))
2445 2445 i = q.find_series(patch)
2446 2446 guards = q.guard_re.findall(q.full_series[i])
2447 2447 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2448 2448 q.parse_series()
2449 2449 q.series_dirty = 1
2450 2450
2451 2451 info = q.isapplied(patch)
2452 2452 if info:
2453 2453 q.applied[info[0]] = statusentry(info[1], name)
2454 2454 q.applied_dirty = 1
2455 2455
2456 2456 destdir = os.path.dirname(absdest)
2457 2457 if not os.path.isdir(destdir):
2458 2458 os.makedirs(destdir)
2459 2459 util.rename(q.join(patch), absdest)
2460 2460 r = q.qrepo()
2461 2461 if r and patch in r.dirstate:
2462 2462 wctx = r[None]
2463 2463 wlock = r.wlock()
2464 2464 try:
2465 2465 if r.dirstate[patch] == 'a':
2466 2466 r.dirstate.forget(patch)
2467 2467 r.dirstate.add(name)
2468 2468 else:
2469 2469 if r.dirstate[name] == 'r':
2470 2470 wctx.undelete([name])
2471 2471 wctx.copy(patch, name)
2472 2472 wctx.remove([patch], False)
2473 2473 finally:
2474 2474 wlock.release()
2475 2475
2476 2476 q.save_dirty()
2477 2477
2478 2478 def restore(ui, repo, rev, **opts):
2479 2479 """restore the queue state saved by a revision (DEPRECATED)
2480 2480
2481 2481 This command is deprecated, use :hg:`rebase` instead."""
2482 2482 rev = repo.lookup(rev)
2483 2483 q = repo.mq
2484 2484 q.restore(repo, rev, delete=opts.get('delete'),
2485 2485 qupdate=opts.get('update'))
2486 2486 q.save_dirty()
2487 2487 return 0
2488 2488
2489 2489 def save(ui, repo, **opts):
2490 2490 """save current queue state (DEPRECATED)
2491 2491
2492 2492 This command is deprecated, use :hg:`rebase` instead."""
2493 2493 q = repo.mq
2494 2494 message = cmdutil.logmessage(opts)
2495 2495 ret = q.save(repo, msg=message)
2496 2496 if ret:
2497 2497 return ret
2498 2498 q.save_dirty()
2499 2499 if opts.get('copy'):
2500 2500 path = q.path
2501 2501 if opts.get('name'):
2502 2502 newpath = os.path.join(q.basepath, opts.get('name'))
2503 2503 if os.path.exists(newpath):
2504 2504 if not os.path.isdir(newpath):
2505 2505 raise util.Abort(_('destination %s exists and is not '
2506 2506 'a directory') % newpath)
2507 2507 if not opts.get('force'):
2508 2508 raise util.Abort(_('destination %s exists, '
2509 2509 'use -f to force') % newpath)
2510 2510 else:
2511 2511 newpath = savename(path)
2512 2512 ui.warn(_("copy %s to %s\n") % (path, newpath))
2513 2513 util.copyfiles(path, newpath)
2514 2514 if opts.get('empty'):
2515 2515 try:
2516 2516 os.unlink(q.join(q.status_path))
2517 2517 except:
2518 2518 pass
2519 2519 return 0
2520 2520
2521 2521 def strip(ui, repo, *revs, **opts):
2522 2522 """strip changesets and all their descendants from the repository
2523 2523
2524 2524 The strip command removes the specified changesets and all their
2525 2525 descendants. If the working directory has uncommitted changes,
2526 2526 the operation is aborted unless the --force flag is supplied.
2527 2527
2528 2528 If a parent of the working directory is stripped, then the working
2529 2529 directory will automatically be updated to the most recent
2530 2530 available ancestor of the stripped parent after the operation
2531 2531 completes.
2532 2532
2533 2533 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2534 2534 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2535 2535 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2536 2536 where BUNDLE is the bundle file created by the strip. Note that
2537 2537 the local revision numbers will in general be different after the
2538 2538 restore.
2539 2539
2540 2540 Use the --no-backup option to discard the backup bundle once the
2541 2541 operation completes.
2542 2542
2543 2543 Return 0 on success.
2544 2544 """
2545 2545 backup = 'all'
2546 2546 if opts.get('backup'):
2547 2547 backup = 'strip'
2548 2548 elif opts.get('no_backup') or opts.get('nobackup'):
2549 2549 backup = 'none'
2550 2550
2551 2551 cl = repo.changelog
2552 2552 revs = set(cmdutil.revrange(repo, revs))
2553 2553 if not revs:
2554 2554 raise util.Abort(_('empty revision set'))
2555 2555
2556 2556 descendants = set(cl.descendants(*revs))
2557 2557 strippedrevs = revs.union(descendants)
2558 2558 roots = revs.difference(descendants)
2559 2559
2560 2560 update = False
2561 2561 # if one of the wdir parent is stripped we'll need
2562 2562 # to update away to an earlier revision
2563 2563 for p in repo.dirstate.parents():
2564 2564 if p != nullid and cl.rev(p) in strippedrevs:
2565 2565 update = True
2566 2566 break
2567 2567
2568 2568 rootnodes = set(cl.node(r) for r in roots)
2569 2569
2570 2570 q = repo.mq
2571 2571 if q.applied:
2572 2572 # refresh queue state if we're about to strip
2573 2573 # applied patches
2574 2574 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2575 2575 q.applied_dirty = True
2576 2576 start = 0
2577 2577 end = len(q.applied)
2578 2578 for i, statusentry in enumerate(q.applied):
2579 2579 if statusentry.node in rootnodes:
2580 2580 # if one of the stripped roots is an applied
2581 2581 # patch, only part of the queue is stripped
2582 2582 start = i
2583 2583 break
2584 2584 del q.applied[start:end]
2585 2585 q.save_dirty()
2586 2586
2587 2587 revs = list(rootnodes)
2588 2588 if update and opts.get('keep'):
2589 2589 wlock = repo.wlock()
2590 2590 try:
2591 2591 urev = repo.mq.qparents(repo, revs[0])
2592 2592 repo.dirstate.rebuild(urev, repo[urev].manifest())
2593 2593 repo.dirstate.write()
2594 2594 update = False
2595 2595 finally:
2596 2596 wlock.release()
2597 2597
2598 2598 repo.mq.strip(repo, revs, backup=backup, update=update,
2599 2599 force=opts.get('force'))
2600 2600 return 0
2601 2601
2602 2602 def select(ui, repo, *args, **opts):
2603 2603 '''set or print guarded patches to push
2604 2604
2605 2605 Use the :hg:`qguard` command to set or print guards on patch, then use
2606 2606 qselect to tell mq which guards to use. A patch will be pushed if
2607 2607 it has no guards or any positive guards match the currently
2608 2608 selected guard, but will not be pushed if any negative guards
2609 2609 match the current guard. For example::
2610 2610
2611 2611 qguard foo.patch -stable (negative guard)
2612 2612 qguard bar.patch +stable (positive guard)
2613 2613 qselect stable
2614 2614
2615 2615 This activates the "stable" guard. mq will skip foo.patch (because
2616 2616 it has a negative match) but push bar.patch (because it has a
2617 2617 positive match).
2618 2618
2619 2619 With no arguments, prints the currently active guards.
2620 2620 With one argument, sets the active guard.
2621 2621
2622 2622 Use -n/--none to deactivate guards (no other arguments needed).
2623 2623 When no guards are active, patches with positive guards are
2624 2624 skipped and patches with negative guards are pushed.
2625 2625
2626 2626 qselect can change the guards on applied patches. It does not pop
2627 2627 guarded patches by default. Use --pop to pop back to the last
2628 2628 applied patch that is not guarded. Use --reapply (which implies
2629 2629 --pop) to push back to the current patch afterwards, but skip
2630 2630 guarded patches.
2631 2631
2632 2632 Use -s/--series to print a list of all guards in the series file
2633 2633 (no other arguments needed). Use -v for more information.
2634 2634
2635 2635 Returns 0 on success.'''
2636 2636
2637 2637 q = repo.mq
2638 2638 guards = q.active()
2639 2639 if args or opts.get('none'):
2640 2640 old_unapplied = q.unapplied(repo)
2641 2641 old_guarded = [i for i in xrange(len(q.applied)) if
2642 2642 not q.pushable(i)[0]]
2643 2643 q.set_active(args)
2644 2644 q.save_dirty()
2645 2645 if not args:
2646 2646 ui.status(_('guards deactivated\n'))
2647 2647 if not opts.get('pop') and not opts.get('reapply'):
2648 2648 unapplied = q.unapplied(repo)
2649 2649 guarded = [i for i in xrange(len(q.applied))
2650 2650 if not q.pushable(i)[0]]
2651 2651 if len(unapplied) != len(old_unapplied):
2652 2652 ui.status(_('number of unguarded, unapplied patches has '
2653 2653 'changed from %d to %d\n') %
2654 2654 (len(old_unapplied), len(unapplied)))
2655 2655 if len(guarded) != len(old_guarded):
2656 2656 ui.status(_('number of guarded, applied patches has changed '
2657 2657 'from %d to %d\n') %
2658 2658 (len(old_guarded), len(guarded)))
2659 2659 elif opts.get('series'):
2660 2660 guards = {}
2661 2661 noguards = 0
2662 2662 for gs in q.series_guards:
2663 2663 if not gs:
2664 2664 noguards += 1
2665 2665 for g in gs:
2666 2666 guards.setdefault(g, 0)
2667 2667 guards[g] += 1
2668 2668 if ui.verbose:
2669 2669 guards['NONE'] = noguards
2670 2670 guards = guards.items()
2671 2671 guards.sort(key=lambda x: x[0][1:])
2672 2672 if guards:
2673 2673 ui.note(_('guards in series file:\n'))
2674 2674 for guard, count in guards:
2675 2675 ui.note('%2d ' % count)
2676 2676 ui.write(guard, '\n')
2677 2677 else:
2678 2678 ui.note(_('no guards in series file\n'))
2679 2679 else:
2680 2680 if guards:
2681 2681 ui.note(_('active guards:\n'))
2682 2682 for g in guards:
2683 2683 ui.write(g, '\n')
2684 2684 else:
2685 2685 ui.write(_('no active guards\n'))
2686 2686 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2687 2687 popped = False
2688 2688 if opts.get('pop') or opts.get('reapply'):
2689 2689 for i in xrange(len(q.applied)):
2690 2690 pushable, reason = q.pushable(i)
2691 2691 if not pushable:
2692 2692 ui.status(_('popping guarded patches\n'))
2693 2693 popped = True
2694 2694 if i == 0:
2695 2695 q.pop(repo, all=True)
2696 2696 else:
2697 2697 q.pop(repo, i - 1)
2698 2698 break
2699 2699 if popped:
2700 2700 try:
2701 2701 if reapply:
2702 2702 ui.status(_('reapplying unguarded patches\n'))
2703 2703 q.push(repo, reapply)
2704 2704 finally:
2705 2705 q.save_dirty()
2706 2706
2707 2707 def finish(ui, repo, *revrange, **opts):
2708 2708 """move applied patches into repository history
2709 2709
2710 2710 Finishes the specified revisions (corresponding to applied
2711 2711 patches) by moving them out of mq control into regular repository
2712 2712 history.
2713 2713
2714 2714 Accepts a revision range or the -a/--applied option. If --applied
2715 2715 is specified, all applied mq revisions are removed from mq
2716 2716 control. Otherwise, the given revisions must be at the base of the
2717 2717 stack of applied patches.
2718 2718
2719 2719 This can be especially useful if your changes have been applied to
2720 2720 an upstream repository, or if you are about to push your changes
2721 2721 to upstream.
2722 2722
2723 2723 Returns 0 on success.
2724 2724 """
2725 2725 if not opts.get('applied') and not revrange:
2726 2726 raise util.Abort(_('no revisions specified'))
2727 2727 elif opts.get('applied'):
2728 2728 revrange = ('qbase::qtip',) + revrange
2729 2729
2730 2730 q = repo.mq
2731 2731 if not q.applied:
2732 2732 ui.status(_('no patches applied\n'))
2733 2733 return 0
2734 2734
2735 2735 revs = cmdutil.revrange(repo, revrange)
2736 2736 q.finish(repo, revs)
2737 2737 q.save_dirty()
2738 2738 return 0
2739 2739
2740 2740 def qqueue(ui, repo, name=None, **opts):
2741 2741 '''manage multiple patch queues
2742 2742
2743 2743 Supports switching between different patch queues, as well as creating
2744 2744 new patch queues and deleting existing ones.
2745 2745
2746 2746 Omitting a queue name or specifying -l/--list will show you the registered
2747 2747 queues - by default the "normal" patches queue is registered. The currently
2748 2748 active queue will be marked with "(active)".
2749 2749
2750 2750 To create a new queue, use -c/--create. The queue is automatically made
2751 2751 active, except in the case where there are applied patches from the
2752 2752 currently active queue in the repository. Then the queue will only be
2753 2753 created and switching will fail.
2754 2754
2755 2755 To delete an existing queue, use --delete. You cannot delete the currently
2756 2756 active queue.
2757 2757
2758 2758 Returns 0 on success.
2759 2759 '''
2760 2760
2761 2761 q = repo.mq
2762 2762
2763 2763 _defaultqueue = 'patches'
2764 2764 _allqueues = 'patches.queues'
2765 2765 _activequeue = 'patches.queue'
2766 2766
2767 2767 def _getcurrent():
2768 2768 cur = os.path.basename(q.path)
2769 2769 if cur.startswith('patches-'):
2770 2770 cur = cur[8:]
2771 2771 return cur
2772 2772
2773 2773 def _noqueues():
2774 2774 try:
2775 2775 fh = repo.opener(_allqueues, 'r')
2776 2776 fh.close()
2777 2777 except IOError:
2778 2778 return True
2779 2779
2780 2780 return False
2781 2781
2782 2782 def _getqueues():
2783 2783 current = _getcurrent()
2784 2784
2785 2785 try:
2786 2786 fh = repo.opener(_allqueues, 'r')
2787 2787 queues = [queue.strip() for queue in fh if queue.strip()]
2788 2788 if current not in queues:
2789 2789 queues.append(current)
2790 2790 except IOError:
2791 2791 queues = [_defaultqueue]
2792 2792
2793 2793 return sorted(queues)
2794 2794
2795 2795 def _setactive(name):
2796 2796 if q.applied:
2797 2797 raise util.Abort(_('patches applied - cannot set new queue active'))
2798 2798 _setactivenocheck(name)
2799 2799
2800 2800 def _setactivenocheck(name):
2801 2801 fh = repo.opener(_activequeue, 'w')
2802 2802 if name != 'patches':
2803 2803 fh.write(name)
2804 2804 fh.close()
2805 2805
2806 2806 def _addqueue(name):
2807 2807 fh = repo.opener(_allqueues, 'a')
2808 2808 fh.write('%s\n' % (name,))
2809 2809 fh.close()
2810 2810
2811 2811 def _queuedir(name):
2812 2812 if name == 'patches':
2813 2813 return repo.join('patches')
2814 2814 else:
2815 2815 return repo.join('patches-' + name)
2816 2816
2817 2817 def _validname(name):
2818 2818 for n in name:
2819 2819 if n in ':\\/.':
2820 2820 return False
2821 2821 return True
2822 2822
2823 2823 def _delete(name):
2824 2824 if name not in existing:
2825 2825 raise util.Abort(_('cannot delete queue that does not exist'))
2826 2826
2827 2827 current = _getcurrent()
2828 2828
2829 2829 if name == current:
2830 2830 raise util.Abort(_('cannot delete currently active queue'))
2831 2831
2832 2832 fh = repo.opener('patches.queues.new', 'w')
2833 2833 for queue in existing:
2834 2834 if queue == name:
2835 2835 continue
2836 2836 fh.write('%s\n' % (queue,))
2837 2837 fh.close()
2838 2838 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2839 2839
2840 2840 if not name or opts.get('list'):
2841 2841 current = _getcurrent()
2842 2842 for queue in _getqueues():
2843 2843 ui.write('%s' % (queue,))
2844 2844 if queue == current and not ui.quiet:
2845 2845 ui.write(_(' (active)\n'))
2846 2846 else:
2847 2847 ui.write('\n')
2848 2848 return
2849 2849
2850 2850 if not _validname(name):
2851 2851 raise util.Abort(
2852 2852 _('invalid queue name, may not contain the characters ":\\/."'))
2853 2853
2854 2854 existing = _getqueues()
2855 2855
2856 2856 if opts.get('create'):
2857 2857 if name in existing:
2858 2858 raise util.Abort(_('queue "%s" already exists') % name)
2859 2859 if _noqueues():
2860 2860 _addqueue(_defaultqueue)
2861 2861 _addqueue(name)
2862 2862 _setactive(name)
2863 2863 elif opts.get('rename'):
2864 2864 current = _getcurrent()
2865 2865 if name == current:
2866 2866 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
2867 2867 if name in existing:
2868 2868 raise util.Abort(_('queue "%s" already exists') % name)
2869 2869
2870 2870 olddir = _queuedir(current)
2871 2871 newdir = _queuedir(name)
2872 2872
2873 2873 if os.path.exists(newdir):
2874 2874 raise util.Abort(_('non-queue directory "%s" already exists') %
2875 2875 newdir)
2876 2876
2877 2877 fh = repo.opener('patches.queues.new', 'w')
2878 2878 for queue in existing:
2879 2879 if queue == current:
2880 2880 fh.write('%s\n' % (name,))
2881 2881 if os.path.exists(olddir):
2882 2882 util.rename(olddir, newdir)
2883 2883 else:
2884 2884 fh.write('%s\n' % (queue,))
2885 2885 fh.close()
2886 2886 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2887 2887 _setactivenocheck(name)
2888 2888 elif opts.get('delete'):
2889 2889 _delete(name)
2890 2890 elif opts.get('purge'):
2891 2891 if name in existing:
2892 2892 _delete(name)
2893 2893 qdir = _queuedir(name)
2894 2894 if os.path.exists(qdir):
2895 2895 shutil.rmtree(qdir)
2896 2896 else:
2897 2897 if name not in existing:
2898 2898 raise util.Abort(_('use --create to create a new queue'))
2899 2899 _setactive(name)
2900 2900
2901 2901 def reposetup(ui, repo):
2902 2902 class mqrepo(repo.__class__):
2903 2903 @util.propertycache
2904 2904 def mq(self):
2905 2905 return queue(self.ui, self.join(""))
2906 2906
2907 2907 def abort_if_wdir_patched(self, errmsg, force=False):
2908 2908 if self.mq.applied and not force:
2909 2909 parent = self.dirstate.parents()[0]
2910 2910 if parent in [s.node for s in self.mq.applied]:
2911 2911 raise util.Abort(errmsg)
2912 2912
2913 2913 def commit(self, text="", user=None, date=None, match=None,
2914 2914 force=False, editor=False, extra={}):
2915 2915 self.abort_if_wdir_patched(
2916 2916 _('cannot commit over an applied mq patch'),
2917 2917 force)
2918 2918
2919 2919 return super(mqrepo, self).commit(text, user, date, match, force,
2920 2920 editor, extra)
2921 2921
2922 2922 def push(self, remote, force=False, revs=None, newbranch=False):
2923 2923 if self.mq.applied and not force:
2924 2924 haspatches = True
2925 2925 if revs:
2926 2926 # Assume applied patches have no non-patch descendants
2927 2927 # and are not on remote already. If they appear in the
2928 2928 # set of resolved 'revs', bail out.
2929 2929 applied = set(e.node for e in self.mq.applied)
2930 2930 haspatches = bool([n for n in revs if n in applied])
2931 2931 if haspatches:
2932 2932 raise util.Abort(_('source has mq patches applied'))
2933 2933 return super(mqrepo, self).push(remote, force, revs, newbranch)
2934 2934
2935 2935 def _findtags(self):
2936 2936 '''augment tags from base class with patch tags'''
2937 2937 result = super(mqrepo, self)._findtags()
2938 2938
2939 2939 q = self.mq
2940 2940 if not q.applied:
2941 2941 return result
2942 2942
2943 2943 mqtags = [(patch.node, patch.name) for patch in q.applied]
2944 2944
2945 2945 if mqtags[-1][0] not in self.changelog.nodemap:
2946 2946 self.ui.warn(_('mq status file refers to unknown node %s\n')
2947 2947 % short(mqtags[-1][0]))
2948 2948 return result
2949 2949
2950 2950 mqtags.append((mqtags[-1][0], 'qtip'))
2951 2951 mqtags.append((mqtags[0][0], 'qbase'))
2952 2952 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2953 2953 tags = result[0]
2954 2954 for patch in mqtags:
2955 2955 if patch[1] in tags:
2956 2956 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2957 2957 % patch[1])
2958 2958 else:
2959 2959 tags[patch[1]] = patch[0]
2960 2960
2961 2961 return result
2962 2962
2963 2963 def _branchtags(self, partial, lrev):
2964 2964 q = self.mq
2965 2965 if not q.applied:
2966 2966 return super(mqrepo, self)._branchtags(partial, lrev)
2967 2967
2968 2968 cl = self.changelog
2969 2969 qbasenode = q.applied[0].node
2970 2970 if qbasenode not in cl.nodemap:
2971 2971 self.ui.warn(_('mq status file refers to unknown node %s\n')
2972 2972 % short(qbasenode))
2973 2973 return super(mqrepo, self)._branchtags(partial, lrev)
2974 2974
2975 2975 qbase = cl.rev(qbasenode)
2976 2976 start = lrev + 1
2977 2977 if start < qbase:
2978 2978 # update the cache (excluding the patches) and save it
2979 2979 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2980 2980 self._updatebranchcache(partial, ctxgen)
2981 2981 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2982 2982 start = qbase
2983 2983 # if start = qbase, the cache is as updated as it should be.
2984 2984 # if start > qbase, the cache includes (part of) the patches.
2985 2985 # we might as well use it, but we won't save it.
2986 2986
2987 2987 # update the cache up to the tip
2988 2988 ctxgen = (self[r] for r in xrange(start, len(cl)))
2989 2989 self._updatebranchcache(partial, ctxgen)
2990 2990
2991 2991 return partial
2992 2992
2993 2993 if repo.local():
2994 2994 repo.__class__ = mqrepo
2995 2995
2996 2996 def mqimport(orig, ui, repo, *args, **kwargs):
2997 2997 if (hasattr(repo, 'abort_if_wdir_patched')
2998 2998 and not kwargs.get('no_commit', False)):
2999 2999 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
3000 3000 kwargs.get('force'))
3001 3001 return orig(ui, repo, *args, **kwargs)
3002 3002
3003 3003 def mqinit(orig, ui, *args, **kwargs):
3004 3004 mq = kwargs.pop('mq', None)
3005 3005
3006 3006 if not mq:
3007 3007 return orig(ui, *args, **kwargs)
3008 3008
3009 3009 if args:
3010 3010 repopath = args[0]
3011 3011 if not hg.islocal(repopath):
3012 3012 raise util.Abort(_('only a local queue repository '
3013 3013 'may be initialized'))
3014 3014 else:
3015 3015 repopath = cmdutil.findrepo(os.getcwd())
3016 3016 if not repopath:
3017 3017 raise util.Abort(_('there is no Mercurial repository here '
3018 3018 '(.hg not found)'))
3019 3019 repo = hg.repository(ui, repopath)
3020 3020 return qinit(ui, repo, True)
3021 3021
3022 3022 def mqcommand(orig, ui, repo, *args, **kwargs):
3023 3023 """Add --mq option to operate on patch repository instead of main"""
3024 3024
3025 3025 # some commands do not like getting unknown options
3026 3026 mq = kwargs.pop('mq', None)
3027 3027
3028 3028 if not mq:
3029 3029 return orig(ui, repo, *args, **kwargs)
3030 3030
3031 3031 q = repo.mq
3032 3032 r = q.qrepo()
3033 3033 if not r:
3034 3034 raise util.Abort(_('no queue repository'))
3035 3035 return orig(r.ui, r, *args, **kwargs)
3036 3036
3037 3037 def summary(orig, ui, repo, *args, **kwargs):
3038 3038 r = orig(ui, repo, *args, **kwargs)
3039 3039 q = repo.mq
3040 3040 m = []
3041 3041 a, u = len(q.applied), len(q.unapplied(repo))
3042 3042 if a:
3043 3043 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3044 3044 if u:
3045 3045 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3046 3046 if m:
3047 3047 ui.write("mq: %s\n" % ', '.join(m))
3048 3048 else:
3049 3049 ui.note(_("mq: (empty queue)\n"))
3050 3050 return r
3051 3051
3052 3052 def uisetup(ui):
3053 3053 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3054 3054
3055 3055 extensions.wrapcommand(commands.table, 'import', mqimport)
3056 3056 extensions.wrapcommand(commands.table, 'summary', summary)
3057 3057
3058 3058 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3059 3059 entry[1].extend(mqopt)
3060 3060
3061 3061 nowrap = set(commands.norepo.split(" ") + ['qrecord'])
3062 3062
3063 3063 def dotable(cmdtable):
3064 3064 for cmd in cmdtable.keys():
3065 3065 cmd = cmdutil.parsealiases(cmd)[0]
3066 3066 if cmd in nowrap:
3067 3067 continue
3068 3068 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3069 3069 entry[1].extend(mqopt)
3070 3070
3071 3071 dotable(commands.table)
3072 3072
3073 3073 for extname, extmodule in extensions.extensions():
3074 3074 if extmodule.__file__ != __file__:
3075 3075 dotable(getattr(extmodule, 'cmdtable', {}))
3076 3076
3077 3077 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
3078 3078
3079 3079 cmdtable = {
3080 3080 "qapplied":
3081 3081 (applied,
3082 3082 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
3083 3083 _('hg qapplied [-1] [-s] [PATCH]')),
3084 3084 "qclone":
3085 3085 (clone,
3086 3086 [('', 'pull', None, _('use pull protocol to copy metadata')),
3087 3087 ('U', 'noupdate', None, _('do not update the new working directories')),
3088 3088 ('', 'uncompressed', None,
3089 3089 _('use uncompressed transfer (fast over LAN)')),
3090 3090 ('p', 'patches', '',
3091 3091 _('location of source patch repository'), _('REPO')),
3092 3092 ] + commands.remoteopts,
3093 3093 _('hg qclone [OPTION]... SOURCE [DEST]')),
3094 3094 "qcommit|qci":
3095 3095 (commit,
3096 3096 commands.table["^commit|ci"][1],
3097 3097 _('hg qcommit [OPTION]... [FILE]...')),
3098 3098 "^qdiff":
3099 3099 (diff,
3100 3100 commands.diffopts + commands.diffopts2 + commands.walkopts,
3101 3101 _('hg qdiff [OPTION]... [FILE]...')),
3102 3102 "qdelete|qremove|qrm":
3103 3103 (delete,
3104 3104 [('k', 'keep', None, _('keep patch file')),
3105 3105 ('r', 'rev', [],
3106 3106 _('stop managing a revision (DEPRECATED)'), _('REV'))],
3107 3107 _('hg qdelete [-k] [PATCH]...')),
3108 3108 'qfold':
3109 3109 (fold,
3110 3110 [('e', 'edit', None, _('edit patch header')),
3111 3111 ('k', 'keep', None, _('keep folded patch files')),
3112 3112 ] + commands.commitopts,
3113 3113 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
3114 3114 'qgoto':
3115 3115 (goto,
3116 3116 [('f', 'force', None, _('overwrite any local changes'))],
3117 3117 _('hg qgoto [OPTION]... PATCH')),
3118 3118 'qguard':
3119 3119 (guard,
3120 3120 [('l', 'list', None, _('list all patches and guards')),
3121 3121 ('n', 'none', None, _('drop all guards'))],
3122 3122 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
3123 3123 'qheader': (header, [], _('hg qheader [PATCH]')),
3124 3124 "qimport":
3125 3125 (qimport,
3126 3126 [('e', 'existing', None, _('import file in patch directory')),
3127 3127 ('n', 'name', '',
3128 3128 _('name of patch file'), _('NAME')),
3129 3129 ('f', 'force', None, _('overwrite existing files')),
3130 3130 ('r', 'rev', [],
3131 3131 _('place existing revisions under mq control'), _('REV')),
3132 3132 ('g', 'git', None, _('use git extended diff format')),
3133 3133 ('P', 'push', None, _('qpush after importing'))],
3134 3134 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
3135 3135 "^qinit":
3136 3136 (init,
3137 3137 [('c', 'create-repo', None, _('create queue repository'))],
3138 3138 _('hg qinit [-c]')),
3139 3139 "^qnew":
3140 3140 (new,
3141 3141 [('e', 'edit', None, _('edit commit message')),
3142 3142 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
3143 3143 ('g', 'git', None, _('use git extended diff format')),
3144 3144 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
3145 3145 ('u', 'user', '',
3146 3146 _('add "From: <USER>" to patch'), _('USER')),
3147 3147 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
3148 3148 ('d', 'date', '',
3149 3149 _('add "Date: <DATE>" to patch'), _('DATE'))
3150 3150 ] + commands.walkopts + commands.commitopts,
3151 3151 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
3152 3152 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
3153 3153 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
3154 3154 "^qpop":
3155 3155 (pop,
3156 3156 [('a', 'all', None, _('pop all patches')),
3157 3157 ('n', 'name', '',
3158 3158 _('queue name to pop (DEPRECATED)'), _('NAME')),
3159 3159 ('f', 'force', None, _('forget any local changes to patched files'))],
3160 3160 _('hg qpop [-a] [-f] [PATCH | INDEX]')),
3161 3161 "^qpush":
3162 3162 (push,
3163 3163 [('f', 'force', None, _('apply on top of local changes')),
3164 3164 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
3165 3165 ('l', 'list', None, _('list patch name in commit text')),
3166 3166 ('a', 'all', None, _('apply all patches')),
3167 3167 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
3168 3168 ('n', 'name', '',
3169 3169 _('merge queue name (DEPRECATED)'), _('NAME')),
3170 3170 ('', 'move', None, _('reorder patch series and apply only the patch'))],
3171 3171 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
3172 3172 "^qrefresh":
3173 3173 (refresh,
3174 3174 [('e', 'edit', None, _('edit commit message')),
3175 3175 ('g', 'git', None, _('use git extended diff format')),
3176 3176 ('s', 'short', None,
3177 3177 _('refresh only files already in the patch and specified files')),
3178 3178 ('U', 'currentuser', None,
3179 3179 _('add/update author field in patch with current user')),
3180 3180 ('u', 'user', '',
3181 3181 _('add/update author field in patch with given user'), _('USER')),
3182 3182 ('D', 'currentdate', None,
3183 3183 _('add/update date field in patch with current date')),
3184 3184 ('d', 'date', '',
3185 3185 _('add/update date field in patch with given date'), _('DATE'))
3186 3186 ] + commands.walkopts + commands.commitopts,
3187 3187 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
3188 3188 'qrename|qmv':
3189 3189 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
3190 3190 "qrestore":
3191 3191 (restore,
3192 3192 [('d', 'delete', None, _('delete save entry')),
3193 3193 ('u', 'update', None, _('update queue working directory'))],
3194 3194 _('hg qrestore [-d] [-u] REV')),
3195 3195 "qsave":
3196 3196 (save,
3197 3197 [('c', 'copy', None, _('copy patch directory')),
3198 3198 ('n', 'name', '',
3199 3199 _('copy directory name'), _('NAME')),
3200 3200 ('e', 'empty', None, _('clear queue status file')),
3201 3201 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3202 3202 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
3203 3203 "qselect":
3204 3204 (select,
3205 3205 [('n', 'none', None, _('disable all guards')),
3206 3206 ('s', 'series', None, _('list all guards in series file')),
3207 3207 ('', 'pop', None, _('pop to before first guarded applied patch')),
3208 3208 ('', 'reapply', None, _('pop, then reapply patches'))],
3209 3209 _('hg qselect [OPTION]... [GUARD]...')),
3210 3210 "qseries":
3211 3211 (series,
3212 3212 [('m', 'missing', None, _('print patches not in series')),
3213 3213 ] + seriesopts,
3214 3214 _('hg qseries [-ms]')),
3215 3215 "strip":
3216 3216 (strip,
3217 3217 [('f', 'force', None, _('force removal of changesets even if the '
3218 3218 'working directory has uncommitted changes')),
3219 3219 ('b', 'backup', None, _('bundle only changesets with local revision'
3220 3220 ' number greater than REV which are not'
3221 3221 ' descendants of REV (DEPRECATED)')),
3222 3222 ('n', 'no-backup', None, _('no backups')),
3223 3223 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
3224 3224 ('k', 'keep', None, _("do not modify working copy during strip"))],
3225 3225 _('hg strip [-k] [-f] [-n] REV...')),
3226 3226 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3227 3227 "qunapplied":
3228 3228 (unapplied,
3229 3229 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3230 3230 _('hg qunapplied [-1] [-s] [PATCH]')),
3231 3231 "qfinish":
3232 3232 (finish,
3233 3233 [('a', 'applied', None, _('finish all applied changesets'))],
3234 3234 _('hg qfinish [-a] [REV]...')),
3235 3235 'qqueue':
3236 3236 (qqueue,
3237 3237 [
3238 3238 ('l', 'list', False, _('list all available queues')),
3239 3239 ('c', 'create', False, _('create new queue')),
3240 3240 ('', 'rename', False, _('rename active queue')),
3241 3241 ('', 'delete', False, _('delete reference to queue')),
3242 3242 ('', 'purge', False, _('delete queue, and remove patch dir')),
3243 3243 ],
3244 3244 _('[OPTION] [QUEUE]')),
3245 3245 }
3246 3246
3247 3247 colortable = {'qguard.negative': 'red',
3248 3248 'qguard.positive': 'yellow',
3249 3249 'qguard.unguarded': 'green',
3250 3250 'qseries.applied': 'blue bold underline',
3251 3251 'qseries.guarded': 'black bold',
3252 3252 'qseries.missing': 'red bold',
3253 3253 'qseries.unapplied': 'black bold'}
@@ -1,577 +1,577 b''
1 1 # rebase.py - rebasing feature for mercurial
2 2 #
3 3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot 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 '''command to move sets of revisions to a different ancestor
9 9
10 10 This extension lets you rebase changesets in an existing Mercurial
11 11 repository.
12 12
13 13 For more information:
14 14 http://mercurial.selenic.com/wiki/RebaseExtension
15 15 '''
16 16
17 17 from mercurial import hg, util, repair, merge, cmdutil, commands
18 18 from mercurial import extensions, ancestor, copies, patch
19 19 from mercurial.commands import templateopts
20 20 from mercurial.node import nullrev
21 21 from mercurial.lock import release
22 22 from mercurial.i18n import _
23 23 import os, errno
24 24
25 25 nullmerge = -2
26 26
27 27 def rebase(ui, repo, **opts):
28 28 """move changeset (and descendants) to a different branch
29 29
30 30 Rebase uses repeated merging to graft changesets from one part of
31 31 history (the source) onto another (the destination). This can be
32 32 useful for linearizing *local* changes relative to a master
33 33 development tree.
34 34
35 35 You should not rebase changesets that have already been shared
36 36 with others. Doing so will force everybody else to perform the
37 37 same rebase or they will end up with duplicated changesets after
38 38 pulling in your rebased changesets.
39 39
40 40 If you don't specify a destination changeset (``-d/--dest``),
41 41 rebase uses the tipmost head of the current named branch as the
42 42 destination. (The destination changeset is not modified by
43 43 rebasing, but new changesets are added as its descendants.)
44 44
45 45 You can specify which changesets to rebase in two ways: as a
46 46 "source" changeset or as a "base" changeset. Both are shorthand
47 47 for a topologically related set of changesets (the "source
48 48 branch"). If you specify source (``-s/--source``), rebase will
49 49 rebase that changeset and all of its descendants onto dest. If you
50 50 specify base (``-b/--base``), rebase will select ancestors of base
51 51 back to but not including the common ancestor with dest. Thus,
52 52 ``-b`` is less precise but more convenient than ``-s``: you can
53 53 specify any changeset in the source branch, and rebase will select
54 54 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
55 55 uses the parent of the working directory as the base.
56 56
57 57 By default, rebase recreates the changesets in the source branch
58 58 as descendants of dest and then destroys the originals. Use
59 59 ``--keep`` to preserve the original source changesets. Some
60 60 changesets in the source branch (e.g. merges from the destination
61 61 branch) may be dropped if they no longer contribute any change.
62 62
63 63 One result of the rules for selecting the destination changeset
64 64 and source branch is that, unlike ``merge``, rebase will do
65 65 nothing if you are at the latest (tipmost) head of a named branch
66 66 with two heads. You need to explicitly specify source and/or
67 67 destination (or ``update`` to the other head, if it's the head of
68 68 the intended source branch).
69 69
70 70 If a rebase is interrupted to manually resolve a merge, it can be
71 71 continued with --continue/-c or aborted with --abort/-a.
72 72
73 73 Returns 0 on success, 1 if nothing to rebase.
74 74 """
75 75 originalwd = target = None
76 76 external = nullrev
77 77 state = {}
78 78 skipped = set()
79 79 targetancestors = set()
80 80
81 81 lock = wlock = None
82 82 try:
83 83 lock = repo.lock()
84 84 wlock = repo.wlock()
85 85
86 86 # Validate input and define rebasing points
87 87 destf = opts.get('dest', None)
88 88 srcf = opts.get('source', None)
89 89 basef = opts.get('base', None)
90 90 contf = opts.get('continue')
91 91 abortf = opts.get('abort')
92 92 collapsef = opts.get('collapse', False)
93 93 extrafn = opts.get('extrafn')
94 94 keepf = opts.get('keep', False)
95 95 keepbranchesf = opts.get('keepbranches', False)
96 96 detachf = opts.get('detach', False)
97 97 # keepopen is not meant for use on the command line, but by
98 98 # other extensions
99 99 keepopen = opts.get('keepopen', False)
100 100
101 101 if contf or abortf:
102 102 if contf and abortf:
103 103 raise util.Abort(_('cannot use both abort and continue'))
104 104 if collapsef:
105 105 raise util.Abort(
106 106 _('cannot use collapse with continue or abort'))
107 107 if detachf:
108 108 raise util.Abort(_('cannot use detach with continue or abort'))
109 109 if srcf or basef or destf:
110 110 raise util.Abort(
111 111 _('abort and continue do not allow specifying revisions'))
112 112
113 113 (originalwd, target, state, skipped, collapsef, keepf,
114 114 keepbranchesf, external) = restorestatus(repo)
115 115 if abortf:
116 116 return abort(repo, originalwd, target, state)
117 117 else:
118 118 if srcf and basef:
119 119 raise util.Abort(_('cannot specify both a '
120 120 'revision and a base'))
121 121 if detachf:
122 122 if not srcf:
123 123 raise util.Abort(
124 124 _('detach requires a revision to be specified'))
125 125 if basef:
126 126 raise util.Abort(_('cannot specify a base with detach'))
127 127
128 128 cmdutil.bail_if_changed(repo)
129 129 result = buildstate(repo, destf, srcf, basef, detachf)
130 130 if not result:
131 131 # Empty state built, nothing to rebase
132 132 ui.status(_('nothing to rebase\n'))
133 133 return 1
134 134 else:
135 135 originalwd, target, state = result
136 136 if collapsef:
137 137 targetancestors = set(repo.changelog.ancestors(target))
138 138 external = checkexternal(repo, state, targetancestors)
139 139
140 140 if keepbranchesf:
141 141 if extrafn:
142 142 raise util.Abort(_('cannot use both keepbranches and extrafn'))
143 143 def extrafn(ctx, extra):
144 144 extra['branch'] = ctx.branch()
145 145
146 146 # Rebase
147 147 if not targetancestors:
148 148 targetancestors = set(repo.changelog.ancestors(target))
149 149 targetancestors.add(target)
150 150
151 151 sortedstate = sorted(state)
152 152 total = len(sortedstate)
153 153 pos = 0
154 154 for rev in sortedstate:
155 155 pos += 1
156 156 if state[rev] == -1:
157 157 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
158 158 _('changesets'), total)
159 159 storestatus(repo, originalwd, target, state, collapsef, keepf,
160 160 keepbranchesf, external)
161 161 p1, p2 = defineparents(repo, rev, target, state,
162 162 targetancestors)
163 163 if len(repo.parents()) == 2:
164 164 repo.ui.debug('resuming interrupted rebase\n')
165 165 else:
166 166 stats = rebasenode(repo, rev, p1, p2, state)
167 167 if stats and stats[3] > 0:
168 168 raise util.Abort(_('unresolved conflicts (see hg '
169 169 'resolve, then hg rebase --continue)'))
170 170 updatedirstate(repo, rev, target, p2)
171 171 if not collapsef:
172 172 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
173 173 else:
174 174 # Skip commit if we are collapsing
175 175 repo.dirstate.setparents(repo[p1].node())
176 176 newrev = None
177 177 # Update the state
178 178 if newrev is not None:
179 179 state[rev] = repo[newrev].rev()
180 180 else:
181 181 if not collapsef:
182 182 ui.note(_('no changes, revision %d skipped\n') % rev)
183 183 ui.debug('next revision set to %s\n' % p1)
184 184 skipped.add(rev)
185 185 state[rev] = p1
186 186
187 187 ui.progress(_('rebasing'), None)
188 188 ui.note(_('rebase merging completed\n'))
189 189
190 190 if collapsef and not keepopen:
191 191 p1, p2 = defineparents(repo, min(state), target,
192 192 state, targetancestors)
193 193 commitmsg = 'Collapsed revision'
194 194 for rebased in state:
195 195 if rebased not in skipped and state[rebased] != nullmerge:
196 196 commitmsg += '\n* %s' % repo[rebased].description()
197 197 commitmsg = ui.edit(commitmsg, repo.ui.username())
198 198 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
199 199 extrafn=extrafn)
200 200
201 201 if 'qtip' in repo.tags():
202 202 updatemq(repo, state, skipped, **opts)
203 203
204 204 if not keepf:
205 205 # Remove no more useful revisions
206 206 rebased = [rev for rev in state if state[rev] != nullmerge]
207 207 if rebased:
208 208 if set(repo.changelog.descendants(min(rebased))) - set(state):
209 209 ui.warn(_("warning: new changesets detected "
210 210 "on source branch, not stripping\n"))
211 211 else:
212 212 # backup the old csets by default
213 213 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
214 214
215 215 clearstatus(repo)
216 216 ui.note(_("rebase completed\n"))
217 217 if os.path.exists(repo.sjoin('undo')):
218 util.unlink(repo.sjoin('undo'))
218 util.unlinkpath(repo.sjoin('undo'))
219 219 if skipped:
220 220 ui.note(_("%d revisions have been skipped\n") % len(skipped))
221 221 finally:
222 222 release(lock, wlock)
223 223
224 224 def rebasemerge(repo, rev, first=False):
225 225 'return the correct ancestor'
226 226 oldancestor = ancestor.ancestor
227 227
228 228 def newancestor(a, b, pfunc):
229 229 if b == rev:
230 230 return repo[rev].parents()[0].rev()
231 231 return oldancestor(a, b, pfunc)
232 232
233 233 if not first:
234 234 ancestor.ancestor = newancestor
235 235 else:
236 236 repo.ui.debug("first revision, do not change ancestor\n")
237 237 try:
238 238 stats = merge.update(repo, rev, True, True, False)
239 239 return stats
240 240 finally:
241 241 ancestor.ancestor = oldancestor
242 242
243 243 def checkexternal(repo, state, targetancestors):
244 244 """Check whether one or more external revisions need to be taken in
245 245 consideration. In the latter case, abort.
246 246 """
247 247 external = nullrev
248 248 source = min(state)
249 249 for rev in state:
250 250 if rev == source:
251 251 continue
252 252 # Check externals and fail if there are more than one
253 253 for p in repo[rev].parents():
254 254 if (p.rev() not in state
255 255 and p.rev() not in targetancestors):
256 256 if external != nullrev:
257 257 raise util.Abort(_('unable to collapse, there is more '
258 258 'than one external parent'))
259 259 external = p.rev()
260 260 return external
261 261
262 262 def updatedirstate(repo, rev, p1, p2):
263 263 """Keep track of renamed files in the revision that is going to be rebased
264 264 """
265 265 # Here we simulate the copies and renames in the source changeset
266 266 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
267 267 m1 = repo[rev].manifest()
268 268 m2 = repo[p1].manifest()
269 269 for k, v in cop.iteritems():
270 270 if k in m1:
271 271 if v in m1 or v in m2:
272 272 repo.dirstate.copy(v, k)
273 273 if v in m2 and v not in m1:
274 274 repo.dirstate.remove(v)
275 275
276 276 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
277 277 'Commit the changes and store useful information in extra'
278 278 try:
279 279 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
280 280 ctx = repo[rev]
281 281 if commitmsg is None:
282 282 commitmsg = ctx.description()
283 283 extra = {'rebase_source': ctx.hex()}
284 284 if extrafn:
285 285 extrafn(ctx, extra)
286 286 # Commit might fail if unresolved files exist
287 287 newrev = repo.commit(text=commitmsg, user=ctx.user(),
288 288 date=ctx.date(), extra=extra)
289 289 repo.dirstate.setbranch(repo[newrev].branch())
290 290 return newrev
291 291 except util.Abort:
292 292 # Invalidate the previous setparents
293 293 repo.dirstate.invalidate()
294 294 raise
295 295
296 296 def rebasenode(repo, rev, p1, p2, state):
297 297 'Rebase a single revision'
298 298 # Merge phase
299 299 # Update to target and merge it with local
300 300 if repo['.'].rev() != repo[p1].rev():
301 301 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
302 302 merge.update(repo, p1, False, True, False)
303 303 else:
304 304 repo.ui.debug(" already in target\n")
305 305 repo.dirstate.write()
306 306 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
307 307 first = repo[rev].rev() == repo[min(state)].rev()
308 308 stats = rebasemerge(repo, rev, first)
309 309 return stats
310 310
311 311 def defineparents(repo, rev, target, state, targetancestors):
312 312 'Return the new parent relationship of the revision that will be rebased'
313 313 parents = repo[rev].parents()
314 314 p1 = p2 = nullrev
315 315
316 316 P1n = parents[0].rev()
317 317 if P1n in targetancestors:
318 318 p1 = target
319 319 elif P1n in state:
320 320 if state[P1n] == nullmerge:
321 321 p1 = target
322 322 else:
323 323 p1 = state[P1n]
324 324 else: # P1n external
325 325 p1 = target
326 326 p2 = P1n
327 327
328 328 if len(parents) == 2 and parents[1].rev() not in targetancestors:
329 329 P2n = parents[1].rev()
330 330 # interesting second parent
331 331 if P2n in state:
332 332 if p1 == target: # P1n in targetancestors or external
333 333 p1 = state[P2n]
334 334 else:
335 335 p2 = state[P2n]
336 336 else: # P2n external
337 337 if p2 != nullrev: # P1n external too => rev is a merged revision
338 338 raise util.Abort(_('cannot use revision %d as base, result '
339 339 'would have 3 parents') % rev)
340 340 p2 = P2n
341 341 repo.ui.debug(" future parents are %d and %d\n" %
342 342 (repo[p1].rev(), repo[p2].rev()))
343 343 return p1, p2
344 344
345 345 def isagitpatch(repo, patchname):
346 346 'Return true if the given patch is in git format'
347 347 mqpatch = os.path.join(repo.mq.path, patchname)
348 348 for line in patch.linereader(file(mqpatch, 'rb')):
349 349 if line.startswith('diff --git'):
350 350 return True
351 351 return False
352 352
353 353 def updatemq(repo, state, skipped, **opts):
354 354 'Update rebased mq patches - finalize and then import them'
355 355 mqrebase = {}
356 356 mq = repo.mq
357 357 for p in mq.applied:
358 358 rev = repo[p.node].rev()
359 359 if rev in state:
360 360 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
361 361 (rev, p.name))
362 362 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
363 363
364 364 if mqrebase:
365 365 mq.finish(repo, mqrebase.keys())
366 366
367 367 # We must start import from the newest revision
368 368 for rev in sorted(mqrebase, reverse=True):
369 369 if rev not in skipped:
370 370 name, isgit = mqrebase[rev]
371 371 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
372 372 mq.qimport(repo, (), patchname=name, git=isgit,
373 373 rev=[str(state[rev])])
374 374 mq.save_dirty()
375 375
376 376 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
377 377 external):
378 378 'Store the current status to allow recovery'
379 379 f = repo.opener("rebasestate", "w")
380 380 f.write(repo[originalwd].hex() + '\n')
381 381 f.write(repo[target].hex() + '\n')
382 382 f.write(repo[external].hex() + '\n')
383 383 f.write('%d\n' % int(collapse))
384 384 f.write('%d\n' % int(keep))
385 385 f.write('%d\n' % int(keepbranches))
386 386 for d, v in state.iteritems():
387 387 oldrev = repo[d].hex()
388 388 newrev = repo[v].hex()
389 389 f.write("%s:%s\n" % (oldrev, newrev))
390 390 f.close()
391 391 repo.ui.debug('rebase status stored\n')
392 392
393 393 def clearstatus(repo):
394 394 'Remove the status files'
395 395 if os.path.exists(repo.join("rebasestate")):
396 util.unlink(repo.join("rebasestate"))
396 util.unlinkpath(repo.join("rebasestate"))
397 397
398 398 def restorestatus(repo):
399 399 'Restore a previously stored status'
400 400 try:
401 401 target = None
402 402 collapse = False
403 403 external = nullrev
404 404 state = {}
405 405 f = repo.opener("rebasestate")
406 406 for i, l in enumerate(f.read().splitlines()):
407 407 if i == 0:
408 408 originalwd = repo[l].rev()
409 409 elif i == 1:
410 410 target = repo[l].rev()
411 411 elif i == 2:
412 412 external = repo[l].rev()
413 413 elif i == 3:
414 414 collapse = bool(int(l))
415 415 elif i == 4:
416 416 keep = bool(int(l))
417 417 elif i == 5:
418 418 keepbranches = bool(int(l))
419 419 else:
420 420 oldrev, newrev = l.split(':')
421 421 state[repo[oldrev].rev()] = repo[newrev].rev()
422 422 skipped = set()
423 423 # recompute the set of skipped revs
424 424 if not collapse:
425 425 seen = set([target])
426 426 for old, new in sorted(state.items()):
427 427 if new != nullrev and new in seen:
428 428 skipped.add(old)
429 429 seen.add(new)
430 430 repo.ui.debug('computed skipped revs: %s\n' % skipped)
431 431 repo.ui.debug('rebase status resumed\n')
432 432 return (originalwd, target, state, skipped,
433 433 collapse, keep, keepbranches, external)
434 434 except IOError, err:
435 435 if err.errno != errno.ENOENT:
436 436 raise
437 437 raise util.Abort(_('no rebase in progress'))
438 438
439 439 def abort(repo, originalwd, target, state):
440 440 'Restore the repository to its original state'
441 441 if set(repo.changelog.descendants(target)) - set(state.values()):
442 442 repo.ui.warn(_("warning: new changesets detected on target branch, "
443 443 "can't abort\n"))
444 444 return -1
445 445 else:
446 446 # Strip from the first rebased revision
447 447 merge.update(repo, repo[originalwd].rev(), False, True, False)
448 448 rebased = filter(lambda x: x > -1 and x != target, state.values())
449 449 if rebased:
450 450 strippoint = min(rebased)
451 451 # no backup of rebased cset versions needed
452 452 repair.strip(repo.ui, repo, repo[strippoint].node())
453 453 clearstatus(repo)
454 454 repo.ui.warn(_('rebase aborted\n'))
455 455 return 0
456 456
457 457 def buildstate(repo, dest, src, base, detach):
458 458 'Define which revisions are going to be rebased and where'
459 459 targetancestors = set()
460 460 detachset = set()
461 461
462 462 if not dest:
463 463 # Destination defaults to the latest revision in the current branch
464 464 branch = repo[None].branch()
465 465 dest = repo[branch].rev()
466 466 else:
467 467 dest = repo[dest].rev()
468 468
469 469 # This check isn't strictly necessary, since mq detects commits over an
470 470 # applied patch. But it prevents messing up the working directory when
471 471 # a partially completed rebase is blocked by mq.
472 472 if 'qtip' in repo.tags() and (repo[dest].node() in
473 473 [s.node for s in repo.mq.applied]):
474 474 raise util.Abort(_('cannot rebase onto an applied mq patch'))
475 475
476 476 if src:
477 477 commonbase = repo[src].ancestor(repo[dest])
478 478 if commonbase == repo[src]:
479 479 raise util.Abort(_('source is ancestor of destination'))
480 480 if commonbase == repo[dest]:
481 481 raise util.Abort(_('source is descendant of destination'))
482 482 source = repo[src].rev()
483 483 if detach:
484 484 # We need to keep track of source's ancestors up to the common base
485 485 srcancestors = set(repo.changelog.ancestors(source))
486 486 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
487 487 detachset = srcancestors - baseancestors
488 488 detachset.discard(commonbase.rev())
489 489 else:
490 490 if base:
491 491 cwd = repo[base].rev()
492 492 else:
493 493 cwd = repo['.'].rev()
494 494
495 495 if cwd == dest:
496 496 repo.ui.debug('source and destination are the same\n')
497 497 return None
498 498
499 499 targetancestors = set(repo.changelog.ancestors(dest))
500 500 if cwd in targetancestors:
501 501 repo.ui.debug('source is ancestor of destination\n')
502 502 return None
503 503
504 504 cwdancestors = set(repo.changelog.ancestors(cwd))
505 505 if dest in cwdancestors:
506 506 repo.ui.debug('source is descendant of destination\n')
507 507 return None
508 508
509 509 cwdancestors.add(cwd)
510 510 rebasingbranch = cwdancestors - targetancestors
511 511 source = min(rebasingbranch)
512 512
513 513 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
514 514 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
515 515 state.update(dict.fromkeys(detachset, nullmerge))
516 516 state[source] = nullrev
517 517 return repo['.'].rev(), repo[dest].rev(), state
518 518
519 519 def pullrebase(orig, ui, repo, *args, **opts):
520 520 'Call rebase after pull if the latter has been invoked with --rebase'
521 521 if opts.get('rebase'):
522 522 if opts.get('update'):
523 523 del opts['update']
524 524 ui.debug('--update and --rebase are not compatible, ignoring '
525 525 'the update flag\n')
526 526
527 527 cmdutil.bail_if_changed(repo)
528 528 revsprepull = len(repo)
529 529 origpostincoming = commands.postincoming
530 530 def _dummy(*args, **kwargs):
531 531 pass
532 532 commands.postincoming = _dummy
533 533 try:
534 534 orig(ui, repo, *args, **opts)
535 535 finally:
536 536 commands.postincoming = origpostincoming
537 537 revspostpull = len(repo)
538 538 if revspostpull > revsprepull:
539 539 rebase(ui, repo, **opts)
540 540 branch = repo[None].branch()
541 541 dest = repo[branch].rev()
542 542 if dest != repo['.'].rev():
543 543 # there was nothing to rebase we force an update
544 544 hg.update(repo, dest)
545 545 else:
546 546 orig(ui, repo, *args, **opts)
547 547
548 548 def uisetup(ui):
549 549 'Replace pull with a decorator to provide --rebase option'
550 550 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
551 551 entry[1].append(('', 'rebase', None,
552 552 _("rebase working directory to branch head"))
553 553 )
554 554
555 555 cmdtable = {
556 556 "rebase":
557 557 (rebase,
558 558 [
559 559 ('s', 'source', '',
560 560 _('rebase from the specified changeset'), _('REV')),
561 561 ('b', 'base', '',
562 562 _('rebase from the base of the specified changeset '
563 563 '(up to greatest common ancestor of base and dest)'),
564 564 _('REV')),
565 565 ('d', 'dest', '',
566 566 _('rebase onto the specified changeset'), _('REV')),
567 567 ('', 'collapse', False, _('collapse the rebased changesets')),
568 568 ('', 'keep', False, _('keep original changesets')),
569 569 ('', 'keepbranches', False, _('keep original branch names')),
570 570 ('', 'detach', False, _('force detaching of source from its original '
571 571 'branch')),
572 572 ('c', 'continue', False, _('continue an interrupted rebase')),
573 573 ('a', 'abort', False, _('abort an interrupted rebase'))] +
574 574 templateopts,
575 575 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
576 576 'hg rebase {-a|-c}'))
577 577 }
@@ -1,4535 +1,4535 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile
12 12 import hg, util, revlog, extensions, copies, error
13 13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset
17 17 import dagparser
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the
25 25 repository.
26 26
27 27 The files will be added to the repository at the next commit. To
28 28 undo an add before that, see :hg:`forget`.
29 29
30 30 If no names are given, add all files to the repository.
31 31
32 32 .. container:: verbose
33 33
34 34 An example showing how new (unknown) files are added
35 35 automatically by :hg:`add`::
36 36
37 37 $ ls
38 38 foo.c
39 39 $ hg status
40 40 ? foo.c
41 41 $ hg add
42 42 adding foo.c
43 43 $ hg status
44 44 A foo.c
45 45
46 46 Returns 0 if all files are successfully added.
47 47 """
48 48
49 49 m = cmdutil.match(repo, pats, opts)
50 50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 51 opts.get('subrepos'), prefix="")
52 52 return rejected and 1 or 0
53 53
54 54 def addremove(ui, repo, *pats, **opts):
55 55 """add all new files, delete all missing files
56 56
57 57 Add all new files and remove all missing files from the
58 58 repository.
59 59
60 60 New files are ignored if they match any of the patterns in
61 61 .hgignore. As with add, these changes take effect at the next
62 62 commit.
63 63
64 64 Use the -s/--similarity option to detect renamed files. With a
65 65 parameter greater than 0, this compares every removed file with
66 66 every added file and records those similar enough as renames. This
67 67 option takes a percentage between 0 (disabled) and 100 (files must
68 68 be identical) as its parameter. Detecting renamed files this way
69 69 can be expensive. After using this option, :hg:`status -C` can be
70 70 used to check which files were identified as moved or renamed.
71 71
72 72 Returns 0 if all files are successfully added.
73 73 """
74 74 try:
75 75 sim = float(opts.get('similarity') or 100)
76 76 except ValueError:
77 77 raise util.Abort(_('similarity must be a number'))
78 78 if sim < 0 or sim > 100:
79 79 raise util.Abort(_('similarity must be between 0 and 100'))
80 80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 81
82 82 def annotate(ui, repo, *pats, **opts):
83 83 """show changeset information by line for each file
84 84
85 85 List changes in files, showing the revision id responsible for
86 86 each line
87 87
88 88 This command is useful for discovering when a change was made and
89 89 by whom.
90 90
91 91 Without the -a/--text option, annotate will avoid processing files
92 92 it detects as binary. With -a, annotate will annotate the file
93 93 anyway, although the results will probably be neither useful
94 94 nor desirable.
95 95
96 96 Returns 0 on success.
97 97 """
98 98 if opts.get('follow'):
99 99 # --follow is deprecated and now just an alias for -f/--file
100 100 # to mimic the behavior of Mercurial before version 1.5
101 101 opts['file'] = 1
102 102
103 103 datefunc = ui.quiet and util.shortdate or util.datestr
104 104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 105
106 106 if not pats:
107 107 raise util.Abort(_('at least one filename or pattern is required'))
108 108
109 109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 110 ('number', lambda x: str(x[0].rev())),
111 111 ('changeset', lambda x: short(x[0].node())),
112 112 ('date', getdate),
113 113 ('file', lambda x: x[0].path()),
114 114 ]
115 115
116 116 if (not opts.get('user') and not opts.get('changeset')
117 117 and not opts.get('date') and not opts.get('file')):
118 118 opts['number'] = 1
119 119
120 120 linenumber = opts.get('line_number') is not None
121 121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 123
124 124 funcmap = [func for op, func in opmap if opts.get(op)]
125 125 if linenumber:
126 126 lastfunc = funcmap[-1]
127 127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 128
129 129 ctx = cmdutil.revsingle(repo, opts.get('rev'))
130 130 m = cmdutil.match(repo, pats, opts)
131 131 follow = not opts.get('no_follow')
132 132 for abs in ctx.walk(m):
133 133 fctx = ctx[abs]
134 134 if not opts.get('text') and util.binary(fctx.data()):
135 135 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
136 136 continue
137 137
138 138 lines = fctx.annotate(follow=follow, linenumber=linenumber)
139 139 pieces = []
140 140
141 141 for f in funcmap:
142 142 l = [f(n) for n, dummy in lines]
143 143 if l:
144 144 sized = [(x, encoding.colwidth(x)) for x in l]
145 145 ml = max([w for x, w in sized])
146 146 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
147 147
148 148 if pieces:
149 149 for p, l in zip(zip(*pieces), lines):
150 150 ui.write("%s: %s" % (" ".join(p), l[1]))
151 151
152 152 def archive(ui, repo, dest, **opts):
153 153 '''create an unversioned archive of a repository revision
154 154
155 155 By default, the revision used is the parent of the working
156 156 directory; use -r/--rev to specify a different revision.
157 157
158 158 The archive type is automatically detected based on file
159 159 extension (or override using -t/--type).
160 160
161 161 Valid types are:
162 162
163 163 :``files``: a directory full of files (default)
164 164 :``tar``: tar archive, uncompressed
165 165 :``tbz2``: tar archive, compressed using bzip2
166 166 :``tgz``: tar archive, compressed using gzip
167 167 :``uzip``: zip archive, uncompressed
168 168 :``zip``: zip archive, compressed using deflate
169 169
170 170 The exact name of the destination archive or directory is given
171 171 using a format string; see :hg:`help export` for details.
172 172
173 173 Each member added to an archive file has a directory prefix
174 174 prepended. Use -p/--prefix to specify a format string for the
175 175 prefix. The default is the basename of the archive, with suffixes
176 176 removed.
177 177
178 178 Returns 0 on success.
179 179 '''
180 180
181 181 ctx = cmdutil.revsingle(repo, opts.get('rev'))
182 182 if not ctx:
183 183 raise util.Abort(_('no working directory: please specify a revision'))
184 184 node = ctx.node()
185 185 dest = cmdutil.make_filename(repo, dest, node)
186 186 if os.path.realpath(dest) == repo.root:
187 187 raise util.Abort(_('repository root cannot be destination'))
188 188
189 189 kind = opts.get('type') or archival.guesskind(dest) or 'files'
190 190 prefix = opts.get('prefix')
191 191
192 192 if dest == '-':
193 193 if kind == 'files':
194 194 raise util.Abort(_('cannot archive plain files to stdout'))
195 195 dest = sys.stdout
196 196 if not prefix:
197 197 prefix = os.path.basename(repo.root) + '-%h'
198 198
199 199 prefix = cmdutil.make_filename(repo, prefix, node)
200 200 matchfn = cmdutil.match(repo, [], opts)
201 201 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
202 202 matchfn, prefix, subrepos=opts.get('subrepos'))
203 203
204 204 def backout(ui, repo, node=None, rev=None, **opts):
205 205 '''reverse effect of earlier changeset
206 206
207 207 The backout command merges the reverse effect of the reverted
208 208 changeset into the working directory.
209 209
210 210 With the --merge option, it first commits the reverted changes
211 211 as a new changeset. This new changeset is a child of the reverted
212 212 changeset.
213 213 The --merge option remembers the parent of the working directory
214 214 before starting the backout, then merges the new head with that
215 215 changeset afterwards.
216 216 This will result in an explicit merge in the history.
217 217
218 218 If you backout a changeset other than the original parent of the
219 219 working directory, the result of this merge is not committed,
220 220 as with a normal merge. Otherwise, no merge is needed and the
221 221 commit is automatic.
222 222
223 223 Note that the default behavior (without --merge) has changed in
224 224 version 1.7. To restore the previous default behavior, use
225 225 :hg:`backout --merge` and then :hg:`update --clean .` to get rid of
226 226 the ongoing merge.
227 227
228 228 See :hg:`help dates` for a list of formats valid for -d/--date.
229 229
230 230 Returns 0 on success.
231 231 '''
232 232 if rev and node:
233 233 raise util.Abort(_("please specify just one revision"))
234 234
235 235 if not rev:
236 236 rev = node
237 237
238 238 if not rev:
239 239 raise util.Abort(_("please specify a revision to backout"))
240 240
241 241 date = opts.get('date')
242 242 if date:
243 243 opts['date'] = util.parsedate(date)
244 244
245 245 cmdutil.bail_if_changed(repo)
246 246 node = cmdutil.revsingle(repo, rev).node()
247 247
248 248 op1, op2 = repo.dirstate.parents()
249 249 a = repo.changelog.ancestor(op1, node)
250 250 if a != node:
251 251 raise util.Abort(_('cannot backout change on a different branch'))
252 252
253 253 p1, p2 = repo.changelog.parents(node)
254 254 if p1 == nullid:
255 255 raise util.Abort(_('cannot backout a change with no parents'))
256 256 if p2 != nullid:
257 257 if not opts.get('parent'):
258 258 raise util.Abort(_('cannot backout a merge changeset without '
259 259 '--parent'))
260 260 p = repo.lookup(opts['parent'])
261 261 if p not in (p1, p2):
262 262 raise util.Abort(_('%s is not a parent of %s') %
263 263 (short(p), short(node)))
264 264 parent = p
265 265 else:
266 266 if opts.get('parent'):
267 267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 268 parent = p1
269 269
270 270 # the backout should appear on the same branch
271 271 branch = repo.dirstate.branch()
272 272 hg.clean(repo, node, show_stats=False)
273 273 repo.dirstate.setbranch(branch)
274 274 revert_opts = opts.copy()
275 275 revert_opts['date'] = None
276 276 revert_opts['all'] = True
277 277 revert_opts['rev'] = hex(parent)
278 278 revert_opts['no_backup'] = None
279 279 revert(ui, repo, **revert_opts)
280 280 if not opts.get('merge') and op1 != node:
281 281 try:
282 282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 283 return hg.update(repo, op1)
284 284 finally:
285 285 ui.setconfig('ui', 'forcemerge', '')
286 286
287 287 commit_opts = opts.copy()
288 288 commit_opts['addremove'] = False
289 289 if not commit_opts['message'] and not commit_opts['logfile']:
290 290 # we don't translate commit messages
291 291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 292 commit_opts['force_editor'] = True
293 293 commit(ui, repo, **commit_opts)
294 294 def nice(node):
295 295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 296 ui.status(_('changeset %s backs out changeset %s\n') %
297 297 (nice(repo.changelog.tip()), nice(node)))
298 298 if opts.get('merge') and op1 != node:
299 299 hg.clean(repo, op1, show_stats=False)
300 300 ui.status(_('merging with changeset %s\n')
301 301 % nice(repo.changelog.tip()))
302 302 try:
303 303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 304 return hg.merge(repo, hex(repo.changelog.tip()))
305 305 finally:
306 306 ui.setconfig('ui', 'forcemerge', '')
307 307 return 0
308 308
309 309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 310 reset=None, good=None, bad=None, skip=None, noupdate=None):
311 311 """subdivision search of changesets
312 312
313 313 This command helps to find changesets which introduce problems. To
314 314 use, mark the earliest changeset you know exhibits the problem as
315 315 bad, then mark the latest changeset which is free from the problem
316 316 as good. Bisect will update your working directory to a revision
317 317 for testing (unless the -U/--noupdate option is specified). Once
318 318 you have performed tests, mark the working directory as good or
319 319 bad, and bisect will either update to another candidate changeset
320 320 or announce that it has found the bad revision.
321 321
322 322 As a shortcut, you can also use the revision argument to mark a
323 323 revision as good or bad without checking it out first.
324 324
325 325 If you supply a command, it will be used for automatic bisection.
326 326 Its exit status will be used to mark revisions as good or bad:
327 327 status 0 means good, 125 means to skip the revision, 127
328 328 (command not found) will abort the bisection, and any other
329 329 non-zero exit status means the revision is bad.
330 330
331 331 Returns 0 on success.
332 332 """
333 333 def print_result(nodes, good):
334 334 displayer = cmdutil.show_changeset(ui, repo, {})
335 335 if len(nodes) == 1:
336 336 # narrowed it down to a single revision
337 337 if good:
338 338 ui.write(_("The first good revision is:\n"))
339 339 else:
340 340 ui.write(_("The first bad revision is:\n"))
341 341 displayer.show(repo[nodes[0]])
342 342 parents = repo[nodes[0]].parents()
343 343 if len(parents) > 1:
344 344 side = good and state['bad'] or state['good']
345 345 num = len(set(i.node() for i in parents) & set(side))
346 346 if num == 1:
347 347 common = parents[0].ancestor(parents[1])
348 348 ui.write(_('Not all ancestors of this changeset have been'
349 349 ' checked.\nTo check the other ancestors, start'
350 350 ' from the common ancestor, %s.\n' % common))
351 351 else:
352 352 # multiple possible revisions
353 353 if good:
354 354 ui.write(_("Due to skipped revisions, the first "
355 355 "good revision could be any of:\n"))
356 356 else:
357 357 ui.write(_("Due to skipped revisions, the first "
358 358 "bad revision could be any of:\n"))
359 359 for n in nodes:
360 360 displayer.show(repo[n])
361 361 displayer.close()
362 362
363 363 def check_state(state, interactive=True):
364 364 if not state['good'] or not state['bad']:
365 365 if (good or bad or skip or reset) and interactive:
366 366 return
367 367 if not state['good']:
368 368 raise util.Abort(_('cannot bisect (no known good revisions)'))
369 369 else:
370 370 raise util.Abort(_('cannot bisect (no known bad revisions)'))
371 371 return True
372 372
373 373 # backward compatibility
374 374 if rev in "good bad reset init".split():
375 375 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
376 376 cmd, rev, extra = rev, extra, None
377 377 if cmd == "good":
378 378 good = True
379 379 elif cmd == "bad":
380 380 bad = True
381 381 else:
382 382 reset = True
383 383 elif extra or good + bad + skip + reset + bool(command) > 1:
384 384 raise util.Abort(_('incompatible arguments'))
385 385
386 386 if reset:
387 387 p = repo.join("bisect.state")
388 388 if os.path.exists(p):
389 389 os.unlink(p)
390 390 return
391 391
392 392 state = hbisect.load_state(repo)
393 393
394 394 if command:
395 395 changesets = 1
396 396 try:
397 397 while changesets:
398 398 # update state
399 399 status = util.system(command)
400 400 if status == 125:
401 401 transition = "skip"
402 402 elif status == 0:
403 403 transition = "good"
404 404 # status < 0 means process was killed
405 405 elif status == 127:
406 406 raise util.Abort(_("failed to execute %s") % command)
407 407 elif status < 0:
408 408 raise util.Abort(_("%s killed") % command)
409 409 else:
410 410 transition = "bad"
411 411 ctx = cmdutil.revsingle(repo, rev)
412 412 rev = None # clear for future iterations
413 413 state[transition].append(ctx.node())
414 414 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
415 415 check_state(state, interactive=False)
416 416 # bisect
417 417 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
418 418 # update to next check
419 419 cmdutil.bail_if_changed(repo)
420 420 hg.clean(repo, nodes[0], show_stats=False)
421 421 finally:
422 422 hbisect.save_state(repo, state)
423 423 print_result(nodes, good)
424 424 return
425 425
426 426 # update state
427 427
428 428 if rev:
429 429 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
430 430 else:
431 431 nodes = [repo.lookup('.')]
432 432
433 433 if good or bad or skip:
434 434 if good:
435 435 state['good'] += nodes
436 436 elif bad:
437 437 state['bad'] += nodes
438 438 elif skip:
439 439 state['skip'] += nodes
440 440 hbisect.save_state(repo, state)
441 441
442 442 if not check_state(state):
443 443 return
444 444
445 445 # actually bisect
446 446 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
447 447 if changesets == 0:
448 448 print_result(nodes, good)
449 449 else:
450 450 assert len(nodes) == 1 # only a single node can be tested next
451 451 node = nodes[0]
452 452 # compute the approximate number of remaining tests
453 453 tests, size = 0, 2
454 454 while size <= changesets:
455 455 tests, size = tests + 1, size * 2
456 456 rev = repo.changelog.rev(node)
457 457 ui.write(_("Testing changeset %d:%s "
458 458 "(%d changesets remaining, ~%d tests)\n")
459 459 % (rev, short(node), changesets, tests))
460 460 if not noupdate:
461 461 cmdutil.bail_if_changed(repo)
462 462 return hg.clean(repo, node)
463 463
464 464 def branch(ui, repo, label=None, **opts):
465 465 """set or show the current branch name
466 466
467 467 With no argument, show the current branch name. With one argument,
468 468 set the working directory branch name (the branch will not exist
469 469 in the repository until the next commit). Standard practice
470 470 recommends that primary development take place on the 'default'
471 471 branch.
472 472
473 473 Unless -f/--force is specified, branch will not let you set a
474 474 branch name that already exists, even if it's inactive.
475 475
476 476 Use -C/--clean to reset the working directory branch to that of
477 477 the parent of the working directory, negating a previous branch
478 478 change.
479 479
480 480 Use the command :hg:`update` to switch to an existing branch. Use
481 481 :hg:`commit --close-branch` to mark this branch as closed.
482 482
483 483 Returns 0 on success.
484 484 """
485 485
486 486 if opts.get('clean'):
487 487 label = repo[None].parents()[0].branch()
488 488 repo.dirstate.setbranch(label)
489 489 ui.status(_('reset working directory to branch %s\n') % label)
490 490 elif label:
491 491 if not opts.get('force') and label in repo.branchtags():
492 492 if label not in [p.branch() for p in repo.parents()]:
493 493 raise util.Abort(_('a branch of the same name already exists'
494 494 " (use 'hg update' to switch to it)"))
495 495 repo.dirstate.setbranch(label)
496 496 ui.status(_('marked working directory as branch %s\n') % label)
497 497 else:
498 498 ui.write("%s\n" % repo.dirstate.branch())
499 499
500 500 def branches(ui, repo, active=False, closed=False):
501 501 """list repository named branches
502 502
503 503 List the repository's named branches, indicating which ones are
504 504 inactive. If -c/--closed is specified, also list branches which have
505 505 been marked closed (see :hg:`commit --close-branch`).
506 506
507 507 If -a/--active is specified, only show active branches. A branch
508 508 is considered active if it contains repository heads.
509 509
510 510 Use the command :hg:`update` to switch to an existing branch.
511 511
512 512 Returns 0.
513 513 """
514 514
515 515 hexfunc = ui.debugflag and hex or short
516 516 activebranches = [repo[n].branch() for n in repo.heads()]
517 517 def testactive(tag, node):
518 518 realhead = tag in activebranches
519 519 open = node in repo.branchheads(tag, closed=False)
520 520 return realhead and open
521 521 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
522 522 for tag, node in repo.branchtags().items()],
523 523 reverse=True)
524 524
525 525 for isactive, node, tag in branches:
526 526 if (not active) or isactive:
527 527 if ui.quiet:
528 528 ui.write("%s\n" % tag)
529 529 else:
530 530 hn = repo.lookup(node)
531 531 if isactive:
532 532 label = 'branches.active'
533 533 notice = ''
534 534 elif hn not in repo.branchheads(tag, closed=False):
535 535 if not closed:
536 536 continue
537 537 label = 'branches.closed'
538 538 notice = _(' (closed)')
539 539 else:
540 540 label = 'branches.inactive'
541 541 notice = _(' (inactive)')
542 542 if tag == repo.dirstate.branch():
543 543 label = 'branches.current'
544 544 rev = str(node).rjust(31 - encoding.colwidth(tag))
545 545 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
546 546 tag = ui.label(tag, label)
547 547 ui.write("%s %s%s\n" % (tag, rev, notice))
548 548
549 549 def bundle(ui, repo, fname, dest=None, **opts):
550 550 """create a changegroup file
551 551
552 552 Generate a compressed changegroup file collecting changesets not
553 553 known to be in another repository.
554 554
555 555 If you omit the destination repository, then hg assumes the
556 556 destination will have all the nodes you specify with --base
557 557 parameters. To create a bundle containing all changesets, use
558 558 -a/--all (or --base null).
559 559
560 560 You can change compression method with the -t/--type option.
561 561 The available compression methods are: none, bzip2, and
562 562 gzip (by default, bundles are compressed using bzip2).
563 563
564 564 The bundle file can then be transferred using conventional means
565 565 and applied to another repository with the unbundle or pull
566 566 command. This is useful when direct push and pull are not
567 567 available or when exporting an entire repository is undesirable.
568 568
569 569 Applying bundles preserves all changeset contents including
570 570 permissions, copy/rename information, and revision history.
571 571
572 572 Returns 0 on success, 1 if no changes found.
573 573 """
574 574 revs = None
575 575 if 'rev' in opts:
576 576 revs = cmdutil.revrange(repo, opts['rev'])
577 577
578 578 if opts.get('all'):
579 579 base = ['null']
580 580 else:
581 581 base = cmdutil.revrange(repo, opts.get('base'))
582 582 if base:
583 583 if dest:
584 584 raise util.Abort(_("--base is incompatible with specifying "
585 585 "a destination"))
586 586 base = [repo.lookup(rev) for rev in base]
587 587 # create the right base
588 588 # XXX: nodesbetween / changegroup* should be "fixed" instead
589 589 o = []
590 590 has = set((nullid,))
591 591 for n in base:
592 592 has.update(repo.changelog.reachable(n))
593 593 if revs:
594 594 revs = [repo.lookup(rev) for rev in revs]
595 595 visit = revs[:]
596 596 has.difference_update(visit)
597 597 else:
598 598 visit = repo.changelog.heads()
599 599 seen = {}
600 600 while visit:
601 601 n = visit.pop(0)
602 602 parents = [p for p in repo.changelog.parents(n) if p not in has]
603 603 if len(parents) == 0:
604 604 if n not in has:
605 605 o.append(n)
606 606 else:
607 607 for p in parents:
608 608 if p not in seen:
609 609 seen[p] = 1
610 610 visit.append(p)
611 611 else:
612 612 dest = ui.expandpath(dest or 'default-push', dest or 'default')
613 613 dest, branches = hg.parseurl(dest, opts.get('branch'))
614 614 other = hg.repository(hg.remoteui(repo, opts), dest)
615 615 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
616 616 if revs:
617 617 revs = [repo.lookup(rev) for rev in revs]
618 618 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
619 619
620 620 if not o:
621 621 ui.status(_("no changes found\n"))
622 622 return 1
623 623
624 624 if revs:
625 625 cg = repo.changegroupsubset(o, revs, 'bundle')
626 626 else:
627 627 cg = repo.changegroup(o, 'bundle')
628 628
629 629 bundletype = opts.get('type', 'bzip2').lower()
630 630 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
631 631 bundletype = btypes.get(bundletype)
632 632 if bundletype not in changegroup.bundletypes:
633 633 raise util.Abort(_('unknown bundle type specified with --type'))
634 634
635 635 changegroup.writebundle(cg, fname, bundletype)
636 636
637 637 def cat(ui, repo, file1, *pats, **opts):
638 638 """output the current or given revision of files
639 639
640 640 Print the specified files as they were at the given revision. If
641 641 no revision is given, the parent of the working directory is used,
642 642 or tip if no revision is checked out.
643 643
644 644 Output may be to a file, in which case the name of the file is
645 645 given using a format string. The formatting rules are the same as
646 646 for the export command, with the following additions:
647 647
648 648 :``%s``: basename of file being printed
649 649 :``%d``: dirname of file being printed, or '.' if in repository root
650 650 :``%p``: root-relative path name of file being printed
651 651
652 652 Returns 0 on success.
653 653 """
654 654 ctx = cmdutil.revsingle(repo, opts.get('rev'))
655 655 err = 1
656 656 m = cmdutil.match(repo, (file1,) + pats, opts)
657 657 for abs in ctx.walk(m):
658 658 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
659 659 data = ctx[abs].data()
660 660 if opts.get('decode'):
661 661 data = repo.wwritedata(abs, data)
662 662 fp.write(data)
663 663 err = 0
664 664 return err
665 665
666 666 def clone(ui, source, dest=None, **opts):
667 667 """make a copy of an existing repository
668 668
669 669 Create a copy of an existing repository in a new directory.
670 670
671 671 If no destination directory name is specified, it defaults to the
672 672 basename of the source.
673 673
674 674 The location of the source is added to the new repository's
675 675 .hg/hgrc file, as the default to be used for future pulls.
676 676
677 677 See :hg:`help urls` for valid source format details.
678 678
679 679 It is possible to specify an ``ssh://`` URL as the destination, but no
680 680 .hg/hgrc and working directory will be created on the remote side.
681 681 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
682 682
683 683 A set of changesets (tags, or branch names) to pull may be specified
684 684 by listing each changeset (tag, or branch name) with -r/--rev.
685 685 If -r/--rev is used, the cloned repository will contain only a subset
686 686 of the changesets of the source repository. Only the set of changesets
687 687 defined by all -r/--rev options (including all their ancestors)
688 688 will be pulled into the destination repository.
689 689 No subsequent changesets (including subsequent tags) will be present
690 690 in the destination.
691 691
692 692 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
693 693 local source repositories.
694 694
695 695 For efficiency, hardlinks are used for cloning whenever the source
696 696 and destination are on the same filesystem (note this applies only
697 697 to the repository data, not to the working directory). Some
698 698 filesystems, such as AFS, implement hardlinking incorrectly, but
699 699 do not report errors. In these cases, use the --pull option to
700 700 avoid hardlinking.
701 701
702 702 In some cases, you can clone repositories and the working directory
703 703 using full hardlinks with ::
704 704
705 705 $ cp -al REPO REPOCLONE
706 706
707 707 This is the fastest way to clone, but it is not always safe. The
708 708 operation is not atomic (making sure REPO is not modified during
709 709 the operation is up to you) and you have to make sure your editor
710 710 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
711 711 this is not compatible with certain extensions that place their
712 712 metadata under the .hg directory, such as mq.
713 713
714 714 Mercurial will update the working directory to the first applicable
715 715 revision from this list:
716 716
717 717 a) null if -U or the source repository has no changesets
718 718 b) if -u . and the source repository is local, the first parent of
719 719 the source repository's working directory
720 720 c) the changeset specified with -u (if a branch name, this means the
721 721 latest head of that branch)
722 722 d) the changeset specified with -r
723 723 e) the tipmost head specified with -b
724 724 f) the tipmost head specified with the url#branch source syntax
725 725 g) the tipmost head of the default branch
726 726 h) tip
727 727
728 728 Returns 0 on success.
729 729 """
730 730 if opts.get('noupdate') and opts.get('updaterev'):
731 731 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
732 732
733 733 r = hg.clone(hg.remoteui(ui, opts), source, dest,
734 734 pull=opts.get('pull'),
735 735 stream=opts.get('uncompressed'),
736 736 rev=opts.get('rev'),
737 737 update=opts.get('updaterev') or not opts.get('noupdate'),
738 738 branch=opts.get('branch'))
739 739
740 740 return r is None
741 741
742 742 def commit(ui, repo, *pats, **opts):
743 743 """commit the specified files or all outstanding changes
744 744
745 745 Commit changes to the given files into the repository. Unlike a
746 746 centralized RCS, this operation is a local operation. See
747 747 :hg:`push` for a way to actively distribute your changes.
748 748
749 749 If a list of files is omitted, all changes reported by :hg:`status`
750 750 will be committed.
751 751
752 752 If you are committing the result of a merge, do not provide any
753 753 filenames or -I/-X filters.
754 754
755 755 If no commit message is specified, Mercurial starts your
756 756 configured editor where you can enter a message. In case your
757 757 commit fails, you will find a backup of your message in
758 758 ``.hg/last-message.txt``.
759 759
760 760 See :hg:`help dates` for a list of formats valid for -d/--date.
761 761
762 762 Returns 0 on success, 1 if nothing changed.
763 763 """
764 764 extra = {}
765 765 if opts.get('close_branch'):
766 766 if repo['.'].node() not in repo.branchheads():
767 767 # The topo heads set is included in the branch heads set of the
768 768 # current branch, so it's sufficient to test branchheads
769 769 raise util.Abort(_('can only close branch heads'))
770 770 extra['close'] = 1
771 771 e = cmdutil.commiteditor
772 772 if opts.get('force_editor'):
773 773 e = cmdutil.commitforceeditor
774 774
775 775 def commitfunc(ui, repo, message, match, opts):
776 776 return repo.commit(message, opts.get('user'), opts.get('date'), match,
777 777 editor=e, extra=extra)
778 778
779 779 branch = repo[None].branch()
780 780 bheads = repo.branchheads(branch)
781 781
782 782 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
783 783 if not node:
784 784 ui.status(_("nothing changed\n"))
785 785 return 1
786 786
787 787 ctx = repo[node]
788 788 parents = ctx.parents()
789 789
790 790 if bheads and not [x for x in parents
791 791 if x.node() in bheads and x.branch() == branch]:
792 792 ui.status(_('created new head\n'))
793 793 # The message is not printed for initial roots. For the other
794 794 # changesets, it is printed in the following situations:
795 795 #
796 796 # Par column: for the 2 parents with ...
797 797 # N: null or no parent
798 798 # B: parent is on another named branch
799 799 # C: parent is a regular non head changeset
800 800 # H: parent was a branch head of the current branch
801 801 # Msg column: whether we print "created new head" message
802 802 # In the following, it is assumed that there already exists some
803 803 # initial branch heads of the current branch, otherwise nothing is
804 804 # printed anyway.
805 805 #
806 806 # Par Msg Comment
807 807 # NN y additional topo root
808 808 #
809 809 # BN y additional branch root
810 810 # CN y additional topo head
811 811 # HN n usual case
812 812 #
813 813 # BB y weird additional branch root
814 814 # CB y branch merge
815 815 # HB n merge with named branch
816 816 #
817 817 # CC y additional head from merge
818 818 # CH n merge with a head
819 819 #
820 820 # HH n head merge: head count decreases
821 821
822 822 if not opts.get('close_branch'):
823 823 for r in parents:
824 824 if r.extra().get('close') and r.branch() == branch:
825 825 ui.status(_('reopening closed branch head %d\n') % r)
826 826
827 827 if ui.debugflag:
828 828 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
829 829 elif ui.verbose:
830 830 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
831 831
832 832 def copy(ui, repo, *pats, **opts):
833 833 """mark files as copied for the next commit
834 834
835 835 Mark dest as having copies of source files. If dest is a
836 836 directory, copies are put in that directory. If dest is a file,
837 837 the source must be a single file.
838 838
839 839 By default, this command copies the contents of files as they
840 840 exist in the working directory. If invoked with -A/--after, the
841 841 operation is recorded, but no copying is performed.
842 842
843 843 This command takes effect with the next commit. To undo a copy
844 844 before that, see :hg:`revert`.
845 845
846 846 Returns 0 on success, 1 if errors are encountered.
847 847 """
848 848 wlock = repo.wlock(False)
849 849 try:
850 850 return cmdutil.copy(ui, repo, pats, opts)
851 851 finally:
852 852 wlock.release()
853 853
854 854 def debugancestor(ui, repo, *args):
855 855 """find the ancestor revision of two revisions in a given index"""
856 856 if len(args) == 3:
857 857 index, rev1, rev2 = args
858 858 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
859 859 lookup = r.lookup
860 860 elif len(args) == 2:
861 861 if not repo:
862 862 raise util.Abort(_("there is no Mercurial repository here "
863 863 "(.hg not found)"))
864 864 rev1, rev2 = args
865 865 r = repo.changelog
866 866 lookup = repo.lookup
867 867 else:
868 868 raise util.Abort(_('either two or three arguments required'))
869 869 a = r.ancestor(lookup(rev1), lookup(rev2))
870 870 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
871 871
872 872 def debugbuilddag(ui, repo, text,
873 873 mergeable_file=False,
874 874 appended_file=False,
875 875 overwritten_file=False,
876 876 new_file=False):
877 877 """builds a repo with a given dag from scratch in the current empty repo
878 878
879 879 Elements:
880 880
881 881 - "+n" is a linear run of n nodes based on the current default parent
882 882 - "." is a single node based on the current default parent
883 883 - "$" resets the default parent to null (implied at the start);
884 884 otherwise the default parent is always the last node created
885 885 - "<p" sets the default parent to the backref p
886 886 - "*p" is a fork at parent p, which is a backref
887 887 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
888 888 - "/p2" is a merge of the preceding node and p2
889 889 - ":tag" defines a local tag for the preceding node
890 890 - "@branch" sets the named branch for subsequent nodes
891 891 - "!command" runs the command using your shell
892 892 - "!!my command\\n" is like "!", but to the end of the line
893 893 - "#...\\n" is a comment up to the end of the line
894 894
895 895 Whitespace between the above elements is ignored.
896 896
897 897 A backref is either
898 898
899 899 - a number n, which references the node curr-n, where curr is the current
900 900 node, or
901 901 - the name of a local tag you placed earlier using ":tag", or
902 902 - empty to denote the default parent.
903 903
904 904 All string valued-elements are either strictly alphanumeric, or must
905 905 be enclosed in double quotes ("..."), with "\\" as escape character.
906 906
907 907 Note that the --overwritten-file and --appended-file options imply the
908 908 use of "HGMERGE=internal:local" during DAG buildup.
909 909 """
910 910
911 911 if not (mergeable_file or appended_file or overwritten_file or new_file):
912 912 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
913 913
914 914 if len(repo.changelog) > 0:
915 915 raise util.Abort(_('repository is not empty'))
916 916
917 917 if overwritten_file or appended_file:
918 918 # we don't want to fail in merges during buildup
919 919 os.environ['HGMERGE'] = 'internal:local'
920 920
921 921 def writefile(fname, text, fmode="wb"):
922 922 f = open(fname, fmode)
923 923 try:
924 924 f.write(text)
925 925 finally:
926 926 f.close()
927 927
928 928 if mergeable_file:
929 929 linesperrev = 2
930 930 # determine number of revs in DAG
931 931 n = 0
932 932 for type, data in dagparser.parsedag(text):
933 933 if type == 'n':
934 934 n += 1
935 935 # make a file with k lines per rev
936 936 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
937 937 + "\n")
938 938
939 939 at = -1
940 940 atbranch = 'default'
941 941 for type, data in dagparser.parsedag(text):
942 942 if type == 'n':
943 943 ui.status('node %s\n' % str(data))
944 944 id, ps = data
945 945 p1 = ps[0]
946 946 if p1 != at:
947 947 update(ui, repo, node=str(p1), clean=True)
948 948 at = p1
949 949 if repo.dirstate.branch() != atbranch:
950 950 branch(ui, repo, atbranch, force=True)
951 951 if len(ps) > 1:
952 952 p2 = ps[1]
953 953 merge(ui, repo, node=p2)
954 954
955 955 if mergeable_file:
956 956 f = open("mf", "rb+")
957 957 try:
958 958 lines = f.read().split("\n")
959 959 lines[id * linesperrev] += " r%i" % id
960 960 f.seek(0)
961 961 f.write("\n".join(lines))
962 962 finally:
963 963 f.close()
964 964
965 965 if appended_file:
966 966 writefile("af", "r%i\n" % id, "ab")
967 967
968 968 if overwritten_file:
969 969 writefile("of", "r%i\n" % id)
970 970
971 971 if new_file:
972 972 writefile("nf%i" % id, "r%i\n" % id)
973 973
974 974 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
975 975 at = id
976 976 elif type == 'l':
977 977 id, name = data
978 978 ui.status('tag %s\n' % name)
979 979 tag(ui, repo, name, local=True)
980 980 elif type == 'a':
981 981 ui.status('branch %s\n' % data)
982 982 atbranch = data
983 983 elif type in 'cC':
984 984 r = util.system(data, cwd=repo.root)
985 985 if r:
986 986 desc, r = util.explain_exit(r)
987 987 raise util.Abort(_('%s command %s') % (data, desc))
988 988
989 989 def debugcommands(ui, cmd='', *args):
990 990 """list all available commands and options"""
991 991 for cmd, vals in sorted(table.iteritems()):
992 992 cmd = cmd.split('|')[0].strip('^')
993 993 opts = ', '.join([i[1] for i in vals[1]])
994 994 ui.write('%s: %s\n' % (cmd, opts))
995 995
996 996 def debugcomplete(ui, cmd='', **opts):
997 997 """returns the completion list associated with the given command"""
998 998
999 999 if opts.get('options'):
1000 1000 options = []
1001 1001 otables = [globalopts]
1002 1002 if cmd:
1003 1003 aliases, entry = cmdutil.findcmd(cmd, table, False)
1004 1004 otables.append(entry[1])
1005 1005 for t in otables:
1006 1006 for o in t:
1007 1007 if "(DEPRECATED)" in o[3]:
1008 1008 continue
1009 1009 if o[0]:
1010 1010 options.append('-%s' % o[0])
1011 1011 options.append('--%s' % o[1])
1012 1012 ui.write("%s\n" % "\n".join(options))
1013 1013 return
1014 1014
1015 1015 cmdlist = cmdutil.findpossible(cmd, table)
1016 1016 if ui.verbose:
1017 1017 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1018 1018 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1019 1019
1020 1020 def debugfsinfo(ui, path = "."):
1021 1021 """show information detected about current filesystem"""
1022 1022 open('.debugfsinfo', 'w').write('')
1023 1023 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1024 1024 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1025 1025 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1026 1026 and 'yes' or 'no'))
1027 1027 os.unlink('.debugfsinfo')
1028 1028
1029 1029 def debugrebuildstate(ui, repo, rev="tip"):
1030 1030 """rebuild the dirstate as it would look like for the given revision"""
1031 1031 ctx = cmdutil.revsingle(repo, rev)
1032 1032 wlock = repo.wlock()
1033 1033 try:
1034 1034 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1035 1035 finally:
1036 1036 wlock.release()
1037 1037
1038 1038 def debugcheckstate(ui, repo):
1039 1039 """validate the correctness of the current dirstate"""
1040 1040 parent1, parent2 = repo.dirstate.parents()
1041 1041 m1 = repo[parent1].manifest()
1042 1042 m2 = repo[parent2].manifest()
1043 1043 errors = 0
1044 1044 for f in repo.dirstate:
1045 1045 state = repo.dirstate[f]
1046 1046 if state in "nr" and f not in m1:
1047 1047 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1048 1048 errors += 1
1049 1049 if state in "a" and f in m1:
1050 1050 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1051 1051 errors += 1
1052 1052 if state in "m" and f not in m1 and f not in m2:
1053 1053 ui.warn(_("%s in state %s, but not in either manifest\n") %
1054 1054 (f, state))
1055 1055 errors += 1
1056 1056 for f in m1:
1057 1057 state = repo.dirstate[f]
1058 1058 if state not in "nrm":
1059 1059 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1060 1060 errors += 1
1061 1061 if errors:
1062 1062 error = _(".hg/dirstate inconsistent with current parent's manifest")
1063 1063 raise util.Abort(error)
1064 1064
1065 1065 def showconfig(ui, repo, *values, **opts):
1066 1066 """show combined config settings from all hgrc files
1067 1067
1068 1068 With no arguments, print names and values of all config items.
1069 1069
1070 1070 With one argument of the form section.name, print just the value
1071 1071 of that config item.
1072 1072
1073 1073 With multiple arguments, print names and values of all config
1074 1074 items with matching section names.
1075 1075
1076 1076 With --debug, the source (filename and line number) is printed
1077 1077 for each config item.
1078 1078
1079 1079 Returns 0 on success.
1080 1080 """
1081 1081
1082 1082 for f in util.rcpath():
1083 1083 ui.debug(_('read config from: %s\n') % f)
1084 1084 untrusted = bool(opts.get('untrusted'))
1085 1085 if values:
1086 1086 sections = [v for v in values if '.' not in v]
1087 1087 items = [v for v in values if '.' in v]
1088 1088 if len(items) > 1 or items and sections:
1089 1089 raise util.Abort(_('only one config item permitted'))
1090 1090 for section, name, value in ui.walkconfig(untrusted=untrusted):
1091 1091 sectname = section + '.' + name
1092 1092 if values:
1093 1093 for v in values:
1094 1094 if v == section:
1095 1095 ui.debug('%s: ' %
1096 1096 ui.configsource(section, name, untrusted))
1097 1097 ui.write('%s=%s\n' % (sectname, value))
1098 1098 elif v == sectname:
1099 1099 ui.debug('%s: ' %
1100 1100 ui.configsource(section, name, untrusted))
1101 1101 ui.write(value, '\n')
1102 1102 else:
1103 1103 ui.debug('%s: ' %
1104 1104 ui.configsource(section, name, untrusted))
1105 1105 ui.write('%s=%s\n' % (sectname, value))
1106 1106
1107 1107 def debugpushkey(ui, repopath, namespace, *keyinfo):
1108 1108 '''access the pushkey key/value protocol
1109 1109
1110 1110 With two args, list the keys in the given namespace.
1111 1111
1112 1112 With five args, set a key to new if it currently is set to old.
1113 1113 Reports success or failure.
1114 1114 '''
1115 1115
1116 1116 target = hg.repository(ui, repopath)
1117 1117 if keyinfo:
1118 1118 key, old, new = keyinfo
1119 1119 r = target.pushkey(namespace, key, old, new)
1120 1120 ui.status(str(r) + '\n')
1121 1121 return not r
1122 1122 else:
1123 1123 for k, v in target.listkeys(namespace).iteritems():
1124 1124 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1125 1125 v.encode('string-escape')))
1126 1126
1127 1127 def debugrevspec(ui, repo, expr):
1128 1128 '''parse and apply a revision specification'''
1129 1129 if ui.verbose:
1130 1130 tree = revset.parse(expr)
1131 1131 ui.note(tree, "\n")
1132 1132 func = revset.match(expr)
1133 1133 for c in func(repo, range(len(repo))):
1134 1134 ui.write("%s\n" % c)
1135 1135
1136 1136 def debugsetparents(ui, repo, rev1, rev2=None):
1137 1137 """manually set the parents of the current working directory
1138 1138
1139 1139 This is useful for writing repository conversion tools, but should
1140 1140 be used with care.
1141 1141
1142 1142 Returns 0 on success.
1143 1143 """
1144 1144
1145 1145 r1 = cmdutil.revsingle(repo, rev1).node()
1146 1146 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1147 1147
1148 1148 wlock = repo.wlock()
1149 1149 try:
1150 1150 repo.dirstate.setparents(r1, r2)
1151 1151 finally:
1152 1152 wlock.release()
1153 1153
1154 1154 def debugstate(ui, repo, nodates=None):
1155 1155 """show the contents of the current dirstate"""
1156 1156 timestr = ""
1157 1157 showdate = not nodates
1158 1158 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1159 1159 if showdate:
1160 1160 if ent[3] == -1:
1161 1161 # Pad or slice to locale representation
1162 1162 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1163 1163 time.localtime(0)))
1164 1164 timestr = 'unset'
1165 1165 timestr = (timestr[:locale_len] +
1166 1166 ' ' * (locale_len - len(timestr)))
1167 1167 else:
1168 1168 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1169 1169 time.localtime(ent[3]))
1170 1170 if ent[1] & 020000:
1171 1171 mode = 'lnk'
1172 1172 else:
1173 1173 mode = '%3o' % (ent[1] & 0777)
1174 1174 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1175 1175 for f in repo.dirstate.copies():
1176 1176 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1177 1177
1178 1178 def debugsub(ui, repo, rev=None):
1179 1179 ctx = cmdutil.revsingle(repo, rev, None)
1180 1180 for k, v in sorted(ctx.substate.items()):
1181 1181 ui.write('path %s\n' % k)
1182 1182 ui.write(' source %s\n' % v[0])
1183 1183 ui.write(' revision %s\n' % v[1])
1184 1184
1185 1185 def debugdag(ui, repo, file_=None, *revs, **opts):
1186 1186 """format the changelog or an index DAG as a concise textual description
1187 1187
1188 1188 If you pass a revlog index, the revlog's DAG is emitted. If you list
1189 1189 revision numbers, they get labelled in the output as rN.
1190 1190
1191 1191 Otherwise, the changelog DAG of the current repo is emitted.
1192 1192 """
1193 1193 spaces = opts.get('spaces')
1194 1194 dots = opts.get('dots')
1195 1195 if file_:
1196 1196 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1197 1197 revs = set((int(r) for r in revs))
1198 1198 def events():
1199 1199 for r in rlog:
1200 1200 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1201 1201 if r in revs:
1202 1202 yield 'l', (r, "r%i" % r)
1203 1203 elif repo:
1204 1204 cl = repo.changelog
1205 1205 tags = opts.get('tags')
1206 1206 branches = opts.get('branches')
1207 1207 if tags:
1208 1208 labels = {}
1209 1209 for l, n in repo.tags().items():
1210 1210 labels.setdefault(cl.rev(n), []).append(l)
1211 1211 def events():
1212 1212 b = "default"
1213 1213 for r in cl:
1214 1214 if branches:
1215 1215 newb = cl.read(cl.node(r))[5]['branch']
1216 1216 if newb != b:
1217 1217 yield 'a', newb
1218 1218 b = newb
1219 1219 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1220 1220 if tags:
1221 1221 ls = labels.get(r)
1222 1222 if ls:
1223 1223 for l in ls:
1224 1224 yield 'l', (r, l)
1225 1225 else:
1226 1226 raise util.Abort(_('need repo for changelog dag'))
1227 1227
1228 1228 for line in dagparser.dagtextlines(events(),
1229 1229 addspaces=spaces,
1230 1230 wraplabels=True,
1231 1231 wrapannotations=True,
1232 1232 wrapnonlinear=dots,
1233 1233 usedots=dots,
1234 1234 maxlinewidth=70):
1235 1235 ui.write(line)
1236 1236 ui.write("\n")
1237 1237
1238 1238 def debugdata(ui, repo, file_, rev):
1239 1239 """dump the contents of a data file revision"""
1240 1240 r = None
1241 1241 if repo:
1242 1242 filelog = repo.file(file_)
1243 1243 if len(filelog):
1244 1244 r = filelog
1245 1245 if not r:
1246 1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1247 1247 try:
1248 1248 ui.write(r.revision(r.lookup(rev)))
1249 1249 except KeyError:
1250 1250 raise util.Abort(_('invalid revision identifier %s') % rev)
1251 1251
1252 1252 def debugdate(ui, date, range=None, **opts):
1253 1253 """parse and display a date"""
1254 1254 if opts["extended"]:
1255 1255 d = util.parsedate(date, util.extendeddateformats)
1256 1256 else:
1257 1257 d = util.parsedate(date)
1258 1258 ui.write("internal: %s %s\n" % d)
1259 1259 ui.write("standard: %s\n" % util.datestr(d))
1260 1260 if range:
1261 1261 m = util.matchdate(range)
1262 1262 ui.write("match: %s\n" % m(d[0]))
1263 1263
1264 1264 def debugindex(ui, repo, file_, **opts):
1265 1265 """dump the contents of an index file"""
1266 1266 r = None
1267 1267 if repo:
1268 1268 filelog = repo.file(file_)
1269 1269 if len(filelog):
1270 1270 r = filelog
1271 1271
1272 1272 format = opts.get('format', 0)
1273 1273 if format not in (0, 1):
1274 1274 raise util.Abort("unknown format %d" % format)
1275 1275
1276 1276 if not r:
1277 1277 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1278 1278
1279 1279 if format == 0:
1280 1280 ui.write(" rev offset length base linkrev"
1281 1281 " nodeid p1 p2\n")
1282 1282 elif format == 1:
1283 1283 ui.write(" rev flag offset length"
1284 1284 " size base link p1 p2 nodeid\n")
1285 1285
1286 1286 for i in r:
1287 1287 node = r.node(i)
1288 1288 if format == 0:
1289 1289 try:
1290 1290 pp = r.parents(node)
1291 1291 except:
1292 1292 pp = [nullid, nullid]
1293 1293 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1294 1294 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1295 1295 short(node), short(pp[0]), short(pp[1])))
1296 1296 elif format == 1:
1297 1297 pr = r.parentrevs(i)
1298 1298 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1299 1299 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1300 1300 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1301 1301
1302 1302 def debugindexdot(ui, repo, file_):
1303 1303 """dump an index DAG as a graphviz dot file"""
1304 1304 r = None
1305 1305 if repo:
1306 1306 filelog = repo.file(file_)
1307 1307 if len(filelog):
1308 1308 r = filelog
1309 1309 if not r:
1310 1310 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1311 1311 ui.write("digraph G {\n")
1312 1312 for i in r:
1313 1313 node = r.node(i)
1314 1314 pp = r.parents(node)
1315 1315 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1316 1316 if pp[1] != nullid:
1317 1317 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1318 1318 ui.write("}\n")
1319 1319
1320 1320 def debuginstall(ui):
1321 1321 '''test Mercurial installation
1322 1322
1323 1323 Returns 0 on success.
1324 1324 '''
1325 1325
1326 1326 def writetemp(contents):
1327 1327 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1328 1328 f = os.fdopen(fd, "wb")
1329 1329 f.write(contents)
1330 1330 f.close()
1331 1331 return name
1332 1332
1333 1333 problems = 0
1334 1334
1335 1335 # encoding
1336 1336 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1337 1337 try:
1338 1338 encoding.fromlocal("test")
1339 1339 except util.Abort, inst:
1340 1340 ui.write(" %s\n" % inst)
1341 1341 ui.write(_(" (check that your locale is properly set)\n"))
1342 1342 problems += 1
1343 1343
1344 1344 # compiled modules
1345 1345 ui.status(_("Checking installed modules (%s)...\n")
1346 1346 % os.path.dirname(__file__))
1347 1347 try:
1348 1348 import bdiff, mpatch, base85, osutil
1349 1349 except Exception, inst:
1350 1350 ui.write(" %s\n" % inst)
1351 1351 ui.write(_(" One or more extensions could not be found"))
1352 1352 ui.write(_(" (check that you compiled the extensions)\n"))
1353 1353 problems += 1
1354 1354
1355 1355 # templates
1356 1356 ui.status(_("Checking templates...\n"))
1357 1357 try:
1358 1358 import templater
1359 1359 templater.templater(templater.templatepath("map-cmdline.default"))
1360 1360 except Exception, inst:
1361 1361 ui.write(" %s\n" % inst)
1362 1362 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1363 1363 problems += 1
1364 1364
1365 1365 # patch
1366 1366 ui.status(_("Checking patch...\n"))
1367 1367 patchproblems = 0
1368 1368 a = "1\n2\n3\n4\n"
1369 1369 b = "1\n2\n3\ninsert\n4\n"
1370 1370 fa = writetemp(a)
1371 1371 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1372 1372 os.path.basename(fa))
1373 1373 fd = writetemp(d)
1374 1374
1375 1375 files = {}
1376 1376 try:
1377 1377 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1378 1378 except util.Abort, e:
1379 1379 ui.write(_(" patch call failed:\n"))
1380 1380 ui.write(" " + str(e) + "\n")
1381 1381 patchproblems += 1
1382 1382 else:
1383 1383 if list(files) != [os.path.basename(fa)]:
1384 1384 ui.write(_(" unexpected patch output!\n"))
1385 1385 patchproblems += 1
1386 1386 a = open(fa).read()
1387 1387 if a != b:
1388 1388 ui.write(_(" patch test failed!\n"))
1389 1389 patchproblems += 1
1390 1390
1391 1391 if patchproblems:
1392 1392 if ui.config('ui', 'patch'):
1393 1393 ui.write(_(" (Current patch tool may be incompatible with patch,"
1394 1394 " or misconfigured. Please check your configuration"
1395 1395 " file)\n"))
1396 1396 else:
1397 1397 ui.write(_(" Internal patcher failure, please report this error"
1398 1398 " to http://mercurial.selenic.com/wiki/BugTracker\n"))
1399 1399 problems += patchproblems
1400 1400
1401 1401 os.unlink(fa)
1402 1402 os.unlink(fd)
1403 1403
1404 1404 # editor
1405 1405 ui.status(_("Checking commit editor...\n"))
1406 1406 editor = ui.geteditor()
1407 1407 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1408 1408 if not cmdpath:
1409 1409 if editor == 'vi':
1410 1410 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1411 1411 ui.write(_(" (specify a commit editor in your configuration"
1412 1412 " file)\n"))
1413 1413 else:
1414 1414 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1415 1415 ui.write(_(" (specify a commit editor in your configuration"
1416 1416 " file)\n"))
1417 1417 problems += 1
1418 1418
1419 1419 # check username
1420 1420 ui.status(_("Checking username...\n"))
1421 1421 try:
1422 1422 ui.username()
1423 1423 except util.Abort, e:
1424 1424 ui.write(" %s\n" % e)
1425 1425 ui.write(_(" (specify a username in your configuration file)\n"))
1426 1426 problems += 1
1427 1427
1428 1428 if not problems:
1429 1429 ui.status(_("No problems detected\n"))
1430 1430 else:
1431 1431 ui.write(_("%s problems detected,"
1432 1432 " please check your install!\n") % problems)
1433 1433
1434 1434 return problems
1435 1435
1436 1436 def debugrename(ui, repo, file1, *pats, **opts):
1437 1437 """dump rename information"""
1438 1438
1439 1439 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1440 1440 m = cmdutil.match(repo, (file1,) + pats, opts)
1441 1441 for abs in ctx.walk(m):
1442 1442 fctx = ctx[abs]
1443 1443 o = fctx.filelog().renamed(fctx.filenode())
1444 1444 rel = m.rel(abs)
1445 1445 if o:
1446 1446 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1447 1447 else:
1448 1448 ui.write(_("%s not renamed\n") % rel)
1449 1449
1450 1450 def debugwalk(ui, repo, *pats, **opts):
1451 1451 """show how files match on given patterns"""
1452 1452 m = cmdutil.match(repo, pats, opts)
1453 1453 items = list(repo.walk(m))
1454 1454 if not items:
1455 1455 return
1456 1456 fmt = 'f %%-%ds %%-%ds %%s' % (
1457 1457 max([len(abs) for abs in items]),
1458 1458 max([len(m.rel(abs)) for abs in items]))
1459 1459 for abs in items:
1460 1460 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1461 1461 ui.write("%s\n" % line.rstrip())
1462 1462
1463 1463 def diff(ui, repo, *pats, **opts):
1464 1464 """diff repository (or selected files)
1465 1465
1466 1466 Show differences between revisions for the specified files.
1467 1467
1468 1468 Differences between files are shown using the unified diff format.
1469 1469
1470 1470 .. note::
1471 1471 diff may generate unexpected results for merges, as it will
1472 1472 default to comparing against the working directory's first
1473 1473 parent changeset if no revisions are specified.
1474 1474
1475 1475 When two revision arguments are given, then changes are shown
1476 1476 between those revisions. If only one revision is specified then
1477 1477 that revision is compared to the working directory, and, when no
1478 1478 revisions are specified, the working directory files are compared
1479 1479 to its parent.
1480 1480
1481 1481 Alternatively you can specify -c/--change with a revision to see
1482 1482 the changes in that changeset relative to its first parent.
1483 1483
1484 1484 Without the -a/--text option, diff will avoid generating diffs of
1485 1485 files it detects as binary. With -a, diff will generate a diff
1486 1486 anyway, probably with undesirable results.
1487 1487
1488 1488 Use the -g/--git option to generate diffs in the git extended diff
1489 1489 format. For more information, read :hg:`help diffs`.
1490 1490
1491 1491 Returns 0 on success.
1492 1492 """
1493 1493
1494 1494 revs = opts.get('rev')
1495 1495 change = opts.get('change')
1496 1496 stat = opts.get('stat')
1497 1497 reverse = opts.get('reverse')
1498 1498
1499 1499 if revs and change:
1500 1500 msg = _('cannot specify --rev and --change at the same time')
1501 1501 raise util.Abort(msg)
1502 1502 elif change:
1503 1503 node2 = repo.lookup(change)
1504 1504 node1 = repo[node2].parents()[0].node()
1505 1505 else:
1506 1506 node1, node2 = cmdutil.revpair(repo, revs)
1507 1507
1508 1508 if reverse:
1509 1509 node1, node2 = node2, node1
1510 1510
1511 1511 diffopts = patch.diffopts(ui, opts)
1512 1512 m = cmdutil.match(repo, pats, opts)
1513 1513 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1514 1514 listsubrepos=opts.get('subrepos'))
1515 1515
1516 1516 def export(ui, repo, *changesets, **opts):
1517 1517 """dump the header and diffs for one or more changesets
1518 1518
1519 1519 Print the changeset header and diffs for one or more revisions.
1520 1520
1521 1521 The information shown in the changeset header is: author, date,
1522 1522 branch name (if non-default), changeset hash, parent(s) and commit
1523 1523 comment.
1524 1524
1525 1525 .. note::
1526 1526 export may generate unexpected diff output for merge
1527 1527 changesets, as it will compare the merge changeset against its
1528 1528 first parent only.
1529 1529
1530 1530 Output may be to a file, in which case the name of the file is
1531 1531 given using a format string. The formatting rules are as follows:
1532 1532
1533 1533 :``%%``: literal "%" character
1534 1534 :``%H``: changeset hash (40 hexadecimal digits)
1535 1535 :``%N``: number of patches being generated
1536 1536 :``%R``: changeset revision number
1537 1537 :``%b``: basename of the exporting repository
1538 1538 :``%h``: short-form changeset hash (12 hexadecimal digits)
1539 1539 :``%n``: zero-padded sequence number, starting at 1
1540 1540 :``%r``: zero-padded changeset revision number
1541 1541
1542 1542 Without the -a/--text option, export will avoid generating diffs
1543 1543 of files it detects as binary. With -a, export will generate a
1544 1544 diff anyway, probably with undesirable results.
1545 1545
1546 1546 Use the -g/--git option to generate diffs in the git extended diff
1547 1547 format. See :hg:`help diffs` for more information.
1548 1548
1549 1549 With the --switch-parent option, the diff will be against the
1550 1550 second parent. It can be useful to review a merge.
1551 1551
1552 1552 Returns 0 on success.
1553 1553 """
1554 1554 changesets += tuple(opts.get('rev', []))
1555 1555 if not changesets:
1556 1556 raise util.Abort(_("export requires at least one changeset"))
1557 1557 revs = cmdutil.revrange(repo, changesets)
1558 1558 if len(revs) > 1:
1559 1559 ui.note(_('exporting patches:\n'))
1560 1560 else:
1561 1561 ui.note(_('exporting patch:\n'))
1562 1562 cmdutil.export(repo, revs, template=opts.get('output'),
1563 1563 switch_parent=opts.get('switch_parent'),
1564 1564 opts=patch.diffopts(ui, opts))
1565 1565
1566 1566 def forget(ui, repo, *pats, **opts):
1567 1567 """forget the specified files on the next commit
1568 1568
1569 1569 Mark the specified files so they will no longer be tracked
1570 1570 after the next commit.
1571 1571
1572 1572 This only removes files from the current branch, not from the
1573 1573 entire project history, and it does not delete them from the
1574 1574 working directory.
1575 1575
1576 1576 To undo a forget before the next commit, see :hg:`add`.
1577 1577
1578 1578 Returns 0 on success.
1579 1579 """
1580 1580
1581 1581 if not pats:
1582 1582 raise util.Abort(_('no files specified'))
1583 1583
1584 1584 m = cmdutil.match(repo, pats, opts)
1585 1585 s = repo.status(match=m, clean=True)
1586 1586 forget = sorted(s[0] + s[1] + s[3] + s[6])
1587 1587 errs = 0
1588 1588
1589 1589 for f in m.files():
1590 1590 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1591 1591 ui.warn(_('not removing %s: file is already untracked\n')
1592 1592 % m.rel(f))
1593 1593 errs = 1
1594 1594
1595 1595 for f in forget:
1596 1596 if ui.verbose or not m.exact(f):
1597 1597 ui.status(_('removing %s\n') % m.rel(f))
1598 1598
1599 1599 repo[None].remove(forget, unlink=False)
1600 1600 return errs
1601 1601
1602 1602 def grep(ui, repo, pattern, *pats, **opts):
1603 1603 """search for a pattern in specified files and revisions
1604 1604
1605 1605 Search revisions of files for a regular expression.
1606 1606
1607 1607 This command behaves differently than Unix grep. It only accepts
1608 1608 Python/Perl regexps. It searches repository history, not the
1609 1609 working directory. It always prints the revision number in which a
1610 1610 match appears.
1611 1611
1612 1612 By default, grep only prints output for the first revision of a
1613 1613 file in which it finds a match. To get it to print every revision
1614 1614 that contains a change in match status ("-" for a match that
1615 1615 becomes a non-match, or "+" for a non-match that becomes a match),
1616 1616 use the --all flag.
1617 1617
1618 1618 Returns 0 if a match is found, 1 otherwise.
1619 1619 """
1620 1620 reflags = 0
1621 1621 if opts.get('ignore_case'):
1622 1622 reflags |= re.I
1623 1623 try:
1624 1624 regexp = re.compile(pattern, reflags)
1625 1625 except re.error, inst:
1626 1626 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1627 1627 return 1
1628 1628 sep, eol = ':', '\n'
1629 1629 if opts.get('print0'):
1630 1630 sep = eol = '\0'
1631 1631
1632 1632 getfile = util.lrucachefunc(repo.file)
1633 1633
1634 1634 def matchlines(body):
1635 1635 begin = 0
1636 1636 linenum = 0
1637 1637 while True:
1638 1638 match = regexp.search(body, begin)
1639 1639 if not match:
1640 1640 break
1641 1641 mstart, mend = match.span()
1642 1642 linenum += body.count('\n', begin, mstart) + 1
1643 1643 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1644 1644 begin = body.find('\n', mend) + 1 or len(body)
1645 1645 lend = begin - 1
1646 1646 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1647 1647
1648 1648 class linestate(object):
1649 1649 def __init__(self, line, linenum, colstart, colend):
1650 1650 self.line = line
1651 1651 self.linenum = linenum
1652 1652 self.colstart = colstart
1653 1653 self.colend = colend
1654 1654
1655 1655 def __hash__(self):
1656 1656 return hash((self.linenum, self.line))
1657 1657
1658 1658 def __eq__(self, other):
1659 1659 return self.line == other.line
1660 1660
1661 1661 matches = {}
1662 1662 copies = {}
1663 1663 def grepbody(fn, rev, body):
1664 1664 matches[rev].setdefault(fn, [])
1665 1665 m = matches[rev][fn]
1666 1666 for lnum, cstart, cend, line in matchlines(body):
1667 1667 s = linestate(line, lnum, cstart, cend)
1668 1668 m.append(s)
1669 1669
1670 1670 def difflinestates(a, b):
1671 1671 sm = difflib.SequenceMatcher(None, a, b)
1672 1672 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1673 1673 if tag == 'insert':
1674 1674 for i in xrange(blo, bhi):
1675 1675 yield ('+', b[i])
1676 1676 elif tag == 'delete':
1677 1677 for i in xrange(alo, ahi):
1678 1678 yield ('-', a[i])
1679 1679 elif tag == 'replace':
1680 1680 for i in xrange(alo, ahi):
1681 1681 yield ('-', a[i])
1682 1682 for i in xrange(blo, bhi):
1683 1683 yield ('+', b[i])
1684 1684
1685 1685 def display(fn, ctx, pstates, states):
1686 1686 rev = ctx.rev()
1687 1687 datefunc = ui.quiet and util.shortdate or util.datestr
1688 1688 found = False
1689 1689 filerevmatches = {}
1690 1690 if opts.get('all'):
1691 1691 iter = difflinestates(pstates, states)
1692 1692 else:
1693 1693 iter = [('', l) for l in states]
1694 1694 for change, l in iter:
1695 1695 cols = [fn, str(rev)]
1696 1696 before, match, after = None, None, None
1697 1697 if opts.get('line_number'):
1698 1698 cols.append(str(l.linenum))
1699 1699 if opts.get('all'):
1700 1700 cols.append(change)
1701 1701 if opts.get('user'):
1702 1702 cols.append(ui.shortuser(ctx.user()))
1703 1703 if opts.get('date'):
1704 1704 cols.append(datefunc(ctx.date()))
1705 1705 if opts.get('files_with_matches'):
1706 1706 c = (fn, rev)
1707 1707 if c in filerevmatches:
1708 1708 continue
1709 1709 filerevmatches[c] = 1
1710 1710 else:
1711 1711 before = l.line[:l.colstart]
1712 1712 match = l.line[l.colstart:l.colend]
1713 1713 after = l.line[l.colend:]
1714 1714 ui.write(sep.join(cols))
1715 1715 if before is not None:
1716 1716 ui.write(sep + before)
1717 1717 ui.write(match, label='grep.match')
1718 1718 ui.write(after)
1719 1719 ui.write(eol)
1720 1720 found = True
1721 1721 return found
1722 1722
1723 1723 skip = {}
1724 1724 revfiles = {}
1725 1725 matchfn = cmdutil.match(repo, pats, opts)
1726 1726 found = False
1727 1727 follow = opts.get('follow')
1728 1728
1729 1729 def prep(ctx, fns):
1730 1730 rev = ctx.rev()
1731 1731 pctx = ctx.parents()[0]
1732 1732 parent = pctx.rev()
1733 1733 matches.setdefault(rev, {})
1734 1734 matches.setdefault(parent, {})
1735 1735 files = revfiles.setdefault(rev, [])
1736 1736 for fn in fns:
1737 1737 flog = getfile(fn)
1738 1738 try:
1739 1739 fnode = ctx.filenode(fn)
1740 1740 except error.LookupError:
1741 1741 continue
1742 1742
1743 1743 copied = flog.renamed(fnode)
1744 1744 copy = follow and copied and copied[0]
1745 1745 if copy:
1746 1746 copies.setdefault(rev, {})[fn] = copy
1747 1747 if fn in skip:
1748 1748 if copy:
1749 1749 skip[copy] = True
1750 1750 continue
1751 1751 files.append(fn)
1752 1752
1753 1753 if fn not in matches[rev]:
1754 1754 grepbody(fn, rev, flog.read(fnode))
1755 1755
1756 1756 pfn = copy or fn
1757 1757 if pfn not in matches[parent]:
1758 1758 try:
1759 1759 fnode = pctx.filenode(pfn)
1760 1760 grepbody(pfn, parent, flog.read(fnode))
1761 1761 except error.LookupError:
1762 1762 pass
1763 1763
1764 1764 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1765 1765 rev = ctx.rev()
1766 1766 parent = ctx.parents()[0].rev()
1767 1767 for fn in sorted(revfiles.get(rev, [])):
1768 1768 states = matches[rev][fn]
1769 1769 copy = copies.get(rev, {}).get(fn)
1770 1770 if fn in skip:
1771 1771 if copy:
1772 1772 skip[copy] = True
1773 1773 continue
1774 1774 pstates = matches.get(parent, {}).get(copy or fn, [])
1775 1775 if pstates or states:
1776 1776 r = display(fn, ctx, pstates, states)
1777 1777 found = found or r
1778 1778 if r and not opts.get('all'):
1779 1779 skip[fn] = True
1780 1780 if copy:
1781 1781 skip[copy] = True
1782 1782 del matches[rev]
1783 1783 del revfiles[rev]
1784 1784
1785 1785 return not found
1786 1786
1787 1787 def heads(ui, repo, *branchrevs, **opts):
1788 1788 """show current repository heads or show branch heads
1789 1789
1790 1790 With no arguments, show all repository branch heads.
1791 1791
1792 1792 Repository "heads" are changesets with no child changesets. They are
1793 1793 where development generally takes place and are the usual targets
1794 1794 for update and merge operations. Branch heads are changesets that have
1795 1795 no child changeset on the same branch.
1796 1796
1797 1797 If one or more REVs are given, only branch heads on the branches
1798 1798 associated with the specified changesets are shown.
1799 1799
1800 1800 If -c/--closed is specified, also show branch heads marked closed
1801 1801 (see :hg:`commit --close-branch`).
1802 1802
1803 1803 If STARTREV is specified, only those heads that are descendants of
1804 1804 STARTREV will be displayed.
1805 1805
1806 1806 If -t/--topo is specified, named branch mechanics will be ignored and only
1807 1807 changesets without children will be shown.
1808 1808
1809 1809 Returns 0 if matching heads are found, 1 if not.
1810 1810 """
1811 1811
1812 1812 start = None
1813 1813 if 'rev' in opts:
1814 1814 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1815 1815
1816 1816 if opts.get('topo'):
1817 1817 heads = [repo[h] for h in repo.heads(start)]
1818 1818 else:
1819 1819 heads = []
1820 1820 for b, ls in repo.branchmap().iteritems():
1821 1821 if start is None:
1822 1822 heads += [repo[h] for h in ls]
1823 1823 continue
1824 1824 startrev = repo.changelog.rev(start)
1825 1825 descendants = set(repo.changelog.descendants(startrev))
1826 1826 descendants.add(startrev)
1827 1827 rev = repo.changelog.rev
1828 1828 heads += [repo[h] for h in ls if rev(h) in descendants]
1829 1829
1830 1830 if branchrevs:
1831 1831 branches = set(repo[br].branch() for br in branchrevs)
1832 1832 heads = [h for h in heads if h.branch() in branches]
1833 1833
1834 1834 if not opts.get('closed'):
1835 1835 heads = [h for h in heads if not h.extra().get('close')]
1836 1836
1837 1837 if opts.get('active') and branchrevs:
1838 1838 dagheads = repo.heads(start)
1839 1839 heads = [h for h in heads if h.node() in dagheads]
1840 1840
1841 1841 if branchrevs:
1842 1842 haveheads = set(h.branch() for h in heads)
1843 1843 if branches - haveheads:
1844 1844 headless = ', '.join(b for b in branches - haveheads)
1845 1845 msg = _('no open branch heads found on branches %s')
1846 1846 if opts.get('rev'):
1847 1847 msg += _(' (started at %s)' % opts['rev'])
1848 1848 ui.warn((msg + '\n') % headless)
1849 1849
1850 1850 if not heads:
1851 1851 return 1
1852 1852
1853 1853 heads = sorted(heads, key=lambda x: -x.rev())
1854 1854 displayer = cmdutil.show_changeset(ui, repo, opts)
1855 1855 for ctx in heads:
1856 1856 displayer.show(ctx)
1857 1857 displayer.close()
1858 1858
1859 1859 def help_(ui, name=None, with_version=False, unknowncmd=False):
1860 1860 """show help for a given topic or a help overview
1861 1861
1862 1862 With no arguments, print a list of commands with short help messages.
1863 1863
1864 1864 Given a topic, extension, or command name, print help for that
1865 1865 topic.
1866 1866
1867 1867 Returns 0 if successful.
1868 1868 """
1869 1869 option_lists = []
1870 1870 textwidth = ui.termwidth() - 2
1871 1871
1872 1872 def addglobalopts(aliases):
1873 1873 if ui.verbose:
1874 1874 option_lists.append((_("global options:"), globalopts))
1875 1875 if name == 'shortlist':
1876 1876 option_lists.append((_('use "hg help" for the full list '
1877 1877 'of commands'), ()))
1878 1878 else:
1879 1879 if name == 'shortlist':
1880 1880 msg = _('use "hg help" for the full list of commands '
1881 1881 'or "hg -v" for details')
1882 1882 elif aliases:
1883 1883 msg = _('use "hg -v help%s" to show builtin aliases and '
1884 1884 'global options') % (name and " " + name or "")
1885 1885 else:
1886 1886 msg = _('use "hg -v help %s" to show global options') % name
1887 1887 option_lists.append((msg, ()))
1888 1888
1889 1889 def helpcmd(name):
1890 1890 if with_version:
1891 1891 version_(ui)
1892 1892 ui.write('\n')
1893 1893
1894 1894 try:
1895 1895 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1896 1896 except error.AmbiguousCommand, inst:
1897 1897 # py3k fix: except vars can't be used outside the scope of the
1898 1898 # except block, nor can be used inside a lambda. python issue4617
1899 1899 prefix = inst.args[0]
1900 1900 select = lambda c: c.lstrip('^').startswith(prefix)
1901 1901 helplist(_('list of commands:\n\n'), select)
1902 1902 return
1903 1903
1904 1904 # check if it's an invalid alias and display its error if it is
1905 1905 if getattr(entry[0], 'badalias', False):
1906 1906 if not unknowncmd:
1907 1907 entry[0](ui)
1908 1908 return
1909 1909
1910 1910 # synopsis
1911 1911 if len(entry) > 2:
1912 1912 if entry[2].startswith('hg'):
1913 1913 ui.write("%s\n" % entry[2])
1914 1914 else:
1915 1915 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1916 1916 else:
1917 1917 ui.write('hg %s\n' % aliases[0])
1918 1918
1919 1919 # aliases
1920 1920 if not ui.quiet and len(aliases) > 1:
1921 1921 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1922 1922
1923 1923 # description
1924 1924 doc = gettext(entry[0].__doc__)
1925 1925 if not doc:
1926 1926 doc = _("(no help text available)")
1927 1927 if hasattr(entry[0], 'definition'): # aliased command
1928 1928 if entry[0].definition.startswith('!'): # shell alias
1929 1929 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1930 1930 else:
1931 1931 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1932 1932 if ui.quiet:
1933 1933 doc = doc.splitlines()[0]
1934 1934 keep = ui.verbose and ['verbose'] or []
1935 1935 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1936 1936 ui.write("\n%s\n" % formatted)
1937 1937 if pruned:
1938 1938 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1939 1939
1940 1940 if not ui.quiet:
1941 1941 # options
1942 1942 if entry[1]:
1943 1943 option_lists.append((_("options:\n"), entry[1]))
1944 1944
1945 1945 addglobalopts(False)
1946 1946
1947 1947 def helplist(header, select=None):
1948 1948 h = {}
1949 1949 cmds = {}
1950 1950 for c, e in table.iteritems():
1951 1951 f = c.split("|", 1)[0]
1952 1952 if select and not select(f):
1953 1953 continue
1954 1954 if (not select and name != 'shortlist' and
1955 1955 e[0].__module__ != __name__):
1956 1956 continue
1957 1957 if name == "shortlist" and not f.startswith("^"):
1958 1958 continue
1959 1959 f = f.lstrip("^")
1960 1960 if not ui.debugflag and f.startswith("debug"):
1961 1961 continue
1962 1962 doc = e[0].__doc__
1963 1963 if doc and 'DEPRECATED' in doc and not ui.verbose:
1964 1964 continue
1965 1965 doc = gettext(doc)
1966 1966 if not doc:
1967 1967 doc = _("(no help text available)")
1968 1968 h[f] = doc.splitlines()[0].rstrip()
1969 1969 cmds[f] = c.lstrip("^")
1970 1970
1971 1971 if not h:
1972 1972 ui.status(_('no commands defined\n'))
1973 1973 return
1974 1974
1975 1975 ui.status(header)
1976 1976 fns = sorted(h)
1977 1977 m = max(map(len, fns))
1978 1978 for f in fns:
1979 1979 if ui.verbose:
1980 1980 commands = cmds[f].replace("|",", ")
1981 1981 ui.write(" %s:\n %s\n"%(commands, h[f]))
1982 1982 else:
1983 1983 ui.write('%s\n' % (util.wrap(h[f], textwidth,
1984 1984 initindent=' %-*s ' % (m, f),
1985 1985 hangindent=' ' * (m + 4))))
1986 1986
1987 1987 if not ui.quiet:
1988 1988 addglobalopts(True)
1989 1989
1990 1990 def helptopic(name):
1991 1991 for names, header, doc in help.helptable:
1992 1992 if name in names:
1993 1993 break
1994 1994 else:
1995 1995 raise error.UnknownCommand(name)
1996 1996
1997 1997 # description
1998 1998 if not doc:
1999 1999 doc = _("(no help text available)")
2000 2000 if hasattr(doc, '__call__'):
2001 2001 doc = doc()
2002 2002
2003 2003 ui.write("%s\n\n" % header)
2004 2004 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2005 2005
2006 2006 def helpext(name):
2007 2007 try:
2008 2008 mod = extensions.find(name)
2009 2009 doc = gettext(mod.__doc__) or _('no help text available')
2010 2010 except KeyError:
2011 2011 mod = None
2012 2012 doc = extensions.disabledext(name)
2013 2013 if not doc:
2014 2014 raise error.UnknownCommand(name)
2015 2015
2016 2016 if '\n' not in doc:
2017 2017 head, tail = doc, ""
2018 2018 else:
2019 2019 head, tail = doc.split('\n', 1)
2020 2020 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2021 2021 if tail:
2022 2022 ui.write(minirst.format(tail, textwidth))
2023 2023 ui.status('\n\n')
2024 2024
2025 2025 if mod:
2026 2026 try:
2027 2027 ct = mod.cmdtable
2028 2028 except AttributeError:
2029 2029 ct = {}
2030 2030 modcmds = set([c.split('|', 1)[0] for c in ct])
2031 2031 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2032 2032 else:
2033 2033 ui.write(_('use "hg help extensions" for information on enabling '
2034 2034 'extensions\n'))
2035 2035
2036 2036 def helpextcmd(name):
2037 2037 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2038 2038 doc = gettext(mod.__doc__).splitlines()[0]
2039 2039
2040 2040 msg = help.listexts(_("'%s' is provided by the following "
2041 2041 "extension:") % cmd, {ext: doc}, len(ext),
2042 2042 indent=4)
2043 2043 ui.write(minirst.format(msg, textwidth))
2044 2044 ui.write('\n\n')
2045 2045 ui.write(_('use "hg help extensions" for information on enabling '
2046 2046 'extensions\n'))
2047 2047
2048 2048 help.addtopichook('revsets', revset.makedoc)
2049 2049
2050 2050 if name and name != 'shortlist':
2051 2051 i = None
2052 2052 if unknowncmd:
2053 2053 queries = (helpextcmd,)
2054 2054 else:
2055 2055 queries = (helptopic, helpcmd, helpext, helpextcmd)
2056 2056 for f in queries:
2057 2057 try:
2058 2058 f(name)
2059 2059 i = None
2060 2060 break
2061 2061 except error.UnknownCommand, inst:
2062 2062 i = inst
2063 2063 if i:
2064 2064 raise i
2065 2065
2066 2066 else:
2067 2067 # program name
2068 2068 if ui.verbose or with_version:
2069 2069 version_(ui)
2070 2070 else:
2071 2071 ui.status(_("Mercurial Distributed SCM\n"))
2072 2072 ui.status('\n')
2073 2073
2074 2074 # list of commands
2075 2075 if name == "shortlist":
2076 2076 header = _('basic commands:\n\n')
2077 2077 else:
2078 2078 header = _('list of commands:\n\n')
2079 2079
2080 2080 helplist(header)
2081 2081 if name != 'shortlist':
2082 2082 exts, maxlength = extensions.enabled()
2083 2083 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2084 2084 if text:
2085 2085 ui.write("\n%s\n" % minirst.format(text, textwidth))
2086 2086
2087 2087 # list all option lists
2088 2088 opt_output = []
2089 2089 multioccur = False
2090 2090 for title, options in option_lists:
2091 2091 opt_output.append(("\n%s" % title, None))
2092 2092 for option in options:
2093 2093 if len(option) == 5:
2094 2094 shortopt, longopt, default, desc, optlabel = option
2095 2095 else:
2096 2096 shortopt, longopt, default, desc = option
2097 2097 optlabel = _("VALUE") # default label
2098 2098
2099 2099 if _("DEPRECATED") in desc and not ui.verbose:
2100 2100 continue
2101 2101 if isinstance(default, list):
2102 2102 numqualifier = " %s [+]" % optlabel
2103 2103 multioccur = True
2104 2104 elif (default is not None) and not isinstance(default, bool):
2105 2105 numqualifier = " %s" % optlabel
2106 2106 else:
2107 2107 numqualifier = ""
2108 2108 opt_output.append(("%2s%s" %
2109 2109 (shortopt and "-%s" % shortopt,
2110 2110 longopt and " --%s%s" %
2111 2111 (longopt, numqualifier)),
2112 2112 "%s%s" % (desc,
2113 2113 default
2114 2114 and _(" (default: %s)") % default
2115 2115 or "")))
2116 2116 if multioccur:
2117 2117 msg = _("\n[+] marked option can be specified multiple times")
2118 2118 if ui.verbose and name != 'shortlist':
2119 2119 opt_output.append((msg, None))
2120 2120 else:
2121 2121 opt_output.insert(-1, (msg, None))
2122 2122
2123 2123 if not name:
2124 2124 ui.write(_("\nadditional help topics:\n\n"))
2125 2125 topics = []
2126 2126 for names, header, doc in help.helptable:
2127 2127 topics.append((sorted(names, key=len, reverse=True)[0], header))
2128 2128 topics_len = max([len(s[0]) for s in topics])
2129 2129 for t, desc in topics:
2130 2130 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2131 2131
2132 2132 if opt_output:
2133 2133 colwidth = encoding.colwidth
2134 2134 # normalize: (opt or message, desc or None, width of opt)
2135 2135 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2136 2136 for opt, desc in opt_output]
2137 2137 hanging = max([e[2] for e in entries])
2138 2138 for opt, desc, width in entries:
2139 2139 if desc:
2140 2140 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2141 2141 hangindent = ' ' * (hanging + 3)
2142 2142 ui.write('%s\n' % (util.wrap(desc, textwidth,
2143 2143 initindent=initindent,
2144 2144 hangindent=hangindent)))
2145 2145 else:
2146 2146 ui.write("%s\n" % opt)
2147 2147
2148 2148 def identify(ui, repo, source=None,
2149 2149 rev=None, num=None, id=None, branch=None, tags=None):
2150 2150 """identify the working copy or specified revision
2151 2151
2152 2152 With no revision, print a summary of the current state of the
2153 2153 repository.
2154 2154
2155 2155 Specifying a path to a repository root or Mercurial bundle will
2156 2156 cause lookup to operate on that repository/bundle.
2157 2157
2158 2158 This summary identifies the repository state using one or two
2159 2159 parent hash identifiers, followed by a "+" if there are
2160 2160 uncommitted changes in the working directory, a list of tags for
2161 2161 this revision and a branch name for non-default branches.
2162 2162
2163 2163 Returns 0 if successful.
2164 2164 """
2165 2165
2166 2166 if not repo and not source:
2167 2167 raise util.Abort(_("there is no Mercurial repository here "
2168 2168 "(.hg not found)"))
2169 2169
2170 2170 hexfunc = ui.debugflag and hex or short
2171 2171 default = not (num or id or branch or tags)
2172 2172 output = []
2173 2173
2174 2174 revs = []
2175 2175 if source:
2176 2176 source, branches = hg.parseurl(ui.expandpath(source))
2177 2177 repo = hg.repository(ui, source)
2178 2178 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2179 2179
2180 2180 if not repo.local():
2181 2181 if not rev and revs:
2182 2182 rev = revs[0]
2183 2183 if not rev:
2184 2184 rev = "tip"
2185 2185 if num or branch or tags:
2186 2186 raise util.Abort(
2187 2187 "can't query remote revision number, branch, or tags")
2188 2188 output = [hexfunc(repo.lookup(rev))]
2189 2189 elif not rev:
2190 2190 ctx = repo[None]
2191 2191 parents = ctx.parents()
2192 2192 changed = False
2193 2193 if default or id or num:
2194 2194 changed = util.any(repo.status())
2195 2195 if default or id:
2196 2196 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2197 2197 (changed) and "+" or "")]
2198 2198 if num:
2199 2199 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2200 2200 (changed) and "+" or ""))
2201 2201 else:
2202 2202 ctx = cmdutil.revsingle(repo, rev)
2203 2203 if default or id:
2204 2204 output = [hexfunc(ctx.node())]
2205 2205 if num:
2206 2206 output.append(str(ctx.rev()))
2207 2207
2208 2208 if repo.local() and default and not ui.quiet:
2209 2209 b = ctx.branch()
2210 2210 if b != 'default':
2211 2211 output.append("(%s)" % b)
2212 2212
2213 2213 # multiple tags for a single parent separated by '/'
2214 2214 t = "/".join(ctx.tags())
2215 2215 if t:
2216 2216 output.append(t)
2217 2217
2218 2218 if branch:
2219 2219 output.append(ctx.branch())
2220 2220
2221 2221 if tags:
2222 2222 output.extend(ctx.tags())
2223 2223
2224 2224 ui.write("%s\n" % ' '.join(output))
2225 2225
2226 2226 def import_(ui, repo, patch1, *patches, **opts):
2227 2227 """import an ordered set of patches
2228 2228
2229 2229 Import a list of patches and commit them individually (unless
2230 2230 --no-commit is specified).
2231 2231
2232 2232 If there are outstanding changes in the working directory, import
2233 2233 will abort unless given the -f/--force flag.
2234 2234
2235 2235 You can import a patch straight from a mail message. Even patches
2236 2236 as attachments work (to use the body part, it must have type
2237 2237 text/plain or text/x-patch). From and Subject headers of email
2238 2238 message are used as default committer and commit message. All
2239 2239 text/plain body parts before first diff are added to commit
2240 2240 message.
2241 2241
2242 2242 If the imported patch was generated by :hg:`export`, user and
2243 2243 description from patch override values from message headers and
2244 2244 body. Values given on command line with -m/--message and -u/--user
2245 2245 override these.
2246 2246
2247 2247 If --exact is specified, import will set the working directory to
2248 2248 the parent of each patch before applying it, and will abort if the
2249 2249 resulting changeset has a different ID than the one recorded in
2250 2250 the patch. This may happen due to character set problems or other
2251 2251 deficiencies in the text patch format.
2252 2252
2253 2253 With -s/--similarity, hg will attempt to discover renames and
2254 2254 copies in the patch in the same way as 'addremove'.
2255 2255
2256 2256 To read a patch from standard input, use "-" as the patch name. If
2257 2257 a URL is specified, the patch will be downloaded from it.
2258 2258 See :hg:`help dates` for a list of formats valid for -d/--date.
2259 2259
2260 2260 Returns 0 on success.
2261 2261 """
2262 2262 patches = (patch1,) + patches
2263 2263
2264 2264 date = opts.get('date')
2265 2265 if date:
2266 2266 opts['date'] = util.parsedate(date)
2267 2267
2268 2268 try:
2269 2269 sim = float(opts.get('similarity') or 0)
2270 2270 except ValueError:
2271 2271 raise util.Abort(_('similarity must be a number'))
2272 2272 if sim < 0 or sim > 100:
2273 2273 raise util.Abort(_('similarity must be between 0 and 100'))
2274 2274
2275 2275 if opts.get('exact') or not opts.get('force'):
2276 2276 cmdutil.bail_if_changed(repo)
2277 2277
2278 2278 d = opts["base"]
2279 2279 strip = opts["strip"]
2280 2280 wlock = lock = None
2281 2281 msgs = []
2282 2282
2283 2283 def tryone(ui, hunk):
2284 2284 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2285 2285 patch.extract(ui, hunk)
2286 2286
2287 2287 if not tmpname:
2288 2288 return None
2289 2289 commitid = _('to working directory')
2290 2290
2291 2291 try:
2292 2292 cmdline_message = cmdutil.logmessage(opts)
2293 2293 if cmdline_message:
2294 2294 # pickup the cmdline msg
2295 2295 message = cmdline_message
2296 2296 elif message:
2297 2297 # pickup the patch msg
2298 2298 message = message.strip()
2299 2299 else:
2300 2300 # launch the editor
2301 2301 message = None
2302 2302 ui.debug('message:\n%s\n' % message)
2303 2303
2304 2304 wp = repo.parents()
2305 2305 if opts.get('exact'):
2306 2306 if not nodeid or not p1:
2307 2307 raise util.Abort(_('not a Mercurial patch'))
2308 2308 p1 = repo.lookup(p1)
2309 2309 p2 = repo.lookup(p2 or hex(nullid))
2310 2310
2311 2311 if p1 != wp[0].node():
2312 2312 hg.clean(repo, p1)
2313 2313 repo.dirstate.setparents(p1, p2)
2314 2314 elif p2:
2315 2315 try:
2316 2316 p1 = repo.lookup(p1)
2317 2317 p2 = repo.lookup(p2)
2318 2318 if p1 == wp[0].node():
2319 2319 repo.dirstate.setparents(p1, p2)
2320 2320 except error.RepoError:
2321 2321 pass
2322 2322 if opts.get('exact') or opts.get('import_branch'):
2323 2323 repo.dirstate.setbranch(branch or 'default')
2324 2324
2325 2325 files = {}
2326 2326 try:
2327 2327 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2328 2328 files=files, eolmode=None)
2329 2329 finally:
2330 2330 files = cmdutil.updatedir(ui, repo, files,
2331 2331 similarity=sim / 100.0)
2332 2332 if opts.get('no_commit'):
2333 2333 if message:
2334 2334 msgs.append(message)
2335 2335 else:
2336 2336 if opts.get('exact'):
2337 2337 m = None
2338 2338 else:
2339 2339 m = cmdutil.matchfiles(repo, files or [])
2340 2340 n = repo.commit(message, opts.get('user') or user,
2341 2341 opts.get('date') or date, match=m,
2342 2342 editor=cmdutil.commiteditor)
2343 2343 if opts.get('exact'):
2344 2344 if hex(n) != nodeid:
2345 2345 repo.rollback()
2346 2346 raise util.Abort(_('patch is damaged'
2347 2347 ' or loses information'))
2348 2348 # Force a dirstate write so that the next transaction
2349 2349 # backups an up-do-date file.
2350 2350 repo.dirstate.write()
2351 2351 if n:
2352 2352 commitid = short(n)
2353 2353
2354 2354 return commitid
2355 2355 finally:
2356 2356 os.unlink(tmpname)
2357 2357
2358 2358 try:
2359 2359 wlock = repo.wlock()
2360 2360 lock = repo.lock()
2361 2361 lastcommit = None
2362 2362 for p in patches:
2363 2363 pf = os.path.join(d, p)
2364 2364
2365 2365 if pf == '-':
2366 2366 ui.status(_("applying patch from stdin\n"))
2367 2367 pf = sys.stdin
2368 2368 else:
2369 2369 ui.status(_("applying %s\n") % p)
2370 2370 pf = url.open(ui, pf)
2371 2371
2372 2372 haspatch = False
2373 2373 for hunk in patch.split(pf):
2374 2374 commitid = tryone(ui, hunk)
2375 2375 if commitid:
2376 2376 haspatch = True
2377 2377 if lastcommit:
2378 2378 ui.status(_('applied %s\n') % lastcommit)
2379 2379 lastcommit = commitid
2380 2380
2381 2381 if not haspatch:
2382 2382 raise util.Abort(_('no diffs found'))
2383 2383
2384 2384 if msgs:
2385 2385 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2386 2386 finally:
2387 2387 release(lock, wlock)
2388 2388
2389 2389 def incoming(ui, repo, source="default", **opts):
2390 2390 """show new changesets found in source
2391 2391
2392 2392 Show new changesets found in the specified path/URL or the default
2393 2393 pull location. These are the changesets that would have been pulled
2394 2394 if a pull at the time you issued this command.
2395 2395
2396 2396 For remote repository, using --bundle avoids downloading the
2397 2397 changesets twice if the incoming is followed by a pull.
2398 2398
2399 2399 See pull for valid source format details.
2400 2400
2401 2401 Returns 0 if there are incoming changes, 1 otherwise.
2402 2402 """
2403 2403 if opts.get('bundle') and opts.get('subrepos'):
2404 2404 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2405 2405
2406 2406 ret = hg.incoming(ui, repo, source, opts)
2407 2407 return ret
2408 2408
2409 2409 def init(ui, dest=".", **opts):
2410 2410 """create a new repository in the given directory
2411 2411
2412 2412 Initialize a new repository in the given directory. If the given
2413 2413 directory does not exist, it will be created.
2414 2414
2415 2415 If no directory is given, the current directory is used.
2416 2416
2417 2417 It is possible to specify an ``ssh://`` URL as the destination.
2418 2418 See :hg:`help urls` for more information.
2419 2419
2420 2420 Returns 0 on success.
2421 2421 """
2422 2422 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2423 2423
2424 2424 def locate(ui, repo, *pats, **opts):
2425 2425 """locate files matching specific patterns
2426 2426
2427 2427 Print files under Mercurial control in the working directory whose
2428 2428 names match the given patterns.
2429 2429
2430 2430 By default, this command searches all directories in the working
2431 2431 directory. To search just the current directory and its
2432 2432 subdirectories, use "--include .".
2433 2433
2434 2434 If no patterns are given to match, this command prints the names
2435 2435 of all files under Mercurial control in the working directory.
2436 2436
2437 2437 If you want to feed the output of this command into the "xargs"
2438 2438 command, use the -0 option to both this command and "xargs". This
2439 2439 will avoid the problem of "xargs" treating single filenames that
2440 2440 contain whitespace as multiple filenames.
2441 2441
2442 2442 Returns 0 if a match is found, 1 otherwise.
2443 2443 """
2444 2444 end = opts.get('print0') and '\0' or '\n'
2445 2445 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2446 2446
2447 2447 ret = 1
2448 2448 m = cmdutil.match(repo, pats, opts, default='relglob')
2449 2449 m.bad = lambda x, y: False
2450 2450 for abs in repo[rev].walk(m):
2451 2451 if not rev and abs not in repo.dirstate:
2452 2452 continue
2453 2453 if opts.get('fullpath'):
2454 2454 ui.write(repo.wjoin(abs), end)
2455 2455 else:
2456 2456 ui.write(((pats and m.rel(abs)) or abs), end)
2457 2457 ret = 0
2458 2458
2459 2459 return ret
2460 2460
2461 2461 def log(ui, repo, *pats, **opts):
2462 2462 """show revision history of entire repository or files
2463 2463
2464 2464 Print the revision history of the specified files or the entire
2465 2465 project.
2466 2466
2467 2467 File history is shown without following rename or copy history of
2468 2468 files. Use -f/--follow with a filename to follow history across
2469 2469 renames and copies. --follow without a filename will only show
2470 2470 ancestors or descendants of the starting revision. --follow-first
2471 2471 only follows the first parent of merge revisions.
2472 2472
2473 2473 If no revision range is specified, the default is ``tip:0`` unless
2474 2474 --follow is set, in which case the working directory parent is
2475 2475 used as the starting revision. You can specify a revision set for
2476 2476 log, see :hg:`help revsets` for more information.
2477 2477
2478 2478 See :hg:`help dates` for a list of formats valid for -d/--date.
2479 2479
2480 2480 By default this command prints revision number and changeset id,
2481 2481 tags, non-trivial parents, user, date and time, and a summary for
2482 2482 each commit. When the -v/--verbose switch is used, the list of
2483 2483 changed files and full commit message are shown.
2484 2484
2485 2485 .. note::
2486 2486 log -p/--patch may generate unexpected diff output for merge
2487 2487 changesets, as it will only compare the merge changeset against
2488 2488 its first parent. Also, only files different from BOTH parents
2489 2489 will appear in files:.
2490 2490
2491 2491 Returns 0 on success.
2492 2492 """
2493 2493
2494 2494 matchfn = cmdutil.match(repo, pats, opts)
2495 2495 limit = cmdutil.loglimit(opts)
2496 2496 count = 0
2497 2497
2498 2498 endrev = None
2499 2499 if opts.get('copies') and opts.get('rev'):
2500 2500 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2501 2501
2502 2502 df = False
2503 2503 if opts["date"]:
2504 2504 df = util.matchdate(opts["date"])
2505 2505
2506 2506 branches = opts.get('branch', []) + opts.get('only_branch', [])
2507 2507 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2508 2508
2509 2509 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2510 2510 def prep(ctx, fns):
2511 2511 rev = ctx.rev()
2512 2512 parents = [p for p in repo.changelog.parentrevs(rev)
2513 2513 if p != nullrev]
2514 2514 if opts.get('no_merges') and len(parents) == 2:
2515 2515 return
2516 2516 if opts.get('only_merges') and len(parents) != 2:
2517 2517 return
2518 2518 if opts.get('branch') and ctx.branch() not in opts['branch']:
2519 2519 return
2520 2520 if df and not df(ctx.date()[0]):
2521 2521 return
2522 2522 if opts['user'] and not [k for k in opts['user']
2523 2523 if k.lower() in ctx.user().lower()]:
2524 2524 return
2525 2525 if opts.get('keyword'):
2526 2526 for k in [kw.lower() for kw in opts['keyword']]:
2527 2527 if (k in ctx.user().lower() or
2528 2528 k in ctx.description().lower() or
2529 2529 k in " ".join(ctx.files()).lower()):
2530 2530 break
2531 2531 else:
2532 2532 return
2533 2533
2534 2534 copies = None
2535 2535 if opts.get('copies') and rev:
2536 2536 copies = []
2537 2537 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2538 2538 for fn in ctx.files():
2539 2539 rename = getrenamed(fn, rev)
2540 2540 if rename:
2541 2541 copies.append((fn, rename[0]))
2542 2542
2543 2543 revmatchfn = None
2544 2544 if opts.get('patch') or opts.get('stat'):
2545 2545 if opts.get('follow') or opts.get('follow_first'):
2546 2546 # note: this might be wrong when following through merges
2547 2547 revmatchfn = cmdutil.match(repo, fns, default='path')
2548 2548 else:
2549 2549 revmatchfn = matchfn
2550 2550
2551 2551 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2552 2552
2553 2553 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2554 2554 if count == limit:
2555 2555 break
2556 2556 if displayer.flush(ctx.rev()):
2557 2557 count += 1
2558 2558 displayer.close()
2559 2559
2560 2560 def manifest(ui, repo, node=None, rev=None):
2561 2561 """output the current or given revision of the project manifest
2562 2562
2563 2563 Print a list of version controlled files for the given revision.
2564 2564 If no revision is given, the first parent of the working directory
2565 2565 is used, or the null revision if no revision is checked out.
2566 2566
2567 2567 With -v, print file permissions, symlink and executable bits.
2568 2568 With --debug, print file revision hashes.
2569 2569
2570 2570 Returns 0 on success.
2571 2571 """
2572 2572
2573 2573 if rev and node:
2574 2574 raise util.Abort(_("please specify just one revision"))
2575 2575
2576 2576 if not node:
2577 2577 node = rev
2578 2578
2579 2579 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2580 2580 ctx = cmdutil.revsingle(repo, node)
2581 2581 for f in ctx:
2582 2582 if ui.debugflag:
2583 2583 ui.write("%40s " % hex(ctx.manifest()[f]))
2584 2584 if ui.verbose:
2585 2585 ui.write(decor[ctx.flags(f)])
2586 2586 ui.write("%s\n" % f)
2587 2587
2588 2588 def merge(ui, repo, node=None, **opts):
2589 2589 """merge working directory with another revision
2590 2590
2591 2591 The current working directory is updated with all changes made in
2592 2592 the requested revision since the last common predecessor revision.
2593 2593
2594 2594 Files that changed between either parent are marked as changed for
2595 2595 the next commit and a commit must be performed before any further
2596 2596 updates to the repository are allowed. The next commit will have
2597 2597 two parents.
2598 2598
2599 2599 ``--tool`` can be used to specify the merge tool used for file
2600 2600 merges. It overrides the HGMERGE environment variable and your
2601 2601 configuration files.
2602 2602
2603 2603 If no revision is specified, the working directory's parent is a
2604 2604 head revision, and the current branch contains exactly one other
2605 2605 head, the other head is merged with by default. Otherwise, an
2606 2606 explicit revision with which to merge with must be provided.
2607 2607
2608 2608 :hg:`resolve` must be used to resolve unresolved files.
2609 2609
2610 2610 To undo an uncommitted merge, use :hg:`update --clean .` which
2611 2611 will check out a clean copy of the original merge parent, losing
2612 2612 all changes.
2613 2613
2614 2614 Returns 0 on success, 1 if there are unresolved files.
2615 2615 """
2616 2616
2617 2617 if opts.get('rev') and node:
2618 2618 raise util.Abort(_("please specify just one revision"))
2619 2619 if not node:
2620 2620 node = opts.get('rev')
2621 2621
2622 2622 if not node:
2623 2623 branch = repo[None].branch()
2624 2624 bheads = repo.branchheads(branch)
2625 2625 if len(bheads) > 2:
2626 2626 raise util.Abort(_(
2627 2627 'branch \'%s\' has %d heads - '
2628 2628 'please merge with an explicit rev\n'
2629 2629 '(run \'hg heads .\' to see heads)')
2630 2630 % (branch, len(bheads)))
2631 2631
2632 2632 parent = repo.dirstate.parents()[0]
2633 2633 if len(bheads) == 1:
2634 2634 if len(repo.heads()) > 1:
2635 2635 raise util.Abort(_(
2636 2636 'branch \'%s\' has one head - '
2637 2637 'please merge with an explicit rev\n'
2638 2638 '(run \'hg heads\' to see all heads)')
2639 2639 % branch)
2640 2640 msg = _('there is nothing to merge')
2641 2641 if parent != repo.lookup(repo[None].branch()):
2642 2642 msg = _('%s - use "hg update" instead') % msg
2643 2643 raise util.Abort(msg)
2644 2644
2645 2645 if parent not in bheads:
2646 2646 raise util.Abort(_('working dir not at a head rev - '
2647 2647 'use "hg update" or merge with an explicit rev'))
2648 2648 node = parent == bheads[0] and bheads[-1] or bheads[0]
2649 2649 else:
2650 2650 node = cmdutil.revsingle(repo, node).node()
2651 2651
2652 2652 if opts.get('preview'):
2653 2653 # find nodes that are ancestors of p2 but not of p1
2654 2654 p1 = repo.lookup('.')
2655 2655 p2 = repo.lookup(node)
2656 2656 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2657 2657
2658 2658 displayer = cmdutil.show_changeset(ui, repo, opts)
2659 2659 for node in nodes:
2660 2660 displayer.show(repo[node])
2661 2661 displayer.close()
2662 2662 return 0
2663 2663
2664 2664 try:
2665 2665 # ui.forcemerge is an internal variable, do not document
2666 2666 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2667 2667 return hg.merge(repo, node, force=opts.get('force'))
2668 2668 finally:
2669 2669 ui.setconfig('ui', 'forcemerge', '')
2670 2670
2671 2671 def outgoing(ui, repo, dest=None, **opts):
2672 2672 """show changesets not found in the destination
2673 2673
2674 2674 Show changesets not found in the specified destination repository
2675 2675 or the default push location. These are the changesets that would
2676 2676 be pushed if a push was requested.
2677 2677
2678 2678 See pull for details of valid destination formats.
2679 2679
2680 2680 Returns 0 if there are outgoing changes, 1 otherwise.
2681 2681 """
2682 2682 ret = hg.outgoing(ui, repo, dest, opts)
2683 2683 return ret
2684 2684
2685 2685 def parents(ui, repo, file_=None, **opts):
2686 2686 """show the parents of the working directory or revision
2687 2687
2688 2688 Print the working directory's parent revisions. If a revision is
2689 2689 given via -r/--rev, the parent of that revision will be printed.
2690 2690 If a file argument is given, the revision in which the file was
2691 2691 last changed (before the working directory revision or the
2692 2692 argument to --rev if given) is printed.
2693 2693
2694 2694 Returns 0 on success.
2695 2695 """
2696 2696
2697 2697 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2698 2698
2699 2699 if file_:
2700 2700 m = cmdutil.match(repo, (file_,), opts)
2701 2701 if m.anypats() or len(m.files()) != 1:
2702 2702 raise util.Abort(_('can only specify an explicit filename'))
2703 2703 file_ = m.files()[0]
2704 2704 filenodes = []
2705 2705 for cp in ctx.parents():
2706 2706 if not cp:
2707 2707 continue
2708 2708 try:
2709 2709 filenodes.append(cp.filenode(file_))
2710 2710 except error.LookupError:
2711 2711 pass
2712 2712 if not filenodes:
2713 2713 raise util.Abort(_("'%s' not found in manifest!") % file_)
2714 2714 fl = repo.file(file_)
2715 2715 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2716 2716 else:
2717 2717 p = [cp.node() for cp in ctx.parents()]
2718 2718
2719 2719 displayer = cmdutil.show_changeset(ui, repo, opts)
2720 2720 for n in p:
2721 2721 if n != nullid:
2722 2722 displayer.show(repo[n])
2723 2723 displayer.close()
2724 2724
2725 2725 def paths(ui, repo, search=None):
2726 2726 """show aliases for remote repositories
2727 2727
2728 2728 Show definition of symbolic path name NAME. If no name is given,
2729 2729 show definition of all available names.
2730 2730
2731 2731 Path names are defined in the [paths] section of your
2732 2732 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2733 2733 repository, ``.hg/hgrc`` is used, too.
2734 2734
2735 2735 The path names ``default`` and ``default-push`` have a special
2736 2736 meaning. When performing a push or pull operation, they are used
2737 2737 as fallbacks if no location is specified on the command-line.
2738 2738 When ``default-push`` is set, it will be used for push and
2739 2739 ``default`` will be used for pull; otherwise ``default`` is used
2740 2740 as the fallback for both. When cloning a repository, the clone
2741 2741 source is written as ``default`` in ``.hg/hgrc``. Note that
2742 2742 ``default`` and ``default-push`` apply to all inbound (e.g.
2743 2743 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2744 2744 :hg:`bundle`) operations.
2745 2745
2746 2746 See :hg:`help urls` for more information.
2747 2747
2748 2748 Returns 0 on success.
2749 2749 """
2750 2750 if search:
2751 2751 for name, path in ui.configitems("paths"):
2752 2752 if name == search:
2753 2753 ui.write("%s\n" % url.hidepassword(path))
2754 2754 return
2755 2755 ui.warn(_("not found!\n"))
2756 2756 return 1
2757 2757 else:
2758 2758 for name, path in ui.configitems("paths"):
2759 2759 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2760 2760
2761 2761 def postincoming(ui, repo, modheads, optupdate, checkout):
2762 2762 if modheads == 0:
2763 2763 return
2764 2764 if optupdate:
2765 2765 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2766 2766 return hg.update(repo, checkout)
2767 2767 else:
2768 2768 ui.status(_("not updating, since new heads added\n"))
2769 2769 if modheads > 1:
2770 2770 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2771 2771 else:
2772 2772 ui.status(_("(run 'hg update' to get a working copy)\n"))
2773 2773
2774 2774 def pull(ui, repo, source="default", **opts):
2775 2775 """pull changes from the specified source
2776 2776
2777 2777 Pull changes from a remote repository to a local one.
2778 2778
2779 2779 This finds all changes from the repository at the specified path
2780 2780 or URL and adds them to a local repository (the current one unless
2781 2781 -R is specified). By default, this does not update the copy of the
2782 2782 project in the working directory.
2783 2783
2784 2784 Use :hg:`incoming` if you want to see what would have been added
2785 2785 by a pull at the time you issued this command. If you then decide
2786 2786 to add those changes to the repository, you should use :hg:`pull
2787 2787 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2788 2788
2789 2789 If SOURCE is omitted, the 'default' path will be used.
2790 2790 See :hg:`help urls` for more information.
2791 2791
2792 2792 Returns 0 on success, 1 if an update had unresolved files.
2793 2793 """
2794 2794 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2795 2795 other = hg.repository(hg.remoteui(repo, opts), source)
2796 2796 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2797 2797 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2798 2798 if revs:
2799 2799 try:
2800 2800 revs = [other.lookup(rev) for rev in revs]
2801 2801 except error.CapabilityError:
2802 2802 err = _("other repository doesn't support revision lookup, "
2803 2803 "so a rev cannot be specified.")
2804 2804 raise util.Abort(err)
2805 2805
2806 2806 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2807 2807 if checkout:
2808 2808 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2809 2809 repo._subtoppath = source
2810 2810 try:
2811 2811 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2812 2812 finally:
2813 2813 del repo._subtoppath
2814 2814
2815 2815 def push(ui, repo, dest=None, **opts):
2816 2816 """push changes to the specified destination
2817 2817
2818 2818 Push changesets from the local repository to the specified
2819 2819 destination.
2820 2820
2821 2821 This operation is symmetrical to pull: it is identical to a pull
2822 2822 in the destination repository from the current one.
2823 2823
2824 2824 By default, push will not allow creation of new heads at the
2825 2825 destination, since multiple heads would make it unclear which head
2826 2826 to use. In this situation, it is recommended to pull and merge
2827 2827 before pushing.
2828 2828
2829 2829 Use --new-branch if you want to allow push to create a new named
2830 2830 branch that is not present at the destination. This allows you to
2831 2831 only create a new branch without forcing other changes.
2832 2832
2833 2833 Use -f/--force to override the default behavior and push all
2834 2834 changesets on all branches.
2835 2835
2836 2836 If -r/--rev is used, the specified revision and all its ancestors
2837 2837 will be pushed to the remote repository.
2838 2838
2839 2839 Please see :hg:`help urls` for important details about ``ssh://``
2840 2840 URLs. If DESTINATION is omitted, a default path will be used.
2841 2841
2842 2842 Returns 0 if push was successful, 1 if nothing to push.
2843 2843 """
2844 2844 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2845 2845 dest, branches = hg.parseurl(dest, opts.get('branch'))
2846 2846 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2847 2847 other = hg.repository(hg.remoteui(repo, opts), dest)
2848 2848 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2849 2849 if revs:
2850 2850 revs = [repo.lookup(rev) for rev in revs]
2851 2851
2852 2852 repo._subtoppath = dest
2853 2853 try:
2854 2854 # push subrepos depth-first for coherent ordering
2855 2855 c = repo['']
2856 2856 subs = c.substate # only repos that are committed
2857 2857 for s in sorted(subs):
2858 2858 if not c.sub(s).push(opts.get('force')):
2859 2859 return False
2860 2860 finally:
2861 2861 del repo._subtoppath
2862 2862 r = repo.push(other, opts.get('force'), revs=revs,
2863 2863 newbranch=opts.get('new_branch'))
2864 2864 return r == 0
2865 2865
2866 2866 def recover(ui, repo):
2867 2867 """roll back an interrupted transaction
2868 2868
2869 2869 Recover from an interrupted commit or pull.
2870 2870
2871 2871 This command tries to fix the repository status after an
2872 2872 interrupted operation. It should only be necessary when Mercurial
2873 2873 suggests it.
2874 2874
2875 2875 Returns 0 if successful, 1 if nothing to recover or verify fails.
2876 2876 """
2877 2877 if repo.recover():
2878 2878 return hg.verify(repo)
2879 2879 return 1
2880 2880
2881 2881 def remove(ui, repo, *pats, **opts):
2882 2882 """remove the specified files on the next commit
2883 2883
2884 2884 Schedule the indicated files for removal from the repository.
2885 2885
2886 2886 This only removes files from the current branch, not from the
2887 2887 entire project history. -A/--after can be used to remove only
2888 2888 files that have already been deleted, -f/--force can be used to
2889 2889 force deletion, and -Af can be used to remove files from the next
2890 2890 revision without deleting them from the working directory.
2891 2891
2892 2892 The following table details the behavior of remove for different
2893 2893 file states (columns) and option combinations (rows). The file
2894 2894 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2895 2895 reported by :hg:`status`). The actions are Warn, Remove (from
2896 2896 branch) and Delete (from disk)::
2897 2897
2898 2898 A C M !
2899 2899 none W RD W R
2900 2900 -f R RD RD R
2901 2901 -A W W W R
2902 2902 -Af R R R R
2903 2903
2904 2904 This command schedules the files to be removed at the next commit.
2905 2905 To undo a remove before that, see :hg:`revert`.
2906 2906
2907 2907 Returns 0 on success, 1 if any warnings encountered.
2908 2908 """
2909 2909
2910 2910 ret = 0
2911 2911 after, force = opts.get('after'), opts.get('force')
2912 2912 if not pats and not after:
2913 2913 raise util.Abort(_('no files specified'))
2914 2914
2915 2915 m = cmdutil.match(repo, pats, opts)
2916 2916 s = repo.status(match=m, clean=True)
2917 2917 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2918 2918
2919 2919 for f in m.files():
2920 2920 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2921 2921 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2922 2922 ret = 1
2923 2923
2924 2924 if force:
2925 2925 remove, forget = modified + deleted + clean, added
2926 2926 elif after:
2927 2927 remove, forget = deleted, []
2928 2928 for f in modified + added + clean:
2929 2929 ui.warn(_('not removing %s: file still exists (use -f'
2930 2930 ' to force removal)\n') % m.rel(f))
2931 2931 ret = 1
2932 2932 else:
2933 2933 remove, forget = deleted + clean, []
2934 2934 for f in modified:
2935 2935 ui.warn(_('not removing %s: file is modified (use -f'
2936 2936 ' to force removal)\n') % m.rel(f))
2937 2937 ret = 1
2938 2938 for f in added:
2939 2939 ui.warn(_('not removing %s: file has been marked for add (use -f'
2940 2940 ' to force removal)\n') % m.rel(f))
2941 2941 ret = 1
2942 2942
2943 2943 for f in sorted(remove + forget):
2944 2944 if ui.verbose or not m.exact(f):
2945 2945 ui.status(_('removing %s\n') % m.rel(f))
2946 2946
2947 2947 repo[None].forget(forget)
2948 2948 repo[None].remove(remove, unlink=not after)
2949 2949 return ret
2950 2950
2951 2951 def rename(ui, repo, *pats, **opts):
2952 2952 """rename files; equivalent of copy + remove
2953 2953
2954 2954 Mark dest as copies of sources; mark sources for deletion. If dest
2955 2955 is a directory, copies are put in that directory. If dest is a
2956 2956 file, there can only be one source.
2957 2957
2958 2958 By default, this command copies the contents of files as they
2959 2959 exist in the working directory. If invoked with -A/--after, the
2960 2960 operation is recorded, but no copying is performed.
2961 2961
2962 2962 This command takes effect at the next commit. To undo a rename
2963 2963 before that, see :hg:`revert`.
2964 2964
2965 2965 Returns 0 on success, 1 if errors are encountered.
2966 2966 """
2967 2967 wlock = repo.wlock(False)
2968 2968 try:
2969 2969 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2970 2970 finally:
2971 2971 wlock.release()
2972 2972
2973 2973 def resolve(ui, repo, *pats, **opts):
2974 2974 """redo merges or set/view the merge status of files
2975 2975
2976 2976 Merges with unresolved conflicts are often the result of
2977 2977 non-interactive merging using the ``internal:merge`` configuration
2978 2978 setting, or a command-line merge tool like ``diff3``. The resolve
2979 2979 command is used to manage the files involved in a merge, after
2980 2980 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2981 2981 working directory must have two parents).
2982 2982
2983 2983 The resolve command can be used in the following ways:
2984 2984
2985 2985 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
2986 2986 files, discarding any previous merge attempts. Re-merging is not
2987 2987 performed for files already marked as resolved. Use ``--all/-a``
2988 2988 to selects all unresolved files. ``--tool`` can be used to specify
2989 2989 the merge tool used for the given files. It overrides the HGMERGE
2990 2990 environment variable and your configuration files.
2991 2991
2992 2992 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2993 2993 (e.g. after having manually fixed-up the files). The default is
2994 2994 to mark all unresolved files.
2995 2995
2996 2996 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2997 2997 default is to mark all resolved files.
2998 2998
2999 2999 - :hg:`resolve -l`: list files which had or still have conflicts.
3000 3000 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3001 3001
3002 3002 Note that Mercurial will not let you commit files with unresolved
3003 3003 merge conflicts. You must use :hg:`resolve -m ...` before you can
3004 3004 commit after a conflicting merge.
3005 3005
3006 3006 Returns 0 on success, 1 if any files fail a resolve attempt.
3007 3007 """
3008 3008
3009 3009 all, mark, unmark, show, nostatus = \
3010 3010 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3011 3011
3012 3012 if (show and (mark or unmark)) or (mark and unmark):
3013 3013 raise util.Abort(_("too many options specified"))
3014 3014 if pats and all:
3015 3015 raise util.Abort(_("can't specify --all and patterns"))
3016 3016 if not (all or pats or show or mark or unmark):
3017 3017 raise util.Abort(_('no files or directories specified; '
3018 3018 'use --all to remerge all files'))
3019 3019
3020 3020 ms = mergemod.mergestate(repo)
3021 3021 m = cmdutil.match(repo, pats, opts)
3022 3022 ret = 0
3023 3023
3024 3024 for f in ms:
3025 3025 if m(f):
3026 3026 if show:
3027 3027 if nostatus:
3028 3028 ui.write("%s\n" % f)
3029 3029 else:
3030 3030 ui.write("%s %s\n" % (ms[f].upper(), f),
3031 3031 label='resolve.' +
3032 3032 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3033 3033 elif mark:
3034 3034 ms.mark(f, "r")
3035 3035 elif unmark:
3036 3036 ms.mark(f, "u")
3037 3037 else:
3038 3038 wctx = repo[None]
3039 3039 mctx = wctx.parents()[-1]
3040 3040
3041 3041 # backup pre-resolve (merge uses .orig for its own purposes)
3042 3042 a = repo.wjoin(f)
3043 3043 util.copyfile(a, a + ".resolve")
3044 3044
3045 3045 try:
3046 3046 # resolve file
3047 3047 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3048 3048 if ms.resolve(f, wctx, mctx):
3049 3049 ret = 1
3050 3050 finally:
3051 3051 ui.setconfig('ui', 'forcemerge', '')
3052 3052
3053 3053 # replace filemerge's .orig file with our resolve file
3054 3054 util.rename(a + ".resolve", a + ".orig")
3055 3055
3056 3056 ms.commit()
3057 3057 return ret
3058 3058
3059 3059 def revert(ui, repo, *pats, **opts):
3060 3060 """restore individual files or directories to an earlier state
3061 3061
3062 3062 .. note::
3063 3063 This command is most likely not what you are looking for.
3064 3064 Revert will partially overwrite content in the working
3065 3065 directory without changing the working directory parents. Use
3066 3066 :hg:`update -r rev` to check out earlier revisions, or
3067 3067 :hg:`update --clean .` to undo a merge which has added another
3068 3068 parent.
3069 3069
3070 3070 With no revision specified, revert the named files or directories
3071 3071 to the contents they had in the parent of the working directory.
3072 3072 This restores the contents of the affected files to an unmodified
3073 3073 state and unschedules adds, removes, copies, and renames. If the
3074 3074 working directory has two parents, you must explicitly specify a
3075 3075 revision.
3076 3076
3077 3077 Using the -r/--rev option, revert the given files or directories
3078 3078 to their contents as of a specific revision. This can be helpful
3079 3079 to "roll back" some or all of an earlier change. See :hg:`help
3080 3080 dates` for a list of formats valid for -d/--date.
3081 3081
3082 3082 Revert modifies the working directory. It does not commit any
3083 3083 changes, or change the parent of the working directory. If you
3084 3084 revert to a revision other than the parent of the working
3085 3085 directory, the reverted files will thus appear modified
3086 3086 afterwards.
3087 3087
3088 3088 If a file has been deleted, it is restored. If the executable mode
3089 3089 of a file was changed, it is reset.
3090 3090
3091 3091 If names are given, all files matching the names are reverted.
3092 3092 If no arguments are given, no files are reverted.
3093 3093
3094 3094 Modified files are saved with a .orig suffix before reverting.
3095 3095 To disable these backups, use --no-backup.
3096 3096
3097 3097 Returns 0 on success.
3098 3098 """
3099 3099
3100 3100 if opts.get("date"):
3101 3101 if opts.get("rev"):
3102 3102 raise util.Abort(_("you can't specify a revision and a date"))
3103 3103 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3104 3104
3105 3105 parent, p2 = repo.dirstate.parents()
3106 3106 if not opts.get('rev') and p2 != nullid:
3107 3107 raise util.Abort(_('uncommitted merge - '
3108 3108 'use "hg update", see "hg help revert"'))
3109 3109
3110 3110 if not pats and not opts.get('all'):
3111 3111 raise util.Abort(_('no files or directories specified; '
3112 3112 'use --all to revert the whole repo'))
3113 3113
3114 3114 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3115 3115 node = ctx.node()
3116 3116 mf = ctx.manifest()
3117 3117 if node == parent:
3118 3118 pmf = mf
3119 3119 else:
3120 3120 pmf = None
3121 3121
3122 3122 # need all matching names in dirstate and manifest of target rev,
3123 3123 # so have to walk both. do not print errors if files exist in one
3124 3124 # but not other.
3125 3125
3126 3126 names = {}
3127 3127
3128 3128 wlock = repo.wlock()
3129 3129 try:
3130 3130 # walk dirstate.
3131 3131
3132 3132 m = cmdutil.match(repo, pats, opts)
3133 3133 m.bad = lambda x, y: False
3134 3134 for abs in repo.walk(m):
3135 3135 names[abs] = m.rel(abs), m.exact(abs)
3136 3136
3137 3137 # walk target manifest.
3138 3138
3139 3139 def badfn(path, msg):
3140 3140 if path in names:
3141 3141 return
3142 3142 path_ = path + '/'
3143 3143 for f in names:
3144 3144 if f.startswith(path_):
3145 3145 return
3146 3146 ui.warn("%s: %s\n" % (m.rel(path), msg))
3147 3147
3148 3148 m = cmdutil.match(repo, pats, opts)
3149 3149 m.bad = badfn
3150 3150 for abs in repo[node].walk(m):
3151 3151 if abs not in names:
3152 3152 names[abs] = m.rel(abs), m.exact(abs)
3153 3153
3154 3154 m = cmdutil.matchfiles(repo, names)
3155 3155 changes = repo.status(match=m)[:4]
3156 3156 modified, added, removed, deleted = map(set, changes)
3157 3157
3158 3158 # if f is a rename, also revert the source
3159 3159 cwd = repo.getcwd()
3160 3160 for f in added:
3161 3161 src = repo.dirstate.copied(f)
3162 3162 if src and src not in names and repo.dirstate[src] == 'r':
3163 3163 removed.add(src)
3164 3164 names[src] = (repo.pathto(src, cwd), True)
3165 3165
3166 3166 def removeforget(abs):
3167 3167 if repo.dirstate[abs] == 'a':
3168 3168 return _('forgetting %s\n')
3169 3169 return _('removing %s\n')
3170 3170
3171 3171 revert = ([], _('reverting %s\n'))
3172 3172 add = ([], _('adding %s\n'))
3173 3173 remove = ([], removeforget)
3174 3174 undelete = ([], _('undeleting %s\n'))
3175 3175
3176 3176 disptable = (
3177 3177 # dispatch table:
3178 3178 # file state
3179 3179 # action if in target manifest
3180 3180 # action if not in target manifest
3181 3181 # make backup if in target manifest
3182 3182 # make backup if not in target manifest
3183 3183 (modified, revert, remove, True, True),
3184 3184 (added, revert, remove, True, False),
3185 3185 (removed, undelete, None, False, False),
3186 3186 (deleted, revert, remove, False, False),
3187 3187 )
3188 3188
3189 3189 for abs, (rel, exact) in sorted(names.items()):
3190 3190 mfentry = mf.get(abs)
3191 3191 target = repo.wjoin(abs)
3192 3192 def handle(xlist, dobackup):
3193 3193 xlist[0].append(abs)
3194 3194 if (dobackup and not opts.get('no_backup') and
3195 3195 os.path.lexists(target)):
3196 3196 bakname = "%s.orig" % rel
3197 3197 ui.note(_('saving current version of %s as %s\n') %
3198 3198 (rel, bakname))
3199 3199 if not opts.get('dry_run'):
3200 3200 util.rename(target, bakname)
3201 3201 if ui.verbose or not exact:
3202 3202 msg = xlist[1]
3203 3203 if not isinstance(msg, basestring):
3204 3204 msg = msg(abs)
3205 3205 ui.status(msg % rel)
3206 3206 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3207 3207 if abs not in table:
3208 3208 continue
3209 3209 # file has changed in dirstate
3210 3210 if mfentry:
3211 3211 handle(hitlist, backuphit)
3212 3212 elif misslist is not None:
3213 3213 handle(misslist, backupmiss)
3214 3214 break
3215 3215 else:
3216 3216 if abs not in repo.dirstate:
3217 3217 if mfentry:
3218 3218 handle(add, True)
3219 3219 elif exact:
3220 3220 ui.warn(_('file not managed: %s\n') % rel)
3221 3221 continue
3222 3222 # file has not changed in dirstate
3223 3223 if node == parent:
3224 3224 if exact:
3225 3225 ui.warn(_('no changes needed to %s\n') % rel)
3226 3226 continue
3227 3227 if pmf is None:
3228 3228 # only need parent manifest in this unlikely case,
3229 3229 # so do not read by default
3230 3230 pmf = repo[parent].manifest()
3231 3231 if abs in pmf:
3232 3232 if mfentry:
3233 3233 # if version of file is same in parent and target
3234 3234 # manifests, do nothing
3235 3235 if (pmf[abs] != mfentry or
3236 3236 pmf.flags(abs) != mf.flags(abs)):
3237 3237 handle(revert, False)
3238 3238 else:
3239 3239 handle(remove, False)
3240 3240
3241 3241 if not opts.get('dry_run'):
3242 3242 def checkout(f):
3243 3243 fc = ctx[f]
3244 3244 repo.wwrite(f, fc.data(), fc.flags())
3245 3245
3246 3246 audit_path = util.path_auditor(repo.root)
3247 3247 for f in remove[0]:
3248 3248 if repo.dirstate[f] == 'a':
3249 3249 repo.dirstate.forget(f)
3250 3250 continue
3251 3251 audit_path(f)
3252 3252 try:
3253 util.unlink(repo.wjoin(f))
3253 util.unlinkpath(repo.wjoin(f))
3254 3254 except OSError:
3255 3255 pass
3256 3256 repo.dirstate.remove(f)
3257 3257
3258 3258 normal = None
3259 3259 if node == parent:
3260 3260 # We're reverting to our parent. If possible, we'd like status
3261 3261 # to report the file as clean. We have to use normallookup for
3262 3262 # merges to avoid losing information about merged/dirty files.
3263 3263 if p2 != nullid:
3264 3264 normal = repo.dirstate.normallookup
3265 3265 else:
3266 3266 normal = repo.dirstate.normal
3267 3267 for f in revert[0]:
3268 3268 checkout(f)
3269 3269 if normal:
3270 3270 normal(f)
3271 3271
3272 3272 for f in add[0]:
3273 3273 checkout(f)
3274 3274 repo.dirstate.add(f)
3275 3275
3276 3276 normal = repo.dirstate.normallookup
3277 3277 if node == parent and p2 == nullid:
3278 3278 normal = repo.dirstate.normal
3279 3279 for f in undelete[0]:
3280 3280 checkout(f)
3281 3281 normal(f)
3282 3282
3283 3283 finally:
3284 3284 wlock.release()
3285 3285
3286 3286 def rollback(ui, repo, **opts):
3287 3287 """roll back the last transaction (dangerous)
3288 3288
3289 3289 This command should be used with care. There is only one level of
3290 3290 rollback, and there is no way to undo a rollback. It will also
3291 3291 restore the dirstate at the time of the last transaction, losing
3292 3292 any dirstate changes since that time. This command does not alter
3293 3293 the working directory.
3294 3294
3295 3295 Transactions are used to encapsulate the effects of all commands
3296 3296 that create new changesets or propagate existing changesets into a
3297 3297 repository. For example, the following commands are transactional,
3298 3298 and their effects can be rolled back:
3299 3299
3300 3300 - commit
3301 3301 - import
3302 3302 - pull
3303 3303 - push (with this repository as the destination)
3304 3304 - unbundle
3305 3305
3306 3306 This command is not intended for use on public repositories. Once
3307 3307 changes are visible for pull by other users, rolling a transaction
3308 3308 back locally is ineffective (someone else may already have pulled
3309 3309 the changes). Furthermore, a race is possible with readers of the
3310 3310 repository; for example an in-progress pull from the repository
3311 3311 may fail if a rollback is performed.
3312 3312
3313 3313 Returns 0 on success, 1 if no rollback data is available.
3314 3314 """
3315 3315 return repo.rollback(opts.get('dry_run'))
3316 3316
3317 3317 def root(ui, repo):
3318 3318 """print the root (top) of the current working directory
3319 3319
3320 3320 Print the root directory of the current repository.
3321 3321
3322 3322 Returns 0 on success.
3323 3323 """
3324 3324 ui.write(repo.root + "\n")
3325 3325
3326 3326 def serve(ui, repo, **opts):
3327 3327 """start stand-alone webserver
3328 3328
3329 3329 Start a local HTTP repository browser and pull server. You can use
3330 3330 this for ad-hoc sharing and browsing of repositories. It is
3331 3331 recommended to use a real web server to serve a repository for
3332 3332 longer periods of time.
3333 3333
3334 3334 Please note that the server does not implement access control.
3335 3335 This means that, by default, anybody can read from the server and
3336 3336 nobody can write to it by default. Set the ``web.allow_push``
3337 3337 option to ``*`` to allow everybody to push to the server. You
3338 3338 should use a real web server if you need to authenticate users.
3339 3339
3340 3340 By default, the server logs accesses to stdout and errors to
3341 3341 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3342 3342 files.
3343 3343
3344 3344 To have the server choose a free port number to listen on, specify
3345 3345 a port number of 0; in this case, the server will print the port
3346 3346 number it uses.
3347 3347
3348 3348 Returns 0 on success.
3349 3349 """
3350 3350
3351 3351 if opts["stdio"]:
3352 3352 if repo is None:
3353 3353 raise error.RepoError(_("There is no Mercurial repository here"
3354 3354 " (.hg not found)"))
3355 3355 s = sshserver.sshserver(ui, repo)
3356 3356 s.serve_forever()
3357 3357
3358 3358 # this way we can check if something was given in the command-line
3359 3359 if opts.get('port'):
3360 3360 opts['port'] = util.getport(opts.get('port'))
3361 3361
3362 3362 baseui = repo and repo.baseui or ui
3363 3363 optlist = ("name templates style address port prefix ipv6"
3364 3364 " accesslog errorlog certificate encoding")
3365 3365 for o in optlist.split():
3366 3366 val = opts.get(o, '')
3367 3367 if val in (None, ''): # should check against default options instead
3368 3368 continue
3369 3369 baseui.setconfig("web", o, val)
3370 3370 if repo and repo.ui != baseui:
3371 3371 repo.ui.setconfig("web", o, val)
3372 3372
3373 3373 o = opts.get('web_conf') or opts.get('webdir_conf')
3374 3374 if not o:
3375 3375 if not repo:
3376 3376 raise error.RepoError(_("There is no Mercurial repository"
3377 3377 " here (.hg not found)"))
3378 3378 o = repo.root
3379 3379
3380 3380 app = hgweb.hgweb(o, baseui=ui)
3381 3381
3382 3382 class service(object):
3383 3383 def init(self):
3384 3384 util.set_signal_handler()
3385 3385 self.httpd = hgweb.server.create_server(ui, app)
3386 3386
3387 3387 if opts['port'] and not ui.verbose:
3388 3388 return
3389 3389
3390 3390 if self.httpd.prefix:
3391 3391 prefix = self.httpd.prefix.strip('/') + '/'
3392 3392 else:
3393 3393 prefix = ''
3394 3394
3395 3395 port = ':%d' % self.httpd.port
3396 3396 if port == ':80':
3397 3397 port = ''
3398 3398
3399 3399 bindaddr = self.httpd.addr
3400 3400 if bindaddr == '0.0.0.0':
3401 3401 bindaddr = '*'
3402 3402 elif ':' in bindaddr: # IPv6
3403 3403 bindaddr = '[%s]' % bindaddr
3404 3404
3405 3405 fqaddr = self.httpd.fqaddr
3406 3406 if ':' in fqaddr:
3407 3407 fqaddr = '[%s]' % fqaddr
3408 3408 if opts['port']:
3409 3409 write = ui.status
3410 3410 else:
3411 3411 write = ui.write
3412 3412 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3413 3413 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3414 3414
3415 3415 def run(self):
3416 3416 self.httpd.serve_forever()
3417 3417
3418 3418 service = service()
3419 3419
3420 3420 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3421 3421
3422 3422 def status(ui, repo, *pats, **opts):
3423 3423 """show changed files in the working directory
3424 3424
3425 3425 Show status of files in the repository. If names are given, only
3426 3426 files that match are shown. Files that are clean or ignored or
3427 3427 the source of a copy/move operation, are not listed unless
3428 3428 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3429 3429 Unless options described with "show only ..." are given, the
3430 3430 options -mardu are used.
3431 3431
3432 3432 Option -q/--quiet hides untracked (unknown and ignored) files
3433 3433 unless explicitly requested with -u/--unknown or -i/--ignored.
3434 3434
3435 3435 .. note::
3436 3436 status may appear to disagree with diff if permissions have
3437 3437 changed or a merge has occurred. The standard diff format does
3438 3438 not report permission changes and diff only reports changes
3439 3439 relative to one merge parent.
3440 3440
3441 3441 If one revision is given, it is used as the base revision.
3442 3442 If two revisions are given, the differences between them are
3443 3443 shown. The --change option can also be used as a shortcut to list
3444 3444 the changed files of a revision from its first parent.
3445 3445
3446 3446 The codes used to show the status of files are::
3447 3447
3448 3448 M = modified
3449 3449 A = added
3450 3450 R = removed
3451 3451 C = clean
3452 3452 ! = missing (deleted by non-hg command, but still tracked)
3453 3453 ? = not tracked
3454 3454 I = ignored
3455 3455 = origin of the previous file listed as A (added)
3456 3456
3457 3457 Returns 0 on success.
3458 3458 """
3459 3459
3460 3460 revs = opts.get('rev')
3461 3461 change = opts.get('change')
3462 3462
3463 3463 if revs and change:
3464 3464 msg = _('cannot specify --rev and --change at the same time')
3465 3465 raise util.Abort(msg)
3466 3466 elif change:
3467 3467 node2 = repo.lookup(change)
3468 3468 node1 = repo[node2].parents()[0].node()
3469 3469 else:
3470 3470 node1, node2 = cmdutil.revpair(repo, revs)
3471 3471
3472 3472 cwd = (pats and repo.getcwd()) or ''
3473 3473 end = opts.get('print0') and '\0' or '\n'
3474 3474 copy = {}
3475 3475 states = 'modified added removed deleted unknown ignored clean'.split()
3476 3476 show = [k for k in states if opts.get(k)]
3477 3477 if opts.get('all'):
3478 3478 show += ui.quiet and (states[:4] + ['clean']) or states
3479 3479 if not show:
3480 3480 show = ui.quiet and states[:4] or states[:5]
3481 3481
3482 3482 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3483 3483 'ignored' in show, 'clean' in show, 'unknown' in show,
3484 3484 opts.get('subrepos'))
3485 3485 changestates = zip(states, 'MAR!?IC', stat)
3486 3486
3487 3487 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3488 3488 ctxn = repo[nullid]
3489 3489 ctx1 = repo[node1]
3490 3490 ctx2 = repo[node2]
3491 3491 added = stat[1]
3492 3492 if node2 is None:
3493 3493 added = stat[0] + stat[1] # merged?
3494 3494
3495 3495 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3496 3496 if k in added:
3497 3497 copy[k] = v
3498 3498 elif v in added:
3499 3499 copy[v] = k
3500 3500
3501 3501 for state, char, files in changestates:
3502 3502 if state in show:
3503 3503 format = "%s %%s%s" % (char, end)
3504 3504 if opts.get('no_status'):
3505 3505 format = "%%s%s" % end
3506 3506
3507 3507 for f in files:
3508 3508 ui.write(format % repo.pathto(f, cwd),
3509 3509 label='status.' + state)
3510 3510 if f in copy:
3511 3511 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3512 3512 label='status.copied')
3513 3513
3514 3514 def summary(ui, repo, **opts):
3515 3515 """summarize working directory state
3516 3516
3517 3517 This generates a brief summary of the working directory state,
3518 3518 including parents, branch, commit status, and available updates.
3519 3519
3520 3520 With the --remote option, this will check the default paths for
3521 3521 incoming and outgoing changes. This can be time-consuming.
3522 3522
3523 3523 Returns 0 on success.
3524 3524 """
3525 3525
3526 3526 ctx = repo[None]
3527 3527 parents = ctx.parents()
3528 3528 pnode = parents[0].node()
3529 3529
3530 3530 for p in parents:
3531 3531 # label with log.changeset (instead of log.parent) since this
3532 3532 # shows a working directory parent *changeset*:
3533 3533 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3534 3534 label='log.changeset')
3535 3535 ui.write(' '.join(p.tags()), label='log.tag')
3536 3536 if p.rev() == -1:
3537 3537 if not len(repo):
3538 3538 ui.write(_(' (empty repository)'))
3539 3539 else:
3540 3540 ui.write(_(' (no revision checked out)'))
3541 3541 ui.write('\n')
3542 3542 if p.description():
3543 3543 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3544 3544 label='log.summary')
3545 3545
3546 3546 branch = ctx.branch()
3547 3547 bheads = repo.branchheads(branch)
3548 3548 m = _('branch: %s\n') % branch
3549 3549 if branch != 'default':
3550 3550 ui.write(m, label='log.branch')
3551 3551 else:
3552 3552 ui.status(m, label='log.branch')
3553 3553
3554 3554 st = list(repo.status(unknown=True))[:6]
3555 3555
3556 3556 c = repo.dirstate.copies()
3557 3557 copied, renamed = [], []
3558 3558 for d, s in c.iteritems():
3559 3559 if s in st[2]:
3560 3560 st[2].remove(s)
3561 3561 renamed.append(d)
3562 3562 else:
3563 3563 copied.append(d)
3564 3564 if d in st[1]:
3565 3565 st[1].remove(d)
3566 3566 st.insert(3, renamed)
3567 3567 st.insert(4, copied)
3568 3568
3569 3569 ms = mergemod.mergestate(repo)
3570 3570 st.append([f for f in ms if ms[f] == 'u'])
3571 3571
3572 3572 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3573 3573 st.append(subs)
3574 3574
3575 3575 labels = [ui.label(_('%d modified'), 'status.modified'),
3576 3576 ui.label(_('%d added'), 'status.added'),
3577 3577 ui.label(_('%d removed'), 'status.removed'),
3578 3578 ui.label(_('%d renamed'), 'status.copied'),
3579 3579 ui.label(_('%d copied'), 'status.copied'),
3580 3580 ui.label(_('%d deleted'), 'status.deleted'),
3581 3581 ui.label(_('%d unknown'), 'status.unknown'),
3582 3582 ui.label(_('%d ignored'), 'status.ignored'),
3583 3583 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3584 3584 ui.label(_('%d subrepos'), 'status.modified')]
3585 3585 t = []
3586 3586 for s, l in zip(st, labels):
3587 3587 if s:
3588 3588 t.append(l % len(s))
3589 3589
3590 3590 t = ', '.join(t)
3591 3591 cleanworkdir = False
3592 3592
3593 3593 if len(parents) > 1:
3594 3594 t += _(' (merge)')
3595 3595 elif branch != parents[0].branch():
3596 3596 t += _(' (new branch)')
3597 3597 elif (parents[0].extra().get('close') and
3598 3598 pnode in repo.branchheads(branch, closed=True)):
3599 3599 t += _(' (head closed)')
3600 3600 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3601 3601 t += _(' (clean)')
3602 3602 cleanworkdir = True
3603 3603 elif pnode not in bheads:
3604 3604 t += _(' (new branch head)')
3605 3605
3606 3606 if cleanworkdir:
3607 3607 ui.status(_('commit: %s\n') % t.strip())
3608 3608 else:
3609 3609 ui.write(_('commit: %s\n') % t.strip())
3610 3610
3611 3611 # all ancestors of branch heads - all ancestors of parent = new csets
3612 3612 new = [0] * len(repo)
3613 3613 cl = repo.changelog
3614 3614 for a in [cl.rev(n) for n in bheads]:
3615 3615 new[a] = 1
3616 3616 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3617 3617 new[a] = 1
3618 3618 for a in [p.rev() for p in parents]:
3619 3619 if a >= 0:
3620 3620 new[a] = 0
3621 3621 for a in cl.ancestors(*[p.rev() for p in parents]):
3622 3622 new[a] = 0
3623 3623 new = sum(new)
3624 3624
3625 3625 if new == 0:
3626 3626 ui.status(_('update: (current)\n'))
3627 3627 elif pnode not in bheads:
3628 3628 ui.write(_('update: %d new changesets (update)\n') % new)
3629 3629 else:
3630 3630 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3631 3631 (new, len(bheads)))
3632 3632
3633 3633 if opts.get('remote'):
3634 3634 t = []
3635 3635 source, branches = hg.parseurl(ui.expandpath('default'))
3636 3636 other = hg.repository(hg.remoteui(repo, {}), source)
3637 3637 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3638 3638 ui.debug('comparing with %s\n' % url.hidepassword(source))
3639 3639 repo.ui.pushbuffer()
3640 3640 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3641 3641 repo.ui.popbuffer()
3642 3642 if incoming:
3643 3643 t.append(_('1 or more incoming'))
3644 3644
3645 3645 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3646 3646 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3647 3647 other = hg.repository(hg.remoteui(repo, {}), dest)
3648 3648 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3649 3649 repo.ui.pushbuffer()
3650 3650 o = discovery.findoutgoing(repo, other)
3651 3651 repo.ui.popbuffer()
3652 3652 o = repo.changelog.nodesbetween(o, None)[0]
3653 3653 if o:
3654 3654 t.append(_('%d outgoing') % len(o))
3655 3655
3656 3656 if t:
3657 3657 ui.write(_('remote: %s\n') % (', '.join(t)))
3658 3658 else:
3659 3659 ui.status(_('remote: (synced)\n'))
3660 3660
3661 3661 def tag(ui, repo, name1, *names, **opts):
3662 3662 """add one or more tags for the current or given revision
3663 3663
3664 3664 Name a particular revision using <name>.
3665 3665
3666 3666 Tags are used to name particular revisions of the repository and are
3667 3667 very useful to compare different revisions, to go back to significant
3668 3668 earlier versions or to mark branch points as releases, etc. Changing
3669 3669 an existing tag is normally disallowed; use -f/--force to override.
3670 3670
3671 3671 If no revision is given, the parent of the working directory is
3672 3672 used, or tip if no revision is checked out.
3673 3673
3674 3674 To facilitate version control, distribution, and merging of tags,
3675 3675 they are stored as a file named ".hgtags" which is managed similarly
3676 3676 to other project files and can be hand-edited if necessary. This
3677 3677 also means that tagging creates a new commit. The file
3678 3678 ".hg/localtags" is used for local tags (not shared among
3679 3679 repositories).
3680 3680
3681 3681 Tag commits are usually made at the head of a branch. If the parent
3682 3682 of the working directory is not a branch head, :hg:`tag` aborts; use
3683 3683 -f/--force to force the tag commit to be based on a non-head
3684 3684 changeset.
3685 3685
3686 3686 See :hg:`help dates` for a list of formats valid for -d/--date.
3687 3687
3688 3688 Since tag names have priority over branch names during revision
3689 3689 lookup, using an existing branch name as a tag name is discouraged.
3690 3690
3691 3691 Returns 0 on success.
3692 3692 """
3693 3693
3694 3694 rev_ = "."
3695 3695 names = [t.strip() for t in (name1,) + names]
3696 3696 if len(names) != len(set(names)):
3697 3697 raise util.Abort(_('tag names must be unique'))
3698 3698 for n in names:
3699 3699 if n in ['tip', '.', 'null']:
3700 3700 raise util.Abort(_('the name \'%s\' is reserved') % n)
3701 3701 if not n:
3702 3702 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3703 3703 if opts.get('rev') and opts.get('remove'):
3704 3704 raise util.Abort(_("--rev and --remove are incompatible"))
3705 3705 if opts.get('rev'):
3706 3706 rev_ = opts['rev']
3707 3707 message = opts.get('message')
3708 3708 if opts.get('remove'):
3709 3709 expectedtype = opts.get('local') and 'local' or 'global'
3710 3710 for n in names:
3711 3711 if not repo.tagtype(n):
3712 3712 raise util.Abort(_('tag \'%s\' does not exist') % n)
3713 3713 if repo.tagtype(n) != expectedtype:
3714 3714 if expectedtype == 'global':
3715 3715 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3716 3716 else:
3717 3717 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3718 3718 rev_ = nullid
3719 3719 if not message:
3720 3720 # we don't translate commit messages
3721 3721 message = 'Removed tag %s' % ', '.join(names)
3722 3722 elif not opts.get('force'):
3723 3723 for n in names:
3724 3724 if n in repo.tags():
3725 3725 raise util.Abort(_('tag \'%s\' already exists '
3726 3726 '(use -f to force)') % n)
3727 3727 if not opts.get('local'):
3728 3728 p1, p2 = repo.dirstate.parents()
3729 3729 if p2 != nullid:
3730 3730 raise util.Abort(_('uncommitted merge'))
3731 3731 bheads = repo.branchheads()
3732 3732 if not opts.get('force') and bheads and p1 not in bheads:
3733 3733 raise util.Abort(_('not at a branch head (use -f to force)'))
3734 3734 r = cmdutil.revsingle(repo, rev_).node()
3735 3735
3736 3736 if not message:
3737 3737 # we don't translate commit messages
3738 3738 message = ('Added tag %s for changeset %s' %
3739 3739 (', '.join(names), short(r)))
3740 3740
3741 3741 date = opts.get('date')
3742 3742 if date:
3743 3743 date = util.parsedate(date)
3744 3744
3745 3745 if opts.get('edit'):
3746 3746 message = ui.edit(message, ui.username())
3747 3747
3748 3748 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3749 3749
3750 3750 def tags(ui, repo):
3751 3751 """list repository tags
3752 3752
3753 3753 This lists both regular and local tags. When the -v/--verbose
3754 3754 switch is used, a third column "local" is printed for local tags.
3755 3755
3756 3756 Returns 0 on success.
3757 3757 """
3758 3758
3759 3759 hexfunc = ui.debugflag and hex or short
3760 3760 tagtype = ""
3761 3761
3762 3762 for t, n in reversed(repo.tagslist()):
3763 3763 if ui.quiet:
3764 3764 ui.write("%s\n" % t)
3765 3765 continue
3766 3766
3767 3767 try:
3768 3768 hn = hexfunc(n)
3769 3769 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3770 3770 except error.LookupError:
3771 3771 r = " ?:%s" % hn
3772 3772 else:
3773 3773 spaces = " " * (30 - encoding.colwidth(t))
3774 3774 if ui.verbose:
3775 3775 if repo.tagtype(t) == 'local':
3776 3776 tagtype = " local"
3777 3777 else:
3778 3778 tagtype = ""
3779 3779 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3780 3780
3781 3781 def tip(ui, repo, **opts):
3782 3782 """show the tip revision
3783 3783
3784 3784 The tip revision (usually just called the tip) is the changeset
3785 3785 most recently added to the repository (and therefore the most
3786 3786 recently changed head).
3787 3787
3788 3788 If you have just made a commit, that commit will be the tip. If
3789 3789 you have just pulled changes from another repository, the tip of
3790 3790 that repository becomes the current tip. The "tip" tag is special
3791 3791 and cannot be renamed or assigned to a different changeset.
3792 3792
3793 3793 Returns 0 on success.
3794 3794 """
3795 3795 displayer = cmdutil.show_changeset(ui, repo, opts)
3796 3796 displayer.show(repo[len(repo) - 1])
3797 3797 displayer.close()
3798 3798
3799 3799 def unbundle(ui, repo, fname1, *fnames, **opts):
3800 3800 """apply one or more changegroup files
3801 3801
3802 3802 Apply one or more compressed changegroup files generated by the
3803 3803 bundle command.
3804 3804
3805 3805 Returns 0 on success, 1 if an update has unresolved files.
3806 3806 """
3807 3807 fnames = (fname1,) + fnames
3808 3808
3809 3809 lock = repo.lock()
3810 3810 try:
3811 3811 for fname in fnames:
3812 3812 f = url.open(ui, fname)
3813 3813 gen = changegroup.readbundle(f, fname)
3814 3814 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3815 3815 lock=lock)
3816 3816 finally:
3817 3817 lock.release()
3818 3818
3819 3819 return postincoming(ui, repo, modheads, opts.get('update'), None)
3820 3820
3821 3821 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3822 3822 """update working directory (or switch revisions)
3823 3823
3824 3824 Update the repository's working directory to the specified
3825 3825 changeset. If no changeset is specified, update to the tip of the
3826 3826 current named branch.
3827 3827
3828 3828 If the changeset is not a descendant of the working directory's
3829 3829 parent, the update is aborted. With the -c/--check option, the
3830 3830 working directory is checked for uncommitted changes; if none are
3831 3831 found, the working directory is updated to the specified
3832 3832 changeset.
3833 3833
3834 3834 The following rules apply when the working directory contains
3835 3835 uncommitted changes:
3836 3836
3837 3837 1. If neither -c/--check nor -C/--clean is specified, and if
3838 3838 the requested changeset is an ancestor or descendant of
3839 3839 the working directory's parent, the uncommitted changes
3840 3840 are merged into the requested changeset and the merged
3841 3841 result is left uncommitted. If the requested changeset is
3842 3842 not an ancestor or descendant (that is, it is on another
3843 3843 branch), the update is aborted and the uncommitted changes
3844 3844 are preserved.
3845 3845
3846 3846 2. With the -c/--check option, the update is aborted and the
3847 3847 uncommitted changes are preserved.
3848 3848
3849 3849 3. With the -C/--clean option, uncommitted changes are discarded and
3850 3850 the working directory is updated to the requested changeset.
3851 3851
3852 3852 Use null as the changeset to remove the working directory (like
3853 3853 :hg:`clone -U`).
3854 3854
3855 3855 If you want to update just one file to an older changeset, use
3856 3856 :hg:`revert`.
3857 3857
3858 3858 See :hg:`help dates` for a list of formats valid for -d/--date.
3859 3859
3860 3860 Returns 0 on success, 1 if there are unresolved files.
3861 3861 """
3862 3862 if rev and node:
3863 3863 raise util.Abort(_("please specify just one revision"))
3864 3864
3865 3865 if not rev:
3866 3866 rev = node
3867 3867
3868 3868 rev = cmdutil.revsingle(repo, rev, rev).rev()
3869 3869
3870 3870 if check and clean:
3871 3871 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3872 3872
3873 3873 if check:
3874 3874 # we could use dirty() but we can ignore merge and branch trivia
3875 3875 c = repo[None]
3876 3876 if c.modified() or c.added() or c.removed():
3877 3877 raise util.Abort(_("uncommitted local changes"))
3878 3878
3879 3879 if date:
3880 3880 if rev:
3881 3881 raise util.Abort(_("you can't specify a revision and a date"))
3882 3882 rev = cmdutil.finddate(ui, repo, date)
3883 3883
3884 3884 if clean or check:
3885 3885 return hg.clean(repo, rev)
3886 3886 else:
3887 3887 return hg.update(repo, rev)
3888 3888
3889 3889 def verify(ui, repo):
3890 3890 """verify the integrity of the repository
3891 3891
3892 3892 Verify the integrity of the current repository.
3893 3893
3894 3894 This will perform an extensive check of the repository's
3895 3895 integrity, validating the hashes and checksums of each entry in
3896 3896 the changelog, manifest, and tracked files, as well as the
3897 3897 integrity of their crosslinks and indices.
3898 3898
3899 3899 Returns 0 on success, 1 if errors are encountered.
3900 3900 """
3901 3901 return hg.verify(repo)
3902 3902
3903 3903 def version_(ui):
3904 3904 """output version and copyright information"""
3905 3905 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3906 3906 % util.version())
3907 3907 ui.status(_(
3908 3908 "(see http://mercurial.selenic.com for more information)\n"
3909 3909 "\nCopyright (C) 2005-2010 Matt Mackall and others\n"
3910 3910 "This is free software; see the source for copying conditions. "
3911 3911 "There is NO\nwarranty; "
3912 3912 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3913 3913 ))
3914 3914
3915 3915 # Command options and aliases are listed here, alphabetically
3916 3916
3917 3917 globalopts = [
3918 3918 ('R', 'repository', '',
3919 3919 _('repository root directory or name of overlay bundle file'),
3920 3920 _('REPO')),
3921 3921 ('', 'cwd', '',
3922 3922 _('change working directory'), _('DIR')),
3923 3923 ('y', 'noninteractive', None,
3924 3924 _('do not prompt, assume \'yes\' for any required answers')),
3925 3925 ('q', 'quiet', None, _('suppress output')),
3926 3926 ('v', 'verbose', None, _('enable additional output')),
3927 3927 ('', 'config', [],
3928 3928 _('set/override config option (use \'section.name=value\')'),
3929 3929 _('CONFIG')),
3930 3930 ('', 'debug', None, _('enable debugging output')),
3931 3931 ('', 'debugger', None, _('start debugger')),
3932 3932 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3933 3933 _('ENCODE')),
3934 3934 ('', 'encodingmode', encoding.encodingmode,
3935 3935 _('set the charset encoding mode'), _('MODE')),
3936 3936 ('', 'traceback', None, _('always print a traceback on exception')),
3937 3937 ('', 'time', None, _('time how long the command takes')),
3938 3938 ('', 'profile', None, _('print command execution profile')),
3939 3939 ('', 'version', None, _('output version information and exit')),
3940 3940 ('h', 'help', None, _('display help and exit')),
3941 3941 ]
3942 3942
3943 3943 dryrunopts = [('n', 'dry-run', None,
3944 3944 _('do not perform actions, just print output'))]
3945 3945
3946 3946 remoteopts = [
3947 3947 ('e', 'ssh', '',
3948 3948 _('specify ssh command to use'), _('CMD')),
3949 3949 ('', 'remotecmd', '',
3950 3950 _('specify hg command to run on the remote side'), _('CMD')),
3951 3951 ]
3952 3952
3953 3953 walkopts = [
3954 3954 ('I', 'include', [],
3955 3955 _('include names matching the given patterns'), _('PATTERN')),
3956 3956 ('X', 'exclude', [],
3957 3957 _('exclude names matching the given patterns'), _('PATTERN')),
3958 3958 ]
3959 3959
3960 3960 commitopts = [
3961 3961 ('m', 'message', '',
3962 3962 _('use text as commit message'), _('TEXT')),
3963 3963 ('l', 'logfile', '',
3964 3964 _('read commit message from file'), _('FILE')),
3965 3965 ]
3966 3966
3967 3967 commitopts2 = [
3968 3968 ('d', 'date', '',
3969 3969 _('record datecode as commit date'), _('DATE')),
3970 3970 ('u', 'user', '',
3971 3971 _('record the specified user as committer'), _('USER')),
3972 3972 ]
3973 3973
3974 3974 templateopts = [
3975 3975 ('', 'style', '',
3976 3976 _('display using template map file'), _('STYLE')),
3977 3977 ('', 'template', '',
3978 3978 _('display with template'), _('TEMPLATE')),
3979 3979 ]
3980 3980
3981 3981 logopts = [
3982 3982 ('p', 'patch', None, _('show patch')),
3983 3983 ('g', 'git', None, _('use git extended diff format')),
3984 3984 ('l', 'limit', '',
3985 3985 _('limit number of changes displayed'), _('NUM')),
3986 3986 ('M', 'no-merges', None, _('do not show merges')),
3987 3987 ('', 'stat', None, _('output diffstat-style summary of changes')),
3988 3988 ] + templateopts
3989 3989
3990 3990 diffopts = [
3991 3991 ('a', 'text', None, _('treat all files as text')),
3992 3992 ('g', 'git', None, _('use git extended diff format')),
3993 3993 ('', 'nodates', None, _('omit dates from diff headers'))
3994 3994 ]
3995 3995
3996 3996 diffopts2 = [
3997 3997 ('p', 'show-function', None, _('show which function each change is in')),
3998 3998 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3999 3999 ('w', 'ignore-all-space', None,
4000 4000 _('ignore white space when comparing lines')),
4001 4001 ('b', 'ignore-space-change', None,
4002 4002 _('ignore changes in the amount of white space')),
4003 4003 ('B', 'ignore-blank-lines', None,
4004 4004 _('ignore changes whose lines are all blank')),
4005 4005 ('U', 'unified', '',
4006 4006 _('number of lines of context to show'), _('NUM')),
4007 4007 ('', 'stat', None, _('output diffstat-style summary of changes')),
4008 4008 ]
4009 4009
4010 4010 similarityopts = [
4011 4011 ('s', 'similarity', '',
4012 4012 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4013 4013 ]
4014 4014
4015 4015 subrepoopts = [
4016 4016 ('S', 'subrepos', None,
4017 4017 _('recurse into subrepositories'))
4018 4018 ]
4019 4019
4020 4020 table = {
4021 4021 "^add": (add, walkopts + subrepoopts + dryrunopts,
4022 4022 _('[OPTION]... [FILE]...')),
4023 4023 "addremove":
4024 4024 (addremove, similarityopts + walkopts + dryrunopts,
4025 4025 _('[OPTION]... [FILE]...')),
4026 4026 "^annotate|blame":
4027 4027 (annotate,
4028 4028 [('r', 'rev', '',
4029 4029 _('annotate the specified revision'), _('REV')),
4030 4030 ('', 'follow', None,
4031 4031 _('follow copies/renames and list the filename (DEPRECATED)')),
4032 4032 ('', 'no-follow', None, _("don't follow copies and renames")),
4033 4033 ('a', 'text', None, _('treat all files as text')),
4034 4034 ('u', 'user', None, _('list the author (long with -v)')),
4035 4035 ('f', 'file', None, _('list the filename')),
4036 4036 ('d', 'date', None, _('list the date (short with -q)')),
4037 4037 ('n', 'number', None, _('list the revision number (default)')),
4038 4038 ('c', 'changeset', None, _('list the changeset')),
4039 4039 ('l', 'line-number', None,
4040 4040 _('show line number at the first appearance'))
4041 4041 ] + walkopts,
4042 4042 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4043 4043 "archive":
4044 4044 (archive,
4045 4045 [('', 'no-decode', None, _('do not pass files through decoders')),
4046 4046 ('p', 'prefix', '',
4047 4047 _('directory prefix for files in archive'), _('PREFIX')),
4048 4048 ('r', 'rev', '',
4049 4049 _('revision to distribute'), _('REV')),
4050 4050 ('t', 'type', '',
4051 4051 _('type of distribution to create'), _('TYPE')),
4052 4052 ] + subrepoopts + walkopts,
4053 4053 _('[OPTION]... DEST')),
4054 4054 "backout":
4055 4055 (backout,
4056 4056 [('', 'merge', None,
4057 4057 _('merge with old dirstate parent after backout')),
4058 4058 ('', 'parent', '',
4059 4059 _('parent to choose when backing out merge'), _('REV')),
4060 4060 ('t', 'tool', '',
4061 4061 _('specify merge tool')),
4062 4062 ('r', 'rev', '',
4063 4063 _('revision to backout'), _('REV')),
4064 4064 ] + walkopts + commitopts + commitopts2,
4065 4065 _('[OPTION]... [-r] REV')),
4066 4066 "bisect":
4067 4067 (bisect,
4068 4068 [('r', 'reset', False, _('reset bisect state')),
4069 4069 ('g', 'good', False, _('mark changeset good')),
4070 4070 ('b', 'bad', False, _('mark changeset bad')),
4071 4071 ('s', 'skip', False, _('skip testing changeset')),
4072 4072 ('c', 'command', '',
4073 4073 _('use command to check changeset state'), _('CMD')),
4074 4074 ('U', 'noupdate', False, _('do not update to target'))],
4075 4075 _("[-gbsr] [-U] [-c CMD] [REV]")),
4076 4076 "branch":
4077 4077 (branch,
4078 4078 [('f', 'force', None,
4079 4079 _('set branch name even if it shadows an existing branch')),
4080 4080 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4081 4081 _('[-fC] [NAME]')),
4082 4082 "branches":
4083 4083 (branches,
4084 4084 [('a', 'active', False,
4085 4085 _('show only branches that have unmerged heads')),
4086 4086 ('c', 'closed', False,
4087 4087 _('show normal and closed branches'))],
4088 4088 _('[-ac]')),
4089 4089 "bundle":
4090 4090 (bundle,
4091 4091 [('f', 'force', None,
4092 4092 _('run even when the destination is unrelated')),
4093 4093 ('r', 'rev', [],
4094 4094 _('a changeset intended to be added to the destination'),
4095 4095 _('REV')),
4096 4096 ('b', 'branch', [],
4097 4097 _('a specific branch you would like to bundle'),
4098 4098 _('BRANCH')),
4099 4099 ('', 'base', [],
4100 4100 _('a base changeset assumed to be available at the destination'),
4101 4101 _('REV')),
4102 4102 ('a', 'all', None, _('bundle all changesets in the repository')),
4103 4103 ('t', 'type', 'bzip2',
4104 4104 _('bundle compression type to use'), _('TYPE')),
4105 4105 ] + remoteopts,
4106 4106 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4107 4107 "cat":
4108 4108 (cat,
4109 4109 [('o', 'output', '',
4110 4110 _('print output to file with formatted name'), _('FORMAT')),
4111 4111 ('r', 'rev', '',
4112 4112 _('print the given revision'), _('REV')),
4113 4113 ('', 'decode', None, _('apply any matching decode filter')),
4114 4114 ] + walkopts,
4115 4115 _('[OPTION]... FILE...')),
4116 4116 "^clone":
4117 4117 (clone,
4118 4118 [('U', 'noupdate', None,
4119 4119 _('the clone will include an empty working copy (only a repository)')),
4120 4120 ('u', 'updaterev', '',
4121 4121 _('revision, tag or branch to check out'), _('REV')),
4122 4122 ('r', 'rev', [],
4123 4123 _('include the specified changeset'), _('REV')),
4124 4124 ('b', 'branch', [],
4125 4125 _('clone only the specified branch'), _('BRANCH')),
4126 4126 ('', 'pull', None, _('use pull protocol to copy metadata')),
4127 4127 ('', 'uncompressed', None,
4128 4128 _('use uncompressed transfer (fast over LAN)')),
4129 4129 ] + remoteopts,
4130 4130 _('[OPTION]... SOURCE [DEST]')),
4131 4131 "^commit|ci":
4132 4132 (commit,
4133 4133 [('A', 'addremove', None,
4134 4134 _('mark new/missing files as added/removed before committing')),
4135 4135 ('', 'close-branch', None,
4136 4136 _('mark a branch as closed, hiding it from the branch list')),
4137 4137 ] + walkopts + commitopts + commitopts2,
4138 4138 _('[OPTION]... [FILE]...')),
4139 4139 "copy|cp":
4140 4140 (copy,
4141 4141 [('A', 'after', None, _('record a copy that has already occurred')),
4142 4142 ('f', 'force', None,
4143 4143 _('forcibly copy over an existing managed file')),
4144 4144 ] + walkopts + dryrunopts,
4145 4145 _('[OPTION]... [SOURCE]... DEST')),
4146 4146 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4147 4147 "debugbuilddag":
4148 4148 (debugbuilddag,
4149 4149 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4150 4150 ('a', 'appended-file', None, _('add single file all revs append to')),
4151 4151 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4152 4152 ('n', 'new-file', None, _('add new file at each rev')),
4153 4153 ],
4154 4154 _('[OPTION]... TEXT')),
4155 4155 "debugcheckstate": (debugcheckstate, [], ''),
4156 4156 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4157 4157 "debugcomplete":
4158 4158 (debugcomplete,
4159 4159 [('o', 'options', None, _('show the command options'))],
4160 4160 _('[-o] CMD')),
4161 4161 "debugdag":
4162 4162 (debugdag,
4163 4163 [('t', 'tags', None, _('use tags as labels')),
4164 4164 ('b', 'branches', None, _('annotate with branch names')),
4165 4165 ('', 'dots', None, _('use dots for runs')),
4166 4166 ('s', 'spaces', None, _('separate elements by spaces')),
4167 4167 ],
4168 4168 _('[OPTION]... [FILE [REV]...]')),
4169 4169 "debugdate":
4170 4170 (debugdate,
4171 4171 [('e', 'extended', None, _('try extended date formats'))],
4172 4172 _('[-e] DATE [RANGE]')),
4173 4173 "debugdata": (debugdata, [], _('FILE REV')),
4174 4174 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4175 4175 "debugindex": (debugindex,
4176 4176 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4177 4177 _('FILE')),
4178 4178 "debugindexdot": (debugindexdot, [], _('FILE')),
4179 4179 "debuginstall": (debuginstall, [], ''),
4180 4180 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4181 4181 "debugrebuildstate":
4182 4182 (debugrebuildstate,
4183 4183 [('r', 'rev', '',
4184 4184 _('revision to rebuild to'), _('REV'))],
4185 4185 _('[-r REV] [REV]')),
4186 4186 "debugrename":
4187 4187 (debugrename,
4188 4188 [('r', 'rev', '',
4189 4189 _('revision to debug'), _('REV'))],
4190 4190 _('[-r REV] FILE')),
4191 4191 "debugrevspec":
4192 4192 (debugrevspec, [], ('REVSPEC')),
4193 4193 "debugsetparents":
4194 4194 (debugsetparents, [], _('REV1 [REV2]')),
4195 4195 "debugstate":
4196 4196 (debugstate,
4197 4197 [('', 'nodates', None, _('do not display the saved mtime'))],
4198 4198 _('[OPTION]...')),
4199 4199 "debugsub":
4200 4200 (debugsub,
4201 4201 [('r', 'rev', '',
4202 4202 _('revision to check'), _('REV'))],
4203 4203 _('[-r REV] [REV]')),
4204 4204 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4205 4205 "^diff":
4206 4206 (diff,
4207 4207 [('r', 'rev', [],
4208 4208 _('revision'), _('REV')),
4209 4209 ('c', 'change', '',
4210 4210 _('change made by revision'), _('REV'))
4211 4211 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4212 4212 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4213 4213 "^export":
4214 4214 (export,
4215 4215 [('o', 'output', '',
4216 4216 _('print output to file with formatted name'), _('FORMAT')),
4217 4217 ('', 'switch-parent', None, _('diff against the second parent')),
4218 4218 ('r', 'rev', [],
4219 4219 _('revisions to export'), _('REV')),
4220 4220 ] + diffopts,
4221 4221 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4222 4222 "^forget":
4223 4223 (forget,
4224 4224 [] + walkopts,
4225 4225 _('[OPTION]... FILE...')),
4226 4226 "grep":
4227 4227 (grep,
4228 4228 [('0', 'print0', None, _('end fields with NUL')),
4229 4229 ('', 'all', None, _('print all revisions that match')),
4230 4230 ('f', 'follow', None,
4231 4231 _('follow changeset history,'
4232 4232 ' or file history across copies and renames')),
4233 4233 ('i', 'ignore-case', None, _('ignore case when matching')),
4234 4234 ('l', 'files-with-matches', None,
4235 4235 _('print only filenames and revisions that match')),
4236 4236 ('n', 'line-number', None, _('print matching line numbers')),
4237 4237 ('r', 'rev', [],
4238 4238 _('only search files changed within revision range'), _('REV')),
4239 4239 ('u', 'user', None, _('list the author (long with -v)')),
4240 4240 ('d', 'date', None, _('list the date (short with -q)')),
4241 4241 ] + walkopts,
4242 4242 _('[OPTION]... PATTERN [FILE]...')),
4243 4243 "heads":
4244 4244 (heads,
4245 4245 [('r', 'rev', '',
4246 4246 _('show only heads which are descendants of STARTREV'),
4247 4247 _('STARTREV')),
4248 4248 ('t', 'topo', False, _('show topological heads only')),
4249 4249 ('a', 'active', False,
4250 4250 _('show active branchheads only (DEPRECATED)')),
4251 4251 ('c', 'closed', False,
4252 4252 _('show normal and closed branch heads')),
4253 4253 ] + templateopts,
4254 4254 _('[-ac] [-r STARTREV] [REV]...')),
4255 4255 "help": (help_, [], _('[TOPIC]')),
4256 4256 "identify|id":
4257 4257 (identify,
4258 4258 [('r', 'rev', '',
4259 4259 _('identify the specified revision'), _('REV')),
4260 4260 ('n', 'num', None, _('show local revision number')),
4261 4261 ('i', 'id', None, _('show global revision id')),
4262 4262 ('b', 'branch', None, _('show branch')),
4263 4263 ('t', 'tags', None, _('show tags'))],
4264 4264 _('[-nibt] [-r REV] [SOURCE]')),
4265 4265 "import|patch":
4266 4266 (import_,
4267 4267 [('p', 'strip', 1,
4268 4268 _('directory strip option for patch. This has the same '
4269 4269 'meaning as the corresponding patch option'),
4270 4270 _('NUM')),
4271 4271 ('b', 'base', '',
4272 4272 _('base path'), _('PATH')),
4273 4273 ('f', 'force', None,
4274 4274 _('skip check for outstanding uncommitted changes')),
4275 4275 ('', 'no-commit', None,
4276 4276 _("don't commit, just update the working directory")),
4277 4277 ('', 'exact', None,
4278 4278 _('apply patch to the nodes from which it was generated')),
4279 4279 ('', 'import-branch', None,
4280 4280 _('use any branch information in patch (implied by --exact)'))] +
4281 4281 commitopts + commitopts2 + similarityopts,
4282 4282 _('[OPTION]... PATCH...')),
4283 4283 "incoming|in":
4284 4284 (incoming,
4285 4285 [('f', 'force', None,
4286 4286 _('run even if remote repository is unrelated')),
4287 4287 ('n', 'newest-first', None, _('show newest record first')),
4288 4288 ('', 'bundle', '',
4289 4289 _('file to store the bundles into'), _('FILE')),
4290 4290 ('r', 'rev', [],
4291 4291 _('a remote changeset intended to be added'), _('REV')),
4292 4292 ('b', 'branch', [],
4293 4293 _('a specific branch you would like to pull'), _('BRANCH')),
4294 4294 ] + logopts + remoteopts + subrepoopts,
4295 4295 _('[-p] [-n] [-M] [-f] [-r REV]...'
4296 4296 ' [--bundle FILENAME] [SOURCE]')),
4297 4297 "^init":
4298 4298 (init,
4299 4299 remoteopts,
4300 4300 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4301 4301 "locate":
4302 4302 (locate,
4303 4303 [('r', 'rev', '',
4304 4304 _('search the repository as it is in REV'), _('REV')),
4305 4305 ('0', 'print0', None,
4306 4306 _('end filenames with NUL, for use with xargs')),
4307 4307 ('f', 'fullpath', None,
4308 4308 _('print complete paths from the filesystem root')),
4309 4309 ] + walkopts,
4310 4310 _('[OPTION]... [PATTERN]...')),
4311 4311 "^log|history":
4312 4312 (log,
4313 4313 [('f', 'follow', None,
4314 4314 _('follow changeset history,'
4315 4315 ' or file history across copies and renames')),
4316 4316 ('', 'follow-first', None,
4317 4317 _('only follow the first parent of merge changesets')),
4318 4318 ('d', 'date', '',
4319 4319 _('show revisions matching date spec'), _('DATE')),
4320 4320 ('C', 'copies', None, _('show copied files')),
4321 4321 ('k', 'keyword', [],
4322 4322 _('do case-insensitive search for a given text'), _('TEXT')),
4323 4323 ('r', 'rev', [],
4324 4324 _('show the specified revision or range'), _('REV')),
4325 4325 ('', 'removed', None, _('include revisions where files were removed')),
4326 4326 ('m', 'only-merges', None, _('show only merges')),
4327 4327 ('u', 'user', [],
4328 4328 _('revisions committed by user'), _('USER')),
4329 4329 ('', 'only-branch', [],
4330 4330 _('show only changesets within the given named branch (DEPRECATED)'),
4331 4331 _('BRANCH')),
4332 4332 ('b', 'branch', [],
4333 4333 _('show changesets within the given named branch'), _('BRANCH')),
4334 4334 ('P', 'prune', [],
4335 4335 _('do not display revision or any of its ancestors'), _('REV')),
4336 4336 ] + logopts + walkopts,
4337 4337 _('[OPTION]... [FILE]')),
4338 4338 "manifest":
4339 4339 (manifest,
4340 4340 [('r', 'rev', '',
4341 4341 _('revision to display'), _('REV'))],
4342 4342 _('[-r REV]')),
4343 4343 "^merge":
4344 4344 (merge,
4345 4345 [('f', 'force', None, _('force a merge with outstanding changes')),
4346 4346 ('t', 'tool', '', _('specify merge tool')),
4347 4347 ('r', 'rev', '',
4348 4348 _('revision to merge'), _('REV')),
4349 4349 ('P', 'preview', None,
4350 4350 _('review revisions to merge (no merge is performed)'))],
4351 4351 _('[-P] [-f] [[-r] REV]')),
4352 4352 "outgoing|out":
4353 4353 (outgoing,
4354 4354 [('f', 'force', None,
4355 4355 _('run even when the destination is unrelated')),
4356 4356 ('r', 'rev', [],
4357 4357 _('a changeset intended to be included in the destination'),
4358 4358 _('REV')),
4359 4359 ('n', 'newest-first', None, _('show newest record first')),
4360 4360 ('b', 'branch', [],
4361 4361 _('a specific branch you would like to push'), _('BRANCH')),
4362 4362 ] + logopts + remoteopts + subrepoopts,
4363 4363 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4364 4364 "parents":
4365 4365 (parents,
4366 4366 [('r', 'rev', '',
4367 4367 _('show parents of the specified revision'), _('REV')),
4368 4368 ] + templateopts,
4369 4369 _('[-r REV] [FILE]')),
4370 4370 "paths": (paths, [], _('[NAME]')),
4371 4371 "^pull":
4372 4372 (pull,
4373 4373 [('u', 'update', None,
4374 4374 _('update to new branch head if changesets were pulled')),
4375 4375 ('f', 'force', None,
4376 4376 _('run even when remote repository is unrelated')),
4377 4377 ('r', 'rev', [],
4378 4378 _('a remote changeset intended to be added'), _('REV')),
4379 4379 ('b', 'branch', [],
4380 4380 _('a specific branch you would like to pull'), _('BRANCH')),
4381 4381 ] + remoteopts,
4382 4382 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4383 4383 "^push":
4384 4384 (push,
4385 4385 [('f', 'force', None, _('force push')),
4386 4386 ('r', 'rev', [],
4387 4387 _('a changeset intended to be included in the destination'),
4388 4388 _('REV')),
4389 4389 ('b', 'branch', [],
4390 4390 _('a specific branch you would like to push'), _('BRANCH')),
4391 4391 ('', 'new-branch', False, _('allow pushing a new branch')),
4392 4392 ] + remoteopts,
4393 4393 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4394 4394 "recover": (recover, []),
4395 4395 "^remove|rm":
4396 4396 (remove,
4397 4397 [('A', 'after', None, _('record delete for missing files')),
4398 4398 ('f', 'force', None,
4399 4399 _('remove (and delete) file even if added or modified')),
4400 4400 ] + walkopts,
4401 4401 _('[OPTION]... FILE...')),
4402 4402 "rename|move|mv":
4403 4403 (rename,
4404 4404 [('A', 'after', None, _('record a rename that has already occurred')),
4405 4405 ('f', 'force', None,
4406 4406 _('forcibly copy over an existing managed file')),
4407 4407 ] + walkopts + dryrunopts,
4408 4408 _('[OPTION]... SOURCE... DEST')),
4409 4409 "resolve":
4410 4410 (resolve,
4411 4411 [('a', 'all', None, _('select all unresolved files')),
4412 4412 ('l', 'list', None, _('list state of files needing merge')),
4413 4413 ('m', 'mark', None, _('mark files as resolved')),
4414 4414 ('u', 'unmark', None, _('mark files as unresolved')),
4415 4415 ('t', 'tool', '', _('specify merge tool')),
4416 4416 ('n', 'no-status', None, _('hide status prefix'))]
4417 4417 + walkopts,
4418 4418 _('[OPTION]... [FILE]...')),
4419 4419 "revert":
4420 4420 (revert,
4421 4421 [('a', 'all', None, _('revert all changes when no arguments given')),
4422 4422 ('d', 'date', '',
4423 4423 _('tipmost revision matching date'), _('DATE')),
4424 4424 ('r', 'rev', '',
4425 4425 _('revert to the specified revision'), _('REV')),
4426 4426 ('', 'no-backup', None, _('do not save backup copies of files')),
4427 4427 ] + walkopts + dryrunopts,
4428 4428 _('[OPTION]... [-r REV] [NAME]...')),
4429 4429 "rollback": (rollback, dryrunopts),
4430 4430 "root": (root, []),
4431 4431 "^serve":
4432 4432 (serve,
4433 4433 [('A', 'accesslog', '',
4434 4434 _('name of access log file to write to'), _('FILE')),
4435 4435 ('d', 'daemon', None, _('run server in background')),
4436 4436 ('', 'daemon-pipefds', '',
4437 4437 _('used internally by daemon mode'), _('NUM')),
4438 4438 ('E', 'errorlog', '',
4439 4439 _('name of error log file to write to'), _('FILE')),
4440 4440 # use string type, then we can check if something was passed
4441 4441 ('p', 'port', '',
4442 4442 _('port to listen on (default: 8000)'), _('PORT')),
4443 4443 ('a', 'address', '',
4444 4444 _('address to listen on (default: all interfaces)'), _('ADDR')),
4445 4445 ('', 'prefix', '',
4446 4446 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4447 4447 ('n', 'name', '',
4448 4448 _('name to show in web pages (default: working directory)'),
4449 4449 _('NAME')),
4450 4450 ('', 'web-conf', '',
4451 4451 _('name of the hgweb config file (see "hg help hgweb")'),
4452 4452 _('FILE')),
4453 4453 ('', 'webdir-conf', '',
4454 4454 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4455 4455 ('', 'pid-file', '',
4456 4456 _('name of file to write process ID to'), _('FILE')),
4457 4457 ('', 'stdio', None, _('for remote clients')),
4458 4458 ('t', 'templates', '',
4459 4459 _('web templates to use'), _('TEMPLATE')),
4460 4460 ('', 'style', '',
4461 4461 _('template style to use'), _('STYLE')),
4462 4462 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4463 4463 ('', 'certificate', '',
4464 4464 _('SSL certificate file'), _('FILE'))],
4465 4465 _('[OPTION]...')),
4466 4466 "showconfig|debugconfig":
4467 4467 (showconfig,
4468 4468 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4469 4469 _('[-u] [NAME]...')),
4470 4470 "^summary|sum":
4471 4471 (summary,
4472 4472 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4473 4473 "^status|st":
4474 4474 (status,
4475 4475 [('A', 'all', None, _('show status of all files')),
4476 4476 ('m', 'modified', None, _('show only modified files')),
4477 4477 ('a', 'added', None, _('show only added files')),
4478 4478 ('r', 'removed', None, _('show only removed files')),
4479 4479 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4480 4480 ('c', 'clean', None, _('show only files without changes')),
4481 4481 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4482 4482 ('i', 'ignored', None, _('show only ignored files')),
4483 4483 ('n', 'no-status', None, _('hide status prefix')),
4484 4484 ('C', 'copies', None, _('show source of copied files')),
4485 4485 ('0', 'print0', None,
4486 4486 _('end filenames with NUL, for use with xargs')),
4487 4487 ('', 'rev', [],
4488 4488 _('show difference from revision'), _('REV')),
4489 4489 ('', 'change', '',
4490 4490 _('list the changed files of a revision'), _('REV')),
4491 4491 ] + walkopts + subrepoopts,
4492 4492 _('[OPTION]... [FILE]...')),
4493 4493 "tag":
4494 4494 (tag,
4495 4495 [('f', 'force', None, _('force tag')),
4496 4496 ('l', 'local', None, _('make the tag local')),
4497 4497 ('r', 'rev', '',
4498 4498 _('revision to tag'), _('REV')),
4499 4499 ('', 'remove', None, _('remove a tag')),
4500 4500 # -l/--local is already there, commitopts cannot be used
4501 4501 ('e', 'edit', None, _('edit commit message')),
4502 4502 ('m', 'message', '',
4503 4503 _('use <text> as commit message'), _('TEXT')),
4504 4504 ] + commitopts2,
4505 4505 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4506 4506 "tags": (tags, [], ''),
4507 4507 "tip":
4508 4508 (tip,
4509 4509 [('p', 'patch', None, _('show patch')),
4510 4510 ('g', 'git', None, _('use git extended diff format')),
4511 4511 ] + templateopts,
4512 4512 _('[-p] [-g]')),
4513 4513 "unbundle":
4514 4514 (unbundle,
4515 4515 [('u', 'update', None,
4516 4516 _('update to new branch head if changesets were unbundled'))],
4517 4517 _('[-u] FILE...')),
4518 4518 "^update|up|checkout|co":
4519 4519 (update,
4520 4520 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4521 4521 ('c', 'check', None,
4522 4522 _('update across branches if no uncommitted changes')),
4523 4523 ('d', 'date', '',
4524 4524 _('tipmost revision matching date'), _('DATE')),
4525 4525 ('r', 'rev', '',
4526 4526 _('revision'), _('REV'))],
4527 4527 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4528 4528 "verify": (verify, []),
4529 4529 "version": (version_, []),
4530 4530 }
4531 4531
4532 4532 norepo = ("clone init version help debugcommands debugcomplete"
4533 4533 " debugdate debuginstall debugfsinfo debugpushkey")
4534 4534 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4535 4535 " debugdata debugindex debugindexdot")
@@ -1,1103 +1,1103 b''
1 1 # context.py - changeset and file context objects for mercurial
2 2 #
3 3 # Copyright 2006, 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 nullid, nullrev, short, hex
9 9 from i18n import _
10 10 import ancestor, bdiff, error, util, subrepo, patch, encoding
11 11 import os, errno, stat
12 12
13 13 propertycache = util.propertycache
14 14
15 15 class changectx(object):
16 16 """A changecontext object makes access to data related to a particular
17 17 changeset convenient."""
18 18 def __init__(self, repo, changeid=''):
19 19 """changeid is a revision number, node, or tag"""
20 20 if changeid == '':
21 21 changeid = '.'
22 22 self._repo = repo
23 23 if isinstance(changeid, (long, int)):
24 24 self._rev = changeid
25 25 self._node = self._repo.changelog.node(changeid)
26 26 else:
27 27 self._node = self._repo.lookup(changeid)
28 28 self._rev = self._repo.changelog.rev(self._node)
29 29
30 30 def __str__(self):
31 31 return short(self.node())
32 32
33 33 def __int__(self):
34 34 return self.rev()
35 35
36 36 def __repr__(self):
37 37 return "<changectx %s>" % str(self)
38 38
39 39 def __hash__(self):
40 40 try:
41 41 return hash(self._rev)
42 42 except AttributeError:
43 43 return id(self)
44 44
45 45 def __eq__(self, other):
46 46 try:
47 47 return self._rev == other._rev
48 48 except AttributeError:
49 49 return False
50 50
51 51 def __ne__(self, other):
52 52 return not (self == other)
53 53
54 54 def __nonzero__(self):
55 55 return self._rev != nullrev
56 56
57 57 @propertycache
58 58 def _changeset(self):
59 59 return self._repo.changelog.read(self.node())
60 60
61 61 @propertycache
62 62 def _manifest(self):
63 63 return self._repo.manifest.read(self._changeset[0])
64 64
65 65 @propertycache
66 66 def _manifestdelta(self):
67 67 return self._repo.manifest.readdelta(self._changeset[0])
68 68
69 69 @propertycache
70 70 def _parents(self):
71 71 p = self._repo.changelog.parentrevs(self._rev)
72 72 if p[1] == nullrev:
73 73 p = p[:-1]
74 74 return [changectx(self._repo, x) for x in p]
75 75
76 76 @propertycache
77 77 def substate(self):
78 78 return subrepo.state(self, self._repo.ui)
79 79
80 80 def __contains__(self, key):
81 81 return key in self._manifest
82 82
83 83 def __getitem__(self, key):
84 84 return self.filectx(key)
85 85
86 86 def __iter__(self):
87 87 for f in sorted(self._manifest):
88 88 yield f
89 89
90 90 def changeset(self):
91 91 return self._changeset
92 92 def manifest(self):
93 93 return self._manifest
94 94 def manifestnode(self):
95 95 return self._changeset[0]
96 96
97 97 def rev(self):
98 98 return self._rev
99 99 def node(self):
100 100 return self._node
101 101 def hex(self):
102 102 return hex(self._node)
103 103 def user(self):
104 104 return self._changeset[1]
105 105 def date(self):
106 106 return self._changeset[2]
107 107 def files(self):
108 108 return self._changeset[3]
109 109 def description(self):
110 110 return self._changeset[4]
111 111 def branch(self):
112 112 return encoding.tolocal(self._changeset[5].get("branch"))
113 113 def extra(self):
114 114 return self._changeset[5]
115 115 def tags(self):
116 116 return self._repo.nodetags(self._node)
117 117
118 118 def parents(self):
119 119 """return contexts for each parent changeset"""
120 120 return self._parents
121 121
122 122 def p1(self):
123 123 return self._parents[0]
124 124
125 125 def p2(self):
126 126 if len(self._parents) == 2:
127 127 return self._parents[1]
128 128 return changectx(self._repo, -1)
129 129
130 130 def children(self):
131 131 """return contexts for each child changeset"""
132 132 c = self._repo.changelog.children(self._node)
133 133 return [changectx(self._repo, x) for x in c]
134 134
135 135 def ancestors(self):
136 136 for a in self._repo.changelog.ancestors(self._rev):
137 137 yield changectx(self._repo, a)
138 138
139 139 def descendants(self):
140 140 for d in self._repo.changelog.descendants(self._rev):
141 141 yield changectx(self._repo, d)
142 142
143 143 def _fileinfo(self, path):
144 144 if '_manifest' in self.__dict__:
145 145 try:
146 146 return self._manifest[path], self._manifest.flags(path)
147 147 except KeyError:
148 148 raise error.LookupError(self._node, path,
149 149 _('not found in manifest'))
150 150 if '_manifestdelta' in self.__dict__ or path in self.files():
151 151 if path in self._manifestdelta:
152 152 return self._manifestdelta[path], self._manifestdelta.flags(path)
153 153 node, flag = self._repo.manifest.find(self._changeset[0], path)
154 154 if not node:
155 155 raise error.LookupError(self._node, path,
156 156 _('not found in manifest'))
157 157
158 158 return node, flag
159 159
160 160 def filenode(self, path):
161 161 return self._fileinfo(path)[0]
162 162
163 163 def flags(self, path):
164 164 try:
165 165 return self._fileinfo(path)[1]
166 166 except error.LookupError:
167 167 return ''
168 168
169 169 def filectx(self, path, fileid=None, filelog=None):
170 170 """get a file context from this changeset"""
171 171 if fileid is None:
172 172 fileid = self.filenode(path)
173 173 return filectx(self._repo, path, fileid=fileid,
174 174 changectx=self, filelog=filelog)
175 175
176 176 def ancestor(self, c2):
177 177 """
178 178 return the ancestor context of self and c2
179 179 """
180 180 # deal with workingctxs
181 181 n2 = c2._node
182 182 if n2 is None:
183 183 n2 = c2._parents[0]._node
184 184 n = self._repo.changelog.ancestor(self._node, n2)
185 185 return changectx(self._repo, n)
186 186
187 187 def walk(self, match):
188 188 fset = set(match.files())
189 189 # for dirstate.walk, files=['.'] means "walk the whole tree".
190 190 # follow that here, too
191 191 fset.discard('.')
192 192 for fn in self:
193 193 for ffn in fset:
194 194 # match if the file is the exact name or a directory
195 195 if ffn == fn or fn.startswith("%s/" % ffn):
196 196 fset.remove(ffn)
197 197 break
198 198 if match(fn):
199 199 yield fn
200 200 for fn in sorted(fset):
201 201 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
202 202 yield fn
203 203
204 204 def sub(self, path):
205 205 return subrepo.subrepo(self, path)
206 206
207 207 def diff(self, ctx2=None, match=None, **opts):
208 208 """Returns a diff generator for the given contexts and matcher"""
209 209 if ctx2 is None:
210 210 ctx2 = self.p1()
211 211 if ctx2 is not None and not isinstance(ctx2, changectx):
212 212 ctx2 = self._repo[ctx2]
213 213 diffopts = patch.diffopts(self._repo.ui, opts)
214 214 return patch.diff(self._repo, ctx2.node(), self.node(),
215 215 match=match, opts=diffopts)
216 216
217 217 class filectx(object):
218 218 """A filecontext object makes access to data related to a particular
219 219 filerevision convenient."""
220 220 def __init__(self, repo, path, changeid=None, fileid=None,
221 221 filelog=None, changectx=None):
222 222 """changeid can be a changeset revision, node, or tag.
223 223 fileid can be a file revision or node."""
224 224 self._repo = repo
225 225 self._path = path
226 226
227 227 assert (changeid is not None
228 228 or fileid is not None
229 229 or changectx is not None), \
230 230 ("bad args: changeid=%r, fileid=%r, changectx=%r"
231 231 % (changeid, fileid, changectx))
232 232
233 233 if filelog:
234 234 self._filelog = filelog
235 235
236 236 if changeid is not None:
237 237 self._changeid = changeid
238 238 if changectx is not None:
239 239 self._changectx = changectx
240 240 if fileid is not None:
241 241 self._fileid = fileid
242 242
243 243 @propertycache
244 244 def _changectx(self):
245 245 return changectx(self._repo, self._changeid)
246 246
247 247 @propertycache
248 248 def _filelog(self):
249 249 return self._repo.file(self._path)
250 250
251 251 @propertycache
252 252 def _changeid(self):
253 253 if '_changectx' in self.__dict__:
254 254 return self._changectx.rev()
255 255 else:
256 256 return self._filelog.linkrev(self._filerev)
257 257
258 258 @propertycache
259 259 def _filenode(self):
260 260 if '_fileid' in self.__dict__:
261 261 return self._filelog.lookup(self._fileid)
262 262 else:
263 263 return self._changectx.filenode(self._path)
264 264
265 265 @propertycache
266 266 def _filerev(self):
267 267 return self._filelog.rev(self._filenode)
268 268
269 269 @propertycache
270 270 def _repopath(self):
271 271 return self._path
272 272
273 273 def __nonzero__(self):
274 274 try:
275 275 self._filenode
276 276 return True
277 277 except error.LookupError:
278 278 # file is missing
279 279 return False
280 280
281 281 def __str__(self):
282 282 return "%s@%s" % (self.path(), short(self.node()))
283 283
284 284 def __repr__(self):
285 285 return "<filectx %s>" % str(self)
286 286
287 287 def __hash__(self):
288 288 try:
289 289 return hash((self._path, self._filenode))
290 290 except AttributeError:
291 291 return id(self)
292 292
293 293 def __eq__(self, other):
294 294 try:
295 295 return (self._path == other._path
296 296 and self._filenode == other._filenode)
297 297 except AttributeError:
298 298 return False
299 299
300 300 def __ne__(self, other):
301 301 return not (self == other)
302 302
303 303 def filectx(self, fileid):
304 304 '''opens an arbitrary revision of the file without
305 305 opening a new filelog'''
306 306 return filectx(self._repo, self._path, fileid=fileid,
307 307 filelog=self._filelog)
308 308
309 309 def filerev(self):
310 310 return self._filerev
311 311 def filenode(self):
312 312 return self._filenode
313 313 def flags(self):
314 314 return self._changectx.flags(self._path)
315 315 def filelog(self):
316 316 return self._filelog
317 317
318 318 def rev(self):
319 319 if '_changectx' in self.__dict__:
320 320 return self._changectx.rev()
321 321 if '_changeid' in self.__dict__:
322 322 return self._changectx.rev()
323 323 return self._filelog.linkrev(self._filerev)
324 324
325 325 def linkrev(self):
326 326 return self._filelog.linkrev(self._filerev)
327 327 def node(self):
328 328 return self._changectx.node()
329 329 def hex(self):
330 330 return hex(self.node())
331 331 def user(self):
332 332 return self._changectx.user()
333 333 def date(self):
334 334 return self._changectx.date()
335 335 def files(self):
336 336 return self._changectx.files()
337 337 def description(self):
338 338 return self._changectx.description()
339 339 def branch(self):
340 340 return self._changectx.branch()
341 341 def extra(self):
342 342 return self._changectx.extra()
343 343 def manifest(self):
344 344 return self._changectx.manifest()
345 345 def changectx(self):
346 346 return self._changectx
347 347
348 348 def data(self):
349 349 return self._filelog.read(self._filenode)
350 350 def path(self):
351 351 return self._path
352 352 def size(self):
353 353 return self._filelog.size(self._filerev)
354 354
355 355 def cmp(self, fctx):
356 356 """compare with other file context
357 357
358 358 returns True if different than fctx.
359 359 """
360 360 if (fctx._filerev is None and self._repo._encodefilterpats
361 361 or self.size() == fctx.size()):
362 362 return self._filelog.cmp(self._filenode, fctx.data())
363 363
364 364 return True
365 365
366 366 def renamed(self):
367 367 """check if file was actually renamed in this changeset revision
368 368
369 369 If rename logged in file revision, we report copy for changeset only
370 370 if file revisions linkrev points back to the changeset in question
371 371 or both changeset parents contain different file revisions.
372 372 """
373 373
374 374 renamed = self._filelog.renamed(self._filenode)
375 375 if not renamed:
376 376 return renamed
377 377
378 378 if self.rev() == self.linkrev():
379 379 return renamed
380 380
381 381 name = self.path()
382 382 fnode = self._filenode
383 383 for p in self._changectx.parents():
384 384 try:
385 385 if fnode == p.filenode(name):
386 386 return None
387 387 except error.LookupError:
388 388 pass
389 389 return renamed
390 390
391 391 def parents(self):
392 392 p = self._path
393 393 fl = self._filelog
394 394 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
395 395
396 396 r = self._filelog.renamed(self._filenode)
397 397 if r:
398 398 pl[0] = (r[0], r[1], None)
399 399
400 400 return [filectx(self._repo, p, fileid=n, filelog=l)
401 401 for p, n, l in pl if n != nullid]
402 402
403 403 def children(self):
404 404 # hard for renames
405 405 c = self._filelog.children(self._filenode)
406 406 return [filectx(self._repo, self._path, fileid=x,
407 407 filelog=self._filelog) for x in c]
408 408
409 409 def annotate(self, follow=False, linenumber=None):
410 410 '''returns a list of tuples of (ctx, line) for each line
411 411 in the file, where ctx is the filectx of the node where
412 412 that line was last changed.
413 413 This returns tuples of ((ctx, linenumber), line) for each line,
414 414 if "linenumber" parameter is NOT "None".
415 415 In such tuples, linenumber means one at the first appearance
416 416 in the managed file.
417 417 To reduce annotation cost,
418 418 this returns fixed value(False is used) as linenumber,
419 419 if "linenumber" parameter is "False".'''
420 420
421 421 def decorate_compat(text, rev):
422 422 return ([rev] * len(text.splitlines()), text)
423 423
424 424 def without_linenumber(text, rev):
425 425 return ([(rev, False)] * len(text.splitlines()), text)
426 426
427 427 def with_linenumber(text, rev):
428 428 size = len(text.splitlines())
429 429 return ([(rev, i) for i in xrange(1, size + 1)], text)
430 430
431 431 decorate = (((linenumber is None) and decorate_compat) or
432 432 (linenumber and with_linenumber) or
433 433 without_linenumber)
434 434
435 435 def pair(parent, child):
436 436 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
437 437 child[0][b1:b2] = parent[0][a1:a2]
438 438 return child
439 439
440 440 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
441 441 def getctx(path, fileid):
442 442 log = path == self._path and self._filelog or getlog(path)
443 443 return filectx(self._repo, path, fileid=fileid, filelog=log)
444 444 getctx = util.lrucachefunc(getctx)
445 445
446 446 def parents(f):
447 447 # we want to reuse filectx objects as much as possible
448 448 p = f._path
449 449 if f._filerev is None: # working dir
450 450 pl = [(n.path(), n.filerev()) for n in f.parents()]
451 451 else:
452 452 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
453 453
454 454 if follow:
455 455 r = f.renamed()
456 456 if r:
457 457 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
458 458
459 459 return [getctx(p, n) for p, n in pl if n != nullrev]
460 460
461 461 # use linkrev to find the first changeset where self appeared
462 462 if self.rev() != self.linkrev():
463 463 base = self.filectx(self.filerev())
464 464 else:
465 465 base = self
466 466
467 467 # find all ancestors
468 468 needed = {base: 1}
469 469 visit = [base]
470 470 files = [base._path]
471 471 while visit:
472 472 f = visit.pop(0)
473 473 for p in parents(f):
474 474 if p not in needed:
475 475 needed[p] = 1
476 476 visit.append(p)
477 477 if p._path not in files:
478 478 files.append(p._path)
479 479 else:
480 480 # count how many times we'll use this
481 481 needed[p] += 1
482 482
483 483 # sort by revision (per file) which is a topological order
484 484 visit = []
485 485 for f in files:
486 486 visit.extend(n for n in needed if n._path == f)
487 487
488 488 hist = {}
489 489 for f in sorted(visit, key=lambda x: x.rev()):
490 490 curr = decorate(f.data(), f)
491 491 for p in parents(f):
492 492 curr = pair(hist[p], curr)
493 493 # trim the history of unneeded revs
494 494 needed[p] -= 1
495 495 if not needed[p]:
496 496 del hist[p]
497 497 hist[f] = curr
498 498
499 499 return zip(hist[f][0], hist[f][1].splitlines(True))
500 500
501 501 def ancestor(self, fc2, actx=None):
502 502 """
503 503 find the common ancestor file context, if any, of self, and fc2
504 504
505 505 If actx is given, it must be the changectx of the common ancestor
506 506 of self's and fc2's respective changesets.
507 507 """
508 508
509 509 if actx is None:
510 510 actx = self.changectx().ancestor(fc2.changectx())
511 511
512 512 # the trivial case: changesets are unrelated, files must be too
513 513 if not actx:
514 514 return None
515 515
516 516 # the easy case: no (relevant) renames
517 517 if fc2.path() == self.path() and self.path() in actx:
518 518 return actx[self.path()]
519 519 acache = {}
520 520
521 521 # prime the ancestor cache for the working directory
522 522 for c in (self, fc2):
523 523 if c._filerev is None:
524 524 pl = [(n.path(), n.filenode()) for n in c.parents()]
525 525 acache[(c._path, None)] = pl
526 526
527 527 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
528 528 def parents(vertex):
529 529 if vertex in acache:
530 530 return acache[vertex]
531 531 f, n = vertex
532 532 if f not in flcache:
533 533 flcache[f] = self._repo.file(f)
534 534 fl = flcache[f]
535 535 pl = [(f, p) for p in fl.parents(n) if p != nullid]
536 536 re = fl.renamed(n)
537 537 if re:
538 538 pl.append(re)
539 539 acache[vertex] = pl
540 540 return pl
541 541
542 542 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
543 543 v = ancestor.ancestor(a, b, parents)
544 544 if v:
545 545 f, n = v
546 546 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
547 547
548 548 return None
549 549
550 550 def ancestors(self):
551 551 seen = set(str(self))
552 552 visit = [self]
553 553 while visit:
554 554 for parent in visit.pop(0).parents():
555 555 s = str(parent)
556 556 if s not in seen:
557 557 visit.append(parent)
558 558 seen.add(s)
559 559 yield parent
560 560
561 561 class workingctx(changectx):
562 562 """A workingctx object makes access to data related to
563 563 the current working directory convenient.
564 564 date - any valid date string or (unixtime, offset), or None.
565 565 user - username string, or None.
566 566 extra - a dictionary of extra values, or None.
567 567 changes - a list of file lists as returned by localrepo.status()
568 568 or None to use the repository status.
569 569 """
570 570 def __init__(self, repo, text="", user=None, date=None, extra=None,
571 571 changes=None):
572 572 self._repo = repo
573 573 self._rev = None
574 574 self._node = None
575 575 self._text = text
576 576 if date:
577 577 self._date = util.parsedate(date)
578 578 if user:
579 579 self._user = user
580 580 if changes:
581 581 self._status = list(changes[:4])
582 582 self._unknown = changes[4]
583 583 self._ignored = changes[5]
584 584 self._clean = changes[6]
585 585 else:
586 586 self._unknown = None
587 587 self._ignored = None
588 588 self._clean = None
589 589
590 590 self._extra = {}
591 591 if extra:
592 592 self._extra = extra.copy()
593 593 if 'branch' not in self._extra:
594 594 try:
595 595 branch = encoding.fromlocal(self._repo.dirstate.branch())
596 596 except UnicodeDecodeError:
597 597 raise util.Abort(_('branch name not in UTF-8!'))
598 598 self._extra['branch'] = branch
599 599 if self._extra['branch'] == '':
600 600 self._extra['branch'] = 'default'
601 601
602 602 def __str__(self):
603 603 return str(self._parents[0]) + "+"
604 604
605 605 def __repr__(self):
606 606 return "<workingctx %s>" % str(self)
607 607
608 608 def __nonzero__(self):
609 609 return True
610 610
611 611 def __contains__(self, key):
612 612 return self._repo.dirstate[key] not in "?r"
613 613
614 614 @propertycache
615 615 def _manifest(self):
616 616 """generate a manifest corresponding to the working directory"""
617 617
618 618 if self._unknown is None:
619 619 self.status(unknown=True)
620 620
621 621 man = self._parents[0].manifest().copy()
622 622 copied = self._repo.dirstate.copies()
623 623 if len(self._parents) > 1:
624 624 man2 = self.p2().manifest()
625 625 def getman(f):
626 626 if f in man:
627 627 return man
628 628 return man2
629 629 else:
630 630 getman = lambda f: man
631 631 def cf(f):
632 632 f = copied.get(f, f)
633 633 return getman(f).flags(f)
634 634 ff = self._repo.dirstate.flagfunc(cf)
635 635 modified, added, removed, deleted = self._status
636 636 unknown = self._unknown
637 637 for i, l in (("a", added), ("m", modified), ("u", unknown)):
638 638 for f in l:
639 639 orig = copied.get(f, f)
640 640 man[f] = getman(orig).get(orig, nullid) + i
641 641 try:
642 642 man.set(f, ff(f))
643 643 except OSError:
644 644 pass
645 645
646 646 for f in deleted + removed:
647 647 if f in man:
648 648 del man[f]
649 649
650 650 return man
651 651
652 652 @propertycache
653 653 def _status(self):
654 654 return self._repo.status()[:4]
655 655
656 656 @propertycache
657 657 def _user(self):
658 658 return self._repo.ui.username()
659 659
660 660 @propertycache
661 661 def _date(self):
662 662 return util.makedate()
663 663
664 664 @propertycache
665 665 def _parents(self):
666 666 p = self._repo.dirstate.parents()
667 667 if p[1] == nullid:
668 668 p = p[:-1]
669 669 self._parents = [changectx(self._repo, x) for x in p]
670 670 return self._parents
671 671
672 672 def status(self, ignored=False, clean=False, unknown=False):
673 673 """Explicit status query
674 674 Unless this method is used to query the working copy status, the
675 675 _status property will implicitly read the status using its default
676 676 arguments."""
677 677 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
678 678 self._unknown = self._ignored = self._clean = None
679 679 if unknown:
680 680 self._unknown = stat[4]
681 681 if ignored:
682 682 self._ignored = stat[5]
683 683 if clean:
684 684 self._clean = stat[6]
685 685 self._status = stat[:4]
686 686 return stat
687 687
688 688 def manifest(self):
689 689 return self._manifest
690 690 def user(self):
691 691 return self._user or self._repo.ui.username()
692 692 def date(self):
693 693 return self._date
694 694 def description(self):
695 695 return self._text
696 696 def files(self):
697 697 return sorted(self._status[0] + self._status[1] + self._status[2])
698 698
699 699 def modified(self):
700 700 return self._status[0]
701 701 def added(self):
702 702 return self._status[1]
703 703 def removed(self):
704 704 return self._status[2]
705 705 def deleted(self):
706 706 return self._status[3]
707 707 def unknown(self):
708 708 assert self._unknown is not None # must call status first
709 709 return self._unknown
710 710 def ignored(self):
711 711 assert self._ignored is not None # must call status first
712 712 return self._ignored
713 713 def clean(self):
714 714 assert self._clean is not None # must call status first
715 715 return self._clean
716 716 def branch(self):
717 717 return encoding.tolocal(self._extra['branch'])
718 718 def extra(self):
719 719 return self._extra
720 720
721 721 def tags(self):
722 722 t = []
723 723 [t.extend(p.tags()) for p in self.parents()]
724 724 return t
725 725
726 726 def children(self):
727 727 return []
728 728
729 729 def flags(self, path):
730 730 if '_manifest' in self.__dict__:
731 731 try:
732 732 return self._manifest.flags(path)
733 733 except KeyError:
734 734 return ''
735 735
736 736 orig = self._repo.dirstate.copies().get(path, path)
737 737
738 738 def findflag(ctx):
739 739 mnode = ctx.changeset()[0]
740 740 node, flag = self._repo.manifest.find(mnode, orig)
741 741 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
742 742 try:
743 743 return ff(path)
744 744 except OSError:
745 745 pass
746 746
747 747 flag = findflag(self._parents[0])
748 748 if flag is None and len(self.parents()) > 1:
749 749 flag = findflag(self._parents[1])
750 750 if flag is None or self._repo.dirstate[path] == 'r':
751 751 return ''
752 752 return flag
753 753
754 754 def filectx(self, path, filelog=None):
755 755 """get a file context from the working directory"""
756 756 return workingfilectx(self._repo, path, workingctx=self,
757 757 filelog=filelog)
758 758
759 759 def ancestor(self, c2):
760 760 """return the ancestor context of self and c2"""
761 761 return self._parents[0].ancestor(c2) # punt on two parents for now
762 762
763 763 def walk(self, match):
764 764 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
765 765 True, False))
766 766
767 767 def dirty(self, missing=False):
768 768 "check whether a working directory is modified"
769 769 # check subrepos first
770 770 for s in self.substate:
771 771 if self.sub(s).dirty():
772 772 return True
773 773 # check current working dir
774 774 return (self.p2() or self.branch() != self.p1().branch() or
775 775 self.modified() or self.added() or self.removed() or
776 776 (missing and self.deleted()))
777 777
778 778 def add(self, list, prefix=""):
779 779 join = lambda f: os.path.join(prefix, f)
780 780 wlock = self._repo.wlock()
781 781 ui, ds = self._repo.ui, self._repo.dirstate
782 782 try:
783 783 rejected = []
784 784 for f in list:
785 785 p = self._repo.wjoin(f)
786 786 try:
787 787 st = os.lstat(p)
788 788 except:
789 789 ui.warn(_("%s does not exist!\n") % join(f))
790 790 rejected.append(f)
791 791 continue
792 792 if st.st_size > 10000000:
793 793 ui.warn(_("%s: up to %d MB of RAM may be required "
794 794 "to manage this file\n"
795 795 "(use 'hg revert %s' to cancel the "
796 796 "pending addition)\n")
797 797 % (f, 3 * st.st_size // 1000000, join(f)))
798 798 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
799 799 ui.warn(_("%s not added: only files and symlinks "
800 800 "supported currently\n") % join(f))
801 801 rejected.append(p)
802 802 elif ds[f] in 'amn':
803 803 ui.warn(_("%s already tracked!\n") % join(f))
804 804 elif ds[f] == 'r':
805 805 ds.normallookup(f)
806 806 else:
807 807 ds.add(f)
808 808 return rejected
809 809 finally:
810 810 wlock.release()
811 811
812 812 def forget(self, list):
813 813 wlock = self._repo.wlock()
814 814 try:
815 815 for f in list:
816 816 if self._repo.dirstate[f] != 'a':
817 817 self._repo.ui.warn(_("%s not added!\n") % f)
818 818 else:
819 819 self._repo.dirstate.forget(f)
820 820 finally:
821 821 wlock.release()
822 822
823 823 def ancestors(self):
824 824 for a in self._repo.changelog.ancestors(
825 825 *[p.rev() for p in self._parents]):
826 826 yield changectx(self._repo, a)
827 827
828 828 def remove(self, list, unlink=False):
829 829 if unlink:
830 830 for f in list:
831 831 try:
832 util.unlink(self._repo.wjoin(f))
832 util.unlinkpath(self._repo.wjoin(f))
833 833 except OSError, inst:
834 834 if inst.errno != errno.ENOENT:
835 835 raise
836 836 wlock = self._repo.wlock()
837 837 try:
838 838 for f in list:
839 839 if unlink and os.path.lexists(self._repo.wjoin(f)):
840 840 self._repo.ui.warn(_("%s still exists!\n") % f)
841 841 elif self._repo.dirstate[f] == 'a':
842 842 self._repo.dirstate.forget(f)
843 843 elif f not in self._repo.dirstate:
844 844 self._repo.ui.warn(_("%s not tracked!\n") % f)
845 845 else:
846 846 self._repo.dirstate.remove(f)
847 847 finally:
848 848 wlock.release()
849 849
850 850 def undelete(self, list):
851 851 pctxs = self.parents()
852 852 wlock = self._repo.wlock()
853 853 try:
854 854 for f in list:
855 855 if self._repo.dirstate[f] != 'r':
856 856 self._repo.ui.warn(_("%s not removed!\n") % f)
857 857 else:
858 858 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
859 859 t = fctx.data()
860 860 self._repo.wwrite(f, t, fctx.flags())
861 861 self._repo.dirstate.normal(f)
862 862 finally:
863 863 wlock.release()
864 864
865 865 def copy(self, source, dest):
866 866 p = self._repo.wjoin(dest)
867 867 if not os.path.lexists(p):
868 868 self._repo.ui.warn(_("%s does not exist!\n") % dest)
869 869 elif not (os.path.isfile(p) or os.path.islink(p)):
870 870 self._repo.ui.warn(_("copy failed: %s is not a file or a "
871 871 "symbolic link\n") % dest)
872 872 else:
873 873 wlock = self._repo.wlock()
874 874 try:
875 875 if self._repo.dirstate[dest] in '?r':
876 876 self._repo.dirstate.add(dest)
877 877 self._repo.dirstate.copy(source, dest)
878 878 finally:
879 879 wlock.release()
880 880
881 881 class workingfilectx(filectx):
882 882 """A workingfilectx object makes access to data related to a particular
883 883 file in the working directory convenient."""
884 884 def __init__(self, repo, path, filelog=None, workingctx=None):
885 885 """changeid can be a changeset revision, node, or tag.
886 886 fileid can be a file revision or node."""
887 887 self._repo = repo
888 888 self._path = path
889 889 self._changeid = None
890 890 self._filerev = self._filenode = None
891 891
892 892 if filelog:
893 893 self._filelog = filelog
894 894 if workingctx:
895 895 self._changectx = workingctx
896 896
897 897 @propertycache
898 898 def _changectx(self):
899 899 return workingctx(self._repo)
900 900
901 901 def __nonzero__(self):
902 902 return True
903 903
904 904 def __str__(self):
905 905 return "%s@%s" % (self.path(), self._changectx)
906 906
907 907 def __repr__(self):
908 908 return "<workingfilectx %s>" % str(self)
909 909
910 910 def data(self):
911 911 return self._repo.wread(self._path)
912 912 def renamed(self):
913 913 rp = self._repo.dirstate.copied(self._path)
914 914 if not rp:
915 915 return None
916 916 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
917 917
918 918 def parents(self):
919 919 '''return parent filectxs, following copies if necessary'''
920 920 def filenode(ctx, path):
921 921 return ctx._manifest.get(path, nullid)
922 922
923 923 path = self._path
924 924 fl = self._filelog
925 925 pcl = self._changectx._parents
926 926 renamed = self.renamed()
927 927
928 928 if renamed:
929 929 pl = [renamed + (None,)]
930 930 else:
931 931 pl = [(path, filenode(pcl[0], path), fl)]
932 932
933 933 for pc in pcl[1:]:
934 934 pl.append((path, filenode(pc, path), fl))
935 935
936 936 return [filectx(self._repo, p, fileid=n, filelog=l)
937 937 for p, n, l in pl if n != nullid]
938 938
939 939 def children(self):
940 940 return []
941 941
942 942 def size(self):
943 943 return os.lstat(self._repo.wjoin(self._path)).st_size
944 944 def date(self):
945 945 t, tz = self._changectx.date()
946 946 try:
947 947 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
948 948 except OSError, err:
949 949 if err.errno != errno.ENOENT:
950 950 raise
951 951 return (t, tz)
952 952
953 953 def cmp(self, fctx):
954 954 """compare with other file context
955 955
956 956 returns True if different than fctx.
957 957 """
958 958 # fctx should be a filectx (not a wfctx)
959 959 # invert comparison to reuse the same code path
960 960 return fctx.cmp(self)
961 961
962 962 class memctx(object):
963 963 """Use memctx to perform in-memory commits via localrepo.commitctx().
964 964
965 965 Revision information is supplied at initialization time while
966 966 related files data and is made available through a callback
967 967 mechanism. 'repo' is the current localrepo, 'parents' is a
968 968 sequence of two parent revisions identifiers (pass None for every
969 969 missing parent), 'text' is the commit message and 'files' lists
970 970 names of files touched by the revision (normalized and relative to
971 971 repository root).
972 972
973 973 filectxfn(repo, memctx, path) is a callable receiving the
974 974 repository, the current memctx object and the normalized path of
975 975 requested file, relative to repository root. It is fired by the
976 976 commit function for every file in 'files', but calls order is
977 977 undefined. If the file is available in the revision being
978 978 committed (updated or added), filectxfn returns a memfilectx
979 979 object. If the file was removed, filectxfn raises an
980 980 IOError. Moved files are represented by marking the source file
981 981 removed and the new file added with copy information (see
982 982 memfilectx).
983 983
984 984 user receives the committer name and defaults to current
985 985 repository username, date is the commit date in any format
986 986 supported by util.parsedate() and defaults to current date, extra
987 987 is a dictionary of metadata or is left empty.
988 988 """
989 989 def __init__(self, repo, parents, text, files, filectxfn, user=None,
990 990 date=None, extra=None):
991 991 self._repo = repo
992 992 self._rev = None
993 993 self._node = None
994 994 self._text = text
995 995 self._date = date and util.parsedate(date) or util.makedate()
996 996 self._user = user
997 997 parents = [(p or nullid) for p in parents]
998 998 p1, p2 = parents
999 999 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1000 1000 files = sorted(set(files))
1001 1001 self._status = [files, [], [], [], []]
1002 1002 self._filectxfn = filectxfn
1003 1003
1004 1004 self._extra = extra and extra.copy() or {}
1005 1005 if 'branch' not in self._extra:
1006 1006 self._extra['branch'] = 'default'
1007 1007 elif self._extra.get('branch') == '':
1008 1008 self._extra['branch'] = 'default'
1009 1009
1010 1010 def __str__(self):
1011 1011 return str(self._parents[0]) + "+"
1012 1012
1013 1013 def __int__(self):
1014 1014 return self._rev
1015 1015
1016 1016 def __nonzero__(self):
1017 1017 return True
1018 1018
1019 1019 def __getitem__(self, key):
1020 1020 return self.filectx(key)
1021 1021
1022 1022 def p1(self):
1023 1023 return self._parents[0]
1024 1024 def p2(self):
1025 1025 return self._parents[1]
1026 1026
1027 1027 def user(self):
1028 1028 return self._user or self._repo.ui.username()
1029 1029 def date(self):
1030 1030 return self._date
1031 1031 def description(self):
1032 1032 return self._text
1033 1033 def files(self):
1034 1034 return self.modified()
1035 1035 def modified(self):
1036 1036 return self._status[0]
1037 1037 def added(self):
1038 1038 return self._status[1]
1039 1039 def removed(self):
1040 1040 return self._status[2]
1041 1041 def deleted(self):
1042 1042 return self._status[3]
1043 1043 def unknown(self):
1044 1044 return self._status[4]
1045 1045 def ignored(self):
1046 1046 return self._status[5]
1047 1047 def clean(self):
1048 1048 return self._status[6]
1049 1049 def branch(self):
1050 1050 return encoding.tolocal(self._extra['branch'])
1051 1051 def extra(self):
1052 1052 return self._extra
1053 1053 def flags(self, f):
1054 1054 return self[f].flags()
1055 1055
1056 1056 def parents(self):
1057 1057 """return contexts for each parent changeset"""
1058 1058 return self._parents
1059 1059
1060 1060 def filectx(self, path, filelog=None):
1061 1061 """get a file context from the working directory"""
1062 1062 return self._filectxfn(self._repo, self, path)
1063 1063
1064 1064 def commit(self):
1065 1065 """commit context to the repo"""
1066 1066 return self._repo.commitctx(self)
1067 1067
1068 1068 class memfilectx(object):
1069 1069 """memfilectx represents an in-memory file to commit.
1070 1070
1071 1071 See memctx for more details.
1072 1072 """
1073 1073 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1074 1074 """
1075 1075 path is the normalized file path relative to repository root.
1076 1076 data is the file content as a string.
1077 1077 islink is True if the file is a symbolic link.
1078 1078 isexec is True if the file is executable.
1079 1079 copied is the source file path if current file was copied in the
1080 1080 revision being committed, or None."""
1081 1081 self._path = path
1082 1082 self._data = data
1083 1083 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1084 1084 self._copied = None
1085 1085 if copied:
1086 1086 self._copied = (copied, nullid)
1087 1087
1088 1088 def __nonzero__(self):
1089 1089 return True
1090 1090 def __str__(self):
1091 1091 return "%s@%s" % (self.path(), self._changectx)
1092 1092 def path(self):
1093 1093 return self._path
1094 1094 def data(self):
1095 1095 return self._data
1096 1096 def flags(self):
1097 1097 return self._flags
1098 1098 def isexec(self):
1099 1099 return 'x' in self._flags
1100 1100 def islink(self):
1101 1101 return 'l' in self._flags
1102 1102 def renamed(self):
1103 1103 return self._copied
@@ -1,549 +1,549 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 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 nullid, nullrev, hex, bin
9 9 from i18n import _
10 10 import util, filemerge, copies, subrepo
11 11 import errno, os, shutil
12 12
13 13 class mergestate(object):
14 14 '''track 3-way merge state of individual files'''
15 15 def __init__(self, repo):
16 16 self._repo = repo
17 17 self._dirty = False
18 18 self._read()
19 19 def reset(self, node=None):
20 20 self._state = {}
21 21 if node:
22 22 self._local = node
23 23 shutil.rmtree(self._repo.join("merge"), True)
24 24 self._dirty = False
25 25 def _read(self):
26 26 self._state = {}
27 27 try:
28 28 f = self._repo.opener("merge/state")
29 29 for i, l in enumerate(f):
30 30 if i == 0:
31 31 self._local = bin(l[:-1])
32 32 else:
33 33 bits = l[:-1].split("\0")
34 34 self._state[bits[0]] = bits[1:]
35 35 except IOError, err:
36 36 if err.errno != errno.ENOENT:
37 37 raise
38 38 self._dirty = False
39 39 def commit(self):
40 40 if self._dirty:
41 41 f = self._repo.opener("merge/state", "w")
42 42 f.write(hex(self._local) + "\n")
43 43 for d, v in self._state.iteritems():
44 44 f.write("\0".join([d] + v) + "\n")
45 45 self._dirty = False
46 46 def add(self, fcl, fco, fca, fd, flags):
47 47 hash = util.sha1(fcl.path()).hexdigest()
48 48 self._repo.opener("merge/" + hash, "w").write(fcl.data())
49 49 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
50 50 hex(fca.filenode()), fco.path(), flags]
51 51 self._dirty = True
52 52 def __contains__(self, dfile):
53 53 return dfile in self._state
54 54 def __getitem__(self, dfile):
55 55 return self._state[dfile][0]
56 56 def __iter__(self):
57 57 l = self._state.keys()
58 58 l.sort()
59 59 for f in l:
60 60 yield f
61 61 def mark(self, dfile, state):
62 62 self._state[dfile][0] = state
63 63 self._dirty = True
64 64 def resolve(self, dfile, wctx, octx):
65 65 if self[dfile] == 'r':
66 66 return 0
67 67 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
68 68 f = self._repo.opener("merge/" + hash)
69 69 self._repo.wwrite(dfile, f.read(), flags)
70 70 fcd = wctx[dfile]
71 71 fco = octx[ofile]
72 72 fca = self._repo.filectx(afile, fileid=anode)
73 73 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
74 74 if not r:
75 75 self.mark(dfile, 'r')
76 76 return r
77 77
78 78 def _checkunknown(wctx, mctx):
79 79 "check for collisions between unknown files and files in mctx"
80 80 for f in wctx.unknown():
81 81 if f in mctx and mctx[f].cmp(wctx[f]):
82 82 raise util.Abort(_("untracked file in working directory differs"
83 83 " from file in requested revision: '%s'") % f)
84 84
85 85 def _checkcollision(mctx):
86 86 "check for case folding collisions in the destination context"
87 87 folded = {}
88 88 for fn in mctx:
89 89 fold = fn.lower()
90 90 if fold in folded:
91 91 raise util.Abort(_("case-folding collision between %s and %s")
92 92 % (fn, folded[fold]))
93 93 folded[fold] = fn
94 94
95 95 def _forgetremoved(wctx, mctx, branchmerge):
96 96 """
97 97 Forget removed files
98 98
99 99 If we're jumping between revisions (as opposed to merging), and if
100 100 neither the working directory nor the target rev has the file,
101 101 then we need to remove it from the dirstate, to prevent the
102 102 dirstate from listing the file when it is no longer in the
103 103 manifest.
104 104
105 105 If we're merging, and the other revision has removed a file
106 106 that is not present in the working directory, we need to mark it
107 107 as removed.
108 108 """
109 109
110 110 action = []
111 111 state = branchmerge and 'r' or 'f'
112 112 for f in wctx.deleted():
113 113 if f not in mctx:
114 114 action.append((f, state))
115 115
116 116 if not branchmerge:
117 117 for f in wctx.removed():
118 118 if f not in mctx:
119 119 action.append((f, "f"))
120 120
121 121 return action
122 122
123 123 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
124 124 """
125 125 Merge p1 and p2 with ancestor pa and generate merge action list
126 126
127 127 overwrite = whether we clobber working files
128 128 partial = function to filter file lists
129 129 """
130 130
131 131 def fmerge(f, f2, fa):
132 132 """merge flags"""
133 133 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
134 134 if m == n: # flags agree
135 135 return m # unchanged
136 136 if m and n and not a: # flags set, don't agree, differ from parent
137 137 r = repo.ui.promptchoice(
138 138 _(" conflicting flags for %s\n"
139 139 "(n)one, e(x)ec or sym(l)ink?") % f,
140 140 (_("&None"), _("E&xec"), _("Sym&link")), 0)
141 141 if r == 1:
142 142 return "x" # Exec
143 143 if r == 2:
144 144 return "l" # Symlink
145 145 return ""
146 146 if m and m != a: # changed from a to m
147 147 return m
148 148 if n and n != a: # changed from a to n
149 149 return n
150 150 return '' # flag was cleared
151 151
152 152 def act(msg, m, f, *args):
153 153 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
154 154 action.append((f, m) + args)
155 155
156 156 action, copy = [], {}
157 157
158 158 if overwrite:
159 159 pa = p1
160 160 elif pa == p2: # backwards
161 161 pa = p1.p1()
162 162 elif pa and repo.ui.configbool("merge", "followcopies", True):
163 163 dirs = repo.ui.configbool("merge", "followdirs", True)
164 164 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
165 165 for of, fl in diverge.iteritems():
166 166 act("divergent renames", "dr", of, fl)
167 167
168 168 repo.ui.note(_("resolving manifests\n"))
169 169 repo.ui.debug(" overwrite %s partial %s\n" % (overwrite, bool(partial)))
170 170 repo.ui.debug(" ancestor %s local %s remote %s\n" % (pa, p1, p2))
171 171
172 172 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
173 173 copied = set(copy.values())
174 174
175 175 if '.hgsubstate' in m1:
176 176 # check whether sub state is modified
177 177 for s in p1.substate:
178 178 if p1.sub(s).dirty():
179 179 m1['.hgsubstate'] += "+"
180 180 break
181 181
182 182 # Compare manifests
183 183 for f, n in m1.iteritems():
184 184 if partial and not partial(f):
185 185 continue
186 186 if f in m2:
187 187 rflags = fmerge(f, f, f)
188 188 a = ma.get(f, nullid)
189 189 if n == m2[f] or m2[f] == a: # same or local newer
190 190 # is file locally modified or flags need changing?
191 191 # dirstate flags may need to be made current
192 192 if m1.flags(f) != rflags or n[20:]:
193 193 act("update permissions", "e", f, rflags)
194 194 elif n == a: # remote newer
195 195 act("remote is newer", "g", f, rflags)
196 196 else: # both changed
197 197 act("versions differ", "m", f, f, f, rflags, False)
198 198 elif f in copied: # files we'll deal with on m2 side
199 199 pass
200 200 elif f in copy:
201 201 f2 = copy[f]
202 202 if f2 not in m2: # directory rename
203 203 act("remote renamed directory to " + f2, "d",
204 204 f, None, f2, m1.flags(f))
205 205 else: # case 2 A,B/B/B or case 4,21 A/B/B
206 206 act("local copied/moved to " + f2, "m",
207 207 f, f2, f, fmerge(f, f2, f2), False)
208 208 elif f in ma: # clean, a different, no remote
209 209 if n != ma[f]:
210 210 if repo.ui.promptchoice(
211 211 _(" local changed %s which remote deleted\n"
212 212 "use (c)hanged version or (d)elete?") % f,
213 213 (_("&Changed"), _("&Delete")), 0):
214 214 act("prompt delete", "r", f)
215 215 else:
216 216 act("prompt keep", "a", f)
217 217 elif n[20:] == "a": # added, no remote
218 218 act("remote deleted", "f", f)
219 219 elif n[20:] != "u":
220 220 act("other deleted", "r", f)
221 221
222 222 for f, n in m2.iteritems():
223 223 if partial and not partial(f):
224 224 continue
225 225 if f in m1 or f in copied: # files already visited
226 226 continue
227 227 if f in copy:
228 228 f2 = copy[f]
229 229 if f2 not in m1: # directory rename
230 230 act("local renamed directory to " + f2, "d",
231 231 None, f, f2, m2.flags(f))
232 232 elif f2 in m2: # rename case 1, A/A,B/A
233 233 act("remote copied to " + f, "m",
234 234 f2, f, f, fmerge(f2, f, f2), False)
235 235 else: # case 3,20 A/B/A
236 236 act("remote moved to " + f, "m",
237 237 f2, f, f, fmerge(f2, f, f2), True)
238 238 elif f not in ma:
239 239 act("remote created", "g", f, m2.flags(f))
240 240 elif n != ma[f]:
241 241 if repo.ui.promptchoice(
242 242 _("remote changed %s which local deleted\n"
243 243 "use (c)hanged version or leave (d)eleted?") % f,
244 244 (_("&Changed"), _("&Deleted")), 0) == 0:
245 245 act("prompt recreating", "g", f, m2.flags(f))
246 246
247 247 return action
248 248
249 249 def actionkey(a):
250 250 return a[1] == 'r' and -1 or 0, a
251 251
252 252 def applyupdates(repo, action, wctx, mctx, actx):
253 253 """apply the merge action list to the working directory
254 254
255 255 wctx is the working copy context
256 256 mctx is the context to be merged into the working copy
257 257 actx is the context of the common ancestor
258 258
259 259 Return a tuple of counts (updated, merged, removed, unresolved) that
260 260 describes how many files were affected by the update.
261 261 """
262 262
263 263 updated, merged, removed, unresolved = 0, 0, 0, 0
264 264 ms = mergestate(repo)
265 265 ms.reset(wctx.parents()[0].node())
266 266 moves = []
267 267 action.sort(key=actionkey)
268 268 substate = wctx.substate # prime
269 269
270 270 # prescan for merges
271 271 u = repo.ui
272 272 for a in action:
273 273 f, m = a[:2]
274 274 if m == 'm': # merge
275 275 f2, fd, flags, move = a[2:]
276 276 if f == '.hgsubstate': # merged internally
277 277 continue
278 278 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
279 279 fcl = wctx[f]
280 280 fco = mctx[f2]
281 281 if mctx == actx: # backwards, use working dir parent as ancestor
282 282 if fcl.parents():
283 283 fca = fcl.parents()[0]
284 284 else:
285 285 fca = repo.filectx(f, fileid=nullrev)
286 286 else:
287 287 fca = fcl.ancestor(fco, actx)
288 288 if not fca:
289 289 fca = repo.filectx(f, fileid=nullrev)
290 290 ms.add(fcl, fco, fca, fd, flags)
291 291 if f != fd and move:
292 292 moves.append(f)
293 293
294 294 # remove renamed files after safely stored
295 295 for f in moves:
296 296 if os.path.lexists(repo.wjoin(f)):
297 297 repo.ui.debug("removing %s\n" % f)
298 298 os.unlink(repo.wjoin(f))
299 299
300 300 audit_path = util.path_auditor(repo.root)
301 301
302 302 numupdates = len(action)
303 303 for i, a in enumerate(action):
304 304 f, m = a[:2]
305 305 u.progress(_('updating'), i + 1, item=f, total=numupdates,
306 306 unit=_('files'))
307 307 if f and f[0] == "/":
308 308 continue
309 309 if m == "r": # remove
310 310 repo.ui.note(_("removing %s\n") % f)
311 311 audit_path(f)
312 312 if f == '.hgsubstate': # subrepo states need updating
313 313 subrepo.submerge(repo, wctx, mctx, wctx)
314 314 try:
315 util.unlink(repo.wjoin(f))
315 util.unlinkpath(repo.wjoin(f))
316 316 except OSError, inst:
317 317 if inst.errno != errno.ENOENT:
318 318 repo.ui.warn(_("update failed to remove %s: %s!\n") %
319 319 (f, inst.strerror))
320 320 removed += 1
321 321 elif m == "m": # merge
322 322 if f == '.hgsubstate': # subrepo states need updating
323 323 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx))
324 324 continue
325 325 f2, fd, flags, move = a[2:]
326 326 r = ms.resolve(fd, wctx, mctx)
327 327 if r is not None and r > 0:
328 328 unresolved += 1
329 329 else:
330 330 if r is None:
331 331 updated += 1
332 332 else:
333 333 merged += 1
334 334 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
335 335 if f != fd and move and os.path.lexists(repo.wjoin(f)):
336 336 repo.ui.debug("removing %s\n" % f)
337 337 os.unlink(repo.wjoin(f))
338 338 elif m == "g": # get
339 339 flags = a[2]
340 340 repo.ui.note(_("getting %s\n") % f)
341 341 t = mctx.filectx(f).data()
342 342 repo.wwrite(f, t, flags)
343 343 t = None
344 344 updated += 1
345 345 if f == '.hgsubstate': # subrepo states need updating
346 346 subrepo.submerge(repo, wctx, mctx, wctx)
347 347 elif m == "d": # directory rename
348 348 f2, fd, flags = a[2:]
349 349 if f:
350 350 repo.ui.note(_("moving %s to %s\n") % (f, fd))
351 351 t = wctx.filectx(f).data()
352 352 repo.wwrite(fd, t, flags)
353 util.unlink(repo.wjoin(f))
353 util.unlinkpath(repo.wjoin(f))
354 354 if f2:
355 355 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
356 356 t = mctx.filectx(f2).data()
357 357 repo.wwrite(fd, t, flags)
358 358 updated += 1
359 359 elif m == "dr": # divergent renames
360 360 fl = a[2]
361 361 repo.ui.warn(_("note: possible conflict - %s was renamed "
362 362 "multiple times to:\n") % f)
363 363 for nf in fl:
364 364 repo.ui.warn(" %s\n" % nf)
365 365 elif m == "e": # exec
366 366 flags = a[2]
367 367 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
368 368 ms.commit()
369 369 u.progress(_('updating'), None, total=numupdates, unit=_('files'))
370 370
371 371 return updated, merged, removed, unresolved
372 372
373 373 def recordupdates(repo, action, branchmerge):
374 374 "record merge actions to the dirstate"
375 375
376 376 for a in action:
377 377 f, m = a[:2]
378 378 if m == "r": # remove
379 379 if branchmerge:
380 380 repo.dirstate.remove(f)
381 381 else:
382 382 repo.dirstate.forget(f)
383 383 elif m == "a": # re-add
384 384 if not branchmerge:
385 385 repo.dirstate.add(f)
386 386 elif m == "f": # forget
387 387 repo.dirstate.forget(f)
388 388 elif m == "e": # exec change
389 389 repo.dirstate.normallookup(f)
390 390 elif m == "g": # get
391 391 if branchmerge:
392 392 repo.dirstate.otherparent(f)
393 393 else:
394 394 repo.dirstate.normal(f)
395 395 elif m == "m": # merge
396 396 f2, fd, flag, move = a[2:]
397 397 if branchmerge:
398 398 # We've done a branch merge, mark this file as merged
399 399 # so that we properly record the merger later
400 400 repo.dirstate.merge(fd)
401 401 if f != f2: # copy/rename
402 402 if move:
403 403 repo.dirstate.remove(f)
404 404 if f != fd:
405 405 repo.dirstate.copy(f, fd)
406 406 else:
407 407 repo.dirstate.copy(f2, fd)
408 408 else:
409 409 # We've update-merged a locally modified file, so
410 410 # we set the dirstate to emulate a normal checkout
411 411 # of that file some time in the past. Thus our
412 412 # merge will appear as a normal local file
413 413 # modification.
414 414 if f2 == fd: # file not locally copied/moved
415 415 repo.dirstate.normallookup(fd)
416 416 if move:
417 417 repo.dirstate.forget(f)
418 418 elif m == "d": # directory rename
419 419 f2, fd, flag = a[2:]
420 420 if not f2 and f not in repo.dirstate:
421 421 # untracked file moved
422 422 continue
423 423 if branchmerge:
424 424 repo.dirstate.add(fd)
425 425 if f:
426 426 repo.dirstate.remove(f)
427 427 repo.dirstate.copy(f, fd)
428 428 if f2:
429 429 repo.dirstate.copy(f2, fd)
430 430 else:
431 431 repo.dirstate.normal(fd)
432 432 if f:
433 433 repo.dirstate.forget(f)
434 434
435 435 def update(repo, node, branchmerge, force, partial):
436 436 """
437 437 Perform a merge between the working directory and the given node
438 438
439 439 node = the node to update to, or None if unspecified
440 440 branchmerge = whether to merge between branches
441 441 force = whether to force branch merging or file overwriting
442 442 partial = a function to filter file lists (dirstate not updated)
443 443
444 444 The table below shows all the behaviors of the update command
445 445 given the -c and -C or no options, whether the working directory
446 446 is dirty, whether a revision is specified, and the relationship of
447 447 the parent rev to the target rev (linear, on the same named
448 448 branch, or on another named branch).
449 449
450 450 This logic is tested by test-update-branches.t.
451 451
452 452 -c -C dirty rev | linear same cross
453 453 n n n n | ok (1) x
454 454 n n n y | ok ok ok
455 455 n n y * | merge (2) (2)
456 456 n y * * | --- discard ---
457 457 y n y * | --- (3) ---
458 458 y n n * | --- ok ---
459 459 y y * * | --- (4) ---
460 460
461 461 x = can't happen
462 462 * = don't-care
463 463 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
464 464 2 = abort: crosses branches (use 'hg merge' to merge or
465 465 use 'hg update -C' to discard changes)
466 466 3 = abort: uncommitted local changes
467 467 4 = incompatible options (checked in commands.py)
468 468
469 469 Return the same tuple as applyupdates().
470 470 """
471 471
472 472 onode = node
473 473 wlock = repo.wlock()
474 474 try:
475 475 wc = repo[None]
476 476 if node is None:
477 477 # tip of current branch
478 478 try:
479 479 node = repo.branchtags()[wc.branch()]
480 480 except KeyError:
481 481 if wc.branch() == "default": # no default branch!
482 482 node = repo.lookup("tip") # update to tip
483 483 else:
484 484 raise util.Abort(_("branch %s not found") % wc.branch())
485 485 overwrite = force and not branchmerge
486 486 pl = wc.parents()
487 487 p1, p2 = pl[0], repo[node]
488 488 pa = p1.ancestor(p2)
489 489 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
490 490 fastforward = False
491 491
492 492 ### check phase
493 493 if not overwrite and len(pl) > 1:
494 494 raise util.Abort(_("outstanding uncommitted merges"))
495 495 if branchmerge:
496 496 if pa == p2:
497 497 raise util.Abort(_("merging with a working directory ancestor"
498 498 " has no effect"))
499 499 elif pa == p1:
500 500 if p1.branch() != p2.branch():
501 501 fastforward = True
502 502 else:
503 503 raise util.Abort(_("nothing to merge (use 'hg update'"
504 504 " or check 'hg heads')"))
505 505 if not force and (wc.files() or wc.deleted()):
506 506 raise util.Abort(_("outstanding uncommitted changes "
507 507 "(use 'hg status' to list changes)"))
508 508 elif not overwrite:
509 509 if pa == p1 or pa == p2: # linear
510 510 pass # all good
511 511 elif wc.files() or wc.deleted():
512 512 raise util.Abort(_("crosses branches (merge branches or use"
513 513 " --clean to discard changes)"))
514 514 elif onode is None:
515 515 raise util.Abort(_("crosses branches (merge branches or use"
516 516 " --check to force update)"))
517 517 else:
518 518 # Allow jumping branches if clean and specific rev given
519 519 overwrite = True
520 520
521 521 ### calculate phase
522 522 action = []
523 523 wc.status(unknown=True) # prime cache
524 524 if not force:
525 525 _checkunknown(wc, p2)
526 526 if not util.checkcase(repo.path):
527 527 _checkcollision(p2)
528 528 action += _forgetremoved(wc, p2, branchmerge)
529 529 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
530 530
531 531 ### apply phase
532 532 if not branchmerge or fastforward: # just jump to the new rev
533 533 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
534 534 if not partial:
535 535 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
536 536
537 537 stats = applyupdates(repo, action, wc, p2, pa)
538 538
539 539 if not partial:
540 540 repo.dirstate.setparents(fp1, fp2)
541 541 recordupdates(repo, action, branchmerge and not fastforward)
542 542 if not branchmerge and not fastforward:
543 543 repo.dirstate.setbranch(p2.branch())
544 544 finally:
545 545 wlock.release()
546 546
547 547 if not partial:
548 548 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
549 549 return stats
@@ -1,1551 +1,1551 b''
1 1 # util.py - Mercurial utility functions and platform specfic implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Mercurial utility functions and platform specfic implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from i18n import _
17 17 import error, osutil, encoding
18 18 import errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 20 import imp, socket
21 21
22 22 # Python compatibility
23 23
24 24 def sha1(s):
25 25 return _fastsha1(s)
26 26
27 27 def _fastsha1(s):
28 28 # This function will import sha1 from hashlib or sha (whichever is
29 29 # available) and overwrite itself with it on the first call.
30 30 # Subsequent calls will go directly to the imported function.
31 31 if sys.version_info >= (2, 5):
32 32 from hashlib import sha1 as _sha1
33 33 else:
34 34 from sha import sha as _sha1
35 35 global _fastsha1, sha1
36 36 _fastsha1 = sha1 = _sha1
37 37 return _sha1(s)
38 38
39 39 import __builtin__
40 40
41 41 if sys.version_info[0] < 3:
42 42 def fakebuffer(sliceable, offset=0):
43 43 return sliceable[offset:]
44 44 else:
45 45 def fakebuffer(sliceable, offset=0):
46 46 return memoryview(sliceable)[offset:]
47 47 try:
48 48 buffer
49 49 except NameError:
50 50 __builtin__.buffer = fakebuffer
51 51
52 52 import subprocess
53 53 closefds = os.name == 'posix'
54 54
55 55 def popen2(cmd, env=None, newlines=False):
56 56 # Setting bufsize to -1 lets the system decide the buffer size.
57 57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 60 close_fds=closefds,
61 61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 62 universal_newlines=newlines,
63 63 env=env)
64 64 return p.stdin, p.stdout
65 65
66 66 def popen3(cmd, env=None, newlines=False):
67 67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 68 close_fds=closefds,
69 69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 70 stderr=subprocess.PIPE,
71 71 universal_newlines=newlines,
72 72 env=env)
73 73 return p.stdin, p.stdout, p.stderr
74 74
75 75 def version():
76 76 """Return version information if available."""
77 77 try:
78 78 import __version__
79 79 return __version__.version
80 80 except ImportError:
81 81 return 'unknown'
82 82
83 83 # used by parsedate
84 84 defaultdateformats = (
85 85 '%Y-%m-%d %H:%M:%S',
86 86 '%Y-%m-%d %I:%M:%S%p',
87 87 '%Y-%m-%d %H:%M',
88 88 '%Y-%m-%d %I:%M%p',
89 89 '%Y-%m-%d',
90 90 '%m-%d',
91 91 '%m/%d',
92 92 '%m/%d/%y',
93 93 '%m/%d/%Y',
94 94 '%a %b %d %H:%M:%S %Y',
95 95 '%a %b %d %I:%M:%S%p %Y',
96 96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 97 '%b %d %H:%M:%S %Y',
98 98 '%b %d %I:%M:%S%p %Y',
99 99 '%b %d %H:%M:%S',
100 100 '%b %d %I:%M:%S%p',
101 101 '%b %d %H:%M',
102 102 '%b %d %I:%M%p',
103 103 '%b %d %Y',
104 104 '%b %d',
105 105 '%H:%M:%S',
106 106 '%I:%M:%S%p',
107 107 '%H:%M',
108 108 '%I:%M%p',
109 109 )
110 110
111 111 extendeddateformats = defaultdateformats + (
112 112 "%Y",
113 113 "%Y-%m",
114 114 "%b",
115 115 "%b %Y",
116 116 )
117 117
118 118 def cachefunc(func):
119 119 '''cache the result of function calls'''
120 120 # XXX doesn't handle keywords args
121 121 cache = {}
122 122 if func.func_code.co_argcount == 1:
123 123 # we gain a small amount of time because
124 124 # we don't need to pack/unpack the list
125 125 def f(arg):
126 126 if arg not in cache:
127 127 cache[arg] = func(arg)
128 128 return cache[arg]
129 129 else:
130 130 def f(*args):
131 131 if args not in cache:
132 132 cache[args] = func(*args)
133 133 return cache[args]
134 134
135 135 return f
136 136
137 137 def lrucachefunc(func):
138 138 '''cache most recent results of function calls'''
139 139 cache = {}
140 140 order = []
141 141 if func.func_code.co_argcount == 1:
142 142 def f(arg):
143 143 if arg not in cache:
144 144 if len(cache) > 20:
145 145 del cache[order.pop(0)]
146 146 cache[arg] = func(arg)
147 147 else:
148 148 order.remove(arg)
149 149 order.append(arg)
150 150 return cache[arg]
151 151 else:
152 152 def f(*args):
153 153 if args not in cache:
154 154 if len(cache) > 20:
155 155 del cache[order.pop(0)]
156 156 cache[args] = func(*args)
157 157 else:
158 158 order.remove(args)
159 159 order.append(args)
160 160 return cache[args]
161 161
162 162 return f
163 163
164 164 class propertycache(object):
165 165 def __init__(self, func):
166 166 self.func = func
167 167 self.name = func.__name__
168 168 def __get__(self, obj, type=None):
169 169 result = self.func(obj)
170 170 setattr(obj, self.name, result)
171 171 return result
172 172
173 173 def pipefilter(s, cmd):
174 174 '''filter string S through command CMD, returning its output'''
175 175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 177 pout, perr = p.communicate(s)
178 178 return pout
179 179
180 180 def tempfilter(s, cmd):
181 181 '''filter string S through a pair of temporary files with CMD.
182 182 CMD is used as a template to create the real command to be run,
183 183 with the strings INFILE and OUTFILE replaced by the real names of
184 184 the temporary files generated.'''
185 185 inname, outname = None, None
186 186 try:
187 187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 188 fp = os.fdopen(infd, 'wb')
189 189 fp.write(s)
190 190 fp.close()
191 191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 192 os.close(outfd)
193 193 cmd = cmd.replace('INFILE', inname)
194 194 cmd = cmd.replace('OUTFILE', outname)
195 195 code = os.system(cmd)
196 196 if sys.platform == 'OpenVMS' and code & 1:
197 197 code = 0
198 198 if code:
199 199 raise Abort(_("command '%s' failed: %s") %
200 200 (cmd, explain_exit(code)))
201 201 return open(outname, 'rb').read()
202 202 finally:
203 203 try:
204 204 if inname:
205 205 os.unlink(inname)
206 206 except:
207 207 pass
208 208 try:
209 209 if outname:
210 210 os.unlink(outname)
211 211 except:
212 212 pass
213 213
214 214 filtertable = {
215 215 'tempfile:': tempfilter,
216 216 'pipe:': pipefilter,
217 217 }
218 218
219 219 def filter(s, cmd):
220 220 "filter a string through a command that transforms its input to its output"
221 221 for name, fn in filtertable.iteritems():
222 222 if cmd.startswith(name):
223 223 return fn(s, cmd[len(name):].lstrip())
224 224 return pipefilter(s, cmd)
225 225
226 226 def binary(s):
227 227 """return true if a string is binary data"""
228 228 return bool(s and '\0' in s)
229 229
230 230 def increasingchunks(source, min=1024, max=65536):
231 231 '''return no less than min bytes per chunk while data remains,
232 232 doubling min after each chunk until it reaches max'''
233 233 def log2(x):
234 234 if not x:
235 235 return 0
236 236 i = 0
237 237 while x:
238 238 x >>= 1
239 239 i += 1
240 240 return i - 1
241 241
242 242 buf = []
243 243 blen = 0
244 244 for chunk in source:
245 245 buf.append(chunk)
246 246 blen += len(chunk)
247 247 if blen >= min:
248 248 if min < max:
249 249 min = min << 1
250 250 nmin = 1 << log2(blen)
251 251 if nmin > min:
252 252 min = nmin
253 253 if min > max:
254 254 min = max
255 255 yield ''.join(buf)
256 256 blen = 0
257 257 buf = []
258 258 if buf:
259 259 yield ''.join(buf)
260 260
261 261 Abort = error.Abort
262 262
263 263 def always(fn):
264 264 return True
265 265
266 266 def never(fn):
267 267 return False
268 268
269 269 def pathto(root, n1, n2):
270 270 '''return the relative path from one place to another.
271 271 root should use os.sep to separate directories
272 272 n1 should use os.sep to separate directories
273 273 n2 should use "/" to separate directories
274 274 returns an os.sep-separated path.
275 275
276 276 If n1 is a relative path, it's assumed it's
277 277 relative to root.
278 278 n2 should always be relative to root.
279 279 '''
280 280 if not n1:
281 281 return localpath(n2)
282 282 if os.path.isabs(n1):
283 283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
284 284 return os.path.join(root, localpath(n2))
285 285 n2 = '/'.join((pconvert(root), n2))
286 286 a, b = splitpath(n1), n2.split('/')
287 287 a.reverse()
288 288 b.reverse()
289 289 while a and b and a[-1] == b[-1]:
290 290 a.pop()
291 291 b.pop()
292 292 b.reverse()
293 293 return os.sep.join((['..'] * len(a)) + b) or '.'
294 294
295 295 def canonpath(root, cwd, myname, auditor=None):
296 296 """return the canonical path of myname, given cwd and root"""
297 297 if endswithsep(root):
298 298 rootsep = root
299 299 else:
300 300 rootsep = root + os.sep
301 301 name = myname
302 302 if not os.path.isabs(name):
303 303 name = os.path.join(root, cwd, name)
304 304 name = os.path.normpath(name)
305 305 if auditor is None:
306 306 auditor = path_auditor(root)
307 307 if name != rootsep and name.startswith(rootsep):
308 308 name = name[len(rootsep):]
309 309 auditor(name)
310 310 return pconvert(name)
311 311 elif name == root:
312 312 return ''
313 313 else:
314 314 # Determine whether `name' is in the hierarchy at or beneath `root',
315 315 # by iterating name=dirname(name) until that causes no change (can't
316 316 # check name == '/', because that doesn't work on windows). For each
317 317 # `name', compare dev/inode numbers. If they match, the list `rel'
318 318 # holds the reversed list of components making up the relative file
319 319 # name we want.
320 320 root_st = os.stat(root)
321 321 rel = []
322 322 while True:
323 323 try:
324 324 name_st = os.stat(name)
325 325 except OSError:
326 326 break
327 327 if samestat(name_st, root_st):
328 328 if not rel:
329 329 # name was actually the same as root (maybe a symlink)
330 330 return ''
331 331 rel.reverse()
332 332 name = os.path.join(*rel)
333 333 auditor(name)
334 334 return pconvert(name)
335 335 dirname, basename = os.path.split(name)
336 336 rel.append(basename)
337 337 if dirname == name:
338 338 break
339 339 name = dirname
340 340
341 341 raise Abort('%s not under root' % myname)
342 342
343 343 _hgexecutable = None
344 344
345 345 def main_is_frozen():
346 346 """return True if we are a frozen executable.
347 347
348 348 The code supports py2exe (most common, Windows only) and tools/freeze
349 349 (portable, not much used).
350 350 """
351 351 return (hasattr(sys, "frozen") or # new py2exe
352 352 hasattr(sys, "importers") or # old py2exe
353 353 imp.is_frozen("__main__")) # tools/freeze
354 354
355 355 def hgexecutable():
356 356 """return location of the 'hg' executable.
357 357
358 358 Defaults to $HG or 'hg' in the search path.
359 359 """
360 360 if _hgexecutable is None:
361 361 hg = os.environ.get('HG')
362 362 if hg:
363 363 set_hgexecutable(hg)
364 364 elif main_is_frozen():
365 365 set_hgexecutable(sys.executable)
366 366 else:
367 367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
368 368 set_hgexecutable(exe)
369 369 return _hgexecutable
370 370
371 371 def set_hgexecutable(path):
372 372 """set location of the 'hg' executable"""
373 373 global _hgexecutable
374 374 _hgexecutable = path
375 375
376 376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
377 377 '''enhanced shell command execution.
378 378 run with environment maybe modified, maybe in different dir.
379 379
380 380 if command fails and onerr is None, return status. if ui object,
381 381 print error message and return status, else raise onerr object as
382 382 exception.
383 383
384 384 if out is specified, it is assumed to be a file-like object that has a
385 385 write() method. stdout and stderr will be redirected to out.'''
386 386 def py2shell(val):
387 387 'convert python object into string that is useful to shell'
388 388 if val is None or val is False:
389 389 return '0'
390 390 if val is True:
391 391 return '1'
392 392 return str(val)
393 393 origcmd = cmd
394 394 cmd = quotecommand(cmd)
395 395 env = dict(os.environ)
396 396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
397 397 env['HG'] = hgexecutable()
398 398 if out is None:
399 399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
400 400 env=env, cwd=cwd)
401 401 else:
402 402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
403 403 env=env, cwd=cwd, stdout=subprocess.PIPE,
404 404 stderr=subprocess.STDOUT)
405 405 for line in proc.stdout:
406 406 out.write(line)
407 407 proc.wait()
408 408 rc = proc.returncode
409 409 if sys.platform == 'OpenVMS' and rc & 1:
410 410 rc = 0
411 411 if rc and onerr:
412 412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
413 413 explain_exit(rc)[0])
414 414 if errprefix:
415 415 errmsg = '%s: %s' % (errprefix, errmsg)
416 416 try:
417 417 onerr.warn(errmsg + '\n')
418 418 except AttributeError:
419 419 raise onerr(errmsg)
420 420 return rc
421 421
422 422 def checksignature(func):
423 423 '''wrap a function with code to check for calling errors'''
424 424 def check(*args, **kwargs):
425 425 try:
426 426 return func(*args, **kwargs)
427 427 except TypeError:
428 428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
429 429 raise error.SignatureError
430 430 raise
431 431
432 432 return check
433 433
434 def unlink(f):
434 def unlinkpath(f):
435 435 """unlink and remove the directory if it is empty"""
436 436 os.unlink(f)
437 437 # try removing directories that might now be empty
438 438 try:
439 439 os.removedirs(os.path.dirname(f))
440 440 except OSError:
441 441 pass
442 442
443 443 def copyfile(src, dest):
444 444 "copy a file, preserving mode and atime/mtime"
445 445 if os.path.islink(src):
446 446 try:
447 447 os.unlink(dest)
448 448 except:
449 449 pass
450 450 os.symlink(os.readlink(src), dest)
451 451 else:
452 452 try:
453 453 shutil.copyfile(src, dest)
454 454 shutil.copymode(src, dest)
455 455 except shutil.Error, inst:
456 456 raise Abort(str(inst))
457 457
458 458 def copyfiles(src, dst, hardlink=None):
459 459 """Copy a directory tree using hardlinks if possible"""
460 460
461 461 if hardlink is None:
462 462 hardlink = (os.stat(src).st_dev ==
463 463 os.stat(os.path.dirname(dst)).st_dev)
464 464
465 465 num = 0
466 466 if os.path.isdir(src):
467 467 os.mkdir(dst)
468 468 for name, kind in osutil.listdir(src):
469 469 srcname = os.path.join(src, name)
470 470 dstname = os.path.join(dst, name)
471 471 hardlink, n = copyfiles(srcname, dstname, hardlink)
472 472 num += n
473 473 else:
474 474 if hardlink:
475 475 try:
476 476 os_link(src, dst)
477 477 except (IOError, OSError):
478 478 hardlink = False
479 479 shutil.copy(src, dst)
480 480 else:
481 481 shutil.copy(src, dst)
482 482 num += 1
483 483
484 484 return hardlink, num
485 485
486 486 class path_auditor(object):
487 487 '''ensure that a filesystem path contains no banned components.
488 488 the following properties of a path are checked:
489 489
490 490 - ends with a directory separator
491 491 - under top-level .hg
492 492 - starts at the root of a windows drive
493 493 - contains ".."
494 494 - traverses a symlink (e.g. a/symlink_here/b)
495 495 - inside a nested repository (a callback can be used to approve
496 496 some nested repositories, e.g., subrepositories)
497 497 '''
498 498
499 499 def __init__(self, root, callback=None):
500 500 self.audited = set()
501 501 self.auditeddir = set()
502 502 self.root = root
503 503 self.callback = callback
504 504
505 505 def __call__(self, path):
506 506 if path in self.audited:
507 507 return
508 508 # AIX ignores "/" at end of path, others raise EISDIR.
509 509 if endswithsep(path):
510 510 raise Abort(_("path ends in directory separator: %s") % path)
511 511 normpath = os.path.normcase(path)
512 512 parts = splitpath(normpath)
513 513 if (os.path.splitdrive(path)[0]
514 514 or parts[0].lower() in ('.hg', '.hg.', '')
515 515 or os.pardir in parts):
516 516 raise Abort(_("path contains illegal component: %s") % path)
517 517 if '.hg' in path.lower():
518 518 lparts = [p.lower() for p in parts]
519 519 for p in '.hg', '.hg.':
520 520 if p in lparts[1:]:
521 521 pos = lparts.index(p)
522 522 base = os.path.join(*parts[:pos])
523 523 raise Abort(_('path %r is inside repo %r') % (path, base))
524 524 def check(prefix):
525 525 curpath = os.path.join(self.root, prefix)
526 526 try:
527 527 st = os.lstat(curpath)
528 528 except OSError, err:
529 529 # EINVAL can be raised as invalid path syntax under win32.
530 530 # They must be ignored for patterns can be checked too.
531 531 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
532 532 raise
533 533 else:
534 534 if stat.S_ISLNK(st.st_mode):
535 535 raise Abort(_('path %r traverses symbolic link %r') %
536 536 (path, prefix))
537 537 elif (stat.S_ISDIR(st.st_mode) and
538 538 os.path.isdir(os.path.join(curpath, '.hg'))):
539 539 if not self.callback or not self.callback(curpath):
540 540 raise Abort(_('path %r is inside repo %r') %
541 541 (path, prefix))
542 542 parts.pop()
543 543 prefixes = []
544 544 while parts:
545 545 prefix = os.sep.join(parts)
546 546 if prefix in self.auditeddir:
547 547 break
548 548 check(prefix)
549 549 prefixes.append(prefix)
550 550 parts.pop()
551 551
552 552 self.audited.add(path)
553 553 # only add prefixes to the cache after checking everything: we don't
554 554 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
555 555 self.auditeddir.update(prefixes)
556 556
557 557 def nlinks(pathname):
558 558 """Return number of hardlinks for the given file."""
559 559 return os.lstat(pathname).st_nlink
560 560
561 561 if hasattr(os, 'link'):
562 562 os_link = os.link
563 563 else:
564 564 def os_link(src, dst):
565 565 raise OSError(0, _("Hardlinks not supported"))
566 566
567 567 def lookup_reg(key, name=None, scope=None):
568 568 return None
569 569
570 570 def hidewindow():
571 571 """Hide current shell window.
572 572
573 573 Used to hide the window opened when starting asynchronous
574 574 child process under Windows, unneeded on other systems.
575 575 """
576 576 pass
577 577
578 578 if os.name == 'nt':
579 579 from windows import *
580 580 else:
581 581 from posix import *
582 582
583 583 def makelock(info, pathname):
584 584 try:
585 585 return os.symlink(info, pathname)
586 586 except OSError, why:
587 587 if why.errno == errno.EEXIST:
588 588 raise
589 589 except AttributeError: # no symlink in os
590 590 pass
591 591
592 592 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
593 593 os.write(ld, info)
594 594 os.close(ld)
595 595
596 596 def readlock(pathname):
597 597 try:
598 598 return os.readlink(pathname)
599 599 except OSError, why:
600 600 if why.errno not in (errno.EINVAL, errno.ENOSYS):
601 601 raise
602 602 except AttributeError: # no symlink in os
603 603 pass
604 604 return posixfile(pathname).read()
605 605
606 606 def fstat(fp):
607 607 '''stat file object that may not have fileno method.'''
608 608 try:
609 609 return os.fstat(fp.fileno())
610 610 except AttributeError:
611 611 return os.stat(fp.name)
612 612
613 613 # File system features
614 614
615 615 def checkcase(path):
616 616 """
617 617 Check whether the given path is on a case-sensitive filesystem
618 618
619 619 Requires a path (like /foo/.hg) ending with a foldable final
620 620 directory component.
621 621 """
622 622 s1 = os.stat(path)
623 623 d, b = os.path.split(path)
624 624 p2 = os.path.join(d, b.upper())
625 625 if path == p2:
626 626 p2 = os.path.join(d, b.lower())
627 627 try:
628 628 s2 = os.stat(p2)
629 629 if s2 == s1:
630 630 return False
631 631 return True
632 632 except:
633 633 return True
634 634
635 635 _fspathcache = {}
636 636 def fspath(name, root):
637 637 '''Get name in the case stored in the filesystem
638 638
639 639 The name is either relative to root, or it is an absolute path starting
640 640 with root. Note that this function is unnecessary, and should not be
641 641 called, for case-sensitive filesystems (simply because it's expensive).
642 642 '''
643 643 # If name is absolute, make it relative
644 644 if name.lower().startswith(root.lower()):
645 645 l = len(root)
646 646 if name[l] == os.sep or name[l] == os.altsep:
647 647 l = l + 1
648 648 name = name[l:]
649 649
650 650 if not os.path.lexists(os.path.join(root, name)):
651 651 return None
652 652
653 653 seps = os.sep
654 654 if os.altsep:
655 655 seps = seps + os.altsep
656 656 # Protect backslashes. This gets silly very quickly.
657 657 seps.replace('\\','\\\\')
658 658 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
659 659 dir = os.path.normcase(os.path.normpath(root))
660 660 result = []
661 661 for part, sep in pattern.findall(name):
662 662 if sep:
663 663 result.append(sep)
664 664 continue
665 665
666 666 if dir not in _fspathcache:
667 667 _fspathcache[dir] = os.listdir(dir)
668 668 contents = _fspathcache[dir]
669 669
670 670 lpart = part.lower()
671 671 lenp = len(part)
672 672 for n in contents:
673 673 if lenp == len(n) and n.lower() == lpart:
674 674 result.append(n)
675 675 break
676 676 else:
677 677 # Cannot happen, as the file exists!
678 678 result.append(part)
679 679 dir = os.path.join(dir, lpart)
680 680
681 681 return ''.join(result)
682 682
683 683 def checkexec(path):
684 684 """
685 685 Check whether the given path is on a filesystem with UNIX-like exec flags
686 686
687 687 Requires a directory (like /foo/.hg)
688 688 """
689 689
690 690 # VFAT on some Linux versions can flip mode but it doesn't persist
691 691 # a FS remount. Frequently we can detect it if files are created
692 692 # with exec bit on.
693 693
694 694 try:
695 695 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
696 696 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
697 697 try:
698 698 os.close(fh)
699 699 m = os.stat(fn).st_mode & 0777
700 700 new_file_has_exec = m & EXECFLAGS
701 701 os.chmod(fn, m ^ EXECFLAGS)
702 702 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
703 703 finally:
704 704 os.unlink(fn)
705 705 except (IOError, OSError):
706 706 # we don't care, the user probably won't be able to commit anyway
707 707 return False
708 708 return not (new_file_has_exec or exec_flags_cannot_flip)
709 709
710 710 def checklink(path):
711 711 """check whether the given path is on a symlink-capable filesystem"""
712 712 # mktemp is not racy because symlink creation will fail if the
713 713 # file already exists
714 714 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
715 715 try:
716 716 os.symlink(".", name)
717 717 os.unlink(name)
718 718 return True
719 719 except (OSError, AttributeError):
720 720 return False
721 721
722 722 def checknlink(testfile):
723 723 '''check whether hardlink count reporting works properly'''
724 724
725 725 # testfile may be open, so we need a separate file for checking to
726 726 # work around issue2543 (or testfile may get lost on Samba shares)
727 727 f1 = testfile + ".hgtmp1"
728 728 if os.path.lexists(f1):
729 729 return False
730 730 try:
731 731 posixfile(f1, 'w').close()
732 732 except IOError:
733 733 return False
734 734
735 735 f2 = testfile + ".hgtmp2"
736 736 fd = None
737 737 try:
738 738 try:
739 739 os_link(f1, f2)
740 740 except OSError:
741 741 return False
742 742
743 743 # nlinks() may behave differently for files on Windows shares if
744 744 # the file is open.
745 745 fd = open(f2)
746 746 return nlinks(f2) > 1
747 747 finally:
748 748 if fd is not None:
749 749 fd.close()
750 750 for f in (f1, f2):
751 751 try:
752 752 os.unlink(f)
753 753 except OSError:
754 754 pass
755 755
756 756 return False
757 757
758 758 def endswithsep(path):
759 759 '''Check path ends with os.sep or os.altsep.'''
760 760 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
761 761
762 762 def splitpath(path):
763 763 '''Split path by os.sep.
764 764 Note that this function does not use os.altsep because this is
765 765 an alternative of simple "xxx.split(os.sep)".
766 766 It is recommended to use os.path.normpath() before using this
767 767 function if need.'''
768 768 return path.split(os.sep)
769 769
770 770 def gui():
771 771 '''Are we running in a GUI?'''
772 772 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
773 773
774 774 def mktempcopy(name, emptyok=False, createmode=None):
775 775 """Create a temporary file with the same contents from name
776 776
777 777 The permission bits are copied from the original file.
778 778
779 779 If the temporary file is going to be truncated immediately, you
780 780 can use emptyok=True as an optimization.
781 781
782 782 Returns the name of the temporary file.
783 783 """
784 784 d, fn = os.path.split(name)
785 785 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
786 786 os.close(fd)
787 787 # Temporary files are created with mode 0600, which is usually not
788 788 # what we want. If the original file already exists, just copy
789 789 # its mode. Otherwise, manually obey umask.
790 790 try:
791 791 st_mode = os.lstat(name).st_mode & 0777
792 792 except OSError, inst:
793 793 if inst.errno != errno.ENOENT:
794 794 raise
795 795 st_mode = createmode
796 796 if st_mode is None:
797 797 st_mode = ~umask
798 798 st_mode &= 0666
799 799 os.chmod(temp, st_mode)
800 800 if emptyok:
801 801 return temp
802 802 try:
803 803 try:
804 804 ifp = posixfile(name, "rb")
805 805 except IOError, inst:
806 806 if inst.errno == errno.ENOENT:
807 807 return temp
808 808 if not getattr(inst, 'filename', None):
809 809 inst.filename = name
810 810 raise
811 811 ofp = posixfile(temp, "wb")
812 812 for chunk in filechunkiter(ifp):
813 813 ofp.write(chunk)
814 814 ifp.close()
815 815 ofp.close()
816 816 except:
817 817 try: os.unlink(temp)
818 818 except: pass
819 819 raise
820 820 return temp
821 821
822 822 class atomictempfile(object):
823 823 """file-like object that atomically updates a file
824 824
825 825 All writes will be redirected to a temporary copy of the original
826 826 file. When rename is called, the copy is renamed to the original
827 827 name, making the changes visible.
828 828 """
829 829 def __init__(self, name, mode='w+b', createmode=None):
830 830 self.__name = name
831 831 self._fp = None
832 832 self.temp = mktempcopy(name, emptyok=('w' in mode),
833 833 createmode=createmode)
834 834 self._fp = posixfile(self.temp, mode)
835 835
836 836 def __getattr__(self, name):
837 837 return getattr(self._fp, name)
838 838
839 839 def rename(self):
840 840 if not self._fp.closed:
841 841 self._fp.close()
842 842 rename(self.temp, localpath(self.__name))
843 843
844 844 def close(self):
845 845 if not self._fp:
846 846 return
847 847 if not self._fp.closed:
848 848 try:
849 849 os.unlink(self.temp)
850 850 except: pass
851 851 self._fp.close()
852 852
853 853 def __del__(self):
854 854 self.close()
855 855
856 856 def makedirs(name, mode=None):
857 857 """recursive directory creation with parent mode inheritance"""
858 858 parent = os.path.abspath(os.path.dirname(name))
859 859 try:
860 860 os.mkdir(name)
861 861 if mode is not None:
862 862 os.chmod(name, mode)
863 863 return
864 864 except OSError, err:
865 865 if err.errno == errno.EEXIST:
866 866 return
867 867 if not name or parent == name or err.errno != errno.ENOENT:
868 868 raise
869 869 makedirs(parent, mode)
870 870 makedirs(name, mode)
871 871
872 872 class opener(object):
873 873 """Open files relative to a base directory
874 874
875 875 This class is used to hide the details of COW semantics and
876 876 remote file access from higher level code.
877 877 """
878 878 def __init__(self, base, audit=True):
879 879 self.base = base
880 880 if audit:
881 881 self.auditor = path_auditor(base)
882 882 else:
883 883 self.auditor = always
884 884 self.createmode = None
885 885 self._trustnlink = None
886 886
887 887 @propertycache
888 888 def _can_symlink(self):
889 889 return checklink(self.base)
890 890
891 891 def _fixfilemode(self, name):
892 892 if self.createmode is None:
893 893 return
894 894 os.chmod(name, self.createmode & 0666)
895 895
896 896 def __call__(self, path, mode="r", text=False, atomictemp=False):
897 897 self.auditor(path)
898 898 f = os.path.join(self.base, path)
899 899
900 900 if not text and "b" not in mode:
901 901 mode += "b" # for that other OS
902 902
903 903 nlink = -1
904 904 dirname, basename = os.path.split(f)
905 905 # If basename is empty, then the path is malformed because it points
906 906 # to a directory. Let the posixfile() call below raise IOError.
907 907 if basename and mode not in ('r', 'rb'):
908 908 if atomictemp:
909 909 if not os.path.isdir(dirname):
910 910 makedirs(dirname, self.createmode)
911 911 return atomictempfile(f, mode, self.createmode)
912 912 try:
913 913 if 'w' in mode:
914 914 os.unlink(f)
915 915 nlink = 0
916 916 else:
917 917 # nlinks() may behave differently for files on Windows
918 918 # shares if the file is open.
919 919 fd = open(f)
920 920 nlink = nlinks(f)
921 921 fd.close()
922 922 except (OSError, IOError):
923 923 nlink = 0
924 924 if not os.path.isdir(dirname):
925 925 makedirs(dirname, self.createmode)
926 926 if nlink > 0:
927 927 if self._trustnlink is None:
928 928 self._trustnlink = nlink > 1 or checknlink(f)
929 929 if nlink > 1 or not self._trustnlink:
930 930 rename(mktempcopy(f), f)
931 931 fp = posixfile(f, mode)
932 932 if nlink == 0:
933 933 self._fixfilemode(f)
934 934 return fp
935 935
936 936 def symlink(self, src, dst):
937 937 self.auditor(dst)
938 938 linkname = os.path.join(self.base, dst)
939 939 try:
940 940 os.unlink(linkname)
941 941 except OSError:
942 942 pass
943 943
944 944 dirname = os.path.dirname(linkname)
945 945 if not os.path.exists(dirname):
946 946 makedirs(dirname, self.createmode)
947 947
948 948 if self._can_symlink:
949 949 try:
950 950 os.symlink(src, linkname)
951 951 except OSError, err:
952 952 raise OSError(err.errno, _('could not symlink to %r: %s') %
953 953 (src, err.strerror), linkname)
954 954 else:
955 955 f = self(dst, "w")
956 956 f.write(src)
957 957 f.close()
958 958 self._fixfilemode(dst)
959 959
960 960 class chunkbuffer(object):
961 961 """Allow arbitrary sized chunks of data to be efficiently read from an
962 962 iterator over chunks of arbitrary size."""
963 963
964 964 def __init__(self, in_iter):
965 965 """in_iter is the iterator that's iterating over the input chunks.
966 966 targetsize is how big a buffer to try to maintain."""
967 967 def splitbig(chunks):
968 968 for chunk in chunks:
969 969 if len(chunk) > 2**20:
970 970 pos = 0
971 971 while pos < len(chunk):
972 972 end = pos + 2 ** 18
973 973 yield chunk[pos:end]
974 974 pos = end
975 975 else:
976 976 yield chunk
977 977 self.iter = splitbig(in_iter)
978 978 self._queue = []
979 979
980 980 def read(self, l):
981 981 """Read L bytes of data from the iterator of chunks of data.
982 982 Returns less than L bytes if the iterator runs dry."""
983 983 left = l
984 984 buf = ''
985 985 queue = self._queue
986 986 while left > 0:
987 987 # refill the queue
988 988 if not queue:
989 989 target = 2**18
990 990 for chunk in self.iter:
991 991 queue.append(chunk)
992 992 target -= len(chunk)
993 993 if target <= 0:
994 994 break
995 995 if not queue:
996 996 break
997 997
998 998 chunk = queue.pop(0)
999 999 left -= len(chunk)
1000 1000 if left < 0:
1001 1001 queue.insert(0, chunk[left:])
1002 1002 buf += chunk[:left]
1003 1003 else:
1004 1004 buf += chunk
1005 1005
1006 1006 return buf
1007 1007
1008 1008 def filechunkiter(f, size=65536, limit=None):
1009 1009 """Create a generator that produces the data in the file size
1010 1010 (default 65536) bytes at a time, up to optional limit (default is
1011 1011 to read all data). Chunks may be less than size bytes if the
1012 1012 chunk is the last chunk in the file, or the file is a socket or
1013 1013 some other type of file that sometimes reads less data than is
1014 1014 requested."""
1015 1015 assert size >= 0
1016 1016 assert limit is None or limit >= 0
1017 1017 while True:
1018 1018 if limit is None:
1019 1019 nbytes = size
1020 1020 else:
1021 1021 nbytes = min(limit, size)
1022 1022 s = nbytes and f.read(nbytes)
1023 1023 if not s:
1024 1024 break
1025 1025 if limit:
1026 1026 limit -= len(s)
1027 1027 yield s
1028 1028
1029 1029 def makedate():
1030 1030 lt = time.localtime()
1031 1031 if lt[8] == 1 and time.daylight:
1032 1032 tz = time.altzone
1033 1033 else:
1034 1034 tz = time.timezone
1035 1035 t = time.mktime(lt)
1036 1036 if t < 0:
1037 1037 hint = _("check your clock")
1038 1038 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1039 1039 return t, tz
1040 1040
1041 1041 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1042 1042 """represent a (unixtime, offset) tuple as a localized time.
1043 1043 unixtime is seconds since the epoch, and offset is the time zone's
1044 1044 number of seconds away from UTC. if timezone is false, do not
1045 1045 append time zone to string."""
1046 1046 t, tz = date or makedate()
1047 1047 if t < 0:
1048 1048 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1049 1049 tz = 0
1050 1050 if "%1" in format or "%2" in format:
1051 1051 sign = (tz > 0) and "-" or "+"
1052 1052 minutes = abs(tz) // 60
1053 1053 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1054 1054 format = format.replace("%2", "%02d" % (minutes % 60))
1055 1055 s = time.strftime(format, time.gmtime(float(t) - tz))
1056 1056 return s
1057 1057
1058 1058 def shortdate(date=None):
1059 1059 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1060 1060 return datestr(date, format='%Y-%m-%d')
1061 1061
1062 1062 def strdate(string, format, defaults=[]):
1063 1063 """parse a localized time string and return a (unixtime, offset) tuple.
1064 1064 if the string cannot be parsed, ValueError is raised."""
1065 1065 def timezone(string):
1066 1066 tz = string.split()[-1]
1067 1067 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1068 1068 sign = (tz[0] == "+") and 1 or -1
1069 1069 hours = int(tz[1:3])
1070 1070 minutes = int(tz[3:5])
1071 1071 return -sign * (hours * 60 + minutes) * 60
1072 1072 if tz == "GMT" or tz == "UTC":
1073 1073 return 0
1074 1074 return None
1075 1075
1076 1076 # NOTE: unixtime = localunixtime + offset
1077 1077 offset, date = timezone(string), string
1078 1078 if offset is not None:
1079 1079 date = " ".join(string.split()[:-1])
1080 1080
1081 1081 # add missing elements from defaults
1082 1082 usenow = False # default to using biased defaults
1083 1083 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1084 1084 found = [True for p in part if ("%"+p) in format]
1085 1085 if not found:
1086 1086 date += "@" + defaults[part][usenow]
1087 1087 format += "@%" + part[0]
1088 1088 else:
1089 1089 # We've found a specific time element, less specific time
1090 1090 # elements are relative to today
1091 1091 usenow = True
1092 1092
1093 1093 timetuple = time.strptime(date, format)
1094 1094 localunixtime = int(calendar.timegm(timetuple))
1095 1095 if offset is None:
1096 1096 # local timezone
1097 1097 unixtime = int(time.mktime(timetuple))
1098 1098 offset = unixtime - localunixtime
1099 1099 else:
1100 1100 unixtime = localunixtime + offset
1101 1101 return unixtime, offset
1102 1102
1103 1103 def parsedate(date, formats=None, bias={}):
1104 1104 """parse a localized date/time and return a (unixtime, offset) tuple.
1105 1105
1106 1106 The date may be a "unixtime offset" string or in one of the specified
1107 1107 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1108 1108 """
1109 1109 if not date:
1110 1110 return 0, 0
1111 1111 if isinstance(date, tuple) and len(date) == 2:
1112 1112 return date
1113 1113 if not formats:
1114 1114 formats = defaultdateformats
1115 1115 date = date.strip()
1116 1116 try:
1117 1117 when, offset = map(int, date.split(' '))
1118 1118 except ValueError:
1119 1119 # fill out defaults
1120 1120 now = makedate()
1121 1121 defaults = {}
1122 1122 nowmap = {}
1123 1123 for part in ("d", "mb", "yY", "HI", "M", "S"):
1124 1124 # this piece is for rounding the specific end of unknowns
1125 1125 b = bias.get(part)
1126 1126 if b is None:
1127 1127 if part[0] in "HMS":
1128 1128 b = "00"
1129 1129 else:
1130 1130 b = "0"
1131 1131
1132 1132 # this piece is for matching the generic end to today's date
1133 1133 n = datestr(now, "%" + part[0])
1134 1134
1135 1135 defaults[part] = (b, n)
1136 1136
1137 1137 for format in formats:
1138 1138 try:
1139 1139 when, offset = strdate(date, format, defaults)
1140 1140 except (ValueError, OverflowError):
1141 1141 pass
1142 1142 else:
1143 1143 break
1144 1144 else:
1145 1145 raise Abort(_('invalid date: %r') % date)
1146 1146 # validate explicit (probably user-specified) date and
1147 1147 # time zone offset. values must fit in signed 32 bits for
1148 1148 # current 32-bit linux runtimes. timezones go from UTC-12
1149 1149 # to UTC+14
1150 1150 if abs(when) > 0x7fffffff:
1151 1151 raise Abort(_('date exceeds 32 bits: %d') % when)
1152 1152 if when < 0:
1153 1153 raise Abort(_('negative date value: %d') % when)
1154 1154 if offset < -50400 or offset > 43200:
1155 1155 raise Abort(_('impossible time zone offset: %d') % offset)
1156 1156 return when, offset
1157 1157
1158 1158 def matchdate(date):
1159 1159 """Return a function that matches a given date match specifier
1160 1160
1161 1161 Formats include:
1162 1162
1163 1163 '{date}' match a given date to the accuracy provided
1164 1164
1165 1165 '<{date}' on or before a given date
1166 1166
1167 1167 '>{date}' on or after a given date
1168 1168
1169 1169 >>> p1 = parsedate("10:29:59")
1170 1170 >>> p2 = parsedate("10:30:00")
1171 1171 >>> p3 = parsedate("10:30:59")
1172 1172 >>> p4 = parsedate("10:31:00")
1173 1173 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1174 1174 >>> f = matchdate("10:30")
1175 1175 >>> f(p1[0])
1176 1176 False
1177 1177 >>> f(p2[0])
1178 1178 True
1179 1179 >>> f(p3[0])
1180 1180 True
1181 1181 >>> f(p4[0])
1182 1182 False
1183 1183 >>> f(p5[0])
1184 1184 False
1185 1185 """
1186 1186
1187 1187 def lower(date):
1188 1188 d = dict(mb="1", d="1")
1189 1189 return parsedate(date, extendeddateformats, d)[0]
1190 1190
1191 1191 def upper(date):
1192 1192 d = dict(mb="12", HI="23", M="59", S="59")
1193 1193 for days in ("31", "30", "29"):
1194 1194 try:
1195 1195 d["d"] = days
1196 1196 return parsedate(date, extendeddateformats, d)[0]
1197 1197 except:
1198 1198 pass
1199 1199 d["d"] = "28"
1200 1200 return parsedate(date, extendeddateformats, d)[0]
1201 1201
1202 1202 date = date.strip()
1203 1203 if date[0] == "<":
1204 1204 when = upper(date[1:])
1205 1205 return lambda x: x <= when
1206 1206 elif date[0] == ">":
1207 1207 when = lower(date[1:])
1208 1208 return lambda x: x >= when
1209 1209 elif date[0] == "-":
1210 1210 try:
1211 1211 days = int(date[1:])
1212 1212 except ValueError:
1213 1213 raise Abort(_("invalid day spec: %s") % date[1:])
1214 1214 when = makedate()[0] - days * 3600 * 24
1215 1215 return lambda x: x >= when
1216 1216 elif " to " in date:
1217 1217 a, b = date.split(" to ")
1218 1218 start, stop = lower(a), upper(b)
1219 1219 return lambda x: x >= start and x <= stop
1220 1220 else:
1221 1221 start, stop = lower(date), upper(date)
1222 1222 return lambda x: x >= start and x <= stop
1223 1223
1224 1224 def shortuser(user):
1225 1225 """Return a short representation of a user name or email address."""
1226 1226 f = user.find('@')
1227 1227 if f >= 0:
1228 1228 user = user[:f]
1229 1229 f = user.find('<')
1230 1230 if f >= 0:
1231 1231 user = user[f + 1:]
1232 1232 f = user.find(' ')
1233 1233 if f >= 0:
1234 1234 user = user[:f]
1235 1235 f = user.find('.')
1236 1236 if f >= 0:
1237 1237 user = user[:f]
1238 1238 return user
1239 1239
1240 1240 def email(author):
1241 1241 '''get email of author.'''
1242 1242 r = author.find('>')
1243 1243 if r == -1:
1244 1244 r = None
1245 1245 return author[author.find('<') + 1:r]
1246 1246
1247 1247 def _ellipsis(text, maxlength):
1248 1248 if len(text) <= maxlength:
1249 1249 return text, False
1250 1250 else:
1251 1251 return "%s..." % (text[:maxlength - 3]), True
1252 1252
1253 1253 def ellipsis(text, maxlength=400):
1254 1254 """Trim string to at most maxlength (default: 400) characters."""
1255 1255 try:
1256 1256 # use unicode not to split at intermediate multi-byte sequence
1257 1257 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1258 1258 maxlength)
1259 1259 if not truncated:
1260 1260 return text
1261 1261 return utext.encode(encoding.encoding)
1262 1262 except (UnicodeDecodeError, UnicodeEncodeError):
1263 1263 return _ellipsis(text, maxlength)[0]
1264 1264
1265 1265 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1266 1266 '''yield every hg repository under path, recursively.'''
1267 1267 def errhandler(err):
1268 1268 if err.filename == path:
1269 1269 raise err
1270 1270 if followsym and hasattr(os.path, 'samestat'):
1271 1271 def _add_dir_if_not_there(dirlst, dirname):
1272 1272 match = False
1273 1273 samestat = os.path.samestat
1274 1274 dirstat = os.stat(dirname)
1275 1275 for lstdirstat in dirlst:
1276 1276 if samestat(dirstat, lstdirstat):
1277 1277 match = True
1278 1278 break
1279 1279 if not match:
1280 1280 dirlst.append(dirstat)
1281 1281 return not match
1282 1282 else:
1283 1283 followsym = False
1284 1284
1285 1285 if (seen_dirs is None) and followsym:
1286 1286 seen_dirs = []
1287 1287 _add_dir_if_not_there(seen_dirs, path)
1288 1288 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1289 1289 dirs.sort()
1290 1290 if '.hg' in dirs:
1291 1291 yield root # found a repository
1292 1292 qroot = os.path.join(root, '.hg', 'patches')
1293 1293 if os.path.isdir(os.path.join(qroot, '.hg')):
1294 1294 yield qroot # we have a patch queue repo here
1295 1295 if recurse:
1296 1296 # avoid recursing inside the .hg directory
1297 1297 dirs.remove('.hg')
1298 1298 else:
1299 1299 dirs[:] = [] # don't descend further
1300 1300 elif followsym:
1301 1301 newdirs = []
1302 1302 for d in dirs:
1303 1303 fname = os.path.join(root, d)
1304 1304 if _add_dir_if_not_there(seen_dirs, fname):
1305 1305 if os.path.islink(fname):
1306 1306 for hgname in walkrepos(fname, True, seen_dirs):
1307 1307 yield hgname
1308 1308 else:
1309 1309 newdirs.append(d)
1310 1310 dirs[:] = newdirs
1311 1311
1312 1312 _rcpath = None
1313 1313
1314 1314 def os_rcpath():
1315 1315 '''return default os-specific hgrc search path'''
1316 1316 path = system_rcpath()
1317 1317 path.extend(user_rcpath())
1318 1318 path = [os.path.normpath(f) for f in path]
1319 1319 return path
1320 1320
1321 1321 def rcpath():
1322 1322 '''return hgrc search path. if env var HGRCPATH is set, use it.
1323 1323 for each item in path, if directory, use files ending in .rc,
1324 1324 else use item.
1325 1325 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1326 1326 if no HGRCPATH, use default os-specific path.'''
1327 1327 global _rcpath
1328 1328 if _rcpath is None:
1329 1329 if 'HGRCPATH' in os.environ:
1330 1330 _rcpath = []
1331 1331 for p in os.environ['HGRCPATH'].split(os.pathsep):
1332 1332 if not p:
1333 1333 continue
1334 1334 p = expandpath(p)
1335 1335 if os.path.isdir(p):
1336 1336 for f, kind in osutil.listdir(p):
1337 1337 if f.endswith('.rc'):
1338 1338 _rcpath.append(os.path.join(p, f))
1339 1339 else:
1340 1340 _rcpath.append(p)
1341 1341 else:
1342 1342 _rcpath = os_rcpath()
1343 1343 return _rcpath
1344 1344
1345 1345 def bytecount(nbytes):
1346 1346 '''return byte count formatted as readable string, with units'''
1347 1347
1348 1348 units = (
1349 1349 (100, 1 << 30, _('%.0f GB')),
1350 1350 (10, 1 << 30, _('%.1f GB')),
1351 1351 (1, 1 << 30, _('%.2f GB')),
1352 1352 (100, 1 << 20, _('%.0f MB')),
1353 1353 (10, 1 << 20, _('%.1f MB')),
1354 1354 (1, 1 << 20, _('%.2f MB')),
1355 1355 (100, 1 << 10, _('%.0f KB')),
1356 1356 (10, 1 << 10, _('%.1f KB')),
1357 1357 (1, 1 << 10, _('%.2f KB')),
1358 1358 (1, 1, _('%.0f bytes')),
1359 1359 )
1360 1360
1361 1361 for multiplier, divisor, format in units:
1362 1362 if nbytes >= divisor * multiplier:
1363 1363 return format % (nbytes / float(divisor))
1364 1364 return units[-1][2] % nbytes
1365 1365
1366 1366 def drop_scheme(scheme, path):
1367 1367 sc = scheme + ':'
1368 1368 if path.startswith(sc):
1369 1369 path = path[len(sc):]
1370 1370 if path.startswith('//'):
1371 1371 if scheme == 'file':
1372 1372 i = path.find('/', 2)
1373 1373 if i == -1:
1374 1374 return ''
1375 1375 # On Windows, absolute paths are rooted at the current drive
1376 1376 # root. On POSIX they are rooted at the file system root.
1377 1377 if os.name == 'nt':
1378 1378 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1379 1379 path = os.path.join(droot, path[i + 1:])
1380 1380 else:
1381 1381 path = path[i:]
1382 1382 else:
1383 1383 path = path[2:]
1384 1384 return path
1385 1385
1386 1386 def uirepr(s):
1387 1387 # Avoid double backslash in Windows path repr()
1388 1388 return repr(s).replace('\\\\', '\\')
1389 1389
1390 1390 #### naming convention of below implementation follows 'textwrap' module
1391 1391
1392 1392 class MBTextWrapper(textwrap.TextWrapper):
1393 1393 """
1394 1394 Extend TextWrapper for double-width characters.
1395 1395
1396 1396 Some Asian characters use two terminal columns instead of one.
1397 1397 A good example of this behavior can be seen with u'\u65e5\u672c',
1398 1398 the two Japanese characters for "Japan":
1399 1399 len() returns 2, but when printed to a terminal, they eat 4 columns.
1400 1400
1401 1401 (Note that this has nothing to do whatsoever with unicode
1402 1402 representation, or encoding of the underlying string)
1403 1403 """
1404 1404 def __init__(self, **kwargs):
1405 1405 textwrap.TextWrapper.__init__(self, **kwargs)
1406 1406
1407 1407 def _cutdown(self, str, space_left):
1408 1408 l = 0
1409 1409 ucstr = unicode(str, encoding.encoding)
1410 1410 colwidth = unicodedata.east_asian_width
1411 1411 for i in xrange(len(ucstr)):
1412 1412 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1413 1413 if space_left < l:
1414 1414 return (ucstr[:i].encode(encoding.encoding),
1415 1415 ucstr[i:].encode(encoding.encoding))
1416 1416 return str, ''
1417 1417
1418 1418 # ----------------------------------------
1419 1419 # overriding of base class
1420 1420
1421 1421 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1422 1422 space_left = max(width - cur_len, 1)
1423 1423
1424 1424 if self.break_long_words:
1425 1425 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1426 1426 cur_line.append(cut)
1427 1427 reversed_chunks[-1] = res
1428 1428 elif not cur_line:
1429 1429 cur_line.append(reversed_chunks.pop())
1430 1430
1431 1431 #### naming convention of above implementation follows 'textwrap' module
1432 1432
1433 1433 def wrap(line, width, initindent='', hangindent=''):
1434 1434 maxindent = max(len(hangindent), len(initindent))
1435 1435 if width <= maxindent:
1436 1436 # adjust for weird terminal size
1437 1437 width = max(78, maxindent + 1)
1438 1438 wrapper = MBTextWrapper(width=width,
1439 1439 initial_indent=initindent,
1440 1440 subsequent_indent=hangindent)
1441 1441 return wrapper.fill(line)
1442 1442
1443 1443 def iterlines(iterator):
1444 1444 for chunk in iterator:
1445 1445 for line in chunk.splitlines():
1446 1446 yield line
1447 1447
1448 1448 def expandpath(path):
1449 1449 return os.path.expanduser(os.path.expandvars(path))
1450 1450
1451 1451 def hgcmd():
1452 1452 """Return the command used to execute current hg
1453 1453
1454 1454 This is different from hgexecutable() because on Windows we want
1455 1455 to avoid things opening new shell windows like batch files, so we
1456 1456 get either the python call or current executable.
1457 1457 """
1458 1458 if main_is_frozen():
1459 1459 return [sys.executable]
1460 1460 return gethgcmd()
1461 1461
1462 1462 def rundetached(args, condfn):
1463 1463 """Execute the argument list in a detached process.
1464 1464
1465 1465 condfn is a callable which is called repeatedly and should return
1466 1466 True once the child process is known to have started successfully.
1467 1467 At this point, the child process PID is returned. If the child
1468 1468 process fails to start or finishes before condfn() evaluates to
1469 1469 True, return -1.
1470 1470 """
1471 1471 # Windows case is easier because the child process is either
1472 1472 # successfully starting and validating the condition or exiting
1473 1473 # on failure. We just poll on its PID. On Unix, if the child
1474 1474 # process fails to start, it will be left in a zombie state until
1475 1475 # the parent wait on it, which we cannot do since we expect a long
1476 1476 # running process on success. Instead we listen for SIGCHLD telling
1477 1477 # us our child process terminated.
1478 1478 terminated = set()
1479 1479 def handler(signum, frame):
1480 1480 terminated.add(os.wait())
1481 1481 prevhandler = None
1482 1482 if hasattr(signal, 'SIGCHLD'):
1483 1483 prevhandler = signal.signal(signal.SIGCHLD, handler)
1484 1484 try:
1485 1485 pid = spawndetached(args)
1486 1486 while not condfn():
1487 1487 if ((pid in terminated or not testpid(pid))
1488 1488 and not condfn()):
1489 1489 return -1
1490 1490 time.sleep(0.1)
1491 1491 return pid
1492 1492 finally:
1493 1493 if prevhandler is not None:
1494 1494 signal.signal(signal.SIGCHLD, prevhandler)
1495 1495
1496 1496 try:
1497 1497 any, all = any, all
1498 1498 except NameError:
1499 1499 def any(iterable):
1500 1500 for i in iterable:
1501 1501 if i:
1502 1502 return True
1503 1503 return False
1504 1504
1505 1505 def all(iterable):
1506 1506 for i in iterable:
1507 1507 if not i:
1508 1508 return False
1509 1509 return True
1510 1510
1511 1511 def interpolate(prefix, mapping, s, fn=None):
1512 1512 """Return the result of interpolating items in the mapping into string s.
1513 1513
1514 1514 prefix is a single character string, or a two character string with
1515 1515 a backslash as the first character if the prefix needs to be escaped in
1516 1516 a regular expression.
1517 1517
1518 1518 fn is an optional function that will be applied to the replacement text
1519 1519 just before replacement.
1520 1520 """
1521 1521 fn = fn or (lambda s: s)
1522 1522 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1523 1523 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1524 1524
1525 1525 def getport(port):
1526 1526 """Return the port for a given network service.
1527 1527
1528 1528 If port is an integer, it's returned as is. If it's a string, it's
1529 1529 looked up using socket.getservbyname(). If there's no matching
1530 1530 service, util.Abort is raised.
1531 1531 """
1532 1532 try:
1533 1533 return int(port)
1534 1534 except ValueError:
1535 1535 pass
1536 1536
1537 1537 try:
1538 1538 return socket.getservbyname(port)
1539 1539 except socket.error:
1540 1540 raise Abort(_("no port number associated with service '%s'") % port)
1541 1541
1542 1542 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1543 1543 '0': False, 'no': False, 'false': False, 'off': False,
1544 1544 'never': False}
1545 1545
1546 1546 def parsebool(s):
1547 1547 """Parse s into a boolean.
1548 1548
1549 1549 If s is not a valid boolean, returns None.
1550 1550 """
1551 1551 return _booleans.get(s.lower(), None)
@@ -1,375 +1,375 b''
1 1 # windows.py - Windows utility function implementations for Mercurial
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 from i18n import _
9 9 import osutil, error
10 10 import errno, msvcrt, os, re, sys, random, subprocess
11 11
12 12 nulldev = 'NUL:'
13 13 umask = 002
14 14
15 15 # wrap osutil.posixfile to provide friendlier exceptions
16 16 def posixfile(name, mode='r', buffering=-1):
17 17 try:
18 18 return osutil.posixfile(name, mode, buffering)
19 19 except WindowsError, err:
20 20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 21 posixfile.__doc__ = osutil.posixfile.__doc__
22 22
23 23 class winstdout(object):
24 24 '''stdout on windows misbehaves if sent through a pipe'''
25 25
26 26 def __init__(self, fp):
27 27 self.fp = fp
28 28
29 29 def __getattr__(self, key):
30 30 return getattr(self.fp, key)
31 31
32 32 def close(self):
33 33 try:
34 34 self.fp.close()
35 35 except: pass
36 36
37 37 def write(self, s):
38 38 try:
39 39 # This is workaround for "Not enough space" error on
40 40 # writing large size of data to console.
41 41 limit = 16000
42 42 l = len(s)
43 43 start = 0
44 44 self.softspace = 0
45 45 while start < l:
46 46 end = start + limit
47 47 self.fp.write(s[start:end])
48 48 start = end
49 49 except IOError, inst:
50 50 if inst.errno != 0:
51 51 raise
52 52 self.close()
53 53 raise IOError(errno.EPIPE, 'Broken pipe')
54 54
55 55 def flush(self):
56 56 try:
57 57 return self.fp.flush()
58 58 except IOError, inst:
59 59 if inst.errno != errno.EINVAL:
60 60 raise
61 61 self.close()
62 62 raise IOError(errno.EPIPE, 'Broken pipe')
63 63
64 64 sys.stdout = winstdout(sys.stdout)
65 65
66 66 def _is_win_9x():
67 67 '''return true if run on windows 95, 98 or me.'''
68 68 try:
69 69 return sys.getwindowsversion()[3] == 1
70 70 except AttributeError:
71 71 return 'command' in os.environ.get('comspec', '')
72 72
73 73 def openhardlinks():
74 74 return not _is_win_9x() and "win32api" in globals()
75 75
76 76 def system_rcpath():
77 77 try:
78 78 return system_rcpath_win32()
79 79 except:
80 80 return [r'c:\mercurial\mercurial.ini']
81 81
82 82 def user_rcpath():
83 83 '''return os-specific hgrc search path to the user dir'''
84 84 try:
85 85 path = user_rcpath_win32()
86 86 except:
87 87 home = os.path.expanduser('~')
88 88 path = [os.path.join(home, 'mercurial.ini'),
89 89 os.path.join(home, '.hgrc')]
90 90 userprofile = os.environ.get('USERPROFILE')
91 91 if userprofile:
92 92 path.append(os.path.join(userprofile, 'mercurial.ini'))
93 93 path.append(os.path.join(userprofile, '.hgrc'))
94 94 return path
95 95
96 96 def parse_patch_output(output_line):
97 97 """parses the output produced by patch and returns the filename"""
98 98 pf = output_line[14:]
99 99 if pf[0] == '`':
100 100 pf = pf[1:-1] # Remove the quotes
101 101 return pf
102 102
103 103 def sshargs(sshcmd, host, user, port):
104 104 '''Build argument list for ssh or Plink'''
105 105 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
106 106 args = user and ("%s@%s" % (user, host)) or host
107 107 return port and ("%s %s %s" % (args, pflag, port)) or args
108 108
109 109 def testpid(pid):
110 110 '''return False if pid dead, True if running or not known'''
111 111 return True
112 112
113 113 def set_flags(f, l, x):
114 114 pass
115 115
116 116 def set_binary(fd):
117 117 # When run without console, pipes may expose invalid
118 118 # fileno(), usually set to -1.
119 119 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
120 120 msvcrt.setmode(fd.fileno(), os.O_BINARY)
121 121
122 122 def pconvert(path):
123 123 return '/'.join(path.split(os.sep))
124 124
125 125 def localpath(path):
126 126 return path.replace('/', '\\')
127 127
128 128 def normpath(path):
129 129 return pconvert(os.path.normpath(path))
130 130
131 131 def realpath(path):
132 132 '''
133 133 Returns the true, canonical file system path equivalent to the given
134 134 path.
135 135 '''
136 136 # TODO: There may be a more clever way to do this that also handles other,
137 137 # less common file systems.
138 138 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
139 139
140 140 def samestat(s1, s2):
141 141 return False
142 142
143 143 # A sequence of backslashes is special iff it precedes a double quote:
144 144 # - if there's an even number of backslashes, the double quote is not
145 145 # quoted (i.e. it ends the quoted region)
146 146 # - if there's an odd number of backslashes, the double quote is quoted
147 147 # - in both cases, every pair of backslashes is unquoted into a single
148 148 # backslash
149 149 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
150 150 # So, to quote a string, we must surround it in double quotes, double
151 151 # the number of backslashes that preceed double quotes and add another
152 152 # backslash before every double quote (being careful with the double
153 153 # quote we've appended to the end)
154 154 _quotere = None
155 155 def shellquote(s):
156 156 global _quotere
157 157 if _quotere is None:
158 158 _quotere = re.compile(r'(\\*)("|\\$)')
159 159 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
160 160
161 161 def quotecommand(cmd):
162 162 """Build a command string suitable for os.popen* calls."""
163 163 if sys.version_info < (2, 7, 1):
164 164 # Python versions since 2.7.1 do this extra quoting themselves
165 165 return '"' + cmd + '"'
166 166 return cmd
167 167
168 168 def popen(command, mode='r'):
169 169 # Work around "popen spawned process may not write to stdout
170 170 # under windows"
171 171 # http://bugs.python.org/issue1366
172 172 command += " 2> %s" % nulldev
173 173 return os.popen(quotecommand(command), mode)
174 174
175 175 def explain_exit(code):
176 176 return _("exited with status %d") % code, code
177 177
178 178 # if you change this stub into a real check, please try to implement the
179 179 # username and groupname functions above, too.
180 180 def isowner(st):
181 181 return True
182 182
183 183 def find_exe(command):
184 184 '''Find executable for command searching like cmd.exe does.
185 185 If command is a basename then PATH is searched for command.
186 186 PATH isn't searched if command is an absolute or relative path.
187 187 An extension from PATHEXT is found and added if not present.
188 188 If command isn't found None is returned.'''
189 189 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
190 190 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
191 191 if os.path.splitext(command)[1].lower() in pathexts:
192 192 pathexts = ['']
193 193
194 194 def findexisting(pathcommand):
195 195 'Will append extension (if needed) and return existing file'
196 196 for ext in pathexts:
197 197 executable = pathcommand + ext
198 198 if os.path.exists(executable):
199 199 return executable
200 200 return None
201 201
202 202 if os.sep in command:
203 203 return findexisting(command)
204 204
205 205 for path in os.environ.get('PATH', '').split(os.pathsep):
206 206 executable = findexisting(os.path.join(path, command))
207 207 if executable is not None:
208 208 return executable
209 209 return findexisting(os.path.expanduser(os.path.expandvars(command)))
210 210
211 211 def set_signal_handler():
212 212 try:
213 213 set_signal_handler_win32()
214 214 except NameError:
215 215 pass
216 216
217 217 def statfiles(files):
218 218 '''Stat each file in files and yield stat or None if file does not exist.
219 219 Cluster and cache stat per directory to minimize number of OS stat calls.'''
220 220 ncase = os.path.normcase
221 221 dircache = {} # dirname -> filename -> status | None if file does not exist
222 222 for nf in files:
223 223 nf = ncase(nf)
224 224 dir, base = os.path.split(nf)
225 225 if not dir:
226 226 dir = '.'
227 227 cache = dircache.get(dir, None)
228 228 if cache is None:
229 229 try:
230 230 dmap = dict([(ncase(n), s)
231 231 for n, k, s in osutil.listdir(dir, True)])
232 232 except OSError, err:
233 233 # handle directory not found in Python version prior to 2.5
234 234 # Python <= 2.4 returns native Windows code 3 in errno
235 235 # Python >= 2.5 returns ENOENT and adds winerror field
236 236 # EINVAL is raised if dir is not a directory.
237 237 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
238 238 errno.ENOTDIR):
239 239 raise
240 240 dmap = {}
241 241 cache = dircache.setdefault(dir, dmap)
242 242 yield cache.get(base, None)
243 243
244 244 def getuser():
245 245 '''return name of current user'''
246 246 raise error.Abort(_('user name not available - set USERNAME '
247 247 'environment variable'))
248 248
249 249 def username(uid=None):
250 250 """Return the name of the user with the given uid.
251 251
252 252 If uid is None, return the name of the current user."""
253 253 return None
254 254
255 255 def groupname(gid=None):
256 256 """Return the name of the group with the given gid.
257 257
258 258 If gid is None, return the name of the current group."""
259 259 return None
260 260
261 261 def _removedirs(name):
262 262 """special version of os.removedirs that does not remove symlinked
263 263 directories or junction points if they actually contain files"""
264 264 if osutil.listdir(name):
265 265 return
266 266 os.rmdir(name)
267 267 head, tail = os.path.split(name)
268 268 if not tail:
269 269 head, tail = os.path.split(head)
270 270 while head and tail:
271 271 try:
272 272 if osutil.listdir(head):
273 273 return
274 274 os.rmdir(head)
275 275 except:
276 276 break
277 277 head, tail = os.path.split(head)
278 278
279 def unlink(f):
279 def unlinkpath(f):
280 280 """unlink and remove the directory if it is empty"""
281 281 os.unlink(f)
282 282 # try removing directories that might now be empty
283 283 try:
284 284 _removedirs(os.path.dirname(f))
285 285 except OSError:
286 286 pass
287 287
288 288 def rename(src, dst):
289 289 '''atomically rename file src to dst, replacing dst if it exists'''
290 290 try:
291 291 os.rename(src, dst)
292 292 except OSError: # FIXME: check err (EEXIST ?)
293 293
294 294 # On windows, rename to existing file is not allowed, so we
295 295 # must delete destination first. But if a file is open, unlink
296 296 # schedules it for delete but does not delete it. Rename
297 297 # happens immediately even for open files, so we rename
298 298 # destination to a temporary name, then delete that. Then
299 299 # rename is safe to do.
300 300 # The temporary name is chosen at random to avoid the situation
301 301 # where a file is left lying around from a previous aborted run.
302 302
303 303 for tries in xrange(10):
304 304 temp = '%s-%08x' % (dst, random.randint(0, 0xffffffff))
305 305 try:
306 306 os.rename(dst, temp) # raises OSError EEXIST if temp exists
307 307 break
308 308 except OSError, e:
309 309 if e.errno != errno.EEXIST:
310 310 raise
311 311 else:
312 312 raise IOError, (errno.EEXIST, "No usable temporary filename found")
313 313
314 314 try:
315 315 os.unlink(temp)
316 316 except:
317 317 # Some rude AV-scanners on Windows may cause the unlink to
318 318 # fail. Not aborting here just leaks the temp file, whereas
319 319 # aborting at this point may leave serious inconsistencies.
320 320 # Ideally, we would notify the user here.
321 321 pass
322 322 os.rename(src, dst)
323 323
324 324 def spawndetached(args):
325 325 # No standard library function really spawns a fully detached
326 326 # process under win32 because they allocate pipes or other objects
327 327 # to handle standard streams communications. Passing these objects
328 328 # to the child process requires handle inheritance to be enabled
329 329 # which makes really detached processes impossible.
330 330 class STARTUPINFO:
331 331 dwFlags = subprocess.STARTF_USESHOWWINDOW
332 332 hStdInput = None
333 333 hStdOutput = None
334 334 hStdError = None
335 335 wShowWindow = subprocess.SW_HIDE
336 336
337 337 args = subprocess.list2cmdline(args)
338 338 # Not running the command in shell mode makes python26 hang when
339 339 # writing to hgweb output socket.
340 340 comspec = os.environ.get("COMSPEC", "cmd.exe")
341 341 args = comspec + " /c " + args
342 342 hp, ht, pid, tid = subprocess.CreateProcess(
343 343 None, args,
344 344 # no special security
345 345 None, None,
346 346 # Do not inherit handles
347 347 0,
348 348 # DETACHED_PROCESS
349 349 0x00000008,
350 350 os.environ,
351 351 os.getcwd(),
352 352 STARTUPINFO())
353 353 return pid
354 354
355 355 def gethgcmd():
356 356 return [sys.executable] + sys.argv[:1]
357 357
358 358 def termwidth():
359 359 # cmd.exe does not handle CR like a unix console, the CR is
360 360 # counted in the line length. On 80 columns consoles, if 80
361 361 # characters are written, the following CR won't apply on the
362 362 # current line but on the new one. Keep room for it.
363 363 return 79
364 364
365 365 def groupmembers(name):
366 366 # Don't support groups on Windows for now
367 367 raise KeyError()
368 368
369 369 try:
370 370 # override functions with win32 versions if possible
371 371 from win32 import *
372 372 except ImportError:
373 373 pass
374 374
375 375 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now