bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 1
by mbp at sourcefrog import from baz patch-364 | 1 | # Copyright (C) 2004, 2005 by Martin Pool
 | 
| 2 | # Copyright (C) 2005 by Canonical Ltd
 | |
| 3 | ||
| 4 | ||
| 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.
 | |
| 9 | ||
| 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.
 | |
| 14 | ||
| 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
 | |
| 18 | ||
| 19 | """Bazaar-NG -- a free distributed version-control tool
 | |
| 20 | ||
| 21 | **WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
 | |
| 22 | ||
| 23 | Current limitation include:
 | |
| 24 | ||
| 25 | * Metadata format is not stable yet -- you may need to
 | |
| 26 |   discard history in the future.
 | |
| 27 | ||
| 28 | * Insufficient error handling.
 | |
| 29 | ||
| 30 | * Many commands unimplemented or partially implemented.
 | |
| 31 | ||
| 32 | * Space-inefficient storage.
 | |
| 33 | ||
| 34 | * No merge operators yet.
 | |
| 35 | ||
| 36 | Interesting commands::
 | |
| 37 | ||
| 85
by mbp at sourcefrog improved help string | 38 |   bzr help [COMMAND]
 | 
| 39 |        Show help screen
 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 40 |   bzr version
 | 
| 41 |        Show software version/licence/non-warranty.
 | |
| 42 |   bzr init
 | |
| 43 |        Start versioning the current directory
 | |
| 44 |   bzr add FILE...
 | |
| 45 |        Make files versioned.
 | |
| 46 |   bzr log
 | |
| 47 |        Show revision history.
 | |
| 48 |   bzr diff
 | |
| 49 |        Show changes from last revision to working copy.
 | |
| 50 |   bzr commit -m 'MESSAGE'
 | |
| 51 |        Store current state as new revision.
 | |
| 52 |   bzr export REVNO DESTINATION
 | |
| 53 |        Export the branch state at a previous version.
 | |
| 54 |   bzr status
 | |
| 55 |        Show summary of pending changes.
 | |
| 56 |   bzr remove FILE...
 | |
| 57 |        Make a file not versioned.
 | |
| 76
by mbp at sourcefrog mention "info" in top-level help | 58 |   bzr info
 | 
| 59 |        Show statistics about this branch.
 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 60 | """
 | 
| 61 | ||
| 62 | ||
| 63 | ||
| 64 | ||
| 65 | import sys, os, random, time, sha, sets, types, re, shutil, tempfile | |
| 66 | import traceback, socket, fnmatch, difflib | |
| 67 | from os import path | |
| 68 | from sets import Set | |
| 69 | from pprint import pprint | |
| 70 | from stat import * | |
| 71 | from glob import glob | |
| 72 | ||
| 73 | import bzrlib | |
| 74 | from bzrlib.store import ImmutableStore | |
| 75 | from bzrlib.trace import mutter, note, log_error | |
| 76 | from bzrlib.errors import bailout, BzrError | |
| 77 | from bzrlib.osutils import quotefn, pumpfile, isdir, isfile | |
| 78 | from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree | |
| 79 | from bzrlib.revision import Revision | |
| 80 | from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \ | |
| 81 |      format_date
 | |
| 82 | ||
| 83 | BZR_DIFF_FORMAT = "## Bazaar-NG diff, format 0 ##\n" | |
| 84 | BZR_PATCHNAME_FORMAT = 'cset:sha1:%s' | |
| 85 | ||
| 86 | ## standard representation
 | |
| 87 | NONE_STRING = '(none)' | |
| 88 | EMPTY = 'empty' | |
| 89 | ||
| 90 | ||
| 91 | ## TODO: Perhaps a different version of inventory commands that
 | |
| 92 | ## returns iterators...
 | |
| 93 | ||
| 94 | ## TODO: Perhaps an AtomicFile class that writes to a temporary file and then renames.
 | |
| 95 | ||
| 96 | ## TODO: Some kind of locking on branches.  Perhaps there should be a
 | |
| 97 | ## parameter to the branch object saying whether we want a read or
 | |
| 98 | ## write lock; release it from destructor.  Perhaps don't even need a
 | |
| 99 | ## read lock to look at immutable objects?
 | |
| 100 | ||
| 101 | ## TODO: Perhaps make UUIDs predictable in test mode to make it easier
 | |
| 102 | ## to compare output?
 | |
| 103 | ||
| 34
by mbp at sourcefrog doc | 104 | ## TODO: Some kind of global code to generate the right Branch object
 | 
| 105 | ## to work on.  Almost, but not quite all, commands need one, and it
 | |
| 106 | ## can be taken either from their parameters or their working
 | |
| 107 | ## directory.
 | |
| 108 | ||
| 46
by Martin Pool todo | 109 | ## TODO: rename command, needed soon: check destination doesn't exist
 | 
| 110 | ## either in working copy or tree; move working copy; update
 | |
| 111 | ## inventory; write out
 | |
