changeset 155:083c38bdfa64

Merge from hgweb
author mpm@selenic.com
date Wed, 25 May 2005 08:54:54 -0800
parents 1f6c61a60586 (diff) 1d5f799ebe1e (current diff)
children 32ce2c5d4d25
files hg mercurial/hg.py mercurial/hgweb.py mercurial/revlog.py setup.py templates/changelogentry.tmpl templates/changeset.tmpl templates/fileannotate.tmpl templates/filediff.tmpl templates/filelog.tmpl templates/filelogentry.tmpl templates/filerevision.tmpl templates/header.tmpl templates/manifest.tmpl templates/map
diffstat 3 files changed, 185 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/hg	Tue May 24 16:08:09 2005 -0700
+++ b/hg	Wed May 25 08:54:54 2005 -0800
@@ -23,23 +23,22 @@
     print """\
  commands:
 
- init                  create a new repository in this directory
+ add [files...]        add the given files in the next commit
+ addremove             add all new files, delete all missing files
+ annotate [files...]   show changeset number per file line
  branch <path>         create a branch of <path> in this directory
- merge <path>          merge changes from <path> 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 <file>            show revision history of a single file
+ diff [files...]       diff working directory (or selected files)
  dump <file> [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)
+ history               show changeset history
+ init                  create a new repository in this directory
+ log <file>            show revision history of a single file
+ merge <path>          merge changes from <path> into local repository
+ remove [files...]     remove the given files in the next commit
+ status                show new, missing, and changed files in working dir
  tags                  show current changeset tags
- annotate [files...]   show changeset number per file line
- blame [files...]      show commit user per file line
 """
 
 def filterfiles(list, files):
@@ -215,47 +214,57 @@
     diff(args, *revs)
 
 elif cmd == "annotate":
+    bcache = {}
+
+    def getnode(rev):
+        return hg.short(repo.changelog.node(rev))
+
+    def getname(rev):
+        try:
+            return bcache[rev]
+        except KeyError:
+            cl = repo.changelog.read(repo.changelog.node(rev))
+            name = cl[1]
+            f = name.find('@')
+            if f >= 0:
+                name = name[:f]
+            bcache[rev] = name
+            return name
+    
     aoptions = {}
-    opts = [('r', 'revision', '', 'revision')]
+    opts = [('r', 'revision', '', 'revision'),
+            ('u', 'user', None, 'show user'),
+            ('n', 'number', None, 'show revision number'),
+            ('c', 'changeset', None, 'show changeset')]
+
     args = fancyopts.fancyopts(args, opts, aoptions,
-                               'hg annotate [-r id] [files]')
+                               'hg annotate [-u] [-c] [-n] [-r id] [files]')
+
+    opmap = [['user', getname], ['number', str], ['changeset', getnode]]
+    if not aoptions['user'] and not aoptions['changeset']:
+        aoptions['number'] = 1
 
     if args:
         if relpath: args = [ os.path.join(relpath, x) for x in args ]
-
         node = repo.current
         if aoptions['revision']:
             node = repo.changelog.lookup(aoptions['revision'])
         change = repo.changelog.read(node)
         mmap = repo.manifest.read(change[0])
+        maxuserlen = 0
+        maxchangelen = 0
         for f in args:
-            for n, l in repo.file(f).annotate(mmap[f]):
-                sys.stdout.write("% 6s:%s"%(n, l))
+            lines = repo.file(f).annotate(mmap[f])
+            pieces = []
 
