commands.py 130 KB
Newer Older
mpm@selenic.com's avatar
mpm@selenic.com committed
1 2
# commands.py - command processing for mercurial
#
3
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
mpm@selenic.com's avatar
mpm@selenic.com committed
4
#
5 6
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
mpm@selenic.com's avatar
mpm@selenic.com committed
7

8
from node import hex, nullid, nullrev, short
9
from lock import release
10
from i18n import _, gettext
11
import os, re, sys, subprocess, difflib, time, tempfile
Matt Mackall's avatar
Matt Mackall committed
12
import hg, util, revlog, bundlerepo, extensions, copies, context, error
13
import patch, help, mdiff, url, encoding
14 15
import archival, changegroup, cmdutil, sshserver, hbisect
from hgweb import server
Matt Mackall's avatar
Matt Mackall committed
16
import merge as merge_
17
import minirst
18

mpm@selenic.com's avatar
mpm@selenic.com committed
19
# Commands start here, listed alphabetically
20

21
def add(ui, repo, *pats, **opts):
22 23
    """add the specified files on the next commit

24
    Schedule files to be version controlled and added to the repository.
25

26 27
    The files will be added to the repository at the next commit. To undo an
    add before that, see hg forget.
28

29
    If no names are given, add all files to the repository.
30 31
    """

32
    bad = []
33
    exacts = {}
34
    names = []
35
    m = cmdutil.match(repo, pats, opts)
36 37 38 39 40 41 42 43 44
    oldbad = m.bad
    m.bad = lambda x,y: bad.append(x) or oldbad(x,y)

    for f in repo.walk(m):
        exact = m.exact(f)
        if exact or f not in repo.dirstate:
            names.append(f)
            if ui.verbose or not exact:
                ui.status(_('adding %s\n') % m.rel(f))
45
    if not opts.get('dry_run'):
46 47
        bad += [f for f in repo.add(names) if f in m.files()]
    return bad and 1 or 0
48

49
def addremove(ui, repo, *pats, **opts):
50
    """add all new files, delete all missing files
51

52
    Add all new files and remove all missing files from the repository.
53

54 55
    New files are ignored if they match any of the patterns in .hgignore. As
    with add, these changes take effect at the next commit.
56

57 58 59 60 61
    Use the -s/--similarity option to detect renamed files. With a parameter
    greater than 0, this compares every removed file with every added file and
    records those similar enough as renames. This option takes a percentage
    between 0 (disabled) and 100 (files must be identical) as its parameter.
    Detecting renamed files this way can be expensive.
62
    """
63 64 65 66
    try:
        sim = float(opts.get('similarity') or 0)
    except ValueError:
        raise util.Abort(_('similarity must be a number'))
67 68 69
    if sim < 0 or sim > 100:
        raise util.Abort(_('similarity must be between 0 and 100'))
    return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
70

71
def annotate(ui, repo, *pats, **opts):
72
    """show changeset information by line for each file
73

74
    List changes in files, showing the revision id responsible for each line
75

76
    This command is useful for discovering when a change was made and by whom.
77

78 79 80
    Without the -a/--text option, annotate will avoid processing files it
    detects as binary. With -a, annotate will annotate the file anyway,
    although the results will probably be neither useful nor desirable.
81
    """
82 83
    datefunc = ui.quiet and util.shortdate or util.datestr
    getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
84

85
    if not pats:
86
        raise util.Abort(_('at least one filename or pattern is required'))
87

88 89 90 91 92 93 94
    opmap = [('user', lambda x: ui.shortuser(x[0].user())),
             ('number', lambda x: str(x[0].rev())),
             ('changeset', lambda x: short(x[0].node())),
             ('date', getdate),
             ('follow', lambda x: x[0].path()),
            ]

95 96
    if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
        and not opts.get('follow')):
97
        opts['number'] = 1
98

99
    linenumber = opts.get('line_number') is not None
100
    if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
101 102 103 104 105 106 107
        raise util.Abort(_('at least one of -n/-c is required for -l'))

    funcmap = [func for op, func in opmap if opts.get(op)]
    if linenumber:
        lastfunc = funcmap[-1]
        funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])

108
    ctx = repo[opts.get('rev')]
109

110
    m = cmdutil.match(repo, pats, opts)
Matt Mackall's avatar
Matt Mackall committed
111 112
    for abs in ctx.walk(m):
        fctx = ctx[abs]