| 112 | ||
| 113 | ## TODO: move command; check destination is a directory and will not
 | |
| 114 | ## clash; move it.
 | |
| 115 | ||
| 116 | ## TODO: command to show renames, one per line, as to->from
 | |
| 117 | ||
| 118 | ||
| 1
by mbp at sourcefrog import from baz patch-364 | 119 | |
| 120 | ||
| 121 | def cmd_status(all=False): | |
| 122 | """Display status summary. | |
| 123 | ||
| 124 |     For each file there is a single line giving its file state and name.
 | |
| 125 |     The name is that in the current revision unless it is deleted or
 | |
| 126 |     missing, in which case the old name is shown.
 | |
| 127 | ||
| 128 |     :todo: Don't show unchanged files unless ``--all`` is given?
 | |
| 129 |     """
 | |
| 130 | Branch('.').show_status(show_all=all) | |
| 131 | ||
| 132 | ||
| 133 | ||
| 134 | ######################################################################
 | |
| 135 | # examining history
 | |
| 136 | def cmd_get_revision(revision_id): | |
| 137 | Branch('.').get_revision(revision_id).write_xml(sys.stdout) | |
| 138 | ||
| 139 | ||
| 140 | def cmd_get_file_text(text_id): | |
| 141 | """Get contents of a file by hash.""" | |
| 142 | sf = Branch('.').text_store[text_id] | |
| 143 | pumpfile(sf, sys.stdout) | |
| 144 | ||
| 145 | ||
| 146 | ||
| 147 | ######################################################################
 | |
| 148 | # commands
 | |
| 149 | ||
| 150 | ||
| 151 | def cmd_revno(): | |
| 152 | """Show number of revisions on this branch""" | |
| 153 | print Branch('.').revno() | |
| 154 | ||
| 155 | ||
| 70
by mbp at sourcefrog Prepare for smart recursive add. | 156 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 157 | def cmd_add(file_list, verbose=False): | 
| 70
by mbp at sourcefrog Prepare for smart recursive add. | 158 | """Add specified files or directories. | 
| 159 | ||
| 160 |     In non-recursive mode, all the named items are added, regardless
 | |
| 161 |     of whether they were previously ignored.  A warning is given if
 | |
| 162 |     any of the named files are already versioned.
 | |
| 163 | ||
| 164 |     In recursive mode (the default), files are treated the same way
 | |
| 165 |     but the behaviour for directories is different.  Directories that
 | |
| 166 |     are already versioned do not give a warning.  All directories,
 | |
| 167 |     whether already versioned or not, are searched for files or
 | |
| 168 |     subdirectories that are neither versioned or ignored, and these
 | |
| 169 |     are added.  This search proceeds recursively into versioned
 | |
| 170 |     directories.
 | |
| 171 | ||
| 172 |     Therefore simply saying 'bzr add .' will version all files that
 | |
| 173 |     are currently unknown.
 | |
| 174 |     """
 | |
| 87
by mbp at sourcefrog - clean up smart_add code, and make it commit the inventory | 175 | bzrlib.add.smart_add(file_list, verbose) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 176 | |
| 177 | ||
| 68
by mbp at sourcefrog - new relpath command and function | 178 | def cmd_relpath(filename): | 
| 87
by mbp at sourcefrog - clean up smart_add code, and make it commit the inventory | 179 | """Show path of file relative to root""" | 
| 68
by mbp at sourcefrog - new relpath command and function | 180 | print Branch(filename).relpath(filename) | 
| 181 | ||
| 182 | ||
| 1
by mbp at sourcefrog import from baz patch-364 | 183 | def cmd_inventory(revision=None): | 
| 184 | """Show inventory of the current working copy.""" | |
| 185 |     ## TODO: Also optionally show a previous inventory
 | |
| 186 |     ## TODO: Format options
 | |
| 187 | b = Branch('.') | |
| 188 | if revision == None: | |
| 189 | inv = b.read_working_inventory() | |
| 190 | else: | |
| 191 | inv = b.get_revision_inventory(b.lookup_revision(revision)) | |
| 192 | ||
| 193 | for path, entry in inv.iter_entries(): | |
| 194 | print '%-50s %s' % (entry.file_id, path) | |
| 195 | ||
| 196 | ||
| 197 | ||
| 198 | def cmd_info(): | |
| 77
by mbp at sourcefrog - split info command out into separate file | 199 | import info | 
| 200 | info.show_info(Branch('.')) | |
| 21
by mbp at sourcefrog - bzr info: show summary information on branch history | 201 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 202 | |
| 203 | ||
| 204 | def cmd_remove(file_list, verbose=False): | |
| 69
by Martin Pool handle add, remove, file-id being given filenames that are | 205 | b = Branch(file_list[0]) | 
| 206 | b.remove([b.relpath(f) for f in file_list], verbose=verbose) | |
| 1
by mbp at sourcefrog import from baz patch-364 | 207 | |
| 208 | ||
| 209 | ||
| 210 | def cmd_file_id(filename): | |
| 69
by Martin Pool handle add, remove, file-id being given filenames that are | 211 | b = Branch(filename) | 
| 212 | i = b.inventory.path2id(b.relpath(filename)) | |
| 1
by mbp at sourcefrog import from baz patch-364 | 213 | if i is None: | 
| 214 | bailout("%s is not a versioned file" % filename) | |
| 215 | else: | |
| 216 | print i | |
| 217 | ||
| 218 | ||
| 219 | def cmd_find_filename(fileid): | |
| 220 | n = find_filename(fileid) | |
| 221 | if n is None: | |
| 222 | bailout("%s is not a live file id" % fileid) | |
| 223 | else: | |
| 224 | print n | |
| 225 | ||
| 226 | ||
| 227 | def cmd_revision_history(): | |
| 228 | for patchid in Branch('.').revision_history(): | |
| 229 | print patchid | |
| 230 | ||
| 231 | ||
| 232 | ||
| 233 | def cmd_init(): | |
| 234 |     # TODO: Check we're not already in a working directory?  At the
 | |
