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