commands.py 162 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
# This software may be used and distributed according to the terms of the
Matt Mackall's avatar
Matt Mackall committed
6
# GNU General Public License version 2 or any later version.
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
Benoit Boissinot's avatar
Benoit Boissinot committed
11
import os, re, sys, difflib, time, tempfile
12
import hg, util, revlog, extensions, copies, error
13
import patch, help, mdiff, url, encoding, templatekw, discovery
14
import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15
import merge as mergemod
16
import minirst, revset
17
import dagparser
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 25
    Schedule files to be version controlled and added to the
    repository.
26

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

30
    If no names are given, add all files to the repository.
31 32 33 34

    .. container:: verbose

       An example showing how new (unknown) files are added
Martin Geisler's avatar
Martin Geisler committed
35
       automatically by :hg:`add`::
36 37 38 39 40 41 42 43

         $ ls
         foo.c
         $ hg status
         ? foo.c
         $ hg add
         adding foo.c
         $ hg status
44
         A foo.c
45 46

    Returns 0 if all files are successfully added.
47 48
    """

49
    m = cmdutil.match(repo, pats, opts)
50 51
    rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
                           opts.get('subrepos'), prefix="")
52
    return rejected and 1 or 0
53

54
def addremove(ui, repo, *pats, **opts):
55
    """add all new files, delete all missing files
56

57 58
    Add all new files and remove all missing files from the
    repository.
59

60 61 62
    New files are ignored if they match any of the patterns in
    .hgignore. As with add, these changes take effect at the next
    commit.
63

64 65 66 67 68
    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
69 70
    can be expensive. After using this option, :hg:`status -C` can be
    used to check which files were identified as moved or renamed.
71 72

    Returns 0 if all files are successfully added.
73
    """
74
    try:
75
        sim = float(opts.get('similarity') or 100)
76 77
    except ValueError:
        raise util.Abort(_('similarity must be a number'))
78 79
    if sim < 0 or sim > 100:
        raise util.Abort(_('similarity must be between 0 and 100'))
80
    return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81

82
def annotate(ui, repo, *pats, **opts):
83
    """show changeset information by line for each file
84

85 86
    List changes in files, showing the revision id responsible for
    each line
87

88 89
    This command is useful for discovering when a change was made and
    by whom.
90

91 92 93 94
    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.
95 96

    Returns 0 on success.