| 235 |     # moment you'll get an ugly error.
 | |
| 236 | ||
| 237 |     # TODO: What if we're in a subdirectory of a branch?  Would like
 | |
| 238 |     # to allow that, but then the parent may need to understand that
 | |
| 239 |     # the children have disappeared, or should they be versioned in
 | |
| 240 |     # both?
 | |
| 241 | ||
| 242 |     # TODO: Take an argument/option for branch name.
 | |
| 243 | Branch('.', init=True) | |
| 244 | ||
| 245 | ||
| 246 | def cmd_diff(revision=None): | |
| 247 | """Show diff from basis to working copy. | |
| 248 | ||
| 249 |     :todo: Take one or two revision arguments, look up those trees,
 | |
| 250 |            and diff them.
 | |
| 251 | ||
| 252 |     :todo: Allow diff across branches.
 | |
| 253 | ||
| 254 |     :todo: Mangle filenames in diff to be more relevant.
 | |
| 255 | ||
| 256 |     :todo: Shouldn't be in the cmd function.
 | |
| 82
by mbp at sourcefrog todo | 257 | |
| 258 |     TODO: Option to use external diff command; could be GNU diff,
 | |
| 259 |     wdiff, or a graphical diff.
 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 260 |     """
 | 
| 261 | ||
| 262 | b = Branch('.') | |
| 263 | ||
| 264 | if revision == None: | |
| 265 | old_tree = b.basis_tree() | |
| 266 | else: | |
| 267 | old_tree = b.revision_tree(b.lookup_revision(revision)) | |
| 268 | ||
| 269 | new_tree = b.working_tree() | |
| 270 | old_inv = old_tree.inventory | |
| 271 | new_inv = new_tree.inventory | |
| 272 | ||
| 273 |     # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
 | |
| 274 | old_label = '' | |
| 275 | new_label = '' | |
| 276 | ||
| 277 | DEVNULL = '/dev/null' | |
| 278 |     # Windows users, don't panic about this filename -- it is a
 | |
| 279 |     # special signal to GNU patch that the file should be created or
 | |
| 280 |     # deleted respectively.
 | |
| 281 | ||
| 282 |     # TODO: Generation of pseudo-diffs for added/deleted files could
 | |
| 283 |     # be usefully made into a much faster special case.
 | |
| 284 | ||
| 285 |     # TODO: Better to return them in sorted order I think.
 | |
| 286 | ||
| 287 | for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree): | |
| 288 | d = None | |
| 289 | ||
| 290 |         # Don't show this by default; maybe do it if an option is passed
 | |
| 291 |         # idlabel = '      {%s}' % fid
 | |
| 292 | idlabel = '' | |
| 293 | ||
| 294 |         # FIXME: Something about the diff format makes patch unhappy
 | |
| 295 |         # with newly-added files.
 | |
| 296 | ||
| 297 | def diffit(*a, **kw): | |
| 298 | sys.stdout.writelines(difflib.unified_diff(*a, **kw)) | |
| 299 |             print
 | |
| 300 | ||
| 301 | if file_state in ['.', '?', 'I']: | |
| 302 |             continue
 | |
