#!/usr/bin/env python # # mercurial - a minimal scalable distributed SCM # v0.4d "oedipa maas" # # Copyright 2005 Matt Mackall # # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. # the psyco compiler makes commits a bit faster # and makes changegroup merge about 20 times slower! # try: # import psyco # psyco.full() # except: # pass import sys, os, time from mercurial import hg, mdiff, fancyopts def help(): print """\ commands: init create a new repository in this directory branch create a branch of in this directory merge merge changes from into local repository checkout [changeset] checkout the latest or given changeset status show new, missing, and changed files in working dir add [files...] add the given files in the next commit remove [files...] remove the given files in the next commit addremove add all new files, delete all missing files commit commit all changes to the repository history show changeset history log show revision history of a single file dump [rev] dump the latest or given revision of a file dumpmanifest [rev] dump the latest or given revision of the manifest diff [files...] diff working directory (or selected files) """ def filterfiles(list, files): l = [ x for x in list if x in files ] for f in files: if f[-1] != os.sep: f += os.sep l += [ x for x in list if x.startswith(f) ] return l def diff(files = None, node1 = None, node2 = None): if node2: change = repo.changelog.read(node2) mmap2 = repo.manifest.read(change[0]) (c, a, d) = repo.diffrevs(node1, node2) def read(f): return repo.file(f).read(mmap2[f]) else: if not node1: node1 = repo.current (c, a, d) = repo.diffdir(repo.root, node1) def read(f): return file(f).read() change = repo.changelog.read(node1) mmap = repo.manifest.read(change[0]) if files: (c, a, d) = map(lambda x: filterfiles(x, files), (c, a, d)) for f in c: to = repo.file(f).read(mmap[f]) tn = read(f) sys.stdout.write(mdiff.unidiff(to, tn, f)) for f in a: to = "" tn = read(f) sys.stdout.write(mdiff.unidiff(to, tn, f)) for f in d: to = repo.file(f).read(mmap[f]) tn = "" sys.stdout.write(mdiff.unidiff(to, tn, f)) options = {} opts = [('v', 'verbose', None, 'verbose'), ('d', 'debug', None, 'debug')] args = fancyopts.fancyopts(sys.argv[1:], opts, options, 'hg [options] [command options] [files]') try: cmd = args[0] args = args[1:] except: cmd = "" ui = hg.ui(options["verbose"], options["debug"]) if cmd == "init": repo = hg.repository(ui, ".", create=1) sys.exit(0) elif cmd == "branch" or cmd == "clone": os.system("cp -al %s/.hg .hg" % args[0]) sys.exit(0) elif cmd == "help": help() sys.exit(0) else: try: repo = hg.repository(ui=ui) except: print "Unable to open repository" sys.exit(0) if cmd == "checkout" or cmd == "co": node = repo.changelog.tip() if args: node = repo.changelog.lookup(args[0]) repo.checkout(node) elif cmd == "add": repo.add(args) elif cmd == "remove" or cmd == "rm" or cmd == "del" or cmd == "delete": repo.remove(args) elif cmd == "commit" or cmd == "checkin" or cmd == "ci": if 1: if len(args) > 0: repo.commit(repo.current, args) else: repo.commit(repo.current) elif cmd == "import" or cmd == "patch": ioptions = {} opts = [('p', 'strip', 1, 'path strip'), ('b', 'base', "", 'base path')] args = fancyopts.fancyopts(args, opts, ioptions, 'hg import [options] ') d = ioptions["base"] strip = ioptions["strip"] for patch in args: ui.status("applying %s\n" % patch) pf = d + patch os.system("patch -p%d < %s > /dev/null" % (strip, pf)) f = os.popen("lsdiff --strip %d %s" % (strip, pf)) files = f.read().splitlines() f.close() repo.commit(repo.current, files) elif cmd == "status": (c, a, d) = repo.diffdir(repo.root, repo.current) for f in c: print "C", f for f in a: print "?", f for f in d: print "R", f elif cmd == "diff": revs = [] if args: doptions = {} opts = [('r', 'revision', [], 'revision')] args = fancyopts.fancyopts(args, opts, doptions, 'hg diff [options] [files]') revs = map(lambda x: repo.changelog.lookup(x), doptions['revision']) if len(revs) > 2: print "too many revisions to diff" sys.exit(1) else: diff(args, *revs) elif cmd == "export": node = repo.changelog.lookup(args[0]) prev = repo.changelog.parents(node)[0] diff(None, prev, node) elif cmd == "debugchangegroup": newer = repo.newer(repo.changelog.lookup(args[0])) cg = repo.changegroup(newer) sys.stdout.write(cg) elif cmd == "debugaddchangegroup": data = sys.stdin.read() repo.addchangegroup(data) elif cmd == "addremove": (c, a, d) = repo.diffdir(repo.root, repo.current) repo.add(a) repo.remove(d) elif cmd == "history": for i in range(repo.changelog.count()): n = repo.changelog.node(i) changes = repo.changelog.read(n) (p1, p2) = repo.changelog.parents(n) (h, h1, h2) = map(hg.hex, (n, p1, p2)) (i1, i2) = map(repo.changelog.rev, (p1, p2)) print "rev: %4d:%s" % (i, h) print "parents: %4d:%s" % (i1, h1) if i2: print " %4d:%s" % (i2, h2) print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]), hg.hex(changes[0])) print "user:", changes[1] print "date:", time.asctime( time.localtime(float(changes[2].split(' ')[0]))) print "files:", " ".join(changes[3]) print "description:" print changes[4] elif cmd == "log": if args: r = repo.file(args[0]) for i in range(r.count()): n = r.node(i) (p1, p2) = r.parents(n) (h, h1, h2) = map(hg.hex, (n, p1, p2)) (i1, i2) = map(r.rev, (p1, p2)) cr = r.linkrev(n) cn = hg.hex(repo.changelog.node(cr)) print "rev: %4d:%s" % (i, h) print "changeset: %4d:%s" % (cr, cn) print "parents: %4d:%s" % (i1, h1) if i2: print " %4d:%s" % (i2, h2) else: print "missing filename" elif cmd == "dump": if args: r = repo.file(args[0]) n = r.tip() if len(args) > 1: n = r.lookup(args[1]) sys.stdout.write(r.read(n)) else: print "missing filename" elif cmd == "dumpmanifest": n = repo.manifest.tip() if len(args) > 0: n = repo.manifest.lookup(args[0]) m = repo.manifest.read(n) files = m.keys() files.sort() for f in files: print hg.hex(m[f]), f elif cmd == "debughash": f = repo.file(args[0]) print f.encodepath(args[0]) elif cmd == "debugindex": r = hg.revlog(open, args[0], "") print " rev offset length base linkrev"+\ " p1 p2 nodeid" for i in range(r.count()): e = r.index[i] print "% 6d % 9d % 7d % 5d % 7d %s.. %s.. %s.." % ( i, e[0], e[1], e[2], e[3], hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])) elif cmd == "merge": if args: other = hg.repository(ui, args[0]) repo.merge(other) else: print "missing source repository" elif cmd == "verify": filelinkrevs = {} filenodes = {} manifestchangeset = {} changesets = revisions = files = 0 print "checking changesets" for i in range(repo.changelog.count()): changesets += 1 n = repo.changelog.node(i) changes = repo.changelog.read(n) manifestchangeset[changes[0]] = n for f in changes[3]: revisions += 1 filelinkrevs.setdefault(f, []).append(i) print "checking manifests" for i in range(repo.manifest.count()): n = repo.manifest.node(i) ca = repo.changelog.node(repo.manifest.linkrev(n)) cc = manifestchangeset[n] if ca != cc: print "manifest %s points to %s, not %s" % \ (hg.hex(n), hg.hex(ca), hg.hex(cc)) m = repo.manifest.read(n) for f, fn in m.items(): filenodes.setdefault(f, {})[fn] = 1 print "crosschecking files in changesets and manifests" for f in filenodes: if f not in filelinkrevs: print "file %s in manifest but not in changesets" for f in filelinkrevs: if f not in filenodes: print "file %s in changeset but not in manifest" print "checking files" for f in filenodes: files += 1 fl = repo.file(f) nodes = {"\0"*20: 1} for i in range(fl.count()): n = fl.node(i) if n not in filenodes[f]: print "%s:%s not in manifests" % (f, hg.hex(n)) else: del filenodes[f][n] flr = fl.linkrev(n) if flr not in filelinkrevs[f]: print "%s:%s points to unexpected changeset rev %d" \ % (f, hg.hex(n), fl.linkrev(n)) else: filelinkrevs[f].remove(flr) # verify contents t = fl.read(n) # verify parents (p1, p2) = fl.parents(n) if p1 not in nodes: print "%s:%s unknown parent 1 %s" % (f, hg.hex(n), hg.hex(p1)) if p2 not in nodes: print "file %s:%s unknown parent %s" % (f, hg.hex(n), hg.hex(p1)) nodes[n] = 1 # cross-check for flr in filelinkrevs[f]: print "changeset rev %d not in %s" % (flr, f) for node in filenodes[f]: print "node %s in manifests not in %s" % (hg.hex(n), f) print "%d files, %d changesets, %d total revisions" % (files, changesets, revisions) else: print "unknown command\n" help() sys.exit(1)