~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: aaron.bentley at utoronto
  • Date: 2005-08-18 02:24:28 UTC
  • mto: (1092.1.41) (1185.3.4) (974.1.47)
  • mto: This revision was merged to the branch mainline in revision 1110.
  • Revision ID: aaron.bentley@utoronto.ca-20050818022428-4c0bf84005f4dba8
mergedĀ mbp@sourcefrog.net-20050817233101-0939da1cf91f2472

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
 
19
 
import sys, os
 
18
# TODO: Split the command framework away from the actual commands.
 
19
 
 
20
# TODO: probably should say which arguments are candidates for glob
 
21
# expansion on windows and do that at the command level.
 
22
 
 
23
import sys
 
24
import os
20
25
 
21
26
import bzrlib
22
27
from bzrlib.trace import mutter, note, log_error, warning
136
141
        raise BzrCommandError(msg)
137
142
    
138
143
 
 
144
def get_merge_type(typestring):
 
145
    """Attempt to find the merge class/factory associated with a string."""
 
146
    from merge import merge_types
 
147
    try:
 
148
        return merge_types[typestring][0]
 
149
    except KeyError:
 
150
        templ = '%s%%7s: %%s' % (' '*12)
 
151
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
152
        type_list = '\n'.join(lines)
 
153
        msg = "No known merge type %s. Supported types are:\n%s" %\
 
154
            (typestring, type_list)
 
155
        raise BzrCommandError(msg)
 
156
    
 
157
 
139
158
 
140
159
def _get_cmd_dict(plugins_override=True):
141
160
    d = {}
237
256
class ExternalCommand(Command):
238
257
    """Class to wrap external commands.
239
258
 
240
 
    We cheat a little here, when get_cmd_class() calls us we actually give it back
241
 
    an object we construct that has the appropriate path, help, options etc for the
242
 
    specified command.
243
 
 
244
 
    When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
245
 
    method, which we override to call the Command.__init__ method. That then calls
246
 
    our run method which is pretty straight forward.
247
 
 
248
 
    The only wrinkle is that we have to map bzr's dictionary of options and arguments
249
 
    back into command line options and arguments for the script.
 
259
    We cheat a little here, when get_cmd_class() calls us we actually
 
260
    give it back an object we construct that has the appropriate path,
 
261
    help, options etc for the specified command.
 
262
 
 
263
    When run_bzr() tries to instantiate that 'class' it gets caught by
 
264
    the __call__ method, which we override to call the Command.__init__
 
265
    method. That then calls our run method which is pretty straight
 
266
    forward.
 
267
 
 
268
    The only wrinkle is that we have to map bzr's dictionary of options
 
269
    and arguments back into command line options and arguments for the
 
270
    script.
250
271
    """
251
272
 
252
273
    def find_command(cls, cmd):
503
524
    def run(self, source_list, dest):
504
525
        b = find_branch('.')
505
526
 
 
527
        # TODO: glob expansion on windows?
506
528
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
507
529
 
508
530
 
528
550
 
529
551
 
530
552
 
 
553
class cmd_mv(Command):
 
554
    """Move or rename a file.
 
555
 
 
556
    usage:
 
557
        bzr mv OLDNAME NEWNAME
 
558
        bzr mv SOURCE... DESTINATION
 
559
 
 
560
    If the last argument is a versioned directory, all the other names
 
561
    are moved into it.  Otherwise, there must be exactly two arguments
 
562
    and the file is changed to a new name, which must not already exist.
 
563
 
 
564
    Files cannot be moved between branches.
 
565
    """
 
566
    takes_args = ['names*']
 
567
    def run(self, names_list):
 
568
        if len(names_list) < 2:
 
569
            raise BzrCommandError("missing file argument")
 
570
        b = find_branch(names_list[0])
 
571
 
 
572
        rel_names = [b.relpath(x) for x in names_list]
 
573
        
 
574
        if os.path.isdir(names_list[-1]):
 
575
            # move into existing directory
 