| 303 | elif file_state == 'A': | |
| 304 | print '*** added %s %r' % (kind, new_name) | |
| 305 | if kind == 'file': | |
| 306 | diffit([], | |
| 307 | new_tree.get_file(fid).readlines(), | |
| 308 | fromfile=DEVNULL, | |
| 309 | tofile=new_label + new_name + idlabel) | |
| 310 | elif file_state == 'D': | |
| 311 | assert isinstance(old_name, types.StringTypes) | |
| 312 | print '*** deleted %s %r' % (kind, old_name) | |
| 313 | if kind == 'file': | |
| 314 | diffit(old_tree.get_file(fid).readlines(), [], | |
| 315 | fromfile=old_label + old_name + idlabel, | |
| 316 | tofile=DEVNULL) | |
| 317 | elif file_state in ['M', 'R']: | |
| 318 | if file_state == 'M': | |
| 319 | assert kind == 'file' | |
| 320 | assert old_name == new_name | |
| 321 | print '*** modified %s %r' % (kind, new_name) | |
| 322 | elif file_state == 'R': | |
| 323 | print '*** renamed %s %r => %r' % (kind, old_name, new_name) | |
| 324 | ||
| 325 | if kind == 'file': | |
| 326 | diffit(old_tree.get_file(fid).readlines(), | |
| 327 | new_tree.get_file(fid).readlines(), | |
| 328 | fromfile=old_label + old_name + idlabel, | |
| 329 | tofile=new_label + new_name) | |
| 330 | else: | |
| 331 | bailout("can't represent state %s {%s}" % (file_state, fid)) | |
| 332 | ||
| 333 | ||
| 334 | ||
| 65
by mbp at sourcefrog rename 'find-branch-root' command to just 'root' | 335 | def cmd_root(filename=None): | 
| 336 | """Print the branch root.""" | |
| 62
by mbp at sourcefrog - new find_branch_root function; based on suggestion from aaron | 337 | print bzrlib.branch.find_branch_root(filename) | 
| 338 | ||
| 339 | ||
| 13
by mbp at sourcefrog fix up cmd_log args | 340 | def cmd_log(timezone='original'): | 
| 1
by mbp at sourcefrog import from baz patch-364 | 341 | """Show log of this branch. | 
| 342 | ||
| 343 |     :todo: Options for utc; to show ids; to limit range; etc.
 | |
| 344 |     """
 | |
| 12
by mbp at sourcefrog new --timezone option for bzr log | 345 | Branch('.').write_log(show_timezone=timezone) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 346 | |
| 347 | ||
| 348 | def cmd_ls(revision=None, verbose=False): | |
| 349 | """List files in a tree. | |
| 350 | ||
| 351 |     :todo: Take a revision or remote path and list that tree instead.
 | |
| 352 |     """
 | |
| 353 | b = Branch('.') | |
| 354 | if revision == None: | |
| 355 | tree = b.working_tree() | |
| 356 | else: | |
| 357 | tree = b.revision_tree(b.lookup_revision(revision)) | |
| 358 | ||
| 359 | for fp, fc, kind, fid in tree.list_files(): | |
| 360 | if verbose: | |
| 361 | if kind == 'directory': | |
| 362 | kindch = '/' | |
| 363 | elif kind == 'file': | |
| 364 | kindch = '' | |
| 365 | else: | |
| 366 | kindch = '???' | |
| 367 | ||
| 368 | print '%-8s %s%s' % (fc, fp, kindch) | |
| 369 | else: | |
| 370 | print fp | |
| 371 | ||
| 372 | ||
| 373 | ||
| 374 | def cmd_unknowns(): | |
| 375 | """List unknown files""" | |
| 376 | for f in Branch('.').unknowns(): | |
| 377 | print quotefn(f) | |
| 378 | ||
| 379 | ||
| 380 | def cmd_lookup_revision(revno): | |
| 381 | try: | |
| 382 | revno = int(revno) | |
| 383 | except ValueError: | |
| 384 | bailout("usage: lookup-revision REVNO", | |
| 385 | ["REVNO is a non-negative revision number for this branch"]) | |
| 386 | ||
| 387 | print Branch('.').lookup_revision(revno) or NONE_STRING | |
| 388 | ||
| 389 | ||
| 390 | ||
| 391 | def cmd_export(revno, dest): | |
| 392 | """Export past revision to destination directory.""" | |
| 393 | b = Branch('.') | |
| 394 | rh = b.lookup_revision(int(revno)) | |
| 395 | t = b.revision_tree(rh) | |
| 396 | t.export(dest) | |
| 397 | ||
| 398 | ||
| 399 | ||
| 400 | ######################################################################
 | |
| 401 | # internal/test commands
 | |
| 402 | ||
| 403 | ||
| 404 | def cmd_uuid(): | |
| 405 | """Print a newly-generated UUID.""" | |
| 63
by mbp at sourcefrog fix up uuid command | 406 | print bzrlib.osutils.uuid() | 
| 1
by mbp at sourcefrog import from baz patch-364 | 407 | |
| 408 | ||
| 409 | ||
| 8
by mbp at sourcefrog store committer's timezone in revision and show | 410 | def cmd_local_time_offset(): | 
| 411 | print bzrlib.osutils.local_time_offset() | |
| 412 | ||
| 413 | ||
| 414 | ||
| 57
by mbp at sourcefrog error if --message is not given for commit | 415 | def cmd_commit(message=None, verbose=False): | 
| 97
by mbp at sourcefrog - more commit help | 416 | """Commit changes to a new revision. | 
| 417 | ||
| 418 | --message MESSAGE
 | |
| 419 |     Description of changes in this revision; free form text.
 | |
| 420 |     It is recommended that the first line be a single-sentence
 | |
| 421 |     summary.
 | |
| 422 | --verbose
 | |
| 423 |     Show status of changed files,
 | |
| 424 | ||
| 425 | TODO: Commit only selected files.
 | |
| 426 | ||
| 427 | TODO: Run hooks on tree to-be-committed, and after commit.
 | |
| 428 | ||
| 429 | TODO: Strict commit that fails if there are unknown or deleted files.
 | |
| 430 | """
 | |
