[PATCH 2 of 5 STABLE] merge: new format for the state file

pierre-yves.david at ens-lyon.org pierre-yves.david at ens-lyon.org
Wed Feb 26 16:58:04 CST 2014


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1393382226 28800
#      Tue Feb 25 18:37:06 2014 -0800
# Branch stable
# Node ID d229b5d2096e7fdcc362fe22ff049ae257365c51
# Parent  a0c9e2941511a01624a0a75c000190f4ce3ed467
merge: new format for the state file

This new format will allow use to address common bugs with doing special merge
(graft, backout, rebase…) and record user choice during conflict resolution.

The format is open so we can add more record for future usage.

I'm keeping hexified version of node in this file to help human willing to debug
it by hand. I do not expect the overhead or oversize to be an issue.

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -3,20 +3,40 @@
 # Copyright 2006, 2007 Matt Mackall <mpm at selenic.com>
 #
 # 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
 
+    current 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
+    '''
+    statepath = "merge/state2"
     def __init__(self, repo):
         self._repo = repo
         self._dirty = False
         self._read()
     def reset(self, node=None):
@@ -27,29 +47,52 @@ class mergestate(object):
         self._dirty = False
     def _read(self):
         self._state = {}
         try:
             f = self._repo.opener(self.statepath)
-            for i, l in enumerate(f):
-                if i == 0:
-                    self._local = bin(l[:-1])
-                else:
-                    bits = l[:-1].split("\0")
+            for rtype, record in self._readrecords(f):
+                if rtype == 'L':
+                    self._local = bin(record)
+                elif rtype == "F":
+                    bits = record.split("\0")
                     self._state[bits[0]] = bits[1:]
+                elif not rtype.islower():
+                    raise util.Abort(_('unsupported merge state record:'
+                                       % rtype))
             f.close()
         except IOError, err:
             if err.errno != errno.ENOENT:
                 raise
         self._dirty = False
+    def _readrecords(self, f):
+        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
+            yield rtype, record
+
     def commit(self):
         if self._dirty:
+            records = []
+            records.append(("L", hex(self._local)))
+            for d, v in self._state.iteritems():
+                records.append(("F", "\0".join([d] + v)))
+            self._writerecords(records)
+            self._dirty = False
+    def _writerecords(self, records):
             f = self._repo.opener(self.statepath, "w")
-            f.write(hex(self._local) + "\n")
-            for d, v in self._state.iteritems():
-                f.write("\0".join([d] + v) + "\n")
+            for key, data in records:
+                assert len(key) == 1
+                format = ">sI%is" % len(data)
+                f.write(_pack(format, key, len(data), data))
             f.close()
-            self._dirty = False
     def add(self, fcl, fco, fca, fd):
         hash = util.sha1(fcl.path()).hexdigest()
         self._repo.opener.write("merge/" + hash, fcl.data())
         self._state[fd] = ['u', hash, fcl.path(), fca.path(),
                            hex(fca.filenode()), fco.path(), fcl.flags()]


More information about the Mercurial-devel mailing list