-elif cmd == "blame":
-    aoptions = {}
-    opts = [('r', 'revision', '', 'revision')]
-    args = fancyopts.fancyopts(args, opts, aoptions,
-                               'hg blame [-r id] [files]')
-    if args:
-        bcache = {}
-        node = repo.current
-        if aoptions['revision']:
-            node = repo.changelog.lookup(aoptions['revision'])
-        change = repo.changelog.read(node)
-        mmap = repo.manifest.read(change[0])
-        for f in args:
-            for n, l in repo.file(f).annotate(mmap[f]):
-                try:
-                    name = bcache[n]
-                except KeyError:
-                    cl = repo.changelog.read(repo.changelog.node(n))
-                    name = cl[1]
-                    f = name.find('@')
-                    if f >= 0:
-                        name = name[:f]
-                    bcache[n] = name
-                sys.stdout.write("% 10s:%s"%(name, l))
+            for o, f in opmap:
+                if aoptions[o]:
+                    l = [ f(n) for n,t in lines ]
+                    m = max(map(len, l))
+                    pieces.append([ "%*s" % (m, x) for x in l])
+
+            for p,l in zip(zip(*pieces), lines):
+                sys.stdout.write(" ".join(p) + ": " + l[1])
 
 elif cmd == "export":
     node = repo.lookup(args[0])
@@ -431,7 +440,7 @@
                 errors += 1
         try:
             changes = repo.changelog.read(n)
-        except Error, inst:
+        except Exception, inst:
             ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
             errors += 1
             
@@ -508,8 +517,8 @@
             # verify contents
             try:
                 t = fl.read(n)
-            except Error, inst:
-                ui.warn("unpacking file %s %s: %s\n" % (f, short(n), inst))
+            except Exception, inst:
+                ui.warn("unpacking file %s %s: %s\n" % (f, hg.short(n), inst))
                 errors += 1
             
             # verify parents
--- a/mercurial/hg.py	Tue May 24 16:08:09 2005 -0700
+++ b/mercurial/hg.py	Wed May 25 08:54:54 2005 -0800
@@ -298,8 +298,6 @@
                            self.join("undo"))
 
     def commit(self, parent, update = None, text = ""):
-        tr = self.transaction()
-        
         try:
             remove = [ l[:-1] for l in self.opener("to-remove") ]
             os.unlink(self.join("to-remove"))
@@ -310,6 +308,12 @@
         if update == None:
             update = self.diffdir(self.root, parent)[0]
 
+        if not update:
+            self.ui.status("nothing changed\n")
+            return
+
+        tr = self.transaction()
+
         # check in files
         new = {}
         linkrev = self.changelog.count()
@@ -509,6 +513,8 @@
         unknown = [tip]
         search = []
         fetch = []
+        seen = {}
+        seenbranch = {}
 
         if tip[0] in m:
             self.ui.note("nothing to do!\n")
@@ -516,10 +522,18 @@
 
         while unknown:
             n = unknown.pop(0)
+            seen[n[0]] = 1
+            
+            self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
             if n == nullid: break
+            if n in seenbranch:
+                self.ui.debug("branch already found\n")
+                continue
             if n[1] and n[1] in m: # do we know the base?
-                self.ui.debug("found incomplete branch %s\n" % short(n[1]))
+                self.ui.debug("found incomplete branch %s:%s\n"
+                              % (short(n[0]), short(n[1])))
                 search.append(n) # schedule branch range for scanning
+                seenbranch[n] = 1
             else:
                 if n[2] in m and n[3] in m:
                     if n[1] not in fetch:
@@ -527,9 +541,19 @@
                                       short(n[1]))
                         fetch.append(n[1]) # earliest unknown
                         continue
-                for b in remote.branches([n[2], n[3]]):
-                    if b[0] not in m:
-                        unknown.append(b)
+
+                r = []
+                for a in n[2:4]:
+                    if a not in seen: r.append(a)
+                    
+                if r:
+                    self.ui.debug("requesting %s\n" %
+                                " ".join(map(short, r)))
+                    for b in remote.branches(r):
+                        self.ui.debug("received %s:%s\n" %
+                                      (short(b[0]), short(b[1])))
+                        if b[0] not in m and b[0] not in seen:
+                            unknown.append(b)
   
         while search:
             n = search.pop(0)
@@ -612,6 +636,7 @@
 
         tr = self.transaction()
         simple = True
+        need = {}
 
         self.ui.status("adding changesets\n")
         # pull off the changeset group
@@ -634,9 +659,62 @@
             simple = False
             resolverev = self.changelog.count()
 
