2
"""Shelf - temporarily set aside changes, then bring them back."""
9
from bzrlib.tests.TestUtil import TestLoader
11
return TestLoader().loadTestsFromModule(tests)
14
raise Exception("Bzr not found!")
1
# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron@aaronbentley.com>
2
# Copyright (C) 2005, 2006 Canonical Limited.
3
# Copyright (C) 2006 Michael Ellerman.
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
Various useful plugins for working with bzr.
25
from bzrlib.lazy_import import lazy_import
26
lazy_import(globals(), """
27
from bzrlib import help, urlutils
31
from version import version_info, __version__
32
from command import BzrToolsCommand
33
from errors import CommandError
34
from patchsource import BzrPatchSource
38
import bzrlib.builtins
39
import bzrlib.commands
40
from bzrlib.branch import Branch
41
from bzrlib.bzrdir import BzrDir
42
from bzrlib.commands import get_cmd_object
43
from bzrlib.errors import BzrCommandError
45
from bzrlib.trace import note
46
from bzrlib.option import Option
48
from command import BzrToolsCommand
50
bzrlib.ignores.add_runtime_ignores(['./.shelf'])
53
class cmd_clean_tree(BzrToolsCommand):
54
"""Remove unwanted files from working tree.
56
By default, only unknown files, not ignored files, are deleted. Versioned
57
files are never deleted.
59
Another class is 'detritus', which includes files emitted by bzr during
60
normal operations and selftests. (The value of these files decreases with
63
If no options are specified, unknown files are deleted. Otherwise, option
64
flags are respected, and may be combined.
66
To check what clean-tree will do, use --dry-run.
68
takes_options = [Option('ignored', help='Delete all ignored files.'),
69
Option('detritus', help='Delete conflict files, merge'
70
' backups, and failed selftest dirs.'),
72
help='Delete files unknown to bzr (default).'),
73
Option('dry-run', help='Show files to delete instead of'
75
def run(self, unknown=False, ignored=False, detritus=False, dry_run=False):
76
from clean_tree import clean_tree
77
if not (unknown or ignored or detritus):
79
clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus,
83
class cmd_graph_ancestry(BzrToolsCommand):
84
"""Produce ancestry graphs using dot.
86
Output format is detected according to file extension. Some of the more
87
common output formats are html, png, gif, svg, ps. An extension of '.dot'
88
will cause a dot graph file to be produced. HTML output has mouseovers
89
that show the commit message.
91
Branches are labeled r?, where ? is the revno. If they have no revno,
92
with the last 5 characters of their revision identifier are used instead.
94
The value starting with d is "(maximum) distance from the null revision".
96
If --merge-branch is specified, the two branches are compared and a merge
100
white normal revision
103
orange COMMON history
104
blue COMMON non-history ancestor
105
green Merge base (COMMON ancestor farthest from the null revision)
106
dotted Ghost revision (missing from branch storage)
108
Ancestry is usually collapsed by skipping revisions with a single parent
109
and descendant. The number of skipped revisions is shown on the arrow.
110
This feature can be disabled with --no-collapse.
112
By default, revisions are ordered by distance from root, but they can be
113
clustered instead using --cluster.
115
If available, rsvg is used to antialias PNG and JPEG output, but this can
116
be disabled with --no-antialias.
118
takes_args = ['file', 'merge_branch?']
119
takes_options = [Option('no-collapse', help="Do not skip simple nodes."),
120
Option('no-antialias',
121
help="Do not use rsvg to produce antialiased output."),
122
Option('merge-branch', type=str,
123
help="Use this branch to calcuate a merge base."),
124
Option('cluster', help="Use clustered output."),
125
Option('max-distance',
126
help="Show no nodes farther than this.", type=int),
128
help='Source branch to use (default is current'
133
def run(self, file, merge_branch=None, no_collapse=False,
134
no_antialias=False, cluster=False, max_distance=100,
136
if max_distance == -1:
143
graph.write_ancestry_file(directory, file, not no_collapse,
144
not no_antialias, merge_branch, ranking,
145
max_distance=max_distance)
148
class cmd_fetch_ghosts(BzrToolsCommand):
149
"""Attempt to retrieve ghosts from another branch.
150
If the other branch is not supplied, the last-pulled branch is used.
152
aliases = ['fetch-missing']
153
takes_args = ['branch?']
154
takes_options = [Option('no-fix', help="Skip additional synchonization.")]
155
def run(self, branch=None, no_fix=False):
156
from fetch_ghosts import fetch_ghosts
157
fetch_ghosts(branch, no_fix)
159
strip_help="""Strip the smallest prefix containing num leading slashes from \
160
each file name found in the patch file."""
163
class cmd_patch(BzrToolsCommand):
164
"""Apply a named patch to the current tree.
166
takes_args = ['filename?']
167
takes_options = [Option('strip', type=int, help=strip_help),
168
Option('silent', help='Suppress chatter.')]
169
def run(self, filename=None, strip=None, silent=False):
170
from patch import patch
171
from bzrlib.workingtree import WorkingTree
172
wt = WorkingTree.open_containing('.')[0]
175
return patch(wt, filename, strip, silent)
178
class cmd_shelve(BzrToolsCommand):
179
"""Temporarily set aside some changes from the current tree.
181
Shelve allows you to temporarily put changes you've made "on the shelf",
182
ie. out of the way, until a later time when you can bring them back from
183
the shelf with the 'unshelve' command.
185
Shelve is intended to help separate several sets of text changes that have
186
been inappropriately mingled. If you just want to get rid of all changes
187
(text and otherwise) and you don't need to restore them later, use revert.
188
If you want to shelve all text changes at once, use shelve --all.
190
By default shelve asks you what you want to shelve, press '?' at the
191
prompt to get help. To shelve everything run shelve --all.
193
If filenames are specified, only the changes to those files will be
194
shelved, other files will be left untouched.
196
If a revision is specified, changes since that revision will be shelved.
198
You can put multiple items on the shelf. Normally each time you run
199
unshelve the most recently shelved changes will be reinstated. However,
200
you can also unshelve changes in a different order by explicitly
201
specifiying which changes to unshelve. This works best when the changes
202
don't depend on each other.
204
While you have patches on the shelf you can view and manipulate them with
205
the 'shelf' command. Run 'bzr shelf -h' for more info.
208
takes_args = ['file*']
209
takes_options = [Option('message',
210
help='A message to associate with the shelved changes.',
211
short_name='m', type=unicode),
213
Option('all', help='Shelve all changes without prompting.'),
214
Option('no-color', help='Never display changes in color.')]
216
def run(self, all=False, file_list=None, message=None, revision=None,
218
if revision is not None and revision:
219
if len(revision) == 1:
220
revision = revision[0]
222
raise CommandError("shelve only accepts a single revision "
225
source = BzrPatchSource(revision, file_list)
226
s = shelf.Shelf(source.base)
227
s.shelve(source, all, message, no_color)
231
# The following classes are only used as subcommands for 'shelf', they're
232
# not to be registered directly with bzr.
234
class cmd_shelf_list(bzrlib.commands.Command):
235
"""List the patches on the current shelf."""
236
aliases = ['list', 'ls']
241
class cmd_shelf_delete(bzrlib.commands.Command):
242
"""Delete the patch from the current shelf."""
243
aliases = ['delete', 'del']
244
takes_args = ['patch']
245
def run(self, patch):
246
self.shelf.delete(patch)
249
class cmd_shelf_switch(bzrlib.commands.Command):
250
"""Switch to the other shelf, create it if necessary."""
252
takes_args = ['othershelf']
253
def run(self, othershelf):
254
s = shelf.Shelf(self.shelf.base, othershelf)
258
class cmd_shelf_show(bzrlib.commands.Command):
259
"""Show the contents of the specified or topmost patch."""
260
aliases = ['show', 'cat', 'display']
261
takes_args = ['patch?']
262
def run(self, patch=None):
263
self.shelf.display(patch)
266
class cmd_shelf_upgrade(bzrlib.commands.Command):
267
"""Upgrade old format shelves."""
268
aliases = ['upgrade']
273
class cmd_shelf(BzrToolsCommand):
274
"""Perform various operations on your shelved patches. See also shelve."""
275
takes_args = ['subcommand', 'args*']
277
subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
278
cmd_shelf_show, cmd_shelf_upgrade]
280
def run(self, subcommand, args_list):
283
if args_list is None:
285
cmd = self._get_cmd_object(subcommand)
286
source = BzrPatchSource()
287
s = shelf.Shelf(source.base)
290
if args_list is None:
292
return cmd.run_argv_aliases(args_list)
294
def _get_cmd_object(self, cmd_name):
295
for cmd_class in self.subcommands:
296
for alias in cmd_class.aliases:
297
if alias == cmd_name:
299
raise CommandError("Unknown shelf subcommand '%s'" % cmd_name)
302
text = ["%s\n\nSubcommands:\n" % self.__doc__]
304
for cmd_class in self.subcommands:
305
text.extend(self.sub_help(cmd_class) + ['\n'])
309
def sub_help(self, cmd_class):
311
cmd_obj = cmd_class()
314
usage = cmd_obj._usage()
315
usage = usage.replace('bzr shelf-', '')
316
text.append('%s%s\n' % (indent, usage))
318
text.append('%s%s\n' % (2 * indent, cmd_class.__doc__))
320
# Somewhat copied from bzrlib.help.help_on_command_options
322
for option_name, option in sorted(cmd_obj.options().items()):
323
if option_name == 'help':
325
option_help.append('%s--%s' % (3 * indent, option_name))
326
if option.type is not None:
327
option_help.append(' %s' % option.argname.upper())
328
if option.short_name():
329
option_help.append(', -%s' % option.short_name())
330
option_help.append('%s%s\n' % (2 * indent, option.help))
332
if len(option_help) > 0:
333
text.append('%soptions:\n' % (2 * indent))
334
text.extend(option_help)
339
class cmd_unshelve(BzrToolsCommand):
340
"""Restore shelved changes.
342
By default the most recently shelved changes are restored. However if you
343
specify a patch by name those changes will be restored instead.
345
See 'shelve' for more information.
348
Option('all', help='Unshelve all changes without prompting.'),
349
Option('force', help='Force unshelving even if errors occur.'),
350
Option('no-color', help='Never display changes in color.')
352
takes_args = ['patch?']
353
def run(self, patch=None, all=False, force=False, no_color=False):
354
source = BzrPatchSource()
355
s = shelf.Shelf(source.base)
356
s.unshelve(source, patch, all, force, no_color)
360
class cmd_shell(BzrToolsCommand):
361
"""Begin an interactive shell tailored for bzr.
362
Bzr commands can be used without typing bzr first, and will be run natively
363
when possible. Tab completion is tailored for bzr. The shell prompt shows
364
the branch nick, revno, and path.
366
If it encounters any moderately complicated shell command, it will punt to
371
bzr bzrtools:287/> status
374
bzr bzrtools:287/> status --[TAB][TAB]
375
--all --help --revision --show-ids
376
bzr bzrtools:287/> status --
380
return shell.run_shell()
383
class cmd_branch_history(BzrToolsCommand):
385
Display the development history of a branch.
387
Each different committer or branch nick is considered a different line of
388
development. Committers are treated as the same if they have the same
389
name, or if they have the same email address.
391
takes_args = ["branch?"]
392
def run(self, branch=None):
393
from branchhistory import branch_history
394
return branch_history(branch)
397
class cmd_zap(BzrToolsCommand):
399
Remove a lightweight checkout, if it can be done safely.
401
This command will remove a lightweight checkout without losing data. That
402
means it only removes lightweight checkouts, and only if they have no
405
If --branch is specified, the branch will be deleted too, but only if the
406
the branch has no new commits (relative to its parent).
408
takes_options = [Option("branch", help="Remove associated branch from"
410
Option('force', help='Delete tree even if contents are'
412
takes_args = ["checkout"]
413
def run(self, checkout, branch=False, force=False):
415
return zap(checkout, remove_branch=branch, allow_modified=force)
418
class cmd_cbranch(BzrToolsCommand):
420
Create a new checkout, associated with a new repository branch.
422
When you cbranch, bzr looks up a target location in locations.conf, and
423
creates the branch there.
425
In your locations.conf, add the following lines:
426
[/working_directory_root]
427
cbranch_target = /branch_root
428
cbranch_target:policy = appendpath
430
This will mean that if you run "bzr cbranch foo/bar foo/baz" in the
431
working directory root, the branch will be created in
432
"/branch_root/foo/baz"
434
NOTE: cbranch also supports "cbranch_root", but that behaviour is
437
takes_options = [Option("lightweight",
438
help="Create a lightweight checkout."), 'revision',
439
Option('files-from', type=unicode,
440
help='Accelerate checkout using files from this'
443
help='Hard-link files from source/files-from tree'
445
takes_args = ["source", "target?"]
446
def run(self, source, target=None, lightweight=False, revision=None,
447
files_from=None, hardlink=False):
448
from cbranch import cbranch
449
return cbranch(source, target, lightweight=lightweight,
450
revision=revision, files_from=files_from,
454
class cmd_branches(BzrToolsCommand):
455
"""Scan a location for branches"""
456
takes_args = ["location?"]
457
def run(self, location=None):
458
from branches import branches
459
return branches(location)
461
class cmd_trees(BzrToolsCommand):
462
"""Scan a location for trees"""
463
takes_args = ['location?']
464
def run(self, location='.'):
465
from bzrlib.workingtree import WorkingTree
466
from bzrlib.transport import get_transport
467
t = get_transport(location)
468
for tree in WorkingTree.find_trees(location):
469
self.outf.write('%s\n' % t.relpath(
470
tree.bzrdir.root_transport.base))
472
class cmd_multi_pull(BzrToolsCommand):
473
"""Pull all the branches under a location, e.g. a repository.
475
Both branches present in the directory and the branches of checkouts are
478
takes_args = ["location?"]
479
def run(self, location=None):
480
from bzrlib.transport import get_transport
481
from bzrtools import iter_branch_tree
484
t = get_transport(location)
485
possible_transports = []
487
print "Can't list this type of location."
489
for branch, wt in iter_branch_tree(t):
494
parent = branch.get_parent()
501
if base.startswith(t.base):
502
relpath = base[len(t.base):].rstrip('/')
505
print "Pulling %s from %s" % (relpath, parent)
507
branch_t = get_transport(parent, possible_transports)
508
pullable.pull(Branch.open_from_transport(branch_t))
514
class cmd_import(BzrToolsCommand):
515
"""Import sources from a directory, tarball or zip file
517
This command will import a directory, tarball or zip file into a bzr
518
tree, replacing any versioned files already present. If a directory is
519
specified, it is used as the target. If the directory does not exist, or
520
is not versioned, it is created.
522
Tarballs may be gzip or bzip2 compressed. This is autodetected.
524
If the tarball or zip has a single root directory, that directory is
525
stripped when extracting the tarball. This is not done for directories.
528
takes_args = ['source', 'tree?']
529
def run(self, source, tree=None):
530
from upstream_import import do_import
531
do_import(source, tree)
534
class cmd_cdiff(BzrToolsCommand):
535
"""A color version of bzr's diff"""
536
takes_args = property(lambda x: get_cmd_object('diff').takes_args)
537
takes_options = list(get_cmd_object('diff').takes_options) + [
538
Option('check-style',
539
help='Warn if trailing whitespace or spurious changes have been'
542
def run(self, check_style=False, *args, **kwargs):
543
from colordiff import colordiff
544
colordiff(check_style, *args, **kwargs)
547
class cmd_rspush(BzrToolsCommand):
548
"""Upload this branch to another location using rsync.
550
If no location is specified, the last-used location will be used. To
551
prevent dirty trees from being uploaded, rspush will error out if there are
552
unknown files or local changes. It will also error out if the upstream
553
directory is non-empty and not an earlier version of the branch.
555
takes_args = ['location?']
556
takes_options = [Option('overwrite', help='Ignore differences between'
557
' branches and overwrite unconditionally.'),
558
Option('no-tree', help='Do not push the working tree,'
561
def run(self, location=None, overwrite=False, no_tree=False):
562
from bzrlib import workingtree
564
cur_branch = workingtree.WorkingTree.open_containing(".")[0]
565
bzrtools.rspush(cur_branch, location, overwrite=overwrite,
566
working_tree=not no_tree)
569
class cmd_link_tree(BzrToolsCommand):
570
"""Hardlink matching files to another tree.
572
Only files with identical content and execute bit will be linked.
574
takes_args = ['location']
576
def run(self, location):
577
from bzrlib import workingtree
578
from bzrlib.plugins.bzrtools.link_tree import link_tree
579
target_tree = workingtree.WorkingTree.open_containing(".")[0]
580
source_tree = workingtree.WorkingTree.open(location)
581
target_tree.lock_write()
583
source_tree.lock_read()
585
link_tree(target_tree, source_tree)
591
from heads import cmd_heads
615
if hasattr(bzrlib.commands, 'register_command'):
616
for command in commands:
617
bzrlib.commands.register_command(command)
621
from bzrlib.tests.TestUtil import TestLoader
623
from doctest import DocTestSuite, ELLIPSIS
624
from unittest import TestSuite
626
import tests.clean_tree
627
import tests.test_dotgraph
628
import tests.is_clean
629
import tests.test_cbranch
630
import tests.test_link_tree
631
import tests.test_patch
632
import tests.test_rspush
633
import tests.upstream_import
635
import tests.blackbox
636
import tests.shelf_tests
638
result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
639
result.addTest(tests.clean_tree.test_suite())
640
result.addTest(tests.test_suite())
641
result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
642
result.addTest(tests.blackbox.test_suite())
643
result.addTest(tests.upstream_import.test_suite())
644
result.addTest(zap.test_suite())
645
result.addTest(TestLoader().loadTestsFromModule(tests.test_dotgraph))
646
result.addTest(TestLoader().loadTestsFromModule(tests.is_clean))
647
result.addTest(TestLoader().loadTestsFromModule(tests.test_link_tree))
648
result.addTest(TestLoader().loadTestsFromModule(tests.test_patch))
649
result.addTest(TestLoader().loadTestsFromModule(tests.test_rspush))
650
result.addTest(TestLoader().loadTestsFromModule(tests.test_cbranch))