113
        if not opts.get('text') and util.binary(fctx.data()):
114
            ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
115 116
            continue

117 118
        lines = fctx.annotate(follow=opts.get('follow'),
                              linenumber=linenumber)
119 120
        pieces = []

121 122 123
        for f in funcmap:
            l = [f(n) for n, dummy in lines]
            if l:
124 125
                ml = max(map(len, l))
                pieces.append(["%*s" % (ml, x) for x in l])
126

mpm@selenic.com's avatar
mpm@selenic.com committed
127 128 129
        if pieces:
            for p, l in zip(zip(*pieces), lines):
                ui.write("%s: %s" % (" ".join(p), l[1]))
130

131
def archive(ui, repo, dest, **opts):
132
    '''create an unversioned archive of a repository revision
133

134 135
    By default, the revision used is the parent of the working directory; use
    -r/--rev to specify a different revision.
136

137
    To specify the type of archive to create, use -t/--type. Valid types are::
138

139 140 141 142 143 144
      "files" (default): a directory full of files
      "tar": tar archive, uncompressed
      "tbz2": tar archive, compressed using bzip2
      "tgz": tar archive, compressed using gzip
      "uzip": zip archive, uncompressed
      "zip": zip archive, compressed using deflate
145

146 147
    The exact name of the destination archive or directory is given using a
    format string; see 'hg help export' for details.
148

149 150 151
    Each member added to an archive file has a directory prefix prepended. Use
    -p/--prefix to specify a format string for the prefix. The default is the
    basename of the archive, with suffixes removed.
152 153
    '''

154
    ctx = repo[opts.get('rev')]
155
    if not ctx:
156
        raise util.Abort(_('no working directory: please specify a revision'))
157
    node = ctx.node()
Vadim Gelfer's avatar
Vadim Gelfer committed
158
    dest = cmdutil.make_filename(repo, dest, node)
159 160
    if os.path.realpath(dest) == repo.root:
        raise util.Abort(_('repository root cannot be destination'))
161
    matchfn = cmdutil.match(repo, [], opts)
162
    kind = opts.get('type') or 'files'
163
    prefix = opts.get('prefix')
164 165 166 167 168
    if dest == '-':
        if kind == 'files':
            raise util.Abort(_('cannot archive plain files to stdout'))
        dest = sys.stdout
        if not prefix: prefix = os.path.basename(repo.root) + '-%h'
Vadim Gelfer's avatar
Vadim Gelfer committed
169
    prefix = cmdutil.make_filename(repo, prefix, node)
170
    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
171
                     matchfn, prefix)
172

Daniel Holth's avatar
Daniel Holth committed
173
def backout(ui, repo, node=None, rev=None, **opts):
Vadim Gelfer's avatar
Vadim Gelfer committed
174 175
    '''reverse effect of earlier changeset

176 177
    Commit the backed out changes as a new changeset. The new changeset is a
    child of the backed out changeset.
Vadim Gelfer's avatar
Vadim Gelfer committed
178

179 180 181
    If you backout a changeset other than the tip, a new head is created. This
    head will be the new tip and you should merge this backout changeset with
    another head.
Vadim Gelfer's avatar
Vadim Gelfer committed
182

183 184 185 186
    The --merge option remembers the parent of the working directory before
    starting the backout, then merges the new head with that changeset
    afterwards. This saves you from doing the merge by hand. The result of
    this merge is not committed, as with a normal merge.
187

188
    See 'hg help dates' for a list of formats valid for -d/--date.
189
    '''
Daniel Holth's avatar
Daniel Holth committed
190 191 192 193 194
    if rev and node:
        raise util.Abort(_("please specify just one revision"))

    if not rev:
        rev = node
Vadim Gelfer's avatar
Vadim Gelfer committed
195

196 197 198
    if not rev:
        raise util.Abort(_("please specify a revision to backout"))

199 200 201 202
    date = opts.get('date')
    if date:
        opts['date'] = util.parsedate(date)

203
    cmdutil.bail_if_changed(repo)
Vadim Gelfer's avatar
Vadim Gelfer committed
204
    node = repo.lookup(rev)
205

206
    op1, op2 = repo.dirstate.parents()
207 208
    a = repo.changelog.ancestor(op1, node)
    if a != node:
209
        raise util.Abort(_('cannot backout change on a different branch'))
210

211 212
    p1, p2 = repo.changelog.parents(node)
    if p1 == nullid:
213
        raise util.Abort(_('cannot backout a change with no parents'))
Vadim Gelfer's avatar
Vadim Gelfer committed
214
    if p2 != nullid:
215
        if not opts.get('parent'):
216
            raise util.Abort(_('cannot backout a merge changeset without '
217 218 219
                               '--parent'))
        p = repo.lookup(opts['parent'])
        if p not in (p1, p2):
220
            raise util.Abort(_('%s is not a parent of %s') %
221
                             (short(p), short(node)))
222 223
        parent = p
    else:
224
        if opts.get('parent'):
225 226
            raise util.Abort(_('cannot use --parent on non-merge changeset'))
        parent = p1
227

228 229
    # the backout should appear on the same branch
    branch = repo.dirstate.branch()
230
    hg.clean(repo, node, show_stats=False)
231
    repo.dirstate.setbranch(branch)
Vadim Gelfer's avatar
Vadim Gelfer committed
232
    revert_opts = opts.copy()
233
    revert_opts['date'] = None
234
    revert_opts['all'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
235
    revert_opts['rev'] = hex(parent)
236
    revert_opts['no_backup'] = None
Vadim Gelfer's avatar
Vadim Gelfer committed
237 238 239
    revert(ui, repo, **revert_opts)
    commit_opts = opts.copy()
    commit_opts['addremove'] = False
240
    if not commit_opts['message'] and not commit_opts['logfile']:
241
        # we don't translate commit messages
242
        commit_opts['message'] = "Backed out changeset %s" % short(node)
243
        commit_opts['force_editor'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
244 245 246 247 248
    commit(ui, repo, **commit_opts)
    def nice(node):
        return '%d:%s' % (repo.changelog.rev(node), short(node))
    ui.status(_('changeset %s backs out changeset %s\n') %
              (nice(repo.changelog.tip()), nice(node)))
249
    if op1 != node:
250
        hg.clean(repo, op1, show_stats=False)
251
        if opts.get('merge'):
252 253
            ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
            hg.merge(repo, hex(repo.changelog.tip()))
254 255 256
        else:
            ui.status(_('the backout changeset is a new head - '
                        'do not forget to merge\n'))
257 258
            ui.status(_('(use "backout --merge" '
                        'if you want to auto-merge)\n'))
Vadim Gelfer's avatar
Vadim Gelfer committed
259

260
def bisect(ui, repo, rev=None, extra=None, command=None,
261 262 263
               reset=None, good=None, bad=None, skip=None, noupdate=None):
    """subdivision search of changesets

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
    This command helps to find changesets which introduce problems. To use,
    mark the earliest changeset you know exhibits the problem as bad, then
    mark the latest changeset which is free from the problem as good. Bisect
    will update your working directory to a revision for testing (unless the
    -U/--noupdate option is specified). Once you have performed tests, mark
    the working directory as good or bad, and bisect will either update to
    another candidate changeset or announce that it has found the bad
    revision.

    As a shortcut, you can also use the revision argument to mark a revision
    as good or bad without checking it out first.

    If you supply a command, it will be used for automatic bisection. Its exit
    status will be used to mark revisions as good or bad: status 0 means good,
    125 means to skip the revision, 127 (command not found) will abort the
    bisection, and any other non-zero exit status means the revision is bad.
280
    """
281 282 283 284
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
285 286 287 288
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
289
            displayer.show(repo[nodes[0]])
290 291
        else:
            # multiple possible revisions
292 293 294 295 296 297
            if good:
                ui.write(_("Due to skipped revisions, the first "
                        "good revision could be any of:\n"))
            else:
                ui.write(_("Due to skipped revisions, the first "
                        "bad revision could be any of:\n"))
298
            for n in nodes:
299
                displayer.show(repo[n])
300 301 302 303 304 305 306 307 308 309 310

    def check_state(state, interactive=True):
        if not state['good'] or not state['bad']:
            if (good or bad or skip or reset) and interactive:
                return
            if not state['good']:
                raise util.Abort(_('cannot bisect (no known good revisions)'))
            else:
                raise util.Abort(_('cannot bisect (no known bad revisions)'))
        return True

311 312 313 314 315 316 317 318 319 320
    # backward compatibility
    if rev in "good bad reset init".split():
        ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
        cmd, rev, extra = rev, extra, None
        if cmd == "good":
            good = True
        elif cmd == "bad":
            bad = True
        else:
            reset = True
321
    elif extra or good + bad + skip + reset + bool(command) > 1:
322
        raise util.Abort(_('incompatible arguments'))
323 324 325 326 327 328 329

    if reset:
        p = repo.join("bisect.state")
        if os.path.exists(p):
            os.unlink(p)
        return

330 331 332
    state = hbisect.load_state(repo)

    if command:
333
        commandpath = util.find_exe(command)
334 335
        if commandpath is None:
            raise util.Abort(_("cannot find executable: %s") % command)
336
        changesets = 1
337 338 339
        try:
            while changesets:
                # update state
340
                status = subprocess.call([commandpath])
341 342 343 344 345 346 347 348 349 350 351
                if status == 125:
                    transition = "skip"
                elif status == 0:
                    transition = "good"
                # status < 0 means process was killed
                elif status == 127:
                    raise util.Abort(_("failed to execute %s") % command)
                elif status < 0:
                    raise util.Abort(_("%s killed") % command)
                else:
                    transition = "bad"
352 353 354
                ctx = repo[rev or '.']
                state[transition].append(ctx.node())
                ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
355 356 357 358 359 360 361 362
                check_state(state, interactive=False)
                # bisect
                nodes, changesets, good = hbisect.bisect(repo.changelog, state)
                # update to next check
                cmdutil.bail_if_changed(repo)
                hg.clean(repo, nodes[0], show_stats=False)
        finally:
            hbisect.save_state(repo, state)
363
        return print_result(nodes, not status)
364 365 366 367 368 369 370 371 372 373

    # update state
    node = repo.lookup(rev or '.')
    if good:
        state['good'].append(node)
    elif bad:
        state['bad'].append(node)
    elif skip:
        state['skip'].append(node)

374
    hbisect.save_state(repo, state)
375

376 377
    if not check_state(state):
        return
378 379

    # actually bisect
380
    nodes, changesets, good = hbisect.bisect(repo.changelog, state)
381
    if changesets == 0:
382
        print_result(nodes, good)
383 384 385
    else:
        assert len(nodes) == 1 # only a single node can be tested next
        node = nodes[0]
386 387 388 389 390
        # compute the approximate number of remaining tests
        tests, size = 0, 2
        while size <= changesets:
            tests, size = tests + 1, size * 2
        rev = repo.changelog.rev(node)
391 392
        ui.write(_("Testing changeset %d:%s "
                   "(%d changesets remaining, ~%d tests)\n")
393
                 % (rev, short(node), changesets, tests))
394 395 396 397
        if not noupdate:
            cmdutil.bail_if_changed(repo)
            return hg.clean(repo, node)

398
def branch(ui, repo, label=None, **opts):
399 400
    """set or show the current branch name

401 402 403 404
    With no argument, show the current branch name. With one argument, set the
    working directory branch name (the branch will not exist in the repository
    until the next commit). Standard practice recommends that primary
    development take place on the 'default' branch.
405

406 407
    Unless -f/--force is specified, branch will not let you set a branch name
    that already exists, even if it's inactive.
408

409 410
    Use -C/--clean to reset the working directory branch to that of the parent
    of the working directory, negating a previous branch change.
411

412 413
    Use the command 'hg update' to switch to an existing branch. Use 'hg
    commit --close-branch' to mark this branch as closed.
414 415
    """

416 417 418 419 420
    if opts.get('clean'):
        label = repo[None].parents()[0].branch()
        repo.dirstate.setbranch(label)
        ui.status(_('reset working directory to branch %s\n') % label)
    elif label:
421
        if not opts.get('force') and label in repo.branchtags():
422
            if label not in [p.branch() for p in repo.parents()]:
423 424
                raise util.Abort(_('a branch of the same name already exists'
                                   ' (use --force to override)'))
425
        repo.dirstate.setbranch(encoding.fromlocal(label))
426
        ui.status(_('marked working directory as branch %s\n') % label)
427
    else:
428
        ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
429

430
def branches(ui, repo, active=False, closed=False):
431 432
    """list repository named branches

433 434 435
    List the repository's named branches, indicating which ones are inactive.
    If -c/--closed is specified, also list branches which have been marked
    closed (see hg commit --close-branch).
436

437 438
    If -a/--active is specified, only show active branches. A branch is
    considered active if it contains repository heads.
439 440

    Use the command 'hg update' to switch to an existing branch.
441
    """
442

443
    hexfunc = ui.debugflag and hex or short
444
    activebranches = [encoding.tolocal(repo[n].branch())
445 446 447 448 449 450
                            for n in repo.heads()]
    def testactive(tag, node):
        realhead = tag in activebranches
        open = node in repo.branchheads(tag, closed=False)
        return realhead and open
    branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
451 452
                          for tag, node in repo.branchtags().items()],
                      reverse=True)
