commands.py 172 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, bookmarks
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, templatefilters
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
    New files are ignored if they match any of the patterns in
61
    ``.hgignore``. As with add, these changes take effect at the next
62
    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 = cmdutil.revsingle(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 = cmdutil.revsingle(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 209
    Prepare a new changeset with the effect of REV undone in the
    current working directory.

210
    If REV is the parent of the working directory, then this new changeset
211 212 213 214 215 216
    is committed automatically. Otherwise, hg needs to merge the
    changes and the merged result is left uncommitted.

    By default, the pending changeset will have one parent,
    maintaining a linear history. With --merge, the pending changeset
    will instead have two parents: the old parent of the working
217
    directory and a new child of REV that simply undoes REV.
218

219
    Before version 1.7, the behavior without --merge was equivalent to
220 221 222
    specifying --merge followed by :hg:`update --clean .` to cancel
    the merge and leave the child of REV as a head to be merged
    separately.
223

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

    Returns 0 on success.
227
    '''
Daniel Holth's avatar
Daniel Holth committed
228 229 230 231 232
    if rev and node:
        raise util.Abort(_("please specify just one revision"))

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

234 235 236
    if not rev:
        raise util.Abort(_("please specify a revision to backout"))

237 238 239 240
    date = opts.get('date')
    if date:
        opts['date'] = util.parsedate(date)

241
    cmdutil.bail_if_changed(repo)
242
    node = cmdutil.revsingle(repo, rev).node()
243

244
    op1, op2 = repo.dirstate.parents()
245 246
    a = repo.changelog.ancestor(op1, node)
    if a != node:
247
        raise util.Abort(_('cannot backout change on a different branch'))
248

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

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

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

305
def bisect(ui, repo, rev=None, extra=None, command=None,
306 307
               reset=None, good=None, bad=None, skip=None, extend=None,
               noupdate=None):
308 309
    """subdivision search of changesets

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

    Returns 0 on success.
329
    """
330 331 332 333 334 335 336 337 338 339 340
    def extendbisectrange(nodes, good):
        # bisect is incomplete when it ends on a merge node and
        # one of the parent was not checked.
        parents = repo[nodes[0]].parents()
        if len(parents) > 1:
            side = good and state['bad'] or state['good']
            num = len(set(i.node() for i in parents) & set(side))
            if num == 1:
                 return parents[0].ancestor(parents[1])
        return None

341 342 343 344
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
345 346 347 348
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
349
            displayer.show(repo[nodes[0]])
350
            parents = repo[nodes[0]].parents()
351 352 353 354 355 356
            extendnode = extendbisectrange(nodes, good)
            if extendnode is not None:
                ui.write(_('Not all ancestors of this changeset have been'
                           ' checked.\nUse bisect --extend to continue the '
                           'bisection from\nthe common ancestor, %s.\n')
                         % short(extendnode.node()))
357 358
        else:
            # multiple possible revisions
359 360 361 362 363 364
            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"))
365
            for n in nodes:
366
                displayer.show(repo[n])
367
        displayer.close()
368 369 370 371 372 373 374 375 376 377 378

    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

379 380 381 382 383 384 385 386 387 388
    # 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
389
    elif extra or good + bad + skip + reset + extend + bool(command) > 1:
390
        raise util.Abort(_('incompatible arguments'))
391 392 393 394 395 396 397

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

398 399 400 401
    state = hbisect.load_state(repo)

    if command:
        changesets = 1
402 403 404
        try:
            while changesets:
                # update state
405
                status = util.system(command)
406 407 408 409 410 411 412 413 414 415 416
                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"
417 418
                ctx = cmdutil.revsingle(repo, rev)
                rev = None # clear for future iterations
419 420
                state[transition].append(ctx.node())
                ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
421 422 423 424 425 426 427 428
                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)
429 430
        print_result(nodes, good)
        return
431 432

    # update state
433 434 435 436 437 438

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

439 440
    if good or bad or skip:
        if good:
441
            state['good'] += nodes
442
        elif bad:
443
            state['bad'] += nodes
444
        elif skip:
445
            state['skip'] += nodes
446
        hbisect.save_state(repo, state)
447

448 449
    if not check_state(state):
        return
450 451

    # actually bisect
452
    nodes, changesets, good = hbisect.bisect(repo.changelog, state)
453 454 455 456 457 458 459 460 461 462 463 464
    if extend:
        if not changesets:
            extendnode = extendbisectrange(nodes, good)
            if extendnode is not None:
                ui.write(_("Extending search to changeset %d:%s\n"
                         % (extendnode.rev(), short(extendnode.node()))))
                if noupdate:
                    return
                cmdutil.bail_if_changed(repo)
                return hg.clean(repo, extendnode.node())
        raise util.Abort(_("nothing to extend"))

465
    if changesets == 0:
466
        print_result(nodes, good)
467 468 469
    else:
        assert len(nodes) == 1 # only a single node can be tested next
        node = nodes[0]
470 471 472 473 474
        # 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)
475 476
        ui.write(_("Testing changeset %d:%s "
                   "(%d changesets remaining, ~%d tests)\n")
477
                 % (rev, short(node), changesets, tests))
478 479 480 481
        if not noupdate:
            cmdutil.bail_if_changed(repo)
            return hg.clean(repo, node)

482 483 484 485 486 487 488 489 490 491 492 493 494 495
def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
    '''track a line of development with movable markers

    Bookmarks are pointers to certain commits that move when
    committing. Bookmarks are local. They can be renamed, copied and
    deleted. It is possible to use bookmark names in :hg:`merge` and
    :hg:`update` to merge and update respectively to a given bookmark.

    You can use :hg:`bookmark NAME` to set a bookmark on the working
    directory's parent revision with the given name. If you specify
    a revision using -r REV (where REV may be an existing bookmark),
    the bookmark is assigned to that revision.

    Bookmarks can be pushed and pulled between repositories (see :hg:`help
496 497 498
    push` and :hg:`help pull`). This requires both the local and remote
    repositories to support bookmarks. For versions prior to 1.8, this means
    the bookmarks extension must be enabled.
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
    '''
    hexfn = ui.debugflag and hex or short
    marks = repo._bookmarks
    cur   = repo.changectx('.').node()

    if rename:
        if rename not in marks:
            raise util.Abort(_("a bookmark of this name does not exist"))
        if mark in marks and not force:
            raise util.Abort(_("a bookmark of the same name already exists"))
        if mark is None:
            raise util.Abort(_("new bookmark name required"))
        marks[mark] = marks[rename]
        if repo._bookmarkcurrent == rename:
            bookmarks.setcurrent(repo, mark)
514
        del marks[rename]
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
        bookmarks.write(repo)
        return

    if delete:
        if mark is None:
            raise util.Abort(_("bookmark name required"))
        if mark not in marks:
            raise util.Abort(_("a bookmark of this name does not exist"))
        if mark == repo._bookmarkcurrent:
            bookmarks.setcurrent(repo, None)
        del marks[mark]
        bookmarks.write(repo)
        return

    if mark is not None:
        if "\n" in mark:
            raise util.Abort(_("bookmark name cannot contain newlines"))
        mark = mark.strip()
        if not mark:
            raise util.Abort(_("bookmark names cannot consist entirely of "
                               "whitespace"))
        if mark in marks and not force:
            raise util.Abort(_("a bookmark of the same name already exists"))
        if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
            and not force):
            raise util.Abort(
                _("a bookmark cannot have the name of an existing branch"))
        if rev:
            marks[mark] = repo.lookup(rev)
        else:
            marks[mark] = repo.changectx('.').node()
546 547
        if repo.changectx('.').node() == marks[mark]:
            bookmarks.setcurrent(repo, mark)
548 549 550 551 552 553 554 555 556
        bookmarks.write(repo)
        return

    if mark is None:
        if rev:
            raise util.Abort(_("bookmark name required"))
        if len(marks) == 0:
            ui.status(_("no bookmarks set\n"))
        else:
557
            for bmark, n in sorted(marks.iteritems()):
558 559 560
                current = repo._bookmarkcurrent
                if bmark == current and n == cur:
                    prefix, label = '*', 'bookmarks.current'
561
                else:
562
                    prefix, label = ' ', ''
563 564 565 566 567 568 569 570 571

                if ui.quiet:
                    ui.write("%s\n" % bmark, label=label)
                else:
                    ui.write(" %s %-25s %d:%s\n" % (
                        prefix, bmark, repo.changelog.rev(n), hexfn(n)),
                        label=label)
        return

572
def branch(ui, repo, label=None, **opts):
573 574
    """set or show the current branch name

575 576 577 578 579
    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.
580

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

584 585 586
    Use -C/--clean to reset the working directory branch to that of
    the parent of the working directory, negating a previous branch
    change.
587

Martin Geisler's avatar
Martin Geisler committed
588 589
    Use the command :hg:`update` to switch to an existing branch. Use
    :hg:`commit --close-branch` to mark this branch as closed.
590 591

    Returns 0 on success.
592 593
    """

594 595 596 597 598
    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:
599
        if not opts.get('force') and label in repo.branchtags():
600
            if label not in [p.branch() for p in repo.parents()]:
601
                raise util.Abort(_('a branch of the same name already exists'
602
                                   " (use 'hg update' to switch to it)"))
603
        repo.dirstate.setbranch(label)
604
        ui.status(_('marked working directory as branch %s\n') % label)
605
    else:
606
        ui.write("%s\n" % repo.dirstate.branch())
607

608
def branches(ui, repo, active=False, closed=False):
609 610
    """list repository named branches

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

615 616
    If -a/--active is specified, only show active branches. A branch
    is considered active if it contains repository heads.
617

Martin Geisler's avatar
Martin Geisler committed
618
    Use the command :hg:`update` to switch to an existing branch.
619 620

    Returns 0.
621
    """
622

623
    hexfunc = ui.debugflag and hex or short
624
    activebranches = [repo[n].branch() for n in repo.heads()]
625 626 627 628 629
    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)
630 631
                          for tag, node in repo.branchtags().items()],
                      reverse=True)
632 633 634

    for isactive, node, tag in branches:
        if (not active) or isactive:
635
            if ui.quiet:
636
                ui.write("%s\n" % tag)
637
            else:
638 639
                hn = repo.lookup(node)
                if isactive:
640
                    label = 'branches.active'
641 642
                    notice = ''
                elif hn not in repo.branchheads(tag, closed=False):
643 644
                    if not closed:
                        continue
645
                    label = 'branches.closed'
646
                    notice = _(' (closed)')
647
                else:
648
                    label = 'branches.inactive'
649
                    notice = _(' (inactive)')
650 651
                if tag == repo.dirstate.branch():
                    label = 'branches.current'
652
                rev = str(node).rjust(31 - encoding.colwidth(tag))
653
                rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
654 655
                tag = ui.label(tag, label)
                ui.write("%s %s%s\n" % (tag, rev, notice))
656

657
def bundle(ui, repo, fname, dest=None, **opts):
658 659
    """create a changegroup file

660 661
    Generate a compressed changegroup file collecting changesets not
    known to be in another repository.
662

timeless's avatar
timeless committed
663 664
    If you omit the destination repository, then hg assumes the
    destination will have all the nodes you specify with --base
665 666
    parameters. To create a bundle containing all changesets, use
    -a/--all (or --base null).
667

668 669 670
    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).
671

672 673 674 675
    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.
676

677 678
    Applying bundles preserves all changeset contents including
    permissions, copy/rename information, and revision history.
679 680

    Returns 0 on success, 1 if no changes found.
681
    """
682 683 684 685
    revs = None
    if 'rev' in opts:
        revs = cmdutil.revrange(repo, opts['rev'])

686 687 688
    if opts.get('all'):
        base = ['null']
    else:
689
        base = cmdutil.revrange(repo, opts.get('base'))
690 691
    if base:
        if dest:
692
            raise util.Abort(_("--base is incompatible with specifying "
693
                               "a destination"))
694 695 696
        base = [repo.lookup(rev) for rev in base]
        # create the right base
        # XXX: nodesbetween / changegroup* should be "fixed" instead
697
        o = []
698
        has = set((nullid,))
699
        for n in base:
Matt Mackall's avatar
Matt Mackall committed
700
            has.update(repo.changelog.reachable(n))
701
        if revs:
702 703 704
            revs = [repo.lookup(rev) for rev in revs]
            visit = revs[:]
            has.difference_update(visit)
705
        else:
706
            visit = repo.changelog.heads()
Matt Mackall's avatar
Matt Mackall committed
707
        seen = {}
708 709
        while visit:
            n = visit.pop(0)
Matt Mackall's avatar
Matt Mackall committed
710
            parents = [p for p in repo.changelog.parents(n) if p not in has]
711
            if len(parents) == 0:
712 713
                if n not in has:
                    o.append(n)
714
            else:
715 716
                for p in parents:
                    if p not in seen:
Matt Mackall's avatar
Matt Mackall committed
717
                        seen[p] = 1
718
                        visit.append(p)
719
    else:
720
        dest = ui.expandpath(dest or 'default-push', dest or 'default')
721
        dest, branches = hg.parseurl(dest, opts.get('branch'))
722
        other = hg.repository(hg.remoteui(repo, opts), dest)
723
        revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
724 725
        if revs:
            revs = [repo.lookup(rev) for rev in revs]
726
        o = discovery.findoutgoing(repo, other, force=opts.get('force'))
727

728 729
    if not o:
        ui.status(_("no changes found\n"))
730
        return 1
731

732 733 734 735
    if revs:
        cg = repo.changegroupsubset(o, revs, 'bundle')
    else:
        cg = repo.changegroup(o, 'bundle')
736 737 738 739 740 741 742 743

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

745
def cat(ui, repo, file1, *pats, **opts):
746
    """output the current or given revision of files
747

748 749 750
    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.
751

752 753
    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
754
    for the export command, with the following additions:
755

756 757 758
    :``%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
759 760

    Returns 0 on success.
761
    """
762
    ctx = cmdutil.revsingle(repo, opts.get('rev'))
Matt Mackall's avatar
Matt Mackall committed
763
    err = 1
764
    m = cmdutil.match(repo, (file1,) + pats, opts)
Matt Mackall's avatar
Matt Mackall committed
765
    for abs in ctx.walk(m):
766
        fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
Matt Mackall's avatar
Matt Mackall committed
767
        data = ctx[abs].data()
768
        if opts.get('decode'):
769 770
            data = repo.wwritedata(abs, data)
        fp.write(data)
771
        fp.close()
Matt Mackall's avatar
Matt Mackall committed
772 773
        err = 0
    return err
mpm@selenic.com's avatar
mpm@selenic.com committed
774

775
def clone(ui, source, dest=None, **opts):
776 777 778 779
    """make a copy of an existing repository

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

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

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

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