13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""builtin bzr commands"""
20
from StringIO import StringIO
21
22
from bzrlib.lazy_import import lazy_import
22
23
lazy_import(globals(), """
29
31
from bzrlib import (
40
42
merge as _mod_merge,
45
47
revision as _mod_revision,
53
55
from bzrlib.branch import Branch
54
56
from bzrlib.conflicts import ConflictList
55
from bzrlib.revisionspec import RevisionSpec, RevisionInfo
57
from bzrlib.revisionspec import RevisionSpec
56
58
from bzrlib.smtp_connection import SMTPConnection
57
59
from bzrlib.workingtree import WorkingTree
60
62
from bzrlib.commands import Command, display_command
61
from bzrlib.option import (
68
from bzrlib.trace import mutter, note, warning, is_quiet, get_verbosity_level
71
def tree_files(file_list, default_branch=u'.', canonicalize=True,
63
from bzrlib.option import ListOption, Option, RegistryOption
64
from bzrlib.progress import DummyProgress, ProgressPhase
65
from bzrlib.trace import mutter, note, log_error, warning, is_quiet, info
68
def tree_files(file_list, default_branch=u'.'):
74
return internal_tree_files(file_list, default_branch, canonicalize,
70
return internal_tree_files(file_list, default_branch)
76
71
except errors.FileInWrongBranch, e:
77
72
raise errors.BzrCommandError("%s is not in the same branch as %s" %
78
73
(e.path, file_list[0]))
81
def tree_files_for_add(file_list):
83
Return a tree and list of absolute paths from a file list.
85
Similar to tree_files, but add handles files a bit differently, so it a
86
custom implementation. In particular, MutableTreeTree.smart_add expects
87
absolute paths, which it immediately converts to relative paths.
89
# FIXME Would be nice to just return the relative paths like
90
# internal_tree_files does, but there are a large number of unit tests
91
# that assume the current interface to mutabletree.smart_add
93
tree, relpath = WorkingTree.open_containing(file_list[0])
94
if tree.supports_views():
95
view_files = tree.views.lookup_view()
97
for filename in file_list:
98
if not osutils.is_inside_any(view_files, filename):
99
raise errors.FileOutsideView(filename, view_files)
100
file_list = file_list[:]
101
file_list[0] = tree.abspath(relpath)
103
tree = WorkingTree.open_containing(u'.')[0]
104
if tree.supports_views():
105
view_files = tree.views.lookup_view()
107
file_list = view_files
108
view_str = views.view_display_str(view_files)
109
note("Ignoring files outside view. View is %s" % view_str)
110
return tree, file_list
113
def _get_one_revision(command_name, revisions):
114
if revisions is None:
116
if len(revisions) != 1:
117
raise errors.BzrCommandError(
118
'bzr %s --revision takes exactly one revision identifier' % (
123
def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
126
if revisions is None:
128
rev_tree = tree.basis_tree()
130
rev_tree = branch.basis_tree()
132
revision = _get_one_revision(command_name, revisions)
133
rev_tree = revision.as_tree(branch)
137
76
# XXX: Bad function name; should possibly also be a class method of
138
77
# WorkingTree rather than a function.
139
def internal_tree_files(file_list, default_branch=u'.', canonicalize=True,
78
def internal_tree_files(file_list, default_branch=u'.'):
141
79
"""Convert command-line paths to a WorkingTree and relative paths.
143
81
This is typically used for command-line processors that take one or
1013
688
' directory exists, but does not already'
1014
689
' have a control directory. This flag will'
1015
690
' allow push to proceed.'),
1017
help='Create a stacked branch that references the public location '
1018
'of the parent branch.'),
1019
Option('stacked-on',
1020
help='Create a stacked branch that refers to another branch '
1021
'for the commit history. Only the work not present in the '
1022
'referenced branch is included in the branch created.',
1025
692
takes_args = ['location?']
1026
693
encoding_type = 'replace'
1028
695
def run(self, location=None, remember=False, overwrite=False,
1029
create_prefix=False, verbose=False, revision=None,
1030
use_existing_dir=False, directory=None, stacked_on=None,
1032
from bzrlib.push import _show_push_branch
1034
# Get the source branch and revision_id
696
create_prefix=False, verbose=False,
697
use_existing_dir=False,
699
# FIXME: Way too big! Put this into a function called from the
1035
701
if directory is None:
1037
703
br_from = Branch.open_containing(directory)[0]
1038
revision = _get_one_revision('push', revision)
1039
if revision is not None:
1040
revision_id = revision.in_history(br_from).rev_id
1044
# Get the stacked_on branch, if any
1045
if stacked_on is not None:
1046
stacked_on = urlutils.normalize_url(stacked_on)
1048
parent_url = br_from.get_parent()
1050
parent = Branch.open(parent_url)
1051
stacked_on = parent.get_public_branch()
1053
# I considered excluding non-http url's here, thus forcing
1054
# 'public' branches only, but that only works for some
1055
# users, so it's best to just depend on the user spotting an
1056
# error by the feedback given to them. RBC 20080227.
1057
stacked_on = parent_url
1059
raise errors.BzrCommandError(
1060
"Could not determine branch to refer to.")
1062
# Get the destination location
704
stored_loc = br_from.get_push_location()
1063
705
if location is None:
1064
stored_loc = br_from.get_push_location()
1065
706
if stored_loc is None:
1066
raise errors.BzrCommandError(
1067
"No push location known or specified.")
707
raise errors.BzrCommandError("No push location known or specified.")
1069
709
display_url = urlutils.unescape_for_display(stored_loc,
1070
710
self.outf.encoding)
1071
self.outf.write("Using saved push location: %s\n" % display_url)
711
self.outf.write("Using saved location: %s\n" % display_url)
1072
712
location = stored_loc
1074
_show_push_branch(br_from, revision_id, location, self.outf,
1075
verbose=verbose, overwrite=overwrite, remember=remember,
1076
stacked_on=stacked_on, create_prefix=create_prefix,
1077
use_existing_dir=use_existing_dir)
714
to_transport = transport.get_transport(location)
716
br_to = repository_to = dir_to = None
718
dir_to = bzrdir.BzrDir.open_from_transport(to_transport)
719
except errors.NotBranchError:
720
pass # Didn't find anything
722
# If we can open a branch, use its direct repository, otherwise see
723
# if there is a repository without a branch.
725
br_to = dir_to.open_branch()
726
except errors.NotBranchError:
727
# Didn't find a branch, can we find a repository?
729
repository_to = dir_to.find_repository()
730
except errors.NoRepositoryPresent:
733
# Found a branch, so we must have found a repository
734
repository_to = br_to.repository
739
# The destination doesn't exist; create it.
740
# XXX: Refactor the create_prefix/no_create_prefix code into a
741
# common helper function
743
to_transport.mkdir('.')
744
except errors.FileExists:
745
if not use_existing_dir:
746
raise errors.BzrCommandError("Target directory %s"
747
" already exists, but does not have a valid .bzr"
748
" directory. Supply --use-existing-dir to push"
749
" there anyway." % location)
750
except errors.NoSuchFile:
751
if not create_prefix:
752
raise errors.BzrCommandError("Parent directory of %s"
754
"\nYou may supply --create-prefix to create all"
755
" leading parent directories."
757
_create_prefix(to_transport)
759
# Now the target directory exists, but doesn't have a .bzr
760
# directory. So we need to create it, along with any work to create
761
# all of the dependent branches, etc.
762
dir_to = br_from.bzrdir.clone_on_transport(to_transport,
763
revision_id=br_from.last_revision())
764
br_to = dir_to.open_branch()
765
# TODO: Some more useful message about what was copied
766
note('Created new branch.')
767
# We successfully created the target, remember it
768
if br_from.get_push_location() is None or remember:
769
br_from.set_push_location(br_to.base)
770
elif repository_to is None:
771
# we have a bzrdir but no branch or repository
772
# XXX: Figure out what to do other than complain.
773
raise errors.BzrCommandError("At %s you have a valid .bzr control"
774
" directory, but not a branch or repository. This is an"
775
" unsupported configuration. Please move the target directory"
776
" out of the way and try again."
779
# We have a repository but no branch, copy the revisions, and then
781
last_revision_id = br_from.last_revision()
782
repository_to.fetch(br_from.repository,
783
revision_id=last_revision_id)
784
br_to = br_from.clone(dir_to, revision_id=last_revision_id)
785
note('Created new branch.')
786
if br_from.get_push_location() is None or remember:
787
br_from.set_push_location(br_to.base)
788
else: # We have a valid to branch
789
# We were able to connect to the remote location, so remember it
790
# we don't need to successfully push because of possible divergence.
791
if br_from.get_push_location() is None or remember:
792
br_from.set_push_location(br_to.base)
794
old_rh = br_to.revision_history()
797
tree_to = dir_to.open_workingtree()
798
except errors.NotLocalUrl:
799
warning("This transport does not update the working "
800
"tree of: %s. See 'bzr help working-trees' for "
801
"more information." % br_to.base)
802
push_result = br_from.push(br_to, overwrite)
803
except errors.NoWorkingTree:
804
push_result = br_from.push(br_to, overwrite)
808
push_result = br_from.push(tree_to.branch, overwrite)
812
except errors.DivergedBranches:
813
raise errors.BzrCommandError('These branches have diverged.'
814
' Try using "merge" and then "push".')
815
if push_result is not None:
816
push_result.report(self.outf)
818
new_rh = br_to.revision_history()
821
from bzrlib.log import show_changed_revisions
822
show_changed_revisions(br_to, old_rh, new_rh,
825
# we probably did a clone rather than a push, so a message was
1080
830
class cmd_branch(Command):
1930
1599
raise errors.BzrCommandError(msg)
1933
def _parse_levels(s):
1937
msg = "The levels argument must be an integer."
1938
raise errors.BzrCommandError(msg)
1941
1602
class cmd_log(Command):
1942
"""Show historical log for a branch or subset of a branch.
1944
log is bzr's default tool for exploring the history of a branch.
1945
The branch to use is taken from the first parameter. If no parameters
1946
are given, the branch containing the working directory is logged.
1947
Here are some simple examples::
1949
bzr log log the current branch
1950
bzr log foo.py log a file in its branch
1951
bzr log http://server/branch log a branch on a server
1953
The filtering, ordering and information shown for each revision can
1954
be controlled as explained below. By default, all revisions are
1955
shown sorted (topologically) so that newer revisions appear before
1956
older ones and descendants always appear before ancestors. If displayed,
1957
merged revisions are shown indented under the revision in which they
1962
The log format controls how information about each revision is
1963
displayed. The standard log formats are called ``long``, ``short``
1964
and ``line``. The default is long. See ``bzr help log-formats``
1965
for more details on log formats.
1967
The following options can be used to control what information is
1970
-l N display a maximum of N revisions
1971
-n N display N levels of revisions (0 for all, 1 for collapsed)
1972
-v display a status summary (delta) for each revision
1973
-p display a diff (patch) for each revision
1974
--show-ids display revision-ids (and file-ids), not just revnos
1976
Note that the default number of levels to display is a function of the
1977
log format. If the -n option is not used, the standard log formats show
1978
just the top level (mainline).
1980
Status summaries are shown using status flags like A, M, etc. To see
1981
the changes explained using words like ``added`` and ``modified``
1982
instead, use the -vv option.
1986
To display revisions from oldest to newest, use the --forward option.
1987
In most cases, using this option will have little impact on the total
1988
time taken to produce a log, though --forward does not incrementally
1989
display revisions like --reverse does when it can.
1991
:Revision filtering:
1993
The -r option can be used to specify what revision or range of revisions
1994
to filter against. The various forms are shown below::
1996
-rX display revision X
1997
-rX.. display revision X and later
1998
-r..Y display up to and including revision Y
1999
-rX..Y display from X to Y inclusive
2001
See ``bzr help revisionspec`` for details on how to specify X and Y.
2002
Some common examples are given below::
2004
-r-1 show just the tip
2005
-r-10.. show the last 10 mainline revisions
2006
-rsubmit:.. show what's new on this branch
2007
-rancestor:path.. show changes since the common ancestor of this
2008
branch and the one at location path
2009
-rdate:yesterday.. show changes since yesterday
2011
When logging a range of revisions using -rX..Y, log starts at
2012
revision Y and searches back in history through the primary
2013
("left-hand") parents until it finds X. When logging just the
2014
top level (using -n1), an error is reported if X is not found
2015
along the way. If multi-level logging is used (-n0), X may be
2016
a nested merge revision and the log will be truncated accordingly.
2020
If parameters are given and the first one is not a branch, the log
2021
will be filtered to show only those revisions that changed the
2022
nominated files or directories.
2024
Filenames are interpreted within their historical context. To log a
2025
deleted file, specify a revision range so that the file existed at
2026
the end or start of the range.
2028
Historical context is also important when interpreting pathnames of
2029
renamed files/directories. Consider the following example:
2031
* revision 1: add tutorial.txt
2032
* revision 2: modify tutorial.txt
2033
* revision 3: rename tutorial.txt to guide.txt; add tutorial.txt
2037
* ``bzr log guide.txt`` will log the file added in revision 1
2039
* ``bzr log tutorial.txt`` will log the new file added in revision 3
2041
* ``bzr log -r2 -p tutorial.txt`` will show the changes made to
2042
the original file in revision 2.
2044
* ``bzr log -r2 -p guide.txt`` will display an error message as there
2045
was no file called guide.txt in revision 2.
2047
Renames are always followed by log. By design, there is no need to
2048
explicitly ask for this (and no way to stop logging a file back
2049
until it was last renamed).
2053
The --message option can be used for finding revisions that match a
2054
regular expression in a commit message.
2058
GUI tools and IDEs are often better at exploring history than command
2059
line tools. You may prefer qlog or glog from the QBzr and Bzr-Gtk packages
2060
respectively for example. (TortoiseBzr uses qlog for displaying logs.) See
2061
http://bazaar-vcs.org/BzrPlugins and http://bazaar-vcs.org/IDEIntegration.
2063
Web interfaces are often better at exploring history than command line
2064
tools, particularly for branches on servers. You may prefer Loggerhead
2065
or one of its alternatives. See http://bazaar-vcs.org/WebInterface.
2067
You may find it useful to add the aliases below to ``bazaar.conf``::
2071
top = log -l10 --line
2074
``bzr tip`` will then show the latest revision while ``bzr top``
2075
will show the last 10 mainline revisions. To see the details of a
2076
particular revision X, ``bzr show -rX``.
2078
If you are interested in looking deeper into a particular merge X,
2079
use ``bzr log -n0 -rX``.
2081
``bzr log -v`` on a branch with lots of history is currently
2082
very slow. A fix for this issue is currently under development.
2083
With or without that fix, it is recommended that a revision range
2084
be given when using the -v option.
2086
bzr has a generic full-text matching plugin, bzr-search, that can be
2087
used to find revisions matching user names, commit messages, etc.
2088
Among other features, this plugin can find all revisions containing
2089
a list of words but not others.
2091
When exploring non-mainline history on large projects with deep
2092
history, the performance of log can be greatly improved by installing
2093
the historycache plugin. This plugin buffers historical information
2094
trading disk space for faster speed.
1603
"""Show log of a branch, file, or directory.
1605
By default show the log of the branch containing the working directory.
1607
To request a range of logs, you can use the command -r begin..end
1608
-r revision requests a specific revision, -r ..end or -r begin.. are
1612
Log the current branch::
1620
Log the last 10 revisions of a branch::
1622
bzr log -r -10.. http://server/branch
2096
takes_args = ['file*']
2097
_see_also = ['log-formats', 'revisionspec']
1625
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
1627
takes_args = ['location?']
2098
1628
takes_options = [
2099
1629
Option('forward',
2100
1630
help='Show from oldest to newest.'),
2102
custom_help('verbose',
1633
help='Display timezone as local, original, or utc.'),
2103
1636
help='Show files changed in each revision.'),
2107
type=bzrlib.option._parse_revision_str,
2109
help='Show just the specified revision.'
2110
' See also "help revisionspec".'),
2114
help='Number of levels to display - 0 for all, 1 for flat.',
2116
type=_parse_levels),
2117
1640
Option('message',
2118
1641
short_name='m',
2119
1642
help='Show revisions whose message matches this '
2120
1643
'regular expression.',
2122
1645
Option('limit',
2124
1646
help='Limit the output to the first N revisions.',
2126
1648
type=_parse_limit),
2129
help='Show changes made in each revision as a patch.'),
2130
Option('include-merges',
2131
help='Show merged revisions like --levels 0 does.'),
2133
1650
encoding_type = 'replace'
2135
1652
@display_command
2136
def run(self, file_list=None, timezone='original',
1653
def run(self, location=None, timezone='original',
2138
1655
show_ids=False,
2142
1658
log_format=None,
2147
include_merges=False):
2148
from bzrlib.log import (
2150
make_log_request_dict,
2151
_get_info_for_log_files,
1661
from bzrlib.log import show_log
1662
assert message is None or isinstance(message, basestring), \
1663
"invalid message argument %r" % message
2153
1664
direction = (forward and 'forward') or 'reverse'
2158
raise errors.BzrCommandError(
2159
'--levels and --include-merges are mutually exclusive')
2161
if change is not None:
2163
raise errors.RangeInChangeOption()
2164
if revision is not None:
2165
raise errors.BzrCommandError(
2166
'--revision and --change are mutually exclusive')
2171
filter_by_dir = False
2173
# find the file ids to log and check for directory filtering
2174
b, file_info_list, rev1, rev2 = _get_info_for_log_files(revision,
2176
for relpath, file_id, kind in file_info_list:
1669
# find the file id to log:
1671
tree, b, fp = bzrdir.BzrDir.open_containing_tree_or_branch(
1675
tree = b.basis_tree()
1676
file_id = tree.path2id(fp)
2177
1677
if file_id is None:
2178
1678
raise errors.BzrCommandError(
2179
"Path unknown at end or start of revision range: %s" %
2181
# If the relpath is the top of the tree, we log everything
2186
file_ids.append(file_id)
2187
filter_by_dir = filter_by_dir or (
2188
kind in ['directory', 'tree-reference'])
1679
"Path does not have any revision history: %s" %
2191
# FIXME ? log the current subdir only RBC 20060203
1683
# FIXME ? log the current subdir only RBC 20060203
2192
1684
if revision is not None \
2193
1685
and len(revision) > 0 and revision[0].get_branch():
2194
1686
location = revision[0].get_branch()
2197
1689
dir, relpath = bzrdir.BzrDir.open_containing(location)
2198
1690
b = dir.open_branch()
2199
rev1, rev2 = _get_revision_range(revision, b, self.name())
2201
# Decide on the type of delta & diff filtering to use
2202
# TODO: add an --all-files option to make this configurable & consistent
2210
diff_type = 'partial'
2216
# Build the log formatter
1694
if revision is None:
1697
elif len(revision) == 1:
1698
rev1 = rev2 = revision[0].in_history(b)
1699
elif len(revision) == 2:
1700
if revision[1].get_branch() != revision[0].get_branch():
1701
# b is taken from revision[0].get_branch(), and
1702
# show_log will use its revision_history. Having
1703
# different branches will lead to weird behaviors.
1704
raise errors.BzrCommandError(
1705
"Log doesn't accept two revisions in different"
1707
rev1 = revision[0].in_history(b)
1708
rev2 = revision[1].in_history(b)
1710
raise errors.BzrCommandError(
1711
'bzr log --revision takes one or two values.')
2217
1713
if log_format is None:
2218
1714
log_format = log.log_formatter_registry.get_default(b)
2219
1716
lf = log_format(show_ids=show_ids, to_file=self.outf,
2220
show_timezone=timezone,
2221
delta_format=get_verbosity_level(),
2223
show_advice=levels is None)
2225
# Choose the algorithm for doing the logging. It's annoying
2226
# having multiple code paths like this but necessary until
2227
# the underlying repository format is faster at generating
2228
# deltas or can provide everything we need from the indices.
2229
# The default algorithm - match-using-deltas - works for
2230
# multiple files and directories and is faster for small
2231
# amounts of history (200 revisions say). However, it's too
2232
# slow for logging a single file in a repository with deep
2233
# history, i.e. > 10K revisions. In the spirit of "do no
2234
# evil when adding features", we continue to use the
2235
# original algorithm - per-file-graph - for the "single
2236
# file that isn't a directory without showing a delta" case.
2237
partial_history = revision and b.repository._format.supports_chks
2238
match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2239
or delta_type or partial_history)
2241
# Build the LogRequest and execute it
2242
if len(file_ids) == 0:
2244
rqst = make_log_request_dict(
2245
direction=direction, specific_fileids=file_ids,
2246
start_revision=rev1, end_revision=rev2, limit=limit,
2247
message_search=message, delta_type=delta_type,
2248
diff_type=diff_type, _match_using_deltas=match_using_deltas)
2249
Logger(b, rqst).show(lf)
1717
show_timezone=timezone)
1723
direction=direction,
1724
start_revision=rev1,
2254
def _get_revision_range(revisionspec_list, branch, command_name):
2255
"""Take the input of a revision option and turn it into a revision range.
2257
It returns RevisionInfo objects which can be used to obtain the rev_id's
2258
of the desired revisions. It does some user input validations.
2260
if revisionspec_list is None:
2263
elif len(revisionspec_list) == 1:
2264
rev1 = rev2 = revisionspec_list[0].in_history(branch)
2265
elif len(revisionspec_list) == 2:
2266
start_spec = revisionspec_list[0]
2267
end_spec = revisionspec_list[1]
2268
if end_spec.get_branch() != start_spec.get_branch():
2269
# b is taken from revision[0].get_branch(), and
2270
# show_log will use its revision_history. Having
2271
# different branches will lead to weird behaviors.
2272
raise errors.BzrCommandError(
2273
"bzr %s doesn't accept two revisions in different"
2274
" branches." % command_name)
2275
rev1 = start_spec.in_history(branch)
2276
# Avoid loading all of history when we know a missing
2277
# end of range means the last revision ...
2278
if end_spec.spec is None:
2279
last_revno, last_revision_id = branch.last_revision_info()
2280
rev2 = RevisionInfo(branch, last_revno, last_revision_id)
2282
rev2 = end_spec.in_history(branch)
2284
raise errors.BzrCommandError(
2285
'bzr %s --revision takes one or two values.' % command_name)
2289
def _revision_range_to_revid_range(revision_range):
2292
if revision_range[0] is not None:
2293
rev_id1 = revision_range[0].rev_id
2294
if revision_range[1] is not None:
2295
rev_id2 = revision_range[1].rev_id
2296
return rev_id1, rev_id2
2298
1732
def get_log_format(long=False, short=False, line=False, default='long'):
2299
1733
log_format = default
3276
2510
short_name='x',
3277
2511
help='Exclude tests that match this regular'
3278
2512
' expression.'),
3280
help='Output test progress via subunit.'),
3281
2513
Option('strict', help='Fail on missing dependencies or '
3282
2514
'known failures.'),
3283
Option('load-list', type=str, argname='TESTLISTFILE',
3284
help='Load a test id list from a text file.'),
3285
ListOption('debugflag', type=str, short_name='E',
3286
help='Turn on a selftest debug flag.'),
3287
ListOption('starting-with', type=str, argname='TESTID',
3288
param_name='starting_with', short_name='s',
3290
'Load only the tests starting with TESTID.'),
3292
2516
encoding_type = 'replace'
3295
Command.__init__(self)
3296
self.additional_selftest_args = {}
3298
def run(self, testspecs_list=None, verbose=False, one=False,
2518
def run(self, testspecs_list=None, verbose=None, one=False,
3299
2519
transport=None, benchmark=None,
3300
2520
lsprof_timed=None, cache_dir=None,
3301
2521
first=False, list_only=False,
3302
randomize=None, exclude=None, strict=False,
3303
load_list=None, debugflag=None, starting_with=None, subunit=False,
2522
randomize=None, exclude=None, strict=False):
3305
2524
from bzrlib.tests import selftest
3306
2525
import bzrlib.benchmarks as benchmarks
3307
2526
from bzrlib.benchmarks import tree_creator
3309
# Make deprecation warnings visible, unless -Werror is set
3310
symbol_versioning.activate_deprecation_warnings(override=False)
2527
from bzrlib.version import show_version
3312
2529
if cache_dir is not None:
3313
2530
tree_creator.TreeCreator.CACHE_ROOT = osutils.abspath(cache_dir)
2532
show_version(show_config=False, show_copyright=False)
3314
2534
if testspecs_list is not None:
3315
2535
pattern = '|'.join(testspecs_list)
3320
from bzrlib.tests import SubUnitBzrRunner
3322
raise errors.BzrCommandError("subunit not available. subunit "
3323
"needs to be installed to use --subunit.")
3324
self.additional_selftest_args['runner_class'] = SubUnitBzrRunner
3326
self.additional_selftest_args.setdefault(
3327
'suite_decorators', []).append(parallel)
3329
2539
test_suite_factory = benchmarks.test_suite
3330
# Unless user explicitly asks for quiet, be verbose in benchmarks
3331
verbose = not is_quiet()
3332
2542
# TODO: should possibly lock the history file...
3333
2543
benchfile = open(".perf_history", "at", buffering=1)
3335
2545
test_suite_factory = None
3336
2548
benchfile = None
3338
selftest_kwargs = {"verbose": verbose,
3340
"stop_on_failure": one,
3341
"transport": transport,
3342
"test_suite_factory": test_suite_factory,
3343
"lsprof_timed": lsprof_timed,
3344
"bench_history": benchfile,
3345
"matching_tests_first": first,
3346
"list_only": list_only,
3347
"random_seed": randomize,
3348
"exclude_pattern": exclude,
3350
"load_list": load_list,
3351
"debug_flags": debugflag,
3352
"starting_with": starting_with
3354
selftest_kwargs.update(self.additional_selftest_args)
3355
result = selftest(**selftest_kwargs)
2550
result = selftest(verbose=verbose,
2552
stop_on_failure=one,
2553
transport=transport,
2554
test_suite_factory=test_suite_factory,
2555
lsprof_timed=lsprof_timed,
2556
bench_history=benchfile,
2557
matching_tests_first=first,
2558
list_only=list_only,
2559
random_seed=randomize,
2560
exclude_pattern=exclude,
3357
2564
if benchfile is not None:
3358
2565
benchfile.close()
2567
info('tests passed')
2569
info('tests failed')
3359
2570
return int(not result)
3362
2573
class cmd_version(Command):
3363
2574
"""Show version of bzr."""
3365
encoding_type = 'replace'
3367
Option("short", help="Print just the version number."),
3370
2576
@display_command
3371
def run(self, short=False):
3372
2578
from bzrlib.version import show_version
3374
self.outf.write(bzrlib.version_string + '\n')
3376
show_version(to_file=self.outf)
3379
2582
class cmd_rocks(Command):
4835
3827
'rather than the one containing the working directory.',
4836
3828
short_name='f',
4838
Option('output', short_name='o',
4839
help='Write merge directive to this file; '
4840
'use - for stdout.',
4842
Option('mail-to', help='Mail the request to this address.',
3830
Option('output', short_name='o', help='Write directive to this file.',
4846
Option('body', help='Body for the email.', type=unicode),
4847
RegistryOption('format',
4848
help='Use the specified output format.',
4849
lazy_registry=('bzrlib.send', 'format_registry'))
3833
RegistryOption.from_kwargs('format',
3834
'Use the specified output format.',
3835
**{'4': 'Bundle format 4, Merge Directive 2 (default)',
3836
'0.9': 'Bundle format 0.9, Merge Directive 1',})
4852
3839
def run(self, submit_branch=None, public_branch=None, no_bundle=False,
4853
3840
no_patch=False, revision=None, remember=False, output=None,
4854
format=None, mail_to=None, message=None, body=None, **kwargs):
4855
from bzrlib.send import send
4856
return send(submit_branch, revision, public_branch, remember,
3841
format='4', **kwargs):
3843
raise errors.BzrCommandError('File must be specified with'
3845
return self._run(submit_branch, revision, public_branch, remember,
4857
3846
format, no_bundle, no_patch, output,
4858
kwargs.get('from', '.'), mail_to, message, body,
3847
kwargs.get('from', '.'))
3849
def _run(self, submit_branch, revision, public_branch, remember, format,
3850
no_bundle, no_patch, output, from_,):
3851
from bzrlib.revision import ensure_null, NULL_REVISION
3855
outfile = open(output, 'wb')
3857
branch = Branch.open_containing(from_)[0]
3858
if remember and submit_branch is None:
3859
raise errors.BzrCommandError(
3860
'--remember requires a branch to be specified.')
3861
stored_submit_branch = branch.get_submit_branch()
3862
remembered_submit_branch = False
3863
if submit_branch is None:
3864
submit_branch = stored_submit_branch
3865
remembered_submit_branch = True
3867
if stored_submit_branch is None or remember:
3868
branch.set_submit_branch(submit_branch)
3869
if submit_branch is None:
3870
submit_branch = branch.get_parent()
3871
remembered_submit_branch = True
3872
if submit_branch is None:
3873
raise errors.BzrCommandError('No submit branch known or'
3875
if remembered_submit_branch:
3876
note('Using saved location: %s', submit_branch)
3878
stored_public_branch = branch.get_public_branch()
3879
if public_branch is None:
3880
public_branch = stored_public_branch
3881
elif stored_public_branch is None or remember:
3882
branch.set_public_branch(public_branch)
3883
if no_bundle and public_branch is None:
3884
raise errors.BzrCommandError('No public branch specified or'
3886
base_revision_id = None
3887
if revision is not None:
3888
if len(revision) > 2:
3889
raise errors.BzrCommandError('bzr send takes '
3890
'at most two one revision identifiers')
3891
revision_id = revision[-1].in_history(branch).rev_id
3892
if len(revision) == 2:
3893
base_revision_id = revision[0].in_history(branch).rev_id
3894
base_revision_id = ensure_null(base_revision_id)
3896
revision_id = branch.last_revision()
3897
revision_id = ensure_null(revision_id)
3898
if revision_id == NULL_REVISION:
3899
raise errors.BzrCommandError('No revisions to submit.')
3901
directive = merge_directive.MergeDirective2.from_objects(
3902
branch.repository, revision_id, time.time(),
3903
osutils.local_time_offset(), submit_branch,
3904
public_branch=public_branch, include_patch=not no_patch,
3905
include_bundle=not no_bundle, message=None,
3906
base_revision_id=base_revision_id)
3907
elif format == '0.9':
3910
patch_type = 'bundle'
3912
raise errors.BzrCommandError('Format 0.9 does not'
3913
' permit bundle with no patch')
3919
directive = merge_directive.MergeDirective.from_objects(
3920
branch.repository, revision_id, time.time(),
3921
osutils.local_time_offset(), submit_branch,
3922
public_branch=public_branch, patch_type=patch_type,
3925
outfile.writelines(directive.to_lines())
4862
3931
class cmd_bundle_revisions(cmd_send):
4863
"""Create a merge-directive for submitting changes.
3933
"""Create a merge-directive for submiting changes.
4865
3935
A merge directive provides many things needed for requesting merges:
5005
4051
short_name='d',
5008
RegistryOption.from_kwargs('sort',
5009
'Sort tags by different criteria.', title='Sorting',
5010
alpha='Sort tags lexicographically (default).',
5011
time='Sort tags chronologically.',
5017
4056
@display_command
5024
4060
branch, relpath = Branch.open_containing(directory)
5026
tags = branch.tags.get_tag_dict().items()
5033
graph = branch.repository.get_graph()
5034
rev1, rev2 = _get_revision_range(revision, branch, self.name())
5035
revid1, revid2 = rev1.rev_id, rev2.rev_id
5036
# only show revisions between revid1 and revid2 (inclusive)
5037
tags = [(tag, revid) for tag, revid in tags if
5038
graph.is_between(revid, revid1, revid2)]
5043
elif sort == 'time':
5045
for tag, revid in tags:
5047
revobj = branch.repository.get_revision(revid)
5048
except errors.NoSuchRevision:
5049
timestamp = sys.maxint # place them at the end
5051
timestamp = revobj.timestamp
5052
timestamps[revid] = timestamp
5053
tags.sort(key=lambda x: timestamps[x[1]])
5055
# [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5056
for index, (tag, revid) in enumerate(tags):
5058
revno = branch.revision_id_to_dotted_revno(revid)
5059
if isinstance(revno, tuple):
5060
revno = '.'.join(map(str, revno))
5061
except errors.NoSuchRevision:
5062
# Bad tag data/merges can lead to tagged revisions
5063
# which are not in this branch. Fail gracefully ...
5065
tags[index] = (tag, revno)
5066
for tag, revspec in tags:
5067
self.outf.write('%-20s %s\n' % (tag, revspec))
5070
class cmd_reconfigure(Command):
5071
"""Reconfigure the type of a bzr directory.
5073
A target configuration must be specified.
5075
For checkouts, the bind-to location will be auto-detected if not specified.
5076
The order of preference is
5077
1. For a lightweight checkout, the current bound location.
5078
2. For branches that used to be checkouts, the previously-bound location.
5079
3. The push location.
5080
4. The parent location.
5081
If none of these is available, --bind-to must be specified.
5084
_see_also = ['branches', 'checkouts', 'standalone-trees', 'working-trees']
5085
takes_args = ['location?']
5087
RegistryOption.from_kwargs(
5089
title='Target type',
5090
help='The type to reconfigure the directory to.',
5091
value_switches=True, enum_switch=False,
5092
branch='Reconfigure to be an unbound branch with no working tree.',
5093
tree='Reconfigure to be an unbound branch with a working tree.',
5094
checkout='Reconfigure to be a bound branch with a working tree.',
5095
lightweight_checkout='Reconfigure to be a lightweight'
5096
' checkout (with no local history).',
5097
standalone='Reconfigure to be a standalone branch '
5098
'(i.e. stop using shared repository).',
5099
use_shared='Reconfigure to use a shared repository.',
5100
with_trees='Reconfigure repository to create '
5101
'working trees on branches by default.',
5102
with_no_trees='Reconfigure repository to not create '
5103
'working trees on branches by default.'
5105
Option('bind-to', help='Branch to bind checkout to.', type=str),
5107
help='Perform reconfiguration even if local changes'
5111
def run(self, location=None, target_type=None, bind_to=None, force=False):
5112
directory = bzrdir.BzrDir.open(location)
5113
if target_type is None:
5114
raise errors.BzrCommandError('No target configuration specified')
5115
elif target_type == 'branch':
5116
reconfiguration = reconfigure.Reconfigure.to_branch(directory)
5117
elif target_type == 'tree':
5118
reconfiguration = reconfigure.Reconfigure.to_tree(directory)
5119
elif target_type == 'checkout':
5120
reconfiguration = reconfigure.Reconfigure.to_checkout(
5122
elif target_type == 'lightweight-checkout':
5123
reconfiguration = reconfigure.Reconfigure.to_lightweight_checkout(
5125
elif target_type == 'use-shared':
5126
reconfiguration = reconfigure.Reconfigure.to_use_shared(directory)
5127
elif target_type == 'standalone':
5128
reconfiguration = reconfigure.Reconfigure.to_standalone(directory)
5129
elif target_type == 'with-trees':
5130
reconfiguration = reconfigure.Reconfigure.set_repository_trees(
5132
elif target_type == 'with-no-trees':
5133
reconfiguration = reconfigure.Reconfigure.set_repository_trees(
5135
reconfiguration.apply(force)
5138
class cmd_switch(Command):
5139
"""Set the branch of a checkout and update.
5141
For lightweight checkouts, this changes the branch being referenced.
5142
For heavyweight checkouts, this checks that there are no local commits
5143
versus the current bound branch, then it makes the local branch a mirror
5144
of the new location and binds to it.
5146
In both cases, the working tree is updated and uncommitted changes
5147
are merged. The user can commit or revert these as they desire.
5149
Pending merges need to be committed or reverted before using switch.
5151
The path to the branch to switch to can be specified relative to the parent
5152
directory of the current branch. For example, if you are currently in a
5153
checkout of /path/to/branch, specifying 'newbranch' will find a branch at
5156
Bound branches use the nickname of its master branch unless it is set
5157
locally, in which case switching will update the the local nickname to be
5161
takes_args = ['to_location']
5162
takes_options = [Option('force',
5163
help='Switch even if local commits will be lost.')
5166
def run(self, to_location, force=False):
5167
from bzrlib import switch
5169
control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5171
branch = control_dir.open_branch()
5172
had_explicit_nick = branch.get_config().has_explicit_nickname()
5173
except errors.NotBranchError:
5174
had_explicit_nick = False
5176
to_branch = Branch.open(to_location)
5177
except errors.NotBranchError:
5178
this_url = self._get_branch_location(control_dir)
5179
to_branch = Branch.open(
5180
urlutils.join(this_url, '..', to_location))
5181
switch.switch(control_dir, to_branch, force)
5182
if had_explicit_nick:
5183
branch = control_dir.open_branch() #get the new branch!
5184
branch.nick = to_branch.nick
5185
note('Switched to branch: %s',
5186
urlutils.unescape_for_display(to_branch.base, 'utf-8'))
5188
def _get_branch_location(self, control_dir):
5189
"""Return location of branch for this control dir."""
5191
this_branch = control_dir.open_branch()
5192
# This may be a heavy checkout, where we want the master branch
5193
master_location = this_branch.get_bound_location()
5194
if master_location is not None:
5195
return master_location
5196
# If not, use a local sibling
5197
return this_branch.base
5198
except errors.NotBranchError:
5199
format = control_dir.find_branch_format()
5200
if getattr(format, 'get_reference', None) is not None:
5201
return format.get_reference(control_dir)
5203
return control_dir.root_transport.base
5206
class cmd_view(Command):
5207
"""Manage filtered views.
5209
Views provide a mask over the tree so that users can focus on
5210
a subset of a tree when doing their work. After creating a view,
5211
commands that support a list of files - status, diff, commit, etc -
5212
effectively have that list of files implicitly given each time.
5213
An explicit list of files can still be given but those files
5214
must be within the current view.
5216
In most cases, a view has a short life-span: it is created to make
5217
a selected change and is deleted once that change is committed.
5218
At other times, you may wish to create one or more named views
5219
and switch between them.
5221
To disable the current view without deleting it, you can switch to
5222
the pseudo view called ``off``. This can be useful when you need
5223
to see the whole tree for an operation or two (e.g. merge) but
5224
want to switch back to your view after that.
5227
To define the current view::
5229
bzr view file1 dir1 ...
5231
To list the current view::
5235
To delete the current view::
5239
To disable the current view without deleting it::
5241
bzr view --switch off
5243
To define a named view and switch to it::
5245
bzr view --name view-name file1 dir1 ...
5247
To list a named view::
5249
bzr view --name view-name
5251
To delete a named view::
5253
bzr view --name view-name --delete
5255
To switch to a named view::
5257
bzr view --switch view-name
5259
To list all views defined::
5263
To delete all views::
5265
bzr view --delete --all
5269
takes_args = ['file*']
5272
help='Apply list or delete action to all views.',
5275
help='Delete the view.',
5278
help='Name of the view to define, list or delete.',
5282
help='Name of the view to switch to.',
5287
def run(self, file_list,
5293
tree, file_list = tree_files(file_list, apply_view=False)
5294
current_view, view_dict = tree.views.get_view_info()
5299
raise errors.BzrCommandError(
5300
"Both --delete and a file list specified")
5302
raise errors.BzrCommandError(
5303
"Both --delete and --switch specified")
5305
tree.views.set_view_info(None, {})
5306
self.outf.write("Deleted all views.\n")
5308
raise errors.BzrCommandError("No current view to delete")
5310
tree.views.delete_view(name)
5311
self.outf.write("Deleted '%s' view.\n" % name)
5314
raise errors.BzrCommandError(
5315
"Both --switch and a file list specified")
5317
raise errors.BzrCommandError(
5318
"Both --switch and --all specified")
5319
elif switch == 'off':
5320
if current_view is None:
5321
raise errors.BzrCommandError("No current view to disable")
5322
tree.views.set_view_info(None, view_dict)
5323
self.outf.write("Disabled '%s' view.\n" % (current_view))
5325
tree.views.set_view_info(switch, view_dict)
5326
view_str = views.view_display_str(tree.views.lookup_view())
5327
self.outf.write("Using '%s' view: %s\n" % (switch, view_str))
5330
self.outf.write('Views defined:\n')
5331
for view in sorted(view_dict):
5332
if view == current_view:
5336
view_str = views.view_display_str(view_dict[view])
5337
self.outf.write('%s %-20s %s\n' % (active, view, view_str))
5339
self.outf.write('No views defined.\n')
5342
# No name given and no current view set
5345
raise errors.BzrCommandError(
5346
"Cannot change the 'off' pseudo view")
5347
tree.views.set_view(name, sorted(file_list))
5348
view_str = views.view_display_str(tree.views.lookup_view())
5349
self.outf.write("Using '%s' view: %s\n" % (name, view_str))
5353
# No name given and no current view set
5354
self.outf.write('No current view.\n')
5356
view_str = views.view_display_str(tree.views.lookup_view(name))
5357
self.outf.write("'%s' view is: %s\n" % (name, view_str))
5360
class cmd_hooks(Command):
5366
for hook_key in sorted(hooks.known_hooks.keys()):
5367
some_hooks = hooks.known_hooks_key_to_object(hook_key)
5368
self.outf.write("%s:\n" % type(some_hooks).__name__)
5369
for hook_name, hook_point in sorted(some_hooks.items()):
5370
self.outf.write(" %s:\n" % (hook_name,))
5371
found_hooks = list(hook_point)
5373
for hook in found_hooks:
5374
self.outf.write(" %s\n" %
5375
(some_hooks.get_hook_name(hook),))
5377
self.outf.write(" <no hooks installed>\n")
5380
class cmd_shelve(Command):
5381
"""Temporarily set aside some changes from the current tree.
5383
Shelve allows you to temporarily put changes you've made "on the shelf",
5384
ie. out of the way, until a later time when you can bring them back from
5385
the shelf with the 'unshelve' command. The changes are stored alongside
5386
your working tree, and so they aren't propagated along with your branch nor
5387
will they survive its deletion.
5389
If shelve --list is specified, previously-shelved changes are listed.
5391
Shelve is intended to help separate several sets of changes that have
5392
been inappropriately mingled. If you just want to get rid of all changes
5393
and you don't need to restore them later, use revert. If you want to
5394
shelve all text changes at once, use shelve --all.
5396
If filenames are specified, only the changes to those files will be
5397
shelved. Other files will be left untouched.
5399
If a revision is specified, changes since that revision will be shelved.
5401
You can put multiple items on the shelf, and by default, 'unshelve' will
5402
restore the most recently shelved changes.
5405
takes_args = ['file*']
5409
Option('all', help='Shelve all changes.'),
5411
RegistryOption('writer', 'Method to use for writing diffs.',
5412
bzrlib.option.diff_writer_registry,
5413
value_switches=True, enum_switch=False),
5415
Option('list', help='List shelved changes.'),
5417
help='Destroy removed changes instead of shelving them.'),
5419
_see_also = ['unshelve']
5421
def run(self, revision=None, all=False, file_list=None, message=None,
5422
writer=None, list=False, destroy=False):
5424
return self.run_for_list()
5425
from bzrlib.shelf_ui import Shelver
5427
writer = bzrlib.option.diff_writer_registry.get()
5429
Shelver.from_args(writer(sys.stdout), revision, all, file_list,
5430
message, destroy=destroy).run()
5431
except errors.UserAbort:
5434
def run_for_list(self):
5435
tree = WorkingTree.open_containing('.')[0]
5438
manager = tree.get_shelf_manager()
5439
shelves = manager.active_shelves()
5440
if len(shelves) == 0:
5441
note('No shelved changes.')
5443
for shelf_id in reversed(shelves):
5444
message = manager.get_metadata(shelf_id).get('message')
5446
message = '<no message>'
5447
self.outf.write('%3d: %s\n' % (shelf_id, message))
5453
class cmd_unshelve(Command):
5454
"""Restore shelved changes.
5456
By default, the most recently shelved changes are restored. However if you
5457
specify a shelf by id those changes will be restored instead. This works
5458
best when the changes don't depend on each other.
5461
takes_args = ['shelf_id?']
5463
RegistryOption.from_kwargs(
5464
'action', help="The action to perform.",
5465
enum_switch=False, value_switches=True,
5466
apply="Apply changes and remove from the shelf.",
5467
dry_run="Show changes, but do not apply or remove them.",
5468
delete_only="Delete changes without applying them."
5471
_see_also = ['shelve']
5473
def run(self, shelf_id=None, action='apply'):
5474
from bzrlib.shelf_ui import Unshelver
5475
Unshelver.from_args(shelf_id, action).run()
5478
class cmd_clean_tree(Command):
5479
"""Remove unwanted files from working tree.
5481
By default, only unknown files, not ignored files, are deleted. Versioned
5482
files are never deleted.
5484
Another class is 'detritus', which includes files emitted by bzr during
5485
normal operations and selftests. (The value of these files decreases with
5488
If no options are specified, unknown files are deleted. Otherwise, option
5489
flags are respected, and may be combined.
5491
To check what clean-tree will do, use --dry-run.
5493
takes_options = [Option('ignored', help='Delete all ignored files.'),
5494
Option('detritus', help='Delete conflict files, merge'
5495
' backups, and failed selftest dirs.'),
5497
help='Delete files unknown to bzr (default).'),
5498
Option('dry-run', help='Show files to delete instead of'
5500
Option('force', help='Do not prompt before deleting.')]
5501
def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
5503
from bzrlib.clean_tree import clean_tree
5504
if not (unknown or ignored or detritus):
5508
clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus,
5509
dry_run=dry_run, no_prompt=force)
5512
class cmd_reference(Command):
5513
"""list, view and set branch locations for nested trees.
5515
If no arguments are provided, lists the branch locations for nested trees.
5516
If one argument is provided, display the branch location for that tree.
5517
If two arguments are provided, set the branch location for that tree.
5522
takes_args = ['path?', 'location?']
5524
def run(self, path=None, location=None):
5526
if path is not None:
5528
tree, branch, relpath =(
5529
bzrdir.BzrDir.open_containing_tree_or_branch(branchdir))
5530
if path is not None:
5533
tree = branch.basis_tree()
5535
info = branch._get_all_reference_info().iteritems()
5536
self._display_reference_info(tree, branch, info)
5538
file_id = tree.path2id(path)
5540
raise errors.NotVersionedError(path)
5541
if location is None:
5542
info = [(file_id, branch.get_reference_info(file_id))]
5543
self._display_reference_info(tree, branch, info)
5545
branch.set_reference_info(file_id, path, location)
5547
def _display_reference_info(self, tree, branch, info):
5549
for file_id, (path, location) in info:
5551
path = tree.id2path(file_id)
5552
except errors.NoSuchId:
5554
ref_list.append((path, location))
5555
for path, location in sorted(ref_list):
5556
self.outf.write('%s %s\n' % (path, location))
4061
for tag_name, target in sorted(branch.tags.get_tag_dict().items()):
4062
self.outf.write('%-20s %s\n' % (tag_name, target))
4065
def _create_prefix(cur_transport):
4066
needed = [cur_transport]
4067
# Recurse upwards until we can create a directory successfully
4069
new_transport = cur_transport.clone('..')
4070
if new_transport.base == cur_transport.base:
4071
raise errors.BzrCommandError(
4072
"Failed to create path prefix for %s."
4073
% cur_transport.base)
4075
new_transport.mkdir('.')
4076
except errors.NoSuchFile:
4077
needed.append(new_transport)
4078
cur_transport = new_transport
4081
# Now we only need to create child directories
4083
cur_transport = needed.pop()
4084
cur_transport.ensure_base()
4087
def _get_mergeable_helper(location):
4088
"""Get a merge directive or bundle if 'location' points to one.
4090
Try try to identify a bundle and returns its mergeable form. If it's not,
4091
we return the tried transport anyway so that it can reused to access the
4094
:param location: can point to a bundle or a branch.
4096
:return: mergeable, transport
4099
url = urlutils.normalize_url(location)
4100
url, filename = urlutils.split(url, exclude_trailing_slash=False)
4101
location_transport = transport.get_transport(url)
4104
# There may be redirections but we ignore the intermediate
4105
# and final transports used
4106
read = bundle.read_mergeable_from_transport
4107
mergeable, t = read(location_transport, filename)
4108
except errors.NotABundle:
4109
# Continue on considering this url a Branch but adjust the
4110
# location_transport
4111
location_transport = location_transport.clone(filename)
4112
return mergeable, location_transport
5559
4115
# these get imported and then picked up by the scan for cmd_*
5560
4116
# TODO: Some more consistent way to split command definitions across files;
5561
# we do need to load at least some information about them to know of
4117
# we do need to load at least some information about them to know of
5562
4118
# aliases. ideally we would avoid loading the implementation until the
5563
4119
# details were needed.
5564
4120
from bzrlib.cmd_version_info import cmd_version_info