97
    """
98 99 100 101 102
    if opts.get('follow'):
        # --follow is deprecated and now just an alias for -f/--file
        # to mimic the behavior of Mercurial before version 1.5
        opts['file'] = 1

103 104
    datefunc = ui.quiet and util.shortdate or util.datestr
    getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105

106
    if not pats:
107
        raise util.Abort(_('at least one filename or pattern is required'))
108

109 110 111 112
    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),
113
             ('file', lambda x: x[0].path()),
114 115
            ]

116
    if (not opts.get('user') and not opts.get('changeset')
117
        and not opts.get('date') and not opts.get('file')):
118
        opts['number'] = 1
119

120
    linenumber = opts.get('line_number') is not None
121
    if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 123 124 125 126 127 128
        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])

129
    ctx = repo[opts.get('rev')]
130
    m = cmdutil.match(repo, pats, opts)
131
    follow = not opts.get('no_follow')
Matt Mackall's avatar
Matt Mackall committed
132 133
    for abs in ctx.walk(m):
        fctx = ctx[abs]
134
        if not opts.get('text') and util.binary(fctx.data()):
135
            ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
136 137
            continue

138
        lines = fctx.annotate(follow=follow, linenumber=linenumber)
139 140
        pieces = []

141 142 143
        for f in funcmap:
            l = [f(n) for n, dummy in lines]
            if l:
144 145 146
                sized = [(x, encoding.colwidth(x)) for x in l]
                ml = max([w for x, w in sized])
                pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
147

mpm@selenic.com's avatar
mpm@selenic.com committed
148 149 150
        if pieces:
            for p, l in zip(zip(*pieces), lines):
                ui.write("%s: %s" % (" ".join(p), l[1]))
151

152
def archive(ui, repo, dest, **opts):
153
    '''create an unversioned archive of a repository revision
154

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

158 159 160 161
    The archive type is automatically detected based on file
    extension (or override using -t/--type).

    Valid types are:
162

163 164 165 166 167 168
    :``files``: a directory full of files (default)
    :``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
169

170
    The exact name of the destination archive or directory is given
Martin Geisler's avatar
Martin Geisler committed
171
    using a format string; see :hg:`help export` for details.
172

173 174 175 176
    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.
177 178

    Returns 0 on success.
179 180
    '''

181
    ctx = repo[opts.get('rev')]
182
    if not ctx:
183
        raise util.Abort(_('no working directory: please specify a revision'))
184
    node = ctx.node()
Vadim Gelfer's avatar
Vadim Gelfer committed
185
    dest = cmdutil.make_filename(repo, dest, node)
186 187
    if os.path.realpath(dest) == repo.root:
        raise util.Abort(_('repository root cannot be destination'))
188

189
    kind = opts.get('type') or archival.guesskind(dest) or 'files'
190
    prefix = opts.get('prefix')
191

192 193 194 195
    if dest == '-':
        if kind == 'files':
            raise util.Abort(_('cannot archive plain files to stdout'))
        dest = sys.stdout
196 197
        if not prefix:
            prefix = os.path.basename(repo.root) + '-%h'
198

Vadim Gelfer's avatar
Vadim Gelfer committed
199
    prefix = cmdutil.make_filename(repo, prefix, node)
200
    matchfn = cmdutil.match(repo, [], opts)
201
    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
202
                     matchfn, prefix, subrepos=opts.get('subrepos'))
203

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

207 208
    The backout command merges the reverse effect of the reverted
    changeset into the working directory.
Vadim Gelfer's avatar
Vadim Gelfer committed
209

210 211 212
    With the --merge option, it first commits the reverted changes
    as a new changeset. This new changeset is a child of the reverted
    changeset.
213 214
    The --merge option remembers the parent of the working directory
    before starting the backout, then merges the new head with that
215 216 217 218 219 220 221 222 223 224 225 226
    changeset afterwards.
    This will result in an explicit merge in the history.

    If you backout a changeset other than the original parent of the
    working directory, the result of this merge is not committed,
    as with a normal merge. Otherwise, no merge is needed and the
    commit is automatic.

    Note that the default behavior (without --merge) has changed in
    version 1.7. To restore the previous default behavior, use
    :hg:`backout --merge` and then :hg:`update --clean .` to get rid of
    the ongoing merge.
227

Martin Geisler's avatar
Martin Geisler committed
228
    See :hg:`help dates` for a list of formats valid for -d/--date.
229 230

    Returns 0 on success.
231
    '''
Daniel Holth's avatar
Daniel Holth committed
232 233 234 235 236
    if rev and node:
        raise util.Abort(_("please specify just one revision"))

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

238 239 240
    if not rev:
        raise util.Abort(_("please specify a revision to backout"))

241 242 243 244
    date = opts.get('date')
    if date:
        opts['date'] = util.parsedate(date)

245
    cmdutil.bail_if_changed(repo)
Vadim Gelfer's avatar
Vadim Gelfer committed
246
    node = repo.lookup(rev)
247

248
    op1, op2 = repo.dirstate.parents()
249 250
    a = repo.changelog.ancestor(op1, node)
    if a != node:
251
        raise util.Abort(_('cannot backout change on a different branch'))
252

253 254
    p1, p2 = repo.changelog.parents(node)
    if p1 == nullid:
255
        raise util.Abort(_('cannot backout a change with no parents'))
Vadim Gelfer's avatar
Vadim Gelfer committed
256
    if p2 != nullid:
257
        if not opts.get('parent'):
258
            raise util.Abort(_('cannot backout a merge changeset without '
259 260 261
                               '--parent'))
        p = repo.lookup(opts['parent'])
        if p not in (p1, p2):
262
            raise util.Abort(_('%s is not a parent of %s') %
263
                             (short(p), short(node)))
264 265
        parent = p
    else:
266
        if opts.get('parent'):
267 268
            raise util.Abort(_('cannot use --parent on non-merge changeset'))
        parent = p1
269

270 271
    # the backout should appear on the same branch
    branch = repo.dirstate.branch()
272
    hg.clean(repo, node, show_stats=False)
273
    repo.dirstate.setbranch(branch)
Vadim Gelfer's avatar
Vadim Gelfer committed
274
    revert_opts = opts.copy()
275
    revert_opts['date'] = None
276
    revert_opts['all'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
277
    revert_opts['rev'] = hex(parent)
278
    revert_opts['no_backup'] = None
Vadim Gelfer's avatar
Vadim Gelfer committed
279
    revert(ui, repo, **revert_opts)
280
    if not opts.get('merge') and op1 != node:
281 282 283 284 285
        try:
            ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
            return hg.update(repo, op1)
        finally:
            ui.setconfig('ui', 'forcemerge', '')
286

Vadim Gelfer's avatar
Vadim Gelfer committed
287 288
    commit_opts = opts.copy()
    commit_opts['addremove'] = False
289
    if not commit_opts['message'] and not commit_opts['logfile']:
290
        # we don't translate commit messages
291
        commit_opts['message'] = "Backed out changeset %s" % short(node)
292
        commit_opts['force_editor'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
293 294 295 296 297
    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)))
298
    if opts.get('merge') and op1 != node:
299
        hg.clean(repo, op1, show_stats=False)
300 301
        ui.status(_('merging with changeset %s\n')
                  % nice(repo.changelog.tip()))
302 303 304 305 306
        try:
            ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
            return hg.merge(repo, hex(repo.changelog.tip()))
        finally:
            ui.setconfig('ui', 'forcemerge', '')
307
    return 0
Vadim Gelfer's avatar
Vadim Gelfer committed
308

309
def bisect(ui, repo, rev=None, extra=None, command=None,
310 311 312
               reset=None, good=None, bad=None, skip=None, noupdate=None):
    """subdivision search of changesets

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
    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.
