[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