comparison mercurial/merge.py @ 20652:2a4871c2511d

merge: adds documentation to the mergestate class Document most the new function involved in the new serialisation process (and a few others).
author Pierre-Yves David <pierre-yves.david@fb.com>
date Wed, 05 Mar 2014 10:49:43 -0800
parents c1a52dd56eb4
children 89059c450c56
comparison
equal deleted inserted replaced
20651:c1a52dd56eb4 20652:2a4871c2511d
60 self._other = other 60 self._other = other
61 shutil.rmtree(self._repo.join("merge"), True) 61 shutil.rmtree(self._repo.join("merge"), True)
62 self._dirty = False 62 self._dirty = False
63 63
64 def _read(self): 64 def _read(self):
65 """Analyse each record content to restore a serialized state from disk
66
67 This function process "record" entry produced by the de-serialization
68 of on disk file.
69 """
65 self._state = {} 70 self._state = {}
66 records = self._readrecords() 71 records = self._readrecords()
67 for rtype, record in records: 72 for rtype, record in records:
68 if rtype == 'L': 73 if rtype == 'L':
69 self._local = bin(record) 74 self._local = bin(record)
76 raise util.Abort(_('unsupported merge state record:' 81 raise util.Abort(_('unsupported merge state record:'
77 % rtype)) 82 % rtype))
78 self._dirty = False 83 self._dirty = False
79 84
80 def _readrecords(self): 85 def _readrecords(self):
86 """Read merge state from disk and return a list of record (TYPE, data)
87
88 We read data from both V1 and Ve files decide which on to use.
89
90 V1 have been used by version prior to 2.9.1 and contains less data than
91 v2. We read both version and check if no data in v2 contradict one in
92 v1. If there is not contradiction we can safely assume that both v1
93 and v2 were written at the same time and use the extract data in v2. If
94 there is contradiction we ignore v2 content as we assume an old version
95 of Mercurial have over written the mergstate file and left an old v2
96 file around.
97
98 returns list of record [(TYPE, data), ...]"""
81 v1records = self._readrecordsv1() 99 v1records = self._readrecordsv1()
82 v2records = self._readrecordsv2() 100 v2records = self._readrecordsv2()
83 oldv2 = set() # old format version of v2 record 101 oldv2 = set() # old format version of v2 record
84 for rec in v2records: 102 for rec in v2records:
85 if rec[0] == 'L': 103 if rec[0] == 'L':
105 return v1records 123 return v1records
106 else: 124 else:
107 return v2records 125 return v2records
108 126
109 def _readrecordsv1(self): 127 def _readrecordsv1(self):
128 """read on disk merge state for version 1 file
129
130 returns list of record [(TYPE, data), ...]
131
132 Note: the "F" data from this file are one entry short
133 (no "other file node" entry)
134 """
110 records = [] 135 records = []
111 try: 136 try:
112 f = self._repo.opener(self.statepathv1) 137 f = self._repo.opener(self.statepathv1)
113 for i, l in enumerate(f): 138 for i, l in enumerate(f):
114 if i == 0: 139 if i == 0:
120 if err.errno != errno.ENOENT: 145 if err.errno != errno.ENOENT:
121 raise 146 raise
122 return records 147 return records
123 148
124 def _readrecordsv2(self): 149 def _readrecordsv2(self):
150 """read on disk merge state for version 2 file
151
152 returns list of record [(TYPE, data), ...]
153 """
125 records = [] 154 records = []
126 try: 155 try:
127 f = self._repo.opener(self.statepathv2) 156 f = self._repo.opener(self.statepathv2)
128 data = f.read() 157 data = f.read()
129 off = 0 158 off = 0
141 if err.errno != errno.ENOENT: 170 if err.errno != errno.ENOENT:
142 raise 171 raise
143 return records 172 return records
144 173
145 def commit(self): 174 def commit(self):
175 """Write current state on disk (if necessary)"""
146 if self._dirty: 176 if self._dirty:
147 records = [] 177 records = []
148 records.append(("L", hex(self._local))) 178 records.append(("L", hex(self._local)))
149 records.append(("O", hex(self._other))) 179 records.append(("O", hex(self._other)))
150 for d, v in self._state.iteritems(): 180 for d, v in self._state.iteritems():
151 records.append(("F", "\0".join([d] + v))) 181 records.append(("F", "\0".join([d] + v)))
152 self._writerecords(records) 182 self._writerecords(records)
153 self._dirty = False 183 self._dirty = False
154 184
155 def _writerecords(self, records): 185 def _writerecords(self, records):
186 """Write current state on disk (both v1 and v2)"""
156 self._writerecordsv1(records) 187 self._writerecordsv1(records)
157 self._writerecordsv2(records) 188 self._writerecordsv2(records)
158 189
159 def _writerecordsv1(self, records): 190 def _writerecordsv1(self, records):
191 """Write current state on disk in a version 1 file"""
160 f = self._repo.opener(self.statepathv1, "w") 192 f = self._repo.opener(self.statepathv1, "w")
161 irecords = iter(records) 193 irecords = iter(records)
162 lrecords = irecords.next() 194 lrecords = irecords.next()
163 assert lrecords[0] == 'L' 195 assert lrecords[0] == 'L'
164 f.write(hex(self._local) + "\n") 196 f.write(hex(self._local) + "\n")
166 if rtype == "F": 198 if rtype == "F":
167 f.write("%s\n" % _droponode(data)) 199 f.write("%s\n" % _droponode(data))
168 f.close() 200 f.close()
169 201
170 def _writerecordsv2(self, records): 202 def _writerecordsv2(self, records):
203 """Write current state on disk in a version 2 file"""
171 f = self._repo.opener(self.statepathv2, "w") 204 f = self._repo.opener(self.statepathv2, "w")
172 for key, data in records: 205 for key, data in records:
173 assert len(key) == 1 206 assert len(key) == 1
174 format = ">sI%is" % len(data) 207 format = ">sI%is" % len(data)
175 f.write(_pack(format, key, len(data), data)) 208 f.write(_pack(format, key, len(data), data))
176 f.close() 209 f.close()
177 210
178 def add(self, fcl, fco, fca, fd): 211 def add(self, fcl, fco, fca, fd):
212 """add a new (potentially?) conflicting file the merge state
213 fcl: file context for local,
214 fco: file context for remote,
215 fca: file context for ancestors,
216 fd: file path of the resulting merge.
217
218 note: also write the local version to the `.hg/merge` directory.
219 """
179 hash = util.sha1(fcl.path()).hexdigest() 220 hash = util.sha1(fcl.path()).hexdigest()
180 self._repo.opener.write("merge/" + hash, fcl.data()) 221 self._repo.opener.write("merge/" + hash, fcl.data())
181 self._state[fd] = ['u', hash, fcl.path(), 222 self._state[fd] = ['u', hash, fcl.path(),
182 fca.path(), hex(fca.filenode()), 223 fca.path(), hex(fca.filenode()),
183 fco.path(), hex(fco.filenode()), 224 fco.path(), hex(fco.filenode()),
202 def mark(self, dfile, state): 243 def mark(self, dfile, state):
203 self._state[dfile][0] = state 244 self._state[dfile][0] = state
204 self._dirty = True 245 self._dirty = True
205 246
206 def resolve(self, dfile, wctx): 247 def resolve(self, dfile, wctx):
248 """rerun merge process for file path `dfile`"""
207 if self[dfile] == 'r': 249 if self[dfile] == 'r':
208 return 0 250 return 0
209 stateentry = self._state[dfile] 251 stateentry = self._state[dfile]
210 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry 252 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
211 octx = self._repo[self._other] 253 octx = self._repo[self._other]