commands.py 125 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 7
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.

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

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

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

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

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

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

31 32
    rejected = None
    exacts = {}
33
    names = []
34 35
    m = cmdutil.match(repo, pats, opts)
    m.bad = lambda x,y: True
Matt Mackall's avatar
Matt Mackall committed
36
    for abs in repo.walk(m):
37
        if m.exact(abs):
38
            if ui.verbose:
39
                ui.status(_('adding %s\n') % m.rel(abs))
40
            names.append(abs)
41
            exacts[abs] = 1
42
        elif abs not in repo.dirstate:
43
            ui.status(_('adding %s\n') % m.rel(abs))
44
            names.append(abs)
45
    if not opts.get('dry_run'):
46 47 48
        rejected = repo.add(names)
        rejected = [p for p in rejected if p in exacts]
    return rejected and 1 or 0
49

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

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

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

60 61 62 63 64 65
    Use the -s/--similarity option to detect renamed files. With a
    parameter > 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.
66
    """
67 68 69 70
    try:
        sim = float(opts.get('similarity') or 0)
    except ValueError:
        raise util.Abort(_('similarity must be a number'))
71 72 73
    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.)
74

75
def annotate(ui, repo, *pats, **opts):
76 77
    """show changeset information per file line

78 79
    List changes in files, showing the revision id responsible for
    each line
80

81 82
    This command is useful to discover who did a change or when a
    change took place.
83

84 85 86
    Without the -a/--text option, annotate will avoid processing files
    it detects as binary. With -a, annotate will generate an
    annotation anyway, probably with undesirable results.
87
    """
88 89
    datefunc = ui.quiet and util.shortdate or util.datestr
    getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90

91
    if not pats:
92
        raise util.Abort(_('at least one file name or pattern required'))
93

94 95 96 97 98 99 100
    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()),
            ]

101 102
    if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
        and not opts.get('follow')):
103
        opts['number'] = 1
104

105
    linenumber = opts.get('line_number') is not None
106
    if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 108 109 110 111 112 113
        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])

114
    ctx = repo[opts.get('rev')]
115

116
    m = cmdutil.match(repo, pats, opts)
Matt Mackall's avatar
Matt Mackall committed
117 118
    for abs in ctx.walk(m):
        fctx = ctx[abs]
119
        if not opts.get('text') and util.binary(fctx.data()):
120
            ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 122
            continue

123 124
        lines = fctx.annotate(follow=opts.get('follow'),
                              linenumber=linenumber)
125 126
        pieces = []

127 128 129
        for f in funcmap:
            l = [f(n) for n, dummy in lines]
            if l:
130 131
                ml = max(map(len, l))
                pieces.append(["%*s" % (ml, x) for x in l])
132

mpm@selenic.com's avatar
mpm@selenic.com committed
133 134 135
        if pieces:
            for p, l in zip(zip(*pieces), lines):
                ui.write("%s: %s" % (" ".join(p), l[1]))
136

137 138 139 140
def archive(ui, repo, dest, **opts):
    '''create unversioned archive of a repository revision

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

143 144
    To specify the type of archive to create, use -t/--type. Valid
    types are:
145 146 147 148 149 150 151 152 153

    "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

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

    Each member added to an archive file has a directory prefix
157 158 159
    prepended. Use -p/--prefix to specify a format string for the
    prefix. The default is the basename of the archive, with suffixes
    removed.
160 161
    '''

162
    ctx = repo[opts.get('rev')]
163
    if not ctx:
164
        raise util.Abort(_('no working directory: please specify a revision'))
165
    node = ctx.node()
Vadim Gelfer's avatar
Vadim Gelfer committed
166
    dest = cmdutil.make_filename(repo, dest, node)
167 168
    if os.path.realpath(dest) == repo.root:
        raise util.Abort(_('repository root cannot be destination'))
169
    matchfn = cmdutil.match(repo, [], opts)
170
    kind = opts.get('type') or 'files'
171
    prefix = opts.get('prefix')
172 173 174 175 176
    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
177
    prefix = cmdutil.make_filename(repo, prefix, node)
178
    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179
                     matchfn, prefix)
180

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

184
    Commit the backed out changes as a new changeset. The new
Vadim Gelfer's avatar
Vadim Gelfer committed
185
    changeset is a child of the backed out changeset.
Vadim Gelfer's avatar
Vadim Gelfer committed
186 187

    If you back out a changeset other than the tip, a new head is
188
    created. This head will be the new tip and you should merge this
189
    backout changeset with another head (current one by default).
Vadim Gelfer's avatar
Vadim Gelfer committed
190 191 192

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

196
    See \'hg help dates\' for a list of formats valid for -d/--date.
197
    '''
Daniel Holth's avatar
Daniel Holth committed
198 199 200 201 202
    if rev and node:
        raise util.Abort(_("please specify just one revision"))

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

204 205 206
    if not rev:
        raise util.Abort(_("please specify a revision to backout"))

207 208 209 210
    date = opts.get('date')
    if date:
        opts['date'] = util.parsedate(date)

211
    cmdutil.bail_if_changed(repo)
Vadim Gelfer's avatar
Vadim Gelfer committed
212
    node = repo.lookup(rev)
213

214
    op1, op2 = repo.dirstate.parents()
215 216 217 218
    a = repo.changelog.ancestor(op1, node)
    if a != node:
        raise util.Abort(_('cannot back out change on a different branch'))

219 220
    p1, p2 = repo.changelog.parents(node)
    if p1 == nullid:
Vadim Gelfer's avatar
Vadim Gelfer committed
221 222
        raise util.Abort(_('cannot back out a change with no parents'))
    if p2 != nullid:
223
        if not opts.get('parent'):
224 225 226 227
            raise util.Abort(_('cannot back out a merge changeset without '
                               '--parent'))
        p = repo.lookup(opts['parent'])
        if p not in (p1, p2):
228
            raise util.Abort(_('%s is not a parent of %s') %
229
                             (short(p), short(node)))
230 231
        parent = p
    else:
232
        if opts.get('parent'):
233 234
            raise util.Abort(_('cannot use --parent on non-merge changeset'))
        parent = p1
235

236 237
    # the backout should appear on the same branch
    branch = repo.dirstate.branch()
238
    hg.clean(repo, node, show_stats=False)
239
    repo.dirstate.setbranch(branch)
Vadim Gelfer's avatar
Vadim Gelfer committed
240
    revert_opts = opts.copy()
241
    revert_opts['date'] = None
242
    revert_opts['all'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
243
    revert_opts['rev'] = hex(parent)
244
    revert_opts['no_backup'] = None
Vadim Gelfer's avatar
Vadim Gelfer committed
245 246 247
    revert(ui, repo, **revert_opts)
    commit_opts = opts.copy()
    commit_opts['addremove'] = False
248
    if not commit_opts['message'] and not commit_opts['logfile']:
249
        commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250
        commit_opts['force_editor'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
251 252 253 254 255
    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)))
256
    if op1 != node:
257
        hg.clean(repo, op1, show_stats=False)
258
        if opts.get('merge'):
259 260
            ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
            hg.merge(repo, hex(repo.changelog.tip()))
261 262 263
        else:
            ui.status(_('the backout changeset is a new head - '
                        'do not forget to merge\n'))
264 265
            ui.status(_('(use "backout --merge" '
                        'if you want to auto-merge)\n'))
Vadim Gelfer's avatar
Vadim Gelfer committed
266

267
def bisect(ui, repo, rev=None, extra=None, command=None,
268 269 270
               reset=None, good=None, bad=None, skip=None, noupdate=None):
    """subdivision search of changesets

