##// END OF EJS Templates
parseurl: also return the revision after the "#"; add a test
Alexis S. L. Carvalho -
r5222:cbe6e263 default
parent child Browse files
Show More
@@ -0,0 +1,50
1 #!/bin/sh
2 # test basic functionality of url#rev syntax
3
4 hg init repo
5 cd repo
6 echo a > a
7 hg ci -qAm 'add a' -d '0 0'
8 hg branch foo
9 echo >> a
10 hg ci -m 'change a' -d '0 0'
11 cd ..
12
13 echo '% clone repo#foo'
14 hg clone 'repo#foo' clone
15 hg --cwd clone heads
16 sed -e 's/default.*#/default = #/' clone/.hg/hgrc
17 echo
18
19 echo '% changing original repo'
20 cd repo
21 echo >> a
22 hg ci -m 'new head of branch foo' -d '0 0'
23 hg up -qC default
24 echo bar > bar
25 hg ci -qAm 'add bar' -d '0 0'
26 hg log
27 echo
28
29 echo '% outgoing'
30 hg -q outgoing '../clone#foo'
31 echo
32
33 echo '% push'
34 hg -q push '../clone#foo'
35 hg --cwd ../clone heads
36 cd ..
37 echo
38
39 echo '% rolling back'
40 cd clone
41 hg rollback
42
43 echo '% incoming'
44 hg -q incoming
45
46 echo '% pull'
47 hg -q pull
48 hg heads
49 echo
50
@@ -0,0 +1,69
1 marked working directory as branch foo
2 % clone repo#foo
3 requesting all changes
4 adding changesets
5 adding manifests
6 adding file changes
7 added 2 changesets with 2 changes to 1 files
8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 changeset: 1:cd2a86ecc814
10 branch: foo
11 tag: tip
12 user: test
13 date: Thu Jan 01 00:00:00 1970 +0000
14 summary: change a
15
16 [paths]
17 default = #foo
18
19 % changing original repo
20 changeset: 3:4cd725637392
21 tag: tip
22 parent: 0:1f0dee641bb7
23 user: test
24 date: Thu Jan 01 00:00:00 1970 +0000
25 summary: add bar
26
27 changeset: 2:faba9097cad4
28 branch: foo
29 user: test
30 date: Thu Jan 01 00:00:00 1970 +0000
31 summary: new head of branch foo
32
33 changeset: 1:cd2a86ecc814
34 branch: foo
35 user: test
36 date: Thu Jan 01 00:00:00 1970 +0000
37 summary: change a
38
39 changeset: 0:1f0dee641bb7
40 user: test
41 date: Thu Jan 01 00:00:00 1970 +0000
42 summary: add a
43
44
45 % outgoing
46 2:faba9097cad4
47
48 % push
49 changeset: 2:faba9097cad4
50 branch: foo
51 tag: tip
52 user: test
53 date: Thu Jan 01 00:00:00 1970 +0000
54 summary: new head of branch foo
55
56
57 % rolling back
58 rolling back last transaction
59 % incoming
60 2:faba9097cad4
61 % pull
62 changeset: 2:faba9097cad4
63 branch: foo
64 tag: tip
65 user: test
66 date: Thu Jan 01 00:00:00 1970 +0000
67 summary: new head of branch foo
68
69
@@ -1,1275 +1,1275
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex
11 11 import mdiff, bdiff, util, templater, patch, commands, hg, lock, time
12 12 import fancyopts, revlog, version, extensions, hook
13 13
14 14 revrangesep = ':'
15 15
16 16 class UnknownCommand(Exception):
17 17 """Exception raised if command is not in the command table."""
18 18 class AmbiguousCommand(Exception):
19 19 """Exception raised if command shortcut matches more than one command."""
20 20 class ParseError(Exception):
21 21 """Exception raised on errors in parsing the command line."""
22 22
23 23 def runcatch(ui, args):
24 24 def catchterm(*args):
25 25 raise util.SignalInterrupt
26 26
27 27 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
28 28 num = getattr(signal, name, None)
29 29 if num: signal.signal(num, catchterm)
30 30
31 31 try:
32 32 try:
33 33 # enter the debugger before command execution
34 34 if '--debugger' in args:
35 35 pdb.set_trace()
36 36 try:
37 37 return dispatch(ui, args)
38 38 finally:
39 39 ui.flush()
40 40 except:
41 41 # enter the debugger when we hit an exception
42 42 if '--debugger' in args:
43 43 pdb.post_mortem(sys.exc_info()[2])
44 44 ui.print_exc()
45 45 raise
46 46
47 47 except ParseError, inst:
48 48 if inst.args[0]:
49 49 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
50 50 commands.help_(ui, inst.args[0])
51 51 else:
52 52 ui.warn(_("hg: %s\n") % inst.args[1])
53 53 commands.help_(ui, 'shortlist')
54 54 except AmbiguousCommand, inst:
55 55 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
56 56 (inst.args[0], " ".join(inst.args[1])))
57 57 except UnknownCommand, inst:
58 58 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
59 59 commands.help_(ui, 'shortlist')
60 60 except hg.RepoError, inst:
61 61 ui.warn(_("abort: %s!\n") % inst)
62 62 except lock.LockHeld, inst:
63 63 if inst.errno == errno.ETIMEDOUT:
64 64 reason = _('timed out waiting for lock held by %s') % inst.locker
65 65 else:
66 66 reason = _('lock held by %s') % inst.locker
67 67 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
68 68 except lock.LockUnavailable, inst:
69 69 ui.warn(_("abort: could not lock %s: %s\n") %
70 70 (inst.desc or inst.filename, inst.strerror))
71 71 except revlog.RevlogError, inst:
72 72 ui.warn(_("abort: %s!\n") % inst)
73 73 except util.SignalInterrupt:
74 74 ui.warn(_("killed!\n"))
75 75 except KeyboardInterrupt:
76 76 try:
77 77 ui.warn(_("interrupted!\n"))
78 78 except IOError, inst:
79 79 if inst.errno == errno.EPIPE:
80 80 if ui.debugflag:
81 81 ui.warn(_("\nbroken pipe\n"))
82 82 else:
83 83 raise
84 84 except socket.error, inst:
85 85 ui.warn(_("abort: %s\n") % inst[1])
86 86 except IOError, inst:
87 87 if hasattr(inst, "code"):
88 88 ui.warn(_("abort: %s\n") % inst)
89 89 elif hasattr(inst, "reason"):
90 90 try: # usually it is in the form (errno, strerror)
91 91 reason = inst.reason.args[1]
92 92 except: # it might be anything, for example a string
93 93 reason = inst.reason
94 94 ui.warn(_("abort: error: %s\n") % reason)
95 95 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
96 96 if ui.debugflag:
97 97 ui.warn(_("broken pipe\n"))
98 98 elif getattr(inst, "strerror", None):
99 99 if getattr(inst, "filename", None):
100 100 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
101 101 else:
102 102 ui.warn(_("abort: %s\n") % inst.strerror)
103 103 else:
104 104 raise
105 105 except OSError, inst:
106 106 if getattr(inst, "filename", None):
107 107 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
108 108 else:
109 109 ui.warn(_("abort: %s\n") % inst.strerror)
110 110 except util.UnexpectedOutput, inst:
111 111 ui.warn(_("abort: %s") % inst[0])
112 112 if not isinstance(inst[1], basestring):
113 113 ui.warn(" %r\n" % (inst[1],))
114 114 elif not inst[1]:
115 115 ui.warn(_(" empty string\n"))
116 116 else:
117 117 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
118 118 except ImportError, inst:
119 119 m = str(inst).split()[-1]
120 120 ui.warn(_("abort: could not import module %s!\n" % m))
121 121 if m in "mpatch bdiff".split():
122 122 ui.warn(_("(did you forget to compile extensions?)\n"))
123 123 elif m in "zlib".split():
124 124 ui.warn(_("(is your Python install correct?)\n"))
125 125
126 126 except util.Abort, inst:
127 127 ui.warn(_("abort: %s\n") % inst)
128 128 except SystemExit, inst:
129 129 # Commands shouldn't sys.exit directly, but give a return code.
130 130 # Just in case catch this and and pass exit code to caller.
131 131 return inst.code
132 132 except:
133 133 ui.warn(_("** unknown exception encountered, details follow\n"))
134 134 ui.warn(_("** report bug details to "
135 135 "http://www.selenic.com/mercurial/bts\n"))
136 136 ui.warn(_("** or mercurial@selenic.com\n"))
137 137 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
138 138 % version.get_version())
139 139 raise
140 140
141 141 return -1
142 142
143 143 def findpossible(ui, cmd):
144 144 """
145 145 Return cmd -> (aliases, command table entry)
146 146 for each matching command.
147 147 Return debug commands (or their aliases) only if no normal command matches.
148 148 """
149 149 choice = {}
150 150 debugchoice = {}
151 151 for e in commands.table.keys():
152 152 aliases = e.lstrip("^").split("|")
153 153 found = None
154 154 if cmd in aliases:
155 155 found = cmd
156 156 elif not ui.config("ui", "strict"):
157 157 for a in aliases:
158 158 if a.startswith(cmd):
159 159 found = a
160 160 break
161 161 if found is not None:
162 162 if aliases[0].startswith("debug") or found.startswith("debug"):
163 163 debugchoice[found] = (aliases, commands.table[e])
164 164 else:
165 165 choice[found] = (aliases, commands.table[e])
166 166
167 167 if not choice and debugchoice:
168 168 choice = debugchoice
169 169
170 170 return choice
171 171
172 172 def findcmd(ui, cmd):
173 173 """Return (aliases, command table entry) for command string."""
174 174 choice = findpossible(ui, cmd)
175 175
176 176 if choice.has_key(cmd):
177 177 return choice[cmd]
178 178
179 179 if len(choice) > 1:
180 180 clist = choice.keys()
181 181 clist.sort()
182 182 raise AmbiguousCommand(cmd, clist)
183 183
184 184 if choice:
185 185 return choice.values()[0]
186 186
187 187 raise UnknownCommand(cmd)
188 188
189 189 def findrepo():
190 190 p = os.getcwd()
191 191 while not os.path.isdir(os.path.join(p, ".hg")):
192 192 oldp, p = p, os.path.dirname(p)
193 193 if p == oldp:
194 194 return None
195 195
196 196 return p
197 197
198 198 def parse(ui, args):
199 199 options = {}
200 200 cmdoptions = {}
201 201
202 202 try:
203 203 args = fancyopts.fancyopts(args, commands.globalopts, options)
204 204 except fancyopts.getopt.GetoptError, inst:
205 205 raise ParseError(None, inst)
206 206
207 207 if args:
208 208 cmd, args = args[0], args[1:]
209 209 aliases, i = findcmd(ui, cmd)
210 210 cmd = aliases[0]
211 211 defaults = ui.config("defaults", cmd)
212 212 if defaults:
213 213 args = shlex.split(defaults) + args
214 214 c = list(i[1])
215 215 else:
216 216 cmd = None
217 217 c = []
218 218
219 219 # combine global options into local
220 220 for o in commands.globalopts:
221 221 c.append((o[0], o[1], options[o[1]], o[3]))
222 222
223 223 try:
224 224 args = fancyopts.fancyopts(args, c, cmdoptions)
225 225 except fancyopts.getopt.GetoptError, inst:
226 226 raise ParseError(cmd, inst)
227 227
228 228 # separate global options back out
229 229 for o in commands.globalopts:
230 230 n = o[1]
231 231 options[n] = cmdoptions[n]
232 232 del cmdoptions[n]
233 233
234 234 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
235 235
236 236 def parseconfig(config):
237 237 """parse the --config options from the command line"""
238 238 parsed = []
239 239 for cfg in config:
240 240 try:
241 241 name, value = cfg.split('=', 1)
242 242 section, name = name.split('.', 1)
243 243 if not section or not name:
244 244 raise IndexError
245 245 parsed.append((section, name, value))
246 246 except (IndexError, ValueError):
247 247 raise util.Abort(_('malformed --config option: %s') % cfg)
248 248 return parsed
249 249
250 250 def earlygetopt(aliases, args):
251 251 """Return list of values for an option (or aliases).
252 252
253 253 The values are listed in the order they appear in args.
254 254 The options and values are removed from args.
255 255 """
256 256 try:
257 257 argcount = args.index("--")
258 258 except ValueError:
259 259 argcount = len(args)
260 260 shortopts = [opt for opt in aliases if len(opt) == 2]
261 261 values = []
262 262 pos = 0
263 263 while pos < argcount:
264 264 if args[pos] in aliases:
265 265 if pos + 1 >= argcount:
266 266 # ignore and let getopt report an error if there is no value
267 267 break
268 268 del args[pos]
269 269 values.append(args.pop(pos))
270 270 argcount -= 2
271 271 elif args[pos][:2] in shortopts:
272 272 # short option can have no following space, e.g. hg log -Rfoo
273 273 values.append(args.pop(pos)[2:])
274 274 argcount -= 1
275 275 else:
276 276 pos += 1
277 277 return values
278 278
279 279 def dispatch(ui, args):
280 280 # read --config before doing anything else
281 281 # (e.g. to change trust settings for reading .hg/hgrc)
282 282 config = earlygetopt(['--config'], args)
283 283 if config:
284 284 ui.updateopts(config=parseconfig(config))
285 285
286 286 # check for cwd
287 287 cwd = earlygetopt(['--cwd'], args)
288 288 if cwd:
289 289 os.chdir(cwd[-1])
290 290
291 291 # read the local repository .hgrc into a local ui object
292 292 path = findrepo() or ""
293 293 if not path:
294 294 lui = ui
295 295 if path:
296 296 try:
297 297 lui = commands.ui.ui(parentui=ui)
298 298 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
299 299 except IOError:
300 300 pass
301 301
302 302 # now we can expand paths, even ones in .hg/hgrc
303 303 rpath = earlygetopt(["-R", "--repository", "--repo"], args)
304 304 if rpath:
305 305 path = lui.expandpath(rpath[-1])
306 306 lui = commands.ui.ui(parentui=ui)
307 307 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
308 308
309 309 extensions.loadall(lui)
310 310 # check for fallback encoding
311 311 fallback = lui.config('ui', 'fallbackencoding')
312 312 if fallback:
313 313 util._fallbackencoding = fallback
314 314
315 315 fullargs = args
316 316 cmd, func, args, options, cmdoptions = parse(lui, args)
317 317
318 318 if options["config"]:
319 319 raise util.Abort(_("Option --config may not be abbreviated!"))
320 320 if options["cwd"]:
321 321 raise util.Abort(_("Option --cwd may not be abbreviated!"))
322 322 if options["repository"]:
323 323 raise util.Abort(_(
324 324 "Option -R has to be separated from other options (i.e. not -qR) "
325 325 "and --repository may only be abbreviated as --repo!"))
326 326
327 327 if options["encoding"]:
328 328 util._encoding = options["encoding"]
329 329 if options["encodingmode"]:
330 330 util._encodingmode = options["encodingmode"]
331 331 if options["time"]:
332 332 def get_times():
333 333 t = os.times()
334 334 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
335 335 t = (t[0], t[1], t[2], t[3], time.clock())
336 336 return t
337 337 s = get_times()
338 338 def print_time():
339 339 t = get_times()
340 340 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
341 341 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
342 342 atexit.register(print_time)
343 343
344 344 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
345 345 not options["noninteractive"], options["traceback"])
346 346
347 347 if options['help']:
348 348 return commands.help_(ui, cmd, options['version'])
349 349 elif options['version']:
350 350 return commands.version_(ui)
351 351 elif not cmd:
352 352 return commands.help_(ui, 'shortlist')
353 353
354 354 repo = None
355 355 if cmd not in commands.norepo.split():
356 356 try:
357 357 repo = hg.repository(ui, path=path)
358 358 ui = repo.ui
359 359 if not repo.local():
360 360 raise util.Abort(_("repository '%s' is not local") % path)
361 361 except hg.RepoError:
362 362 if cmd not in commands.optionalrepo.split():
363 363 if not path:
364 364 raise hg.RepoError(_("There is no Mercurial repository here"
365 365 " (.hg not found)"))
366 366 raise
367 367 d = lambda: func(ui, repo, *args, **cmdoptions)
368 368 else:
369 369 d = lambda: func(ui, *args, **cmdoptions)
370 370
371 371 # run pre-hook, and abort if it fails
372 372 ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
373 373 if ret:
374 374 return ret
375 375 ret = runcommand(ui, options, cmd, d)
376 376 # run post-hook, passing command result
377 377 hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
378 378 result = ret)
379 379 return ret
380 380
381 381 def runcommand(ui, options, cmd, cmdfunc):
382 382 def checkargs():
383 383 try:
384 384 return cmdfunc()
385 385 except TypeError, inst:
386 386 # was this an argument error?
387 387 tb = traceback.extract_tb(sys.exc_info()[2])
388 388 if len(tb) != 2: # no
389 389 raise
390 390 raise ParseError(cmd, _("invalid arguments"))
391 391
392 392 if options['profile']:
393 393 import hotshot, hotshot.stats
394 394 prof = hotshot.Profile("hg.prof")
395 395 try:
396 396 try:
397 397 return prof.runcall(checkargs)
398 398 except:
399 399 try:
400 400 ui.warn(_('exception raised - generating '
401 401 'profile anyway\n'))
402 402 except:
403 403 pass
404 404 raise
405 405 finally:
406 406 prof.close()
407 407 stats = hotshot.stats.load("hg.prof")
408 408 stats.strip_dirs()
409 409 stats.sort_stats('time', 'calls')
410 410 stats.print_stats(40)
411 411 elif options['lsprof']:
412 412 try:
413 413 from mercurial import lsprof
414 414 except ImportError:
415 415 raise util.Abort(_(
416 416 'lsprof not available - install from '
417 417 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
418 418 p = lsprof.Profiler()
419 419 p.enable(subcalls=True)
420 420 try:
421 421 return checkargs()
422 422 finally:
423 423 p.disable()
424 424 stats = lsprof.Stats(p.getstats())
425 425 stats.sort()
426 426 stats.pprint(top=10, file=sys.stderr, climit=5)
427 427 else:
428 428 return checkargs()
429 429
430 430 def bail_if_changed(repo):
431 431 modified, added, removed, deleted = repo.status()[:4]
432 432 if modified or added or removed or deleted:
433 433 raise util.Abort(_("outstanding uncommitted changes"))
434 434
435 435 def logmessage(opts):
436 436 """ get the log message according to -m and -l option """
437 437 message = opts['message']
438 438 logfile = opts['logfile']
439 439
440 440 if message and logfile:
441 441 raise util.Abort(_('options --message and --logfile are mutually '
442 442 'exclusive'))
443 443 if not message and logfile:
444 444 try:
445 445 if logfile == '-':
446 446 message = sys.stdin.read()
447 447 else:
448 448 message = open(logfile).read()
449 449 except IOError, inst:
450 450 raise util.Abort(_("can't read commit message '%s': %s") %
451 451 (logfile, inst.strerror))
452 452 return message
453 453
454 454 def setremoteconfig(ui, opts):
455 455 "copy remote options to ui tree"
456 456 if opts.get('ssh'):
457 457 ui.setconfig("ui", "ssh", opts['ssh'])
458 458 if opts.get('remotecmd'):
459 459 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
460 460
461 461 def parseurl(url, revs):
462 462 '''parse url#branch, returning url, branch + revs'''
463 463
464 464 if '#' not in url:
465 return url, (revs or None)
465 return url, (revs or None), None
466 466
467 467 url, rev = url.split('#', 1)
468 return url, revs + [rev]
468 return url, revs + [rev], rev
469 469
470 470 def revpair(repo, revs):
471 471 '''return pair of nodes, given list of revisions. second item can
472 472 be None, meaning use working dir.'''
473 473
474 474 def revfix(repo, val, defval):
475 475 if not val and val != 0 and defval is not None:
476 476 val = defval
477 477 return repo.lookup(val)
478 478
479 479 if not revs:
480 480 return repo.dirstate.parents()[0], None
481 481 end = None
482 482 if len(revs) == 1:
483 483 if revrangesep in revs[0]:
484 484 start, end = revs[0].split(revrangesep, 1)
485 485 start = revfix(repo, start, 0)
486 486 end = revfix(repo, end, repo.changelog.count() - 1)
487 487 else:
488 488 start = revfix(repo, revs[0], None)
489 489 elif len(revs) == 2:
490 490 if revrangesep in revs[0] or revrangesep in revs[1]:
491 491 raise util.Abort(_('too many revisions specified'))
492 492 start = revfix(repo, revs[0], None)
493 493 end = revfix(repo, revs[1], None)
494 494 else:
495 495 raise util.Abort(_('too many revisions specified'))
496 496 return start, end
497 497
498 498 def revrange(repo, revs):
499 499 """Yield revision as strings from a list of revision specifications."""
500 500
501 501 def revfix(repo, val, defval):
502 502 if not val and val != 0 and defval is not None:
503 503 return defval
504 504 return repo.changelog.rev(repo.lookup(val))
505 505
506 506 seen, l = {}, []
507 507 for spec in revs:
508 508 if revrangesep in spec:
509 509 start, end = spec.split(revrangesep, 1)
510 510 start = revfix(repo, start, 0)
511 511 end = revfix(repo, end, repo.changelog.count() - 1)
512 512 step = start > end and -1 or 1
513 513 for rev in xrange(start, end+step, step):
514 514 if rev in seen:
515 515 continue
516 516 seen[rev] = 1
517 517 l.append(rev)
518 518 else:
519 519 rev = revfix(repo, spec, None)
520 520 if rev in seen:
521 521 continue
522 522 seen[rev] = 1
523 523 l.append(rev)
524 524
525 525 return l
526 526
527 527 def make_filename(repo, pat, node,
528 528 total=None, seqno=None, revwidth=None, pathname=None):
529 529 node_expander = {
530 530 'H': lambda: hex(node),
531 531 'R': lambda: str(repo.changelog.rev(node)),
532 532 'h': lambda: short(node),
533 533 }
534 534 expander = {
535 535 '%': lambda: '%',
536 536 'b': lambda: os.path.basename(repo.root),
537 537 }
538 538
539 539 try:
540 540 if node:
541 541 expander.update(node_expander)
542 542 if node:
543 543 expander['r'] = (lambda:
544 544 str(repo.changelog.rev(node)).zfill(revwidth or 0))
545 545 if total is not None:
546 546 expander['N'] = lambda: str(total)
547 547 if seqno is not None:
548 548 expander['n'] = lambda: str(seqno)
549 549 if total is not None and seqno is not None:
550 550 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
551 551 if pathname is not None:
552 552 expander['s'] = lambda: os.path.basename(pathname)
553 553 expander['d'] = lambda: os.path.dirname(pathname) or '.'
554 554 expander['p'] = lambda: pathname
555 555
556 556 newname = []
557 557 patlen = len(pat)
558 558 i = 0
559 559 while i < patlen:
560 560 c = pat[i]
561 561 if c == '%':
562 562 i += 1
563 563 c = pat[i]
564 564 c = expander[c]()
565 565 newname.append(c)
566 566 i += 1
567 567 return ''.join(newname)
568 568 except KeyError, inst:
569 569 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
570 570 inst.args[0])
571 571
572 572 def make_file(repo, pat, node=None,
573 573 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
574 574 if not pat or pat == '-':
575 575 return 'w' in mode and sys.stdout or sys.stdin
576 576 if hasattr(pat, 'write') and 'w' in mode:
577 577 return pat
578 578 if hasattr(pat, 'read') and 'r' in mode:
579 579 return pat
580 580 return open(make_filename(repo, pat, node, total, seqno, revwidth,
581 581 pathname),
582 582 mode)
583 583
584 584 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
585 585 cwd = repo.getcwd()
586 586 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
587 587 opts.get('exclude'), globbed=globbed,
588 588 default=default)
589 589
590 590 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
591 591 default=None):
592 592 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
593 593 default=default)
594 594 exact = dict.fromkeys(files)
595 595 cwd = repo.getcwd()
596 596 for src, fn in repo.walk(node=node, files=files, match=matchfn,
597 597 badmatch=badmatch):
598 598 yield src, fn, repo.pathto(fn, cwd), fn in exact
599 599
600 600 def findrenames(repo, added=None, removed=None, threshold=0.5):
601 601 '''find renamed files -- yields (before, after, score) tuples'''
602 602 if added is None or removed is None:
603 603 added, removed = repo.status()[1:3]
604 604 ctx = repo.changectx()
605 605 for a in added:
606 606 aa = repo.wread(a)
607 607 bestname, bestscore = None, threshold
608 608 for r in removed:
609 609 rr = ctx.filectx(r).data()
610 610
611 611 # bdiff.blocks() returns blocks of matching lines
612 612 # count the number of bytes in each
613 613 equal = 0
614 614 alines = mdiff.splitnewlines(aa)
615 615 matches = bdiff.blocks(aa, rr)
616 616 for x1,x2,y1,y2 in matches:
617 617 for line in alines[x1:x2]:
618 618 equal += len(line)
619 619
620 620 lengths = len(aa) + len(rr)
621 621 if lengths:
622 622 myscore = equal*2.0 / lengths
623 623 if myscore >= bestscore:
624 624 bestname, bestscore = r, myscore
625 625 if bestname:
626 626 yield bestname, a, bestscore
627 627
628 628 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
629 629 similarity=None):
630 630 if dry_run is None:
631 631 dry_run = opts.get('dry_run')
632 632 if similarity is None:
633 633 similarity = float(opts.get('similarity') or 0)
634 634 add, remove = [], []
635 635 mapping = {}
636 636 for src, abs, rel, exact in walk(repo, pats, opts):
637 637 target = repo.wjoin(abs)
638 638 if src == 'f' and repo.dirstate.state(abs) == '?':
639 639 add.append(abs)
640 640 mapping[abs] = rel, exact
641 641 if repo.ui.verbose or not exact:
642 642 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
643 643 if repo.dirstate.state(abs) != 'r' and not util.lexists(target):
644 644 remove.append(abs)
645 645 mapping[abs] = rel, exact
646 646 if repo.ui.verbose or not exact:
647 647 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
648 648 if not dry_run:
649 649 repo.add(add, wlock=wlock)
650 650 repo.remove(remove, wlock=wlock)
651 651 if similarity > 0:
652 652 for old, new, score in findrenames(repo, add, remove, similarity):
653 653 oldrel, oldexact = mapping[old]
654 654 newrel, newexact = mapping[new]
655 655 if repo.ui.verbose or not oldexact or not newexact:
656 656 repo.ui.status(_('recording removal of %s as rename to %s '
657 657 '(%d%% similar)\n') %
658 658 (oldrel, newrel, score * 100))
659 659 if not dry_run:
660 660 repo.copy(old, new, wlock=wlock)
661 661
662 662 def service(opts, parentfn=None, initfn=None, runfn=None):
663 663 '''Run a command as a service.'''
664 664
665 665 if opts['daemon'] and not opts['daemon_pipefds']:
666 666 rfd, wfd = os.pipe()
667 667 args = sys.argv[:]
668 668 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
669 669 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
670 670 args[0], args)
671 671 os.close(wfd)
672 672 os.read(rfd, 1)
673 673 if parentfn:
674 674 return parentfn(pid)
675 675 else:
676 676 os._exit(0)
677 677
678 678 if initfn:
679 679 initfn()
680 680
681 681 if opts['pid_file']:
682 682 fp = open(opts['pid_file'], 'w')
683 683 fp.write(str(os.getpid()) + '\n')
684 684 fp.close()
685 685
686 686 if opts['daemon_pipefds']:
687 687 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
688 688 os.close(rfd)
689 689 try:
690 690 os.setsid()
691 691 except AttributeError:
692 692 pass
693 693 os.write(wfd, 'y')
694 694 os.close(wfd)
695 695 sys.stdout.flush()
696 696 sys.stderr.flush()
697 697 fd = os.open(util.nulldev, os.O_RDWR)
698 698 if fd != 0: os.dup2(fd, 0)
699 699 if fd != 1: os.dup2(fd, 1)
700 700 if fd != 2: os.dup2(fd, 2)
701 701 if fd not in (0, 1, 2): os.close(fd)
702 702
703 703 if runfn:
704 704 return runfn()
705 705
706 706 class changeset_printer(object):
707 707 '''show changeset information when templating not requested.'''
708 708
709 709 def __init__(self, ui, repo, patch, buffered):
710 710 self.ui = ui
711 711 self.repo = repo
712 712 self.buffered = buffered
713 713 self.patch = patch
714 714 self.header = {}
715 715 self.hunk = {}
716 716 self.lastheader = None
717 717
718 718 def flush(self, rev):
719 719 if rev in self.header:
720 720 h = self.header[rev]
721 721 if h != self.lastheader:
722 722 self.lastheader = h
723 723 self.ui.write(h)
724 724 del self.header[rev]
725 725 if rev in self.hunk:
726 726 self.ui.write(self.hunk[rev])
727 727 del self.hunk[rev]
728 728 return 1
729 729 return 0
730 730
731 731 def show(self, rev=0, changenode=None, copies=(), **props):
732 732 if self.buffered:
733 733 self.ui.pushbuffer()
734 734 self._show(rev, changenode, copies, props)
735 735 self.hunk[rev] = self.ui.popbuffer()
736 736 else:
737 737 self._show(rev, changenode, copies, props)
738 738
739 739 def _show(self, rev, changenode, copies, props):
740 740 '''show a single changeset or file revision'''
741 741 log = self.repo.changelog
742 742 if changenode is None:
743 743 changenode = log.node(rev)
744 744 elif not rev:
745 745 rev = log.rev(changenode)
746 746
747 747 if self.ui.quiet:
748 748 self.ui.write("%d:%s\n" % (rev, short(changenode)))
749 749 return
750 750
751 751 changes = log.read(changenode)
752 752 date = util.datestr(changes[2])
753 753 extra = changes[5]
754 754 branch = extra.get("branch")
755 755
756 756 hexfunc = self.ui.debugflag and hex or short
757 757
758 758 parents = [(p, hexfunc(log.node(p)))
759 759 for p in self._meaningful_parentrevs(log, rev)]
760 760
761 761 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
762 762
763 763 # don't show the default branch name
764 764 if branch != 'default':
765 765 branch = util.tolocal(branch)
766 766 self.ui.write(_("branch: %s\n") % branch)
767 767 for tag in self.repo.nodetags(changenode):
768 768 self.ui.write(_("tag: %s\n") % tag)
769 769 for parent in parents:
770 770 self.ui.write(_("parent: %d:%s\n") % parent)
771 771
772 772 if self.ui.debugflag:
773 773 self.ui.write(_("manifest: %d:%s\n") %
774 774 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
775 775 self.ui.write(_("user: %s\n") % changes[1])
776 776 self.ui.write(_("date: %s\n") % date)
777 777
778 778 if self.ui.debugflag:
779 779 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
780 780 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
781 781 files):
782 782 if value:
783 783 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
784 784 elif changes[3] and self.ui.verbose:
785 785 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
786 786 if copies and self.ui.verbose:
787 787 copies = ['%s (%s)' % c for c in copies]
788 788 self.ui.write(_("copies: %s\n") % ' '.join(copies))
789 789
790 790 if extra and self.ui.debugflag:
791 791 extraitems = extra.items()
792 792 extraitems.sort()
793 793 for key, value in extraitems:
794 794 self.ui.write(_("extra: %s=%s\n")
795 795 % (key, value.encode('string_escape')))
796 796
797 797 description = changes[4].strip()
798 798 if description:
799 799 if self.ui.verbose:
800 800 self.ui.write(_("description:\n"))
801 801 self.ui.write(description)
802 802 self.ui.write("\n\n")
803 803 else:
804 804 self.ui.write(_("summary: %s\n") %
805 805 description.splitlines()[0])
806 806 self.ui.write("\n")
807 807
808 808 self.showpatch(changenode)
809 809
810 810 def showpatch(self, node):
811 811 if self.patch:
812 812 prev = self.repo.changelog.parents(node)[0]
813 813 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
814 814 opts=patch.diffopts(self.ui))
815 815 self.ui.write("\n")
816 816
817 817 def _meaningful_parentrevs(self, log, rev):
818 818 """Return list of meaningful (or all if debug) parentrevs for rev.
819 819
820 820 For merges (two non-nullrev revisions) both parents are meaningful.
821 821 Otherwise the first parent revision is considered meaningful if it
822 822 is not the preceding revision.
823 823 """
824 824 parents = log.parentrevs(rev)
825 825 if not self.ui.debugflag and parents[1] == nullrev:
826 826 if parents[0] >= rev - 1:
827 827 parents = []
828 828 else:
829 829 parents = [parents[0]]
830 830 return parents
831 831
832 832
833 833 class changeset_templater(changeset_printer):
834 834 '''format changeset information.'''
835 835
836 836 def __init__(self, ui, repo, patch, mapfile, buffered):
837 837 changeset_printer.__init__(self, ui, repo, patch, buffered)
838 838 filters = templater.common_filters.copy()
839 839 filters['formatnode'] = (ui.debugflag and (lambda x: x)
840 840 or (lambda x: x[:12]))
841 841 self.t = templater.templater(mapfile, filters,
842 842 cache={
843 843 'parent': '{rev}:{node|formatnode} ',
844 844 'manifest': '{rev}:{node|formatnode}',
845 845 'filecopy': '{name} ({source})'})
846 846
847 847 def use_template(self, t):
848 848 '''set template string to use'''
849 849 self.t.cache['changeset'] = t
850 850
851 851 def _show(self, rev, changenode, copies, props):
852 852 '''show a single changeset or file revision'''
853 853 log = self.repo.changelog
854 854 if changenode is None:
855 855 changenode = log.node(rev)
856 856 elif not rev:
857 857 rev = log.rev(changenode)
858 858
859 859 changes = log.read(changenode)
860 860
861 861 def showlist(name, values, plural=None, **args):
862 862 '''expand set of values.
863 863 name is name of key in template map.
864 864 values is list of strings or dicts.
865 865 plural is plural of name, if not simply name + 's'.
866 866
867 867 expansion works like this, given name 'foo'.
868 868
869 869 if values is empty, expand 'no_foos'.
870 870
871 871 if 'foo' not in template map, return values as a string,
872 872 joined by space.
873 873
874 874 expand 'start_foos'.
875 875
876 876 for each value, expand 'foo'. if 'last_foo' in template
877 877 map, expand it instead of 'foo' for last key.
878 878
879 879 expand 'end_foos'.
880 880 '''
881 881 if plural: names = plural
882 882 else: names = name + 's'
883 883 if not values:
884 884 noname = 'no_' + names
885 885 if noname in self.t:
886 886 yield self.t(noname, **args)
887 887 return
888 888 if name not in self.t:
889 889 if isinstance(values[0], str):
890 890 yield ' '.join(values)
891 891 else:
892 892 for v in values:
893 893 yield dict(v, **args)
894 894 return
895 895 startname = 'start_' + names
896 896 if startname in self.t:
897 897 yield self.t(startname, **args)
898 898 vargs = args.copy()
899 899 def one(v, tag=name):
900 900 try:
901 901 vargs.update(v)
902 902 except (AttributeError, ValueError):
903 903 try:
904 904 for a, b in v:
905 905 vargs[a] = b
906 906 except ValueError:
907 907 vargs[name] = v
908 908 return self.t(tag, **vargs)
909 909 lastname = 'last_' + name
910 910 if lastname in self.t:
911 911 last = values.pop()
912 912 else:
913 913 last = None
914 914 for v in values:
915 915 yield one(v)
916 916 if last is not None:
917 917 yield one(last, tag=lastname)
918 918 endname = 'end_' + names
919 919 if endname in self.t:
920 920 yield self.t(endname, **args)
921 921
922 922 def showbranches(**args):
923 923 branch = changes[5].get("branch")
924 924 if branch != 'default':
925 925 branch = util.tolocal(branch)
926 926 return showlist('branch', [branch], plural='branches', **args)
927 927
928 928 def showparents(**args):
929 929 parents = [[('rev', p), ('node', hex(log.node(p)))]
930 930 for p in self._meaningful_parentrevs(log, rev)]
931 931 return showlist('parent', parents, **args)
932 932
933 933 def showtags(**args):
934 934 return showlist('tag', self.repo.nodetags(changenode), **args)
935 935
936 936 def showextras(**args):
937 937 extras = changes[5].items()
938 938 extras.sort()
939 939 for key, value in extras:
940 940 args = args.copy()
941 941 args.update(dict(key=key, value=value))
942 942 yield self.t('extra', **args)
943 943
944 944 def showcopies(**args):
945 945 c = [{'name': x[0], 'source': x[1]} for x in copies]
946 946 return showlist('file_copy', c, plural='file_copies', **args)
947 947
948 948 if self.ui.debugflag:
949 949 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
950 950 def showfiles(**args):
951 951 return showlist('file', files[0], **args)
952 952 def showadds(**args):
953 953 return showlist('file_add', files[1], **args)
954 954 def showdels(**args):
955 955 return showlist('file_del', files[2], **args)
956 956 def showmanifest(**args):
957 957 args = args.copy()
958 958 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
959 959 node=hex(changes[0])))
960 960 return self.t('manifest', **args)
961 961 else:
962 962 def showfiles(**args):
963 963 return showlist('file', changes[3], **args)
964 964 showadds = ''
965 965 showdels = ''
966 966 showmanifest = ''
967 967
968 968 defprops = {
969 969 'author': changes[1],
970 970 'branches': showbranches,
971 971 'date': changes[2],
972 972 'desc': changes[4].strip(),
973 973 'file_adds': showadds,
974 974 'file_dels': showdels,
975 975 'files': showfiles,
976 976 'file_copies': showcopies,
977 977 'manifest': showmanifest,
978 978 'node': hex(changenode),
979 979 'parents': showparents,
980 980 'rev': rev,
981 981 'tags': showtags,
982 982 'extras': showextras,
983 983 }
984 984 props = props.copy()
985 985 props.update(defprops)
986 986
987 987 try:
988 988 if self.ui.debugflag and 'header_debug' in self.t:
989 989 key = 'header_debug'
990 990 elif self.ui.quiet and 'header_quiet' in self.t:
991 991 key = 'header_quiet'
992 992 elif self.ui.verbose and 'header_verbose' in self.t:
993 993 key = 'header_verbose'
994 994 elif 'header' in self.t:
995 995 key = 'header'
996 996 else:
997 997 key = ''
998 998 if key:
999 999 h = templater.stringify(self.t(key, **props))
1000 1000 if self.buffered:
1001 1001 self.header[rev] = h
1002 1002 else:
1003 1003 self.ui.write(h)
1004 1004 if self.ui.debugflag and 'changeset_debug' in self.t:
1005 1005 key = 'changeset_debug'
1006 1006 elif self.ui.quiet and 'changeset_quiet' in self.t:
1007 1007 key = 'changeset_quiet'
1008 1008 elif self.ui.verbose and 'changeset_verbose' in self.t:
1009 1009 key = 'changeset_verbose'
1010 1010 else:
1011 1011 key = 'changeset'
1012 1012 self.ui.write(templater.stringify(self.t(key, **props)))
1013 1013 self.showpatch(changenode)
1014 1014 except KeyError, inst:
1015 1015 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
1016 1016 inst.args[0]))
1017 1017 except SyntaxError, inst:
1018 1018 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
1019 1019
1020 1020 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
1021 1021 """show one changeset using template or regular display.
1022 1022
1023 1023 Display format will be the first non-empty hit of:
1024 1024 1. option 'template'
1025 1025 2. option 'style'
1026 1026 3. [ui] setting 'logtemplate'
1027 1027 4. [ui] setting 'style'
1028 1028 If all of these values are either the unset or the empty string,
1029 1029 regular display via changeset_printer() is done.
1030 1030 """
1031 1031 # options
1032 1032 patch = False
1033 1033 if opts.get('patch'):
1034 1034 patch = matchfn or util.always
1035 1035
1036 1036 tmpl = opts.get('template')
1037 1037 mapfile = None
1038 1038 if tmpl:
1039 1039 tmpl = templater.parsestring(tmpl, quoted=False)
1040 1040 else:
1041 1041 mapfile = opts.get('style')
1042 1042 # ui settings
1043 1043 if not mapfile:
1044 1044 tmpl = ui.config('ui', 'logtemplate')
1045 1045 if tmpl:
1046 1046 tmpl = templater.parsestring(tmpl)
1047 1047 else:
1048 1048 mapfile = ui.config('ui', 'style')
1049 1049
1050 1050 if tmpl or mapfile:
1051 1051 if mapfile:
1052 1052 if not os.path.split(mapfile)[0]:
1053 1053 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1054 1054 or templater.templatepath(mapfile))
1055 1055 if mapname: mapfile = mapname
1056 1056 try:
1057 1057 t = changeset_templater(ui, repo, patch, mapfile, buffered)
1058 1058 except SyntaxError, inst:
1059 1059 raise util.Abort(inst.args[0])
1060 1060 if tmpl: t.use_template(tmpl)
1061 1061 return t
1062 1062 return changeset_printer(ui, repo, patch, buffered)
1063 1063
1064 1064 def finddate(ui, repo, date):
1065 1065 """Find the tipmost changeset that matches the given date spec"""
1066 1066 df = util.matchdate(date + " to " + date)
1067 1067 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1068 1068 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
1069 1069 results = {}
1070 1070 for st, rev, fns in changeiter:
1071 1071 if st == 'add':
1072 1072 d = get(rev)[2]
1073 1073 if df(d[0]):
1074 1074 results[rev] = d
1075 1075 elif st == 'iter':
1076 1076 if rev in results:
1077 1077 ui.status("Found revision %s from %s\n" %
1078 1078 (rev, util.datestr(results[rev])))
1079 1079 return str(rev)
1080 1080
1081 1081 raise util.Abort(_("revision matching date not found"))
1082 1082
1083 1083 def walkchangerevs(ui, repo, pats, change, opts):
1084 1084 '''Iterate over files and the revs they changed in.
1085 1085
1086 1086 Callers most commonly need to iterate backwards over the history
1087 1087 it is interested in. Doing so has awful (quadratic-looking)
1088 1088 performance, so we use iterators in a "windowed" way.
1089 1089
1090 1090 We walk a window of revisions in the desired order. Within the
1091 1091 window, we first walk forwards to gather data, then in the desired
1092 1092 order (usually backwards) to display it.
1093 1093
1094 1094 This function returns an (iterator, matchfn) tuple. The iterator
1095 1095 yields 3-tuples. They will be of one of the following forms:
1096 1096
1097 1097 "window", incrementing, lastrev: stepping through a window,
1098 1098 positive if walking forwards through revs, last rev in the
1099 1099 sequence iterated over - use to reset state for the current window
1100 1100
1101 1101 "add", rev, fns: out-of-order traversal of the given file names
1102 1102 fns, which changed during revision rev - use to gather data for
1103 1103 possible display
1104 1104
1105 1105 "iter", rev, None: in-order traversal of the revs earlier iterated
1106 1106 over with "add" - use to display data'''
1107 1107
1108 1108 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1109 1109 if start < end:
1110 1110 while start < end:
1111 1111 yield start, min(windowsize, end-start)
1112 1112 start += windowsize
1113 1113 if windowsize < sizelimit:
1114 1114 windowsize *= 2
1115 1115 else:
1116 1116 while start > end:
1117 1117 yield start, min(windowsize, start-end-1)
1118 1118 start -= windowsize
1119 1119 if windowsize < sizelimit:
1120 1120 windowsize *= 2
1121 1121
1122 1122 files, matchfn, anypats = matchpats(repo, pats, opts)
1123 1123 follow = opts.get('follow') or opts.get('follow_first')
1124 1124
1125 1125 if repo.changelog.count() == 0:
1126 1126 return [], matchfn
1127 1127
1128 1128 if follow:
1129 1129 defrange = '%s:0' % repo.changectx().rev()
1130 1130 else:
1131 1131 defrange = 'tip:0'
1132 1132 revs = revrange(repo, opts['rev'] or [defrange])
1133 1133 wanted = {}
1134 1134 slowpath = anypats or opts.get('removed')
1135 1135 fncache = {}
1136 1136
1137 1137 if not slowpath and not files:
1138 1138 # No files, no patterns. Display all revs.
1139 1139 wanted = dict.fromkeys(revs)
1140 1140 copies = []
1141 1141 if not slowpath:
1142 1142 # Only files, no patterns. Check the history of each file.
1143 1143 def filerevgen(filelog, node):
1144 1144 cl_count = repo.changelog.count()
1145 1145 if node is None:
1146 1146 last = filelog.count() - 1
1147 1147 else:
1148 1148 last = filelog.rev(node)
1149 1149 for i, window in increasing_windows(last, nullrev):
1150 1150 revs = []
1151 1151 for j in xrange(i - window, i + 1):
1152 1152 n = filelog.node(j)
1153 1153 revs.append((filelog.linkrev(n),
1154 1154 follow and filelog.renamed(n)))
1155 1155 revs.reverse()
1156 1156 for rev in revs:
1157 1157 # only yield rev for which we have the changelog, it can
1158 1158 # happen while doing "hg log" during a pull or commit
1159 1159 if rev[0] < cl_count:
1160 1160 yield rev
1161 1161 def iterfiles():
1162 1162 for filename in files:
1163 1163 yield filename, None
1164 1164 for filename_node in copies:
1165 1165 yield filename_node
1166 1166 minrev, maxrev = min(revs), max(revs)
1167 1167 for file_, node in iterfiles():
1168 1168 filelog = repo.file(file_)
1169 1169 # A zero count may be a directory or deleted file, so
1170 1170 # try to find matching entries on the slow path.
1171 1171 if filelog.count() == 0:
1172 1172 slowpath = True
1173 1173 break
1174 1174 for rev, copied in filerevgen(filelog, node):
1175 1175 if rev <= maxrev:
1176 1176 if rev < minrev:
1177 1177 break
1178 1178 fncache.setdefault(rev, [])
1179 1179 fncache[rev].append(file_)
1180 1180 wanted[rev] = 1
1181 1181 if follow and copied:
1182 1182 copies.append(copied)
1183 1183 if slowpath:
1184 1184 if follow:
1185 1185 raise util.Abort(_('can only follow copies/renames for explicit '
1186 1186 'file names'))
1187 1187
1188 1188 # The slow path checks files modified in every changeset.
1189 1189 def changerevgen():
1190 1190 for i, window in increasing_windows(repo.changelog.count()-1,
1191 1191 nullrev):
1192 1192 for j in xrange(i - window, i + 1):
1193 1193 yield j, change(j)[3]
1194 1194
1195 1195 for rev, changefiles in changerevgen():
1196 1196 matches = filter(matchfn, changefiles)
1197 1197 if matches:
1198 1198 fncache[rev] = matches
1199 1199 wanted[rev] = 1
1200 1200
1201 1201 class followfilter:
1202 1202 def __init__(self, onlyfirst=False):
1203 1203 self.startrev = nullrev
1204 1204 self.roots = []
1205 1205 self.onlyfirst = onlyfirst
1206 1206
1207 1207 def match(self, rev):
1208 1208 def realparents(rev):
1209 1209 if self.onlyfirst:
1210 1210 return repo.changelog.parentrevs(rev)[0:1]
1211 1211 else:
1212 1212 return filter(lambda x: x != nullrev,
1213 1213 repo.changelog.parentrevs(rev))
1214 1214
1215 1215 if self.startrev == nullrev:
1216 1216 self.startrev = rev
1217 1217 return True
1218 1218
1219 1219 if rev > self.startrev:
1220 1220 # forward: all descendants
1221 1221 if not self.roots:
1222 1222 self.roots.append(self.startrev)
1223 1223 for parent in realparents(rev):
1224 1224 if parent in self.roots:
1225 1225 self.roots.append(rev)
1226 1226 return True
1227 1227 else:
1228 1228 # backwards: all parents
1229 1229 if not self.roots:
1230 1230 self.roots.extend(realparents(self.startrev))
1231 1231 if rev in self.roots:
1232 1232 self.roots.remove(rev)
1233 1233 self.roots.extend(realparents(rev))
1234 1234 return True
1235 1235
1236 1236 return False
1237 1237
1238 1238 # it might be worthwhile to do this in the iterator if the rev range
1239 1239 # is descending and the prune args are all within that range
1240 1240 for rev in opts.get('prune', ()):
1241 1241 rev = repo.changelog.rev(repo.lookup(rev))
1242 1242 ff = followfilter()
1243 1243 stop = min(revs[0], revs[-1])
1244 1244 for x in xrange(rev, stop-1, -1):
1245 1245 if ff.match(x) and x in wanted:
1246 1246 del wanted[x]
1247 1247
1248 1248 def iterate():
1249 1249 if follow and not files:
1250 1250 ff = followfilter(onlyfirst=opts.get('follow_first'))
1251 1251 def want(rev):
1252 1252 if ff.match(rev) and rev in wanted:
1253 1253 return True
1254 1254 return False
1255 1255 else:
1256 1256 def want(rev):
1257 1257 return rev in wanted
1258 1258
1259 1259 for i, window in increasing_windows(0, len(revs)):
1260 1260 yield 'window', revs[0] < revs[-1], revs[-1]
1261 1261 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1262 1262 srevs = list(nrevs)
1263 1263 srevs.sort()
1264 1264 for rev in srevs:
1265 1265 fns = fncache.get(rev)
1266 1266 if not fns:
1267 1267 def fns_generator():
1268 1268 for f in change(rev)[3]:
1269 1269 if matchfn(f):
1270 1270 yield f
1271 1271 fns = fns_generator()
1272 1272 yield 'add', rev, fns
1273 1273 for rev in nrevs:
1274 1274 yield 'iter', rev, None
1275 1275 return iterate(), matchfn
@@ -1,3142 +1,3144
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import demandimport; demandimport.enable()
9 9 from node import *
10 10 from i18n import _
11 11 import bisect, os, re, sys, urllib, shlex, stat
12 12 import ui, hg, util, revlog, bundlerepo, extensions
13 13 import difflib, patch, time, help, mdiff, tempfile
14 14 import errno, version, socket
15 15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16 16
17 17 # Commands start here, listed alphabetically
18 18
19 19 def add(ui, repo, *pats, **opts):
20 20 """add the specified files on the next commit
21 21
22 22 Schedule files to be version controlled and added to the repository.
23 23
24 24 The files will be added to the repository at the next commit. To
25 25 undo an add before that, see hg revert.
26 26
27 27 If no names are given, add all files in the repository.
28 28 """
29 29
30 30 names = []
31 31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
32 32 if exact:
33 33 if ui.verbose:
34 34 ui.status(_('adding %s\n') % rel)
35 35 names.append(abs)
36 36 elif repo.dirstate.state(abs) == '?':
37 37 ui.status(_('adding %s\n') % rel)
38 38 names.append(abs)
39 39 if not opts.get('dry_run'):
40 40 repo.add(names)
41 41
42 42 def addremove(ui, repo, *pats, **opts):
43 43 """add all new files, delete all missing files
44 44
45 45 Add all new files and remove all missing files from the repository.
46 46
47 47 New files are ignored if they match any of the patterns in .hgignore. As
48 48 with add, these changes take effect at the next commit.
49 49
50 50 Use the -s option to detect renamed files. With a parameter > 0,
51 51 this compares every removed file with every added file and records
52 52 those similar enough as renames. This option takes a percentage
53 53 between 0 (disabled) and 100 (files must be identical) as its
54 54 parameter. Detecting renamed files this way can be expensive.
55 55 """
56 56 try:
57 57 sim = float(opts.get('similarity') or 0)
58 58 except ValueError:
59 59 raise util.Abort(_('similarity must be a number'))
60 60 if sim < 0 or sim > 100:
61 61 raise util.Abort(_('similarity must be between 0 and 100'))
62 62 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
63 63
64 64 def annotate(ui, repo, *pats, **opts):
65 65 """show changeset information per file line
66 66
67 67 List changes in files, showing the revision id responsible for each line
68 68
69 69 This command is useful to discover who did a change or when a change took
70 70 place.
71 71
72 72 Without the -a option, annotate will avoid processing files it
73 73 detects as binary. With -a, annotate will generate an annotation
74 74 anyway, probably with undesirable results.
75 75 """
76 76 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
77 77
78 78 if not pats:
79 79 raise util.Abort(_('at least one file name or pattern required'))
80 80
81 81 opmap = [['user', lambda x: ui.shortuser(x.user())],
82 82 ['number', lambda x: str(x.rev())],
83 83 ['changeset', lambda x: short(x.node())],
84 84 ['date', getdate], ['follow', lambda x: x.path()]]
85 85 if (not opts['user'] and not opts['changeset'] and not opts['date']
86 86 and not opts['follow']):
87 87 opts['number'] = 1
88 88
89 89 ctx = repo.changectx(opts['rev'])
90 90
91 91 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
92 92 node=ctx.node()):
93 93 fctx = ctx.filectx(abs)
94 94 if not opts['text'] and util.binary(fctx.data()):
95 95 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
96 96 continue
97 97
98 98 lines = fctx.annotate(follow=opts.get('follow'))
99 99 pieces = []
100 100
101 101 for o, f in opmap:
102 102 if opts[o]:
103 103 l = [f(n) for n, dummy in lines]
104 104 if l:
105 105 m = max(map(len, l))
106 106 pieces.append(["%*s" % (m, x) for x in l])
107 107
108 108 if pieces:
109 109 for p, l in zip(zip(*pieces), lines):
110 110 ui.write("%s: %s" % (" ".join(p), l[1]))
111 111
112 112 def archive(ui, repo, dest, **opts):
113 113 '''create unversioned archive of a repository revision
114 114
115 115 By default, the revision used is the parent of the working
116 116 directory; use "-r" to specify a different revision.
117 117
118 118 To specify the type of archive to create, use "-t". Valid
119 119 types are:
120 120
121 121 "files" (default): a directory full of files
122 122 "tar": tar archive, uncompressed
123 123 "tbz2": tar archive, compressed using bzip2
124 124 "tgz": tar archive, compressed using gzip
125 125 "uzip": zip archive, uncompressed
126 126 "zip": zip archive, compressed using deflate
127 127
128 128 The exact name of the destination archive or directory is given
129 129 using a format string; see "hg help export" for details.
130 130
131 131 Each member added to an archive file has a directory prefix
132 132 prepended. Use "-p" to specify a format string for the prefix.
133 133 The default is the basename of the archive, with suffixes removed.
134 134 '''
135 135
136 136 ctx = repo.changectx(opts['rev'])
137 137 if not ctx:
138 138 raise util.Abort(_('repository has no revisions'))
139 139 node = ctx.node()
140 140 dest = cmdutil.make_filename(repo, dest, node)
141 141 if os.path.realpath(dest) == repo.root:
142 142 raise util.Abort(_('repository root cannot be destination'))
143 143 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
144 144 kind = opts.get('type') or 'files'
145 145 prefix = opts['prefix']
146 146 if dest == '-':
147 147 if kind == 'files':
148 148 raise util.Abort(_('cannot archive plain files to stdout'))
149 149 dest = sys.stdout
150 150 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
151 151 prefix = cmdutil.make_filename(repo, prefix, node)
152 152 archival.archive(repo, dest, node, kind, not opts['no_decode'],
153 153 matchfn, prefix)
154 154
155 155 def backout(ui, repo, node=None, rev=None, **opts):
156 156 '''reverse effect of earlier changeset
157 157
158 158 Commit the backed out changes as a new changeset. The new
159 159 changeset is a child of the backed out changeset.
160 160
161 161 If you back out a changeset other than the tip, a new head is
162 162 created. This head is the parent of the working directory. If
163 163 you back out an old changeset, your working directory will appear
164 164 old after the backout. You should merge the backout changeset
165 165 with another head.
166 166
167 167 The --merge option remembers the parent of the working directory
168 168 before starting the backout, then merges the new head with that
169 169 changeset afterwards. This saves you from doing the merge by
170 170 hand. The result of this merge is not committed, as for a normal
171 171 merge.'''
172 172 if rev and node:
173 173 raise util.Abort(_("please specify just one revision"))
174 174
175 175 if not rev:
176 176 rev = node
177 177
178 178 if not rev:
179 179 raise util.Abort(_("please specify a revision to backout"))
180 180
181 181 cmdutil.bail_if_changed(repo)
182 182 op1, op2 = repo.dirstate.parents()
183 183 if op2 != nullid:
184 184 raise util.Abort(_('outstanding uncommitted merge'))
185 185 node = repo.lookup(rev)
186 186 p1, p2 = repo.changelog.parents(node)
187 187 if p1 == nullid:
188 188 raise util.Abort(_('cannot back out a change with no parents'))
189 189 if p2 != nullid:
190 190 if not opts['parent']:
191 191 raise util.Abort(_('cannot back out a merge changeset without '
192 192 '--parent'))
193 193 p = repo.lookup(opts['parent'])
194 194 if p not in (p1, p2):
195 195 raise util.Abort(_('%s is not a parent of %s') %
196 196 (short(p), short(node)))
197 197 parent = p
198 198 else:
199 199 if opts['parent']:
200 200 raise util.Abort(_('cannot use --parent on non-merge changeset'))
201 201 parent = p1
202 202 hg.clean(repo, node, show_stats=False)
203 203 revert_opts = opts.copy()
204 204 revert_opts['date'] = None
205 205 revert_opts['all'] = True
206 206 revert_opts['rev'] = hex(parent)
207 207 revert(ui, repo, **revert_opts)
208 208 commit_opts = opts.copy()
209 209 commit_opts['addremove'] = False
210 210 if not commit_opts['message'] and not commit_opts['logfile']:
211 211 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
212 212 commit_opts['force_editor'] = True
213 213 commit(ui, repo, **commit_opts)
214 214 def nice(node):
215 215 return '%d:%s' % (repo.changelog.rev(node), short(node))
216 216 ui.status(_('changeset %s backs out changeset %s\n') %
217 217 (nice(repo.changelog.tip()), nice(node)))
218 218 if op1 != node:
219 219 if opts['merge']:
220 220 ui.status(_('merging with changeset %s\n') % nice(op1))
221 221 hg.merge(repo, hex(op1))
222 222 else:
223 223 ui.status(_('the backout changeset is a new head - '
224 224 'do not forget to merge\n'))
225 225 ui.status(_('(use "backout --merge" '
226 226 'if you want to auto-merge)\n'))
227 227
228 228 def branch(ui, repo, label=None, **opts):
229 229 """set or show the current branch name
230 230
231 231 With no argument, show the current branch name. With one argument,
232 232 set the working directory branch name (the branch does not exist in
233 233 the repository until the next commit).
234 234
235 235 Unless --force is specified, branch will not let you set a
236 236 branch name that shadows an existing branch.
237 237 """
238 238
239 239 if label:
240 240 if not opts.get('force') and label in repo.branchtags():
241 241 if label not in [p.branch() for p in repo.workingctx().parents()]:
242 242 raise util.Abort(_('a branch of the same name already exists'
243 243 ' (use --force to override)'))
244 244 repo.dirstate.setbranch(util.fromlocal(label))
245 245 ui.status(_('marked working directory as branch %s\n') % label)
246 246 else:
247 247 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
248 248
249 249 def branches(ui, repo, active=False):
250 250 """list repository named branches
251 251
252 252 List the repository's named branches, indicating which ones are
253 253 inactive. If active is specified, only show active branches.
254 254
255 255 A branch is considered active if it contains unmerged heads.
256 256 """
257 257 b = repo.branchtags()
258 258 heads = dict.fromkeys(repo.heads(), 1)
259 259 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
260 260 l.sort()
261 261 l.reverse()
262 262 for ishead, r, n, t in l:
263 263 if active and not ishead:
264 264 # If we're only displaying active branches, abort the loop on
265 265 # encountering the first inactive head
266 266 break
267 267 else:
268 268 hexfunc = ui.debugflag and hex or short
269 269 if ui.quiet:
270 270 ui.write("%s\n" % t)
271 271 else:
272 272 spaces = " " * (30 - util.locallen(t))
273 273 # The code only gets here if inactive branches are being
274 274 # displayed or the branch is active.
275 275 isinactive = ((not ishead) and " (inactive)") or ''
276 276 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
277 277
278 278 def bundle(ui, repo, fname, dest=None, **opts):
279 279 """create a changegroup file
280 280
281 281 Generate a compressed changegroup file collecting changesets not
282 282 found in the other repository.
283 283
284 284 If no destination repository is specified the destination is assumed
285 285 to have all the nodes specified by one or more --base parameters.
286 286
287 287 The bundle file can then be transferred using conventional means and
288 288 applied to another repository with the unbundle or pull command.
289 289 This is useful when direct push and pull are not available or when
290 290 exporting an entire repository is undesirable.
291 291
292 292 Applying bundles preserves all changeset contents including
293 293 permissions, copy/rename information, and revision history.
294 294 """
295 295 revs = opts.get('rev') or None
296 296 if revs:
297 297 revs = [repo.lookup(rev) for rev in revs]
298 298 base = opts.get('base')
299 299 if base:
300 300 if dest:
301 301 raise util.Abort(_("--base is incompatible with specifiying "
302 302 "a destination"))
303 303 base = [repo.lookup(rev) for rev in base]
304 304 # create the right base
305 305 # XXX: nodesbetween / changegroup* should be "fixed" instead
306 306 o = []
307 307 has = {nullid: None}
308 308 for n in base:
309 309 has.update(repo.changelog.reachable(n))
310 310 if revs:
311 311 visit = list(revs)
312 312 else:
313 313 visit = repo.changelog.heads()
314 314 seen = {}
315 315 while visit:
316 316 n = visit.pop(0)
317 317 parents = [p for p in repo.changelog.parents(n) if p not in has]
318 318 if len(parents) == 0:
319 319 o.insert(0, n)
320 320 else:
321 321 for p in parents:
322 322 if p not in seen:
323 323 seen[p] = 1
324 324 visit.append(p)
325 325 else:
326 326 cmdutil.setremoteconfig(ui, opts)
327 dest, revs = cmdutil.parseurl(
327 dest, revs, checkout = cmdutil.parseurl(
328 328 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
329 329 other = hg.repository(ui, dest)
330 330 o = repo.findoutgoing(other, force=opts['force'])
331 331
332 332 if revs:
333 333 cg = repo.changegroupsubset(o, revs, 'bundle')
334 334 else:
335 335 cg = repo.changegroup(o, 'bundle')
336 336 changegroup.writebundle(cg, fname, "HG10BZ")
337 337
338 338 def cat(ui, repo, file1, *pats, **opts):
339 339 """output the current or given revision of files
340 340
341 341 Print the specified files as they were at the given revision.
342 342 If no revision is given, the parent of the working directory is used,
343 343 or tip if no revision is checked out.
344 344
345 345 Output may be to a file, in which case the name of the file is
346 346 given using a format string. The formatting rules are the same as
347 347 for the export command, with the following additions:
348 348
349 349 %s basename of file being printed
350 350 %d dirname of file being printed, or '.' if in repo root
351 351 %p root-relative path name of file being printed
352 352 """
353 353 ctx = repo.changectx(opts['rev'])
354 354 err = 1
355 355 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
356 356 ctx.node()):
357 357 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
358 358 fp.write(ctx.filectx(abs).data())
359 359 err = 0
360 360 return err
361 361
362 362 def clone(ui, source, dest=None, **opts):
363 363 """make a copy of an existing repository
364 364
365 365 Create a copy of an existing repository in a new directory.
366 366
367 367 If no destination directory name is specified, it defaults to the
368 368 basename of the source.
369 369
370 370 The location of the source is added to the new repository's
371 371 .hg/hgrc file, as the default to be used for future pulls.
372 372
373 373 For efficiency, hardlinks are used for cloning whenever the source
374 374 and destination are on the same filesystem (note this applies only
375 375 to the repository data, not to the checked out files). Some
376 376 filesystems, such as AFS, implement hardlinking incorrectly, but
377 377 do not report errors. In these cases, use the --pull option to
378 378 avoid hardlinking.
379 379
380 380 You can safely clone repositories and checked out files using full
381 381 hardlinks with
382 382
383 383 $ cp -al REPO REPOCLONE
384 384
385 385 which is the fastest way to clone. However, the operation is not
386 386 atomic (making sure REPO is not modified during the operation is
387 387 up to you) and you have to make sure your editor breaks hardlinks
388 388 (Emacs and most Linux Kernel tools do so).
389 389
390 390 If you use the -r option to clone up to a specific revision, no
391 391 subsequent revisions will be present in the cloned repository.
392 392 This option implies --pull, even on local repositories.
393 393
394 394 See pull for valid source format details.
395 395
396 396 It is possible to specify an ssh:// URL as the destination, but no
397 397 .hg/hgrc and working directory will be created on the remote side.
398 398 Look at the help text for the pull command for important details
399 399 about ssh:// URLs.
400 400 """
401 401 cmdutil.setremoteconfig(ui, opts)
402 402 hg.clone(ui, source, dest,
403 403 pull=opts['pull'],
404 404 stream=opts['uncompressed'],
405 405 rev=opts['rev'],
406 406 update=not opts['noupdate'])
407 407
408 408 def commit(ui, repo, *pats, **opts):
409 409 """commit the specified files or all outstanding changes
410 410
411 411 Commit changes to the given files into the repository.
412 412
413 413 If a list of files is omitted, all changes reported by "hg status"
414 414 will be committed.
415 415
416 416 If no commit message is specified, the editor configured in your hgrc
417 417 or in the EDITOR environment variable is started to enter a message.
418 418 """
419 419 message = cmdutil.logmessage(opts)
420 420
421 421 if opts['addremove']:
422 422 cmdutil.addremove(repo, pats, opts)
423 423 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
424 424 if pats:
425 425 status = repo.status(files=fns, match=match)
426 426 modified, added, removed, deleted, unknown = status[:5]
427 427 files = modified + added + removed
428 428 slist = None
429 429 for f in fns:
430 430 if f == '.':
431 431 continue
432 432 if f not in files:
433 433 rf = repo.wjoin(f)
434 434 try:
435 435 mode = os.lstat(rf)[stat.ST_MODE]
436 436 except OSError:
437 437 raise util.Abort(_("file %s not found!") % rf)
438 438 if stat.S_ISDIR(mode):
439 439 name = f + '/'
440 440 if slist is None:
441 441 slist = list(files)
442 442 slist.sort()
443 443 i = bisect.bisect(slist, name)
444 444 if i >= len(slist) or not slist[i].startswith(name):
445 445 raise util.Abort(_("no match under directory %s!")
446 446 % rf)
447 447 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
448 448 raise util.Abort(_("can't commit %s: "
449 449 "unsupported file type!") % rf)
450 450 elif repo.dirstate.state(f) == '?':
451 451 raise util.Abort(_("file %s not tracked!") % rf)
452 452 else:
453 453 files = []
454 454 try:
455 455 repo.commit(files, message, opts['user'], opts['date'], match,
456 456 force_editor=opts.get('force_editor'))
457 457 except ValueError, inst:
458 458 raise util.Abort(str(inst))
459 459
460 460 def docopy(ui, repo, pats, opts, wlock):
461 461 # called with the repo lock held
462 462 #
463 463 # hgsep => pathname that uses "/" to separate directories
464 464 # ossep => pathname that uses os.sep to separate directories
465 465 cwd = repo.getcwd()
466 466 errors = 0
467 467 copied = []
468 468 targets = {}
469 469
470 470 # abs: hgsep
471 471 # rel: ossep
472 472 # return: hgsep
473 473 def okaytocopy(abs, rel, exact):
474 474 reasons = {'?': _('is not managed'),
475 475 'r': _('has been marked for remove')}
476 476 state = repo.dirstate.state(abs)
477 477 reason = reasons.get(state)
478 478 if reason:
479 479 if exact:
480 480 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
481 481 else:
482 482 if state == 'a':
483 483 origsrc = repo.dirstate.copied(abs)
484 484 if origsrc is not None:
485 485 return origsrc
486 486 return abs
487 487
488 488 # origsrc: hgsep
489 489 # abssrc: hgsep
490 490 # relsrc: ossep
491 491 # otarget: ossep
492 492 def copy(origsrc, abssrc, relsrc, otarget, exact):
493 493 abstarget = util.canonpath(repo.root, cwd, otarget)
494 494 reltarget = repo.pathto(abstarget, cwd)
495 495 prevsrc = targets.get(abstarget)
496 496 src = repo.wjoin(abssrc)
497 497 target = repo.wjoin(abstarget)
498 498 if prevsrc is not None:
499 499 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
500 500 (reltarget, repo.pathto(abssrc, cwd),
501 501 repo.pathto(prevsrc, cwd)))
502 502 return
503 503 if (not opts['after'] and os.path.exists(target) or
504 504 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
505 505 if not opts['force']:
506 506 ui.warn(_('%s: not overwriting - file exists\n') %
507 507 reltarget)
508 508 return
509 509 if not opts['after'] and not opts.get('dry_run'):
510 510 os.unlink(target)
511 511 if opts['after']:
512 512 if not os.path.exists(target):
513 513 return
514 514 else:
515 515 targetdir = os.path.dirname(target) or '.'
516 516 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
517 517 os.makedirs(targetdir)
518 518 try:
519 519 restore = repo.dirstate.state(abstarget) == 'r'
520 520 if restore and not opts.get('dry_run'):
521 521 repo.undelete([abstarget], wlock)
522 522 try:
523 523 if not opts.get('dry_run'):
524 524 util.copyfile(src, target)
525 525 restore = False
526 526 finally:
527 527 if restore:
528 528 repo.remove([abstarget], wlock=wlock)
529 529 except IOError, inst:
530 530 if inst.errno == errno.ENOENT:
531 531 ui.warn(_('%s: deleted in working copy\n') % relsrc)
532 532 else:
533 533 ui.warn(_('%s: cannot copy - %s\n') %
534 534 (relsrc, inst.strerror))
535 535 errors += 1
536 536 return
537 537 if ui.verbose or not exact:
538 538 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
539 539 targets[abstarget] = abssrc
540 540 if abstarget != origsrc:
541 541 if repo.dirstate.state(origsrc) == 'a':
542 542 if not ui.quiet:
543 543 ui.warn(_("%s has not been committed yet, so no copy "
544 544 "data will be stored for %s.\n")
545 545 % (repo.pathto(origsrc, cwd), reltarget))
546 546 if abstarget not in repo.dirstate and not opts.get('dry_run'):
547 547 repo.add([abstarget], wlock)
548 548 elif not opts.get('dry_run'):
549 549 repo.copy(origsrc, abstarget, wlock)
550 550 copied.append((abssrc, relsrc, exact))
551 551
552 552 # pat: ossep
553 553 # dest ossep
554 554 # srcs: list of (hgsep, hgsep, ossep, bool)
555 555 # return: function that takes hgsep and returns ossep
556 556 def targetpathfn(pat, dest, srcs):
557 557 if os.path.isdir(pat):
558 558 abspfx = util.canonpath(repo.root, cwd, pat)
559 559 abspfx = util.localpath(abspfx)
560 560 if destdirexists:
561 561 striplen = len(os.path.split(abspfx)[0])
562 562 else:
563 563 striplen = len(abspfx)
564 564 if striplen:
565 565 striplen += len(os.sep)
566 566 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
567 567 elif destdirexists:
568 568 res = lambda p: os.path.join(dest,
569 569 os.path.basename(util.localpath(p)))
570 570 else:
571 571 res = lambda p: dest
572 572 return res
573 573
574 574 # pat: ossep
575 575 # dest ossep
576 576 # srcs: list of (hgsep, hgsep, ossep, bool)
577 577 # return: function that takes hgsep and returns ossep
578 578 def targetpathafterfn(pat, dest, srcs):
579 579 if util.patkind(pat, None)[0]:
580 580 # a mercurial pattern
581 581 res = lambda p: os.path.join(dest,
582 582 os.path.basename(util.localpath(p)))
583 583 else:
584 584 abspfx = util.canonpath(repo.root, cwd, pat)
585 585 if len(abspfx) < len(srcs[0][0]):
586 586 # A directory. Either the target path contains the last
587 587 # component of the source path or it does not.
588 588 def evalpath(striplen):
589 589 score = 0
590 590 for s in srcs:
591 591 t = os.path.join(dest, util.localpath(s[0])[striplen:])
592 592 if os.path.exists(t):
593 593 score += 1
594 594 return score
595 595
596 596 abspfx = util.localpath(abspfx)
597 597 striplen = len(abspfx)
598 598 if striplen:
599 599 striplen += len(os.sep)
600 600 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
601 601 score = evalpath(striplen)
602 602 striplen1 = len(os.path.split(abspfx)[0])
603 603 if striplen1:
604 604 striplen1 += len(os.sep)
605 605 if evalpath(striplen1) > score:
606 606 striplen = striplen1
607 607 res = lambda p: os.path.join(dest,
608 608 util.localpath(p)[striplen:])
609 609 else:
610 610 # a file
611 611 if destdirexists:
612 612 res = lambda p: os.path.join(dest,
613 613 os.path.basename(util.localpath(p)))
614 614 else:
615 615 res = lambda p: dest
616 616 return res
617 617
618 618
619 619 pats = util.expand_glob(pats)
620 620 if not pats:
621 621 raise util.Abort(_('no source or destination specified'))
622 622 if len(pats) == 1:
623 623 raise util.Abort(_('no destination specified'))
624 624 dest = pats.pop()
625 625 destdirexists = os.path.isdir(dest)
626 626 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
627 627 raise util.Abort(_('with multiple sources, destination must be an '
628 628 'existing directory'))
629 629 if opts['after']:
630 630 tfn = targetpathafterfn
631 631 else:
632 632 tfn = targetpathfn
633 633 copylist = []
634 634 for pat in pats:
635 635 srcs = []
636 636 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
637 637 globbed=True):
638 638 origsrc = okaytocopy(abssrc, relsrc, exact)
639 639 if origsrc:
640 640 srcs.append((origsrc, abssrc, relsrc, exact))
641 641 if not srcs:
642 642 continue
643 643 copylist.append((tfn(pat, dest, srcs), srcs))
644 644 if not copylist:
645 645 raise util.Abort(_('no files to copy'))
646 646
647 647 for targetpath, srcs in copylist:
648 648 for origsrc, abssrc, relsrc, exact in srcs:
649 649 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
650 650
651 651 if errors:
652 652 ui.warn(_('(consider using --after)\n'))
653 653 return errors, copied
654 654
655 655 def copy(ui, repo, *pats, **opts):
656 656 """mark files as copied for the next commit
657 657
658 658 Mark dest as having copies of source files. If dest is a
659 659 directory, copies are put in that directory. If dest is a file,
660 660 there can only be one source.
661 661
662 662 By default, this command copies the contents of files as they
663 663 stand in the working directory. If invoked with --after, the
664 664 operation is recorded, but no copying is performed.
665 665
666 666 This command takes effect in the next commit. To undo a copy
667 667 before that, see hg revert.
668 668 """
669 669 wlock = repo.wlock(0)
670 670 errs, copied = docopy(ui, repo, pats, opts, wlock)
671 671 return errs
672 672
673 673 def debugancestor(ui, index, rev1, rev2):
674 674 """find the ancestor revision of two revisions in a given index"""
675 675 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
676 676 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
677 677 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
678 678
679 679 def debugcomplete(ui, cmd='', **opts):
680 680 """returns the completion list associated with the given command"""
681 681
682 682 if opts['options']:
683 683 options = []
684 684 otables = [globalopts]
685 685 if cmd:
686 686 aliases, entry = cmdutil.findcmd(ui, cmd)
687 687 otables.append(entry[1])
688 688 for t in otables:
689 689 for o in t:
690 690 if o[0]:
691 691 options.append('-%s' % o[0])
692 692 options.append('--%s' % o[1])
693 693 ui.write("%s\n" % "\n".join(options))
694 694 return
695 695
696 696 clist = cmdutil.findpossible(ui, cmd).keys()
697 697 clist.sort()
698 698 ui.write("%s\n" % "\n".join(clist))
699 699
700 700 def debugrebuildstate(ui, repo, rev=""):
701 701 """rebuild the dirstate as it would look like for the given revision"""
702 702 if rev == "":
703 703 rev = repo.changelog.tip()
704 704 ctx = repo.changectx(rev)
705 705 files = ctx.manifest()
706 706 wlock = repo.wlock()
707 707 repo.dirstate.rebuild(rev, files)
708 708
709 709 def debugcheckstate(ui, repo):
710 710 """validate the correctness of the current dirstate"""
711 711 parent1, parent2 = repo.dirstate.parents()
712 712 dc = repo.dirstate
713 713 m1 = repo.changectx(parent1).manifest()
714 714 m2 = repo.changectx(parent2).manifest()
715 715 errors = 0
716 716 for f in dc:
717 717 state = repo.dirstate.state(f)
718 718 if state in "nr" and f not in m1:
719 719 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
720 720 errors += 1
721 721 if state in "a" and f in m1:
722 722 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
723 723 errors += 1
724 724 if state in "m" and f not in m1 and f not in m2:
725 725 ui.warn(_("%s in state %s, but not in either manifest\n") %
726 726 (f, state))
727 727 errors += 1
728 728 for f in m1:
729 729 state = repo.dirstate.state(f)
730 730 if state not in "nrm":
731 731 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
732 732 errors += 1
733 733 if errors:
734 734 error = _(".hg/dirstate inconsistent with current parent's manifest")
735 735 raise util.Abort(error)
736 736
737 737 def showconfig(ui, repo, *values, **opts):
738 738 """show combined config settings from all hgrc files
739 739
740 740 With no args, print names and values of all config items.
741 741
742 742 With one arg of the form section.name, print just the value of
743 743 that config item.
744 744
745 745 With multiple args, print names and values of all config items
746 746 with matching section names."""
747 747
748 748 untrusted = bool(opts.get('untrusted'))
749 749 if values:
750 750 if len([v for v in values if '.' in v]) > 1:
751 751 raise util.Abort(_('only one config item permitted'))
752 752 for section, name, value in ui.walkconfig(untrusted=untrusted):
753 753 sectname = section + '.' + name
754 754 if values:
755 755 for v in values:
756 756 if v == section:
757 757 ui.write('%s=%s\n' % (sectname, value))
758 758 elif v == sectname:
759 759 ui.write(value, '\n')
760 760 else:
761 761 ui.write('%s=%s\n' % (sectname, value))
762 762
763 763 def debugsetparents(ui, repo, rev1, rev2=None):
764 764 """manually set the parents of the current working directory
765 765
766 766 This is useful for writing repository conversion tools, but should
767 767 be used with care.
768 768 """
769 769
770 770 if not rev2:
771 771 rev2 = hex(nullid)
772 772
773 773 wlock = repo.wlock()
774 774 try:
775 775 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
776 776 finally:
777 777 wlock.release()
778 778
779 779 def debugstate(ui, repo):
780 780 """show the contents of the current dirstate"""
781 781 dc = repo.dirstate
782 782 for file_ in dc:
783 783 if dc[file_][3] == -1:
784 784 # Pad or slice to locale representation
785 785 locale_len = len(time.strftime("%x %X", time.localtime(0)))
786 786 timestr = 'unset'
787 787 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
788 788 else:
789 789 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
790 790 ui.write("%c %3o %10d %s %s\n"
791 791 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
792 792 timestr, file_))
793 793 for f in repo.dirstate.copies():
794 794 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
795 795
796 796 def debugdata(ui, file_, rev):
797 797 """dump the contents of a data file revision"""
798 798 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
799 799 try:
800 800 ui.write(r.revision(r.lookup(rev)))
801 801 except KeyError:
802 802 raise util.Abort(_('invalid revision identifier %s') % rev)
803 803
804 804 def debugdate(ui, date, range=None, **opts):
805 805 """parse and display a date"""
806 806 if opts["extended"]:
807 807 d = util.parsedate(date, util.extendeddateformats)
808 808 else:
809 809 d = util.parsedate(date)
810 810 ui.write("internal: %s %s\n" % d)
811 811 ui.write("standard: %s\n" % util.datestr(d))
812 812 if range:
813 813 m = util.matchdate(range)
814 814 ui.write("match: %s\n" % m(d[0]))
815 815
816 816 def debugindex(ui, file_):
817 817 """dump the contents of an index file"""
818 818 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
819 819 ui.write(" rev offset length base linkrev" +
820 820 " nodeid p1 p2\n")
821 821 for i in xrange(r.count()):
822 822 node = r.node(i)
823 823 pp = r.parents(node)
824 824 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
825 825 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
826 826 short(node), short(pp[0]), short(pp[1])))
827 827
828 828 def debugindexdot(ui, file_):
829 829 """dump an index DAG as a .dot file"""
830 830 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
831 831 ui.write("digraph G {\n")
832 832 for i in xrange(r.count()):
833 833 node = r.node(i)
834 834 pp = r.parents(node)
835 835 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
836 836 if pp[1] != nullid:
837 837 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
838 838 ui.write("}\n")
839 839
840 840 def debuginstall(ui):
841 841 '''test Mercurial installation'''
842 842
843 843 def writetemp(contents):
844 844 (fd, name) = tempfile.mkstemp()
845 845 f = os.fdopen(fd, "wb")
846 846 f.write(contents)
847 847 f.close()
848 848 return name
849 849
850 850 problems = 0
851 851
852 852 # encoding
853 853 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
854 854 try:
855 855 util.fromlocal("test")
856 856 except util.Abort, inst:
857 857 ui.write(" %s\n" % inst)
858 858 ui.write(_(" (check that your locale is properly set)\n"))
859 859 problems += 1
860 860
861 861 # compiled modules
862 862 ui.status(_("Checking extensions...\n"))
863 863 try:
864 864 import bdiff, mpatch, base85
865 865 except Exception, inst:
866 866 ui.write(" %s\n" % inst)
867 867 ui.write(_(" One or more extensions could not be found"))
868 868 ui.write(_(" (check that you compiled the extensions)\n"))
869 869 problems += 1
870 870
871 871 # templates
872 872 ui.status(_("Checking templates...\n"))
873 873 try:
874 874 import templater
875 875 t = templater.templater(templater.templatepath("map-cmdline.default"))
876 876 except Exception, inst:
877 877 ui.write(" %s\n" % inst)
878 878 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
879 879 problems += 1
880 880
881 881 # patch
882 882 ui.status(_("Checking patch...\n"))
883 883 patcher = ui.config('ui', 'patch')
884 884 patcher = ((patcher and util.find_exe(patcher)) or
885 885 util.find_exe('gpatch') or
886 886 util.find_exe('patch'))
887 887 if not patcher:
888 888 ui.write(_(" Can't find patch or gpatch in PATH\n"))
889 889 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
890 890 problems += 1
891 891 else:
892 892 # actually attempt a patch here
893 893 a = "1\n2\n3\n4\n"
894 894 b = "1\n2\n3\ninsert\n4\n"
895 895 fa = writetemp(a)
896 896 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
897 897 fd = writetemp(d)
898 898
899 899 files = {}
900 900 try:
901 901 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
902 902 except util.Abort, e:
903 903 ui.write(_(" patch call failed:\n"))
904 904 ui.write(" " + str(e) + "\n")
905 905 problems += 1
906 906 else:
907 907 if list(files) != [os.path.basename(fa)]:
908 908 ui.write(_(" unexpected patch output!"))
909 909 ui.write(_(" (you may have an incompatible version of patch)\n"))
910 910 problems += 1
911 911 a = file(fa).read()
912 912 if a != b:
913 913 ui.write(_(" patch test failed!"))
914 914 ui.write(_(" (you may have an incompatible version of patch)\n"))
915 915 problems += 1
916 916
917 917 os.unlink(fa)
918 918 os.unlink(fd)
919 919
920 920 # merge helper
921 921 ui.status(_("Checking merge helper...\n"))
922 922 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
923 923 or "hgmerge")
924 924 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
925 925 if not cmdpath:
926 926 if cmd == 'hgmerge':
927 927 ui.write(_(" No merge helper set and can't find default"
928 928 " hgmerge script in PATH\n"))
929 929 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
930 930 else:
931 931 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
932 932 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
933 933 problems += 1
934 934 else:
935 935 # actually attempt a patch here
936 936 fa = writetemp("1\n2\n3\n4\n")
937 937 fl = writetemp("1\n2\n3\ninsert\n4\n")
938 938 fr = writetemp("begin\n1\n2\n3\n4\n")
939 939 r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
940 940 if r:
941 941 ui.write(_(" Got unexpected merge error %d!\n") % r)
942 942 problems += 1
943 943 m = file(fl).read()
944 944 if m != "begin\n1\n2\n3\ninsert\n4\n":
945 945 ui.write(_(" Got unexpected merge results!\n"))
946 946 ui.write(_(" (your merge helper may have the"
947 947 " wrong argument order)\n"))
948 948 ui.write(_(" Result: %r\n") % m)
949 949 problems += 1
950 950 os.unlink(fa)
951 951 os.unlink(fl)
952 952 os.unlink(fr)
953 953
954 954 # editor
955 955 ui.status(_("Checking commit editor...\n"))
956 956 editor = (os.environ.get("HGEDITOR") or
957 957 ui.config("ui", "editor") or
958 958 os.environ.get("EDITOR", "vi"))
959 959 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
960 960 if not cmdpath:
961 961 if editor == 'vi':
962 962 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
963 963 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
964 964 else:
965 965 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
966 966 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
967 967 problems += 1
968 968
969 969 # check username
970 970 ui.status(_("Checking username...\n"))
971 971 user = os.environ.get("HGUSER")
972 972 if user is None:
973 973 user = ui.config("ui", "username")
974 974 if user is None:
975 975 user = os.environ.get("EMAIL")
976 976 if not user:
977 977 ui.warn(" ")
978 978 ui.username()
979 979 ui.write(_(" (specify a username in your .hgrc file)\n"))
980 980
981 981 if not problems:
982 982 ui.status(_("No problems detected\n"))
983 983 else:
984 984 ui.write(_("%s problems detected,"
985 985 " please check your install!\n") % problems)
986 986
987 987 return problems
988 988
989 989 def debugrename(ui, repo, file1, *pats, **opts):
990 990 """dump rename information"""
991 991
992 992 ctx = repo.changectx(opts.get('rev', 'tip'))
993 993 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
994 994 ctx.node()):
995 995 m = ctx.filectx(abs).renamed()
996 996 if m:
997 997 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
998 998 else:
999 999 ui.write(_("%s not renamed\n") % rel)
1000 1000
1001 1001 def debugwalk(ui, repo, *pats, **opts):
1002 1002 """show how files match on given patterns"""
1003 1003 items = list(cmdutil.walk(repo, pats, opts))
1004 1004 if not items:
1005 1005 return
1006 1006 fmt = '%%s %%-%ds %%-%ds %%s' % (
1007 1007 max([len(abs) for (src, abs, rel, exact) in items]),
1008 1008 max([len(rel) for (src, abs, rel, exact) in items]))
1009 1009 for src, abs, rel, exact in items:
1010 1010 line = fmt % (src, abs, rel, exact and 'exact' or '')
1011 1011 ui.write("%s\n" % line.rstrip())
1012 1012
1013 1013 def diff(ui, repo, *pats, **opts):
1014 1014 """diff repository (or selected files)
1015 1015
1016 1016 Show differences between revisions for the specified files.
1017 1017
1018 1018 Differences between files are shown using the unified diff format.
1019 1019
1020 1020 NOTE: diff may generate unexpected results for merges, as it will
1021 1021 default to comparing against the working directory's first parent
1022 1022 changeset if no revisions are specified.
1023 1023
1024 1024 When two revision arguments are given, then changes are shown
1025 1025 between those revisions. If only one revision is specified then
1026 1026 that revision is compared to the working directory, and, when no
1027 1027 revisions are specified, the working directory files are compared
1028 1028 to its parent.
1029 1029
1030 1030 Without the -a option, diff will avoid generating diffs of files
1031 1031 it detects as binary. With -a, diff will generate a diff anyway,
1032 1032 probably with undesirable results.
1033 1033 """
1034 1034 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1035 1035
1036 1036 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1037 1037
1038 1038 patch.diff(repo, node1, node2, fns, match=matchfn,
1039 1039 opts=patch.diffopts(ui, opts))
1040 1040
1041 1041 def export(ui, repo, *changesets, **opts):
1042 1042 """dump the header and diffs for one or more changesets
1043 1043
1044 1044 Print the changeset header and diffs for one or more revisions.
1045 1045
1046 1046 The information shown in the changeset header is: author,
1047 1047 changeset hash, parent(s) and commit comment.
1048 1048
1049 1049 NOTE: export may generate unexpected diff output for merge changesets,
1050 1050 as it will compare the merge changeset against its first parent only.
1051 1051
1052 1052 Output may be to a file, in which case the name of the file is
1053 1053 given using a format string. The formatting rules are as follows:
1054 1054
1055 1055 %% literal "%" character
1056 1056 %H changeset hash (40 bytes of hexadecimal)
1057 1057 %N number of patches being generated
1058 1058 %R changeset revision number
1059 1059 %b basename of the exporting repository
1060 1060 %h short-form changeset hash (12 bytes of hexadecimal)
1061 1061 %n zero-padded sequence number, starting at 1
1062 1062 %r zero-padded changeset revision number
1063 1063
1064 1064 Without the -a option, export will avoid generating diffs of files
1065 1065 it detects as binary. With -a, export will generate a diff anyway,
1066 1066 probably with undesirable results.
1067 1067
1068 1068 With the --switch-parent option, the diff will be against the second
1069 1069 parent. It can be useful to review a merge.
1070 1070 """
1071 1071 if not changesets:
1072 1072 raise util.Abort(_("export requires at least one changeset"))
1073 1073 revs = cmdutil.revrange(repo, changesets)
1074 1074 if len(revs) > 1:
1075 1075 ui.note(_('exporting patches:\n'))
1076 1076 else:
1077 1077 ui.note(_('exporting patch:\n'))
1078 1078 patch.export(repo, revs, template=opts['output'],
1079 1079 switch_parent=opts['switch_parent'],
1080 1080 opts=patch.diffopts(ui, opts))
1081 1081
1082 1082 def grep(ui, repo, pattern, *pats, **opts):
1083 1083 """search for a pattern in specified files and revisions
1084 1084
1085 1085 Search revisions of files for a regular expression.
1086 1086
1087 1087 This command behaves differently than Unix grep. It only accepts
1088 1088 Python/Perl regexps. It searches repository history, not the
1089 1089 working directory. It always prints the revision number in which
1090 1090 a match appears.
1091 1091
1092 1092 By default, grep only prints output for the first revision of a
1093 1093 file in which it finds a match. To get it to print every revision
1094 1094 that contains a change in match status ("-" for a match that
1095 1095 becomes a non-match, or "+" for a non-match that becomes a match),
1096 1096 use the --all flag.
1097 1097 """
1098 1098 reflags = 0
1099 1099 if opts['ignore_case']:
1100 1100 reflags |= re.I
1101 1101 try:
1102 1102 regexp = re.compile(pattern, reflags)
1103 1103 except Exception, inst:
1104 1104 ui.warn(_("grep: invalid match pattern: %s!\n") % inst)
1105 1105 return None
1106 1106 sep, eol = ':', '\n'
1107 1107 if opts['print0']:
1108 1108 sep = eol = '\0'
1109 1109
1110 1110 fcache = {}
1111 1111 def getfile(fn):
1112 1112 if fn not in fcache:
1113 1113 fcache[fn] = repo.file(fn)
1114 1114 return fcache[fn]
1115 1115
1116 1116 def matchlines(body):
1117 1117 begin = 0
1118 1118 linenum = 0
1119 1119 while True:
1120 1120 match = regexp.search(body, begin)
1121 1121 if not match:
1122 1122 break
1123 1123 mstart, mend = match.span()
1124 1124 linenum += body.count('\n', begin, mstart) + 1
1125 1125 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1126 1126 lend = body.find('\n', mend)
1127 1127 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1128 1128 begin = lend + 1
1129 1129
1130 1130 class linestate(object):
1131 1131 def __init__(self, line, linenum, colstart, colend):
1132 1132 self.line = line
1133 1133 self.linenum = linenum
1134 1134 self.colstart = colstart
1135 1135 self.colend = colend
1136 1136
1137 1137 def __eq__(self, other):
1138 1138 return self.line == other.line
1139 1139
1140 1140 matches = {}
1141 1141 copies = {}
1142 1142 def grepbody(fn, rev, body):
1143 1143 matches[rev].setdefault(fn, [])
1144 1144 m = matches[rev][fn]
1145 1145 for lnum, cstart, cend, line in matchlines(body):
1146 1146 s = linestate(line, lnum, cstart, cend)
1147 1147 m.append(s)
1148 1148
1149 1149 def difflinestates(a, b):
1150 1150 sm = difflib.SequenceMatcher(None, a, b)
1151 1151 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1152 1152 if tag == 'insert':
1153 1153 for i in xrange(blo, bhi):
1154 1154 yield ('+', b[i])
1155 1155 elif tag == 'delete':
1156 1156 for i in xrange(alo, ahi):
1157 1157 yield ('-', a[i])
1158 1158 elif tag == 'replace':
1159 1159 for i in xrange(alo, ahi):
1160 1160 yield ('-', a[i])
1161 1161 for i in xrange(blo, bhi):
1162 1162 yield ('+', b[i])
1163 1163
1164 1164 prev = {}
1165 1165 def display(fn, rev, states, prevstates):
1166 1166 found = False
1167 1167 filerevmatches = {}
1168 1168 r = prev.get(fn, -1)
1169 1169 if opts['all']:
1170 1170 iter = difflinestates(states, prevstates)
1171 1171 else:
1172 1172 iter = [('', l) for l in prevstates]
1173 1173 for change, l in iter:
1174 1174 cols = [fn, str(r)]
1175 1175 if opts['line_number']:
1176 1176 cols.append(str(l.linenum))
1177 1177 if opts['all']:
1178 1178 cols.append(change)
1179 1179 if opts['user']:
1180 1180 cols.append(ui.shortuser(get(r)[1]))
1181 1181 if opts['files_with_matches']:
1182 1182 c = (fn, r)
1183 1183 if c in filerevmatches:
1184 1184 continue
1185 1185 filerevmatches[c] = 1
1186 1186 else:
1187 1187 cols.append(l.line)
1188 1188 ui.write(sep.join(cols), eol)
1189 1189 found = True
1190 1190 return found
1191 1191
1192 1192 fstate = {}
1193 1193 skip = {}
1194 1194 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1195 1195 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1196 1196 found = False
1197 1197 follow = opts.get('follow')
1198 1198 for st, rev, fns in changeiter:
1199 1199 if st == 'window':
1200 1200 matches.clear()
1201 1201 elif st == 'add':
1202 1202 mf = repo.changectx(rev).manifest()
1203 1203 matches[rev] = {}
1204 1204 for fn in fns:
1205 1205 if fn in skip:
1206 1206 continue
1207 1207 try:
1208 1208 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1209 1209 fstate.setdefault(fn, [])
1210 1210 if follow:
1211 1211 copied = getfile(fn).renamed(mf[fn])
1212 1212 if copied:
1213 1213 copies.setdefault(rev, {})[fn] = copied[0]
1214 1214 except KeyError:
1215 1215 pass
1216 1216 elif st == 'iter':
1217 1217 states = matches[rev].items()
1218 1218 states.sort()
1219 1219 for fn, m in states:
1220 1220 copy = copies.get(rev, {}).get(fn)
1221 1221 if fn in skip:
1222 1222 if copy:
1223 1223 skip[copy] = True
1224 1224 continue
1225 1225 if fn in prev or fstate[fn]:
1226 1226 r = display(fn, rev, m, fstate[fn])
1227 1227 found = found or r
1228 1228 if r and not opts['all']:
1229 1229 skip[fn] = True
1230 1230 if copy:
1231 1231 skip[copy] = True
1232 1232 fstate[fn] = m
1233 1233 if copy:
1234 1234 fstate[copy] = m
1235 1235 prev[fn] = rev
1236 1236
1237 1237 fstate = fstate.items()
1238 1238 fstate.sort()
1239 1239 for fn, state in fstate:
1240 1240 if fn in skip:
1241 1241 continue
1242 1242 if fn not in copies.get(prev[fn], {}):
1243 1243 found = display(fn, rev, {}, state) or found
1244 1244 return (not found and 1) or 0
1245 1245
1246 1246 def heads(ui, repo, *branchrevs, **opts):
1247 1247 """show current repository heads or show branch heads
1248 1248
1249 1249 With no arguments, show all repository head changesets.
1250 1250
1251 1251 If branch or revisions names are given this will show the heads of
1252 1252 the specified branches or the branches those revisions are tagged
1253 1253 with.
1254 1254
1255 1255 Repository "heads" are changesets that don't have child
1256 1256 changesets. They are where development generally takes place and
1257 1257 are the usual targets for update and merge operations.
1258 1258
1259 1259 Branch heads are changesets that have a given branch tag, but have
1260 1260 no child changesets with that tag. They are usually where
1261 1261 development on the given branch takes place.
1262 1262 """
1263 1263 if opts['rev']:
1264 1264 start = repo.lookup(opts['rev'])
1265 1265 else:
1266 1266 start = None
1267 1267 if not branchrevs:
1268 1268 # Assume we're looking repo-wide heads if no revs were specified.
1269 1269 heads = repo.heads(start)
1270 1270 else:
1271 1271 heads = []
1272 1272 visitedset = util.set()
1273 1273 for branchrev in branchrevs:
1274 1274 branch = repo.changectx(branchrev).branch()
1275 1275 if branch in visitedset:
1276 1276 continue
1277 1277 visitedset.add(branch)
1278 1278 bheads = repo.branchheads(branch, start)
1279 1279 if not bheads:
1280 1280 if branch != branchrev:
1281 1281 ui.warn(_("no changes on branch %s containing %s are "
1282 1282 "reachable from %s\n")
1283 1283 % (branch, branchrev, opts['rev']))
1284 1284 else:
1285 1285 ui.warn(_("no changes on branch %s are reachable from %s\n")
1286 1286 % (branch, opts['rev']))
1287 1287 heads.extend(bheads)
1288 1288 if not heads:
1289 1289 return 1
1290 1290 displayer = cmdutil.show_changeset(ui, repo, opts)
1291 1291 for n in heads:
1292 1292 displayer.show(changenode=n)
1293 1293
1294 1294 def help_(ui, name=None, with_version=False):
1295 1295 """show help for a command, extension, or list of commands
1296 1296
1297 1297 With no arguments, print a list of commands and short help.
1298 1298
1299 1299 Given a command name, print help for that command.
1300 1300
1301 1301 Given an extension name, print help for that extension, and the
1302 1302 commands it provides."""
1303 1303 option_lists = []
1304 1304
1305 1305 def addglobalopts(aliases):
1306 1306 if ui.verbose:
1307 1307 option_lists.append((_("global options:"), globalopts))
1308 1308 if name == 'shortlist':
1309 1309 option_lists.append((_('use "hg help" for the full list '
1310 1310 'of commands'), ()))
1311 1311 else:
1312 1312 if name == 'shortlist':
1313 1313 msg = _('use "hg help" for the full list of commands '
1314 1314 'or "hg -v" for details')
1315 1315 elif aliases:
1316 1316 msg = _('use "hg -v help%s" to show aliases and '
1317 1317 'global options') % (name and " " + name or "")
1318 1318 else:
1319 1319 msg = _('use "hg -v help %s" to show global options') % name
1320 1320 option_lists.append((msg, ()))
1321 1321
1322 1322 def helpcmd(name):
1323 1323 if with_version:
1324 1324 version_(ui)
1325 1325 ui.write('\n')
1326 1326 aliases, i = cmdutil.findcmd(ui, name)
1327 1327 # synopsis
1328 1328 ui.write("%s\n\n" % i[2])
1329 1329
1330 1330 # description
1331 1331 doc = i[0].__doc__
1332 1332 if not doc:
1333 1333 doc = _("(No help text available)")
1334 1334 if ui.quiet:
1335 1335 doc = doc.splitlines(0)[0]
1336 1336 ui.write("%s\n" % doc.rstrip())
1337 1337
1338 1338 if not ui.quiet:
1339 1339 # aliases
1340 1340 if len(aliases) > 1:
1341 1341 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1342 1342
1343 1343 # options
1344 1344 if i[1]:
1345 1345 option_lists.append((_("options:\n"), i[1]))
1346 1346
1347 1347 addglobalopts(False)
1348 1348
1349 1349 def helplist(header, select=None):
1350 1350 h = {}
1351 1351 cmds = {}
1352 1352 for c, e in table.items():
1353 1353 f = c.split("|", 1)[0]
1354 1354 if select and not select(f):
1355 1355 continue
1356 1356 if name == "shortlist" and not f.startswith("^"):
1357 1357 continue
1358 1358 f = f.lstrip("^")
1359 1359 if not ui.debugflag and f.startswith("debug"):
1360 1360 continue
1361 1361 doc = e[0].__doc__
1362 1362 if not doc:
1363 1363 doc = _("(No help text available)")
1364 1364 h[f] = doc.splitlines(0)[0].rstrip()
1365 1365 cmds[f] = c.lstrip("^")
1366 1366
1367 1367 if not h:
1368 1368 ui.status(_('no commands defined\n'))
1369 1369 return
1370 1370
1371 1371 ui.status(header)
1372 1372 fns = h.keys()
1373 1373 fns.sort()
1374 1374 m = max(map(len, fns))
1375 1375 for f in fns:
1376 1376 if ui.verbose:
1377 1377 commands = cmds[f].replace("|",", ")
1378 1378 ui.write(" %s:\n %s\n"%(commands, h[f]))
1379 1379 else:
1380 1380 ui.write(' %-*s %s\n' % (m, f, h[f]))
1381 1381
1382 1382 if not ui.quiet:
1383 1383 addglobalopts(True)
1384 1384
1385 1385 def helptopic(name):
1386 1386 v = None
1387 1387 for i in help.helptable:
1388 1388 l = i.split('|')
1389 1389 if name in l:
1390 1390 v = i
1391 1391 header = l[-1]
1392 1392 if not v:
1393 1393 raise cmdutil.UnknownCommand(name)
1394 1394
1395 1395 # description
1396 1396 doc = help.helptable[v]
1397 1397 if not doc:
1398 1398 doc = _("(No help text available)")
1399 1399 if callable(doc):
1400 1400 doc = doc()
1401 1401
1402 1402 ui.write("%s\n" % header)
1403 1403 ui.write("%s\n" % doc.rstrip())
1404 1404
1405 1405 def helpext(name):
1406 1406 try:
1407 1407 mod = extensions.find(name)
1408 1408 except KeyError:
1409 1409 raise cmdutil.UnknownCommand(name)
1410 1410
1411 1411 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1412 1412 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1413 1413 for d in doc[1:]:
1414 1414 ui.write(d, '\n')
1415 1415
1416 1416 ui.status('\n')
1417 1417
1418 1418 try:
1419 1419 ct = mod.cmdtable
1420 1420 except AttributeError:
1421 1421 ct = {}
1422 1422
1423 1423 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1424 1424 helplist(_('list of commands:\n\n'), modcmds.has_key)
1425 1425
1426 1426 if name and name != 'shortlist':
1427 1427 i = None
1428 1428 for f in (helpcmd, helptopic, helpext):
1429 1429 try:
1430 1430 f(name)
1431 1431 i = None
1432 1432 break
1433 1433 except cmdutil.UnknownCommand, inst:
1434 1434 i = inst
1435 1435 if i:
1436 1436 raise i
1437 1437
1438 1438 else:
1439 1439 # program name
1440 1440 if ui.verbose or with_version:
1441 1441 version_(ui)
1442 1442 else:
1443 1443 ui.status(_("Mercurial Distributed SCM\n"))
1444 1444 ui.status('\n')
1445 1445
1446 1446 # list of commands
1447 1447 if name == "shortlist":
1448 1448 header = _('basic commands:\n\n')
1449 1449 else:
1450 1450 header = _('list of commands:\n\n')
1451 1451
1452 1452 helplist(header)
1453 1453
1454 1454 # list all option lists
1455 1455 opt_output = []
1456 1456 for title, options in option_lists:
1457 1457 opt_output.append(("\n%s" % title, None))
1458 1458 for shortopt, longopt, default, desc in options:
1459 1459 if "DEPRECATED" in desc and not ui.verbose: continue
1460 1460 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1461 1461 longopt and " --%s" % longopt),
1462 1462 "%s%s" % (desc,
1463 1463 default
1464 1464 and _(" (default: %s)") % default
1465 1465 or "")))
1466 1466
1467 1467 if opt_output:
1468 1468 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1469 1469 for first, second in opt_output:
1470 1470 if second:
1471 1471 ui.write(" %-*s %s\n" % (opts_len, first, second))
1472 1472 else:
1473 1473 ui.write("%s\n" % first)
1474 1474
1475 1475 def identify(ui, repo, source=None,
1476 1476 rev=None, num=None, id=None, branch=None, tags=None):
1477 1477 """identify the working copy or specified revision
1478 1478
1479 1479 With no revision, print a summary of the current state of the repo.
1480 1480
1481 1481 With a path, do a lookup in another repository.
1482 1482
1483 1483 This summary identifies the repository state using one or two parent
1484 1484 hash identifiers, followed by a "+" if there are uncommitted changes
1485 1485 in the working directory, a list of tags for this revision and a branch
1486 1486 name for non-default branches.
1487 1487 """
1488 1488
1489 1489 hexfunc = ui.debugflag and hex or short
1490 1490 default = not (num or id or branch or tags)
1491 1491 output = []
1492 1492
1493 1493 if source:
1494 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1494 source, revs, checkout = cmdutil.parseurl(ui.expandpath(source), [])
1495 1495 srepo = hg.repository(ui, source)
1496 1496 if not rev and revs:
1497 1497 rev = revs[0]
1498 1498 if not rev:
1499 1499 rev = "tip"
1500 1500 if num or branch or tags:
1501 1501 raise util.Abort(
1502 1502 "can't query remote revision number, branch, or tags")
1503 1503 output = [hexfunc(srepo.lookup(rev))]
1504 1504 elif not rev:
1505 1505 ctx = repo.workingctx()
1506 1506 parents = ctx.parents()
1507 1507 changed = False
1508 1508 if default or id or num:
1509 1509 changed = ctx.files() + ctx.deleted()
1510 1510 if default or id:
1511 1511 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1512 1512 (changed) and "+" or "")]
1513 1513 if num:
1514 1514 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1515 1515 (changed) and "+" or ""))
1516 1516 else:
1517 1517 ctx = repo.changectx(rev)
1518 1518 if default or id:
1519 1519 output = [hexfunc(ctx.node())]
1520 1520 if num:
1521 1521 output.append(str(ctx.rev()))
1522 1522
1523 1523 if not source and default and not ui.quiet:
1524 1524 b = util.tolocal(ctx.branch())
1525 1525 if b != 'default':
1526 1526 output.append("(%s)" % b)
1527 1527
1528 1528 # multiple tags for a single parent separated by '/'
1529 1529 t = "/".join(ctx.tags())
1530 1530 if t:
1531 1531 output.append(t)
1532 1532
1533 1533 if branch:
1534 1534 output.append(util.tolocal(ctx.branch()))
1535 1535
1536 1536 if tags:
1537 1537 output.extend(ctx.tags())
1538 1538
1539 1539 ui.write("%s\n" % ' '.join(output))
1540 1540
1541 1541 def import_(ui, repo, patch1, *patches, **opts):
1542 1542 """import an ordered set of patches
1543 1543
1544 1544 Import a list of patches and commit them individually.
1545 1545
1546 1546 If there are outstanding changes in the working directory, import
1547 1547 will abort unless given the -f flag.
1548 1548
1549 1549 You can import a patch straight from a mail message. Even patches
1550 1550 as attachments work (body part must be type text/plain or
1551 1551 text/x-patch to be used). From and Subject headers of email
1552 1552 message are used as default committer and commit message. All
1553 1553 text/plain body parts before first diff are added to commit
1554 1554 message.
1555 1555
1556 1556 If the imported patch was generated by hg export, user and description
1557 1557 from patch override values from message headers and body. Values
1558 1558 given on command line with -m and -u override these.
1559 1559
1560 1560 If --exact is specified, import will set the working directory
1561 1561 to the parent of each patch before applying it, and will abort
1562 1562 if the resulting changeset has a different ID than the one
1563 1563 recorded in the patch. This may happen due to character set
1564 1564 problems or other deficiencies in the text patch format.
1565 1565
1566 1566 To read a patch from standard input, use patch name "-".
1567 1567 """
1568 1568 patches = (patch1,) + patches
1569 1569
1570 1570 if opts.get('exact') or not opts['force']:
1571 1571 cmdutil.bail_if_changed(repo)
1572 1572
1573 1573 d = opts["base"]
1574 1574 strip = opts["strip"]
1575 1575
1576 1576 wlock = repo.wlock()
1577 1577 lock = repo.lock()
1578 1578
1579 1579 for p in patches:
1580 1580 pf = os.path.join(d, p)
1581 1581
1582 1582 if pf == '-':
1583 1583 ui.status(_("applying patch from stdin\n"))
1584 1584 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1585 1585 else:
1586 1586 ui.status(_("applying %s\n") % p)
1587 1587 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, file(pf, 'rb'))
1588 1588
1589 1589 if tmpname is None:
1590 1590 raise util.Abort(_('no diffs found'))
1591 1591
1592 1592 try:
1593 1593 cmdline_message = cmdutil.logmessage(opts)
1594 1594 if cmdline_message:
1595 1595 # pickup the cmdline msg
1596 1596 message = cmdline_message
1597 1597 elif message:
1598 1598 # pickup the patch msg
1599 1599 message = message.strip()
1600 1600 else:
1601 1601 # launch the editor
1602 1602 message = None
1603 1603 ui.debug(_('message:\n%s\n') % message)
1604 1604
1605 1605 wp = repo.workingctx().parents()
1606 1606 if opts.get('exact'):
1607 1607 if not nodeid or not p1:
1608 1608 raise util.Abort(_('not a mercurial patch'))
1609 1609 p1 = repo.lookup(p1)
1610 1610 p2 = repo.lookup(p2 or hex(nullid))
1611 1611
1612 1612 if p1 != wp[0].node():
1613 1613 hg.clean(repo, p1, wlock=wlock)
1614 1614 repo.dirstate.setparents(p1, p2)
1615 1615 elif p2:
1616 1616 try:
1617 1617 p1 = repo.lookup(p1)
1618 1618 p2 = repo.lookup(p2)
1619 1619 if p1 == wp[0].node():
1620 1620 repo.dirstate.setparents(p1, p2)
1621 1621 except hg.RepoError:
1622 1622 pass
1623 1623 if opts.get('exact') or opts.get('import_branch'):
1624 1624 repo.dirstate.setbranch(branch or 'default')
1625 1625
1626 1626 files = {}
1627 1627 try:
1628 1628 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1629 1629 files=files)
1630 1630 finally:
1631 1631 files = patch.updatedir(ui, repo, files, wlock=wlock)
1632 1632 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1633 1633 if opts.get('exact'):
1634 1634 if hex(n) != nodeid:
1635 1635 repo.rollback(wlock=wlock, lock=lock)
1636 1636 raise util.Abort(_('patch is damaged or loses information'))
1637 1637 finally:
1638 1638 os.unlink(tmpname)
1639 1639
1640 1640 def incoming(ui, repo, source="default", **opts):
1641 1641 """show new changesets found in source
1642 1642
1643 1643 Show new changesets found in the specified path/URL or the default
1644 1644 pull location. These are the changesets that would be pulled if a pull
1645 1645 was requested.
1646 1646
1647 1647 For remote repository, using --bundle avoids downloading the changesets
1648 1648 twice if the incoming is followed by a pull.
1649 1649
1650 1650 See pull for valid source format details.
1651 1651 """
1652 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1652 source, revs, checkout = cmdutil.parseurl(ui.expandpath(source),
1653 opts['rev'])
1653 1654 cmdutil.setremoteconfig(ui, opts)
1654 1655
1655 1656 other = hg.repository(ui, source)
1656 1657 ui.status(_('comparing with %s\n') % source)
1657 1658 if revs:
1658 1659 if 'lookup' in other.capabilities:
1659 1660 revs = [other.lookup(rev) for rev in revs]
1660 1661 else:
1661 1662 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1662 1663 raise util.Abort(error)
1663 1664 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1664 1665 if not incoming:
1665 1666 try:
1666 1667 os.unlink(opts["bundle"])
1667 1668 except:
1668 1669 pass
1669 1670 ui.status(_("no changes found\n"))
1670 1671 return 1
1671 1672
1672 1673 cleanup = None
1673 1674 try:
1674 1675 fname = opts["bundle"]
1675 1676 if fname or not other.local():
1676 1677 # create a bundle (uncompressed if other repo is not local)
1677 1678 if revs is None:
1678 1679 cg = other.changegroup(incoming, "incoming")
1679 1680 else:
1680 1681 if 'changegroupsubset' not in other.capabilities:
1681 1682 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1682 1683 cg = other.changegroupsubset(incoming, revs, 'incoming')
1683 1684 bundletype = other.local() and "HG10BZ" or "HG10UN"
1684 1685 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1685 1686 # keep written bundle?
1686 1687 if opts["bundle"]:
1687 1688 cleanup = None
1688 1689 if not other.local():
1689 1690 # use the created uncompressed bundlerepo
1690 1691 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1691 1692
1692 1693 o = other.changelog.nodesbetween(incoming, revs)[0]
1693 1694 if opts['newest_first']:
1694 1695 o.reverse()
1695 1696 displayer = cmdutil.show_changeset(ui, other, opts)
1696 1697 for n in o:
1697 1698 parents = [p for p in other.changelog.parents(n) if p != nullid]
1698 1699 if opts['no_merges'] and len(parents) == 2:
1699 1700 continue
1700 1701 displayer.show(changenode=n)
1701 1702 finally:
1702 1703 if hasattr(other, 'close'):
1703 1704 other.close()
1704 1705 if cleanup:
1705 1706 os.unlink(cleanup)
1706 1707
1707 1708 def init(ui, dest=".", **opts):
1708 1709 """create a new repository in the given directory
1709 1710
1710 1711 Initialize a new repository in the given directory. If the given
1711 1712 directory does not exist, it is created.
1712 1713
1713 1714 If no directory is given, the current directory is used.
1714 1715
1715 1716 It is possible to specify an ssh:// URL as the destination.
1716 1717 Look at the help text for the pull command for important details
1717 1718 about ssh:// URLs.
1718 1719 """
1719 1720 cmdutil.setremoteconfig(ui, opts)
1720 1721 hg.repository(ui, dest, create=1)
1721 1722
1722 1723 def locate(ui, repo, *pats, **opts):
1723 1724 """locate files matching specific patterns
1724 1725
1725 1726 Print all files under Mercurial control whose names match the
1726 1727 given patterns.
1727 1728
1728 1729 This command searches the entire repository by default. To search
1729 1730 just the current directory and its subdirectories, use
1730 1731 "--include .".
1731 1732
1732 1733 If no patterns are given to match, this command prints all file
1733 1734 names.
1734 1735
1735 1736 If you want to feed the output of this command into the "xargs"
1736 1737 command, use the "-0" option to both this command and "xargs".
1737 1738 This will avoid the problem of "xargs" treating single filenames
1738 1739 that contain white space as multiple filenames.
1739 1740 """
1740 1741 end = opts['print0'] and '\0' or '\n'
1741 1742 rev = opts['rev']
1742 1743 if rev:
1743 1744 node = repo.lookup(rev)
1744 1745 else:
1745 1746 node = None
1746 1747
1747 1748 ret = 1
1748 1749 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1749 1750 badmatch=util.always,
1750 1751 default='relglob'):
1751 1752 if src == 'b':
1752 1753 continue
1753 1754 if not node and repo.dirstate.state(abs) == '?':
1754 1755 continue
1755 1756 if opts['fullpath']:
1756 1757 ui.write(os.path.join(repo.root, abs), end)
1757 1758 else:
1758 1759 ui.write(((pats and rel) or abs), end)
1759 1760 ret = 0
1760 1761
1761 1762 return ret
1762 1763
1763 1764 def log(ui, repo, *pats, **opts):
1764 1765 """show revision history of entire repository or files
1765 1766
1766 1767 Print the revision history of the specified files or the entire
1767 1768 project.
1768 1769
1769 1770 File history is shown without following rename or copy history of
1770 1771 files. Use -f/--follow with a file name to follow history across
1771 1772 renames and copies. --follow without a file name will only show
1772 1773 ancestors or descendants of the starting revision. --follow-first
1773 1774 only follows the first parent of merge revisions.
1774 1775
1775 1776 If no revision range is specified, the default is tip:0 unless
1776 1777 --follow is set, in which case the working directory parent is
1777 1778 used as the starting revision.
1778 1779
1779 1780 By default this command outputs: changeset id and hash, tags,
1780 1781 non-trivial parents, user, date and time, and a summary for each
1781 1782 commit. When the -v/--verbose switch is used, the list of changed
1782 1783 files and full commit message is shown.
1783 1784
1784 1785 NOTE: log -p may generate unexpected diff output for merge
1785 1786 changesets, as it will compare the merge changeset against its
1786 1787 first parent only. Also, the files: list will only reflect files
1787 1788 that are different from BOTH parents.
1788 1789
1789 1790 """
1790 1791
1791 1792 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1792 1793 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1793 1794
1794 1795 if opts['limit']:
1795 1796 try:
1796 1797 limit = int(opts['limit'])
1797 1798 except ValueError:
1798 1799 raise util.Abort(_('limit must be a positive integer'))
1799 1800 if limit <= 0: raise util.Abort(_('limit must be positive'))
1800 1801 else:
1801 1802 limit = sys.maxint
1802 1803 count = 0
1803 1804
1804 1805 if opts['copies'] and opts['rev']:
1805 1806 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1806 1807 else:
1807 1808 endrev = repo.changelog.count()
1808 1809 rcache = {}
1809 1810 ncache = {}
1810 1811 dcache = []
1811 1812 def getrenamed(fn, rev, man):
1812 1813 '''looks up all renames for a file (up to endrev) the first
1813 1814 time the file is given. It indexes on the changerev and only
1814 1815 parses the manifest if linkrev != changerev.
1815 1816 Returns rename info for fn at changerev rev.'''
1816 1817 if fn not in rcache:
1817 1818 rcache[fn] = {}
1818 1819 ncache[fn] = {}
1819 1820 fl = repo.file(fn)
1820 1821 for i in xrange(fl.count()):
1821 1822 node = fl.node(i)
1822 1823 lr = fl.linkrev(node)
1823 1824 renamed = fl.renamed(node)
1824 1825 rcache[fn][lr] = renamed
1825 1826 if renamed:
1826 1827 ncache[fn][node] = renamed
1827 1828 if lr >= endrev:
1828 1829 break
1829 1830 if rev in rcache[fn]:
1830 1831 return rcache[fn][rev]
1831 1832 mr = repo.manifest.rev(man)
1832 1833 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1833 1834 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1834 1835 if not dcache or dcache[0] != man:
1835 1836 dcache[:] = [man, repo.manifest.readdelta(man)]
1836 1837 if fn in dcache[1]:
1837 1838 return ncache[fn].get(dcache[1][fn])
1838 1839 return None
1839 1840
1840 1841 df = False
1841 1842 if opts["date"]:
1842 1843 df = util.matchdate(opts["date"])
1843 1844
1844 1845 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1845 1846 for st, rev, fns in changeiter:
1846 1847 if st == 'add':
1847 1848 changenode = repo.changelog.node(rev)
1848 1849 parents = [p for p in repo.changelog.parentrevs(rev)
1849 1850 if p != nullrev]
1850 1851 if opts['no_merges'] and len(parents) == 2:
1851 1852 continue
1852 1853 if opts['only_merges'] and len(parents) != 2:
1853 1854 continue
1854 1855
1855 1856 if df:
1856 1857 changes = get(rev)
1857 1858 if not df(changes[2][0]):
1858 1859 continue
1859 1860
1860 1861 if opts['keyword']:
1861 1862 changes = get(rev)
1862 1863 miss = 0
1863 1864 for k in [kw.lower() for kw in opts['keyword']]:
1864 1865 if not (k in changes[1].lower() or
1865 1866 k in changes[4].lower() or
1866 1867 k in " ".join(changes[3]).lower()):
1867 1868 miss = 1
1868 1869 break
1869 1870 if miss:
1870 1871 continue
1871 1872
1872 1873 copies = []
1873 1874 if opts.get('copies') and rev:
1874 1875 mf = get(rev)[0]
1875 1876 for fn in get(rev)[3]:
1876 1877 rename = getrenamed(fn, rev, mf)
1877 1878 if rename:
1878 1879 copies.append((fn, rename[0]))
1879 1880 displayer.show(rev, changenode, copies=copies)
1880 1881 elif st == 'iter':
1881 1882 if count == limit: break
1882 1883 if displayer.flush(rev):
1883 1884 count += 1
1884 1885
1885 1886 def manifest(ui, repo, rev=None):
1886 1887 """output the current or given revision of the project manifest
1887 1888
1888 1889 Print a list of version controlled files for the given revision.
1889 1890 If no revision is given, the parent of the working directory is used,
1890 1891 or tip if no revision is checked out.
1891 1892
1892 1893 The manifest is the list of files being version controlled. If no revision
1893 1894 is given then the first parent of the working directory is used.
1894 1895
1895 1896 With -v flag, print file permissions. With --debug flag, print
1896 1897 file revision hashes.
1897 1898 """
1898 1899
1899 1900 m = repo.changectx(rev).manifest()
1900 1901 files = m.keys()
1901 1902 files.sort()
1902 1903
1903 1904 for f in files:
1904 1905 if ui.debugflag:
1905 1906 ui.write("%40s " % hex(m[f]))
1906 1907 if ui.verbose:
1907 1908 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1908 1909 ui.write("%s\n" % f)
1909 1910
1910 1911 def merge(ui, repo, node=None, force=None, rev=None):
1911 1912 """merge working directory with another revision
1912 1913
1913 1914 Merge the contents of the current working directory and the
1914 1915 requested revision. Files that changed between either parent are
1915 1916 marked as changed for the next commit and a commit must be
1916 1917 performed before any further updates are allowed.
1917 1918
1918 1919 If no revision is specified, the working directory's parent is a
1919 1920 head revision, and the repository contains exactly one other head,
1920 1921 the other head is merged with by default. Otherwise, an explicit
1921 1922 revision to merge with must be provided.
1922 1923 """
1923 1924
1924 1925 if rev and node:
1925 1926 raise util.Abort(_("please specify just one revision"))
1926 1927
1927 1928 if not node:
1928 1929 node = rev
1929 1930
1930 1931 if not node:
1931 1932 heads = repo.heads()
1932 1933 if len(heads) > 2:
1933 1934 raise util.Abort(_('repo has %d heads - '
1934 1935 'please merge with an explicit rev') %
1935 1936 len(heads))
1936 1937 if len(heads) == 1:
1937 1938 raise util.Abort(_('there is nothing to merge - '
1938 1939 'use "hg update" instead'))
1939 1940 parent = repo.dirstate.parents()[0]
1940 1941 if parent not in heads:
1941 1942 raise util.Abort(_('working dir not at a head rev - '
1942 1943 'use "hg update" or merge with an explicit rev'))
1943 1944 node = parent == heads[0] and heads[-1] or heads[0]
1944 1945 return hg.merge(repo, node, force=force)
1945 1946
1946 1947 def outgoing(ui, repo, dest=None, **opts):
1947 1948 """show changesets not found in destination
1948 1949
1949 1950 Show changesets not found in the specified destination repository or
1950 1951 the default push location. These are the changesets that would be pushed
1951 1952 if a push was requested.
1952 1953
1953 1954 See pull for valid destination format details.
1954 1955 """
1955 dest, revs = cmdutil.parseurl(
1956 dest, revs, checkout = cmdutil.parseurl(
1956 1957 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1957 1958 cmdutil.setremoteconfig(ui, opts)
1958 1959 if revs:
1959 1960 revs = [repo.lookup(rev) for rev in revs]
1960 1961
1961 1962 other = hg.repository(ui, dest)
1962 1963 ui.status(_('comparing with %s\n') % dest)
1963 1964 o = repo.findoutgoing(other, force=opts['force'])
1964 1965 if not o:
1965 1966 ui.status(_("no changes found\n"))
1966 1967 return 1
1967 1968 o = repo.changelog.nodesbetween(o, revs)[0]
1968 1969 if opts['newest_first']:
1969 1970 o.reverse()
1970 1971 displayer = cmdutil.show_changeset(ui, repo, opts)
1971 1972 for n in o:
1972 1973 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1973 1974 if opts['no_merges'] and len(parents) == 2:
1974 1975 continue
1975 1976 displayer.show(changenode=n)
1976 1977
1977 1978 def parents(ui, repo, file_=None, **opts):
1978 1979 """show the parents of the working dir or revision
1979 1980
1980 1981 Print the working directory's parent revisions. If a
1981 1982 revision is given via --rev, the parent of that revision
1982 1983 will be printed. If a file argument is given, revision in
1983 1984 which the file was last changed (before the working directory
1984 1985 revision or the argument to --rev if given) is printed.
1985 1986 """
1986 1987 rev = opts.get('rev')
1987 1988 if file_:
1988 1989 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1989 1990 if anypats or len(files) != 1:
1990 1991 raise util.Abort(_('can only specify an explicit file name'))
1991 1992 ctx = repo.filectx(files[0], changeid=rev)
1992 1993 elif rev:
1993 1994 ctx = repo.changectx(rev)
1994 1995 else:
1995 1996 ctx = repo.workingctx()
1996 1997 p = [cp.node() for cp in ctx.parents()]
1997 1998
1998 1999 displayer = cmdutil.show_changeset(ui, repo, opts)
1999 2000 for n in p:
2000 2001 if n != nullid:
2001 2002 displayer.show(changenode=n)
2002 2003
2003 2004 def paths(ui, repo, search=None):
2004 2005 """show definition of symbolic path names
2005 2006
2006 2007 Show definition of symbolic path name NAME. If no name is given, show
2007 2008 definition of available names.
2008 2009
2009 2010 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2010 2011 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2011 2012 """
2012 2013 if search:
2013 2014 for name, path in ui.configitems("paths"):
2014 2015 if name == search:
2015 2016 ui.write("%s\n" % path)
2016 2017 return
2017 2018 ui.warn(_("not found!\n"))
2018 2019 return 1
2019 2020 else:
2020 2021 for name, path in ui.configitems("paths"):
2021 2022 ui.write("%s = %s\n" % (name, path))
2022 2023
2023 2024 def postincoming(ui, repo, modheads, optupdate):
2024 2025 if modheads == 0:
2025 2026 return
2026 2027 if optupdate:
2027 2028 if modheads <= 1:
2028 2029 return hg.update(repo, None)
2029 2030 else:
2030 2031 ui.status(_("not updating, since new heads added\n"))
2031 2032 if modheads > 1:
2032 2033 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2033 2034 else:
2034 2035 ui.status(_("(run 'hg update' to get a working copy)\n"))
2035 2036
2036 2037 def pull(ui, repo, source="default", **opts):
2037 2038 """pull changes from the specified source
2038 2039
2039 2040 Pull changes from a remote repository to a local one.
2040 2041
2041 2042 This finds all changes from the repository at the specified path
2042 2043 or URL and adds them to the local repository. By default, this
2043 2044 does not update the copy of the project in the working directory.
2044 2045
2045 2046 Valid URLs are of the form:
2046 2047
2047 2048 local/filesystem/path (or file://local/filesystem/path)
2048 2049 http://[user@]host[:port]/[path]
2049 2050 https://[user@]host[:port]/[path]
2050 2051 ssh://[user@]host[:port]/[path]
2051 2052 static-http://host[:port]/[path]
2052 2053
2053 2054 Paths in the local filesystem can either point to Mercurial
2054 2055 repositories or to bundle files (as created by 'hg bundle' or
2055 2056 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2056 2057 allows access to a Mercurial repository where you simply use a web
2057 2058 server to publish the .hg directory as static content.
2058 2059
2059 2060 An optional identifier after # indicates a particular branch, tag,
2060 2061 or changeset to pull.
2061 2062
2062 2063 Some notes about using SSH with Mercurial:
2063 2064 - SSH requires an accessible shell account on the destination machine
2064 2065 and a copy of hg in the remote path or specified with as remotecmd.
2065 2066 - path is relative to the remote user's home directory by default.
2066 2067 Use an extra slash at the start of a path to specify an absolute path:
2067 2068 ssh://example.com//tmp/repository
2068 2069 - Mercurial doesn't use its own compression via SSH; the right thing
2069 2070 to do is to configure it in your ~/.ssh/config, e.g.:
2070 2071 Host *.mylocalnetwork.example.com
2071 2072 Compression no
2072 2073 Host *
2073 2074 Compression yes
2074 2075 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2075 2076 with the --ssh command line option.
2076 2077 """
2077 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2078 source, revs, checkout = cmdutil.parseurl(ui.expandpath(source),
2079 opts['rev'])
2078 2080 cmdutil.setremoteconfig(ui, opts)
2079 2081
2080 2082 other = hg.repository(ui, source)
2081 2083 ui.status(_('pulling from %s\n') % (source))
2082 2084 if revs:
2083 2085 if 'lookup' in other.capabilities:
2084 2086 revs = [other.lookup(rev) for rev in revs]
2085 2087 else:
2086 2088 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2087 2089 raise util.Abort(error)
2088 2090
2089 2091 modheads = repo.pull(other, heads=revs, force=opts['force'])
2090 2092 return postincoming(ui, repo, modheads, opts['update'])
2091 2093
2092 2094 def push(ui, repo, dest=None, **opts):
2093 2095 """push changes to the specified destination
2094 2096
2095 2097 Push changes from the local repository to the given destination.
2096 2098
2097 2099 This is the symmetrical operation for pull. It helps to move
2098 2100 changes from the current repository to a different one. If the
2099 2101 destination is local this is identical to a pull in that directory
2100 2102 from the current one.
2101 2103
2102 2104 By default, push will refuse to run if it detects the result would
2103 2105 increase the number of remote heads. This generally indicates the
2104 2106 the client has forgotten to sync and merge before pushing.
2105 2107
2106 2108 Valid URLs are of the form:
2107 2109
2108 2110 local/filesystem/path (or file://local/filesystem/path)
2109 2111 ssh://[user@]host[:port]/[path]
2110 2112 http://[user@]host[:port]/[path]
2111 2113 https://[user@]host[:port]/[path]
2112 2114
2113 2115 An optional identifier after # indicates a particular branch, tag,
2114 2116 or changeset to push.
2115 2117
2116 2118 Look at the help text for the pull command for important details
2117 2119 about ssh:// URLs.
2118 2120
2119 2121 Pushing to http:// and https:// URLs is only possible, if this
2120 2122 feature is explicitly enabled on the remote Mercurial server.
2121 2123 """
2122 dest, revs = cmdutil.parseurl(
2124 dest, revs, checkout = cmdutil.parseurl(
2123 2125 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2124 2126 cmdutil.setremoteconfig(ui, opts)
2125 2127
2126 2128 other = hg.repository(ui, dest)
2127 2129 ui.status('pushing to %s\n' % (dest))
2128 2130 if revs:
2129 2131 revs = [repo.lookup(rev) for rev in revs]
2130 2132 r = repo.push(other, opts['force'], revs=revs)
2131 2133 return r == 0
2132 2134
2133 2135 def rawcommit(ui, repo, *pats, **opts):
2134 2136 """raw commit interface (DEPRECATED)
2135 2137
2136 2138 (DEPRECATED)
2137 2139 Lowlevel commit, for use in helper scripts.
2138 2140
2139 2141 This command is not intended to be used by normal users, as it is
2140 2142 primarily useful for importing from other SCMs.
2141 2143
2142 2144 This command is now deprecated and will be removed in a future
2143 2145 release, please use debugsetparents and commit instead.
2144 2146 """
2145 2147
2146 2148 ui.warn(_("(the rawcommit command is deprecated)\n"))
2147 2149
2148 2150 message = cmdutil.logmessage(opts)
2149 2151
2150 2152 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2151 2153 if opts['files']:
2152 2154 files += open(opts['files']).read().splitlines()
2153 2155
2154 2156 parents = [repo.lookup(p) for p in opts['parent']]
2155 2157
2156 2158 try:
2157 2159 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2158 2160 except ValueError, inst:
2159 2161 raise util.Abort(str(inst))
2160 2162
2161 2163 def recover(ui, repo):
2162 2164 """roll back an interrupted transaction
2163 2165
2164 2166 Recover from an interrupted commit or pull.
2165 2167
2166 2168 This command tries to fix the repository status after an interrupted
2167 2169 operation. It should only be necessary when Mercurial suggests it.
2168 2170 """
2169 2171 if repo.recover():
2170 2172 return hg.verify(repo)
2171 2173 return 1
2172 2174
2173 2175 def remove(ui, repo, *pats, **opts):
2174 2176 """remove the specified files on the next commit
2175 2177
2176 2178 Schedule the indicated files for removal from the repository.
2177 2179
2178 2180 This only removes files from the current branch, not from the
2179 2181 entire project history. If the files still exist in the working
2180 2182 directory, they will be deleted from it. If invoked with --after,
2181 2183 files are marked as removed, but not actually unlinked unless --force
2182 2184 is also given. Without exact file names, --after will only mark
2183 2185 files as removed if they are no longer in the working directory.
2184 2186
2185 2187 This command schedules the files to be removed at the next commit.
2186 2188 To undo a remove before that, see hg revert.
2187 2189
2188 2190 Modified files and added files are not removed by default. To
2189 2191 remove them, use the -f/--force option.
2190 2192 """
2191 2193 names = []
2192 2194 if not opts['after'] and not pats:
2193 2195 raise util.Abort(_('no files specified'))
2194 2196 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2195 2197 exact = dict.fromkeys(files)
2196 2198 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2197 2199 modified, added, removed, deleted, unknown = mardu
2198 2200 remove, forget = [], []
2199 2201 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2200 2202 reason = None
2201 2203 if abs in modified and not opts['force']:
2202 2204 reason = _('is modified (use -f to force removal)')
2203 2205 elif abs in added:
2204 2206 if opts['force']:
2205 2207 forget.append(abs)
2206 2208 continue
2207 2209 reason = _('has been marked for add (use -f to force removal)')
2208 2210 elif repo.dirstate.state(abs) == '?':
2209 2211 reason = _('is not managed')
2210 2212 elif opts['after'] and not exact and abs not in deleted:
2211 2213 continue
2212 2214 elif abs in removed:
2213 2215 continue
2214 2216 if reason:
2215 2217 if exact:
2216 2218 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2217 2219 else:
2218 2220 if ui.verbose or not exact:
2219 2221 ui.status(_('removing %s\n') % rel)
2220 2222 remove.append(abs)
2221 2223 repo.forget(forget)
2222 2224 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2223 2225
2224 2226 def rename(ui, repo, *pats, **opts):
2225 2227 """rename files; equivalent of copy + remove
2226 2228
2227 2229 Mark dest as copies of sources; mark sources for deletion. If
2228 2230 dest is a directory, copies are put in that directory. If dest is
2229 2231 a file, there can only be one source.
2230 2232
2231 2233 By default, this command copies the contents of files as they
2232 2234 stand in the working directory. If invoked with --after, the
2233 2235 operation is recorded, but no copying is performed.
2234 2236
2235 2237 This command takes effect in the next commit. To undo a rename
2236 2238 before that, see hg revert.
2237 2239 """
2238 2240 wlock = repo.wlock(0)
2239 2241 errs, copied = docopy(ui, repo, pats, opts, wlock)
2240 2242 names = []
2241 2243 for abs, rel, exact in copied:
2242 2244 if ui.verbose or not exact:
2243 2245 ui.status(_('removing %s\n') % rel)
2244 2246 names.append(abs)
2245 2247 if not opts.get('dry_run'):
2246 2248 repo.remove(names, True, wlock=wlock)
2247 2249 return errs
2248 2250
2249 2251 def revert(ui, repo, *pats, **opts):
2250 2252 """revert files or dirs to their states as of some revision
2251 2253
2252 2254 With no revision specified, revert the named files or directories
2253 2255 to the contents they had in the parent of the working directory.
2254 2256 This restores the contents of the affected files to an unmodified
2255 2257 state and unschedules adds, removes, copies, and renames. If the
2256 2258 working directory has two parents, you must explicitly specify the
2257 2259 revision to revert to.
2258 2260
2259 2261 Modified files are saved with a .orig suffix before reverting.
2260 2262 To disable these backups, use --no-backup.
2261 2263
2262 2264 Using the -r option, revert the given files or directories to their
2263 2265 contents as of a specific revision. This can be helpful to "roll
2264 2266 back" some or all of a change that should not have been committed.
2265 2267
2266 2268 Revert modifies the working directory. It does not commit any
2267 2269 changes, or change the parent of the working directory. If you
2268 2270 revert to a revision other than the parent of the working
2269 2271 directory, the reverted files will thus appear modified
2270 2272 afterwards.
2271 2273
2272 2274 If a file has been deleted, it is restored. If the executable
2273 2275 mode of a file was changed, it is reset.
2274 2276
2275 2277 If names are given, all files matching the names are reverted.
2276 2278
2277 2279 If no arguments are given, no files are reverted.
2278 2280 """
2279 2281
2280 2282 if opts["date"]:
2281 2283 if opts["rev"]:
2282 2284 raise util.Abort(_("you can't specify a revision and a date"))
2283 2285 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2284 2286
2285 2287 if not pats and not opts['all']:
2286 2288 raise util.Abort(_('no files or directories specified; '
2287 2289 'use --all to revert the whole repo'))
2288 2290
2289 2291 parent, p2 = repo.dirstate.parents()
2290 2292 if not opts['rev'] and p2 != nullid:
2291 2293 raise util.Abort(_('uncommitted merge - please provide a '
2292 2294 'specific revision'))
2293 2295 ctx = repo.changectx(opts['rev'])
2294 2296 node = ctx.node()
2295 2297 mf = ctx.manifest()
2296 2298 if node == parent:
2297 2299 pmf = mf
2298 2300 else:
2299 2301 pmf = None
2300 2302
2301 2303 wlock = repo.wlock()
2302 2304
2303 2305 # need all matching names in dirstate and manifest of target rev,
2304 2306 # so have to walk both. do not print errors if files exist in one
2305 2307 # but not other.
2306 2308
2307 2309 names = {}
2308 2310 target_only = {}
2309 2311
2310 2312 # walk dirstate.
2311 2313
2312 2314 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2313 2315 badmatch=mf.has_key):
2314 2316 names[abs] = (rel, exact)
2315 2317 if src == 'b':
2316 2318 target_only[abs] = True
2317 2319
2318 2320 # walk target manifest.
2319 2321
2320 2322 def badmatch(path):
2321 2323 if path in names:
2322 2324 return True
2323 2325 path_ = path + '/'
2324 2326 for f in names:
2325 2327 if f.startswith(path_):
2326 2328 return True
2327 2329 return False
2328 2330
2329 2331 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2330 2332 badmatch=badmatch):
2331 2333 if abs in names or src == 'b':
2332 2334 continue
2333 2335 names[abs] = (rel, exact)
2334 2336 target_only[abs] = True
2335 2337
2336 2338 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2337 2339 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2338 2340
2339 2341 revert = ([], _('reverting %s\n'))
2340 2342 add = ([], _('adding %s\n'))
2341 2343 remove = ([], _('removing %s\n'))
2342 2344 forget = ([], _('forgetting %s\n'))
2343 2345 undelete = ([], _('undeleting %s\n'))
2344 2346 update = {}
2345 2347
2346 2348 disptable = (
2347 2349 # dispatch table:
2348 2350 # file state
2349 2351 # action if in target manifest
2350 2352 # action if not in target manifest
2351 2353 # make backup if in target manifest
2352 2354 # make backup if not in target manifest
2353 2355 (modified, revert, remove, True, True),
2354 2356 (added, revert, forget, True, False),
2355 2357 (removed, undelete, None, False, False),
2356 2358 (deleted, revert, remove, False, False),
2357 2359 (unknown, add, None, True, False),
2358 2360 (target_only, add, None, False, False),
2359 2361 )
2360 2362
2361 2363 entries = names.items()
2362 2364 entries.sort()
2363 2365
2364 2366 for abs, (rel, exact) in entries:
2365 2367 mfentry = mf.get(abs)
2366 2368 target = repo.wjoin(abs)
2367 2369 def handle(xlist, dobackup):
2368 2370 xlist[0].append(abs)
2369 2371 update[abs] = 1
2370 2372 if dobackup and not opts['no_backup'] and util.lexists(target):
2371 2373 bakname = "%s.orig" % rel
2372 2374 ui.note(_('saving current version of %s as %s\n') %
2373 2375 (rel, bakname))
2374 2376 if not opts.get('dry_run'):
2375 2377 util.copyfile(target, bakname)
2376 2378 if ui.verbose or not exact:
2377 2379 ui.status(xlist[1] % rel)
2378 2380 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2379 2381 if abs not in table: continue
2380 2382 # file has changed in dirstate
2381 2383 if mfentry:
2382 2384 handle(hitlist, backuphit)
2383 2385 elif misslist is not None:
2384 2386 handle(misslist, backupmiss)
2385 2387 else:
2386 2388 if exact: ui.warn(_('file not managed: %s\n') % rel)
2387 2389 break
2388 2390 else:
2389 2391 # file has not changed in dirstate
2390 2392 if node == parent:
2391 2393 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2392 2394 continue
2393 2395 if pmf is None:
2394 2396 # only need parent manifest in this unlikely case,
2395 2397 # so do not read by default
2396 2398 pmf = repo.changectx(parent).manifest()
2397 2399 if abs in pmf:
2398 2400 if mfentry:
2399 2401 # if version of file is same in parent and target
2400 2402 # manifests, do nothing
2401 2403 if pmf[abs] != mfentry:
2402 2404 handle(revert, False)
2403 2405 else:
2404 2406 handle(remove, False)
2405 2407
2406 2408 if not opts.get('dry_run'):
2407 2409 repo.dirstate.forget(forget[0])
2408 2410 r = hg.revert(repo, node, update.has_key, wlock)
2409 2411 repo.dirstate.update(add[0], 'a')
2410 2412 repo.dirstate.update(undelete[0], 'n')
2411 2413 repo.dirstate.update(remove[0], 'r')
2412 2414 return r
2413 2415
2414 2416 def rollback(ui, repo):
2415 2417 """roll back the last transaction in this repository
2416 2418
2417 2419 Roll back the last transaction in this repository, restoring the
2418 2420 project to its state prior to the transaction.
2419 2421
2420 2422 Transactions are used to encapsulate the effects of all commands
2421 2423 that create new changesets or propagate existing changesets into a
2422 2424 repository. For example, the following commands are transactional,
2423 2425 and their effects can be rolled back:
2424 2426
2425 2427 commit
2426 2428 import
2427 2429 pull
2428 2430 push (with this repository as destination)
2429 2431 unbundle
2430 2432
2431 2433 This command should be used with care. There is only one level of
2432 2434 rollback, and there is no way to undo a rollback. It will also
2433 2435 restore the dirstate at the time of the last transaction, which
2434 2436 may lose subsequent dirstate changes.
2435 2437
2436 2438 This command is not intended for use on public repositories. Once
2437 2439 changes are visible for pull by other users, rolling a transaction
2438 2440 back locally is ineffective (someone else may already have pulled
2439 2441 the changes). Furthermore, a race is possible with readers of the
2440 2442 repository; for example an in-progress pull from the repository
2441 2443 may fail if a rollback is performed.
2442 2444 """
2443 2445 repo.rollback()
2444 2446
2445 2447 def root(ui, repo):
2446 2448 """print the root (top) of the current working dir
2447 2449
2448 2450 Print the root directory of the current repository.
2449 2451 """
2450 2452 ui.write(repo.root + "\n")
2451 2453
2452 2454 def serve(ui, repo, **opts):
2453 2455 """export the repository via HTTP
2454 2456
2455 2457 Start a local HTTP repository browser and pull server.
2456 2458
2457 2459 By default, the server logs accesses to stdout and errors to
2458 2460 stderr. Use the "-A" and "-E" options to log to files.
2459 2461 """
2460 2462
2461 2463 if opts["stdio"]:
2462 2464 if repo is None:
2463 2465 raise hg.RepoError(_("There is no Mercurial repository here"
2464 2466 " (.hg not found)"))
2465 2467 s = sshserver.sshserver(ui, repo)
2466 2468 s.serve_forever()
2467 2469
2468 2470 parentui = ui.parentui or ui
2469 2471 optlist = ("name templates style address port ipv6"
2470 2472 " accesslog errorlog webdir_conf")
2471 2473 for o in optlist.split():
2472 2474 if opts[o]:
2473 2475 parentui.setconfig("web", o, str(opts[o]))
2474 2476 if (repo is not None) and (repo.ui != parentui):
2475 2477 repo.ui.setconfig("web", o, str(opts[o]))
2476 2478
2477 2479 if repo is None and not ui.config("web", "webdir_conf"):
2478 2480 raise hg.RepoError(_("There is no Mercurial repository here"
2479 2481 " (.hg not found)"))
2480 2482
2481 2483 class service:
2482 2484 def init(self):
2483 2485 util.set_signal_handler()
2484 2486 try:
2485 2487 self.httpd = hgweb.server.create_server(parentui, repo)
2486 2488 except socket.error, inst:
2487 2489 raise util.Abort(_('cannot start server: ') + inst.args[1])
2488 2490
2489 2491 if not ui.verbose: return
2490 2492
2491 2493 if self.httpd.port != 80:
2492 2494 ui.status(_('listening at http://%s:%d/\n') %
2493 2495 (self.httpd.addr, self.httpd.port))
2494 2496 else:
2495 2497 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2496 2498
2497 2499 def run(self):
2498 2500 self.httpd.serve_forever()
2499 2501
2500 2502 service = service()
2501 2503
2502 2504 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2503 2505
2504 2506 def status(ui, repo, *pats, **opts):
2505 2507 """show changed files in the working directory
2506 2508
2507 2509 Show status of files in the repository. If names are given, only
2508 2510 files that match are shown. Files that are clean or ignored, are
2509 2511 not listed unless -c (clean), -i (ignored) or -A is given.
2510 2512
2511 2513 NOTE: status may appear to disagree with diff if permissions have
2512 2514 changed or a merge has occurred. The standard diff format does not
2513 2515 report permission changes and diff only reports changes relative
2514 2516 to one merge parent.
2515 2517
2516 2518 If one revision is given, it is used as the base revision.
2517 2519 If two revisions are given, the difference between them is shown.
2518 2520
2519 2521 The codes used to show the status of files are:
2520 2522 M = modified
2521 2523 A = added
2522 2524 R = removed
2523 2525 C = clean
2524 2526 ! = deleted, but still tracked
2525 2527 ? = not tracked
2526 2528 I = ignored (not shown by default)
2527 2529 = the previous added file was copied from here
2528 2530 """
2529 2531
2530 2532 all = opts['all']
2531 2533 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2532 2534
2533 2535 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2534 2536 cwd = (pats and repo.getcwd()) or ''
2535 2537 modified, added, removed, deleted, unknown, ignored, clean = [
2536 2538 n for n in repo.status(node1=node1, node2=node2, files=files,
2537 2539 match=matchfn,
2538 2540 list_ignored=all or opts['ignored'],
2539 2541 list_clean=all or opts['clean'])]
2540 2542
2541 2543 changetypes = (('modified', 'M', modified),
2542 2544 ('added', 'A', added),
2543 2545 ('removed', 'R', removed),
2544 2546 ('deleted', '!', deleted),
2545 2547 ('unknown', '?', unknown),
2546 2548 ('ignored', 'I', ignored))
2547 2549
2548 2550 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2549 2551
2550 2552 end = opts['print0'] and '\0' or '\n'
2551 2553
2552 2554 for opt, char, changes in ([ct for ct in explicit_changetypes
2553 2555 if all or opts[ct[0]]]
2554 2556 or changetypes):
2555 2557 if opts['no_status']:
2556 2558 format = "%%s%s" % end
2557 2559 else:
2558 2560 format = "%s %%s%s" % (char, end)
2559 2561
2560 2562 for f in changes:
2561 2563 ui.write(format % repo.pathto(f, cwd))
2562 2564 if ((all or opts.get('copies')) and not opts.get('no_status')):
2563 2565 copied = repo.dirstate.copied(f)
2564 2566 if copied:
2565 2567 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2566 2568
2567 2569 def tag(ui, repo, name, rev_=None, **opts):
2568 2570 """add a tag for the current or given revision
2569 2571
2570 2572 Name a particular revision using <name>.
2571 2573
2572 2574 Tags are used to name particular revisions of the repository and are
2573 2575 very useful to compare different revision, to go back to significant
2574 2576 earlier versions or to mark branch points as releases, etc.
2575 2577
2576 2578 If no revision is given, the parent of the working directory is used,
2577 2579 or tip if no revision is checked out.
2578 2580
2579 2581 To facilitate version control, distribution, and merging of tags,
2580 2582 they are stored as a file named ".hgtags" which is managed
2581 2583 similarly to other project files and can be hand-edited if
2582 2584 necessary. The file '.hg/localtags' is used for local tags (not
2583 2585 shared among repositories).
2584 2586 """
2585 2587 if name in ['tip', '.', 'null']:
2586 2588 raise util.Abort(_("the name '%s' is reserved") % name)
2587 2589 if rev_ is not None:
2588 2590 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2589 2591 "please use 'hg tag [-r REV] NAME' instead\n"))
2590 2592 if opts['rev']:
2591 2593 raise util.Abort(_("use only one form to specify the revision"))
2592 2594 if opts['rev'] and opts['remove']:
2593 2595 raise util.Abort(_("--rev and --remove are incompatible"))
2594 2596 if opts['rev']:
2595 2597 rev_ = opts['rev']
2596 2598 message = opts['message']
2597 2599 if opts['remove']:
2598 2600 if not name in repo.tags():
2599 2601 raise util.Abort(_('tag %s does not exist') % name)
2600 2602 rev_ = nullid
2601 2603 if not message:
2602 2604 message = _('Removed tag %s') % name
2603 2605 elif name in repo.tags() and not opts['force']:
2604 2606 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2605 2607 % name)
2606 2608 if not rev_ and repo.dirstate.parents()[1] != nullid:
2607 2609 raise util.Abort(_('uncommitted merge - please provide a '
2608 2610 'specific revision'))
2609 2611 r = repo.changectx(rev_).node()
2610 2612
2611 2613 if not message:
2612 2614 message = _('Added tag %s for changeset %s') % (name, short(r))
2613 2615
2614 2616 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2615 2617
2616 2618 def tags(ui, repo):
2617 2619 """list repository tags
2618 2620
2619 2621 List the repository tags.
2620 2622
2621 2623 This lists both regular and local tags.
2622 2624 """
2623 2625
2624 2626 l = repo.tagslist()
2625 2627 l.reverse()
2626 2628 hexfunc = ui.debugflag and hex or short
2627 2629 for t, n in l:
2628 2630 try:
2629 2631 hn = hexfunc(n)
2630 2632 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2631 2633 except revlog.LookupError:
2632 2634 r = " ?:%s" % hn
2633 2635 if ui.quiet:
2634 2636 ui.write("%s\n" % t)
2635 2637 else:
2636 2638 spaces = " " * (30 - util.locallen(t))
2637 2639 ui.write("%s%s %s\n" % (t, spaces, r))
2638 2640
2639 2641 def tip(ui, repo, **opts):
2640 2642 """show the tip revision
2641 2643
2642 2644 Show the tip revision.
2643 2645 """
2644 2646 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2645 2647
2646 2648 def unbundle(ui, repo, fname1, *fnames, **opts):
2647 2649 """apply one or more changegroup files
2648 2650
2649 2651 Apply one or more compressed changegroup files generated by the
2650 2652 bundle command.
2651 2653 """
2652 2654 fnames = (fname1,) + fnames
2653 2655 result = None
2654 2656 for fname in fnames:
2655 2657 if os.path.exists(fname):
2656 2658 f = open(fname, "rb")
2657 2659 else:
2658 2660 f = urllib.urlopen(fname)
2659 2661 gen = changegroup.readbundle(f, fname)
2660 2662 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2661 2663
2662 2664 return postincoming(ui, repo, modheads, opts['update'])
2663 2665
2664 2666 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2665 2667 """update working directory
2666 2668
2667 2669 Update the working directory to the specified revision, or the
2668 2670 tip of the current branch if none is specified.
2669 2671
2670 2672 If there are no outstanding changes in the working directory and
2671 2673 there is a linear relationship between the current version and the
2672 2674 requested version, the result is the requested version.
2673 2675
2674 2676 To merge the working directory with another revision, use the
2675 2677 merge command.
2676 2678
2677 2679 By default, update will refuse to run if doing so would require
2678 2680 discarding local changes.
2679 2681 """
2680 2682 if rev and node:
2681 2683 raise util.Abort(_("please specify just one revision"))
2682 2684
2683 2685 if not rev:
2684 2686 rev = node
2685 2687
2686 2688 if date:
2687 2689 if rev:
2688 2690 raise util.Abort(_("you can't specify a revision and a date"))
2689 2691 rev = cmdutil.finddate(ui, repo, date)
2690 2692
2691 2693 if clean:
2692 2694 return hg.clean(repo, rev)
2693 2695 else:
2694 2696 return hg.update(repo, rev)
2695 2697
2696 2698 def verify(ui, repo):
2697 2699 """verify the integrity of the repository
2698 2700
2699 2701 Verify the integrity of the current repository.
2700 2702
2701 2703 This will perform an extensive check of the repository's
2702 2704 integrity, validating the hashes and checksums of each entry in
2703 2705 the changelog, manifest, and tracked files, as well as the
2704 2706 integrity of their crosslinks and indices.
2705 2707 """
2706 2708 return hg.verify(repo)
2707 2709
2708 2710 def version_(ui):
2709 2711 """output version and copyright information"""
2710 2712 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2711 2713 % version.get_version())
2712 2714 ui.status(_(
2713 2715 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2714 2716 "This is free software; see the source for copying conditions. "
2715 2717 "There is NO\nwarranty; "
2716 2718 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2717 2719 ))
2718 2720
2719 2721 # Command options and aliases are listed here, alphabetically
2720 2722
2721 2723 globalopts = [
2722 2724 ('R', 'repository', '',
2723 2725 _('repository root directory or symbolic path name')),
2724 2726 ('', 'cwd', '', _('change working directory')),
2725 2727 ('y', 'noninteractive', None,
2726 2728 _('do not prompt, assume \'yes\' for any required answers')),
2727 2729 ('q', 'quiet', None, _('suppress output')),
2728 2730 ('v', 'verbose', None, _('enable additional output')),
2729 2731 ('', 'config', [], _('set/override config option')),
2730 2732 ('', 'debug', None, _('enable debugging output')),
2731 2733 ('', 'debugger', None, _('start debugger')),
2732 2734 ('', 'encoding', util._encoding, _('set the charset encoding')),
2733 2735 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2734 2736 ('', 'lsprof', None, _('print improved command execution profile')),
2735 2737 ('', 'traceback', None, _('print traceback on exception')),
2736 2738 ('', 'time', None, _('time how long the command takes')),
2737 2739 ('', 'profile', None, _('print command execution profile')),
2738 2740 ('', 'version', None, _('output version information and exit')),
2739 2741 ('h', 'help', None, _('display help and exit')),
2740 2742 ]
2741 2743
2742 2744 dryrunopts = [('n', 'dry-run', None,
2743 2745 _('do not perform actions, just print output'))]
2744 2746
2745 2747 remoteopts = [
2746 2748 ('e', 'ssh', '', _('specify ssh command to use')),
2747 2749 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2748 2750 ]
2749 2751
2750 2752 walkopts = [
2751 2753 ('I', 'include', [], _('include names matching the given patterns')),
2752 2754 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2753 2755 ]
2754 2756
2755 2757 commitopts = [
2756 2758 ('m', 'message', '', _('use <text> as commit message')),
2757 2759 ('l', 'logfile', '', _('read commit message from <file>')),
2758 2760 ]
2759 2761
2760 2762 table = {
2761 2763 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2762 2764 "addremove":
2763 2765 (addremove,
2764 2766 [('s', 'similarity', '',
2765 2767 _('guess renamed files by similarity (0<=s<=100)')),
2766 2768 ] + walkopts + dryrunopts,
2767 2769 _('hg addremove [OPTION]... [FILE]...')),
2768 2770 "^annotate":
2769 2771 (annotate,
2770 2772 [('r', 'rev', '', _('annotate the specified revision')),
2771 2773 ('f', 'follow', None, _('follow file copies and renames')),
2772 2774 ('a', 'text', None, _('treat all files as text')),
2773 2775 ('u', 'user', None, _('list the author')),
2774 2776 ('d', 'date', None, _('list the date')),
2775 2777 ('n', 'number', None, _('list the revision number (default)')),
2776 2778 ('c', 'changeset', None, _('list the changeset')),
2777 2779 ] + walkopts,
2778 2780 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2779 2781 "archive":
2780 2782 (archive,
2781 2783 [('', 'no-decode', None, _('do not pass files through decoders')),
2782 2784 ('p', 'prefix', '', _('directory prefix for files in archive')),
2783 2785 ('r', 'rev', '', _('revision to distribute')),
2784 2786 ('t', 'type', '', _('type of distribution to create')),
2785 2787 ] + walkopts,
2786 2788 _('hg archive [OPTION]... DEST')),
2787 2789 "backout":
2788 2790 (backout,
2789 2791 [('', 'merge', None,
2790 2792 _('merge with old dirstate parent after backout')),
2791 2793 ('d', 'date', '', _('record datecode as commit date')),
2792 2794 ('', 'parent', '', _('parent to choose when backing out merge')),
2793 2795 ('u', 'user', '', _('record user as committer')),
2794 2796 ('r', 'rev', '', _('revision to backout')),
2795 2797 ] + walkopts + commitopts,
2796 2798 _('hg backout [OPTION]... [-r] REV')),
2797 2799 "branch":
2798 2800 (branch,
2799 2801 [('f', 'force', None,
2800 2802 _('set branch name even if it shadows an existing branch'))],
2801 2803 _('hg branch [NAME]')),
2802 2804 "branches":
2803 2805 (branches,
2804 2806 [('a', 'active', False,
2805 2807 _('show only branches that have unmerged heads'))],
2806 2808 _('hg branches [-a]')),
2807 2809 "bundle":
2808 2810 (bundle,
2809 2811 [('f', 'force', None,
2810 2812 _('run even when remote repository is unrelated')),
2811 2813 ('r', 'rev', [],
2812 2814 _('a changeset you would like to bundle')),
2813 2815 ('', 'base', [],
2814 2816 _('a base changeset to specify instead of a destination')),
2815 2817 ] + remoteopts,
2816 2818 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2817 2819 "cat":
2818 2820 (cat,
2819 2821 [('o', 'output', '', _('print output to file with formatted name')),
2820 2822 ('r', 'rev', '', _('print the given revision')),
2821 2823 ] + walkopts,
2822 2824 _('hg cat [OPTION]... FILE...')),
2823 2825 "^clone":
2824 2826 (clone,
2825 2827 [('U', 'noupdate', None, _('do not update the new working directory')),
2826 2828 ('r', 'rev', [],
2827 2829 _('a changeset you would like to have after cloning')),
2828 2830 ('', 'pull', None, _('use pull protocol to copy metadata')),
2829 2831 ('', 'uncompressed', None,
2830 2832 _('use uncompressed transfer (fast over LAN)')),
2831 2833 ] + remoteopts,
2832 2834 _('hg clone [OPTION]... SOURCE [DEST]')),
2833 2835 "^commit|ci":
2834 2836 (commit,
2835 2837 [('A', 'addremove', None,
2836 2838 _('mark new/missing files as added/removed before committing')),
2837 2839 ('d', 'date', '', _('record datecode as commit date')),
2838 2840 ('u', 'user', '', _('record user as commiter')),
2839 2841 ] + walkopts + commitopts,
2840 2842 _('hg commit [OPTION]... [FILE]...')),
2841 2843 "copy|cp":
2842 2844 (copy,
2843 2845 [('A', 'after', None, _('record a copy that has already occurred')),
2844 2846 ('f', 'force', None,
2845 2847 _('forcibly copy over an existing managed file')),
2846 2848 ] + walkopts + dryrunopts,
2847 2849 _('hg copy [OPTION]... [SOURCE]... DEST')),
2848 2850 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2849 2851 "debugcomplete":
2850 2852 (debugcomplete,
2851 2853 [('o', 'options', None, _('show the command options'))],
2852 2854 _('debugcomplete [-o] CMD')),
2853 2855 "debuginstall": (debuginstall, [], _('debuginstall')),
2854 2856 "debugrebuildstate":
2855 2857 (debugrebuildstate,
2856 2858 [('r', 'rev', '', _('revision to rebuild to'))],
2857 2859 _('debugrebuildstate [-r REV] [REV]')),
2858 2860 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2859 2861 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2860 2862 "debugstate": (debugstate, [], _('debugstate')),
2861 2863 "debugdate":
2862 2864 (debugdate,
2863 2865 [('e', 'extended', None, _('try extended date formats'))],
2864 2866 _('debugdate [-e] DATE [RANGE]')),
2865 2867 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2866 2868 "debugindex": (debugindex, [], _('debugindex FILE')),
2867 2869 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2868 2870 "debugrename":
2869 2871 (debugrename,
2870 2872 [('r', 'rev', '', _('revision to debug'))],
2871 2873 _('debugrename [-r REV] FILE')),
2872 2874 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2873 2875 "^diff":
2874 2876 (diff,
2875 2877 [('r', 'rev', [], _('revision')),
2876 2878 ('a', 'text', None, _('treat all files as text')),
2877 2879 ('p', 'show-function', None,
2878 2880 _('show which function each change is in')),
2879 2881 ('g', 'git', None, _('use git extended diff format')),
2880 2882 ('', 'nodates', None, _("don't include dates in diff headers")),
2881 2883 ('w', 'ignore-all-space', None,
2882 2884 _('ignore white space when comparing lines')),
2883 2885 ('b', 'ignore-space-change', None,
2884 2886 _('ignore changes in the amount of white space')),
2885 2887 ('B', 'ignore-blank-lines', None,
2886 2888 _('ignore changes whose lines are all blank')),
2887 2889 ] + walkopts,
2888 2890 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2889 2891 "^export":
2890 2892 (export,
2891 2893 [('o', 'output', '', _('print output to file with formatted name')),
2892 2894 ('a', 'text', None, _('treat all files as text')),
2893 2895 ('g', 'git', None, _('use git extended diff format')),
2894 2896 ('', 'nodates', None, _("don't include dates in diff headers")),
2895 2897 ('', 'switch-parent', None, _('diff against the second parent'))],
2896 2898 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2897 2899 "grep":
2898 2900 (grep,
2899 2901 [('0', 'print0', None, _('end fields with NUL')),
2900 2902 ('', 'all', None, _('print all revisions that match')),
2901 2903 ('f', 'follow', None,
2902 2904 _('follow changeset history, or file history across copies and renames')),
2903 2905 ('i', 'ignore-case', None, _('ignore case when matching')),
2904 2906 ('l', 'files-with-matches', None,
2905 2907 _('print only filenames and revs that match')),
2906 2908 ('n', 'line-number', None, _('print matching line numbers')),
2907 2909 ('r', 'rev', [], _('search in given revision range')),
2908 2910 ('u', 'user', None, _('print user who committed change')),
2909 2911 ] + walkopts,
2910 2912 _('hg grep [OPTION]... PATTERN [FILE]...')),
2911 2913 "heads":
2912 2914 (heads,
2913 2915 [('', 'style', '', _('display using template map file')),
2914 2916 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2915 2917 ('', 'template', '', _('display with template'))],
2916 2918 _('hg heads [-r REV] [REV]...')),
2917 2919 "help": (help_, [], _('hg help [COMMAND]')),
2918 2920 "identify|id":
2919 2921 (identify,
2920 2922 [('r', 'rev', '', _('identify the specified rev')),
2921 2923 ('n', 'num', None, _('show local revision number')),
2922 2924 ('i', 'id', None, _('show global revision id')),
2923 2925 ('b', 'branch', None, _('show branch')),
2924 2926 ('t', 'tags', None, _('show tags'))],
2925 2927 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2926 2928 "import|patch":
2927 2929 (import_,
2928 2930 [('p', 'strip', 1,
2929 2931 _('directory strip option for patch. This has the same\n'
2930 2932 'meaning as the corresponding patch option')),
2931 2933 ('b', 'base', '', _('base path')),
2932 2934 ('f', 'force', None,
2933 2935 _('skip check for outstanding uncommitted changes')),
2934 2936 ('', 'exact', None,
2935 2937 _('apply patch to the nodes from which it was generated')),
2936 2938 ('', 'import-branch', None,
2937 2939 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2938 2940 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2939 2941 "incoming|in": (incoming,
2940 2942 [('M', 'no-merges', None, _('do not show merges')),
2941 2943 ('f', 'force', None,
2942 2944 _('run even when remote repository is unrelated')),
2943 2945 ('', 'style', '', _('display using template map file')),
2944 2946 ('n', 'newest-first', None, _('show newest record first')),
2945 2947 ('', 'bundle', '', _('file to store the bundles into')),
2946 2948 ('p', 'patch', None, _('show patch')),
2947 2949 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2948 2950 ('', 'template', '', _('display with template')),
2949 2951 ] + remoteopts,
2950 2952 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2951 2953 ' [--bundle FILENAME] [SOURCE]')),
2952 2954 "^init":
2953 2955 (init,
2954 2956 remoteopts,
2955 2957 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2956 2958 "locate":
2957 2959 (locate,
2958 2960 [('r', 'rev', '', _('search the repository as it stood at rev')),
2959 2961 ('0', 'print0', None,
2960 2962 _('end filenames with NUL, for use with xargs')),
2961 2963 ('f', 'fullpath', None,
2962 2964 _('print complete paths from the filesystem root')),
2963 2965 ] + walkopts,
2964 2966 _('hg locate [OPTION]... [PATTERN]...')),
2965 2967 "^log|history":
2966 2968 (log,
2967 2969 [('f', 'follow', None,
2968 2970 _('follow changeset history, or file history across copies and renames')),
2969 2971 ('', 'follow-first', None,
2970 2972 _('only follow the first parent of merge changesets')),
2971 2973 ('d', 'date', '', _('show revs matching date spec')),
2972 2974 ('C', 'copies', None, _('show copied files')),
2973 2975 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2974 2976 ('l', 'limit', '', _('limit number of changes displayed')),
2975 2977 ('r', 'rev', [], _('show the specified revision or range')),
2976 2978 ('', 'removed', None, _('include revs where files were removed')),
2977 2979 ('M', 'no-merges', None, _('do not show merges')),
2978 2980 ('', 'style', '', _('display using template map file')),
2979 2981 ('m', 'only-merges', None, _('show only merges')),
2980 2982 ('p', 'patch', None, _('show patch')),
2981 2983 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2982 2984 ('', 'template', '', _('display with template')),
2983 2985 ] + walkopts,
2984 2986 _('hg log [OPTION]... [FILE]')),
2985 2987 "manifest": (manifest, [], _('hg manifest [REV]')),
2986 2988 "^merge":
2987 2989 (merge,
2988 2990 [('f', 'force', None, _('force a merge with outstanding changes')),
2989 2991 ('r', 'rev', '', _('revision to merge')),
2990 2992 ],
2991 2993 _('hg merge [-f] [[-r] REV]')),
2992 2994 "outgoing|out": (outgoing,
2993 2995 [('M', 'no-merges', None, _('do not show merges')),
2994 2996 ('f', 'force', None,
2995 2997 _('run even when remote repository is unrelated')),
2996 2998 ('p', 'patch', None, _('show patch')),
2997 2999 ('', 'style', '', _('display using template map file')),
2998 3000 ('r', 'rev', [], _('a specific revision you would like to push')),
2999 3001 ('n', 'newest-first', None, _('show newest record first')),
3000 3002 ('', 'template', '', _('display with template')),
3001 3003 ] + remoteopts,
3002 3004 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3003 3005 "^parents":
3004 3006 (parents,
3005 3007 [('r', 'rev', '', _('show parents from the specified rev')),
3006 3008 ('', 'style', '', _('display using template map file')),
3007 3009 ('', 'template', '', _('display with template'))],
3008 3010 _('hg parents [-r REV] [FILE]')),
3009 3011 "paths": (paths, [], _('hg paths [NAME]')),
3010 3012 "^pull":
3011 3013 (pull,
3012 3014 [('u', 'update', None,
3013 3015 _('update to new tip if changesets were pulled')),
3014 3016 ('f', 'force', None,
3015 3017 _('run even when remote repository is unrelated')),
3016 3018 ('r', 'rev', [],
3017 3019 _('a specific revision up to which you would like to pull')),
3018 3020 ] + remoteopts,
3019 3021 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3020 3022 "^push":
3021 3023 (push,
3022 3024 [('f', 'force', None, _('force push')),
3023 3025 ('r', 'rev', [], _('a specific revision you would like to push')),
3024 3026 ] + remoteopts,
3025 3027 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3026 3028 "debugrawcommit|rawcommit":
3027 3029 (rawcommit,
3028 3030 [('p', 'parent', [], _('parent')),
3029 3031 ('d', 'date', '', _('date code')),
3030 3032 ('u', 'user', '', _('user')),
3031 3033 ('F', 'files', '', _('file list'))
3032 3034 ] + commitopts,
3033 3035 _('hg debugrawcommit [OPTION]... [FILE]...')),
3034 3036 "recover": (recover, [], _('hg recover')),
3035 3037 "^remove|rm":
3036 3038 (remove,
3037 3039 [('A', 'after', None, _('record remove that has already occurred')),
3038 3040 ('f', 'force', None, _('remove file even if modified')),
3039 3041 ] + walkopts,
3040 3042 _('hg remove [OPTION]... FILE...')),
3041 3043 "rename|mv":
3042 3044 (rename,
3043 3045 [('A', 'after', None, _('record a rename that has already occurred')),
3044 3046 ('f', 'force', None,
3045 3047 _('forcibly copy over an existing managed file')),
3046 3048 ] + walkopts + dryrunopts,
3047 3049 _('hg rename [OPTION]... SOURCE... DEST')),
3048 3050 "^revert":
3049 3051 (revert,
3050 3052 [('a', 'all', None, _('revert all changes when no arguments given')),
3051 3053 ('d', 'date', '', _('tipmost revision matching date')),
3052 3054 ('r', 'rev', '', _('revision to revert to')),
3053 3055 ('', 'no-backup', None, _('do not save backup copies of files')),
3054 3056 ] + walkopts + dryrunopts,
3055 3057 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3056 3058 "rollback": (rollback, [], _('hg rollback')),
3057 3059 "root": (root, [], _('hg root')),
3058 3060 "showconfig|debugconfig":
3059 3061 (showconfig,
3060 3062 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3061 3063 _('showconfig [-u] [NAME]...')),
3062 3064 "^serve":
3063 3065 (serve,
3064 3066 [('A', 'accesslog', '', _('name of access log file to write to')),
3065 3067 ('d', 'daemon', None, _('run server in background')),
3066 3068 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3067 3069 ('E', 'errorlog', '', _('name of error log file to write to')),
3068 3070 ('p', 'port', 0, _('port to use (default: 8000)')),
3069 3071 ('a', 'address', '', _('address to use')),
3070 3072 ('n', 'name', '',
3071 3073 _('name to show in web pages (default: working dir)')),
3072 3074 ('', 'webdir-conf', '', _('name of the webdir config file'
3073 3075 ' (serve more than one repo)')),
3074 3076 ('', 'pid-file', '', _('name of file to write process ID to')),
3075 3077 ('', 'stdio', None, _('for remote clients')),
3076 3078 ('t', 'templates', '', _('web templates to use')),
3077 3079 ('', 'style', '', _('template style to use')),
3078 3080 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3079 3081 _('hg serve [OPTION]...')),
3080 3082 "^status|st":
3081 3083 (status,
3082 3084 [('A', 'all', None, _('show status of all files')),
3083 3085 ('m', 'modified', None, _('show only modified files')),
3084 3086 ('a', 'added', None, _('show only added files')),
3085 3087 ('r', 'removed', None, _('show only removed files')),
3086 3088 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3087 3089 ('c', 'clean', None, _('show only files without changes')),
3088 3090 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3089 3091 ('i', 'ignored', None, _('show only ignored files')),
3090 3092 ('n', 'no-status', None, _('hide status prefix')),
3091 3093 ('C', 'copies', None, _('show source of copied files')),
3092 3094 ('0', 'print0', None,
3093 3095 _('end filenames with NUL, for use with xargs')),
3094 3096 ('', 'rev', [], _('show difference from revision')),
3095 3097 ] + walkopts,
3096 3098 _('hg status [OPTION]... [FILE]...')),
3097 3099 "tag":
3098 3100 (tag,
3099 3101 [('f', 'force', None, _('replace existing tag')),
3100 3102 ('l', 'local', None, _('make the tag local')),
3101 3103 ('m', 'message', '', _('message for tag commit log entry')),
3102 3104 ('d', 'date', '', _('record datecode as commit date')),
3103 3105 ('u', 'user', '', _('record user as commiter')),
3104 3106 ('r', 'rev', '', _('revision to tag')),
3105 3107 ('', 'remove', None, _('remove a tag'))],
3106 3108 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3107 3109 "tags": (tags, [], _('hg tags')),
3108 3110 "tip":
3109 3111 (tip,
3110 3112 [('', 'style', '', _('display using template map file')),
3111 3113 ('p', 'patch', None, _('show patch')),
3112 3114 ('', 'template', '', _('display with template'))],
3113 3115 _('hg tip [-p]')),
3114 3116 "unbundle":
3115 3117 (unbundle,
3116 3118 [('u', 'update', None,
3117 3119 _('update to new tip if changesets were unbundled'))],
3118 3120 _('hg unbundle [-u] FILE...')),
3119 3121 "^update|up|checkout|co":
3120 3122 (update,
3121 3123 [('C', 'clean', None, _('overwrite locally modified files')),
3122 3124 ('d', 'date', '', _('tipmost revision matching date')),
3123 3125 ('r', 'rev', '', _('revision'))],
3124 3126 _('hg update [-C] [-d DATE] [[-r] REV]')),
3125 3127 "verify": (verify, [], _('hg verify')),
3126 3128 "version": (version_, [], _('hg version')),
3127 3129 }
3128 3130
3129 3131 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3130 3132 " debugindex debugindexdot debugdate debuginstall")
3131 3133 optionalrepo = ("paths serve showconfig")
3132 3134
3133 3135 def dispatch(args):
3134 3136 try:
3135 3137 u = ui.ui(traceback='--traceback' in args)
3136 3138 except util.Abort, inst:
3137 3139 sys.stderr.write(_("abort: %s\n") % inst)
3138 3140 return -1
3139 3141 return cmdutil.runcatch(u, args)
3140 3142
3141 3143 def run():
3142 3144 sys.exit(dispatch(sys.argv[1:]))
@@ -1,290 +1,290
1 1 # hg.py - repository classes for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 from node import *
10 10 from repo import *
11 11 from i18n import _
12 12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
13 13 import errno, lock, os, shutil, util, cmdutil
14 14 import merge as _merge
15 15 import verify as _verify
16 16
17 17 def _local(path):
18 18 return (os.path.isfile(util.drop_scheme('file', path)) and
19 19 bundlerepo or localrepo)
20 20
21 21 schemes = {
22 22 'bundle': bundlerepo,
23 23 'file': _local,
24 24 'hg': httprepo,
25 25 'http': httprepo,
26 26 'https': httprepo,
27 27 'old-http': statichttprepo,
28 28 'ssh': sshrepo,
29 29 'static-http': statichttprepo,
30 30 }
31 31
32 32 def _lookup(path):
33 33 scheme = 'file'
34 34 if path:
35 35 c = path.find(':')
36 36 if c > 0:
37 37 scheme = path[:c]
38 38 thing = schemes.get(scheme) or schemes['file']
39 39 try:
40 40 return thing(path)
41 41 except TypeError:
42 42 return thing
43 43
44 44 def islocal(repo):
45 45 '''return true if repo or path is local'''
46 46 if isinstance(repo, str):
47 47 try:
48 48 return _lookup(repo).islocal(repo)
49 49 except AttributeError:
50 50 return False
51 51 return repo.local()
52 52
53 53 repo_setup_hooks = []
54 54
55 55 def repository(ui, path='', create=False):
56 56 """return a repository object for the specified path"""
57 57 repo = _lookup(path).instance(ui, path, create)
58 58 ui = getattr(repo, "ui", ui)
59 59 for hook in repo_setup_hooks:
60 60 hook(ui, repo)
61 61 return repo
62 62
63 63 def defaultdest(source):
64 64 '''return default destination of clone if none is given'''
65 65 return os.path.basename(os.path.normpath(source))
66 66
67 67 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
68 68 stream=False):
69 69 """Make a copy of an existing repository.
70 70
71 71 Create a copy of an existing repository in a new directory. The
72 72 source and destination are URLs, as passed to the repository
73 73 function. Returns a pair of repository objects, the source and
74 74 newly created destination.
75 75
76 76 The location of the source is added to the new repository's
77 77 .hg/hgrc file, as the default to be used for future pulls and
78 78 pushes.
79 79
80 80 If an exception is raised, the partly cloned/updated destination
81 81 repository will be deleted.
82 82
83 83 Arguments:
84 84
85 85 source: repository object or URL
86 86
87 87 dest: URL of destination repository to create (defaults to base
88 88 name of source repository)
89 89
90 90 pull: always pull from source repository, even in local case
91 91
92 92 stream: stream raw data uncompressed from repository (fast over
93 93 LAN, slow over WAN)
94 94
95 95 rev: revision to clone up to (implies pull=True)
96 96
97 97 update: update working directory after clone completes, if
98 98 destination is local repository
99 99 """
100 100
101 101 origsource = source
102 source, rev = cmdutil.parseurl(ui.expandpath(source), rev)
102 source, rev, checkout = cmdutil.parseurl(ui.expandpath(source), rev)
103 103
104 104 if isinstance(source, str):
105 105 src_repo = repository(ui, source)
106 106 else:
107 107 src_repo = source
108 108 source = src_repo.url()
109 109
110 110 if dest is None:
111 111 dest = defaultdest(source)
112 112 ui.status(_("destination directory: %s\n") % dest)
113 113
114 114 def localpath(path):
115 115 if path.startswith('file://'):
116 116 return path[7:]
117 117 if path.startswith('file:'):
118 118 return path[5:]
119 119 return path
120 120
121 121 dest = localpath(dest)
122 122 source = localpath(source)
123 123
124 124 if os.path.exists(dest):
125 125 raise util.Abort(_("destination '%s' already exists") % dest)
126 126
127 127 class DirCleanup(object):
128 128 def __init__(self, dir_):
129 129 self.rmtree = shutil.rmtree
130 130 self.dir_ = dir_
131 131 def close(self):
132 132 self.dir_ = None
133 133 def __del__(self):
134 134 if self.dir_:
135 135 self.rmtree(self.dir_, True)
136 136
137 137 dir_cleanup = None
138 138 if islocal(dest):
139 139 dir_cleanup = DirCleanup(dest)
140 140
141 141 abspath = origsource
142 142 copy = False
143 143 if src_repo.local() and islocal(dest):
144 144 abspath = os.path.abspath(origsource)
145 145 copy = not pull and not rev
146 146
147 147 src_lock, dest_lock = None, None
148 148 if copy:
149 149 try:
150 150 # we use a lock here because if we race with commit, we
151 151 # can end up with extra data in the cloned revlogs that's
152 152 # not pointed to by changesets, thus causing verify to
153 153 # fail
154 154 src_lock = src_repo.lock()
155 155 except lock.LockException:
156 156 copy = False
157 157
158 158 if copy:
159 159 def force_copy(src, dst):
160 160 try:
161 161 util.copyfiles(src, dst)
162 162 except OSError, inst:
163 163 if inst.errno != errno.ENOENT:
164 164 raise
165 165
166 166 src_store = os.path.realpath(src_repo.spath)
167 167 if not os.path.exists(dest):
168 168 os.mkdir(dest)
169 169 dest_path = os.path.realpath(os.path.join(dest, ".hg"))
170 170 os.mkdir(dest_path)
171 171 if src_repo.spath != src_repo.path:
172 172 dest_store = os.path.join(dest_path, "store")
173 173 os.mkdir(dest_store)
174 174 else:
175 175 dest_store = dest_path
176 176 # copy the requires file
177 177 force_copy(src_repo.join("requires"),
178 178 os.path.join(dest_path, "requires"))
179 179 # we lock here to avoid premature writing to the target
180 180 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
181 181
182 182 files = ("data",
183 183 "00manifest.d", "00manifest.i",
184 184 "00changelog.d", "00changelog.i")
185 185 for f in files:
186 186 src = os.path.join(src_store, f)
187 187 dst = os.path.join(dest_store, f)
188 188 force_copy(src, dst)
189 189
190 190 # we need to re-init the repo after manually copying the data
191 191 # into it
192 192 dest_repo = repository(ui, dest)
193 193
194 194 else:
195 195 dest_repo = repository(ui, dest, create=True)
196 196
197 197 revs = None
198 198 if rev:
199 199 if 'lookup' not in src_repo.capabilities:
200 200 raise util.Abort(_("src repository does not support revision "
201 201 "lookup and so doesn't support clone by "
202 202 "revision"))
203 203 revs = [src_repo.lookup(r) for r in rev]
204 204
205 205 if dest_repo.local():
206 206 dest_repo.clone(src_repo, heads=revs, stream=stream)
207 207 elif src_repo.local():
208 208 src_repo.push(dest_repo, revs=revs)
209 209 else:
210 210 raise util.Abort(_("clone from remote to remote not supported"))
211 211
212 212 if src_lock:
213 213 src_lock.release()
214 214
215 215 if dir_cleanup:
216 216 dir_cleanup.close()
217 217
218 218 if dest_repo.local():
219 219 fp = dest_repo.opener("hgrc", "w", text=True)
220 220 fp.write("[paths]\n")
221 221 fp.write("default = %s\n" % abspath)
222 222 fp.close()
223 223
224 224 if dest_lock:
225 225 dest_lock.release()
226 226
227 227 if update:
228 228 try:
229 229 checkout = dest_repo.lookup("default")
230 230 except:
231 231 checkout = dest_repo.changelog.tip()
232 232 _update(dest_repo, checkout)
233 233
234 234 return src_repo, dest_repo
235 235
236 236 def _showstats(repo, stats):
237 237 stats = ((stats[0], _("updated")),
238 238 (stats[1], _("merged")),
239 239 (stats[2], _("removed")),
240 240 (stats[3], _("unresolved")))
241 241 note = ", ".join([_("%d files %s") % s for s in stats])
242 242 repo.ui.status("%s\n" % note)
243 243
244 244 def _update(repo, node): return update(repo, node)
245 245
246 246 def update(repo, node):
247 247 """update the working directory to node, merging linear changes"""
248 248 pl = repo.parents()
249 249 stats = _merge.update(repo, node, False, False, None, None)
250 250 _showstats(repo, stats)
251 251 if stats[3]:
252 252 repo.ui.status(_("There are unresolved merges with"
253 253 " locally modified files.\n"))
254 254 if stats[1]:
255 255 repo.ui.status(_("You can finish the partial merge using:\n"))
256 256 else:
257 257 repo.ui.status(_("You can redo the full merge using:\n"))
258 258 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
259 259 repo.ui.status(_(" hg update %s\n hg update %s\n")
260 260 % (pl[0].rev(), repo.changectx(node).rev()))
261 261 return stats[3]
262 262
263 263 def clean(repo, node, wlock=None, show_stats=True):
264 264 """forcibly switch the working directory to node, clobbering changes"""
265 265 stats = _merge.update(repo, node, False, True, None, wlock)
266 266 if show_stats: _showstats(repo, stats)
267 267 return stats[3]
268 268
269 269 def merge(repo, node, force=None, remind=True, wlock=None):
270 270 """branch merge with node, resolving changes"""
271 271 stats = _merge.update(repo, node, True, force, False, wlock)
272 272 _showstats(repo, stats)
273 273 if stats[3]:
274 274 pl = repo.parents()
275 275 repo.ui.status(_("There are unresolved merges,"
276 276 " you can redo the full merge using:\n"
277 277 " hg update -C %s\n"
278 278 " hg merge %s\n")
279 279 % (pl[0].rev(), pl[1].rev()))
280 280 elif remind:
281 281 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
282 282 return stats[3]
283 283
284 284 def revert(repo, node, choose, wlock):
285 285 """revert changes to revision in node without updating dirstate"""
286 286 return _merge.update(repo, node, False, True, choose, wlock)[3]
287 287
288 288 def verify(repo):
289 289 """verify the consistency of a repository"""
290 290 return _verify.verify(repo)
General Comments 0
You need to be logged in to leave comments. Login now