| 431 | ||
| 57
by mbp at sourcefrog error if --message is not given for commit | 432 | if not message: | 
| 433 | bailout("please specify a commit message") | |
| 1
by mbp at sourcefrog import from baz patch-364 | 434 | Branch('.').commit(message, verbose=verbose) | 
| 435 | ||
| 436 | ||
| 437 | def cmd_check(): | |
| 438 | """Check consistency of the branch.""" | |
| 439 | check() | |
| 440 | ||
| 441 | ||
| 442 | def cmd_is(pred, *rest): | |
| 443 | """Test whether PREDICATE is true.""" | |
| 444 | try: | |
| 445 | cmd_handler = globals()['assert_' + pred.replace('-', '_')] | |
| 446 | except KeyError: | |
| 447 | bailout("unknown predicate: %s" % quotefn(pred)) | |
| 448 | ||
| 449 | try: | |
| 450 | cmd_handler(*rest) | |
| 451 | except BzrCheckError: | |
| 452 |         # by default we don't print the message so that this can
 | |
| 453 |         # be used from shell scripts without producing noise
 | |
| 454 | sys.exit(1) | |
| 455 | ||
| 456 | ||
| 457 | def cmd_username(): | |
| 458 | print bzrlib.osutils.username() | |
| 459 | ||
| 460 | ||
| 461 | def cmd_user_email(): | |
| 462 | print bzrlib.osutils.user_email() | |
| 463 | ||
| 464 | ||
| 465 | def cmd_gen_revision_id(): | |
| 466 | import time | |
| 467 | print bzrlib.branch._gen_revision_id(time.time()) | |
| 468 | ||
| 469 | ||
| 55
by mbp at sourcefrog bzr selftest shows some counts of tests | 470 | def cmd_selftest(verbose=False): | 
| 471 | """Run internal test suite""" | |
| 1
by mbp at sourcefrog import from baz patch-364 | 472 |     ## -v, if present, is seen by doctest; the argument is just here
 | 
| 473 |     ## so our parser doesn't complain
 | |
| 474 | ||
| 475 |     ## TODO: --verbose option
 | |
| 55
by mbp at sourcefrog bzr selftest shows some counts of tests | 476 | |
| 477 | failures, tests = 0, 0 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 478 | |
| 55
by mbp at sourcefrog bzr selftest shows some counts of tests | 479 | import doctest, bzrlib.store, bzrlib.tests | 
| 1
by mbp at sourcefrog import from baz patch-364 | 480 | bzrlib.trace.verbose = False | 
| 55
by mbp at sourcefrog bzr selftest shows some counts of tests | 481 | |
| 482 | for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \ | |
| 70
by mbp at sourcefrog Prepare for smart recursive add. | 483 | bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add: | 
| 55
by mbp at sourcefrog bzr selftest shows some counts of tests | 484 | mf, mt = doctest.testmod(m) | 
| 485 | failures += mf | |
| 486 | tests += mt | |
| 487 | print '%-40s %3d tests' % (m.__name__, mt), | |
| 488 | if mf: | |
| 489 | print '%3d FAILED!' % mf | |
| 490 | else: | |
| 491 |             print
 | |
| 492 | ||
| 493 | print '%-40s %3d tests' % ('total', tests), | |
| 494 | if failures: | |
| 495 | print '%3d FAILED!' % failures | |
| 496 | else: | |
| 497 |         print
 | |
| 498 | ||
| 499 | ||
| 500 | ||
| 501 | # deprecated
 | |
| 502 | cmd_doctest = cmd_selftest | |
| 53
by mbp at sourcefrog 'selftest' command instead of 'doctest' | 503 | |
| 504 | ||
| 1
by mbp at sourcefrog import from baz patch-364 | 505 | ######################################################################
 | 
| 506 | # help
 | |
| 507 | ||
| 508 | ||
| 83
by mbp at sourcefrog Can now say "bzr help COMMAND" for more detailed help | 509 | def cmd_help(topic=None): | 
| 510 | if topic == None: | |
| 511 | print __doc__ | |
| 512 |         return
 | |
| 513 | ||
| 514 |     # otherwise, maybe the name of a command?
 | |