453 454 455

    for isactive, node, tag in branches:
        if (not active) or isactive:
456
            if ui.quiet:
457
                ui.write("%s\n" % tag)
458
            else:
459 460 461 462
                hn = repo.lookup(node)
                if isactive:
                    notice = ''
                elif hn not in repo.branchheads(tag, closed=False):
463 464
                    if not closed:
                        continue
465 466 467
                    notice = ' (closed)'
                else:
                    notice = ' (inactive)'
468
                rev = str(node).rjust(31 - encoding.colwidth(tag))
469
                data = tag, rev, hexfunc(hn), notice
470
                ui.write("%s %s:%s%s\n" % data)
471

472
def bundle(ui, repo, fname, dest=None, **opts):
473 474
    """create a changegroup file

475 476
    Generate a compressed changegroup file collecting changesets not known to
    be in another repository.
477

478 479 480
    If no destination repository is specified the destination is assumed to
    have all the nodes specified by one or more --base parameters. To create a
    bundle containing all changesets, use -a/--all (or --base null).
481

482 483 484
    You can change compression method with the -t/--type option. The available
    compression methods are: none, bzip2, and gzip (by default, bundles are
    compressed using bzip2).
485

486 487 488 489
    The bundle file can then be transferred using conventional means and
    applied to another repository with the unbundle or pull command. This is
    useful when direct push and pull are not available or when exporting an
    entire repository is undesirable.
490

491 492
    Applying bundles preserves all changeset contents including permissions,
    copy/rename information, and revision history.
493
    """
