[Checkins] SVN: zope.app.fssync/branches/jim-hack/ Added merge command. It allows merging of changes from one checkout to

Amos Latteier amos at latteier.com
Wed Feb 11 15:46:48 EST 2009


Log message for revision 96452:
  Added merge command. It allows merging of changes from one checkout to 
  another.
  

Changed:
  U   zope.app.fssync/branches/jim-hack/buildout.cfg
  U   zope.app.fssync/branches/jim-hack/src/zope/app/fssync/fssync.py
  U   zope.app.fssync/branches/jim-hack/src/zope/app/fssync/main.py
  A   zope.app.fssync/branches/jim-hack/src/zope/app/fssync/merge.py

-=-
Modified: zope.app.fssync/branches/jim-hack/buildout.cfg
===================================================================
--- zope.app.fssync/branches/jim-hack/buildout.cfg	2009-02-11 18:48:43 UTC (rev 96451)
+++ zope.app.fssync/branches/jim-hack/buildout.cfg	2009-02-11 20:46:48 UTC (rev 96452)
@@ -1,5 +1,5 @@
 [buildout]
-develop = .
+develop = . ../z4m-zsync-branch/zope.fssync
 parts = demo test zsync zsync-demo-server
 index = http://pypi.python.org/simple
 extends = http://download.zope.org/zope3.4/3.4.0/versions.cfg

Modified: zope.app.fssync/branches/jim-hack/src/zope/app/fssync/fssync.py
===================================================================
--- zope.app.fssync/branches/jim-hack/src/zope/app/fssync/fssync.py	2009-02-11 18:48:43 UTC (rev 96451)
+++ zope.app.fssync/branches/jim-hack/src/zope/app/fssync/fssync.py	2009-02-11 20:46:48 UTC (rev 96452)
@@ -2,14 +2,14 @@
 #
 # Copyright (c) 2003 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
-# 
+#
 ##############################################################################
 """Highest-level classes to support filesystem synchronization:
 
@@ -43,6 +43,7 @@
 from zope.fssync.snarf import Snarfer, Unsnarfer
 from zope.app.fssync.passwd import PasswordManager
 from zope.app.fssync.ssh import SSHConnection
+import zope.app.fssync.merge
 
 if sys.platform[:3].lower() == "win":
     DEV_NULL = r".\nul"
@@ -209,7 +210,7 @@
         assert self.rooturl
         if not path.endswith("/"):
             path += "/"
-        path = urllib.quote(path)  
+        path = urllib.quote(path)
         path += view
         if self.roottype == "https":
             conn = httplib.HTTPSConnection(self.host_port)
@@ -217,7 +218,7 @@
             conn = SSHConnection(self.host_port, self.user_passwd)
         else:
             conn = httplib.HTTPConnection(self.host_port)
-         
+
         if datasource is None:
             conn.putrequest("GET", path)
         else:
@@ -232,7 +233,7 @@
             tmp = tempfile.TemporaryFile('w+b')
             datasource(tmp)
             conn.putheader("Content-Length", str(tmp.tell()))
-            
+
         if self.user_passwd:
             if ":" not in self.user_passwd:
                 auth = self.getToken(self.roottype,
@@ -245,7 +246,7 @@
         conn.putheader("Connection", "close")
         conn.endheaders()
         if datasource is not None:
-            #XXX If chunking works again, replace the following lines with 
+            #XXX If chunking works again, replace the following lines with
             # datasource(PretendStream(conn))
             # conn.send("0\r\n\r\n")
             tmp.seek(0)
@@ -253,8 +254,8 @@
             while data:
                 conn.send(data)
                 data = tmp.read(1<<16)
-            tmp.close()   
-                
+            tmp.close()
+
         response = conn.getresponse()
         if response.status != 200:
             raise Error("HTTP error %s (%s); error document:\n%s",
@@ -463,6 +464,35 @@
         finally:
             fp.close()
 
+    def merge(self, args):
+        source = args[0]
+        if len(args) == 1:
+            target = os.curdir
+        else:
+            target = args[1]
+
+        # make sure that we're merging from compatible directories
+        if not self.metadata.getentry(target):
+            target = join(target, 'root')
+        target_entry = self.metadata.getentry(target)
+        if not target_entry:
+            print 'Target must be a fssync checkout directory'
+            return
+        if not self.metadata.getentry(source):
+            source = join(source, 'root')
+        source_entry = self.metadata.getentry(source)
+        if not source_entry:
+            print 'Source must be a fssync checkout directory'
+            return
+        if source_entry[u'id'] != target_entry[u'id']:
+            print 'Cannot merge from %s to %s' % (source_entry[u'id'],
+                                                  target_entry[u'id'])
+            return
+        
+        zope.app.fssync.merge.merge(source, target, self)
+        print 'All done.'
+
+
     def merge_snarffile(self, fp, localdir, tail):
         uns = Unsnarfer(fp)
         tmpdir = tempfile.mktemp()
@@ -533,7 +563,7 @@
     def reporter(self, msg):
         if msg[0] not in "/*":
             print msg.encode('utf-8') # uo: is encode needed here?
-    
+
     def diff(self, target, mode=1, diffopts="", need_original=True):
         assert mode == 1, "modes 2 and 3 are not yet supported"
         entry = self.metadata.getentry(target)

Modified: zope.app.fssync/branches/jim-hack/src/zope/app/fssync/main.py
===================================================================
--- zope.app.fssync/branches/jim-hack/src/zope/app/fssync/main.py	2009-02-11 18:48:43 UTC (rev 96451)
+++ zope.app.fssync/branches/jim-hack/src/zope/app/fssync/main.py	2009-02-11 20:46:48 UTC (rev 96452)
@@ -3,14 +3,14 @@
 #
 # Copyright (c) 2003 Zope Corporation and Contributors.
 # All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
-# 
+#
 ##############################################################################
 """Filesystem synchronization utility for Zope 3.
 