| 515 | try: | |
| 516 | cmdfn = globals()['cmd_' + topic.replace('-', '_')] | |
| 517 | except KeyError: | |
| 518 | bailout("no help for %r" % topic) | |
| 519 | ||
| 520 | doc = cmdfn.__doc__ | |
| 521 | if doc == None: | |
| 522 | bailout("sorry, no detailed help yet for %r" % topic) | |
| 523 | ||
| 524 | print doc | |
| 525 | ||
| 526 | ||
| 1
by mbp at sourcefrog import from baz patch-364 | 527 | |
| 528 | ||
| 529 | def cmd_version(): | |
| 84
by mbp at sourcefrog - update version string | 530 | print "bzr (bazaar-ng) %s" % bzrlib.__version__ | 
| 531 | print bzrlib.__copyright__ | |
| 1
by mbp at sourcefrog import from baz patch-364 | 532 | print "http://bazaar-ng.org/" | 
| 533 |     print
 | |
| 534 |     print \
 | |
| 535 | """bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and
 | |
| 536 | you may use, modify and redistribute it under the terms of the GNU 
 | |
| 537 | General Public License version 2 or later."""
 | |
| 538 | ||
| 539 | ||
| 540 | def cmd_rocks(): | |
| 541 | """Statement of optimism.""" | |
| 542 | print "it sure does!" | |
| 543 | ||
| 544 | ||
| 545 | ||
| 546 | ######################################################################
 | |
| 547 | # main routine
 | |
| 548 | ||
| 549 | ||
| 550 | # list of all available options; the rhs can be either None for an
 | |
| 551 | # option that takes no argument, or a constructor function that checks
 | |
| 552 | # the type.
 | |
| 553 | OPTIONS = { | |
| 554 | 'all': None, | |
| 555 | 'help': None, | |
| 556 | 'message': unicode, | |
| 557 | 'revision': int, | |
| 558 | 'show-ids': None, | |
| 12
by mbp at sourcefrog new --timezone option for bzr log | 559 | 'timezone': str, | 
| 1
by mbp at sourcefrog import from baz patch-364 | 560 | 'verbose': None, | 
| 561 | 'version': None, | |
| 562 |     }
 | |
| 563 | ||
| 564 | SHORT_OPTIONS = { | |
| 565 | 'm': 'message', | |
| 566 | 'r': 'revision', | |
| 567 | 'v': 'verbose', | |
| 568 | }
 | |
| 569 | ||
| 570 | # List of options that apply to particular commands; commands not
 | |
| 571 | # listed take none.
 | |
| 572 | cmd_options = { | |
| 573 | 'add': ['verbose'], | |
| 574 | 'commit': ['message', 'verbose'], | |
| 575 | 'diff': ['revision'], | |
| 576 | 'inventory': ['revision'], | |
| 12
by mbp at sourcefrog new --timezone option for bzr log | 577 | 'log': ['show-ids', 'timezone'], | 
| 1
by mbp at sourcefrog import from baz patch-364 | 578 | 'ls': ['revision', 'verbose'], | 
| 12
by mbp at sourcefrog new --timezone option for bzr log | 579 | 'remove': ['verbose'], | 
| 1
by mbp at sourcefrog import from baz patch-364 | 580 | 'status': ['all'], | 
| 581 |     }
 | |
| 582 | ||
| 583 | ||
| 584 | cmd_args = { | |
| 585 | 'add': ['file+'], | |
| 586 | 'commit': [], | |
| 587 | 'diff': [], | |
| 83
by mbp at sourcefrog Can now say "bzr help COMMAND" for more detailed help | 588 | 'export': ['revno', 'dest'], | 
| 1
by mbp at sourcefrog import from baz patch-364 | 589 | 'file-id': ['filename'], | 
| 590 | 'get-file-text': ['text_id'], | |
| 591 | 'get-inventory': ['inventory_id'], | |
| 592 | 'get-revision': ['revision_id'], | |
| 593 | 'get-revision-inventory': ['revision_id'], | |
| 83
by mbp at sourcefrog Can now say "bzr help COMMAND" for more detailed help | 594 | 'help': ['topic?'], | 
| 595 | 'init': [], | |
| 1
by mbp at sourcefrog import from baz patch-364 | 596 | 'log': [], | 
| 597 | 'lookup-revision': ['revno'], | |
| 83
by mbp at sourcefrog Can now say "bzr help COMMAND" for more detailed help | 598 | 'relpath': ['filename'], | 
| 1
by mbp at sourcefrog import from baz patch-364 | 599 | 'remove': ['file+'], | 
| 83
by mbp at sourcefrog Can now say "bzr help COMMAND" for more detailed help | 600 | 'root': ['filename?'], | 
| 1
by mbp at sourcefrog import from baz patch-364 | 601 | 'status': [], | 
| 602 |     }
 | |