271 272 273 274
    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
275 276 277 278
    for testing (unless the -U/--noupdate option is specified). Once
    you have performed tests, mark the working directory as bad or
    good and bisect will either update to another candidate changeset
    or announce that it has found the bad revision.
279

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

283 284 285 286 287
    If you supply a command it will be used for automatic bisection.
    Its exit status will be used as flag to mark revision as bad or
    good. In case exit status is 0 the revision is marked as good, 125
    - skipped, 127 (command not found) - bisection will be aborted;
    any other status bigger than 0 will mark revision as bad.
288
    """
289 290 291 292
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
293 294 295 296
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
297
            displayer.show(repo[nodes[0]])
298 299
        else:
            # multiple possible revisions
300 301 302 303 304 305
            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"))
306
            for n in nodes:
307
                displayer.show(repo[n])
308 309 310 311 312 313 314 315 316 317 318

    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

319 320 321 322 323 324 325 326 327 328
    # 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
329
    elif extra or good + bad + skip + reset + bool(command) > 1:
330
        raise util.Abort(_('incompatible arguments'))
331 332 333 334 335 336 337

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

338 339 340
    state = hbisect.load_state(repo)

    if command:
341
        commandpath = util.find_exe(command)
342
        changesets = 1
343 344 345
        try:
            while changesets:
                # update state
Augie Fackler's avatar
Augie Fackler committed
346
                status = os.spawnl(os.P_WAIT, commandpath, commandpath)
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
                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"
                node = repo.lookup(rev or '.')
                state[transition].append(node)
                ui.note(_('Changeset %s: %s\n') % (short(node), transition))
                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)
369
        return print_result(nodes, not status)
370 371 372 373 374 375 376 377 378 379

    # 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)

380
    hbisect.save_state(repo, state)
381

382 383
    if not check_state(state):
        return
384 385

    # actually bisect
386
    nodes, changesets, good = hbisect.bisect(repo.changelog, state)
387
    if changesets == 0:
388
        print_result(nodes, good)
389 390 391
    else:
        assert len(nodes) == 1 # only a single node can be tested next
        node = nodes[0]
392 393 394 395 396 397 398
        # 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)
        ui.write(_("Testing changeset %s:%s "
                   "(%s changesets remaining, ~%s tests)\n")
399
                 % (rev, short(node), changesets, tests))
400 401 402 403
        if not noupdate:
            cmdutil.bail_if_changed(repo)
            return hg.clean(repo, node)

404
def branch(ui, repo, label=None, **opts):
405 406
    """set or show the current branch name