@@ -352,6 +352,19 @@
     fs = FSSync()
     fs.multiple(args, fs.revert)
 
+def merge(opts, args):
+    """%(program)s merge [TARGETDIR] SOURCEDIR
+
+    Merge changes from one sandbox directory to another. If two
+    directories are specified then the first one is the target and the
+    second is the source. If only one directory is specified, then the
+    target directory is assumed to the be current sandbox.
+    """
+    if len(args) not in (1,2):
+        raise Usage('Merge requires one or two arguments')
+    fs = FSSync()
+    fs.merge(args)
+
 def extract_message(opts, cmd):
     L = []
     message = None
@@ -387,6 +400,7 @@
     (diff,     "di",      "bBcC:iNuU:", "brief context= unified="),
     (login,    "",        "u:",         "user="),
     (logout,   "",        "u:",         "user="),
+    (merge,    "",        "",           ""),
     (mkdir,    "",        "",           ""),
     (remove,   "del delete rm", "",     ""),
     (resolve,  "",        "",           ""),

Added: zope.app.fssync/branches/jim-hack/src/zope/app/fssync/merge.py
===================================================================
--- zope.app.fssync/branches/jim-hack/src/zope/app/fssync/merge.py	                        (rev 0)
+++ zope.app.fssync/branches/jim-hack/src/zope/app/fssync/merge.py	2009-02-11 20:46:48 UTC (rev 96452)
@@ -0,0 +1,134 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+Merge from one fssync repository to another
+
+This differs from merging from Zope in that changes from the source
+are not considered changes to the orginal.
+"""
+import os
+import os.path
+
+import zope.fssync.fsutil
+import zope.fssync.copier
+
+class MergeCopier(zope.fssync.copier.ObjectCopier):
+    """
+    Copies from a checkout to another checkout, but doesn't add sync
+    entries in the destination checkout, since they should already be
+    present.
+    """
+    def _copyspecials(self, source, target, getwhat):
+        src = getwhat(source)
+        if os.path.isdir(src):
+            dst = getwhat(target)
+            zope.fssync.fsutil.ensuredir(dst)
+            copier = MergeCopier(self.sync)
+            for name in self.sync.metadata.getnames(src):
+                copier.copy(os.path.join(src, name), os.path.join(dst, name))
+            self.sync.metadata.flush()
+    
+    def _syncadd(self, target, type, factory):
+        pass
+
+
+def same_type(path1, path2):
+    if (os.path.isfile(path1) == os.path.isfile(path2) or
+        os.path.isdir(path1) == os.path.isdir(path2)):
+        return True
+    return False
+
+def same_file(path1, path2):
+    chunk_size = 32768
+    f1 = open(path1)
+    f2 = open(path2)
+    while True:
+        s1 = f1.read(chunk_size)
+        s2 = f2.read(chunk_size)
+        if s1 != s2:
+            return False
+        if not s1:
+            break
+    f1.close()
+    f2.close()
+    return True
+
+def same_entries(path1, path2, metadata):
+    names1 = metadata.getnames(path1)
+    names2 = metadata.getnames(path2)
+    if names1 != names2:
+        return False
+    for name in names1:
+        f1 = os.path.join(path1, name)
+        f2 = os.path.join(path2, name)
+        if not same_file(f1, f2):
+            return False
+    return True
+        
+def same_specials(path1, path2, metadata):
+    extra1 = zope.fssync.fsutil.getextra(path1)
+    extra2 = zope.fssync.fsutil.getextra(path2)
+    if os.path.exists(extra1) != os.path.exists(extra2):
+        return False
+    if os.path.exists(extra1) and os.path.exists(extra2):
+        if not same_entries(extra1, extra2, metadata):
+            return False
+
+    annotations1 = zope.fssync.fsutil.getannotations(path1)
+    annotations2 = zope.fssync.fsutil.getannotations(path2)
+    if os.path.exists(annotations1) != os.path.exists(annotations2):
+        return False
+    if os.path.exists(annotations1) and os.path.exists(annotations2):
+        if not same_entries(annotations1, annotations2, metadata):
+            return False
+
+    return True
+
+
+
+def merge(source, target, sync):
+    """
+    Merge difference from source into target. Treat differences as
+    local changes in target. Don't delete anything from target, only
+    add things from source. Only merge stuff from target if it is
+    actually in the repository (thus don't copy temp files, svn files,
+    etc.)
+    """
+    metadata = sync.metadata
+    object_copier = zope.fssync.copier.ObjectCopier(sync)
+    merge_copier = MergeCopier(sync)
+    
+    for root, dirs, files in os.walk(source):
+        if '@@Zope' in dirs:
+            dirs.remove('@@Zope')
+        for filename in (files + ['']):
+            source_path = os.path.join(root, filename)
+            if metadata.getentry(source_path):
+                directory = root[len(source) + 1:]
+                target_path = os.path.join(target, directory, filename)
+                if not metadata.getentry(target_path):
+                    object_copier.copy(source_path, target_path, False)
+                elif not same_type(source_path, target_path):
+                    print 'C %s' % target_path
+                    metadata.getentry(target_path)['conflict'] = True
+                    metadata.flush()
+                elif os.path.isfile(source_path):
+                    if (not same_file(source_path, target_path) or
+                        not same_specials(source_path, target_path, metadata)):
+                        print 'M %s' % target_path
+                        merge_copier.copy(source_path, target_path, False)
+                elif os.path.isdir(source_path):
+                    if not same_specials(source_path, target_path, metadata):
+                        print 'M %s' % target_path
+                        merge_copier.copy(source_path, target_path, False)



More information about the Checkins mailing list