330 331

    Returns 0 on success.
332
    """
333 334 335 336
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
337 338 339 340
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
341
            displayer.show(repo[nodes[0]])
342 343
            parents = repo[nodes[0]].parents()
            if len(parents) > 1:
344
                side = good and state['bad'] or state['good']
345 346 347 348 349 350
                num = len(set(i.node() for i in parents) & set(side))
                if num == 1:
                    common = parents[0].ancestor(parents[1])
                    ui.write(_('Not all ancestors of this changeset have been'
                               ' checked.\nTo check the other ancestors, start'
                               ' from the common ancestor, %s.\n' % common))
351 352
        else:
            # multiple possible revisions
353 354 355 356 357 358
            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"))
359
            for n in nodes:
360
                displayer.show(repo[n])
361
        displayer.close()
362 363 364 365 366 367 368 369 370 371 372

    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

373 374 375 376 377 378 379 380 381 382
    # 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
383
    elif extra or good + bad + skip + reset + bool(command) > 1:
384
        raise util.Abort(_('incompatible arguments'))
385 386 387 388 389 390 391

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

392 393 394 395
    state = hbisect.load_state(repo)

    if command:
        changesets = 1
396 397 398
        try:
            while changesets:
                # update state
399
                status = util.system(command)
400 401 402 403 404 405 406 407 408 409 410
                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"
411 412 413
                ctx = repo[rev or '.']
                state[transition].append(ctx.node())
                ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
414 415 416 417 418 419 420 421
                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)
422 423
        print_result(nodes, good)
        return
424 425

    # update state
426 427 428 429 430 431

    if rev:
        nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
    else:
        nodes = [repo.lookup('.')]

432 433
    if good or bad or skip:
        if good:
434
            state['good'] += nodes
435
        elif bad:
436
            state['bad'] += nodes
437
        elif skip:
438
            state['skip'] += nodes
439
        hbisect.save_state(repo, state)
440

441 442
    if not check_state(state):
        return
443 444

    # actually bisect
445
    nodes, changesets, good = hbisect.bisect(repo.changelog, state)
446
    if changesets == 0:
447
        print_result(nodes, good)
448 449 450
    else:
        assert len(nodes) == 1 # only a single node can be tested next
        node = nodes[0]
451 452 453 454 455
        # 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)
456 457
        ui.write(_("Testing changeset %d:%s "
                   "(%d changesets remaining, ~%d tests)\n")
458
                 % (rev, short(node), changesets, tests))
459 460 461 462
        if not noupdate:
            cmdutil.bail_if_changed(repo)
            return hg.clean(repo, node)

463
def branch(ui, repo, label=None, **opts):
464 465
    """set or show the current branch name

466 467 468 469 470
    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.
471

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

475 476 477
    Use -C/--clean to reset the working directory branch to that of
    the parent of the working directory, negating a previous branch
    change.
478

Martin Geisler's avatar
Martin Geisler committed
479 480
    Use the command :hg:`update` to switch to an existing branch. Use
    :hg:`commit --close-branch` to mark this branch as closed.
481 482

    Returns 0 on success.
483 484
    """

485 486 487 488 489
    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:
490 491
        utflabel = encoding.fromlocal(label)
        if not opts.get('force') and utflabel in repo.branchtags():
492
            if label not in [p.branch() for p in repo.parents()]:
493
                raise util.Abort(_('a branch of the same name already exists'
494
                                   " (use 'hg update' to switch to it)"))
495
        repo.dirstate.setbranch(utflabel)
496
        ui.status(_('marked working directory as branch %s\n') % label)
497
    else:
498
        ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
499

500
def branches(ui, repo, active=False, closed=False):
501 502
    """list repository named branches