494 495 496
    revs = opts.get('rev') or None
    if revs:
        revs = [repo.lookup(rev) for rev in revs]
497 498 499 500
    if opts.get('all'):
        base = ['null']
    else:
        base = opts.get('base')
501 502
    if base:
        if dest:
503
            raise util.Abort(_("--base is incompatible with specifying "
504
                               "a destination"))
505 506 507
        base = [repo.lookup(rev) for rev in base]
        # create the right base
        # XXX: nodesbetween / changegroup* should be "fixed" instead
508
        o = []
509
        has = set((nullid,))
510
        for n in base:
Matt Mackall's avatar
Matt Mackall committed
511
            has.update(repo.changelog.reachable(n))
512
        if revs:
513
            visit = list(revs)
514
        else:
515
            visit = repo.changelog.heads()
Matt Mackall's avatar
Matt Mackall committed
516
        seen = {}
517 518
        while visit:
            n = visit.pop(0)
Matt Mackall's avatar
Matt Mackall committed
519
            parents = [p for p in repo.changelog.parents(n) if p not in has]
520 521 522
            if len(parents) == 0:
                o.insert(0, n)
            else:
523 524
                for p in parents:
                    if p not in seen:
Matt Mackall's avatar
Matt Mackall committed
525
                        seen[p] = 1
526
                        visit.append(p)
527
    else:
Alexis S. L. Carvalho's avatar
Alexis S. L. Carvalho committed
528
        dest, revs, checkout = hg.parseurl(
Matt Mackall's avatar
Matt Mackall committed
529
            ui.expandpath(dest or 'default-push', dest or 'default'), revs)
Matt Mackall's avatar
Matt Mackall committed
530
        other = hg.repository(cmdutil.remoteui(repo, opts), dest)
531
        o = repo.findoutgoing(other, force=opts.get('force'))
532 533 534 535 536

    if revs:
        cg = repo.changegroupsubset(o, revs, 'bundle')
    else:
        cg = repo.changegroup(o, 'bundle')
