##// END OF EJS Templates
mergestate: move commit() from base class to on-disk subclass...
Martin von Zweigbergk -
r46073:0e75c088 default
parent child Browse files
Show More
@@ -1,927 +1,926 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import collections
3 import collections
4 import errno
4 import errno
5 import shutil
5 import shutil
6 import struct
6 import struct
7
7
8 from .i18n import _
8 from .i18n import _
9 from .node import (
9 from .node import (
10 bin,
10 bin,
11 hex,
11 hex,
12 nullhex,
12 nullhex,
13 nullid,
13 nullid,
14 )
14 )
15 from . import (
15 from . import (
16 error,
16 error,
17 filemerge,
17 filemerge,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21 from .utils import hashutil
21 from .utils import hashutil
22
22
23 _pack = struct.pack
23 _pack = struct.pack
24 _unpack = struct.unpack
24 _unpack = struct.unpack
25
25
26
26
27 def _droponode(data):
27 def _droponode(data):
28 # used for compatibility for v1
28 # used for compatibility for v1
29 bits = data.split(b'\0')
29 bits = data.split(b'\0')
30 bits = bits[:-2] + bits[-1:]
30 bits = bits[:-2] + bits[-1:]
31 return b'\0'.join(bits)
31 return b'\0'.join(bits)
32
32
33
33
34 def _filectxorabsent(hexnode, ctx, f):
34 def _filectxorabsent(hexnode, ctx, f):
35 if hexnode == nullhex:
35 if hexnode == nullhex:
36 return filemerge.absentfilectx(ctx, f)
36 return filemerge.absentfilectx(ctx, f)
37 else:
37 else:
38 return ctx[f]
38 return ctx[f]
39
39
40
40
41 # Merge state record types. See ``mergestate`` docs for more.
41 # Merge state record types. See ``mergestate`` docs for more.
42
42
43 ####
43 ####
44 # merge records which records metadata about a current merge
44 # merge records which records metadata about a current merge
45 # exists only once in a mergestate
45 # exists only once in a mergestate
46 #####
46 #####
47 RECORD_LOCAL = b'L'
47 RECORD_LOCAL = b'L'
48 RECORD_OTHER = b'O'
48 RECORD_OTHER = b'O'
49 # record merge labels
49 # record merge labels
50 RECORD_LABELS = b'l'
50 RECORD_LABELS = b'l'
51 # store info about merge driver used and it's state
51 # store info about merge driver used and it's state
52 RECORD_MERGE_DRIVER_STATE = b'm'
52 RECORD_MERGE_DRIVER_STATE = b'm'
53
53
54 #####
54 #####
55 # record extra information about files, with one entry containing info about one
55 # record extra information about files, with one entry containing info about one
56 # file. Hence, multiple of them can exists
56 # file. Hence, multiple of them can exists
57 #####
57 #####
58 RECORD_FILE_VALUES = b'f'
58 RECORD_FILE_VALUES = b'f'
59
59
60 #####
60 #####
61 # merge records which represents state of individual merges of files/folders
61 # merge records which represents state of individual merges of files/folders
62 # These are top level records for each entry containing merge related info.
62 # These are top level records for each entry containing merge related info.
63 # Each record of these has info about one file. Hence multiple of them can
63 # Each record of these has info about one file. Hence multiple of them can
64 # exists
64 # exists
65 #####
65 #####
66 RECORD_MERGED = b'F'
66 RECORD_MERGED = b'F'
67 RECORD_CHANGEDELETE_CONFLICT = b'C'
67 RECORD_CHANGEDELETE_CONFLICT = b'C'
68 RECORD_MERGE_DRIVER_MERGE = b'D'
68 RECORD_MERGE_DRIVER_MERGE = b'D'
69 # the path was dir on one side of merge and file on another
69 # the path was dir on one side of merge and file on another
70 RECORD_PATH_CONFLICT = b'P'
70 RECORD_PATH_CONFLICT = b'P'
71
71
72 #####
72 #####
73 # possible state which a merge entry can have. These are stored inside top-level
73 # possible state which a merge entry can have. These are stored inside top-level
74 # merge records mentioned just above.
74 # merge records mentioned just above.
75 #####
75 #####
76 MERGE_RECORD_UNRESOLVED = b'u'
76 MERGE_RECORD_UNRESOLVED = b'u'
77 MERGE_RECORD_RESOLVED = b'r'
77 MERGE_RECORD_RESOLVED = b'r'
78 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
78 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
79 MERGE_RECORD_RESOLVED_PATH = b'pr'
79 MERGE_RECORD_RESOLVED_PATH = b'pr'
80 MERGE_RECORD_DRIVER_RESOLVED = b'd'
80 MERGE_RECORD_DRIVER_RESOLVED = b'd'
81 # represents that the file was automatically merged in favor
81 # represents that the file was automatically merged in favor
82 # of other version. This info is used on commit.
82 # of other version. This info is used on commit.
83 # This is now deprecated and commit related information is now
83 # This is now deprecated and commit related information is now
84 # stored in RECORD_FILE_VALUES
84 # stored in RECORD_FILE_VALUES
85 MERGE_RECORD_MERGED_OTHER = b'o'
85 MERGE_RECORD_MERGED_OTHER = b'o'
86
86
87 #####
87 #####
88 # top level record which stores other unknown records. Multiple of these can
88 # top level record which stores other unknown records. Multiple of these can
89 # exists
89 # exists
90 #####
90 #####
91 RECORD_OVERRIDE = b't'
91 RECORD_OVERRIDE = b't'
92
92
93 #####
93 #####
94 # possible states which a merge driver can have. These are stored inside a
94 # possible states which a merge driver can have. These are stored inside a
95 # RECORD_MERGE_DRIVER_STATE entry
95 # RECORD_MERGE_DRIVER_STATE entry
96 #####
96 #####
97 MERGE_DRIVER_STATE_UNMARKED = b'u'
97 MERGE_DRIVER_STATE_UNMARKED = b'u'
98 MERGE_DRIVER_STATE_MARKED = b'm'
98 MERGE_DRIVER_STATE_MARKED = b'm'
99 MERGE_DRIVER_STATE_SUCCESS = b's'
99 MERGE_DRIVER_STATE_SUCCESS = b's'
100
100
101 #####
101 #####
102 # legacy records which are no longer used but kept to prevent breaking BC
102 # legacy records which are no longer used but kept to prevent breaking BC
103 #####
103 #####
104 # This record was release in 5.4 and usage was removed in 5.5
104 # This record was release in 5.4 and usage was removed in 5.5
105 LEGACY_RECORD_RESOLVED_OTHER = b'R'
105 LEGACY_RECORD_RESOLVED_OTHER = b'R'
106
106
107
107
108 ACTION_FORGET = b'f'
108 ACTION_FORGET = b'f'
109 ACTION_REMOVE = b'r'
109 ACTION_REMOVE = b'r'
110 ACTION_ADD = b'a'
110 ACTION_ADD = b'a'
111 ACTION_GET = b'g'
111 ACTION_GET = b'g'
112 ACTION_PATH_CONFLICT = b'p'
112 ACTION_PATH_CONFLICT = b'p'
113 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
113 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
114 ACTION_ADD_MODIFIED = b'am'
114 ACTION_ADD_MODIFIED = b'am'
115 ACTION_CREATED = b'c'
115 ACTION_CREATED = b'c'
116 ACTION_DELETED_CHANGED = b'dc'
116 ACTION_DELETED_CHANGED = b'dc'
117 ACTION_CHANGED_DELETED = b'cd'
117 ACTION_CHANGED_DELETED = b'cd'
118 ACTION_MERGE = b'm'
118 ACTION_MERGE = b'm'
119 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
119 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
120 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
120 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
121 ACTION_KEEP = b'k'
121 ACTION_KEEP = b'k'
122 # the file was absent on local side before merge and we should
122 # the file was absent on local side before merge and we should
123 # keep it absent (absent means file not present, it can be a result
123 # keep it absent (absent means file not present, it can be a result
124 # of file deletion, rename etc.)
124 # of file deletion, rename etc.)
125 ACTION_KEEP_ABSENT = b'ka'
125 ACTION_KEEP_ABSENT = b'ka'
126 ACTION_EXEC = b'e'
126 ACTION_EXEC = b'e'
127 ACTION_CREATED_MERGE = b'cm'
127 ACTION_CREATED_MERGE = b'cm'
128
128
129
129
130 class _mergestate_base(object):
130 class _mergestate_base(object):
131 '''track 3-way merge state of individual files
131 '''track 3-way merge state of individual files
132
132
133 The merge state is stored on disk when needed. Two files are used: one with
133 The merge state is stored on disk when needed. Two files are used: one with
134 an old format (version 1), and one with a new format (version 2). Version 2
134 an old format (version 1), and one with a new format (version 2). Version 2
135 stores a superset of the data in version 1, including new kinds of records
135 stores a superset of the data in version 1, including new kinds of records
136 in the future. For more about the new format, see the documentation for
136 in the future. For more about the new format, see the documentation for
137 `_readrecordsv2`.
137 `_readrecordsv2`.
138
138
139 Each record can contain arbitrary content, and has an associated type. This
139 Each record can contain arbitrary content, and has an associated type. This
140 `type` should be a letter. If `type` is uppercase, the record is mandatory:
140 `type` should be a letter. If `type` is uppercase, the record is mandatory:
141 versions of Mercurial that don't support it should abort. If `type` is
141 versions of Mercurial that don't support it should abort. If `type` is
142 lowercase, the record can be safely ignored.
142 lowercase, the record can be safely ignored.
143
143
144 Currently known records:
144 Currently known records:
145
145
146 L: the node of the "local" part of the merge (hexified version)
146 L: the node of the "local" part of the merge (hexified version)
147 O: the node of the "other" part of the merge (hexified version)
147 O: the node of the "other" part of the merge (hexified version)
148 F: a file to be merged entry
148 F: a file to be merged entry
149 C: a change/delete or delete/change conflict
149 C: a change/delete or delete/change conflict
150 D: a file that the external merge driver will merge internally
150 D: a file that the external merge driver will merge internally
151 (experimental)
151 (experimental)
152 P: a path conflict (file vs directory)
152 P: a path conflict (file vs directory)
153 m: the external merge driver defined for this merge plus its run state
153 m: the external merge driver defined for this merge plus its run state
154 (experimental)
154 (experimental)
155 f: a (filename, dictionary) tuple of optional values for a given file
155 f: a (filename, dictionary) tuple of optional values for a given file
156 l: the labels for the parts of the merge.
156 l: the labels for the parts of the merge.
157
157
158 Merge driver run states (experimental):
158 Merge driver run states (experimental):
159 u: driver-resolved files unmarked -- needs to be run next time we're about
159 u: driver-resolved files unmarked -- needs to be run next time we're about
160 to resolve or commit
160 to resolve or commit
161 m: driver-resolved files marked -- only needs to be run before commit
161 m: driver-resolved files marked -- only needs to be run before commit
162 s: success/skipped -- does not need to be run any more
162 s: success/skipped -- does not need to be run any more
163
163
164 Merge record states (stored in self._state, indexed by filename):
164 Merge record states (stored in self._state, indexed by filename):
165 u: unresolved conflict
165 u: unresolved conflict
166 r: resolved conflict
166 r: resolved conflict
167 pu: unresolved path conflict (file conflicts with directory)
167 pu: unresolved path conflict (file conflicts with directory)
168 pr: resolved path conflict
168 pr: resolved path conflict
169 d: driver-resolved conflict
169 d: driver-resolved conflict
170
170
171 The resolve command transitions between 'u' and 'r' for conflicts and
171 The resolve command transitions between 'u' and 'r' for conflicts and
172 'pu' and 'pr' for path conflicts.
172 'pu' and 'pr' for path conflicts.
173 '''
173 '''
174
174
175 def __init__(self, repo):
175 def __init__(self, repo):
176 """Initialize the merge state.
176 """Initialize the merge state.
177
177
178 Do not use this directly! Instead call read() or clean()."""
178 Do not use this directly! Instead call read() or clean()."""
179 self._repo = repo
179 self._repo = repo
180 self._state = {}
180 self._state = {}
181 self._stateextras = collections.defaultdict(dict)
181 self._stateextras = collections.defaultdict(dict)
182 self._local = None
182 self._local = None
183 self._other = None
183 self._other = None
184 self._labels = None
184 self._labels = None
185 self._readmergedriver = None
185 self._readmergedriver = None
186 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
186 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
187 # contains a mapping of form:
187 # contains a mapping of form:
188 # {filename : (merge_return_value, action_to_be_performed}
188 # {filename : (merge_return_value, action_to_be_performed}
189 # these are results of re-running merge process
189 # these are results of re-running merge process
190 # this dict is used to perform actions on dirstate caused by re-running
190 # this dict is used to perform actions on dirstate caused by re-running
191 # the merge
191 # the merge
192 self._results = {}
192 self._results = {}
193 self._dirty = False
193 self._dirty = False
194
194
195 def reset(self):
195 def reset(self):
196 pass
196 pass
197
197
198 def start(self, node, other, labels=None):
198 def start(self, node, other, labels=None):
199 self._local = node
199 self._local = node
200 self._other = other
200 self._other = other
201 self._labels = labels
201 self._labels = labels
202 if self.mergedriver:
202 if self.mergedriver:
203 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
203 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
204
204
205 @util.propertycache
205 @util.propertycache
206 def mergedriver(self):
206 def mergedriver(self):
207 # protect against the following:
207 # protect against the following:
208 # - A configures a malicious merge driver in their hgrc, then
208 # - A configures a malicious merge driver in their hgrc, then
209 # pauses the merge
209 # pauses the merge
210 # - A edits their hgrc to remove references to the merge driver
210 # - A edits their hgrc to remove references to the merge driver
211 # - A gives a copy of their entire repo, including .hg, to B
211 # - A gives a copy of their entire repo, including .hg, to B
212 # - B inspects .hgrc and finds it to be clean
212 # - B inspects .hgrc and finds it to be clean
213 # - B then continues the merge and the malicious merge driver
213 # - B then continues the merge and the malicious merge driver
214 # gets invoked
214 # gets invoked
215 configmergedriver = self._repo.ui.config(
215 configmergedriver = self._repo.ui.config(
216 b'experimental', b'mergedriver'
216 b'experimental', b'mergedriver'
217 )
217 )
218 if (
218 if (
219 self._readmergedriver is not None
219 self._readmergedriver is not None
220 and self._readmergedriver != configmergedriver
220 and self._readmergedriver != configmergedriver
221 ):
221 ):
222 raise error.ConfigError(
222 raise error.ConfigError(
223 _(b"merge driver changed since merge started"),
223 _(b"merge driver changed since merge started"),
224 hint=_(b"revert merge driver change or abort merge"),
224 hint=_(b"revert merge driver change or abort merge"),
225 )
225 )
226
226
227 return configmergedriver
227 return configmergedriver
228
228
229 @util.propertycache
229 @util.propertycache
230 def local(self):
230 def local(self):
231 if self._local is None:
231 if self._local is None:
232 msg = b"local accessed but self._local isn't set"
232 msg = b"local accessed but self._local isn't set"
233 raise error.ProgrammingError(msg)
233 raise error.ProgrammingError(msg)
234 return self._local
234 return self._local
235
235
236 @util.propertycache
236 @util.propertycache
237 def localctx(self):
237 def localctx(self):
238 return self._repo[self.local]
238 return self._repo[self.local]
239
239
240 @util.propertycache
240 @util.propertycache
241 def other(self):
241 def other(self):
242 if self._other is None:
242 if self._other is None:
243 msg = b"other accessed but self._other isn't set"
243 msg = b"other accessed but self._other isn't set"
244 raise error.ProgrammingError(msg)
244 raise error.ProgrammingError(msg)
245 return self._other
245 return self._other
246
246
247 @util.propertycache
247 @util.propertycache
248 def otherctx(self):
248 def otherctx(self):
249 return self._repo[self.other]
249 return self._repo[self.other]
250
250
251 def active(self):
251 def active(self):
252 """Whether mergestate is active.
252 """Whether mergestate is active.
253
253
254 Returns True if there appears to be mergestate. This is a rough proxy
254 Returns True if there appears to be mergestate. This is a rough proxy
255 for "is a merge in progress."
255 for "is a merge in progress."
256 """
256 """
257 return bool(self._local) or bool(self._state)
257 return bool(self._local) or bool(self._state)
258
258
259 def commit(self):
259 def commit(self):
260 """Write current state on disk (if necessary)"""
260 """Write current state on disk (if necessary)"""
261 if self._dirty:
262 records = self._makerecords()
263 self._writerecords(records)
264 self._dirty = False
265
266 def _makerecords(self):
267 records = []
268 records.append((RECORD_LOCAL, hex(self._local)))
269 records.append((RECORD_OTHER, hex(self._other)))
270 if self.mergedriver:
271 records.append(
272 (
273 RECORD_MERGE_DRIVER_STATE,
274 b'\0'.join([self.mergedriver, self._mdstate]),
275 )
276 )
277 # Write out state items. In all cases, the value of the state map entry
278 # is written as the contents of the record. The record type depends on
279 # the type of state that is stored, and capital-letter records are used
280 # to prevent older versions of Mercurial that do not support the feature
281 # from loading them.
282 for filename, v in pycompat.iteritems(self._state):
283 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
284 # Driver-resolved merge. These are stored in 'D' records.
285 records.append(
286 (RECORD_MERGE_DRIVER_MERGE, b'\0'.join([filename] + v))
287 )
288 elif v[0] in (
289 MERGE_RECORD_UNRESOLVED_PATH,
290 MERGE_RECORD_RESOLVED_PATH,
291 ):
292 # Path conflicts. These are stored in 'P' records. The current
293 # resolution state ('pu' or 'pr') is stored within the record.
294 records.append(
295 (RECORD_PATH_CONFLICT, b'\0'.join([filename] + v))
296 )
297 elif v[1] == nullhex or v[6] == nullhex:
298 # Change/Delete or Delete/Change conflicts. These are stored in
299 # 'C' records. v[1] is the local file, and is nullhex when the
300 # file is deleted locally ('dc'). v[6] is the remote file, and
301 # is nullhex when the file is deleted remotely ('cd').
302 records.append(
303 (RECORD_CHANGEDELETE_CONFLICT, b'\0'.join([filename] + v))
304 )
305 else:
306 # Normal files. These are stored in 'F' records.
307 records.append((RECORD_MERGED, b'\0'.join([filename] + v)))
308 for filename, extras in sorted(pycompat.iteritems(self._stateextras)):
309 rawextras = b'\0'.join(
310 b'%s\0%s' % (k, v) for k, v in pycompat.iteritems(extras)
311 )
312 records.append(
313 (RECORD_FILE_VALUES, b'%s\0%s' % (filename, rawextras))
314 )
315 if self._labels is not None:
316 labels = b'\0'.join(self._labels)
317 records.append((RECORD_LABELS, labels))
318 return records
319
261
320 @staticmethod
262 @staticmethod
321 def getlocalkey(path):
263 def getlocalkey(path):
322 """hash the path of a local file context for storage in the .hg/merge
264 """hash the path of a local file context for storage in the .hg/merge
323 directory."""
265 directory."""
324
266
325 return hex(hashutil.sha1(path).digest())
267 return hex(hashutil.sha1(path).digest())
326
268
327 def _make_backup(self, fctx, localkey):
269 def _make_backup(self, fctx, localkey):
328 raise NotImplementedError()
270 raise NotImplementedError()
329
271
330 def _restore_backup(self, fctx, localkey, flags):
272 def _restore_backup(self, fctx, localkey, flags):
331 raise NotImplementedError()
273 raise NotImplementedError()
332
274
333 def add(self, fcl, fco, fca, fd):
275 def add(self, fcl, fco, fca, fd):
334 """add a new (potentially?) conflicting file the merge state
276 """add a new (potentially?) conflicting file the merge state
335 fcl: file context for local,
277 fcl: file context for local,
336 fco: file context for remote,
278 fco: file context for remote,
337 fca: file context for ancestors,
279 fca: file context for ancestors,
338 fd: file path of the resulting merge.
280 fd: file path of the resulting merge.
339
281
340 note: also write the local version to the `.hg/merge` directory.
282 note: also write the local version to the `.hg/merge` directory.
341 """
283 """
342 if fcl.isabsent():
284 if fcl.isabsent():
343 localkey = nullhex
285 localkey = nullhex
344 else:
286 else:
345 localkey = mergestate.getlocalkey(fcl.path())
287 localkey = mergestate.getlocalkey(fcl.path())
346 self._make_backup(fcl, localkey)
288 self._make_backup(fcl, localkey)
347 self._state[fd] = [
289 self._state[fd] = [
348 MERGE_RECORD_UNRESOLVED,
290 MERGE_RECORD_UNRESOLVED,
349 localkey,
291 localkey,
350 fcl.path(),
292 fcl.path(),
351 fca.path(),
293 fca.path(),
352 hex(fca.filenode()),
294 hex(fca.filenode()),
353 fco.path(),
295 fco.path(),
354 hex(fco.filenode()),
296 hex(fco.filenode()),
355 fcl.flags(),
297 fcl.flags(),
356 ]
298 ]
357 self._stateextras[fd] = {b'ancestorlinknode': hex(fca.node())}
299 self._stateextras[fd] = {b'ancestorlinknode': hex(fca.node())}
358 self._dirty = True
300 self._dirty = True
359
301
360 def addpathconflict(self, path, frename, forigin):
302 def addpathconflict(self, path, frename, forigin):
361 """add a new conflicting path to the merge state
303 """add a new conflicting path to the merge state
362 path: the path that conflicts
304 path: the path that conflicts
363 frename: the filename the conflicting file was renamed to
305 frename: the filename the conflicting file was renamed to
364 forigin: origin of the file ('l' or 'r' for local/remote)
306 forigin: origin of the file ('l' or 'r' for local/remote)
365 """
307 """
366 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
308 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
367 self._dirty = True
309 self._dirty = True
368
310
369 def addcommitinfo(self, path, data):
311 def addcommitinfo(self, path, data):
370 """ stores information which is required at commit
312 """ stores information which is required at commit
371 into _stateextras """
313 into _stateextras """
372 self._stateextras[path].update(data)
314 self._stateextras[path].update(data)
373 self._dirty = True
315 self._dirty = True
374
316
375 def __contains__(self, dfile):
317 def __contains__(self, dfile):
376 return dfile in self._state
318 return dfile in self._state
377
319
378 def __getitem__(self, dfile):
320 def __getitem__(self, dfile):
379 return self._state[dfile][0]
321 return self._state[dfile][0]
380
322
381 def __iter__(self):
323 def __iter__(self):
382 return iter(sorted(self._state))
324 return iter(sorted(self._state))
383
325
384 def files(self):
326 def files(self):
385 return self._state.keys()
327 return self._state.keys()
386
328
387 def mark(self, dfile, state):
329 def mark(self, dfile, state):
388 self._state[dfile][0] = state
330 self._state[dfile][0] = state
389 self._dirty = True
331 self._dirty = True
390
332
391 def mdstate(self):
333 def mdstate(self):
392 return self._mdstate
334 return self._mdstate
393
335
394 def unresolved(self):
336 def unresolved(self):
395 """Obtain the paths of unresolved files."""
337 """Obtain the paths of unresolved files."""
396
338
397 for f, entry in pycompat.iteritems(self._state):
339 for f, entry in pycompat.iteritems(self._state):
398 if entry[0] in (
340 if entry[0] in (
399 MERGE_RECORD_UNRESOLVED,
341 MERGE_RECORD_UNRESOLVED,
400 MERGE_RECORD_UNRESOLVED_PATH,
342 MERGE_RECORD_UNRESOLVED_PATH,
401 ):
343 ):
402 yield f
344 yield f
403
345
404 def driverresolved(self):
346 def driverresolved(self):
405 """Obtain the paths of driver-resolved files."""
347 """Obtain the paths of driver-resolved files."""
406
348
407 for f, entry in self._state.items():
349 for f, entry in self._state.items():
408 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
350 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
409 yield f
351 yield f
410
352
411 def extras(self, filename):
353 def extras(self, filename):
412 return self._stateextras[filename]
354 return self._stateextras[filename]
413
355
414 def _resolve(self, preresolve, dfile, wctx):
356 def _resolve(self, preresolve, dfile, wctx):
415 """rerun merge process for file path `dfile`.
357 """rerun merge process for file path `dfile`.
416 Returns whether the merge was completed and the return value of merge
358 Returns whether the merge was completed and the return value of merge
417 obtained from filemerge._filemerge().
359 obtained from filemerge._filemerge().
418 """
360 """
419 if self[dfile] in (MERGE_RECORD_RESOLVED, MERGE_RECORD_DRIVER_RESOLVED):
361 if self[dfile] in (MERGE_RECORD_RESOLVED, MERGE_RECORD_DRIVER_RESOLVED):
420 return True, 0
362 return True, 0
421 stateentry = self._state[dfile]
363 stateentry = self._state[dfile]
422 state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
364 state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
423 octx = self._repo[self._other]
365 octx = self._repo[self._other]
424 extras = self.extras(dfile)
366 extras = self.extras(dfile)
425 anccommitnode = extras.get(b'ancestorlinknode')
367 anccommitnode = extras.get(b'ancestorlinknode')
426 if anccommitnode:
368 if anccommitnode:
427 actx = self._repo[anccommitnode]
369 actx = self._repo[anccommitnode]
428 else:
370 else:
429 actx = None
371 actx = None
430 fcd = _filectxorabsent(localkey, wctx, dfile)
372 fcd = _filectxorabsent(localkey, wctx, dfile)
431 fco = _filectxorabsent(onode, octx, ofile)
373 fco = _filectxorabsent(onode, octx, ofile)
432 # TODO: move this to filectxorabsent
374 # TODO: move this to filectxorabsent
433 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
375 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
434 # "premerge" x flags
376 # "premerge" x flags
435 flo = fco.flags()
377 flo = fco.flags()
436 fla = fca.flags()
378 fla = fca.flags()
437 if b'x' in flags + flo + fla and b'l' not in flags + flo + fla:
379 if b'x' in flags + flo + fla and b'l' not in flags + flo + fla:
438 if fca.node() == nullid and flags != flo:
380 if fca.node() == nullid and flags != flo:
439 if preresolve:
381 if preresolve:
440 self._repo.ui.warn(
382 self._repo.ui.warn(
441 _(
383 _(
442 b'warning: cannot merge flags for %s '
384 b'warning: cannot merge flags for %s '
443 b'without common ancestor - keeping local flags\n'
385 b'without common ancestor - keeping local flags\n'
444 )
386 )
445 % afile
387 % afile
446 )
388 )
447 elif flags == fla:
389 elif flags == fla:
448 flags = flo
390 flags = flo
449 if preresolve:
391 if preresolve:
450 # restore local
392 # restore local
451 if localkey != nullhex:
393 if localkey != nullhex:
452 self._restore_backup(wctx[dfile], localkey, flags)
394 self._restore_backup(wctx[dfile], localkey, flags)
453 else:
395 else:
454 wctx[dfile].remove(ignoremissing=True)
396 wctx[dfile].remove(ignoremissing=True)
455 complete, merge_ret, deleted = filemerge.premerge(
397 complete, merge_ret, deleted = filemerge.premerge(
456 self._repo,
398 self._repo,
457 wctx,
399 wctx,
458 self._local,
400 self._local,
459 lfile,
401 lfile,
460 fcd,
402 fcd,
461 fco,
403 fco,
462 fca,
404 fca,
463 labels=self._labels,
405 labels=self._labels,
464 )
406 )
465 else:
407 else:
466 complete, merge_ret, deleted = filemerge.filemerge(
408 complete, merge_ret, deleted = filemerge.filemerge(
467 self._repo,
409 self._repo,
468 wctx,
410 wctx,
469 self._local,
411 self._local,
470 lfile,
412 lfile,
471 fcd,
413 fcd,
472 fco,
414 fco,
473 fca,
415 fca,
474 labels=self._labels,
416 labels=self._labels,
475 )
417 )
476 if merge_ret is None:
418 if merge_ret is None:
477 # If return value of merge is None, then there are no real conflict
419 # If return value of merge is None, then there are no real conflict
478 del self._state[dfile]
420 del self._state[dfile]
479 self._stateextras.pop(dfile, None)
421 self._stateextras.pop(dfile, None)
480 self._dirty = True
422 self._dirty = True
481 elif not merge_ret:
423 elif not merge_ret:
482 self.mark(dfile, MERGE_RECORD_RESOLVED)
424 self.mark(dfile, MERGE_RECORD_RESOLVED)
483
425
484 if complete:
426 if complete:
485 action = None
427 action = None
486 if deleted:
428 if deleted:
487 if fcd.isabsent():
429 if fcd.isabsent():
488 # dc: local picked. Need to drop if present, which may
430 # dc: local picked. Need to drop if present, which may
489 # happen on re-resolves.
431 # happen on re-resolves.
490 action = ACTION_FORGET
432 action = ACTION_FORGET
491 else:
433 else:
492 # cd: remote picked (or otherwise deleted)
434 # cd: remote picked (or otherwise deleted)
493 action = ACTION_REMOVE
435 action = ACTION_REMOVE
494 else:
436 else:
495 if fcd.isabsent(): # dc: remote picked
437 if fcd.isabsent(): # dc: remote picked
496 action = ACTION_GET
438 action = ACTION_GET
497 elif fco.isabsent(): # cd: local picked
439 elif fco.isabsent(): # cd: local picked
498 if dfile in self.localctx:
440 if dfile in self.localctx:
499 action = ACTION_ADD_MODIFIED
441 action = ACTION_ADD_MODIFIED
500 else:
442 else:
501 action = ACTION_ADD
443 action = ACTION_ADD
502 # else: regular merges (no action necessary)
444 # else: regular merges (no action necessary)
503 self._results[dfile] = merge_ret, action
445 self._results[dfile] = merge_ret, action
504
446
505 return complete, merge_ret
447 return complete, merge_ret
506
448
507 def preresolve(self, dfile, wctx):
449 def preresolve(self, dfile, wctx):
508 """run premerge process for dfile
450 """run premerge process for dfile
509
451
510 Returns whether the merge is complete, and the exit code."""
452 Returns whether the merge is complete, and the exit code."""
511 return self._resolve(True, dfile, wctx)
453 return self._resolve(True, dfile, wctx)
512
454
513 def resolve(self, dfile, wctx):
455 def resolve(self, dfile, wctx):
514 """run merge process (assuming premerge was run) for dfile
456 """run merge process (assuming premerge was run) for dfile
515
457
516 Returns the exit code of the merge."""
458 Returns the exit code of the merge."""
517 return self._resolve(False, dfile, wctx)[1]
459 return self._resolve(False, dfile, wctx)[1]
518
460
519 def counts(self):
461 def counts(self):
520 """return counts for updated, merged and removed files in this
462 """return counts for updated, merged and removed files in this
521 session"""
463 session"""
522 updated, merged, removed = 0, 0, 0
464 updated, merged, removed = 0, 0, 0
523 for r, action in pycompat.itervalues(self._results):
465 for r, action in pycompat.itervalues(self._results):
524 if r is None:
466 if r is None:
525 updated += 1
467 updated += 1
526 elif r == 0:
468 elif r == 0:
527 if action == ACTION_REMOVE:
469 if action == ACTION_REMOVE:
528 removed += 1
470 removed += 1
529 else:
471 else:
530 merged += 1
472 merged += 1
531 return updated, merged, removed
473 return updated, merged, removed
532
474
533 def unresolvedcount(self):
475 def unresolvedcount(self):
534 """get unresolved count for this merge (persistent)"""
476 """get unresolved count for this merge (persistent)"""
535 return len(list(self.unresolved()))
477 return len(list(self.unresolved()))
536
478
537 def actions(self):
479 def actions(self):
538 """return lists of actions to perform on the dirstate"""
480 """return lists of actions to perform on the dirstate"""
539 actions = {
481 actions = {
540 ACTION_REMOVE: [],
482 ACTION_REMOVE: [],
541 ACTION_FORGET: [],
483 ACTION_FORGET: [],
542 ACTION_ADD: [],
484 ACTION_ADD: [],
543 ACTION_ADD_MODIFIED: [],
485 ACTION_ADD_MODIFIED: [],
544 ACTION_GET: [],
486 ACTION_GET: [],
545 }
487 }
546 for f, (r, action) in pycompat.iteritems(self._results):
488 for f, (r, action) in pycompat.iteritems(self._results):
547 if action is not None:
489 if action is not None:
548 actions[action].append((f, None, b"merge result"))
490 actions[action].append((f, None, b"merge result"))
549 return actions
491 return actions
550
492
551 def queueremove(self, f):
493 def queueremove(self, f):
552 """queues a file to be removed from the dirstate
494 """queues a file to be removed from the dirstate
553
495
554 Meant for use by custom merge drivers."""
496 Meant for use by custom merge drivers."""
555 self._results[f] = 0, ACTION_REMOVE
497 self._results[f] = 0, ACTION_REMOVE
556
498
557 def queueadd(self, f):
499 def queueadd(self, f):
558 """queues a file to be added to the dirstate
500 """queues a file to be added to the dirstate
559
501
560 Meant for use by custom merge drivers."""
502 Meant for use by custom merge drivers."""
561 self._results[f] = 0, ACTION_ADD
503 self._results[f] = 0, ACTION_ADD
562
504
563 def queueget(self, f):
505 def queueget(self, f):
564 """queues a file to be marked modified in the dirstate
506 """queues a file to be marked modified in the dirstate
565
507
566 Meant for use by custom merge drivers."""
508 Meant for use by custom merge drivers."""
567 self._results[f] = 0, ACTION_GET
509 self._results[f] = 0, ACTION_GET
568
510
569
511
570 class mergestate(_mergestate_base):
512 class mergestate(_mergestate_base):
571
513
572 statepathv1 = b'merge/state'
514 statepathv1 = b'merge/state'
573 statepathv2 = b'merge/state2'
515 statepathv2 = b'merge/state2'
574
516
575 @staticmethod
517 @staticmethod
576 def clean(repo):
518 def clean(repo):
577 """Initialize a brand new merge state, removing any existing state on
519 """Initialize a brand new merge state, removing any existing state on
578 disk."""
520 disk."""
579 ms = mergestate(repo)
521 ms = mergestate(repo)
580 ms.reset()
522 ms.reset()
581 return ms
523 return ms
582
524
583 @staticmethod
525 @staticmethod
584 def read(repo):
526 def read(repo):
585 """Initialize the merge state, reading it from disk."""
527 """Initialize the merge state, reading it from disk."""
586 ms = mergestate(repo)
528 ms = mergestate(repo)
587 ms._read()
529 ms._read()
588 return ms
530 return ms
589
531
590 def _read(self):
532 def _read(self):
591 """Analyse each record content to restore a serialized state from disk
533 """Analyse each record content to restore a serialized state from disk
592
534
593 This function process "record" entry produced by the de-serialization
535 This function process "record" entry produced by the de-serialization
594 of on disk file.
536 of on disk file.
595 """
537 """
596 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
538 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
597 unsupported = set()
539 unsupported = set()
598 records = self._readrecords()
540 records = self._readrecords()
599 for rtype, record in records:
541 for rtype, record in records:
600 if rtype == RECORD_LOCAL:
542 if rtype == RECORD_LOCAL:
601 self._local = bin(record)
543 self._local = bin(record)
602 elif rtype == RECORD_OTHER:
544 elif rtype == RECORD_OTHER:
603 self._other = bin(record)
545 self._other = bin(record)
604 elif rtype == RECORD_MERGE_DRIVER_STATE:
546 elif rtype == RECORD_MERGE_DRIVER_STATE:
605 bits = record.split(b'\0', 1)
547 bits = record.split(b'\0', 1)
606 mdstate = bits[1]
548 mdstate = bits[1]
607 if len(mdstate) != 1 or mdstate not in (
549 if len(mdstate) != 1 or mdstate not in (
608 MERGE_DRIVER_STATE_UNMARKED,
550 MERGE_DRIVER_STATE_UNMARKED,
609 MERGE_DRIVER_STATE_MARKED,
551 MERGE_DRIVER_STATE_MARKED,
610 MERGE_DRIVER_STATE_SUCCESS,
552 MERGE_DRIVER_STATE_SUCCESS,
611 ):
553 ):
612 # the merge driver should be idempotent, so just rerun it
554 # the merge driver should be idempotent, so just rerun it
613 mdstate = MERGE_DRIVER_STATE_UNMARKED
555 mdstate = MERGE_DRIVER_STATE_UNMARKED
614
556
615 self._readmergedriver = bits[0]
557 self._readmergedriver = bits[0]
616 self._mdstate = mdstate
558 self._mdstate = mdstate
617 elif rtype in (
559 elif rtype in (
618 RECORD_MERGED,
560 RECORD_MERGED,
619 RECORD_CHANGEDELETE_CONFLICT,
561 RECORD_CHANGEDELETE_CONFLICT,
620 RECORD_PATH_CONFLICT,
562 RECORD_PATH_CONFLICT,
621 RECORD_MERGE_DRIVER_MERGE,
563 RECORD_MERGE_DRIVER_MERGE,
622 LEGACY_RECORD_RESOLVED_OTHER,
564 LEGACY_RECORD_RESOLVED_OTHER,
623 ):
565 ):
624 bits = record.split(b'\0')
566 bits = record.split(b'\0')
625 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
567 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
626 # and we now store related information in _stateextras, so
568 # and we now store related information in _stateextras, so
627 # lets write to _stateextras directly
569 # lets write to _stateextras directly
628 if bits[1] == MERGE_RECORD_MERGED_OTHER:
570 if bits[1] == MERGE_RECORD_MERGED_OTHER:
629 self._stateextras[bits[0]][b'filenode-source'] = b'other'
571 self._stateextras[bits[0]][b'filenode-source'] = b'other'
630 else:
572 else:
631 self._state[bits[0]] = bits[1:]
573 self._state[bits[0]] = bits[1:]
632 elif rtype == RECORD_FILE_VALUES:
574 elif rtype == RECORD_FILE_VALUES:
633 filename, rawextras = record.split(b'\0', 1)
575 filename, rawextras = record.split(b'\0', 1)
634 extraparts = rawextras.split(b'\0')
576 extraparts = rawextras.split(b'\0')
635 extras = {}
577 extras = {}
636 i = 0
578 i = 0
637 while i < len(extraparts):
579 while i < len(extraparts):
638 extras[extraparts[i]] = extraparts[i + 1]
580 extras[extraparts[i]] = extraparts[i + 1]
639 i += 2
581 i += 2
640
582
641 self._stateextras[filename] = extras
583 self._stateextras[filename] = extras
642 elif rtype == RECORD_LABELS:
584 elif rtype == RECORD_LABELS:
643 labels = record.split(b'\0', 2)
585 labels = record.split(b'\0', 2)
644 self._labels = [l for l in labels if len(l) > 0]
586 self._labels = [l for l in labels if len(l) > 0]
645 elif not rtype.islower():
587 elif not rtype.islower():
646 unsupported.add(rtype)
588 unsupported.add(rtype)
647
589
648 if unsupported:
590 if unsupported:
649 raise error.UnsupportedMergeRecords(unsupported)
591 raise error.UnsupportedMergeRecords(unsupported)
650
592
651 def _readrecords(self):
593 def _readrecords(self):
652 """Read merge state from disk and return a list of record (TYPE, data)
594 """Read merge state from disk and return a list of record (TYPE, data)
653
595
654 We read data from both v1 and v2 files and decide which one to use.
596 We read data from both v1 and v2 files and decide which one to use.
655
597
656 V1 has been used by version prior to 2.9.1 and contains less data than
598 V1 has been used by version prior to 2.9.1 and contains less data than
657 v2. We read both versions and check if no data in v2 contradicts
599 v2. We read both versions and check if no data in v2 contradicts
658 v1. If there is not contradiction we can safely assume that both v1
600 v1. If there is not contradiction we can safely assume that both v1
659 and v2 were written at the same time and use the extract data in v2. If
601 and v2 were written at the same time and use the extract data in v2. If
660 there is contradiction we ignore v2 content as we assume an old version
602 there is contradiction we ignore v2 content as we assume an old version
661 of Mercurial has overwritten the mergestate file and left an old v2
603 of Mercurial has overwritten the mergestate file and left an old v2
662 file around.
604 file around.
663
605
664 returns list of record [(TYPE, data), ...]"""
606 returns list of record [(TYPE, data), ...]"""
665 v1records = self._readrecordsv1()
607 v1records = self._readrecordsv1()
666 v2records = self._readrecordsv2()
608 v2records = self._readrecordsv2()
667 if self._v1v2match(v1records, v2records):
609 if self._v1v2match(v1records, v2records):
668 return v2records
610 return v2records
669 else:
611 else:
670 # v1 file is newer than v2 file, use it
612 # v1 file is newer than v2 file, use it
671 # we have to infer the "other" changeset of the merge
613 # we have to infer the "other" changeset of the merge
672 # we cannot do better than that with v1 of the format
614 # we cannot do better than that with v1 of the format
673 mctx = self._repo[None].parents()[-1]
615 mctx = self._repo[None].parents()[-1]
674 v1records.append((RECORD_OTHER, mctx.hex()))
616 v1records.append((RECORD_OTHER, mctx.hex()))
675 # add place holder "other" file node information
617 # add place holder "other" file node information
676 # nobody is using it yet so we do no need to fetch the data
618 # nobody is using it yet so we do no need to fetch the data
677 # if mctx was wrong `mctx[bits[-2]]` may fails.
619 # if mctx was wrong `mctx[bits[-2]]` may fails.
678 for idx, r in enumerate(v1records):
620 for idx, r in enumerate(v1records):
679 if r[0] == RECORD_MERGED:
621 if r[0] == RECORD_MERGED:
680 bits = r[1].split(b'\0')
622 bits = r[1].split(b'\0')
681 bits.insert(-2, b'')
623 bits.insert(-2, b'')
682 v1records[idx] = (r[0], b'\0'.join(bits))
624 v1records[idx] = (r[0], b'\0'.join(bits))
683 return v1records
625 return v1records
684
626
685 def _v1v2match(self, v1records, v2records):
627 def _v1v2match(self, v1records, v2records):
686 oldv2 = set() # old format version of v2 record
628 oldv2 = set() # old format version of v2 record
687 for rec in v2records:
629 for rec in v2records:
688 if rec[0] == RECORD_LOCAL:
630 if rec[0] == RECORD_LOCAL:
689 oldv2.add(rec)
631 oldv2.add(rec)
690 elif rec[0] == RECORD_MERGED:
632 elif rec[0] == RECORD_MERGED:
691 # drop the onode data (not contained in v1)
633 # drop the onode data (not contained in v1)
692 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
634 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
693 for rec in v1records:
635 for rec in v1records:
694 if rec not in oldv2:
636 if rec not in oldv2:
695 return False
637 return False
696 else:
638 else:
697 return True
639 return True
698
640
699 def _readrecordsv1(self):
641 def _readrecordsv1(self):
700 """read on disk merge state for version 1 file
642 """read on disk merge state for version 1 file
701
643
702 returns list of record [(TYPE, data), ...]
644 returns list of record [(TYPE, data), ...]
703
645
704 Note: the "F" data from this file are one entry short
646 Note: the "F" data from this file are one entry short
705 (no "other file node" entry)
647 (no "other file node" entry)
706 """
648 """
707 records = []
649 records = []
708 try:
650 try:
709 f = self._repo.vfs(self.statepathv1)
651 f = self._repo.vfs(self.statepathv1)
710 for i, l in enumerate(f):
652 for i, l in enumerate(f):
711 if i == 0:
653 if i == 0:
712 records.append((RECORD_LOCAL, l[:-1]))
654 records.append((RECORD_LOCAL, l[:-1]))
713 else:
655 else:
714 records.append((RECORD_MERGED, l[:-1]))
656 records.append((RECORD_MERGED, l[:-1]))
715 f.close()
657 f.close()
716 except IOError as err:
658 except IOError as err:
717 if err.errno != errno.ENOENT:
659 if err.errno != errno.ENOENT:
718 raise
660 raise
719 return records
661 return records
720
662
721 def _readrecordsv2(self):
663 def _readrecordsv2(self):
722 """read on disk merge state for version 2 file
664 """read on disk merge state for version 2 file
723
665
724 This format is a list of arbitrary records of the form:
666 This format is a list of arbitrary records of the form:
725
667
726 [type][length][content]
668 [type][length][content]
727
669
728 `type` is a single character, `length` is a 4 byte integer, and
670 `type` is a single character, `length` is a 4 byte integer, and
729 `content` is an arbitrary byte sequence of length `length`.
671 `content` is an arbitrary byte sequence of length `length`.
730
672
731 Mercurial versions prior to 3.7 have a bug where if there are
673 Mercurial versions prior to 3.7 have a bug where if there are
732 unsupported mandatory merge records, attempting to clear out the merge
674 unsupported mandatory merge records, attempting to clear out the merge
733 state with hg update --clean or similar aborts. The 't' record type
675 state with hg update --clean or similar aborts. The 't' record type
734 works around that by writing out what those versions treat as an
676 works around that by writing out what those versions treat as an
735 advisory record, but later versions interpret as special: the first
677 advisory record, but later versions interpret as special: the first
736 character is the 'real' record type and everything onwards is the data.
678 character is the 'real' record type and everything onwards is the data.
737
679
738 Returns list of records [(TYPE, data), ...]."""
680 Returns list of records [(TYPE, data), ...]."""
739 records = []
681 records = []
740 try:
682 try:
741 f = self._repo.vfs(self.statepathv2)
683 f = self._repo.vfs(self.statepathv2)
742 data = f.read()
684 data = f.read()
743 off = 0
685 off = 0
744 end = len(data)
686 end = len(data)
745 while off < end:
687 while off < end:
746 rtype = data[off : off + 1]
688 rtype = data[off : off + 1]
747 off += 1
689 off += 1
748 length = _unpack(b'>I', data[off : (off + 4)])[0]
690 length = _unpack(b'>I', data[off : (off + 4)])[0]
749 off += 4
691 off += 4
750 record = data[off : (off + length)]
692 record = data[off : (off + length)]
751 off += length
693 off += length
752 if rtype == RECORD_OVERRIDE:
694 if rtype == RECORD_OVERRIDE:
753 rtype, record = record[0:1], record[1:]
695 rtype, record = record[0:1], record[1:]
754 records.append((rtype, record))
696 records.append((rtype, record))
755 f.close()
697 f.close()
756 except IOError as err:
698 except IOError as err:
757 if err.errno != errno.ENOENT:
699 if err.errno != errno.ENOENT:
758 raise
700 raise
759 return records
701 return records
760
702
703 def commit(self):
704 if self._dirty:
705 records = self._makerecords()
706 self._writerecords(records)
707 self._dirty = False
708
709 def _makerecords(self):
710 records = []
711 records.append((RECORD_LOCAL, hex(self._local)))
712 records.append((RECORD_OTHER, hex(self._other)))
713 if self.mergedriver:
714 records.append(
715 (
716 RECORD_MERGE_DRIVER_STATE,
717 b'\0'.join([self.mergedriver, self._mdstate]),
718 )
719 )
720 # Write out state items. In all cases, the value of the state map entry
721 # is written as the contents of the record. The record type depends on
722 # the type of state that is stored, and capital-letter records are used
723 # to prevent older versions of Mercurial that do not support the feature
724 # from loading them.
725 for filename, v in pycompat.iteritems(self._state):
726 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
727 # Driver-resolved merge. These are stored in 'D' records.
728 records.append(
729 (RECORD_MERGE_DRIVER_MERGE, b'\0'.join([filename] + v))
730 )
731 elif v[0] in (
732 MERGE_RECORD_UNRESOLVED_PATH,
733 MERGE_RECORD_RESOLVED_PATH,
734 ):
735 # Path conflicts. These are stored in 'P' records. The current
736 # resolution state ('pu' or 'pr') is stored within the record.
737 records.append(
738 (RECORD_PATH_CONFLICT, b'\0'.join([filename] + v))
739 )
740 elif v[1] == nullhex or v[6] == nullhex:
741 # Change/Delete or Delete/Change conflicts. These are stored in
742 # 'C' records. v[1] is the local file, and is nullhex when the
743 # file is deleted locally ('dc'). v[6] is the remote file, and
744 # is nullhex when the file is deleted remotely ('cd').
745 records.append(
746 (RECORD_CHANGEDELETE_CONFLICT, b'\0'.join([filename] + v))
747 )
748 else:
749 # Normal files. These are stored in 'F' records.
750 records.append((RECORD_MERGED, b'\0'.join([filename] + v)))
751 for filename, extras in sorted(pycompat.iteritems(self._stateextras)):
752 rawextras = b'\0'.join(
753 b'%s\0%s' % (k, v) for k, v in pycompat.iteritems(extras)
754 )
755 records.append(
756 (RECORD_FILE_VALUES, b'%s\0%s' % (filename, rawextras))
757 )
758 if self._labels is not None:
759 labels = b'\0'.join(self._labels)
760 records.append((RECORD_LABELS, labels))
761 return records
762
761 def _writerecords(self, records):
763 def _writerecords(self, records):
762 """Write current state on disk (both v1 and v2)"""
764 """Write current state on disk (both v1 and v2)"""
763 self._writerecordsv1(records)
765 self._writerecordsv1(records)
764 self._writerecordsv2(records)
766 self._writerecordsv2(records)
765
767
766 def _writerecordsv1(self, records):
768 def _writerecordsv1(self, records):
767 """Write current state on disk in a version 1 file"""
769 """Write current state on disk in a version 1 file"""
768 f = self._repo.vfs(self.statepathv1, b'wb')
770 f = self._repo.vfs(self.statepathv1, b'wb')
769 irecords = iter(records)
771 irecords = iter(records)
770 lrecords = next(irecords)
772 lrecords = next(irecords)
771 assert lrecords[0] == RECORD_LOCAL
773 assert lrecords[0] == RECORD_LOCAL
772 f.write(hex(self._local) + b'\n')
774 f.write(hex(self._local) + b'\n')
773 for rtype, data in irecords:
775 for rtype, data in irecords:
774 if rtype == RECORD_MERGED:
776 if rtype == RECORD_MERGED:
775 f.write(b'%s\n' % _droponode(data))
777 f.write(b'%s\n' % _droponode(data))
776 f.close()
778 f.close()
777
779
778 def _writerecordsv2(self, records):
780 def _writerecordsv2(self, records):
779 """Write current state on disk in a version 2 file
781 """Write current state on disk in a version 2 file
780
782
781 See the docstring for _readrecordsv2 for why we use 't'."""
783 See the docstring for _readrecordsv2 for why we use 't'."""
782 # these are the records that all version 2 clients can read
784 # these are the records that all version 2 clients can read
783 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
785 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
784 f = self._repo.vfs(self.statepathv2, b'wb')
786 f = self._repo.vfs(self.statepathv2, b'wb')
785 for key, data in records:
787 for key, data in records:
786 assert len(key) == 1
788 assert len(key) == 1
787 if key not in allowlist:
789 if key not in allowlist:
788 key, data = RECORD_OVERRIDE, b'%s%s' % (key, data)
790 key, data = RECORD_OVERRIDE, b'%s%s' % (key, data)
789 format = b'>sI%is' % len(data)
791 format = b'>sI%is' % len(data)
790 f.write(_pack(format, key, len(data), data))
792 f.write(_pack(format, key, len(data), data))
791 f.close()
793 f.close()
792
794
793 def _make_backup(self, fctx, localkey):
795 def _make_backup(self, fctx, localkey):
794 self._repo.vfs.write(b'merge/' + localkey, fctx.data())
796 self._repo.vfs.write(b'merge/' + localkey, fctx.data())
795
797
796 def _restore_backup(self, fctx, localkey, flags):
798 def _restore_backup(self, fctx, localkey, flags):
797 with self._repo.vfs(b'merge/' + localkey) as f:
799 with self._repo.vfs(b'merge/' + localkey) as f:
798 fctx.write(f.read(), flags)
800 fctx.write(f.read(), flags)
799
801
800 def reset(self):
802 def reset(self):
801 shutil.rmtree(self._repo.vfs.join(b'merge'), True)
803 shutil.rmtree(self._repo.vfs.join(b'merge'), True)
802
804
803
805
804 class memmergestate(_mergestate_base):
806 class memmergestate(_mergestate_base):
805 def __init__(self, repo):
807 def __init__(self, repo):
806 super(memmergestate, self).__init__(repo)
808 super(memmergestate, self).__init__(repo)
807 self._backups = {}
809 self._backups = {}
808
810
809 def _make_backup(self, fctx, localkey):
811 def _make_backup(self, fctx, localkey):
810 self._backups[localkey] = fctx.data()
812 self._backups[localkey] = fctx.data()
811
813
812 def _restore_backup(self, fctx, localkey, flags):
814 def _restore_backup(self, fctx, localkey, flags):
813 fctx.write(self._backups[localkey], flags)
815 fctx.write(self._backups[localkey], flags)
814
816
815 @util.propertycache
817 @util.propertycache
816 def mergedriver(self):
818 def mergedriver(self):
817 configmergedriver = self._repo.ui.config(
819 configmergedriver = self._repo.ui.config(
818 b'experimental', b'mergedriver'
820 b'experimental', b'mergedriver'
819 )
821 )
820 if configmergedriver:
822 if configmergedriver:
821 raise error.InMemoryMergeConflictsError(
823 raise error.InMemoryMergeConflictsError(
822 b"in-memory merge does not support mergedriver"
824 b"in-memory merge does not support mergedriver"
823 )
825 )
824 return None
826 return None
825
827
826 def commit(self):
827 pass
828
829
828
830 def recordupdates(repo, actions, branchmerge, getfiledata):
829 def recordupdates(repo, actions, branchmerge, getfiledata):
831 """record merge actions to the dirstate"""
830 """record merge actions to the dirstate"""
832 # remove (must come first)
831 # remove (must come first)
833 for f, args, msg in actions.get(ACTION_REMOVE, []):
832 for f, args, msg in actions.get(ACTION_REMOVE, []):
834 if branchmerge:
833 if branchmerge:
835 repo.dirstate.remove(f)
834 repo.dirstate.remove(f)
836 else:
835 else:
837 repo.dirstate.drop(f)
836 repo.dirstate.drop(f)
838
837
839 # forget (must come first)
838 # forget (must come first)
840 for f, args, msg in actions.get(ACTION_FORGET, []):
839 for f, args, msg in actions.get(ACTION_FORGET, []):
841 repo.dirstate.drop(f)
840 repo.dirstate.drop(f)
842
841
843 # resolve path conflicts
842 # resolve path conflicts
844 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
843 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
845 (f0, origf0) = args
844 (f0, origf0) = args
846 repo.dirstate.add(f)
845 repo.dirstate.add(f)
847 repo.dirstate.copy(origf0, f)
846 repo.dirstate.copy(origf0, f)
848 if f0 == origf0:
847 if f0 == origf0:
849 repo.dirstate.remove(f0)
848 repo.dirstate.remove(f0)
850 else:
849 else:
851 repo.dirstate.drop(f0)
850 repo.dirstate.drop(f0)
852
851
853 # re-add
852 # re-add
854 for f, args, msg in actions.get(ACTION_ADD, []):
853 for f, args, msg in actions.get(ACTION_ADD, []):
855 repo.dirstate.add(f)
854 repo.dirstate.add(f)
856
855
857 # re-add/mark as modified
856 # re-add/mark as modified
858 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
857 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
859 if branchmerge:
858 if branchmerge:
860 repo.dirstate.normallookup(f)
859 repo.dirstate.normallookup(f)
861 else:
860 else:
862 repo.dirstate.add(f)
861 repo.dirstate.add(f)
863
862
864 # exec change
863 # exec change
865 for f, args, msg in actions.get(ACTION_EXEC, []):
864 for f, args, msg in actions.get(ACTION_EXEC, []):
866 repo.dirstate.normallookup(f)
865 repo.dirstate.normallookup(f)
867
866
868 # keep
867 # keep
869 for f, args, msg in actions.get(ACTION_KEEP, []):
868 for f, args, msg in actions.get(ACTION_KEEP, []):
870 pass
869 pass
871
870
872 # keep deleted
871 # keep deleted
873 for f, args, msg in actions.get(ACTION_KEEP_ABSENT, []):
872 for f, args, msg in actions.get(ACTION_KEEP_ABSENT, []):
874 pass
873 pass
875
874
876 # get
875 # get
877 for f, args, msg in actions.get(ACTION_GET, []):
876 for f, args, msg in actions.get(ACTION_GET, []):
878 if branchmerge:
877 if branchmerge:
879 repo.dirstate.otherparent(f)
878 repo.dirstate.otherparent(f)
880 else:
879 else:
881 parentfiledata = getfiledata[f] if getfiledata else None
880 parentfiledata = getfiledata[f] if getfiledata else None
882 repo.dirstate.normal(f, parentfiledata=parentfiledata)
881 repo.dirstate.normal(f, parentfiledata=parentfiledata)
883
882
884 # merge
883 # merge
885 for f, args, msg in actions.get(ACTION_MERGE, []):
884 for f, args, msg in actions.get(ACTION_MERGE, []):
886 f1, f2, fa, move, anc = args
885 f1, f2, fa, move, anc = args
887 if branchmerge:
886 if branchmerge:
888 # We've done a branch merge, mark this file as merged
887 # We've done a branch merge, mark this file as merged
889 # so that we properly record the merger later
888 # so that we properly record the merger later
890 repo.dirstate.merge(f)
889 repo.dirstate.merge(f)
891 if f1 != f2: # copy/rename
890 if f1 != f2: # copy/rename
892 if move:
891 if move:
893 repo.dirstate.remove(f1)
892 repo.dirstate.remove(f1)
894 if f1 != f:
893 if f1 != f:
895 repo.dirstate.copy(f1, f)
894 repo.dirstate.copy(f1, f)
896 else:
895 else:
897 repo.dirstate.copy(f2, f)
896 repo.dirstate.copy(f2, f)
898 else:
897 else:
899 # We've update-merged a locally modified file, so
898 # We've update-merged a locally modified file, so
900 # we set the dirstate to emulate a normal checkout
899 # we set the dirstate to emulate a normal checkout
901 # of that file some time in the past. Thus our
900 # of that file some time in the past. Thus our
902 # merge will appear as a normal local file
901 # merge will appear as a normal local file
903 # modification.
902 # modification.
904 if f2 == f: # file not locally copied/moved
903 if f2 == f: # file not locally copied/moved
905 repo.dirstate.normallookup(f)
904 repo.dirstate.normallookup(f)
906 if move:
905 if move:
907 repo.dirstate.drop(f1)
906 repo.dirstate.drop(f1)
908
907
909 # directory rename, move local
908 # directory rename, move local
910 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
909 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
911 f0, flag = args
910 f0, flag = args
912 if branchmerge:
911 if branchmerge:
913 repo.dirstate.add(f)
912 repo.dirstate.add(f)
914 repo.dirstate.remove(f0)
913 repo.dirstate.remove(f0)
915 repo.dirstate.copy(f0, f)
914 repo.dirstate.copy(f0, f)
916 else:
915 else:
917 repo.dirstate.normal(f)
916 repo.dirstate.normal(f)
918 repo.dirstate.drop(f0)
917 repo.dirstate.drop(f0)
919
918
920 # directory rename, get
919 # directory rename, get
921 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
920 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
922 f0, flag = args
921 f0, flag = args
923 if branchmerge:
922 if branchmerge:
924 repo.dirstate.add(f)
923 repo.dirstate.add(f)
925 repo.dirstate.copy(f0, f)
924 repo.dirstate.copy(f0, f)
926 else:
925 else:
927 repo.dirstate.normal(f)
926 repo.dirstate.normal(f)
General Comments 0
You need to be logged in to leave comments. Login now