| 603 | ||
| 604 | ||
| 605 | def parse_args(argv): | |
| 606 | """Parse command line. | |
| 607 |     
 | |
| 608 |     Arguments and options are parsed at this level before being passed
 | |
| 609 |     down to specific command handlers.  This routine knows, from a
 | |
| 610 |     lookup table, something about the available options, what optargs
 | |
| 611 |     they take, and which commands will accept them.
 | |
| 612 | ||
| 31
by Martin Pool fix up parse_args doctest | 613 |     >>> parse_args('--help'.split())
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 614 |     ([], {'help': True})
 | 
| 31
by Martin Pool fix up parse_args doctest | 615 |     >>> parse_args('--version'.split())
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 616 |     ([], {'version': True})
 | 
| 31
by Martin Pool fix up parse_args doctest | 617 |     >>> parse_args('status --all'.split())
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 618 |     (['status'], {'all': True})
 | 
| 31
by Martin Pool fix up parse_args doctest | 619 |     >>> parse_args('commit --message=biter'.split())
 | 
| 17
by mbp at sourcefrog allow --option=ARG syntax | 620 |     (['commit'], {'message': u'biter'})
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 621 |     """
 | 
| 622 | args = [] | |
| 623 | opts = {} | |
| 624 | ||
| 625 |     # TODO: Maybe handle '--' to end options?
 | |
| 626 | ||
| 26
by mbp at sourcefrog fix StopIteration error on python2.3(?) | 627 | while argv: | 
| 628 | a = argv.pop(0) | |
| 1
by mbp at sourcefrog import from baz patch-364 | 629 | if a[0] == '-': | 
| 17
by mbp at sourcefrog allow --option=ARG syntax | 630 | optarg = None | 
| 1
by mbp at sourcefrog import from baz patch-364 | 631 | if a[1] == '-': | 
| 632 | mutter(" got option %r" % a) | |
| 17
by mbp at sourcefrog allow --option=ARG syntax | 633 | if '=' in a: | 
| 634 | optname, optarg = a[2:].split('=', 1) | |
| 635 | else: | |
| 636 | optname = a[2:] | |
| 1
by mbp at sourcefrog import from baz patch-364 | 637 | if optname not in OPTIONS: | 
| 638 | bailout('unknown long option %r' % a) | |
| 639 | else: | |
| 640 | shortopt = a[1:] | |
| 641 | if shortopt not in SHORT_OPTIONS: | |
| 642 | bailout('unknown short option %r' % a) | |
| 643 | optname = SHORT_OPTIONS[shortopt] | |
| 644 | ||
| 645 | if optname in opts: | |
| 646 |                 # XXX: Do we ever want to support this, e.g. for -r?
 | |
| 647 | bailout('repeated option %r' % a) | |
| 17
by mbp at sourcefrog allow --option=ARG syntax | 648 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 649 | optargfn = OPTIONS[optname] | 
| 650 | if optargfn: | |
| 17
by mbp at sourcefrog allow --option=ARG syntax | 651 | if optarg == None: | 
| 26
by mbp at sourcefrog fix StopIteration error on python2.3(?) | 652 | if not argv: | 
| 17
by mbp at sourcefrog allow --option=ARG syntax | 653 | bailout('option %r needs an argument' % a) | 
| 654 | else: | |
| 26
by mbp at sourcefrog fix StopIteration error on python2.3(?) | 655 | optarg = argv.pop(0) | 
| 17
by mbp at sourcefrog allow --option=ARG syntax | 656 | opts[optname] = optargfn(optarg) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 657 | mutter(" option argument %r" % opts[optname]) | 
| 658 | else: | |
| 17
by mbp at sourcefrog allow --option=ARG syntax | 659 | if optarg != None: | 
| 660 | bailout('option %r takes no argument' % optname) | |
| 1
by mbp at sourcefrog import from baz patch-364 | 661 | opts[optname] = True | 
| 662 | else: | |
| 663 | args.append(a) | |
| 664 | ||
| 665 | return args, opts | |
| 666 | ||
| 667 | ||
| 668 | ||
| 669 | def _match_args(cmd, args): | |
| 670 | """Check non-option arguments match required pattern. | |
| 671 | ||
| 672 |     >>> _match_args('status', ['asdasdsadasd'])
 | |
| 673 |     Traceback (most recent call last):
 | |
| 674 |     ...
 | |
| 675 |     BzrError: ("extra arguments to command status: ['asdasdsadasd']", [])
 | |
| 676 |     >>> _match_args('add', ['asdasdsadasd'])
 | |
| 677 |     {'file_list': ['asdasdsadasd']}
 | |
| 678 |     >>> _match_args('add', 'abc def gj'.split())
 | |
| 679 |     {'file_list': ['abc', 'def', 'gj']}
 | |
| 680 |     """
 | |
| 681 |     # match argument pattern
 | |
| 682 | argform = cmd_args.get(cmd, []) | |
| 683 | argdict = {} | |
| 684 |     # TODO: Need a way to express 'cp SRC... DEST', where it matches
 | |
| 685 |     # all but one.
 | |