537 538 539 540 541 542 543 544

    bundletype = opts.get('type', 'bzip2').lower()
    btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
    bundletype = btypes.get(bundletype)
    if bundletype not in changegroup.bundletypes:
        raise util.Abort(_('unknown bundle type specified with --type'))

    changegroup.writebundle(cg, fname, bundletype)
545

546
def cat(ui, repo, file1, *pats, **opts):
547
    """output the current or given revision of files
548

549 550 551
    Print the specified files as they were at the given revision. If no
    revision is given, the parent of the working directory is used, or tip if
    no revision is checked out.
552

553 554
    Output may be to a file, in which case the name of the file is given using
    a format string. The formatting rules are the same as for the export
555
    command, with the following additions::
556

557 558 559
      %s   basename of file being printed
      %d   dirname of file being printed, or '.' if in repository root
      %p   root-relative path name of file being printed
560
    """
561
    ctx = repo[opts.get('rev')]
Matt Mackall's avatar
Matt Mackall committed
562
    err = 1
563
    m = cmdutil.match(repo, (file1,) + pats, opts)
Matt Mackall's avatar
Matt Mackall committed
564
    for abs in ctx.walk(m):
565
        fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
Matt Mackall's avatar
Matt Mackall committed
566
        data = ctx[abs].data()
567
        if opts.get('decode'):
568 569
            data = repo.wwritedata(abs, data)
        fp.write(data)
Matt Mackall's avatar
Matt Mackall committed
570 571
        err = 0
    return err
mpm@selenic.com's avatar
mpm@selenic.com committed
572

573
def clone(ui, source, dest=None, **opts):
574 575 576 577
    """make a copy of an existing repository

    Create a copy of an existing repository in a new directory.

578 579
    If no destination directory name is specified, it defaults to the basename
    of the source.
580

581 582
    The location of the source is added to the new repository's .hg/hgrc file,
    as the default to be used for future pulls.
583

584 585 586
    If you use the -r/--rev option to clone up to a specific revision, no
    subsequent revisions (including subsequent tags) will be present in the
    cloned repository. This option implies --pull, even on local repositories.
587

588 589 590
    By default, clone will check out the head of the 'default' branch. If the
    -U/--noupdate option is used, the new clone will contain only a repository
    (.hg) and no working copy (the working copy parent is the null revision).
591 592 593 594

    See 'hg help urls' for valid source format details.

    It is possible to specify an ssh:// URL as the destination, but no
595 596
    .hg/hgrc and working directory will be created on the remote side. Please
    see 'hg help urls' for important details about ssh:// URLs.
597

598 599 600 601 602
    For efficiency, hardlinks are used for cloning whenever the source and
    destination are on the same filesystem (note this applies only to the
    repository data, not to the checked out files). Some filesystems, such as
    AFS, implement hardlinking incorrectly, but do not report errors. In these
    cases, use the --pull option to avoid hardlinking.
603

604
    In some cases, you can clone repositories and checked out files using full
605
    hardlinks with ::
606 607 608

      $ cp -al REPO REPOCLONE

609 610 611 612 613
    This is the fastest way to clone, but it is not always safe. The operation
    is not atomic (making sure REPO is not modified during the operation is up
    to you) and you have to make sure your editor breaks hardlinks (Emacs and
    most Linux Kernel tools do so). Also, this is not compatible with certain
    extensions that place their metadata under the .hg directory, such as mq.
614
    """
Matt Mackall's avatar
Matt Mackall committed
615
    hg.clone(cmdutil.remoteui(ui, opts), source, dest,
616 617 618 619
             pull=opts.get('pull'),
             stream=opts.get('uncompressed'),
             rev=opts.get('rev'),
             update=not opts.get('noupdate'))
mpm@selenic.com's avatar
mpm@selenic.com committed
620

621
def commit(ui, repo, *pats, **opts):
622 623
    """commit the specified files or all outstanding changes

624
    Commit changes to the given files into the repository. Unlike a
625 626
    centralized RCS, this operation is a local operation. See hg push for a
    way to actively distribute your changes.
627

628 629
    If a list of files is omitted, all changes reported by "hg status" will be
    committed.
630

631 632
    If you are committing the result of a merge, do not provide any filenames
    or -I/-X filters.
633

634 635
    If no commit message is specified, the configured editor is started to
    prompt you for a message.
636 637

    See 'hg help dates' for a list of formats valid for -d/--date.
638
    """
639 640 641
    extra = {}
    if opts.get('close_branch'):
        extra['close'] = 1