503 504
    List the repository's named branches, indicating which ones are
    inactive. If -c/--closed is specified, also list branches which have
505
    been marked closed (see :hg:`commit --close-branch`).
506

507 508
    If -a/--active is specified, only show active branches. A branch
    is considered active if it contains repository heads.
509

Martin Geisler's avatar
Martin Geisler committed
510
    Use the command :hg:`update` to switch to an existing branch.
511 512

    Returns 0.
513
    """
514

515
    hexfunc = ui.debugflag and hex or short
516
    activebranches = [repo[n].branch() for n in repo.heads()]
517 518 519 520 521
    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)
522 523
                          for tag, node in repo.branchtags().items()],
                      reverse=True)
524 525 526

    for isactive, node, tag in branches:
        if (not active) or isactive:
527
            encodedtag = encoding.tolocal(tag)
528
            if ui.quiet:
529
                ui.write("%s\n" % encodedtag)
530
            else:
531 532
                hn = repo.lookup(node)
                if isactive:
533
                    label = 'branches.active'
534 535
                    notice = ''
                elif hn not in repo.branchheads(tag, closed=False):
536 537
                    if not closed:
                        continue
538
                    label = 'branches.closed'
539
                    notice = _(' (closed)')
540
                else:
541
                    label = 'branches.inactive'
542
                    notice = _(' (inactive)')
543 544
                if tag == repo.dirstate.branch():
                    label = 'branches.current'
545
                rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
546 547 548
                rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
                encodedtag = ui.label(encodedtag, label)
                ui.write("%s %s%s\n" % (encodedtag, rev, notice))
549

550
def bundle(ui, repo, fname, dest=None, **opts):
551 552
    """create a changegroup file

553 554
    Generate a compressed changegroup file collecting changesets not
    known to be in another repository.
555

timeless's avatar
timeless committed
556 557
    If you omit the destination repository, then hg assumes the
    destination will have all the nodes you specify with --base
558 559
    parameters. To create a bundle containing all changesets, use
    -a/--all (or --base null).
560

561 562 563
    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).
564

565 566 567 568
    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.
569

570 571
    Applying bundles preserves all changeset contents including
    permissions, copy/rename information, and revision history.
572 573

    Returns 0 on success, 1 if no changes found.
574
    """
575
    revs = opts.get('rev') or None
576 577 578 579
    if opts.get('all'):
        base = ['null']
    else:
        base = opts.get('base')
580 581
    if base:
        if dest:
582
            raise util.Abort(_("--base is incompatible with specifying "
583
                               "a destination"))
584 585 586
        base = [repo.lookup(rev) for rev in base]
        # create the right base
        # XXX: nodesbetween / changegroup* should be "fixed" instead
587
        o = []
588
        has = set((nullid,))
589
        for n in base:
Matt Mackall's avatar
Matt Mackall committed
590
            has.update(repo.changelog.reachable(n))
591
        if revs:
592 593 594
            revs = [repo.lookup(rev) for rev in revs]
            visit = revs[:]
            has.difference_update(visit)
595
        else:
596
            visit = repo.changelog.heads()
Matt Mackall's avatar
Matt Mackall committed
597
        seen = {}
598 599
        while visit:
            n = visit.pop(0)
Matt Mackall's avatar
Matt Mackall committed
600
            parents = [p for p in repo.changelog.parents(n) if p not in has]
601
            if len(parents) == 0:
602 603
                if n not in has:
                    o.append(n)
604
            else:
605 606
                for p in parents:
                    if p not in seen:
Matt Mackall's avatar
Matt Mackall committed
607
                        seen[p] = 1
608
                        visit.append(p)
609
    else:
610
        dest = ui.expandpath(dest or 'default-push', dest or 'default')
611
        dest, branches = hg.parseurl(dest, opts.get('branch'))
612
        other = hg.repository(hg.remoteui(repo, opts), dest)
613
        revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
614 615
        if revs:
            revs = [repo.lookup(rev) for rev in revs]
616
        o = discovery.findoutgoing(repo, other, force=opts.get('force'))
617

618 619
    if not o:
        ui.status(_("no changes found\n"))
620
        return 1
621

622 623 624 625
    if revs:
        cg = repo.changegroupsubset(o, revs, 'bundle')
    else:
        cg = repo.changegroup(o, 'bundle')
626 627 628 629 630 631 632 633

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

635
def cat(ui, repo, file1, *pats, **opts):
636
    """output the current or given revision of files
