diff --git a/bin/dulwich b/bin/dulwich index 7b8bb6ad..7ceec883 100755 --- a/bin/dulwich +++ b/bin/dulwich @@ -1,466 +1,534 @@ #!/usr/bin/python -u # # dulwich - Simple command-line interface to Dulwich # Copyright (C) 2008-2011 Jelmer Vernooij # vim: expandtab # # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU # General Public License as public by the Free Software Foundation; version 2.0 # or (at your option) any later version. You can redistribute it and/or # modify it under the terms of either of these two licenses. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # You should have received a copy of the licenses; if not, see # for a copy of the GNU General Public License # and for a copy of the Apache # License, Version 2.0. # """Simple command-line interface to Dulwich> This is a very simple command-line wrapper for Dulwich. It is by no means intended to be a full-blown Git command-line interface but just a way to test Dulwich. """ import os import sys from getopt import getopt import optparse import signal def signal_int(signal, frame): sys.exit(1) signal.signal(signal.SIGINT, signal_int) from dulwich import porcelain from dulwich.client import get_transport_and_path from dulwich.errors import ApplyDeltaError from dulwich.index import Index from dulwich.pack import Pack, sha_to_hex from dulwich.patch import write_tree_diff from dulwich.repo import Repo -def cmd_archive(args): - opts, args = getopt(args, "", []) - client, path = get_transport_and_path(args.pop(0)) - location = args.pop(0) - committish = args.pop(0) - porcelain.archive(location, committish, outstream=sys.stdout, - errstream=sys.stderr) - - -def cmd_add(args): - opts, args = getopt(args, "", []) - - porcelain.add(".", paths=args) - - -def cmd_rm(args): - opts, args = getopt(args, "", []) - - porcelain.rm(".", paths=args) - - -def cmd_fetch_pack(args): - opts, args = getopt(args, "", ["all"]) - opts = dict(opts) - client, path = get_transport_and_path(args.pop(0)) - r = Repo(".") - if "--all" in opts: - determine_wants = r.object_store.determine_wants_all - else: - determine_wants = lambda x: [y for y in args if not y in r.object_store] - client.fetch(path, r, determine_wants) - - -def cmd_fetch(args): - opts, args = getopt(args, "", []) - opts = dict(opts) - client, path = get_transport_and_path(args.pop(0)) - r = Repo(".") - if "--all" in opts: - determine_wants = r.object_store.determine_wants_all - refs = client.fetch(path, r, progress=sys.stdout.write) - print("Remote refs:") - for item in refs.items(): - print("%s -> %s" % item) - - -def cmd_log(args): - parser = optparse.OptionParser() - parser.add_option("--reverse", dest="reverse", action="store_true", - help="Reverse order in which entries are printed") - parser.add_option("--name-status", dest="name_status", action="store_true", - help="Print name/status for each changed file") - options, args = parser.parse_args(args) - - porcelain.log(".", paths=args, reverse=options.reverse, - name_status=options.name_status, - outstream=sys.stdout) - - -def cmd_diff(args): - opts, args = getopt(args, "", []) - - if args == []: - print("Usage: dulwich diff COMMITID") - sys.exit(1) - - r = Repo(".") - commit_id = args[0] - commit = r[commit_id] - parent_commit = r[commit.parents[0]] - write_tree_diff(sys.stdout, r.object_store, parent_commit.tree, commit.tree) - - -def cmd_dump_pack(args): - opts, args = getopt(args, "", []) - - if args == []: - print("Usage: dulwich dump-pack FILENAME") - sys.exit(1) - - basename, _ = os.path.splitext(args[0]) - x = Pack(basename) - print("Object names checksum: %s" % x.name()) - print("Checksum: %s" % sha_to_hex(x.get_stored_checksum())) - if not x.check(): - print("CHECKSUM DOES NOT MATCH") - print("Length: %d" % len(x)) - for name in x: - try: - print("\t%s" % x[name]) - except KeyError as k: - print("\t%s: Unable to resolve base %s" % (name, k)) - except ApplyDeltaError as e: - print("\t%s: Unable to apply delta: %r" % (name, e)) +class Command(object): + """A Dulwich subcommand.""" + + def run(self, args): + """Run the command.""" + raise NotImplementedError(self.run) + + +class cmd_archive(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + client, path = get_transport_and_path(args.pop(0)) + location = args.pop(0) + committish = args.pop(0) + porcelain.archive(location, committish, outstream=sys.stdout, + errstream=sys.stderr) + + +class cmd_add(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + + porcelain.add(".", paths=args) + + +class cmd_rm(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + + porcelain.rm(".", paths=args) + + +class cmd_fetch_pack(Command): + + def run(self, args): + opts, args = getopt(args, "", ["all"]) + opts = dict(opts) + client, path = get_transport_and_path(args.pop(0)) + r = Repo(".") + if "--all" in opts: + determine_wants = r.object_store.determine_wants_all + else: + determine_wants = lambda x: [y for y in args if not y in r.object_store] + client.fetch(path, r, determine_wants) + + +class cmd_fetch(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + opts = dict(opts) + client, path = get_transport_and_path(args.pop(0)) + r = Repo(".") + if "--all" in opts: + determine_wants = r.object_store.determine_wants_all + refs = client.fetch(path, r, progress=sys.stdout.write) + print("Remote refs:") + for item in refs.items(): + print("%s -> %s" % item) + + +class cmd_log(Command): + + def run(self, args): + parser = optparse.OptionParser() + parser.add_option("--reverse", dest="reverse", action="store_true", + help="Reverse order in which entries are printed") + parser.add_option("--name-status", dest="name_status", action="store_true", + help="Print name/status for each changed file") + options, args = parser.parse_args(args) + + porcelain.log(".", paths=args, reverse=options.reverse, + name_status=options.name_status, + outstream=sys.stdout) + + +class cmd_diff(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + if args == []: + print("Usage: dulwich diff COMMITID") + sys.exit(1) -def cmd_dump_index(args): - opts, args = getopt(args, "", []) + r = Repo(".") + commit_id = args[0] + commit = r[commit_id] + parent_commit = r[commit.parents[0]] + write_tree_diff(sys.stdout, r.object_store, parent_commit.tree, commit.tree) - if args == []: - print("Usage: dulwich dump-index FILENAME") - sys.exit(1) - filename = args[0] - idx = Index(filename) +class cmd_dump_pack(Command): - for o in idx: - print(o, idx[o]) + def run(self, args): + opts, args = getopt(args, "", []) + if args == []: + print("Usage: dulwich dump-pack FILENAME") + sys.exit(1) -def cmd_init(args): - opts, args = getopt(args, "", ["bare"]) - opts = dict(opts) + basename, _ = os.path.splitext(args[0]) + x = Pack(basename) + print("Object names checksum: %s" % x.name()) + print("Checksum: %s" % sha_to_hex(x.get_stored_checksum())) + if not x.check(): + print("CHECKSUM DOES NOT MATCH") + print("Length: %d" % len(x)) + for name in x: + try: + print("\t%s" % x[name]) + except KeyError as k: + print("\t%s: Unable to resolve base %s" % (name, k)) + except ApplyDeltaError as e: + print("\t%s: Unable to apply delta: %r" % (name, e)) - if args == []: - path = os.getcwd() - else: - path = args[0] - porcelain.init(path, bare=("--bare" in opts)) +class cmd_dump_index(Command): + def run(self, args): + opts, args = getopt(args, "", []) -def cmd_clone(args): - opts, args = getopt(args, "", ["bare"]) - opts = dict(opts) + if args == []: + print("Usage: dulwich dump-index FILENAME") + sys.exit(1) - if args == []: - print("usage: dulwich clone host:path [PATH]") - sys.exit(1) + filename = args[0] + idx = Index(filename) - source = args.pop(0) - if len(args) > 0: - target = args.pop(0) - else: - target = None + for o in idx: + print(o, idx[o]) - porcelain.clone(source, target, bare=("--bare" in opts)) +class cmd_init(Command): -def cmd_commit(args): - opts, args = getopt(args, "", ["message"]) - opts = dict(opts) - porcelain.commit(".", message=opts["--message"]) + def run(self, args): + opts, args = getopt(args, "", ["bare"]) + opts = dict(opts) + if args == []: + path = os.getcwd() + else: + path = args[0] -def cmd_commit_tree(args): - opts, args = getopt(args, "", ["message"]) - if args == []: - print("usage: dulwich commit-tree tree") - sys.exit(1) - opts = dict(opts) - porcelain.commit_tree(".", tree=args[0], message=opts["--message"]) + porcelain.init(path, bare=("--bare" in opts)) -def cmd_update_server_info(args): - porcelain.update_server_info(".") +class cmd_clone(Command): + def run(self, args): + opts, args = getopt(args, "", ["bare"]) + opts = dict(opts) -def cmd_symbolic_ref(args): - opts, args = getopt(args, "", ["ref-name", "force"]) - if not args: - print("Usage: dulwich symbolic-ref REF_NAME [--force]") - sys.exit(1) + if args == []: + print("usage: dulwich clone host:path [PATH]") + sys.exit(1) - ref_name = args.pop(0) - porcelain.symbolic_ref(".", ref_name=ref_name, force='--force' in args) + source = args.pop(0) + if len(args) > 0: + target = args.pop(0) + else: + target = None + porcelain.clone(source, target, bare=("--bare" in opts)) -def cmd_show(args): - opts, args = getopt(args, "", []) - porcelain.show(".", args) +class cmd_commit(Command): -def cmd_diff_tree(args): - opts, args = getopt(args, "", []) - if len(args) < 2: - print("Usage: dulwich diff-tree OLD-TREE NEW-TREE") - sys.exit(1) - porcelain.diff_tree(".", args[0], args[1]) + def run(self, args): + opts, args = getopt(args, "", ["message"]) + opts = dict(opts) + porcelain.commit(".", message=opts["--message"]) -def cmd_rev_list(args): - opts, args = getopt(args, "", []) - if len(args) < 1: - print('Usage: dulwich rev-list COMMITID...') - sys.exit(1) - porcelain.rev_list('.', args) +class cmd_commit_tree(Command): + def run(self, args): + opts, args = getopt(args, "", ["message"]) + if args == []: + print("usage: dulwich commit-tree tree") + sys.exit(1) + opts = dict(opts) + porcelain.commit_tree(".", tree=args[0], message=opts["--message"]) -def cmd_tag(args): - opts, args = getopt(args, '', []) - if len(args) < 2: - print('Usage: dulwich tag NAME') - sys.exit(1) - porcelain.tag('.', args[0]) +class cmd_update_server_info(Command): -def cmd_repack(args): - opts, args = getopt(args, "", []) - opts = dict(opts) - porcelain.repack('.') - + def run(self, args): + porcelain.update_server_info(".") -def cmd_reset(args): - opts, args = getopt(args, "", ["hard", "soft", "mixed"]) - opts = dict(opts) - mode = "" - if "--hard" in opts: - mode = "hard" - elif "--soft" in opts: - mode = "soft" - elif "--mixed" in opts: - mode = "mixed" - porcelain.reset('.', mode=mode, *args) - - -def cmd_daemon(args): - from dulwich import log_utils - from dulwich.protocol import TCP_GIT_PORT - parser = optparse.OptionParser() - parser.add_option("-l", "--listen_address", dest="listen_address", - default="localhost", - help="Binding IP address.") - parser.add_option("-p", "--port", dest="port", type=int, - default=TCP_GIT_PORT, - help="Binding TCP port.") - options, args = parser.parse_args(args) - - log_utils.default_logging_config() - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - from dulwich import porcelain - porcelain.daemon(gitdir, address=options.listen_address, - port=options.port) - - -def cmd_web_daemon(args): - from dulwich import log_utils - parser = optparse.OptionParser() - parser.add_option("-l", "--listen_address", dest="listen_address", - default="", - help="Binding IP address.") - parser.add_option("-p", "--port", dest="port", type=int, - default=8000, - help="Binding TCP port.") - options, args = parser.parse_args(args) - - log_utils.default_logging_config() - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - from dulwich import porcelain - porcelain.web_daemon(gitdir, address=options.listen_address, + +class cmd_symbolic_ref(Command): + + def run(self, args): + opts, args = getopt(args, "", ["ref-name", "force"]) + if not args: + print("Usage: dulwich symbolic-ref REF_NAME [--force]") + sys.exit(1) + + ref_name = args.pop(0) + porcelain.symbolic_ref(".", ref_name=ref_name, force='--force' in args) + + +class cmd_show(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + porcelain.show(".", args) + + +class cmd_diff_tree(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + if len(args) < 2: + print("Usage: dulwich diff-tree OLD-TREE NEW-TREE") + sys.exit(1) + porcelain.diff_tree(".", args[0], args[1]) + + +class cmd_rev_list(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + if len(args) < 1: + print('Usage: dulwich rev-list COMMITID...') + sys.exit(1) + porcelain.rev_list('.', args) + + +class cmd_tag(Command): + + def run(self, args): + opts, args = getopt(args, '', []) + if len(args) < 2: + print('Usage: dulwich tag NAME') + sys.exit(1) + porcelain.tag('.', args[0]) + + +class cmd_repack(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + opts = dict(opts) + porcelain.repack('.') + + +class cmd_reset(Command): + + def run(self, args): + opts, args = getopt(args, "", ["hard", "soft", "mixed"]) + opts = dict(opts) + mode = "" + if "--hard" in opts: + mode = "hard" + elif "--soft" in opts: + mode = "soft" + elif "--mixed" in opts: + mode = "mixed" + porcelain.reset('.', mode=mode, *args) + + +class cmd_daemon(Command): + + def run(self, args): + from dulwich import log_utils + from dulwich.protocol import TCP_GIT_PORT + parser = optparse.OptionParser() + parser.add_option("-l", "--listen_address", dest="listen_address", + default="localhost", + help="Binding IP address.") + parser.add_option("-p", "--port", dest="port", type=int, + default=TCP_GIT_PORT, + help="Binding TCP port.") + options, args = parser.parse_args(args) + + log_utils.default_logging_config() + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + from dulwich import porcelain + porcelain.daemon(gitdir, address=options.listen_address, port=options.port) -def cmd_receive_pack(args): - parser = optparse.OptionParser() - options, args = parser.parse_args(args) - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - porcelain.receive_pack(gitdir) - - -def cmd_upload_pack(args): - parser = optparse.OptionParser() - options, args = parser.parse_args(args) - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - porcelain.upload_pack(gitdir) - - -def cmd_status(args): - parser = optparse.OptionParser() - options, args = parser.parse_args(args) - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - status = porcelain.status(gitdir) - if any(names for (kind, names) in status.staged.items()): - sys.stdout.write("Changes to be committed:\n\n") - for kind, names in status.staged.items(): - for name in names: - sys.stdout.write("\t%s: %s\n" % ( - kind, name.decode(sys.getfilesystemencoding()))) - sys.stdout.write("\n") - if status.unstaged: - sys.stdout.write("Changes not staged for commit:\n\n") - for name in status.unstaged: - sys.stdout.write("\t%s\n" % - name.decode(sys.getfilesystemencoding())) - sys.stdout.write("\n") - if status.untracked: - sys.stdout.write("Untracked files:\n\n") - for name in status.untracked: - sys.stdout.write("\t%s\n" % name) - sys.stdout.write("\n") - - -def cmd_ls_remote(args): - opts, args = getopt(args, '', []) - if len(args) < 1: - print('Usage: dulwich ls-remote URL') - sys.exit(1) - refs = porcelain.ls_remote(args[0]) - for ref in sorted(refs): - sys.stdout.write("%s\t%s\n" % (ref, refs[ref])) - - -def cmd_ls_tree(args): - parser = optparse.OptionParser() - parser.add_option("-r", "--recursive", action="store_true", - help="Recusively list tree contents.") - parser.add_option("--name-only", action="store_true", - help="Only display name.") - options, args = parser.parse_args(args) - try: - treeish = args.pop(0) - except IndexError: - treeish = None - porcelain.ls_tree( - '.', treeish, outstream=sys.stdout, recursive=options.recursive, - name_only=options.name_only) - - -def cmd_pack_objects(args): - opts, args = getopt(args, '', ['stdout']) - opts = dict(opts) - if len(args) < 1 and not '--stdout' in args: - print('Usage: dulwich pack-objects basename') - sys.exit(1) - object_ids = [l.strip() for l in sys.stdin.readlines()] - basename = args[0] - if '--stdout' in opts: - packf = getattr(sys.stdout, 'buffer', sys.stdout) - idxf = None - close = [] - else: - packf = open(basename + '.pack', 'w') - idxf = open(basename + '.idx', 'w') - close = [packf, idxf] - porcelain.pack_objects('.', object_ids, packf, idxf) - for f in close: - f.close() - - -def cmd_help(args): - parser = optparse.OptionParser() - parser.add_option("-a", "--all", dest="all", - action="store_true", - help="List all commands.") - options, args = parser.parse_args(args) - - if options.all: - print('Available commands:') - for cmd in sorted(commands): - print(' %s' % cmd) - else: - print("""\ +class cmd_web_daemon(Command): + + def run(self, args): + from dulwich import log_utils + parser = optparse.OptionParser() + parser.add_option("-l", "--listen_address", dest="listen_address", + default="", + help="Binding IP address.") + parser.add_option("-p", "--port", dest="port", type=int, + default=8000, + help="Binding TCP port.") + options, args = parser.parse_args(args) + + log_utils.default_logging_config() + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + from dulwich import porcelain + porcelain.web_daemon(gitdir, address=options.listen_address, + port=options.port) + + +class cmd_receive_pack(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + porcelain.receive_pack(gitdir) + + +class cmd_upload_pack(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + porcelain.upload_pack(gitdir) + + +class cmd_status(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + status = porcelain.status(gitdir) + if any(names for (kind, names) in status.staged.items()): + sys.stdout.write("Changes to be committed:\n\n") + for kind, names in status.staged.items(): + for name in names: + sys.stdout.write("\t%s: %s\n" % ( + kind, name.decode(sys.getfilesystemencoding()))) + sys.stdout.write("\n") + if status.unstaged: + sys.stdout.write("Changes not staged for commit:\n\n") + for name in status.unstaged: + sys.stdout.write("\t%s\n" % + name.decode(sys.getfilesystemencoding())) + sys.stdout.write("\n") + if status.untracked: + sys.stdout.write("Untracked files:\n\n") + for name in status.untracked: + sys.stdout.write("\t%s\n" % name) + sys.stdout.write("\n") + + +class cmd_ls_remote(Command): + + def run(self, args): + opts, args = getopt(args, '', []) + if len(args) < 1: + print('Usage: dulwich ls-remote URL') + sys.exit(1) + refs = porcelain.ls_remote(args[0]) + for ref in sorted(refs): + sys.stdout.write("%s\t%s\n" % (ref, refs[ref])) + + +class cmd_ls_tree(Command): + + def run(self, args): + parser = optparse.OptionParser() + parser.add_option("-r", "--recursive", action="store_true", + help="Recusively list tree contents.") + parser.add_option("--name-only", action="store_true", + help="Only display name.") + options, args = parser.parse_args(args) + try: + treeish = args.pop(0) + except IndexError: + treeish = None + porcelain.ls_tree( + '.', treeish, outstream=sys.stdout, recursive=options.recursive, + name_only=options.name_only) + + +class cmd_pack_objects(Command): + + def run(self, args): + opts, args = getopt(args, '', ['stdout']) + opts = dict(opts) + if len(args) < 1 and not '--stdout' in args: + print('Usage: dulwich pack-objects basename') + sys.exit(1) + object_ids = [l.strip() for l in sys.stdin.readlines()] + basename = args[0] + if '--stdout' in opts: + packf = getattr(sys.stdout, 'buffer', sys.stdout) + idxf = None + close = [] + else: + packf = open(basename + '.pack', 'w') + idxf = open(basename + '.idx', 'w') + close = [packf, idxf] + porcelain.pack_objects('.', object_ids, packf, idxf) + for f in close: + f.close() + + +class cmd_help(Command): + + def run(self, args): + parser = optparse.OptionParser() + parser.add_option("-a", "--all", dest="all", + action="store_true", + help="List all commands.") + options, args = parser.parse_args(args) + + if options.all: + print('Available commands:') + for cmd in sorted(commands): + print(' %s' % cmd) + else: + print("""\ The dulwich command line tool is currently a very basic frontend for the Dulwich python module. For full functionality, please see the API reference. For a list of supported commands, see 'dulwich help -a'. """) commands = { "add": cmd_add, "archive": cmd_archive, "clone": cmd_clone, "commit": cmd_commit, "commit-tree": cmd_commit_tree, "daemon": cmd_daemon, "diff": cmd_diff, "diff-tree": cmd_diff_tree, "dump-pack": cmd_dump_pack, "dump-index": cmd_dump_index, "fetch-pack": cmd_fetch_pack, "fetch": cmd_fetch, "help": cmd_help, "init": cmd_init, "log": cmd_log, "ls-remote": cmd_ls_remote, "ls-tree": cmd_ls_tree, "pack-objects": cmd_pack_objects, "receive-pack": cmd_receive_pack, "repack": cmd_repack, "reset": cmd_reset, "rev-list": cmd_rev_list, "rm": cmd_rm, "show": cmd_show, "status": cmd_status, "symbolic-ref": cmd_symbolic_ref, "tag": cmd_tag, "update-server-info": cmd_update_server_info, "upload-pack": cmd_upload_pack, "web-daemon": cmd_web_daemon, } if len(sys.argv) < 2: print("Usage: %s <%s> [OPTIONS...]" % (sys.argv[0], "|".join(commands.keys()))) sys.exit(1) cmd = sys.argv[1] if not cmd in commands: print("No such subcommand: %s" % cmd) sys.exit(1) -commands[cmd](sys.argv[2:]) +commands[cmd]().run(sys.argv[2:])