642 643 644 645
    e = cmdutil.commiteditor
    if opts.get('force_editor'):
        e = cmdutil.commitforceeditor

646
    def commitfunc(ui, repo, message, match, opts):
647 648
        return repo.commit(message, opts.get('user'), opts.get('date'), match,
                           editor=e, extra=extra)
649

650 651
    node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
    if not node:
652
        ui.status(_("nothing changed\n"))
653 654 655 656 657 658
        return
    cl = repo.changelog
    rev = cl.rev(node)
    parents = cl.parentrevs(rev)
    if rev - 1 in parents:
        # one of the parents was the old tip
659 660 661 662
        pass
    elif (parents == (nullrev, nullrev) or
          len(cl.heads(cl.node(parents[0]))) > 1 and
          (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
663
        ui.status(_('created new head\n'))
Bryan O'Sullivan's avatar
Bryan O'Sullivan committed
664

665
    if ui.debugflag:
666
        ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
667
    elif ui.verbose:
668
        ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
669

Bryan O'Sullivan's avatar
Bryan O'Sullivan committed
670
def copy(ui, repo, *pats, **opts):
671 672
    """mark files as copied for the next commit

673 674 675
    Mark dest as having copies of source files. If dest is a directory, copies
    are put in that directory. If dest is a file, the source must be a single
    file.
676

677 678 679
    By default, this command copies the contents of files as they exist in the
    working directory. If invoked with -A/--after, the operation is recorded,
    but no copying is performed.
680

681 682
    This command takes effect with the next commit. To undo a copy before
    that, see hg revert.
683
    """
Matt Mackall's avatar
Matt Mackall committed
684
    wlock = repo.wlock(False)
685
    try:
Matt Mackall's avatar
Matt Mackall committed
686
        return cmdutil.copy(ui, repo, pats, opts)
687
    finally:
688
        wlock.release()
mpm@selenic.com's avatar
mpm@selenic.com committed
689

690
def debugancestor(ui, repo, *args):
mpm@selenic.com's avatar
mpm@selenic.com committed
691
    """find the ancestor revision of two revisions in a given index"""
692 693
    if len(args) == 3:
        index, rev1, rev2 = args
694
        r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
695
        lookup = r.lookup
696
    elif len(args) == 2:
697 698 699
        if not repo:
            raise util.Abort(_("There is no Mercurial repository here "
                               "(.hg not found)"))
700
        rev1, rev2 = args
701
        r = repo.changelog
702
        lookup = repo.lookup
703 704
    else:
        raise util.Abort(_('either two or three arguments required'))
705
    a = r.ancestor(lookup(rev1), lookup(rev2))
mpm@selenic.com's avatar
mpm@selenic.com committed
706 707
    ui.write("%d:%s\n" % (r.rev(a), hex(a)))

708
def debugcommands(ui, cmd='', *args):
709
    for cmd, vals in sorted(table.iteritems()):
710 711 712 713
        cmd = cmd.split('|')[0].strip('^')
        opts = ', '.join([i[1] for i in vals[1]])
        ui.write('%s: %s\n' % (cmd, opts))

714
def debugcomplete(ui, cmd='', **opts):
Benoit Boissinot's avatar
Benoit Boissinot committed
715
    """returns the completion list associated with the given command"""
716

717
    if opts.get('options'):
718 719 720
        options = []
        otables = [globalopts]
        if cmd:
721
            aliases, entry = cmdutil.findcmd(cmd, table, False)
722 723 724 725 726 727 728 729 730
            otables.append(entry[1])
        for t in otables:
            for o in t:
                if o[0]:
                    options.append('-%s' % o[0])
                options.append('--%s' % o[1])
        ui.write("%s\n" % "\n".join(options))
        return

731 732 733
    cmdlist = cmdutil.findpossible(cmd, table)
    if ui.verbose:
        cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
734
    ui.write("%s\n" % "\n".join(sorted(cmdlist)))
Benoit Boissinot's avatar
Benoit Boissinot committed
735

736
def debugfsinfo(ui, path = "."):
737
    open('.debugfsinfo', 'w').write('')
738 739
    ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
    ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
740
    ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
741 742 743
                                and 'yes' or 'no'))
    os.unlink('.debugfsinfo')

744
def debugrebuildstate(ui, repo, rev="tip"):