+            # resolve the manifest to determine which files
+            # we care about merging
+            self.ui.status("resolving manifests\n")
+            ma = self.manifest.ancestor(mm, mo)
+            omap = self.manifest.read(mo) # other
+            amap = self.manifest.read(ma) # ancestor
+            mmap = self.manifest.read(mm) # mine
+            nmap = {}
+
+            self.ui.debug(" ancestor %s local %s remote %s\n" %
+                          (short(ma), short(mm), short(mo)))
+
+            for f, mid in mmap.iteritems():
+                if f in omap:
+                    if mid != omap[f]:
+                        self.ui.debug(" %s versions differ, do resolve\n" % f)
+                        need[f] = mid # use merged version or local version
+                    else:
+                        nmap[f] = mid # keep ours
+                    del omap[f]
+                elif f in amap:
+                    if mid != amap[f]:
+                        r = self.ui.prompt(
+                            (" local changed %s which remote deleted\n" % f) +
+                            "(k)eep or (d)elete?", "[kd]", "k")
+                        if r == "k": nmap[f] = mid
+                    else:
+                        self.ui.debug("other deleted %s\n" % f)
+                        pass # other deleted it
+                else:
+                    self.ui.debug("local created %s\n" %f)
+                    nmap[f] = mid # we created it
+
+            del mmap
+
+            for f, oid in omap.iteritems():
+                if f in amap:
+                    if oid != amap[f]:
+                        r = self.ui.prompt(
+                            ("remote changed %s which local deleted\n" % f) +
+                            "(k)eep or (d)elete?", "[kd]", "k")
+                        if r == "k": nmap[f] = oid
+                    else:
+                        pass # probably safe
+                else:
+                    self.ui.debug("remote created %s, do resolve\n" % f)
+                    need[f] = oid
+
+            del omap
+            del amap
+
+        new = need.keys()
+        new.sort()
+
         # process the files
         self.ui.status("adding files\n")
-        new = {}
         while 1:
             f = getchunk(4)
             if not f: break
@@ -645,11 +723,17 @@
             fl = self.file(f)
             o = fl.tip()
             n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
-            if not simple:
-                if o == n: continue
-                # this file has changed between branches, so it must be
-                # represented in the merge changeset
-                new[f] = self.merge3(fl, f, o, n, tr, resolverev)
+            if f in need:
+                del need[f]
+                # manifest resolve determined we need to merge the tips
+                nmap[f] = self.merge3(fl, f, o, n, tr, resolverev)
+
+        if need:
+            # we need to do trivial merges on local files
+            for f in new:
+                if f not in need: continue
+                fl = self.file(f)
+                nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev)
 
         # For simple merges, we don't need to resolve manifests or changesets
         if simple:
@@ -657,64 +741,11 @@
             tr.close()
             return
 
-        # resolve the manifest to point to all the merged files
-        self.ui.status("resolving manifests\n")
-        ma = self.manifest.ancestor(mm, mo)
-        omap = self.manifest.read(mo) # other
-        amap = self.manifest.read(ma) # ancestor
-        mmap = self.manifest.read(mm) # mine
-        self.ui.debug("ancestor %s local %s remote %s\n" %
-                      (short(ma), short(mm), short(mo)))
-        nmap = {}
-
-        for f, mid in mmap.iteritems():
-            if f in omap:
-                if mid != omap[f]:
-                    self.ui.debug("%s versions differ\n" % f)
-                    if f in new: self.ui.debug("%s updated in resolve\n" % f)
-                    # use merged version or local version
-                    nmap[f] = new.get(f, mid)
-                else:
-                    nmap[f] = mid # keep ours
-                del omap[f]
-            elif f in amap:
-                if mid != amap[f]:
-                    r = self.ui.prompt(
-                        ("local changed %s which remote deleted\n" % f) +
-                        "(k)eep or (d)elete?", "[kd]", "k")
-                    if r == "k": nmap[f] = mid
-                else:
-                    self.ui.debug("other deleted %s\n" % f)
-                    pass # other deleted it
-            else:
-                self.ui.debug("local created %s\n" %f)
-                nmap[f] = mid # we created it
-                
-        del mmap
-
-        for f, oid in omap.iteritems():
-            if f in amap:
-                if oid != amap[f]:
-                    r = self.ui.prompt(
-                        ("remote changed %s which local deleted\n" % f) +
-                        "(k)eep or (d)elete?", "[kd]", "k")
-                    if r == "k": nmap[f] = oid
-                else:
-                    pass # probably safe
-            else:
-                self.ui.debug("remote created %s\n" % f)
-                nmap[f] = new.get(f, oid) # remote created it
-
-        del omap
-        del amap
-
         node = self.manifest.add(nmap, tr, resolverev, mm, mo)
 
         # Now all files and manifests are merged, we add the changed files
         # and manifest id to the changelog
         self.ui.status("committing merge changeset\n")