576
            b.move(rel_names[:-1], rel_names[-1])
 
577
        else:
 
578
            if len(names_list) != 2:
 
579
                raise BzrCommandError('to mv multiple files the destination '
 
580
                                      'must be a versioned directory')
 
581
            b.move(rel_names[0], rel_names[1])
 
582
            
 
583
    
531
584
 
532
585
 
533
586
class cmd_pull(Command):
600
653
    """
601
654
    takes_args = ['from_location', 'to_location?']
602
655
    takes_options = ['revision']
 
656
    aliases = ['get', 'clone']
603
657
 
604
658
    def run(self, from_location, to_location=None, revision=None):
605
659
        import errno
606
660
        from bzrlib.merge import merge
607
 
        from bzrlib.branch import DivergedBranches, NoSuchRevision, \
 
661
        from bzrlib.branch import DivergedBranches, \
608
662
             find_cached_branch, Branch
609
663
        from shutil import rmtree
610
664
        from meta_store import CachedStore
651
705
                    revno, rev_id = br_from.get_revision_info(revision[0])
652
706
                try:
653
707
                    br_to.update_revisions(br_from, stop_revision=revno)
654
 
                except NoSuchRevision:
 
708
                except bzrlib.errors.NoSuchRevision:
655
709
                    rmtree(to_location)
656
710
                    msg = "The branch %s has no revision %d." % (from_location,
657
711
                                                                 revno)
797
851
    If files are listed, only the changes in those files are listed.
798
852
    Otherwise, all changes for the tree are listed.
799
853
 
800
 
    TODO: Given two revision arguments, show the difference between them.
801
 
 
802
854
    TODO: Allow diff across branches.
803
855
 
804
856
    TODO: Option to use external diff command; could be GNU diff, wdiff,
813
865
          deleted files.
814
866
 
815
867
    TODO: This probably handles non-Unix newlines poorly.
 
868
 
 
869
    examples:
 
870
        bzr diff
 
871
        bzr diff -r1
 
872
        bzr diff -r1:2
816
873
    """
817
874
    
818
875
    takes_args = ['file*']
831
888
        else:
832
889
            b = find_branch('.')
833
890
 
834
 
        # TODO: Make show_diff support taking 2 arguments
835
 
        base_rev = None
836
891
        if revision is not None:
837
 
            if len(revision) != 1:
838
 
                raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
839
 
            base_rev = revision[0]
840
 
    
841
 
        show_diff(b, base_rev, specific_files=file_list,
842
 
                  external_diff_options=diff_options)
843
 
 
 
892
            if len(revision) == 1:
 
893
                show_diff(b, revision[0], specific_files=file_list,
 
894
                          external_diff_options=diff_options)
 
895
            elif len(revision) == 2:
 
896
                show_diff(b, revision[0], specific_files=file_list,
 
897
                          external_diff_options=diff_options,
 
898
                          revision2=revision[1])
 
899
            else:
 
900
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
 
901
        else:
 
902
            show_diff(b, None, specific_files=file_list,
 
903
                      external_diff_options=diff_options)
844
904
 
845
905
        
846
906
 
872
932
    """List files modified in working tree."""
873
933
    hidden = True
874
934
    def run(self):
875
 
        from bzrlib.diff import compare_trees
 
935
        from bzrlib.delta import compare_trees
876
936
 
877
937
        b = find_branch('.')
878
938
        td = compare_trees(b.basis_tree(), b.working_tree())
927
987
    """
928
988
 
929
989
    takes_args = ['filename?']
930
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
 
990
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
 
991
                     'long', 'message', 'short',]
931
992
    
932
993
    def run(self, filename=None, timezone='original',
933
994
            verbose=False,
935
996
            forward=False,
936
997
            revision=None,
937
998
            message=None,
938
 
            long=False):
 
999
            long=False,
 
1000
            short=False):
939
1001
        from bzrlib.branch import find_branch
940
1002
        from bzrlib.log import log_formatter, show_log
941
1003
        import codecs
975
1037
        # in e.g. the default C locale.
976
1038
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
977
1039
 
978
 
        if long:
 
1040
        if not short:
979
1041
            log_format = 'long'
980
1042
        else:
981
1043
            log_format = 'short'
1198
1260
 
1199
1261
class cmd_commit(Command):
1200
1262
    """Commit changes into a new revision.
 