| 26
by mbp at sourcefrog fix StopIteration error on python2.3(?) | 686 | |
| 62
by mbp at sourcefrog - new find_branch_root function; based on suggestion from aaron | 687 |     # step through args and argform, allowing appropriate 0-many matches
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 688 | for ap in argform: | 
| 689 | argname = ap[:-1] | |
| 690 | if ap[-1] == '?': | |
| 62
by mbp at sourcefrog - new find_branch_root function; based on suggestion from aaron | 691 | if args: | 
| 692 | argdict[argname] = args.pop(0) | |
| 1
by mbp at sourcefrog import from baz patch-364 | 693 | elif ap[-1] == '*': | 
| 694 | assert 0 | |
| 695 | elif ap[-1] == '+': | |
| 696 | if not args: | |
| 697 | bailout("command %r needs one or more %s" | |
| 698 | % (cmd, argname.upper())) | |
| 699 | else: | |
| 700 | argdict[argname + '_list'] = args[:] | |
| 701 | args = [] | |
| 702 | else: | |
| 703 |             # just a plain arg
 | |
| 704 | argname = ap | |
| 705 | if not args: | |
| 706 | bailout("command %r requires argument %s" | |
| 707 | % (cmd, argname.upper())) | |
| 708 | else: | |
| 709 | argdict[argname] = args.pop(0) | |
| 710 | ||
| 711 | if args: | |
| 712 | bailout("extra arguments to command %s: %r" | |
| 713 | % (cmd, args)) | |
| 714 | ||
| 715 | return argdict | |
| 716 | ||
| 717 | ||
| 718 | ||
| 719 | def run_bzr(argv): | |
| 720 | """Execute a command. | |
| 721 | ||
| 722 |     This is similar to main(), but without all the trappings for
 | |
| 723 |     logging and error handling.
 | |
| 724 |     """
 | |
| 725 | try: | |
| 26
by mbp at sourcefrog fix StopIteration error on python2.3(?) | 726 | args, opts = parse_args(argv[1:]) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 727 | if 'help' in opts: | 
| 728 |             # TODO: pass down other arguments in case they asked for
 | |
| 729 |             # help on a command name?
 | |
| 730 | cmd_help() | |
| 731 | return 0 | |
| 732 | elif 'version' in opts: | |
| 733 | cmd_version() | |
| 734 | return 0 | |
| 735 | cmd = args.pop(0) | |
| 736 | except IndexError: | |
| 737 | log_error('usage: bzr COMMAND\n') | |
| 738 | log_error(' try "bzr help"\n') | |
| 739 | return 1 | |
| 740 | ||
| 741 | try: | |
| 742 | cmd_handler = globals()['cmd_' + cmd.replace('-', '_')] | |
| 743 | except KeyError: | |
| 744 | bailout("unknown command " + `cmd`) | |
| 745 | ||
| 746 |     # TODO: special --profile option to turn on the Python profiler
 | |
| 747 | ||
| 748 |     # check options are reasonable
 | |
| 749 | allowed = cmd_options.get(cmd, []) | |
| 750 | for oname in opts: | |
| 751 | if oname not in allowed: | |
| 752 | bailout("option %r is not allowed for command %r" | |
| 753 | % (oname, cmd)) | |
| 754 | ||
| 755 | cmdargs = _match_args(cmd, args) | |
| 756 | cmdargs.update(opts) | |
| 757 | ||
| 758 | ret = cmd_handler(**cmdargs) or 0 | |
| 759 | ||
| 760 | ||
| 761 | ||
| 762 | def main(argv): | |
| 763 |     ## TODO: Handle command-line options; probably know what options are valid for
 | |
| 764 |     ## each command
 | |
| 765 | ||
| 766 |     ## TODO: If the arguments are wrong, give a usage message rather
 | |
| 767 |     ## than just a backtrace.
 | |
| 768 | ||
| 59
by mbp at sourcefrog lift out tracefile creation code | 769 | bzrlib.trace.create_tracefile(argv) | 
| 770 | ||
| 1
by mbp at sourcefrog import from baz patch-364 | 771 | try: | 
| 772 | ret = run_bzr(argv) | |
| 773 | return ret | |
| 774 | except BzrError, e: | |
| 775 | log_error('bzr: error: ' + e.args[0] + '\n') | |
| 776 | if len(e.args) > 1: | |
| 777 | for h in e.args[1]: | |
| 778 | log_error(' ' + h + '\n') | |
| 779 | return 1 | |
| 780 | except Exception, e: | |
| 781 | log_error('bzr: exception: %s\n' % e) | |
| 782 | log_error(' see .bzr.log for details\n') | |
| 783 | traceback.print_exc(None, bzrlib.trace._tracefile) | |
| 784 | traceback.print_exc(None, sys.stderr) | |
| 785 | return 1 | |
| 786 | ||
| 787 |     # TODO: Maybe nicer handling of IOError?
 | |
| 788 | ||
| 789 | ||
| 790 | ||
| 791 | if __name__ == '__main__': | |
| 792 | sys.exit(main(sys.argv)) | |
| 793 |     ##import profile
 | |
| 794 |     ##profile.run('main(sys.argv)')
 | |
| 795 |