Show More
@@ -47,6 +47,20 b' def _droponode(data):' | |||||
47 | bits = bits[:-2] + bits[-1:] |
|
47 | bits = bits[:-2] + bits[-1:] | |
48 | return '\0'.join(bits) |
|
48 | return '\0'.join(bits) | |
49 |
|
49 | |||
|
50 | # Merge state record types. See ``mergestate`` docs for more. | |||
|
51 | RECORD_LOCAL = b'L' | |||
|
52 | RECORD_OTHER = b'O' | |||
|
53 | RECORD_MERGED = b'F' | |||
|
54 | RECORD_CHANGEDELETE_CONFLICT = b'C' | |||
|
55 | RECORD_MERGE_DRIVER_MERGE = b'D' | |||
|
56 | RECORD_PATH_CONFLICT = b'P' | |||
|
57 | RECORD_MERGE_DRIVER_STATE = b'm' | |||
|
58 | RECORD_FILE_VALUES = b'f' | |||
|
59 | RECORD_LABELS = b'l' | |||
|
60 | RECORD_OVERRIDE = b't' | |||
|
61 | RECORD_UNSUPPORTED_MANDATORY = b'X' | |||
|
62 | RECORD_UNSUPPORTED_ADVISORY = b'x' | |||
|
63 | ||||
50 | class mergestate(object): |
|
64 | class mergestate(object): | |
51 | '''track 3-way merge state of individual files |
|
65 | '''track 3-way merge state of individual files | |
52 |
|
66 | |||
@@ -158,11 +172,11 b' class mergestate(object):' | |||||
158 | unsupported = set() |
|
172 | unsupported = set() | |
159 | records = self._readrecords() |
|
173 | records = self._readrecords() | |
160 | for rtype, record in records: |
|
174 | for rtype, record in records: | |
161 |
if rtype == |
|
175 | if rtype == RECORD_LOCAL: | |
162 | self._local = bin(record) |
|
176 | self._local = bin(record) | |
163 |
elif rtype == |
|
177 | elif rtype == RECORD_OTHER: | |
164 | self._other = bin(record) |
|
178 | self._other = bin(record) | |
165 |
elif rtype == |
|
179 | elif rtype == RECORD_MERGE_DRIVER_STATE: | |
166 | bits = record.split('\0', 1) |
|
180 | bits = record.split('\0', 1) | |
167 | mdstate = bits[1] |
|
181 | mdstate = bits[1] | |
168 | if len(mdstate) != 1 or mdstate not in 'ums': |
|
182 | if len(mdstate) != 1 or mdstate not in 'ums': | |
@@ -171,10 +185,11 b' class mergestate(object):' | |||||
171 |
|
185 | |||
172 | self._readmergedriver = bits[0] |
|
186 | self._readmergedriver = bits[0] | |
173 | self._mdstate = mdstate |
|
187 | self._mdstate = mdstate | |
174 | elif rtype in 'FDCP': |
|
188 | elif rtype in (RECORD_MERGED, RECORD_CHANGEDELETE_CONFLICT, | |
|
189 | RECORD_PATH_CONFLICT, RECORD_MERGE_DRIVER_MERGE): | |||
175 | bits = record.split('\0') |
|
190 | bits = record.split('\0') | |
176 | self._state[bits[0]] = bits[1:] |
|
191 | self._state[bits[0]] = bits[1:] | |
177 |
elif rtype == |
|
192 | elif rtype == RECORD_FILE_VALUES: | |
178 | filename, rawextras = record.split('\0', 1) |
|
193 | filename, rawextras = record.split('\0', 1) | |
179 | extraparts = rawextras.split('\0') |
|
194 | extraparts = rawextras.split('\0') | |
180 | extras = {} |
|
195 | extras = {} | |
@@ -184,7 +199,7 b' class mergestate(object):' | |||||
184 | i += 2 |
|
199 | i += 2 | |
185 |
|
200 | |||
186 | self._stateextras[filename] = extras |
|
201 | self._stateextras[filename] = extras | |
187 |
elif rtype == |
|
202 | elif rtype == RECORD_LABELS: | |
188 | labels = record.split('\0', 2) |
|
203 | labels = record.split('\0', 2) | |
189 | self._labels = [l for l in labels if len(l) > 0] |
|
204 | self._labels = [l for l in labels if len(l) > 0] | |
190 | elif not rtype.islower(): |
|
205 | elif not rtype.islower(): | |
@@ -218,12 +233,12 b' class mergestate(object):' | |||||
218 | # we have to infer the "other" changeset of the merge |
|
233 | # we have to infer the "other" changeset of the merge | |
219 | # we cannot do better than that with v1 of the format |
|
234 | # we cannot do better than that with v1 of the format | |
220 | mctx = self._repo[None].parents()[-1] |
|
235 | mctx = self._repo[None].parents()[-1] | |
221 |
v1records.append(( |
|
236 | v1records.append((RECORD_OTHER, mctx.hex())) | |
222 | # add place holder "other" file node information |
|
237 | # add place holder "other" file node information | |
223 | # nobody is using it yet so we do no need to fetch the data |
|
238 | # nobody is using it yet so we do no need to fetch the data | |
224 | # if mctx was wrong `mctx[bits[-2]]` may fails. |
|
239 | # if mctx was wrong `mctx[bits[-2]]` may fails. | |
225 | for idx, r in enumerate(v1records): |
|
240 | for idx, r in enumerate(v1records): | |
226 |
if r[0] == |
|
241 | if r[0] == RECORD_MERGED: | |
227 | bits = r[1].split('\0') |
|
242 | bits = r[1].split('\0') | |
228 | bits.insert(-2, '') |
|
243 | bits.insert(-2, '') | |
229 | v1records[idx] = (r[0], '\0'.join(bits)) |
|
244 | v1records[idx] = (r[0], '\0'.join(bits)) | |
@@ -232,11 +247,11 b' class mergestate(object):' | |||||
232 | def _v1v2match(self, v1records, v2records): |
|
247 | def _v1v2match(self, v1records, v2records): | |
233 | oldv2 = set() # old format version of v2 record |
|
248 | oldv2 = set() # old format version of v2 record | |
234 | for rec in v2records: |
|
249 | for rec in v2records: | |
235 |
if rec[0] == |
|
250 | if rec[0] == RECORD_LOCAL: | |
236 | oldv2.add(rec) |
|
251 | oldv2.add(rec) | |
237 |
elif rec[0] == |
|
252 | elif rec[0] == RECORD_MERGED: | |
238 | # drop the onode data (not contained in v1) |
|
253 | # drop the onode data (not contained in v1) | |
239 |
oldv2.add(( |
|
254 | oldv2.add((RECORD_MERGED, _droponode(rec[1]))) | |
240 | for rec in v1records: |
|
255 | for rec in v1records: | |
241 | if rec not in oldv2: |
|
256 | if rec not in oldv2: | |
242 | return False |
|
257 | return False | |
@@ -256,9 +271,9 b' class mergestate(object):' | |||||
256 | f = self._repo.vfs(self.statepathv1) |
|
271 | f = self._repo.vfs(self.statepathv1) | |
257 | for i, l in enumerate(f): |
|
272 | for i, l in enumerate(f): | |
258 | if i == 0: |
|
273 | if i == 0: | |
259 |
records.append(( |
|
274 | records.append((RECORD_LOCAL, l[:-1])) | |
260 | else: |
|
275 | else: | |
261 |
records.append(( |
|
276 | records.append((RECORD_MERGED, l[:-1])) | |
262 | f.close() |
|
277 | f.close() | |
263 | except IOError as err: |
|
278 | except IOError as err: | |
264 | if err.errno != errno.ENOENT: |
|
279 | if err.errno != errno.ENOENT: | |
@@ -296,7 +311,7 b' class mergestate(object):' | |||||
296 | off += 4 |
|
311 | off += 4 | |
297 | record = data[off:(off + length)] |
|
312 | record = data[off:(off + length)] | |
298 | off += length |
|
313 | off += length | |
299 |
if rtype == |
|
314 | if rtype == RECORD_OVERRIDE: | |
300 | rtype, record = record[0:1], record[1:] |
|
315 | rtype, record = record[0:1], record[1:] | |
301 | records.append((rtype, record)) |
|
316 | records.append((rtype, record)) | |
302 | f.close() |
|
317 | f.close() | |
@@ -359,10 +374,10 b' class mergestate(object):' | |||||
359 |
|
374 | |||
360 | def _makerecords(self): |
|
375 | def _makerecords(self): | |
361 | records = [] |
|
376 | records = [] | |
362 |
records.append(( |
|
377 | records.append((RECORD_LOCAL, hex(self._local))) | |
363 |
records.append(( |
|
378 | records.append((RECORD_OTHER, hex(self._other))) | |
364 | if self.mergedriver: |
|
379 | if self.mergedriver: | |
365 |
records.append(( |
|
380 | records.append((RECORD_MERGE_DRIVER_STATE, '\0'.join([ | |
366 | self.mergedriver, self._mdstate]))) |
|
381 | self.mergedriver, self._mdstate]))) | |
367 | # Write out state items. In all cases, the value of the state map entry |
|
382 | # Write out state items. In all cases, the value of the state map entry | |
368 | # is written as the contents of the record. The record type depends on |
|
383 | # is written as the contents of the record. The record type depends on | |
@@ -372,27 +387,32 b' class mergestate(object):' | |||||
372 | for filename, v in self._state.iteritems(): |
|
387 | for filename, v in self._state.iteritems(): | |
373 | if v[0] == 'd': |
|
388 | if v[0] == 'd': | |
374 | # Driver-resolved merge. These are stored in 'D' records. |
|
389 | # Driver-resolved merge. These are stored in 'D' records. | |
375 | records.append(('D', '\0'.join([filename] + v))) |
|
390 | records.append((RECORD_MERGE_DRIVER_MERGE, | |
|
391 | '\0'.join([filename] + v))) | |||
376 | elif v[0] in ('pu', 'pr'): |
|
392 | elif v[0] in ('pu', 'pr'): | |
377 | # Path conflicts. These are stored in 'P' records. The current |
|
393 | # Path conflicts. These are stored in 'P' records. The current | |
378 | # resolution state ('pu' or 'pr') is stored within the record. |
|
394 | # resolution state ('pu' or 'pr') is stored within the record. | |
379 | records.append(('P', '\0'.join([filename] + v))) |
|
395 | records.append((RECORD_PATH_CONFLICT, | |
|
396 | '\0'.join([filename] + v))) | |||
380 | elif v[1] == nullhex or v[6] == nullhex: |
|
397 | elif v[1] == nullhex or v[6] == nullhex: | |
381 | # Change/Delete or Delete/Change conflicts. These are stored in |
|
398 | # Change/Delete or Delete/Change conflicts. These are stored in | |
382 | # 'C' records. v[1] is the local file, and is nullhex when the |
|
399 | # 'C' records. v[1] is the local file, and is nullhex when the | |
383 | # file is deleted locally ('dc'). v[6] is the remote file, and |
|
400 | # file is deleted locally ('dc'). v[6] is the remote file, and | |
384 | # is nullhex when the file is deleted remotely ('cd'). |
|
401 | # is nullhex when the file is deleted remotely ('cd'). | |
385 | records.append(('C', '\0'.join([filename] + v))) |
|
402 | records.append((RECORD_CHANGEDELETE_CONFLICT, | |
|
403 | '\0'.join([filename] + v))) | |||
386 | else: |
|
404 | else: | |
387 | # Normal files. These are stored in 'F' records. |
|
405 | # Normal files. These are stored in 'F' records. | |
388 |
records.append(( |
|
406 | records.append((RECORD_MERGED, | |
|
407 | '\0'.join([filename] + v))) | |||
389 | for filename, extras in sorted(self._stateextras.iteritems()): |
|
408 | for filename, extras in sorted(self._stateextras.iteritems()): | |
390 | rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in |
|
409 | rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in | |
391 | extras.iteritems()) |
|
410 | extras.iteritems()) | |
392 | records.append(('f', '%s\0%s' % (filename, rawextras))) |
|
411 | records.append((RECORD_FILE_VALUES, | |
|
412 | '%s\0%s' % (filename, rawextras))) | |||
393 | if self._labels is not None: |
|
413 | if self._labels is not None: | |
394 | labels = '\0'.join(self._labels) |
|
414 | labels = '\0'.join(self._labels) | |
395 |
records.append(( |
|
415 | records.append((RECORD_LABELS, labels)) | |
396 | return records |
|
416 | return records | |
397 |
|
417 | |||
398 | def _writerecords(self, records): |
|
418 | def _writerecords(self, records): | |
@@ -405,10 +425,10 b' class mergestate(object):' | |||||
405 | f = self._repo.vfs(self.statepathv1, 'wb') |
|
425 | f = self._repo.vfs(self.statepathv1, 'wb') | |
406 | irecords = iter(records) |
|
426 | irecords = iter(records) | |
407 | lrecords = next(irecords) |
|
427 | lrecords = next(irecords) | |
408 |
assert lrecords[0] == |
|
428 | assert lrecords[0] == RECORD_LOCAL | |
409 | f.write(hex(self._local) + '\n') |
|
429 | f.write(hex(self._local) + '\n') | |
410 | for rtype, data in irecords: |
|
430 | for rtype, data in irecords: | |
411 |
if rtype == |
|
431 | if rtype == RECORD_MERGED: | |
412 | f.write('%s\n' % _droponode(data)) |
|
432 | f.write('%s\n' % _droponode(data)) | |
413 | f.close() |
|
433 | f.close() | |
414 |
|
434 | |||
@@ -417,12 +437,12 b' class mergestate(object):' | |||||
417 |
|
437 | |||
418 | See the docstring for _readrecordsv2 for why we use 't'.""" |
|
438 | See the docstring for _readrecordsv2 for why we use 't'.""" | |
419 | # these are the records that all version 2 clients can read |
|
439 | # these are the records that all version 2 clients can read | |
420 | whitelist = 'LOF' |
|
440 | allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED) | |
421 | f = self._repo.vfs(self.statepathv2, 'wb') |
|
441 | f = self._repo.vfs(self.statepathv2, 'wb') | |
422 | for key, data in records: |
|
442 | for key, data in records: | |
423 | assert len(key) == 1 |
|
443 | assert len(key) == 1 | |
424 |
if key not in w |
|
444 | if key not in allowlist: | |
425 |
key, data = |
|
445 | key, data = RECORD_OVERRIDE, '%s%s' % (key, data) | |
426 | format = '>sI%is' % len(data) |
|
446 | format = '>sI%is' % len(data) | |
427 | f.write(_pack(format, key, len(data), data)) |
|
447 | f.write(_pack(format, key, len(data), data)) | |
428 | f.close() |
|
448 | f.close() |
General Comments 0
You need to be logged in to leave comments.
Login now