commands.py 182 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, bin, 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, scmutil, util, revlog, extensions, copies, error, bookmarks
13
import patch, help, 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, context, simplemerge
18
import random, setdiscovery, treediscovery, dagutil
19

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

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

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

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

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

    .. container:: verbose

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

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

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

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

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

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

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

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

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

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

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

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

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

    Returns 0 on success.
98
    """
99 100 101
    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
102
        opts['file'] = True
103

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

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

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

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

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

130 131 132
    def bad(x, y):
        raise util.Abort("%s: %s" % (x, y))

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

143
        lines = fctx.annotate(follow=follow, linenumber=linenumber)
144 145
        pieces = []

146 147 148
        for f in funcmap:
            l = [f(n) for n, dummy in lines]
            if l:
149 150 151
                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])
152

mpm@selenic.com's avatar
mpm@selenic.com committed
153 154 155
        if pieces:
            for p, l in zip(zip(*pieces), lines):
                ui.write("%s: %s" % (" ".join(p), l[1]))
156

157
def archive(ui, repo, dest, **opts):
158
    '''create an unversioned archive of a repository revision
159

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

163 164 165 166
    The archive type is automatically detected based on file
    extension (or override using -t/--type).

    Valid types are:
167

168 169 170 171 172 173
    :``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
174

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

178 179 180 181
    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.
182 183

    Returns 0 on success.
184 185
    '''

186
    ctx = cmdutil.revsingle(repo, opts.get('rev'))
187
    if not ctx:
188
        raise util.Abort(_('no working directory: please specify a revision'))
189
    node = ctx.node()
190
    dest = cmdutil.makefilename(repo, dest, node)
191 192
    if os.path.realpath(dest) == repo.root:
        raise util.Abort(_('repository root cannot be destination'))
193

194
    kind = opts.get('type') or archival.guesskind(dest) or 'files'
195
    prefix = opts.get('prefix')
196

197 198 199 200
    if dest == '-':
        if kind == 'files':
            raise util.Abort(_('cannot archive plain files to stdout'))
        dest = sys.stdout
201 202
        if not prefix:
            prefix = os.path.basename(repo.root) + '-%h'
203

204
    prefix = cmdutil.makefilename(repo, prefix, node)
205
    matchfn = cmdutil.match(repo, [], opts)
206
    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
207
                     matchfn, prefix, subrepos=opts.get('subrepos'))
208

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

212 213 214
    Prepare a new changeset with the effect of REV undone in the
    current working directory.

215
    If REV is the parent of the working directory, then this new changeset
216 217 218 219 220 221
    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
222
    directory and a new child of REV that simply undoes REV.
223

224
    Before version 1.7, the behavior without --merge was equivalent to
225 226 227
    specifying --merge followed by :hg:`update --clean .` to cancel
    the merge and leave the child of REV as a head to be merged
    separately.
228

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

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

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

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

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

246
    cmdutil.bailifchanged(repo)
247
    node = cmdutil.revsingle(repo, rev).node()
248

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

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

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

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

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

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

    Returns 0 on success.
334
    """
335 336 337 338 339 340 341 342
    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:
Yuya Nishihara's avatar
Yuya Nishihara committed
343
                return parents[0].ancestor(parents[1])
344 345
        return None

346 347 348 349
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
350 351 352 353
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
354
            displayer.show(repo[nodes[0]])
355 356 357 358 359
            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')
360
                         % extendnode)
361 362
        else:
            # multiple possible revisions
363 364 365 366 367 368
            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"))
369
            for n in nodes:
370
                displayer.show(repo[n])
371
        displayer.close()
372 373 374 375 376 377 378 379 380 381 382

    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

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

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

402 403 404 405
    state = hbisect.load_state(repo)

    if command:
        changesets = 1
406 407 408
        try:
            while changesets:
                # update state
409
                status = util.system(command)
410 411 412 413 414 415 416 417 418 419 420
                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"
421 422
                ctx = cmdutil.revsingle(repo, rev)
                rev = None # clear for future iterations
423 424
                state[transition].append(ctx.node())
                ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
425 426 427 428
                check_state(state, interactive=False)
                # bisect
                nodes, changesets, good = hbisect.bisect(repo.changelog, state)
                # update to next check
