Mercurial > hg
changeset 20590:2b7d54e929b4 stable
merge: introduce new format for the state file
This new format will allow us to address common bugs while 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.
This file still store hexified version of node to help human willing to debug
it by hand. The overhead or oversize are not expected be an issue.
The old format is still used. It will be written to disk along side the newer
format. And at parse time we detect if the data from old version of the
mergestate are different from the one in the new version file. If its the same,
both have most likely be written at the same time and you can trust the extra
data from the new file. If it differs, the old file have been written by an
older version of mercurial that did not knew about the new file. In that case we
use the content of the old file.
author | Pierre-Yves David <pierre-yves.david@fb.com> |
---|---|
date | Tue, 25 Feb 2014 18:37:06 -0800 |
parents | 31993cd23b11 |
children | 02c60e380fd0 |
files | mercurial/merge.py |
diffstat | 1 files changed, 70 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- 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())