mercurial/merge.py
branchstable
changeset 20590 2b7d54e929b4
parent 20589 31993cd23b11
child 20591 02c60e380fd0
--- a/mercurial/merge.py	Thu Feb 27 12:59:41 2014 -0800
+++ b/mercurial/merge.py	Tue Feb 25 18:37:06 2014 -0800
@@ -5,15 +5,41 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+import struct
+
 from node import nullid, nullrev, hex, bin
 from i18n import _
 from mercurial import obsolete
 import error, util, filemerge, copies, subrepo, worker, dicthelpers
 import errno, os, shutil
 
+_pack = struct.pack
+_unpack = struct.unpack
+
 class mergestate(object):
-    '''track 3-way merge state of individual files'''
-    statepath = "merge/state"
+    '''track 3-way merge state of individual files
+
+    it is stored on disk when needed. Two file are used, one with an old
+    format, one with a new format. Both contains similar data, but the new
+    format can store new kind of field.
+
+    Current new format is a list of arbitrary record of the form:
+
+        [type][length][content]
+
+    Type is a single character, length is a 4 bytes integer, content is an
+    arbitrary suites of bytes of lenght `length`.
+
+    Type should be a letter. Capital letter are mandatory record, Mercurial
+    should abort if they are unknown. lower case record can be safely ignored.
+
+    Currently known record:
+
+    L: the node of the "local" part of the merge (hexified version)
+    F: a file to be merged entry
+    '''
+    statepathv1 = "merge/state"
+    statepathv2 = "merge/state2"
     def __init__(self, repo):
         self._repo = repo
         self._dirty = False
@@ -38,9 +64,19 @@
                                    % rtype))
         self._dirty = False
     def _readrecords(self):
+        v1records = self._readrecordsv1()
+        v2records = self._readrecordsv2()
+        allv2 = set(v2records)
+        for rev in v1records:
+            if rev not in allv2:
+                # v1 file is newer than v2 file, use it
+                return v1records
+        else:
+            return v2records
+    def _readrecordsv1(self):
         records = []
         try:
-            f = self._repo.opener(self.statepath)
+            f = self._repo.opener(self.statepathv1)
             for i, l in enumerate(f):
                 if i == 0:
                     records.append(('L', l[:-1]))
@@ -51,6 +87,26 @@
             if err.errno != errno.ENOENT:
                 raise
         return records
+    def _readrecordsv2(self):
+        records = []
+        try:
+            f = self._repo.opener(self.statepathv2)
+            data = f.read()
+            off = 0
+            end = len(data)
+            while off < end:
+                rtype = data[off]
+                off += 1
+                lenght = _unpack('>I', data[off:(off + 4)])[0]
+                off += 4
+                record = data[off:(off + lenght)]
+                off += lenght
+                records.append((rtype, record))
+            f.close()
+        except IOError, err:
+            if err.errno != errno.ENOENT:
+                raise
+        return records
     def commit(self):
         if self._dirty:
             records = []
@@ -60,7 +116,10 @@
             self._writerecords(records)
             self._dirty = False
     def _writerecords(self, records):
-        f = self._repo.opener(self.statepath, "w")
+        self._writerecordsv1(records)
+        self._writerecordsv2(records)
+    def _writerecordsv1(self, records):
+        f = self._repo.opener(self.statepathv1, "w")
         irecords = iter(records)
         lrecords = irecords.next()
         assert lrecords[0] == 'L'
@@ -69,6 +128,13 @@
             if rtype == "F":
                 f.write("%s\n" % data)
         f.close()
+    def _writerecordsv2(self, records):
+        f = self._repo.opener(self.statepathv2, "w")
+        for key, data in records:
+            assert len(key) == 1
+            format = ">sI%is" % len(data)
+            f.write(_pack(format, key, len(data), data))
+        f.close()
     def add(self, fcl, fco, fca, fd):
         hash = util.sha1(fcl.path()).hexdigest()
         self._repo.opener.write("merge/" + hash, fcl.data())