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