429
                cmdutil.bailifchanged(repo)
430 431 432
                hg.clean(repo, nodes[0], show_stats=False)
        finally:
            hbisect.save_state(repo, state)
433 434
        print_result(nodes, good)
        return
435 436

    # update state
437 438 439 440 441 442

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

443 444
    if good or bad or skip:
        if good:
445
            state['good'] += nodes
446
        elif bad:
447
            state['bad'] += nodes
448
        elif skip:
449
            state['skip'] += nodes
450
        hbisect.save_state(repo, state)
451

452 453
    if not check_state(state):
        return
454 455

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

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

486 487
def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
             rename=None, inactive=False):
488 489 490 491 492 493 494 495 496 497 498 499 500
    '''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
501 502 503
    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.
504 505 506 507 508 509 510
    '''
    hexfn = ui.debugflag and hex or short
    marks = repo._bookmarks
    cur   = repo.changectx('.').node()

    if rename:
        if rename not in marks:
511
            raise util.Abort(_("bookmark '%s' does not exist") % rename)
512
        if mark in marks and not force:
513 514
            raise util.Abort(_("bookmark '%s' already exists "
                               "(use -f to force)") % mark)
515 516 517
        if mark is None:
            raise util.Abort(_("new bookmark name required"))
        marks[mark] = marks[rename]
518
        if repo._bookmarkcurrent == rename and not inactive:
519
            bookmarks.setcurrent(repo, mark)
520
        del marks[rename]
521 522 523 524 525 526 527
        bookmarks.write(repo)
        return

    if delete:
        if mark is None:
            raise util.Abort(_("bookmark name required"))
        if mark not in marks:
528
            raise util.Abort(_("bookmark '%s' does not exist") % mark)
529 530 531 532 533 534 535 536 537 538 539 540 541
        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"))
542 543 544
        if inactive and mark == repo._bookmarkcurrent:
            bookmarks.setcurrent(repo, None)
            return
545
        if mark in marks and not force:
546 547
            raise util.Abort(_("bookmark '%s' already exists "
                               "(use -f to force)") % mark)
548 549 550 551 552 553 554 555
        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()
556
        if not inactive and repo.changectx('.').node() == marks[mark]:
557
            bookmarks.setcurrent(repo, mark)
558 559 560 561 562 563 564 565 566
        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:
567
            for bmark, n in sorted(marks.iteritems()):
568 569 570
                current = repo._bookmarkcurrent
                if bmark == current and n == cur:
                    prefix, label = '*', 'bookmarks.current'
571
                else:
572
                    prefix, label = ' ', ''
573 574 575 576 577 578 579 580 581

                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

582
def branch(ui, repo, label=None, **opts):
583 584
    """set or show the current branch name

585 586 587 588 589
    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.
590

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

594 595 596
    Use -C/--clean to reset the working directory branch to that of
    the parent of the working directory, negating a previous branch
    change.
597

Martin Geisler's avatar
Martin Geisler committed
598 599
    Use the command :hg:`update` to switch to an existing branch. Use
    :hg:`commit --close-branch` to mark this branch as closed.
600 601

    Returns 0 on success.
602 603
    """

604
    if opts.get('clean'):
605
        label = repo[None].p1().branch()
606 607 608
        repo.dirstate.setbranch(label)
        ui.status(_('reset working directory to branch %s\n') % label)
    elif label:
609
        if not opts.get('force') and label in repo.branchtags():
610
            if label not in [p.branch() for p in repo.parents()]:
611
                raise util.Abort(_('a branch of the same name already exists'),
612
                                 # i18n: "it" refers to an existing branch
613
                                 hint=_("use 'hg update' to switch to it"))
614
        repo.dirstate.setbranch(label)
615
        ui.status(_('marked working directory as branch %s\n') % label)
616
    else:
617
        ui.write("%s\n" % repo.dirstate.branch())
618

619
def branches(ui, repo, active=False, closed=False):
620 621
    """list repository named branches

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

626 627
    If -a/--active is specified, only show active branches. A branch
    is considered active if it contains repository heads.
628

Martin Geisler's avatar
Martin Geisler committed
629
    Use the command :hg:`update` to switch to an existing branch.
630 631

    Returns 0.
632
    """