637

638 639 640
    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.
641

642 643
    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
644
    for the export command, with the following additions:
645

646 647 648
    :``%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
649 650

    Returns 0 on success.
651
    """
652
    ctx = cmdutil.revsingle(repo, opts.get('rev'))
Matt Mackall's avatar
Matt Mackall committed
653
    err = 1
654
    m = cmdutil.match(repo, (file1,) + pats, opts)
Matt Mackall's avatar
Matt Mackall committed
655
    for abs in ctx.walk(m):
656
        fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
Matt Mackall's avatar
Matt Mackall committed
657
        data = ctx[abs].data()
658
        if opts.get('decode'):
659 660
            data = repo.wwritedata(abs, data)
        fp.write(data)
Matt Mackall's avatar
Matt Mackall committed
661 662
        err = 0
    return err
mpm@selenic.com's avatar
mpm@selenic.com committed
663

664
def clone(ui, source, dest=None, **opts):
665 666 667 668
    """make a copy of an existing repository

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

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

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

Martin Geisler's avatar
Martin Geisler committed
675
    See :hg:`help urls` for valid source format details.
676

677
    It is possible to specify an ``ssh://`` URL as the destination, but no
678
    .hg/hgrc and working directory will be created on the remote side.
Martin Geisler's avatar
Martin Geisler committed
679
    Please see :hg:`help urls` for important details about ``ssh://`` URLs.
680

681 682 683 684
    A set of changesets (tags, or branch names) to pull may be specified
    by listing each changeset (tag, or branch name) with -r/--rev.
    If -r/--rev is used, the cloned repository will contain only a subset
    of the changesets of the source repository. Only the set of changesets
Wagner Bruna's avatar
Wagner Bruna committed
685 686
    defined by all -r/--rev options (including all their ancestors)
    will be pulled into the destination repository.
687 688 689 690 691 692
    No subsequent changesets (including subsequent tags) will be present
    in the destination.

    Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
    local source repositories.

693 694
    For efficiency, hardlinks are used for cloning whenever the source
    and destination are on the same filesystem (note this applies only
695
    to the repository data, not to the working directory). Some
696 697 698
    filesystems, such as AFS, implement hardlinking incorrectly, but
    do not report errors. In these cases, use the --pull option to
    avoid hardlinking.
699

700
    In some cases, you can clone repositories and the working directory
701
    using full hardlinks with ::
702 703 704

      $ cp -al REPO REPOCLONE

705 706 707 708 709 710
    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.
711 712 713 714

    Mercurial will update the working directory to the first applicable
    revision from this list:

715 716 717 718 719 720 721 722 723 724
    a) null if -U or the source repository has no changesets
    b) if -u . and the source repository is local, the first parent of
       the source repository's working directory
    c) the changeset specified with -u (if a branch name, this means the
       latest head of that branch)
    d) the changeset specified with -r
    e) the tipmost head specified with -b
    f) the tipmost head specified with the url#branch source syntax
    g) the tipmost head of the default branch
    h) tip
725 726

    Returns 0 on success.
727
    """
728 729 730
    if opts.get('noupdate') and opts.get('updaterev'):
        raise util.Abort(_("cannot specify both --noupdate and --updaterev"))

731
    r = hg.clone(hg.remoteui(ui, opts), source, dest,
732 733 734 735 736 737 738
                 pull=opts.get('pull'),
                 stream=opts.get('uncompressed'),
                 rev=opts.get('rev'),
                 update=opts.get('updaterev') or not opts.get('noupdate'),
                 branch=opts.get('branch'))

    return r is None
mpm@selenic.com's avatar
mpm@selenic.com committed
739

740
def commit(ui, repo, *pats, **opts):
741 742
    """commit the specified files or all outstanding changes

743
    Commit changes to the given files into the repository. Unlike a
744 745
    centralized RCS, this operation is a local operation. See
    :hg:`push` for a way to actively distribute your changes.
746

Martin Geisler's avatar
Martin Geisler committed
747
    If a list of files is omitted, all changes reported by :hg:`status`
748
    will be committed.
749

750 751
    If you are committing the result of a merge, do not provide any
    filenames or -I/-X filters.
752

753 754 755 756
    If no commit message is specified, Mercurial starts your
    configured editor where you can enter a message. In case your
    commit fails, you will find a backup of your message in
    ``.hg/last-message.txt``.
757

Martin Geisler's avatar
Martin Geisler committed
758
    See :hg:`help dates` for a list of formats valid for -d/--date.
</