comparison mercurial/merge.py @ 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
comparison
equal deleted inserted replaced
20589:31993cd23b11 20590:2b7d54e929b4
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 # 4 #
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 import struct
9
8 from node import nullid, nullrev, hex, bin 10 from node import nullid, nullrev, hex, bin
9 from i18n import _ 11 from i18n import _
10 from mercurial import obsolete 12 from mercurial import obsolete
11 import error, util, filemerge, copies, subrepo, worker, dicthelpers 13 import error, util, filemerge, copies, subrepo, worker, dicthelpers
12 import errno, os, shutil 14 import errno, os, shutil
13 15
16 _pack = struct.pack
17 _unpack = struct.unpack
18
14 class mergestate(object): 19 class mergestate(object):
15 '''track 3-way merge state of individual files''' 20 '''track 3-way merge state of individual files
16 statepath = "merge/state" 21
22 it is stored on disk when needed. Two file are used, one with an old
23 format, one with a new format. Both contains similar data, but the new
24 format can store new kind of field.
25
26 Current new format is a list of arbitrary record of the form:
27
28 [type][length][content]
29
30 Type is a single character, length is a 4 bytes integer, content is an
31 arbitrary suites of bytes of lenght `length`.
32
33 Type should be a letter. Capital letter are mandatory record, Mercurial
34 should abort if they are unknown. lower case record can be safely ignored.
35
36 Currently known record:
37
38 L: the node of the "local" part of the merge (hexified version)
39 F: a file to be merged entry
40 '''
41 statepathv1 = "merge/state"
42 statepathv2 = "merge/state2"
17 def __init__(self, repo): 43 def __init__(self, repo):
18 self._repo = repo 44 self._repo = repo
19 self._dirty = False 45 self._dirty = False
20 self._read() 46 self._read()
21 def reset(self, node=None): 47 def reset(self, node=None):
36 elif not rtype.islower(): 62 elif not rtype.islower():
37 raise util.Abort(_('unsupported merge state record:' 63 raise util.Abort(_('unsupported merge state record:'
38 % rtype)) 64 % rtype))
39 self._dirty = False 65 self._dirty = False
40 def _readrecords(self): 66 def _readrecords(self):
67 v1records = self._readrecordsv1()
68 v2records = self._readrecordsv2()
69 allv2 = set(v2records)
70 for rev in v1records:
71 if rev not in allv2:
72 # v1 file is newer than v2 file, use it
73 return v1records
74 else:
75 return v2records
76 def _readrecordsv1(self):
41 records = [] 77 records = []
42 try: 78 try:
43 f = self._repo.opener(self.statepath) 79 f = self._repo.opener(self.statepathv1)
44 for i, l in enumerate(f): 80 for i, l in enumerate(f):
45 if i == 0: 81 if i == 0:
46 records.append(('L', l[:-1])) 82 records.append(('L', l[:-1]))
47 else: 83 else:
48 records.append(('F', l[:-1])) 84 records.append(('F', l[:-1]))
85 f.close()
86 except IOError, err:
87 if err.errno != errno.ENOENT:
88 raise
89 return records
90 def _readrecordsv2(self):
91 records = []
92 try:
93 f = self._repo.opener(self.statepathv2)
94 data = f.read()
95 off = 0
96 end = len(data)
97 while off < end:
98 rtype = data[off]
99 off += 1
100 lenght = _unpack('>I', data[off:(off + 4)])[0]
101 off += 4
102 record = data[off:(off + lenght)]
103 off += lenght
104 records.append((rtype, record))
49 f.close() 105 f.close()
50 except IOError, err: 106 except IOError, err:
51 if err.errno != errno.ENOENT: 107 if err.errno != errno.ENOENT:
52 raise 108 raise
53 return records 109 return records
58 for d, v in self._state.iteritems(): 114 for d, v in self._state.iteritems():
59 records.append(("F", "\0".join([d] + v))) 115 records.append(("F", "\0".join([d] + v)))
60 self._writerecords(records) 116 self._writerecords(records)
61 self._dirty = False 117 self._dirty = False
62 def _writerecords(self, records): 118 def _writerecords(self, records):
63 f = self._repo.opener(self.statepath, "w") 119 self._writerecordsv1(records)
120 self._writerecordsv2(records)
121 def _writerecordsv1(self, records):
122 f = self._repo.opener(self.statepathv1, "w")
64 irecords = iter(records) 123 irecords = iter(records)
65 lrecords = irecords.next() 124 lrecords = irecords.next()
66 assert lrecords[0] == 'L' 125 assert lrecords[0] == 'L'
67 f.write(hex(self._local) + "\n") 126 f.write(hex(self._local) + "\n")
68 for rtype, data in irecords: 127 for rtype, data in irecords:
69 if rtype == "F": 128 if rtype == "F":
70 f.write("%s\n" % data) 129 f.write("%s\n" % data)
130 f.close()
131 def _writerecordsv2(self, records):
132 f = self._repo.opener(self.statepathv2, "w")
133 for key, data in records:
134 assert len(key) == 1
135 format = ">sI%is" % len(data)
136 f.write(_pack(format, key, len(data), data))
71 f.close() 137 f.close()
72 def add(self, fcl, fco, fca, fd): 138 def add(self, fcl, fco, fca, fd):
73 hash = util.sha1(fcl.path()).hexdigest() 139 hash = util.sha1(fcl.path()).hexdigest()
74 self._repo.opener.write("merge/" + hash, fcl.data()) 140 self._repo.opener.write("merge/" + hash, fcl.data())
75 self._state[fd] = ['u', hash, fcl.path(), fca.path(), 141 self._state[fd] = ['u', hash, fcl.path(), fca.path(),