1263
    
 
1264
    If no arguments are given, the entire tree is committed.
1201
1265
 
1202
1266
    If selected files are specified, only changes to those files are
1203
 
    committed.  If a directory is specified then its contents are also
1204
 
    committed.
 
1267
    committed.  If a directory is specified then the directory and everything 
 
1268
    within it is committed.
1205
1269
 
1206
1270
    A selected-file commit may fail in some cases where the committed
1207
1271
    tree would be invalid, such as trying to commit a file in a
1215
1279
    takes_options = ['message', 'file', 'verbose', 'unchanged']
1216
1280
    aliases = ['ci', 'checkin']
1217
1281
 
 
1282
    # TODO: Give better message for -s, --summary, used by tla people
 
1283
    
1218
1284
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1219
1285
            unchanged=False):
1220
1286
        from bzrlib.errors import PointlessCommit
1222
1288
 
1223
1289
        ## Warning: shadows builtin file()
1224
1290
        if not message and not file:
 
1291
            # FIXME: Ugly; change status code to send to a provided function?
 
1292
            
1225
1293
            import cStringIO
1226
1294
            stdout = sys.stdout
1227
1295
            catcher = cStringIO.StringIO()
1242
1310
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1243
1311
 
1244
1312
        b = find_branch('.')
1245
 
 
 
1313
        if selected_list:
 
1314
            selected_list = [b.relpath(s) for s in selected_list]
 
1315
            
1246
1316
        try:
1247
1317
            b.commit(message, verbose=verbose,
1248
1318
                     specific_files=selected_list,
1483
1553
 
1484
1554
 
1485
1555
 
 
1556
class cmd_missing(Command):
 
1557
    """What is missing in this branch relative to other branch.
 
1558
    """
 
1559
    takes_args = ['remote?']
 
1560
    aliases = ['mis', 'miss']
 
1561
    # We don't have to add quiet to the list, because 
 
1562
    # unknown options are parsed as booleans
 
1563
    takes_options = ['verbose', 'quiet']
 
1564
 
 
1565
    def run(self, remote=None, verbose=False, quiet=False):
 
1566
        from bzrlib.branch import find_branch, DivergedBranches
 
1567
        from bzrlib.errors import BzrCommandError
 
1568
        from bzrlib.missing import get_parent, show_missing
 
1569
 
 
1570
        if verbose and quiet:
 
1571
            raise BzrCommandError('Cannot pass both quiet and verbose')
 
1572
 
 
1573
        b = find_branch('.')
 
1574
        parent = get_parent(b)
 
1575
        if remote is None:
 
1576
            if parent is None:
 
1577
                raise BzrCommandError("No missing location known or specified.")
 
1578
            else:
 
1579
                if not quiet:
 
1580
                    print "Using last location: %s" % parent
 
1581
                remote = parent
 
1582
        elif parent is None:
 
1583
            # We only update x-pull if it did not exist, missing should not change the parent
 
1584
            b.controlfile('x-pull', 'wb').write(remote + '\n')
 
1585
        br_remote = find_branch(remote)
 
1586
 
 
1587
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
 
1588
 
 
1589
 
1486
1590
class cmd_plugins(Command):
1487
1591
    """List plugins"""
1488
1592
    hidden = True
1515
1619
    'no-recurse':             None,
1516
1620
    'profile':                None,
1517
1621
    'revision':               _parse_revision_str,
 
1622
    'short':                  None,
1518
1623
    'show-ids':               None,
1519
1624
    'timezone':               str,
1520
1625
    'verbose':                None,
1681
1786
    return argdict
1682
1787
 
1683
1788
 
1684
 
def _parse_master_args(argv):
1685
 
    """Parse the arguments that always go with the original command.
1686
 
    These are things like bzr --no-plugins, etc.
1687
 
 
1688
 
    There are now 2 types of option flags. Ones that come *before* the command,
1689
 
    and ones that come *after* the command.
1690
 
    Ones coming *before* the command are applied against all possible commands.
1691
 
    And are generally applied before plugins are loaded.
1692
 
 
1693
 
    The current list are:
1694
 
        --builtin   Allow plugins to load, but don't let them override builtin commands,
1695
 
                    they will still be allowed if they do not override a builtin.
1696
 
        --no-plugins    Don't load any plugins. This lets you get back to official source
1697
 
                        behavior.
1698
 
        --profile   Enable the hotspot profile before running the command.
1699
 
                    For backwards compatibility, this is also a non-master option.
1700
 
        --version   Spit out the version of bzr that is running and exit.
1701
 
                    This is also a non-master option.
1702
 
        --help      Run help and exit, also a non-master option (I think that should stay, though)
1703
 
 
1704
 
    >>> argv, opts = _parse_master_args(['--test'])
1705
 
    Traceback (most recent call last):
1706
 
    ...
1707
 
    BzrCommandError: Invalid master option: 'test'
1708
 
    >>> argv, opts = _parse_master_args(['--version', 'command'])
1709
 
    >>> print argv
1710
 
    ['command']
1711
 
    >>> print opts['version']
1712
 
    True
1713
 
    >>> argv, opts = _parse_master_args(['--profile', 'command', '--more-options'])
1714
 
    >>> print argv
1715
 
    ['command', '--more-options']
1716
 
    >>> print opts['profile']
1717
 
    True
1718
 
    >>> argv, opts = _parse_master_args(['--no-plugins', 'command'])
1719
 
    >>> print argv
1720
 
    ['command']
1721
 
    >>> print opts['no-plugins']
1722
 
    True
1723
 
    >>> print opts['profile']
1724
 
    False
1725
 
    >>> argv, opts = _parse_master_args(['command', '--profile'])
1726
 
    >>> print argv
1727
 
    ['command', '--profile']
1728
 
    >>> print opts['profile']
1729
 
    False
1730
 
    """
1731
 
    master_opts = {'builtin':False,
1732
 
        'no-plugins':False,
1733
 
        'version':False,
1734
 
        'profile':False,
1735
 
        'help':False
1736
 
    }
1737
 
 
1738
 
    for arg in argv[:]:
1739
 
        if arg[:2] != '--': # at the first non-option, we return the rest
1740
 
            break
1741
 
        arg = arg[2:] # Remove '--'
1742
 
        if arg not in master_opts:
1743
 
            # We could say that this is not an error, that we should
1744
 
            # just let it be handled by the main section instead
1745
 
            raise BzrCommandError('Invalid master option: %r' % arg)
1746
 
        argv.pop(0) # We are consuming this entry
1747
 
        master_opts[arg] = True
1748
 
    return argv, master_opts
1749
 
 
1750
 
 
1751
1789
 
1752
1790
def run_bzr(argv):
1753
1791
    """Execute a command.
1756
1794
    logging and error handling.  
1757
1795
    
1758
1796
    argv
1759
 
       The command-line arguments, without the program name.
 
1797
       The command-line arguments, without the program name from argv[0]
1760
1798
    
1761
1799
    Returns a command status or raises an exception.
 
1800
 
 
1801
    Special master options: these must come before the command because
 
1802
    they control how the command is interpreted.
 
1803
 
 
1804
    --no-plugins
 
1805
        Do not load plugin modules at all
 
1806
 
 
1807
    --builtin
 
1808
        Only use builtin commands.  (Plugins are still allowed to change
 
1809
        other behaviour.)
 
1810
 
 
1811
    --profile
 
1812
        Run under the Python profiler.
1762
1813
    """
 
1814
    
1763
1815
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
1764
1816
 
1765
 
    # some options like --builtin and --no-plugins have special effects
1766
 
    argv, master_opts = _parse_master_args(argv)
1767
 
    if not master_opts['no-plugins']:
 
1817
    opt_profile = opt_no_plugins = opt_builtin = False
 
1818
 
 
1819
    # --no-plugins is handled specially at a very early stage. We need
 
1820
    # to load plugins before doing other command parsing so that they
 
1821
    # can override commands, but this needs to happen first.
 
1822
 
 
1823
    for a in argv[:]:
 
1824
        if a == '--profile':
 
1825
            opt_profile = True
 
1826
        elif a == '--no-plugins':
 
1827
            opt_no_plugins = True
 
1828
        elif a == '--builtin':
 
1829
            opt_builtin = True
 
1830
        else:
 
1831
            break
 
1832
        argv.remove(a)
 
1833
 
 
1834
    if not opt_no_plugins:
1768
1835
        from bzrlib.plugin import load_plugins
1769
1836
        load_plugins()
1770
1837
 
1771
1838
    args, opts = parse_args(argv)
1772
1839
 
1773
 
    if master_opts.get('help') or 'help' in opts:
 
1840
    if 'help' in opts:
1774
1841
        from bzrlib.help import help
1775
 
        if argv:
1776
 
            help(argv[0])
 
1842
        if args:
 
1843
            help(args[0])
1777
1844
        else:
1778
1845
            help()
1779
1846
        return 0            
1782
1849
        show_version()
1783
1850
        return 0
1784
1851
    
1785
 
    if args and args[0] == 'builtin':
1786
 
        include_plugins=False
1787
 
        args = args[1:]
1788
 
    
1789
 
    try:
1790
 
        cmd = str(args.pop(0))
1791
 
    except IndexError:
 
1852
    if not args:
1792
1853
        print >>sys.stderr, "please try 'bzr help' for help"
1793
1854
        return 1
1794
 
 
1795
 
    plugins_override = not (master_opts['builtin'])
1796
 
    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1797
 
 
1798
 
    profile = master_opts['profile']
1799
 
    # For backwards compatibility, I would rather stick with --profile being a
1800
 
    # master/global option
1801
 
    if 'profile' in opts:
1802
 
        profile = True
1803
 
        del opts['profile']
 
1855
    
 
1856
    cmd = str(args.pop(0))
 
1857
 
 
1858
    canonical_cmd, cmd_class = \
 
1859
                   get_cmd_class(cmd, plugins_override=not opt_builtin)
1804
1860
 
1805
1861
    # check options are reasonable
1806
1862
    allowed = cmd_class.takes_options
1815
1871
    for k, v in opts.items():
1816
1872
        cmdopts[k.replace('-', '_')] = v
1817
1873
 
1818
 
    if profile:
 
1874
    if opt_profile:
1819
1875
        import hotshot, tempfile
1820
1876
        pffileno, pfname = tempfile.mkstemp()
1821
1877
        try:
1842
1898
 
1843
1899
def _report_exception(summary, quiet=False):
1844
1900
    import traceback
 
1901
    
1845
1902
    log_error('bzr: ' + summary)
1846
1903
    bzrlib.trace.log_exception()
1847
1904
 
 
1905
    if os.environ.get('BZR_DEBUG'):
 
1906
        traceback.print_exc()
 
1907
 
1848
1908
    if not quiet:
 
1909
        sys.stderr.write('\n')
1849
1910
        tb = sys.exc_info()[2]
1850
1911
        exinfo = traceback.extract_tb(tb)
1851
1912
        if exinfo:
1867
1928
                sys.stdout.flush()
1868
1929
        except BzrError, e:
1869
1930
            quiet = isinstance(e, (BzrCommandError))
1870
 
            _report_exception('error: ' + e.args[0], quiet=quiet)
 
1931
            _report_exception('error: ' + str(e), quiet=quiet)
1871
1932
            if len(e.args) > 1:
1872
1933
                for h in e.args[1]:
1873
1934
                    # some explanation or hints