407
    With no argument, show the current branch name. With one argument,
408 409 410
    set the working directory branch name (the branch does not exist
    in the repository until the next commit). It is recommended to use
    the 'default' branch as your primary development branch.
411

412 413
    Unless -f/--force is specified, branch will not let you set a
    branch name that shadows an existing branch.
414

415 416
    Use -C/--clean to reset the working directory branch to that of
    the parent of the working directory, negating a previous branch
417
    change.
418

419
    Use the command 'hg update' to switch to an existing branch.
420 421
    """

422 423 424 425 426
    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:
427
        if not opts.get('force') and label in repo.branchtags():
428
            if label not in [p.branch() for p in repo.parents()]:
429 430
                raise util.Abort(_('a branch of the same name already exists'
                                   ' (use --force to override)'))
431
        repo.dirstate.setbranch(encoding.fromlocal(label))
432
        ui.status(_('marked working directory as branch %s\n') % label)
433
    else:
434
        ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
435

436
def branches(ui, repo, active=False):
437 438
    """list repository named branches

439
    List the repository's named branches, indicating which ones are
440
    inactive. If active is specified, only show active branches.
441

442
    A branch is considered active if it contains repository heads.
443 444

    Use the command 'hg update' to switch to an existing branch.
445
    """
446
    hexfunc = ui.debugflag and hex or short
447
    activebranches = [encoding.tolocal(repo[n].branch())
448
                            for n in repo.heads(closed=False)]
449
    branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
450 451
                          for tag, node in repo.branchtags().items()],
                      reverse=True)
452 453 454

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

469
def bundle(ui, repo, fname, dest=None, **opts):
470 471
    """create a changegroup file

472
    Generate a compressed changegroup file collecting changesets not
473
    known to be in another repository.
474

475 476
    If no destination repository is specified the destination is
    assumed to have all the nodes specified by one or more --base
477
    parameters. To create a bundle containing all changesets, use
478 479 480
    -a/--all (or --base null). To change the compression method
    applied, use the -t/--type option (by default, bundles are
    compressed using bz2).
481

482 483 484 485
    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.
486

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

    if revs:
        cg = repo.changegroupsubset(o, revs, 'bundle')
    else:
        cg = repo.changegroup(o, 'bundle')
533 534 535 536 537 538 539 540

    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)
541

542
def cat(ui, repo, file1, *pats, **opts):
543
    """output the current or given revision of files
544

545 546
    Print the specified files as they were at the given revision. If
    no revision is given, the parent of the working directory is used,
547
    or tip if no revision is checked out.
548 549

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

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

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

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

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

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

580 581 582 583
    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.
584 585

    By default, clone will check out the head of the 'default' branch.
586 587 588
    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).
589 590 591 592 593

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

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

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

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

      $ cp -al REPO REPOCLONE

609
    This is the fastest way to clone, but it is not always safe. The
610 611
    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
612
    breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
613 614
    this is not compatible with certain extensions that place their
    metadata under the .hg directory, such as mq.
615

616
    """
Matt Mackall's avatar
Matt Mackall committed
617
    hg.clone(cmdutil.remoteui(ui, opts), source, dest,
618 619 620 621
             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
622

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

626 627
    Commit changes to the given files into the repository. Unlike a
    centralized RCS, this operation is a local operation. See hg push
628
    for means to actively distribute your changes.
629 630

    If a list of files is omitted, all changes reported by "hg status"
631
    will be committed.
632

633 634 635
    If you are committing the result of a merge, do not provide any
    file names or -I/-X filters.

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

    See 'hg help dates' for a list of formats valid for -d/--date.
640
    """
641 642 643
    extra = {}
    if opts.get('close_branch'):
        extra['close'] = 1
644
    def commitfunc(ui, repo, message, match, opts):
645 646 647
        return repo.commit(match.files(), message, opts.get('user'),
            opts.get('date'), match, force_editor=opts.get('force_editor'),
            extra=extra)
648

649 650 651 652 653 654 655 656
    node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
    if not node:
        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
657 658 659 660
        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)):
661
        ui.status(_('created new head\n'))
Bryan O'Sullivan's avatar
Bryan O'Sullivan committed
662

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

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

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

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

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

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

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

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

715
    if opts.get('options'):
716 717 718
        options = []
        otables = [globalopts]
        if cmd:
719
            aliases, entry = cmdutil.findcmd(cmd, table, False)
720 721 722 723 724 725 726 727 728
            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

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

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

742
def debugrebuildstate(ui, repo, rev="tip"):
743
    """rebuild the dirstate as it would look like for the given revision"""
744
    ctx = repo[rev]
745
    wlock = repo.wlock()
746
    try:
747
        repo.dirstate.rebuild(ctx.node(), ctx.manifest())
748
    finally:
749
        wlock.release()