hg-relink: do not compare .d files
authorBrendan Cully <brendan@kublai.com>
Fri, 23 Mar 2007 14:11:31 -0700
changeset 4270 29eb88bd5c8d
parent 4264 bda63383d529
child 4271 1eaa8d90c689
hg-relink: do not compare .d files
contrib/hg-relink
--- a/contrib/hg-relink	Wed Mar 21 14:06:25 2007 -0700
+++ b/contrib/hg-relink	Fri Mar 23 14:11:31 2007 -0700
@@ -23,20 +23,13 @@
             if not os.path.exists(os.path.join(d, '.hg')):
                 raise ConfigError("%s: not a mercurial repository" % d)
 
-try:
-    cfg = Config(sys.argv)
-except ConfigError, inst:
-    print str(inst)
-    usage()
-    sys.exit(1)
-
 def collect(src):
     seplen = len(os.path.sep)
     candidates = []
     for dirpath, dirnames, filenames in os.walk(src):
         relpath = dirpath[len(src) + seplen:]
         for filename in filenames:
-            if not (filename.endswith('.i') or filename.endswith('.d')):
+            if not filename.endswith('.i'):
                 continue
             st = os.stat(os.path.join(dirpath, filename))
             candidates.append((os.path.join(relpath, filename), st))
@@ -44,25 +37,56 @@
     return candidates
 
 def prune(candidates, dst):
+    def getdatafile(path):
+        if not path.endswith('.i'):
+            return None, None
+        df = path[:-1] + 'd'
+        try:
+            st = os.stat(df)
+        except OSError:
+            return None, None
+        return df, st
+
+    def linkfilter(dst, st):
+        try:
+            ts = os.stat(dst)
+        except OSError:
+            # Destination doesn't have this file?
+            return False
+        if st.st_ino == ts.st_ino:
+            return False
+        if st.st_dev != ts.st_dev:
+            # No point in continuing
+            raise Exception('Source and destination are on different devices')
+        if st.st_size != ts.st_size:
+            # TODO: compare revlog heads
+            return False
+        return st
+
     targets = []
     for fn, st in candidates:
         tgt = os.path.join(dst, fn)
-        try:
-            ts = os.stat(tgt)
-        except OSError:
-            # Destination doesn't have this file?
-            continue
-        if st.st_ino == ts.st_ino:
-            continue
-        if st.st_dev != ts.st_dev:
-            raise Exception('Source and destination are on different devices')
-        if st.st_size != ts.st_size:
+        ts = linkfilter(tgt, st)
+        if not ts:
             continue
         targets.append((fn, ts.st_size))
+        df, ts = getdatafile(tgt)
+        if df:
+            targets.append((fn[:-1] + 'd', ts.st_size))
 
     return targets
 
 def relink(src, dst, files):
+    def relinkfile(src, dst):
+        bak = dst + '.bak'
+        os.rename(dst, bak)
+        try:
+            os.link(src, dst)
+        except OSError:
+            os.rename(bak, dst)
+            raise
+        os.remove(bak)
+
     CHUNKLEN = 65536
     relinked = 0
     savedbytes = 0
@@ -81,21 +105,22 @@
         if sin:
             continue
         try:
-            os.rename(tgt, tgt + '.bak')
-            try:
-                os.link(source, tgt)
-            except OSError:
-                os.rename(tgt + '.bak', tgt)
-                raise
+            relinkfile(source, tgt)
             print 'Relinked %s' % f
             relinked += 1
             savedbytes += sz
-            os.remove(tgt + '.bak')
         except OSError, inst:
             print '%s: %s' % (tgt, str(inst))
 
     print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes)
 
+try:
+    cfg = Config(sys.argv)
+except ConfigError, inst:
+    print str(inst)
+    usage()
+    sys.exit(1)
+
 src = os.path.join(cfg.src, '.hg')
 dst = os.path.join(cfg.dst, '.hg')
 candidates = collect(src)