mercurial/commands.py
changeset 245 fef0f8e041aa
parent 241 afe895fcc0d0
child 246 96cde50a746f
equal deleted inserted replaced
244:43105253cf5e 245:fef0f8e041aa
     1 import os, re, traceback, sys, signal, time
     1 import os, re, traceback, sys, signal, time, mdiff
     2 from mercurial import fancyopts, ui, hg
     2 from mercurial import fancyopts, ui, hg
     3 
     3 
     4 class UnknownCommand(Exception): pass
     4 class UnknownCommand(Exception): pass
     5 
     5 
     6 def filterfiles(list, files):
     6 def filterfiles(filters, files):
     7     l = [ x for x in list if x in files ]
     7     l = [ x for x in files if x in filters ]
     8 
     8 
     9     for f in files:
     9     for t in filters:
    10         if f[-1] != os.sep: f += os.sep
    10         if t and t[-1] != os.sep: t += os.sep
    11         l += [ x for x in list if x.startswith(f) ]
    11         l += [ x for x in files if x.startswith(t) ]
    12     return l
    12     return l
    13 
    13 
    14 def relfilter(repo, args):
    14 def relfilter(repo, files):
    15     if os.getcwd() != repo.root:
    15     if os.getcwd() != repo.root:
    16         p = os.getcwd()[len(repo.root) + 1: ]
    16         p = os.getcwd()[len(repo.root) + 1: ]
    17         return filterfiles(p, args)
    17         return filterfiles(p, files)
    18     return args
    18     return files
    19 
    19 
    20 def relpath(repo, args):
    20 def relpath(repo, args):
    21     if os.getcwd() != repo.root:
    21     if os.getcwd() != repo.root:
    22         p = os.getcwd()[len(repo.root) + 1: ]
    22         p = os.getcwd()[len(repo.root) + 1: ]
    23         return [ os.path.join(p, x) for x in args ]
    23         return [ os.path.normpath(os.path.join(p, x)) for x in args ]
    24     return args
    24     return args
       
    25 
       
    26 def dodiff(repo, files = None, node1 = None, node2 = None):
       
    27     def date(c):
       
    28         return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
       
    29 
       
    30     if node2:
       
    31         change = repo.changelog.read(node2)
       
    32         mmap2 = repo.manifest.read(change[0])
       
    33         (c, a, d) = repo.diffrevs(node1, node2)
       
    34         def read(f): return repo.file(f).read(mmap2[f])
       
    35         date2 = date(change)
       
    36     else:
       
    37         date2 = time.asctime()
       
    38         (c, a, d, u) = repo.diffdir(repo.root, node1)
       
    39         if not node1:
       
    40             node1 = repo.dirstate.parents()[0]
       
    41         def read(f): return file(os.path.join(repo.root, f)).read()
       
    42 
       
    43     change = repo.changelog.read(node1)
       
    44     mmap = repo.manifest.read(change[0])
       
    45     date1 = date(change)
       
    46 
       
    47     if files:
       
    48         c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
       
    49 
       
    50     for f in c:
       
    51         to = repo.file(f).read(mmap[f])
       
    52         tn = read(f)
       
    53         sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
       
    54     for f in a:
       
    55         to = ""
       
    56         tn = read(f)
       
    57         sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
       
    58     for f in d:
       
    59         to = repo.file(f).read(mmap[f])
       
    60         tn = ""
       
    61         sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
    25     
    62     
    26 def help(ui, cmd=None):
    63 def help(ui, cmd=None):
    27     '''show help'''
    64     '''show help'''
    28     if cmd:
    65     if cmd:
    29         try:
    66         try:
    57  status                show new, missing, and changed files in working dir
    94  status                show new, missing, and changed files in working dir
    58  tags                  show current changeset tags
    95  tags                  show current changeset tags
    59  undo                  undo the last transaction
    96  undo                  undo the last transaction
    60 """)
    97 """)
    61 
    98 
    62 def init(ui):
    99 def add(ui, repo, file, *files):
    63     """create a repository"""
   100     '''add the specified files on the next commit'''
    64     hg.repository(ui, ".", create=1)
   101     repo.add(relpath(repo, (file,) + files))
    65 
   102 
    66 def branch(ui, path):
   103 def addremove(ui, repo):
    67     '''branch from a local repository'''
       
    68     # this should eventually support remote repos
       
    69     os.system("cp -al %s/.hg .hg" % path)
       
    70 
       
    71 def checkout(ui, repo, changeset=None):
       
    72     '''checkout a given changeset or the current tip'''
       
    73     (c, a, d, u) = repo.diffdir(repo.root)
   104     (c, a, d, u) = repo.diffdir(repo.root)
    74     if c or a or d:
   105     repo.add(a)
    75         ui.warn("aborting (outstanding changes in working directory)\n")
   106     repo.remove(d)
    76         sys.exit(1)
   107 
    77 
   108 def annotate(u, repo, file, *files, **ops):
    78     node = repo.changelog.tip()
       
    79     if changeset:
       
    80         node = repo.lookup(changeset)
       
    81     repo.checkout(node)
       
    82 
       
    83 def annotate(u, repo, *args, **ops):
       
    84     def getnode(rev):
   109     def getnode(rev):
    85         return hg.short(repo.changelog.node(rev))
   110         return hg.short(repo.changelog.node(rev))
    86 
   111 
    87     def getname(rev):
   112     def getname(rev):
    88         try:
   113         try:
    99     bcache = {}
   124     bcache = {}
   100     opmap = [['user', getname], ['number', str], ['changeset', getnode]]
   125     opmap = [['user', getname], ['number', str], ['changeset', getnode]]
   101     if not ops['user'] and not ops['changeset']:
   126     if not ops['user'] and not ops['changeset']:
   102         ops['number'] = 1
   127         ops['number'] = 1
   103 
   128 
   104     args = relpath(repo, args)
       
   105     node = repo.dirstate.parents()[0]
   129     node = repo.dirstate.parents()[0]
   106     if ops['revision']:
   130     if ops['revision']:
   107         node = repo.changelog.lookup(ops['revision'])
   131         node = repo.changelog.lookup(ops['revision'])
   108     change = repo.changelog.read(node)
   132     change = repo.changelog.read(node)
   109     mmap = repo.manifest.read(change[0])
   133     mmap = repo.manifest.read(change[0])
   110     maxuserlen = 0
   134     maxuserlen = 0
   111     maxchangelen = 0
   135     maxchangelen = 0
   112     for f in args:
   136     for f in relpath(repo, (file,) + files):
   113         lines = repo.file(f).annotate(mmap[f])
   137         lines = repo.file(f).annotate(mmap[f])
   114         pieces = []
   138         pieces = []
   115 
   139 
   116         for o, f in opmap:
   140         for o, f in opmap:
   117             if ops[o]:
   141             if ops[o]:
   119                 m = max(map(len, l))
   143                 m = max(map(len, l))
   120                 pieces.append([ "%*s" % (m, x) for x in l])
   144                 pieces.append([ "%*s" % (m, x) for x in l])
   121 
   145 
   122         for p,l in zip(zip(*pieces), lines):
   146         for p,l in zip(zip(*pieces), lines):
   123             u.write(" ".join(p) + ": " + l[1])
   147             u.write(" ".join(p) + ": " + l[1])
       
   148 
       
   149 def branch(ui, path):
       
   150     '''branch from a local repository'''
       
   151     # this should eventually support remote repos
       
   152     os.system("cp -al %s/.hg .hg" % path)
       
   153 
       
   154 def checkout(ui, repo, changeset=None):
       
   155     '''checkout a given changeset or the current tip'''
       
   156     (c, a, d, u) = repo.diffdir(repo.root)
       
   157     if c or a or d:
       
   158         ui.warn("aborting (outstanding changes in working directory)\n")
       
   159         sys.exit(1)
       
   160 
       
   161     node = repo.changelog.tip()
       
   162     if changeset:
       
   163         node = repo.lookup(changeset)
       
   164     repo.checkout(node)
       
   165 
       
   166 def commit(ui, repo, *files):
       
   167     """commit the specified files or all outstanding changes"""
       
   168     repo.commit(relpath(repo, files))
       
   169 
       
   170 def diff(ui, repo, *files, **opts):
       
   171     revs = []
       
   172     if opts['rev']:
       
   173         revs = map(lambda x: repo.lookup(x), opts['rev'])
       
   174     
       
   175     if len(revs) > 2:
       
   176         self.ui.warn("too many revisions to diff\n")
       
   177         sys.exit(1)
       
   178 
       
   179     if files:
       
   180         files = relpath(repo, files)
       
   181     else:
       
   182         files = relpath(repo, [""])
       
   183 
       
   184     dodiff(repo, files, *revs)
       
   185 
       
   186 def forget(ui, repo, file, *files):
       
   187     """don't add the specified files on the next commit"""
       
   188     repo.forget(relpath(repo, (file,) + files))
   124 
   189 
   125 def heads(ui, repo):
   190 def heads(ui, repo):
   126     '''show current repository heads'''
   191     '''show current repository heads'''
   127     for n in repo.changelog.heads():
   192     for n in repo.changelog.heads():
   128         i = repo.changelog.rev(n)
   193         i = repo.changelog.rev(n)
   140             time.localtime(float(changes[2].split(' ')[0])))
   205             time.localtime(float(changes[2].split(' ')[0])))
   141         if ui.verbose: print "files:", " ".join(changes[3])
   206         if ui.verbose: print "files:", " ".join(changes[3])
   142         print "description:"
   207         print "description:"
   143         print changes[4]
   208         print changes[4]
   144 
   209 
       
   210 def init(ui):
       
   211     """create a repository"""
       
   212     hg.repository(ui, ".", create=1)
       
   213 
       
   214 def log(ui, repo, f):
       
   215     f = relpath(repo, [f])[0]
       
   216 
       
   217     r = repo.file(f)
       
   218     for i in range(r.count()):
       
   219         n = r.node(i)
       
   220         (p1, p2) = r.parents(n)
       
   221         (h, h1, h2) = map(hg.hex, (n, p1, p2))
       
   222         (i1, i2) = map(r.rev, (p1, p2))
       
   223         cr = r.linkrev(n)
       
   224         cn = hg.hex(repo.changelog.node(cr))
       
   225         print "rev:       %4d:%s" % (i, h)
       
   226         print "changeset: %4d:%s" % (cr, cn)
       
   227         print "parents:   %4d:%s" % (i1, h1)
       
   228         if i2: print "           %4d:%s" % (i2, h2)
       
   229         changes = repo.changelog.read(repo.changelog.node(cr))
       
   230         print "user: %s" % changes[1]
       
   231         print "date: %s" % time.asctime(
       
   232             time.localtime(float(changes[2].split(' ')[0])))
       
   233         print "description:"
       
   234         print changes[4].rstrip()
       
   235         print
       
   236 
   145 def parents(ui, repo, node = None):
   237 def parents(ui, repo, node = None):
   146     '''show the parents of the current working dir'''
   238     '''show the parents of the current working dir'''
   147     if node:
   239     if node:
   148         p = repo.changelog.parents(repo.lookup(hg.bin(node)))
   240         p = repo.changelog.parents(repo.lookup(hg.bin(node)))
   149     else:
   241     else:
   151 
   243 
   152     for n in p:
   244     for n in p:
   153         if n != hg.nullid:
   245         if n != hg.nullid:
   154             ui.write("%d:%s\n" % (repo.changelog.rev(n), hg.hex(n)))
   246             ui.write("%d:%s\n" % (repo.changelog.rev(n), hg.hex(n)))
   155 
   247 
   156 def resolve(ui, repo, node = None):
   248 def recover(ui, repo):
       
   249     repo.recover()
       
   250 
       
   251 def remove(ui, repo, file, *files):
       
   252     """remove the specified files on the next commit"""
       
   253     repo.remove(relpath(repo, (file,) + files))
       
   254 
       
   255 def resolve(ui, repo, node=None):
   157     '''merge a given node or the current tip into the working dir'''
   256     '''merge a given node or the current tip into the working dir'''
   158     if not node:
   257     if not node:
   159         node = repo.changelog.tip()
   258         node = repo.changelog.tip()
   160     else:
   259     else:
   161         node = repo.lookup(node)
   260         node = repo.lookup(node)
   162     repo.resolve(node)
   261     repo.resolve(node)
   163 
   262 
       
   263 def serve(ui, repo, **opts):
       
   264     from mercurial import hgweb
       
   265     hgweb.server(repo.root, opts["name"], opts["templates"],
       
   266                  opts["address"], opts["port"])
       
   267     
   164 def status(ui, repo):
   268 def status(ui, repo):
   165     '''show changed files in the working directory
   269     '''show changed files in the working directory
   166 
   270 
   167 C = changed
   271     C = changed
   168 A = added
   272     A = added
   169 R = removed
   273     R = removed
   170 ? = not tracked'''
   274     ? = not tracked'''
       
   275     
   171     (c, a, d, u) = repo.diffdir(repo.root)
   276     (c, a, d, u) = repo.diffdir(repo.root)
   172     (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
   277     (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
   173 
   278 
   174     for f in c: print "C", f
   279     for f in c: print "C", f
   175     for f in a: print "A", f
   280     for f in a: print "A", f
   176     for f in d: print "R", f
   281     for f in d: print "R", f
   177     for f in u: print "?", f
   282     for f in u: print "?", f
   178 
   283 
       
   284 def tip(ui, repo):
       
   285     n = repo.changelog.tip()
       
   286     t = repo.changelog.rev(n)
       
   287     ui.status("%d:%s\n" % (t, hg.hex(n)))
       
   288 
   179 def undo(ui, repo):
   289 def undo(ui, repo):
   180     repo.undo()
   290     repo.undo()
   181 
   291 
   182 table = {
   292 table = {
   183     "init": (init, [], 'hg init'),
   293     "add": (add, [], "hg add [files]"),
   184     "branch|clone": (branch, [], 'hg branch [path]'),
   294     "addremove": (addremove, [], "hg addremove"),
   185     "heads": (heads, [], 'hg heads'),
       
   186     "help": (help, [], 'hg help [command]'),
       
   187     "checkout|co": (checkout, [], 'hg checkout [changeset]'),
       
   188     "ann|annotate": (annotate,
   295     "ann|annotate": (annotate,
   189                      [('r', 'revision', '', 'revision'),
   296                      [('r', 'revision', '', 'revision'),
   190                       ('u', 'user', None, 'show user'),
   297                       ('u', 'user', None, 'show user'),
   191                       ('n', 'number', None, 'show revision number'),
   298                       ('n', 'number', None, 'show revision number'),
   192                       ('c', 'changeset', None, 'show changeset')],
   299                       ('c', 'changeset', None, 'show changeset')],
   193                      'hg annotate [-u] [-c] [-n] [-r id] [files]'),
   300                      'hg annotate [-u] [-c] [-n] [-r id] [files]'),
       
   301     "branch|clone": (branch, [], 'hg branch [path]'),
       
   302     "checkout|co": (checkout, [], 'hg checkout [changeset]'),
       
   303     "commit|ci": (commit, [], 'hg commit [files]'),
       
   304     "diff": (diff, [('r', 'rev', [], 'revision')],
       
   305              'hg diff [-r A] [-r B] [files]'),
       
   306     "forget": (forget, [], "hg forget [files]"),
       
   307     "heads": (heads, [], 'hg heads'),
       
   308     "help": (help, [], 'hg help [command]'),
       
   309     "init": (init, [], 'hg init'),
       
   310     "log": (log, [], 'hg log <file>'),
   194     "parents": (parents, [], 'hg parents [node]'),
   311     "parents": (parents, [], 'hg parents [node]'),
       
   312     "recover": (recover, [], "hg recover"),
       
   313     "remove": (remove, [], "hg remove [files]"),
   195     "resolve": (resolve, [], 'hg resolve [node]'),
   314     "resolve": (resolve, [], 'hg resolve [node]'),
       
   315     "serve": (serve, [('p', 'port', 8000, 'listen port'),
       
   316                       ('a', 'address', '', 'interface address'),
       
   317                       ('n', 'name', os.getcwd(), 'repository name'),
       
   318                       ('t', 'templates', "", 'template map')],
       
   319               "hg serve [options]"),
   196     "status": (status, [], 'hg status'),
   320     "status": (status, [], 'hg status'),
       
   321     "tip": (tip, [], 'hg tip'),
   197     "undo": (undo, [], 'hg undo'),
   322     "undo": (undo, [], 'hg undo'),
   198     }
   323     }
   199 
   324 
   200 norepo = "init branch help"
   325 norepo = "init branch help"
   201 
   326