633

634
    hexfunc = ui.debugflag and hex or short
635
    activebranches = [repo[n].branch() for n in repo.heads()]
636 637 638 639 640
    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)
641 642
                          for tag, node in repo.branchtags().items()],
                      reverse=True)
643 644 645

    for isactive, node, tag in branches:
        if (not active) or isactive:
646
            if ui.quiet:
647
                ui.write("%s\n" % tag)
648
            else:
649 650
                hn = repo.lookup(node)
                if isactive:
651
                    label = 'branches.active'
652 653
                    notice = ''
                elif hn not in repo.branchheads(tag, closed=False):
654 655
                    if not closed:
                        continue
656
                    label = 'branches.closed'
657
                    notice = _(' (closed)')
658
                else:
659
                    label = 'branches.inactive'
660
                    notice = _(' (inactive)')
661 662
                if tag == repo.dirstate.branch():
                    label = 'branches.current'
663
                rev = str(node).rjust(31 - encoding.colwidth(tag))
664
                rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
665 666
                tag = ui.label(tag, label)
                ui.write("%s %s%s\n" % (tag, rev, notice))
667

668
def bundle(ui, repo, fname, dest=None, **opts):
669 670
    """create a changegroup file

671 672
    Generate a compressed changegroup file collecting changesets not
    known to be in another repository.
673

timeless's avatar
timeless committed
674 675
    If you omit the destination repository, then hg assumes the
    destination will have all the nodes you specify with --base
676 677
    parameters. To create a bundle containing all changesets, use
    -a/--all (or --base null).
678

679 680 681
    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).
682

683 684 685 686
    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.
687

688 689
    Applying bundles preserves all changeset contents including
    permissions, copy/rename information, and revision history.
690 691

    Returns 0 on success, 1 if no changes found.
692
    """
693 694 695 696
    revs = None
    if 'rev' in opts:
        revs = cmdutil.revrange(repo, opts['rev'])

697 698 699
    if opts.get('all'):
        base = ['null']
    else:
700
        base = cmdutil.revrange(repo, opts.get('base'))
701 702
    if base:
        if dest:
703
            raise util.Abort(_("--base is incompatible with specifying "
704
                               "a destination"))
705
        common = [repo.lookup(rev) for rev in base]
706
        heads = revs and map(repo.lookup, revs) or revs
707
    else:
708
        dest = ui.expandpath(dest or 'default-push', dest or 'default')
709
        dest, branches = hg.parseurl(dest, opts.get('branch'))
710
        other = hg.repository(hg.remoteui(repo, opts), dest)
711
        revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
712 713 714 715
        heads = revs and map(repo.lookup, revs) or revs
        common, outheads = discovery.findcommonoutgoing(repo, other,
                                                        onlyheads=heads,
                                                        force=opts.get('force'))
716

717
    cg = repo.getbundle('bundle', common=common, heads=heads)
718
    if not cg:
719
        ui.status(_("no changes found\n"))
720
        return 1
721

722 723 724 725 726 727 728
    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)
729

730
def cat(ui, repo, file1, *pats, **opts):
731
    """output the current or given revision of files
732

733 734 735
    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.
736

737 738
    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
739
    for the export command, with the following additions:
740

741 742 743
    :``%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
744 745

    Returns 0 on success.
746
    """
747
    ctx = cmdutil.revsingle(repo, opts.get('rev'))
Matt Mackall's avatar
Matt Mackall committed
748
    err = 1
749
    m = cmdutil.match(repo, (file1,) + pats, opts)
Matt Mackall's avatar
Matt Mackall committed
750
    for abs in ctx.walk(m):
751 752
        fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
                                 pathname=abs)
Matt Mackall's avatar
Matt Mackall committed
753
        data = ctx[abs].data()
754
        if opts.get('decode'):
755 756
            data = repo.wwritedata(abs, data)
        fp.write(data)
757
        fp.close()
Matt Mackall's avatar
Matt Mackall committed
758 759
        err = 0
    return err
mpm@selenic.com's avatar
mpm@selenic.com committed
760

Thomas Arendsen Hein's avatar