-        new = new.keys()
-        new.sort()
         if co == cn: cn = -1
 
         edittext = "\nHG: merge resolve\n" + \
@@ -749,7 +780,7 @@
 
             cmd = os.environ["HGMERGE"]
             self.ui.debug("invoking merge with %s\n" % cmd)
-            r = os.system("%s %s %s %s" % (cmd, a, b, c))
+            r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn))
             if r:
                 raise "Merge failed!"
 
@@ -776,7 +807,7 @@
     def branches(self, nodes):
         n = " ".join(map(hex, nodes))
         d = self.do_cmd("branches", nodes=n).read()
-        br = [ map(bin, b.split(" ")) for b in d.splitlines() ]
+        br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
         return br
 
     def between(self, pairs):
--- a/mercurial/revlog.py	Tue May 24 16:08:09 2005 -0700
+++ b/mercurial/revlog.py	Wed May 25 08:54:54 2005 -0800
@@ -8,7 +8,7 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import zlib, struct, sha, os, tempfile, binascii
+import zlib, struct, sha, os, tempfile, binascii, heapq
 from mercurial import mdiff
 
 def hex(node): return binascii.hexlify(node)
@@ -276,38 +276,42 @@
         return node
 
     def ancestor(self, a, b):
-        def expand(list, map):
-            a = []
-            while list:
-                n = list.pop(0)
-                map[n] = 1
-                yield n
-                for p in self.parents(n):
-                    if p != nullid and p not in map:
-                        list.append(p)
-            yield nullid
+        # calculate the distance of every node from root
+        dist = {nullid: 0}
+        for i in xrange(self.count()):
+            n = self.node(i)
+            p1, p2 = self.parents(n)
+            dist[n] = max(dist[p1], dist[p2]) + 1
+        
+        # traverse ancestors in order of decreasing distance from root
+        def ancestors(node):
+            # we store negative distances because heap returns smallest member
+            h = [(-dist[node], node)]
+            seen = {}
+            earliest = self.count()
+            while h:
+                d, n = heapq.heappop(h)
+                r = self.rev(n)
+                if n not in seen:
+                    seen[n] = 1
+                    yield (-d, n)
+                    for p in self.parents(n):
+                        heapq.heappush(h, (-dist[p], p))
 
-        amap = {}
-        bmap = {}
-        ag = expand([a], amap)
-        bg = expand([b], bmap)
-        adone = bdone = 0
+        x = ancestors(a)
+        y = ancestors(b)
+        lx = x.next()
+        ly = y.next()
 
-        while not adone or not bdone:
-            if not adone:
-                an = ag.next()
-                if an == nullid:
-                    adone = 1
-                elif an in bmap:
-                    return an
-            if not bdone:
-                bn = bg.next()
-                if bn == nullid:
-                    bdone = 1
-                elif bn in amap:
-                    return bn
-
-        return nullid
+        # increment each ancestor list until it is closer to root than
+        # the other, or they match
+        while 1:
+            if lx == ly:
+                return lx[1]
+            elif lx < ly:
+                ly = y.next()
+            elif lx > ly:
+                lx = x.next()
 
     def group(self, linkmap):
         # given a list of changeset revs, return a set of deltas and