##// END OF EJS Templates
merge: do the trivial resolution after updating sparse checkout...
Pulkit Goyal -
r38780:d49e490a @94 stable
parent child Browse files
Show More
@@ -1,2243 +1,2242 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import hashlib
11 import hashlib
12 import shutil
12 import shutil
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 addednodeid,
17 addednodeid,
18 bin,
18 bin,
19 hex,
19 hex,
20 modifiednodeid,
20 modifiednodeid,
21 nullhex,
21 nullhex,
22 nullid,
22 nullid,
23 nullrev,
23 nullrev,
24 )
24 )
25 from .thirdparty import (
25 from .thirdparty import (
26 attr,
26 attr,
27 )
27 )
28 from . import (
28 from . import (
29 copies,
29 copies,
30 error,
30 error,
31 filemerge,
31 filemerge,
32 match as matchmod,
32 match as matchmod,
33 obsutil,
33 obsutil,
34 pycompat,
34 pycompat,
35 scmutil,
35 scmutil,
36 subrepoutil,
36 subrepoutil,
37 util,
37 util,
38 worker,
38 worker,
39 )
39 )
40
40
41 _pack = struct.pack
41 _pack = struct.pack
42 _unpack = struct.unpack
42 _unpack = struct.unpack
43
43
44 def _droponode(data):
44 def _droponode(data):
45 # used for compatibility for v1
45 # used for compatibility for v1
46 bits = data.split('\0')
46 bits = data.split('\0')
47 bits = bits[:-2] + bits[-1:]
47 bits = bits[:-2] + bits[-1:]
48 return '\0'.join(bits)
48 return '\0'.join(bits)
49
49
50 # Merge state record types. See ``mergestate`` docs for more.
50 # Merge state record types. See ``mergestate`` docs for more.
51 RECORD_LOCAL = b'L'
51 RECORD_LOCAL = b'L'
52 RECORD_OTHER = b'O'
52 RECORD_OTHER = b'O'
53 RECORD_MERGED = b'F'
53 RECORD_MERGED = b'F'
54 RECORD_CHANGEDELETE_CONFLICT = b'C'
54 RECORD_CHANGEDELETE_CONFLICT = b'C'
55 RECORD_MERGE_DRIVER_MERGE = b'D'
55 RECORD_MERGE_DRIVER_MERGE = b'D'
56 RECORD_PATH_CONFLICT = b'P'
56 RECORD_PATH_CONFLICT = b'P'
57 RECORD_MERGE_DRIVER_STATE = b'm'
57 RECORD_MERGE_DRIVER_STATE = b'm'
58 RECORD_FILE_VALUES = b'f'
58 RECORD_FILE_VALUES = b'f'
59 RECORD_LABELS = b'l'
59 RECORD_LABELS = b'l'
60 RECORD_OVERRIDE = b't'
60 RECORD_OVERRIDE = b't'
61 RECORD_UNSUPPORTED_MANDATORY = b'X'
61 RECORD_UNSUPPORTED_MANDATORY = b'X'
62 RECORD_UNSUPPORTED_ADVISORY = b'x'
62 RECORD_UNSUPPORTED_ADVISORY = b'x'
63
63
64 MERGE_DRIVER_STATE_UNMARKED = b'u'
64 MERGE_DRIVER_STATE_UNMARKED = b'u'
65 MERGE_DRIVER_STATE_MARKED = b'm'
65 MERGE_DRIVER_STATE_MARKED = b'm'
66 MERGE_DRIVER_STATE_SUCCESS = b's'
66 MERGE_DRIVER_STATE_SUCCESS = b's'
67
67
68 MERGE_RECORD_UNRESOLVED = b'u'
68 MERGE_RECORD_UNRESOLVED = b'u'
69 MERGE_RECORD_RESOLVED = b'r'
69 MERGE_RECORD_RESOLVED = b'r'
70 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
70 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
71 MERGE_RECORD_RESOLVED_PATH = b'pr'
71 MERGE_RECORD_RESOLVED_PATH = b'pr'
72 MERGE_RECORD_DRIVER_RESOLVED = b'd'
72 MERGE_RECORD_DRIVER_RESOLVED = b'd'
73
73
74 ACTION_FORGET = b'f'
74 ACTION_FORGET = b'f'
75 ACTION_REMOVE = b'r'
75 ACTION_REMOVE = b'r'
76 ACTION_ADD = b'a'
76 ACTION_ADD = b'a'
77 ACTION_GET = b'g'
77 ACTION_GET = b'g'
78 ACTION_PATH_CONFLICT = b'p'
78 ACTION_PATH_CONFLICT = b'p'
79 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
79 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
80 ACTION_ADD_MODIFIED = b'am'
80 ACTION_ADD_MODIFIED = b'am'
81 ACTION_CREATED = b'c'
81 ACTION_CREATED = b'c'
82 ACTION_DELETED_CHANGED = b'dc'
82 ACTION_DELETED_CHANGED = b'dc'
83 ACTION_CHANGED_DELETED = b'cd'
83 ACTION_CHANGED_DELETED = b'cd'
84 ACTION_MERGE = b'm'
84 ACTION_MERGE = b'm'
85 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
85 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
86 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
86 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
87 ACTION_KEEP = b'k'
87 ACTION_KEEP = b'k'
88 ACTION_EXEC = b'e'
88 ACTION_EXEC = b'e'
89 ACTION_CREATED_MERGE = b'cm'
89 ACTION_CREATED_MERGE = b'cm'
90
90
91 class mergestate(object):
91 class mergestate(object):
92 '''track 3-way merge state of individual files
92 '''track 3-way merge state of individual files
93
93
94 The merge state is stored on disk when needed. Two files are used: one with
94 The merge state is stored on disk when needed. Two files are used: one with
95 an old format (version 1), and one with a new format (version 2). Version 2
95 an old format (version 1), and one with a new format (version 2). Version 2
96 stores a superset of the data in version 1, including new kinds of records
96 stores a superset of the data in version 1, including new kinds of records
97 in the future. For more about the new format, see the documentation for
97 in the future. For more about the new format, see the documentation for
98 `_readrecordsv2`.
98 `_readrecordsv2`.
99
99
100 Each record can contain arbitrary content, and has an associated type. This
100 Each record can contain arbitrary content, and has an associated type. This
101 `type` should be a letter. If `type` is uppercase, the record is mandatory:
101 `type` should be a letter. If `type` is uppercase, the record is mandatory:
102 versions of Mercurial that don't support it should abort. If `type` is
102 versions of Mercurial that don't support it should abort. If `type` is
103 lowercase, the record can be safely ignored.
103 lowercase, the record can be safely ignored.
104
104
105 Currently known records:
105 Currently known records:
106
106
107 L: the node of the "local" part of the merge (hexified version)
107 L: the node of the "local" part of the merge (hexified version)
108 O: the node of the "other" part of the merge (hexified version)
108 O: the node of the "other" part of the merge (hexified version)
109 F: a file to be merged entry
109 F: a file to be merged entry
110 C: a change/delete or delete/change conflict
110 C: a change/delete or delete/change conflict
111 D: a file that the external merge driver will merge internally
111 D: a file that the external merge driver will merge internally
112 (experimental)
112 (experimental)
113 P: a path conflict (file vs directory)
113 P: a path conflict (file vs directory)
114 m: the external merge driver defined for this merge plus its run state
114 m: the external merge driver defined for this merge plus its run state
115 (experimental)
115 (experimental)
116 f: a (filename, dictionary) tuple of optional values for a given file
116 f: a (filename, dictionary) tuple of optional values for a given file
117 X: unsupported mandatory record type (used in tests)
117 X: unsupported mandatory record type (used in tests)
118 x: unsupported advisory record type (used in tests)
118 x: unsupported advisory record type (used in tests)
119 l: the labels for the parts of the merge.
119 l: the labels for the parts of the merge.
120
120
121 Merge driver run states (experimental):
121 Merge driver run states (experimental):
122 u: driver-resolved files unmarked -- needs to be run next time we're about
122 u: driver-resolved files unmarked -- needs to be run next time we're about
123 to resolve or commit
123 to resolve or commit
124 m: driver-resolved files marked -- only needs to be run before commit
124 m: driver-resolved files marked -- only needs to be run before commit
125 s: success/skipped -- does not need to be run any more
125 s: success/skipped -- does not need to be run any more
126
126
127 Merge record states (stored in self._state, indexed by filename):
127 Merge record states (stored in self._state, indexed by filename):
128 u: unresolved conflict
128 u: unresolved conflict
129 r: resolved conflict
129 r: resolved conflict
130 pu: unresolved path conflict (file conflicts with directory)
130 pu: unresolved path conflict (file conflicts with directory)
131 pr: resolved path conflict
131 pr: resolved path conflict
132 d: driver-resolved conflict
132 d: driver-resolved conflict
133
133
134 The resolve command transitions between 'u' and 'r' for conflicts and
134 The resolve command transitions between 'u' and 'r' for conflicts and
135 'pu' and 'pr' for path conflicts.
135 'pu' and 'pr' for path conflicts.
136 '''
136 '''
137 statepathv1 = 'merge/state'
137 statepathv1 = 'merge/state'
138 statepathv2 = 'merge/state2'
138 statepathv2 = 'merge/state2'
139
139
140 @staticmethod
140 @staticmethod
141 def clean(repo, node=None, other=None, labels=None):
141 def clean(repo, node=None, other=None, labels=None):
142 """Initialize a brand new merge state, removing any existing state on
142 """Initialize a brand new merge state, removing any existing state on
143 disk."""
143 disk."""
144 ms = mergestate(repo)
144 ms = mergestate(repo)
145 ms.reset(node, other, labels)
145 ms.reset(node, other, labels)
146 return ms
146 return ms
147
147
148 @staticmethod
148 @staticmethod
149 def read(repo):
149 def read(repo):
150 """Initialize the merge state, reading it from disk."""
150 """Initialize the merge state, reading it from disk."""
151 ms = mergestate(repo)
151 ms = mergestate(repo)
152 ms._read()
152 ms._read()
153 return ms
153 return ms
154
154
155 def __init__(self, repo):
155 def __init__(self, repo):
156 """Initialize the merge state.
156 """Initialize the merge state.
157
157
158 Do not use this directly! Instead call read() or clean()."""
158 Do not use this directly! Instead call read() or clean()."""
159 self._repo = repo
159 self._repo = repo
160 self._dirty = False
160 self._dirty = False
161 self._labels = None
161 self._labels = None
162
162
163 def reset(self, node=None, other=None, labels=None):
163 def reset(self, node=None, other=None, labels=None):
164 self._state = {}
164 self._state = {}
165 self._stateextras = {}
165 self._stateextras = {}
166 self._local = None
166 self._local = None
167 self._other = None
167 self._other = None
168 self._labels = labels
168 self._labels = labels
169 for var in ('localctx', 'otherctx'):
169 for var in ('localctx', 'otherctx'):
170 if var in vars(self):
170 if var in vars(self):
171 delattr(self, var)
171 delattr(self, var)
172 if node:
172 if node:
173 self._local = node
173 self._local = node
174 self._other = other
174 self._other = other
175 self._readmergedriver = None
175 self._readmergedriver = None
176 if self.mergedriver:
176 if self.mergedriver:
177 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
177 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
178 else:
178 else:
179 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
179 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
180 shutil.rmtree(self._repo.vfs.join('merge'), True)
180 shutil.rmtree(self._repo.vfs.join('merge'), True)
181 self._results = {}
181 self._results = {}
182 self._dirty = False
182 self._dirty = False
183
183
184 def _read(self):
184 def _read(self):
185 """Analyse each record content to restore a serialized state from disk
185 """Analyse each record content to restore a serialized state from disk
186
186
187 This function process "record" entry produced by the de-serialization
187 This function process "record" entry produced by the de-serialization
188 of on disk file.
188 of on disk file.
189 """
189 """
190 self._state = {}
190 self._state = {}
191 self._stateextras = {}
191 self._stateextras = {}
192 self._local = None
192 self._local = None
193 self._other = None
193 self._other = None
194 for var in ('localctx', 'otherctx'):
194 for var in ('localctx', 'otherctx'):
195 if var in vars(self):
195 if var in vars(self):
196 delattr(self, var)
196 delattr(self, var)
197 self._readmergedriver = None
197 self._readmergedriver = None
198 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
198 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
199 unsupported = set()
199 unsupported = set()
200 records = self._readrecords()
200 records = self._readrecords()
201 for rtype, record in records:
201 for rtype, record in records:
202 if rtype == RECORD_LOCAL:
202 if rtype == RECORD_LOCAL:
203 self._local = bin(record)
203 self._local = bin(record)
204 elif rtype == RECORD_OTHER:
204 elif rtype == RECORD_OTHER:
205 self._other = bin(record)
205 self._other = bin(record)
206 elif rtype == RECORD_MERGE_DRIVER_STATE:
206 elif rtype == RECORD_MERGE_DRIVER_STATE:
207 bits = record.split('\0', 1)
207 bits = record.split('\0', 1)
208 mdstate = bits[1]
208 mdstate = bits[1]
209 if len(mdstate) != 1 or mdstate not in (
209 if len(mdstate) != 1 or mdstate not in (
210 MERGE_DRIVER_STATE_UNMARKED, MERGE_DRIVER_STATE_MARKED,
210 MERGE_DRIVER_STATE_UNMARKED, MERGE_DRIVER_STATE_MARKED,
211 MERGE_DRIVER_STATE_SUCCESS):
211 MERGE_DRIVER_STATE_SUCCESS):
212 # the merge driver should be idempotent, so just rerun it
212 # the merge driver should be idempotent, so just rerun it
213 mdstate = MERGE_DRIVER_STATE_UNMARKED
213 mdstate = MERGE_DRIVER_STATE_UNMARKED
214
214
215 self._readmergedriver = bits[0]
215 self._readmergedriver = bits[0]
216 self._mdstate = mdstate
216 self._mdstate = mdstate
217 elif rtype in (RECORD_MERGED, RECORD_CHANGEDELETE_CONFLICT,
217 elif rtype in (RECORD_MERGED, RECORD_CHANGEDELETE_CONFLICT,
218 RECORD_PATH_CONFLICT, RECORD_MERGE_DRIVER_MERGE):
218 RECORD_PATH_CONFLICT, RECORD_MERGE_DRIVER_MERGE):
219 bits = record.split('\0')
219 bits = record.split('\0')
220 self._state[bits[0]] = bits[1:]
220 self._state[bits[0]] = bits[1:]
221 elif rtype == RECORD_FILE_VALUES:
221 elif rtype == RECORD_FILE_VALUES:
222 filename, rawextras = record.split('\0', 1)
222 filename, rawextras = record.split('\0', 1)
223 extraparts = rawextras.split('\0')
223 extraparts = rawextras.split('\0')
224 extras = {}
224 extras = {}
225 i = 0
225 i = 0
226 while i < len(extraparts):
226 while i < len(extraparts):
227 extras[extraparts[i]] = extraparts[i + 1]
227 extras[extraparts[i]] = extraparts[i + 1]
228 i += 2
228 i += 2
229
229
230 self._stateextras[filename] = extras
230 self._stateextras[filename] = extras
231 elif rtype == RECORD_LABELS:
231 elif rtype == RECORD_LABELS:
232 labels = record.split('\0', 2)
232 labels = record.split('\0', 2)
233 self._labels = [l for l in labels if len(l) > 0]
233 self._labels = [l for l in labels if len(l) > 0]
234 elif not rtype.islower():
234 elif not rtype.islower():
235 unsupported.add(rtype)
235 unsupported.add(rtype)
236 self._results = {}
236 self._results = {}
237 self._dirty = False
237 self._dirty = False
238
238
239 if unsupported:
239 if unsupported:
240 raise error.UnsupportedMergeRecords(unsupported)
240 raise error.UnsupportedMergeRecords(unsupported)
241
241
242 def _readrecords(self):
242 def _readrecords(self):
243 """Read merge state from disk and return a list of record (TYPE, data)
243 """Read merge state from disk and return a list of record (TYPE, data)
244
244
245 We read data from both v1 and v2 files and decide which one to use.
245 We read data from both v1 and v2 files and decide which one to use.
246
246
247 V1 has been used by version prior to 2.9.1 and contains less data than
247 V1 has been used by version prior to 2.9.1 and contains less data than
248 v2. We read both versions and check if no data in v2 contradicts
248 v2. We read both versions and check if no data in v2 contradicts
249 v1. If there is not contradiction we can safely assume that both v1
249 v1. If there is not contradiction we can safely assume that both v1
250 and v2 were written at the same time and use the extract data in v2. If
250 and v2 were written at the same time and use the extract data in v2. If
251 there is contradiction we ignore v2 content as we assume an old version
251 there is contradiction we ignore v2 content as we assume an old version
252 of Mercurial has overwritten the mergestate file and left an old v2
252 of Mercurial has overwritten the mergestate file and left an old v2
253 file around.
253 file around.
254
254
255 returns list of record [(TYPE, data), ...]"""
255 returns list of record [(TYPE, data), ...]"""
256 v1records = self._readrecordsv1()
256 v1records = self._readrecordsv1()
257 v2records = self._readrecordsv2()
257 v2records = self._readrecordsv2()
258 if self._v1v2match(v1records, v2records):
258 if self._v1v2match(v1records, v2records):
259 return v2records
259 return v2records
260 else:
260 else:
261 # v1 file is newer than v2 file, use it
261 # v1 file is newer than v2 file, use it
262 # we have to infer the "other" changeset of the merge
262 # we have to infer the "other" changeset of the merge
263 # we cannot do better than that with v1 of the format
263 # we cannot do better than that with v1 of the format
264 mctx = self._repo[None].parents()[-1]
264 mctx = self._repo[None].parents()[-1]
265 v1records.append((RECORD_OTHER, mctx.hex()))
265 v1records.append((RECORD_OTHER, mctx.hex()))
266 # add place holder "other" file node information
266 # add place holder "other" file node information
267 # nobody is using it yet so we do no need to fetch the data
267 # nobody is using it yet so we do no need to fetch the data
268 # if mctx was wrong `mctx[bits[-2]]` may fails.
268 # if mctx was wrong `mctx[bits[-2]]` may fails.
269 for idx, r in enumerate(v1records):
269 for idx, r in enumerate(v1records):
270 if r[0] == RECORD_MERGED:
270 if r[0] == RECORD_MERGED:
271 bits = r[1].split('\0')
271 bits = r[1].split('\0')
272 bits.insert(-2, '')
272 bits.insert(-2, '')
273 v1records[idx] = (r[0], '\0'.join(bits))
273 v1records[idx] = (r[0], '\0'.join(bits))
274 return v1records
274 return v1records
275
275
276 def _v1v2match(self, v1records, v2records):
276 def _v1v2match(self, v1records, v2records):
277 oldv2 = set() # old format version of v2 record
277 oldv2 = set() # old format version of v2 record
278 for rec in v2records:
278 for rec in v2records:
279 if rec[0] == RECORD_LOCAL:
279 if rec[0] == RECORD_LOCAL:
280 oldv2.add(rec)
280 oldv2.add(rec)
281 elif rec[0] == RECORD_MERGED:
281 elif rec[0] == RECORD_MERGED:
282 # drop the onode data (not contained in v1)
282 # drop the onode data (not contained in v1)
283 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
283 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
284 for rec in v1records:
284 for rec in v1records:
285 if rec not in oldv2:
285 if rec not in oldv2:
286 return False
286 return False
287 else:
287 else:
288 return True
288 return True
289
289
290 def _readrecordsv1(self):
290 def _readrecordsv1(self):
291 """read on disk merge state for version 1 file
291 """read on disk merge state for version 1 file
292
292
293 returns list of record [(TYPE, data), ...]
293 returns list of record [(TYPE, data), ...]
294
294
295 Note: the "F" data from this file are one entry short
295 Note: the "F" data from this file are one entry short
296 (no "other file node" entry)
296 (no "other file node" entry)
297 """
297 """
298 records = []
298 records = []
299 try:
299 try:
300 f = self._repo.vfs(self.statepathv1)
300 f = self._repo.vfs(self.statepathv1)
301 for i, l in enumerate(f):
301 for i, l in enumerate(f):
302 if i == 0:
302 if i == 0:
303 records.append((RECORD_LOCAL, l[:-1]))
303 records.append((RECORD_LOCAL, l[:-1]))
304 else:
304 else:
305 records.append((RECORD_MERGED, l[:-1]))
305 records.append((RECORD_MERGED, l[:-1]))
306 f.close()
306 f.close()
307 except IOError as err:
307 except IOError as err:
308 if err.errno != errno.ENOENT:
308 if err.errno != errno.ENOENT:
309 raise
309 raise
310 return records
310 return records
311
311
312 def _readrecordsv2(self):
312 def _readrecordsv2(self):
313 """read on disk merge state for version 2 file
313 """read on disk merge state for version 2 file
314
314
315 This format is a list of arbitrary records of the form:
315 This format is a list of arbitrary records of the form:
316
316
317 [type][length][content]
317 [type][length][content]
318
318
319 `type` is a single character, `length` is a 4 byte integer, and
319 `type` is a single character, `length` is a 4 byte integer, and
320 `content` is an arbitrary byte sequence of length `length`.
320 `content` is an arbitrary byte sequence of length `length`.
321
321
322 Mercurial versions prior to 3.7 have a bug where if there are
322 Mercurial versions prior to 3.7 have a bug where if there are
323 unsupported mandatory merge records, attempting to clear out the merge
323 unsupported mandatory merge records, attempting to clear out the merge
324 state with hg update --clean or similar aborts. The 't' record type
324 state with hg update --clean or similar aborts. The 't' record type
325 works around that by writing out what those versions treat as an
325 works around that by writing out what those versions treat as an
326 advisory record, but later versions interpret as special: the first
326 advisory record, but later versions interpret as special: the first
327 character is the 'real' record type and everything onwards is the data.
327 character is the 'real' record type and everything onwards is the data.
328
328
329 Returns list of records [(TYPE, data), ...]."""
329 Returns list of records [(TYPE, data), ...]."""
330 records = []
330 records = []
331 try:
331 try:
332 f = self._repo.vfs(self.statepathv2)
332 f = self._repo.vfs(self.statepathv2)
333 data = f.read()
333 data = f.read()
334 off = 0
334 off = 0
335 end = len(data)
335 end = len(data)
336 while off < end:
336 while off < end:
337 rtype = data[off:off + 1]
337 rtype = data[off:off + 1]
338 off += 1
338 off += 1
339 length = _unpack('>I', data[off:(off + 4)])[0]
339 length = _unpack('>I', data[off:(off + 4)])[0]
340 off += 4
340 off += 4
341 record = data[off:(off + length)]
341 record = data[off:(off + length)]
342 off += length
342 off += length
343 if rtype == RECORD_OVERRIDE:
343 if rtype == RECORD_OVERRIDE:
344 rtype, record = record[0:1], record[1:]
344 rtype, record = record[0:1], record[1:]
345 records.append((rtype, record))
345 records.append((rtype, record))
346 f.close()
346 f.close()
347 except IOError as err:
347 except IOError as err:
348 if err.errno != errno.ENOENT:
348 if err.errno != errno.ENOENT:
349 raise
349 raise
350 return records
350 return records
351
351
352 @util.propertycache
352 @util.propertycache
353 def mergedriver(self):
353 def mergedriver(self):
354 # protect against the following:
354 # protect against the following:
355 # - A configures a malicious merge driver in their hgrc, then
355 # - A configures a malicious merge driver in their hgrc, then
356 # pauses the merge
356 # pauses the merge
357 # - A edits their hgrc to remove references to the merge driver
357 # - A edits their hgrc to remove references to the merge driver
358 # - A gives a copy of their entire repo, including .hg, to B
358 # - A gives a copy of their entire repo, including .hg, to B
359 # - B inspects .hgrc and finds it to be clean
359 # - B inspects .hgrc and finds it to be clean
360 # - B then continues the merge and the malicious merge driver
360 # - B then continues the merge and the malicious merge driver
361 # gets invoked
361 # gets invoked
362 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
362 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
363 if (self._readmergedriver is not None
363 if (self._readmergedriver is not None
364 and self._readmergedriver != configmergedriver):
364 and self._readmergedriver != configmergedriver):
365 raise error.ConfigError(
365 raise error.ConfigError(
366 _("merge driver changed since merge started"),
366 _("merge driver changed since merge started"),
367 hint=_("revert merge driver change or abort merge"))
367 hint=_("revert merge driver change or abort merge"))
368
368
369 return configmergedriver
369 return configmergedriver
370
370
371 @util.propertycache
371 @util.propertycache
372 def localctx(self):
372 def localctx(self):
373 if self._local is None:
373 if self._local is None:
374 msg = "localctx accessed but self._local isn't set"
374 msg = "localctx accessed but self._local isn't set"
375 raise error.ProgrammingError(msg)
375 raise error.ProgrammingError(msg)
376 return self._repo[self._local]
376 return self._repo[self._local]
377
377
378 @util.propertycache
378 @util.propertycache
379 def otherctx(self):
379 def otherctx(self):
380 if self._other is None:
380 if self._other is None:
381 msg = "otherctx accessed but self._other isn't set"
381 msg = "otherctx accessed but self._other isn't set"
382 raise error.ProgrammingError(msg)
382 raise error.ProgrammingError(msg)
383 return self._repo[self._other]
383 return self._repo[self._other]
384
384
385 def active(self):
385 def active(self):
386 """Whether mergestate is active.
386 """Whether mergestate is active.
387
387
388 Returns True if there appears to be mergestate. This is a rough proxy
388 Returns True if there appears to be mergestate. This is a rough proxy
389 for "is a merge in progress."
389 for "is a merge in progress."
390 """
390 """
391 # Check local variables before looking at filesystem for performance
391 # Check local variables before looking at filesystem for performance
392 # reasons.
392 # reasons.
393 return bool(self._local) or bool(self._state) or \
393 return bool(self._local) or bool(self._state) or \
394 self._repo.vfs.exists(self.statepathv1) or \
394 self._repo.vfs.exists(self.statepathv1) or \
395 self._repo.vfs.exists(self.statepathv2)
395 self._repo.vfs.exists(self.statepathv2)
396
396
397 def commit(self):
397 def commit(self):
398 """Write current state on disk (if necessary)"""
398 """Write current state on disk (if necessary)"""
399 if self._dirty:
399 if self._dirty:
400 records = self._makerecords()
400 records = self._makerecords()
401 self._writerecords(records)
401 self._writerecords(records)
402 self._dirty = False
402 self._dirty = False
403
403
404 def _makerecords(self):
404 def _makerecords(self):
405 records = []
405 records = []
406 records.append((RECORD_LOCAL, hex(self._local)))
406 records.append((RECORD_LOCAL, hex(self._local)))
407 records.append((RECORD_OTHER, hex(self._other)))
407 records.append((RECORD_OTHER, hex(self._other)))
408 if self.mergedriver:
408 if self.mergedriver:
409 records.append((RECORD_MERGE_DRIVER_STATE, '\0'.join([
409 records.append((RECORD_MERGE_DRIVER_STATE, '\0'.join([
410 self.mergedriver, self._mdstate])))
410 self.mergedriver, self._mdstate])))
411 # Write out state items. In all cases, the value of the state map entry
411 # Write out state items. In all cases, the value of the state map entry
412 # is written as the contents of the record. The record type depends on
412 # is written as the contents of the record. The record type depends on
413 # the type of state that is stored, and capital-letter records are used
413 # the type of state that is stored, and capital-letter records are used
414 # to prevent older versions of Mercurial that do not support the feature
414 # to prevent older versions of Mercurial that do not support the feature
415 # from loading them.
415 # from loading them.
416 for filename, v in self._state.iteritems():
416 for filename, v in self._state.iteritems():
417 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
417 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
418 # Driver-resolved merge. These are stored in 'D' records.
418 # Driver-resolved merge. These are stored in 'D' records.
419 records.append((RECORD_MERGE_DRIVER_MERGE,
419 records.append((RECORD_MERGE_DRIVER_MERGE,
420 '\0'.join([filename] + v)))
420 '\0'.join([filename] + v)))
421 elif v[0] in (MERGE_RECORD_UNRESOLVED_PATH,
421 elif v[0] in (MERGE_RECORD_UNRESOLVED_PATH,
422 MERGE_RECORD_RESOLVED_PATH):
422 MERGE_RECORD_RESOLVED_PATH):
423 # Path conflicts. These are stored in 'P' records. The current
423 # Path conflicts. These are stored in 'P' records. The current
424 # resolution state ('pu' or 'pr') is stored within the record.
424 # resolution state ('pu' or 'pr') is stored within the record.
425 records.append((RECORD_PATH_CONFLICT,
425 records.append((RECORD_PATH_CONFLICT,
426 '\0'.join([filename] + v)))
426 '\0'.join([filename] + v)))
427 elif v[1] == nullhex or v[6] == nullhex:
427 elif v[1] == nullhex or v[6] == nullhex:
428 # Change/Delete or Delete/Change conflicts. These are stored in
428 # Change/Delete or Delete/Change conflicts. These are stored in
429 # 'C' records. v[1] is the local file, and is nullhex when the
429 # 'C' records. v[1] is the local file, and is nullhex when the
430 # file is deleted locally ('dc'). v[6] is the remote file, and
430 # file is deleted locally ('dc'). v[6] is the remote file, and
431 # is nullhex when the file is deleted remotely ('cd').
431 # is nullhex when the file is deleted remotely ('cd').
432 records.append((RECORD_CHANGEDELETE_CONFLICT,
432 records.append((RECORD_CHANGEDELETE_CONFLICT,
433 '\0'.join([filename] + v)))
433 '\0'.join([filename] + v)))
434 else:
434 else:
435 # Normal files. These are stored in 'F' records.
435 # Normal files. These are stored in 'F' records.
436 records.append((RECORD_MERGED,
436 records.append((RECORD_MERGED,
437 '\0'.join([filename] + v)))
437 '\0'.join([filename] + v)))
438 for filename, extras in sorted(self._stateextras.iteritems()):
438 for filename, extras in sorted(self._stateextras.iteritems()):
439 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
439 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
440 extras.iteritems())
440 extras.iteritems())
441 records.append((RECORD_FILE_VALUES,
441 records.append((RECORD_FILE_VALUES,
442 '%s\0%s' % (filename, rawextras)))
442 '%s\0%s' % (filename, rawextras)))
443 if self._labels is not None:
443 if self._labels is not None:
444 labels = '\0'.join(self._labels)
444 labels = '\0'.join(self._labels)
445 records.append((RECORD_LABELS, labels))
445 records.append((RECORD_LABELS, labels))
446 return records
446 return records
447
447
448 def _writerecords(self, records):
448 def _writerecords(self, records):
449 """Write current state on disk (both v1 and v2)"""
449 """Write current state on disk (both v1 and v2)"""
450 self._writerecordsv1(records)
450 self._writerecordsv1(records)
451 self._writerecordsv2(records)
451 self._writerecordsv2(records)
452
452
453 def _writerecordsv1(self, records):
453 def _writerecordsv1(self, records):
454 """Write current state on disk in a version 1 file"""
454 """Write current state on disk in a version 1 file"""
455 f = self._repo.vfs(self.statepathv1, 'wb')
455 f = self._repo.vfs(self.statepathv1, 'wb')
456 irecords = iter(records)
456 irecords = iter(records)
457 lrecords = next(irecords)
457 lrecords = next(irecords)
458 assert lrecords[0] == RECORD_LOCAL
458 assert lrecords[0] == RECORD_LOCAL
459 f.write(hex(self._local) + '\n')
459 f.write(hex(self._local) + '\n')
460 for rtype, data in irecords:
460 for rtype, data in irecords:
461 if rtype == RECORD_MERGED:
461 if rtype == RECORD_MERGED:
462 f.write('%s\n' % _droponode(data))
462 f.write('%s\n' % _droponode(data))
463 f.close()
463 f.close()
464
464
465 def _writerecordsv2(self, records):
465 def _writerecordsv2(self, records):
466 """Write current state on disk in a version 2 file
466 """Write current state on disk in a version 2 file
467
467
468 See the docstring for _readrecordsv2 for why we use 't'."""
468 See the docstring for _readrecordsv2 for why we use 't'."""
469 # these are the records that all version 2 clients can read
469 # these are the records that all version 2 clients can read
470 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
470 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
471 f = self._repo.vfs(self.statepathv2, 'wb')
471 f = self._repo.vfs(self.statepathv2, 'wb')
472 for key, data in records:
472 for key, data in records:
473 assert len(key) == 1
473 assert len(key) == 1
474 if key not in allowlist:
474 if key not in allowlist:
475 key, data = RECORD_OVERRIDE, '%s%s' % (key, data)
475 key, data = RECORD_OVERRIDE, '%s%s' % (key, data)
476 format = '>sI%is' % len(data)
476 format = '>sI%is' % len(data)
477 f.write(_pack(format, key, len(data), data))
477 f.write(_pack(format, key, len(data), data))
478 f.close()
478 f.close()
479
479
480 def add(self, fcl, fco, fca, fd):
480 def add(self, fcl, fco, fca, fd):
481 """add a new (potentially?) conflicting file the merge state
481 """add a new (potentially?) conflicting file the merge state
482 fcl: file context for local,
482 fcl: file context for local,
483 fco: file context for remote,
483 fco: file context for remote,
484 fca: file context for ancestors,
484 fca: file context for ancestors,
485 fd: file path of the resulting merge.
485 fd: file path of the resulting merge.
486
486
487 note: also write the local version to the `.hg/merge` directory.
487 note: also write the local version to the `.hg/merge` directory.
488 """
488 """
489 if fcl.isabsent():
489 if fcl.isabsent():
490 hash = nullhex
490 hash = nullhex
491 else:
491 else:
492 hash = hex(hashlib.sha1(fcl.path()).digest())
492 hash = hex(hashlib.sha1(fcl.path()).digest())
493 self._repo.vfs.write('merge/' + hash, fcl.data())
493 self._repo.vfs.write('merge/' + hash, fcl.data())
494 self._state[fd] = [MERGE_RECORD_UNRESOLVED, hash, fcl.path(),
494 self._state[fd] = [MERGE_RECORD_UNRESOLVED, hash, fcl.path(),
495 fca.path(), hex(fca.filenode()),
495 fca.path(), hex(fca.filenode()),
496 fco.path(), hex(fco.filenode()),
496 fco.path(), hex(fco.filenode()),
497 fcl.flags()]
497 fcl.flags()]
498 self._stateextras[fd] = {'ancestorlinknode': hex(fca.node())}
498 self._stateextras[fd] = {'ancestorlinknode': hex(fca.node())}
499 self._dirty = True
499 self._dirty = True
500
500
501 def addpath(self, path, frename, forigin):
501 def addpath(self, path, frename, forigin):
502 """add a new conflicting path to the merge state
502 """add a new conflicting path to the merge state
503 path: the path that conflicts
503 path: the path that conflicts
504 frename: the filename the conflicting file was renamed to
504 frename: the filename the conflicting file was renamed to
505 forigin: origin of the file ('l' or 'r' for local/remote)
505 forigin: origin of the file ('l' or 'r' for local/remote)
506 """
506 """
507 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
507 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
508 self._dirty = True
508 self._dirty = True
509
509
510 def __contains__(self, dfile):
510 def __contains__(self, dfile):
511 return dfile in self._state
511 return dfile in self._state
512
512
513 def __getitem__(self, dfile):
513 def __getitem__(self, dfile):
514 return self._state[dfile][0]
514 return self._state[dfile][0]
515
515
516 def __iter__(self):
516 def __iter__(self):
517 return iter(sorted(self._state))
517 return iter(sorted(self._state))
518
518
519 def files(self):
519 def files(self):
520 return self._state.keys()
520 return self._state.keys()
521
521
522 def mark(self, dfile, state):
522 def mark(self, dfile, state):
523 self._state[dfile][0] = state
523 self._state[dfile][0] = state
524 self._dirty = True
524 self._dirty = True
525
525
526 def mdstate(self):
526 def mdstate(self):
527 return self._mdstate
527 return self._mdstate
528
528
529 def unresolved(self):
529 def unresolved(self):
530 """Obtain the paths of unresolved files."""
530 """Obtain the paths of unresolved files."""
531
531
532 for f, entry in self._state.iteritems():
532 for f, entry in self._state.iteritems():
533 if entry[0] in (MERGE_RECORD_UNRESOLVED,
533 if entry[0] in (MERGE_RECORD_UNRESOLVED,
534 MERGE_RECORD_UNRESOLVED_PATH):
534 MERGE_RECORD_UNRESOLVED_PATH):
535 yield f
535 yield f
536
536
537 def driverresolved(self):
537 def driverresolved(self):
538 """Obtain the paths of driver-resolved files."""
538 """Obtain the paths of driver-resolved files."""
539
539
540 for f, entry in self._state.items():
540 for f, entry in self._state.items():
541 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
541 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
542 yield f
542 yield f
543
543
544 def extras(self, filename):
544 def extras(self, filename):
545 return self._stateextras.setdefault(filename, {})
545 return self._stateextras.setdefault(filename, {})
546
546
547 def _resolve(self, preresolve, dfile, wctx):
547 def _resolve(self, preresolve, dfile, wctx):
548 """rerun merge process for file path `dfile`"""
548 """rerun merge process for file path `dfile`"""
549 if self[dfile] in (MERGE_RECORD_RESOLVED,
549 if self[dfile] in (MERGE_RECORD_RESOLVED,
550 MERGE_RECORD_DRIVER_RESOLVED):
550 MERGE_RECORD_DRIVER_RESOLVED):
551 return True, 0
551 return True, 0
552 stateentry = self._state[dfile]
552 stateentry = self._state[dfile]
553 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
553 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
554 octx = self._repo[self._other]
554 octx = self._repo[self._other]
555 extras = self.extras(dfile)
555 extras = self.extras(dfile)
556 anccommitnode = extras.get('ancestorlinknode')
556 anccommitnode = extras.get('ancestorlinknode')
557 if anccommitnode:
557 if anccommitnode:
558 actx = self._repo[anccommitnode]
558 actx = self._repo[anccommitnode]
559 else:
559 else:
560 actx = None
560 actx = None
561 fcd = self._filectxorabsent(hash, wctx, dfile)
561 fcd = self._filectxorabsent(hash, wctx, dfile)
562 fco = self._filectxorabsent(onode, octx, ofile)
562 fco = self._filectxorabsent(onode, octx, ofile)
563 # TODO: move this to filectxorabsent
563 # TODO: move this to filectxorabsent
564 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
564 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
565 # "premerge" x flags
565 # "premerge" x flags
566 flo = fco.flags()
566 flo = fco.flags()
567 fla = fca.flags()
567 fla = fca.flags()
568 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
568 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
569 if fca.node() == nullid and flags != flo:
569 if fca.node() == nullid and flags != flo:
570 if preresolve:
570 if preresolve:
571 self._repo.ui.warn(
571 self._repo.ui.warn(
572 _('warning: cannot merge flags for %s '
572 _('warning: cannot merge flags for %s '
573 'without common ancestor - keeping local flags\n')
573 'without common ancestor - keeping local flags\n')
574 % afile)
574 % afile)
575 elif flags == fla:
575 elif flags == fla:
576 flags = flo
576 flags = flo
577 if preresolve:
577 if preresolve:
578 # restore local
578 # restore local
579 if hash != nullhex:
579 if hash != nullhex:
580 f = self._repo.vfs('merge/' + hash)
580 f = self._repo.vfs('merge/' + hash)
581 wctx[dfile].write(f.read(), flags)
581 wctx[dfile].write(f.read(), flags)
582 f.close()
582 f.close()
583 else:
583 else:
584 wctx[dfile].remove(ignoremissing=True)
584 wctx[dfile].remove(ignoremissing=True)
585 complete, r, deleted = filemerge.premerge(self._repo, wctx,
585 complete, r, deleted = filemerge.premerge(self._repo, wctx,
586 self._local, lfile, fcd,
586 self._local, lfile, fcd,
587 fco, fca,
587 fco, fca,
588 labels=self._labels)
588 labels=self._labels)
589 else:
589 else:
590 complete, r, deleted = filemerge.filemerge(self._repo, wctx,
590 complete, r, deleted = filemerge.filemerge(self._repo, wctx,
591 self._local, lfile, fcd,
591 self._local, lfile, fcd,
592 fco, fca,
592 fco, fca,
593 labels=self._labels)
593 labels=self._labels)
594 if r is None:
594 if r is None:
595 # no real conflict
595 # no real conflict
596 del self._state[dfile]
596 del self._state[dfile]
597 self._stateextras.pop(dfile, None)
597 self._stateextras.pop(dfile, None)
598 self._dirty = True
598 self._dirty = True
599 elif not r:
599 elif not r:
600 self.mark(dfile, MERGE_RECORD_RESOLVED)
600 self.mark(dfile, MERGE_RECORD_RESOLVED)
601
601
602 if complete:
602 if complete:
603 action = None
603 action = None
604 if deleted:
604 if deleted:
605 if fcd.isabsent():
605 if fcd.isabsent():
606 # dc: local picked. Need to drop if present, which may
606 # dc: local picked. Need to drop if present, which may
607 # happen on re-resolves.
607 # happen on re-resolves.
608 action = ACTION_FORGET
608 action = ACTION_FORGET
609 else:
609 else:
610 # cd: remote picked (or otherwise deleted)
610 # cd: remote picked (or otherwise deleted)
611 action = ACTION_REMOVE
611 action = ACTION_REMOVE
612 else:
612 else:
613 if fcd.isabsent(): # dc: remote picked
613 if fcd.isabsent(): # dc: remote picked
614 action = ACTION_GET
614 action = ACTION_GET
615 elif fco.isabsent(): # cd: local picked
615 elif fco.isabsent(): # cd: local picked
616 if dfile in self.localctx:
616 if dfile in self.localctx:
617 action = ACTION_ADD_MODIFIED
617 action = ACTION_ADD_MODIFIED
618 else:
618 else:
619 action = ACTION_ADD
619 action = ACTION_ADD
620 # else: regular merges (no action necessary)
620 # else: regular merges (no action necessary)
621 self._results[dfile] = r, action
621 self._results[dfile] = r, action
622
622
623 return complete, r
623 return complete, r
624
624
625 def _filectxorabsent(self, hexnode, ctx, f):
625 def _filectxorabsent(self, hexnode, ctx, f):
626 if hexnode == nullhex:
626 if hexnode == nullhex:
627 return filemerge.absentfilectx(ctx, f)
627 return filemerge.absentfilectx(ctx, f)
628 else:
628 else:
629 return ctx[f]
629 return ctx[f]
630
630
631 def preresolve(self, dfile, wctx):
631 def preresolve(self, dfile, wctx):
632 """run premerge process for dfile
632 """run premerge process for dfile
633
633
634 Returns whether the merge is complete, and the exit code."""
634 Returns whether the merge is complete, and the exit code."""
635 return self._resolve(True, dfile, wctx)
635 return self._resolve(True, dfile, wctx)
636
636
637 def resolve(self, dfile, wctx):
637 def resolve(self, dfile, wctx):
638 """run merge process (assuming premerge was run) for dfile
638 """run merge process (assuming premerge was run) for dfile
639
639
640 Returns the exit code of the merge."""
640 Returns the exit code of the merge."""
641 return self._resolve(False, dfile, wctx)[1]
641 return self._resolve(False, dfile, wctx)[1]
642
642
643 def counts(self):
643 def counts(self):
644 """return counts for updated, merged and removed files in this
644 """return counts for updated, merged and removed files in this
645 session"""
645 session"""
646 updated, merged, removed = 0, 0, 0
646 updated, merged, removed = 0, 0, 0
647 for r, action in self._results.itervalues():
647 for r, action in self._results.itervalues():
648 if r is None:
648 if r is None:
649 updated += 1
649 updated += 1
650 elif r == 0:
650 elif r == 0:
651 if action == ACTION_REMOVE:
651 if action == ACTION_REMOVE:
652 removed += 1
652 removed += 1
653 else:
653 else:
654 merged += 1
654 merged += 1
655 return updated, merged, removed
655 return updated, merged, removed
656
656
657 def unresolvedcount(self):
657 def unresolvedcount(self):
658 """get unresolved count for this merge (persistent)"""
658 """get unresolved count for this merge (persistent)"""
659 return len(list(self.unresolved()))
659 return len(list(self.unresolved()))
660
660
661 def actions(self):
661 def actions(self):
662 """return lists of actions to perform on the dirstate"""
662 """return lists of actions to perform on the dirstate"""
663 actions = {
663 actions = {
664 ACTION_REMOVE: [],
664 ACTION_REMOVE: [],
665 ACTION_FORGET: [],
665 ACTION_FORGET: [],
666 ACTION_ADD: [],
666 ACTION_ADD: [],
667 ACTION_ADD_MODIFIED: [],
667 ACTION_ADD_MODIFIED: [],
668 ACTION_GET: [],
668 ACTION_GET: [],
669 }
669 }
670 for f, (r, action) in self._results.iteritems():
670 for f, (r, action) in self._results.iteritems():
671 if action is not None:
671 if action is not None:
672 actions[action].append((f, None, "merge result"))
672 actions[action].append((f, None, "merge result"))
673 return actions
673 return actions
674
674
675 def recordactions(self):
675 def recordactions(self):
676 """record remove/add/get actions in the dirstate"""
676 """record remove/add/get actions in the dirstate"""
677 branchmerge = self._repo.dirstate.p2() != nullid
677 branchmerge = self._repo.dirstate.p2() != nullid
678 recordupdates(self._repo, self.actions(), branchmerge)
678 recordupdates(self._repo, self.actions(), branchmerge)
679
679
680 def queueremove(self, f):
680 def queueremove(self, f):
681 """queues a file to be removed from the dirstate
681 """queues a file to be removed from the dirstate
682
682
683 Meant for use by custom merge drivers."""
683 Meant for use by custom merge drivers."""
684 self._results[f] = 0, ACTION_REMOVE
684 self._results[f] = 0, ACTION_REMOVE
685
685
686 def queueadd(self, f):
686 def queueadd(self, f):
687 """queues a file to be added to the dirstate
687 """queues a file to be added to the dirstate
688
688
689 Meant for use by custom merge drivers."""
689 Meant for use by custom merge drivers."""
690 self._results[f] = 0, ACTION_ADD
690 self._results[f] = 0, ACTION_ADD
691
691
692 def queueget(self, f):
692 def queueget(self, f):
693 """queues a file to be marked modified in the dirstate
693 """queues a file to be marked modified in the dirstate
694
694
695 Meant for use by custom merge drivers."""
695 Meant for use by custom merge drivers."""
696 self._results[f] = 0, ACTION_GET
696 self._results[f] = 0, ACTION_GET
697
697
698 def _getcheckunknownconfig(repo, section, name):
698 def _getcheckunknownconfig(repo, section, name):
699 config = repo.ui.config(section, name)
699 config = repo.ui.config(section, name)
700 valid = ['abort', 'ignore', 'warn']
700 valid = ['abort', 'ignore', 'warn']
701 if config not in valid:
701 if config not in valid:
702 validstr = ', '.join(["'" + v + "'" for v in valid])
702 validstr = ', '.join(["'" + v + "'" for v in valid])
703 raise error.ConfigError(_("%s.%s not valid "
703 raise error.ConfigError(_("%s.%s not valid "
704 "('%s' is none of %s)")
704 "('%s' is none of %s)")
705 % (section, name, config, validstr))
705 % (section, name, config, validstr))
706 return config
706 return config
707
707
708 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
708 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
709 if wctx.isinmemory():
709 if wctx.isinmemory():
710 # Nothing to do in IMM because nothing in the "working copy" can be an
710 # Nothing to do in IMM because nothing in the "working copy" can be an
711 # unknown file.
711 # unknown file.
712 #
712 #
713 # Note that we should bail out here, not in ``_checkunknownfiles()``,
713 # Note that we should bail out here, not in ``_checkunknownfiles()``,
714 # because that function does other useful work.
714 # because that function does other useful work.
715 return False
715 return False
716
716
717 if f2 is None:
717 if f2 is None:
718 f2 = f
718 f2 = f
719 return (repo.wvfs.audit.check(f)
719 return (repo.wvfs.audit.check(f)
720 and repo.wvfs.isfileorlink(f)
720 and repo.wvfs.isfileorlink(f)
721 and repo.dirstate.normalize(f) not in repo.dirstate
721 and repo.dirstate.normalize(f) not in repo.dirstate
722 and mctx[f2].cmp(wctx[f]))
722 and mctx[f2].cmp(wctx[f]))
723
723
724 class _unknowndirschecker(object):
724 class _unknowndirschecker(object):
725 """
725 """
726 Look for any unknown files or directories that may have a path conflict
726 Look for any unknown files or directories that may have a path conflict
727 with a file. If any path prefix of the file exists as a file or link,
727 with a file. If any path prefix of the file exists as a file or link,
728 then it conflicts. If the file itself is a directory that contains any
728 then it conflicts. If the file itself is a directory that contains any
729 file that is not tracked, then it conflicts.
729 file that is not tracked, then it conflicts.
730
730
731 Returns the shortest path at which a conflict occurs, or None if there is
731 Returns the shortest path at which a conflict occurs, or None if there is
732 no conflict.
732 no conflict.
733 """
733 """
734 def __init__(self):
734 def __init__(self):
735 # A set of paths known to be good. This prevents repeated checking of
735 # A set of paths known to be good. This prevents repeated checking of
736 # dirs. It will be updated with any new dirs that are checked and found
736 # dirs. It will be updated with any new dirs that are checked and found
737 # to be safe.
737 # to be safe.
738 self._unknowndircache = set()
738 self._unknowndircache = set()
739
739
740 # A set of paths that are known to be absent. This prevents repeated
740 # A set of paths that are known to be absent. This prevents repeated
741 # checking of subdirectories that are known not to exist. It will be
741 # checking of subdirectories that are known not to exist. It will be
742 # updated with any new dirs that are checked and found to be absent.
742 # updated with any new dirs that are checked and found to be absent.
743 self._missingdircache = set()
743 self._missingdircache = set()
744
744
745 def __call__(self, repo, wctx, f):
745 def __call__(self, repo, wctx, f):
746 if wctx.isinmemory():
746 if wctx.isinmemory():
747 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
747 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
748 return False
748 return False
749
749
750 # Check for path prefixes that exist as unknown files.
750 # Check for path prefixes that exist as unknown files.
751 for p in reversed(list(util.finddirs(f))):
751 for p in reversed(list(util.finddirs(f))):
752 if p in self._missingdircache:
752 if p in self._missingdircache:
753 return
753 return
754 if p in self._unknowndircache:
754 if p in self._unknowndircache:
755 continue
755 continue
756 if repo.wvfs.audit.check(p):
756 if repo.wvfs.audit.check(p):
757 if (repo.wvfs.isfileorlink(p)
757 if (repo.wvfs.isfileorlink(p)
758 and repo.dirstate.normalize(p) not in repo.dirstate):
758 and repo.dirstate.normalize(p) not in repo.dirstate):
759 return p
759 return p
760 if not repo.wvfs.lexists(p):
760 if not repo.wvfs.lexists(p):
761 self._missingdircache.add(p)
761 self._missingdircache.add(p)
762 return
762 return
763 self._unknowndircache.add(p)
763 self._unknowndircache.add(p)
764
764
765 # Check if the file conflicts with a directory containing unknown files.
765 # Check if the file conflicts with a directory containing unknown files.
766 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
766 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
767 # Does the directory contain any files that are not in the dirstate?
767 # Does the directory contain any files that are not in the dirstate?
768 for p, dirs, files in repo.wvfs.walk(f):
768 for p, dirs, files in repo.wvfs.walk(f):
769 for fn in files:
769 for fn in files:
770 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
770 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
771 relf = repo.dirstate.normalize(relf, isknown=True)
771 relf = repo.dirstate.normalize(relf, isknown=True)
772 if relf not in repo.dirstate:
772 if relf not in repo.dirstate:
773 return f
773 return f
774 return None
774 return None
775
775
776 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
776 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
777 """
777 """
778 Considers any actions that care about the presence of conflicting unknown
778 Considers any actions that care about the presence of conflicting unknown
779 files. For some actions, the result is to abort; for others, it is to
779 files. For some actions, the result is to abort; for others, it is to
780 choose a different action.
780 choose a different action.
781 """
781 """
782 fileconflicts = set()
782 fileconflicts = set()
783 pathconflicts = set()
783 pathconflicts = set()
784 warnconflicts = set()
784 warnconflicts = set()
785 abortconflicts = set()
785 abortconflicts = set()
786 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
786 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
787 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
787 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
788 pathconfig = repo.ui.configbool('experimental', 'merge.checkpathconflicts')
788 pathconfig = repo.ui.configbool('experimental', 'merge.checkpathconflicts')
789 if not force:
789 if not force:
790 def collectconflicts(conflicts, config):
790 def collectconflicts(conflicts, config):
791 if config == 'abort':
791 if config == 'abort':
792 abortconflicts.update(conflicts)
792 abortconflicts.update(conflicts)
793 elif config == 'warn':
793 elif config == 'warn':
794 warnconflicts.update(conflicts)
794 warnconflicts.update(conflicts)
795
795
796 checkunknowndirs = _unknowndirschecker()
796 checkunknowndirs = _unknowndirschecker()
797 for f, (m, args, msg) in actions.iteritems():
797 for f, (m, args, msg) in actions.iteritems():
798 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED):
798 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED):
799 if _checkunknownfile(repo, wctx, mctx, f):
799 if _checkunknownfile(repo, wctx, mctx, f):
800 fileconflicts.add(f)
800 fileconflicts.add(f)
801 elif pathconfig and f not in wctx:
801 elif pathconfig and f not in wctx:
802 path = checkunknowndirs(repo, wctx, f)
802 path = checkunknowndirs(repo, wctx, f)
803 if path is not None:
803 if path is not None:
804 pathconflicts.add(path)
804 pathconflicts.add(path)
805 elif m == ACTION_LOCAL_DIR_RENAME_GET:
805 elif m == ACTION_LOCAL_DIR_RENAME_GET:
806 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
806 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
807 fileconflicts.add(f)
807 fileconflicts.add(f)
808
808
809 allconflicts = fileconflicts | pathconflicts
809 allconflicts = fileconflicts | pathconflicts
810 ignoredconflicts = set([c for c in allconflicts
810 ignoredconflicts = set([c for c in allconflicts
811 if repo.dirstate._ignore(c)])
811 if repo.dirstate._ignore(c)])
812 unknownconflicts = allconflicts - ignoredconflicts
812 unknownconflicts = allconflicts - ignoredconflicts
813 collectconflicts(ignoredconflicts, ignoredconfig)
813 collectconflicts(ignoredconflicts, ignoredconfig)
814 collectconflicts(unknownconflicts, unknownconfig)
814 collectconflicts(unknownconflicts, unknownconfig)
815 else:
815 else:
816 for f, (m, args, msg) in actions.iteritems():
816 for f, (m, args, msg) in actions.iteritems():
817 if m == ACTION_CREATED_MERGE:
817 if m == ACTION_CREATED_MERGE:
818 fl2, anc = args
818 fl2, anc = args
819 different = _checkunknownfile(repo, wctx, mctx, f)
819 different = _checkunknownfile(repo, wctx, mctx, f)
820 if repo.dirstate._ignore(f):
820 if repo.dirstate._ignore(f):
821 config = ignoredconfig
821 config = ignoredconfig
822 else:
822 else:
823 config = unknownconfig
823 config = unknownconfig
824
824
825 # The behavior when force is True is described by this table:
825 # The behavior when force is True is described by this table:
826 # config different mergeforce | action backup
826 # config different mergeforce | action backup
827 # * n * | get n
827 # * n * | get n
828 # * y y | merge -
828 # * y y | merge -
829 # abort y n | merge - (1)
829 # abort y n | merge - (1)
830 # warn y n | warn + get y
830 # warn y n | warn + get y
831 # ignore y n | get y
831 # ignore y n | get y
832 #
832 #
833 # (1) this is probably the wrong behavior here -- we should
833 # (1) this is probably the wrong behavior here -- we should
834 # probably abort, but some actions like rebases currently
834 # probably abort, but some actions like rebases currently
835 # don't like an abort happening in the middle of
835 # don't like an abort happening in the middle of
836 # merge.update.
836 # merge.update.
837 if not different:
837 if not different:
838 actions[f] = (ACTION_GET, (fl2, False), 'remote created')
838 actions[f] = (ACTION_GET, (fl2, False), 'remote created')
839 elif mergeforce or config == 'abort':
839 elif mergeforce or config == 'abort':
840 actions[f] = (ACTION_MERGE, (f, f, None, False, anc),
840 actions[f] = (ACTION_MERGE, (f, f, None, False, anc),
841 'remote differs from untracked local')
841 'remote differs from untracked local')
842 elif config == 'abort':
842 elif config == 'abort':
843 abortconflicts.add(f)
843 abortconflicts.add(f)
844 else:
844 else:
845 if config == 'warn':
845 if config == 'warn':
846 warnconflicts.add(f)
846 warnconflicts.add(f)
847 actions[f] = (ACTION_GET, (fl2, True), 'remote created')
847 actions[f] = (ACTION_GET, (fl2, True), 'remote created')
848
848
849 for f in sorted(abortconflicts):
849 for f in sorted(abortconflicts):
850 warn = repo.ui.warn
850 warn = repo.ui.warn
851 if f in pathconflicts:
851 if f in pathconflicts:
852 if repo.wvfs.isfileorlink(f):
852 if repo.wvfs.isfileorlink(f):
853 warn(_("%s: untracked file conflicts with directory\n") % f)
853 warn(_("%s: untracked file conflicts with directory\n") % f)
854 else:
854 else:
855 warn(_("%s: untracked directory conflicts with file\n") % f)
855 warn(_("%s: untracked directory conflicts with file\n") % f)
856 else:
856 else:
857 warn(_("%s: untracked file differs\n") % f)
857 warn(_("%s: untracked file differs\n") % f)
858 if abortconflicts:
858 if abortconflicts:
859 raise error.Abort(_("untracked files in working directory "
859 raise error.Abort(_("untracked files in working directory "
860 "differ from files in requested revision"))
860 "differ from files in requested revision"))
861
861
862 for f in sorted(warnconflicts):
862 for f in sorted(warnconflicts):
863 if repo.wvfs.isfileorlink(f):
863 if repo.wvfs.isfileorlink(f):
864 repo.ui.warn(_("%s: replacing untracked file\n") % f)
864 repo.ui.warn(_("%s: replacing untracked file\n") % f)
865 else:
865 else:
866 repo.ui.warn(_("%s: replacing untracked files in directory\n") % f)
866 repo.ui.warn(_("%s: replacing untracked files in directory\n") % f)
867
867
868 for f, (m, args, msg) in actions.iteritems():
868 for f, (m, args, msg) in actions.iteritems():
869 if m == ACTION_CREATED:
869 if m == ACTION_CREATED:
870 backup = (f in fileconflicts or f in pathconflicts or
870 backup = (f in fileconflicts or f in pathconflicts or
871 any(p in pathconflicts for p in util.finddirs(f)))
871 any(p in pathconflicts for p in util.finddirs(f)))
872 flags, = args
872 flags, = args
873 actions[f] = (ACTION_GET, (flags, backup), msg)
873 actions[f] = (ACTION_GET, (flags, backup), msg)
874
874
875 def _forgetremoved(wctx, mctx, branchmerge):
875 def _forgetremoved(wctx, mctx, branchmerge):
876 """
876 """
877 Forget removed files
877 Forget removed files
878
878
879 If we're jumping between revisions (as opposed to merging), and if
879 If we're jumping between revisions (as opposed to merging), and if
880 neither the working directory nor the target rev has the file,
880 neither the working directory nor the target rev has the file,
881 then we need to remove it from the dirstate, to prevent the
881 then we need to remove it from the dirstate, to prevent the
882 dirstate from listing the file when it is no longer in the
882 dirstate from listing the file when it is no longer in the
883 manifest.
883 manifest.
884
884
885 If we're merging, and the other revision has removed a file
885 If we're merging, and the other revision has removed a file
886 that is not present in the working directory, we need to mark it
886 that is not present in the working directory, we need to mark it
887 as removed.
887 as removed.
888 """
888 """
889
889
890 actions = {}
890 actions = {}
891 m = ACTION_FORGET
891 m = ACTION_FORGET
892 if branchmerge:
892 if branchmerge:
893 m = ACTION_REMOVE
893 m = ACTION_REMOVE
894 for f in wctx.deleted():
894 for f in wctx.deleted():
895 if f not in mctx:
895 if f not in mctx:
896 actions[f] = m, None, "forget deleted"
896 actions[f] = m, None, "forget deleted"
897
897
898 if not branchmerge:
898 if not branchmerge:
899 for f in wctx.removed():
899 for f in wctx.removed():
900 if f not in mctx:
900 if f not in mctx:
901 actions[f] = ACTION_FORGET, None, "forget removed"
901 actions[f] = ACTION_FORGET, None, "forget removed"
902
902
903 return actions
903 return actions
904
904
905 def _checkcollision(repo, wmf, actions):
905 def _checkcollision(repo, wmf, actions):
906 """
906 """
907 Check for case-folding collisions.
907 Check for case-folding collisions.
908 """
908 """
909
909
910 # If the repo is narrowed, filter out files outside the narrowspec.
910 # If the repo is narrowed, filter out files outside the narrowspec.
911 narrowmatch = repo.narrowmatch()
911 narrowmatch = repo.narrowmatch()
912 if not narrowmatch.always():
912 if not narrowmatch.always():
913 wmf = wmf.matches(narrowmatch)
913 wmf = wmf.matches(narrowmatch)
914 if actions:
914 if actions:
915 narrowactions = {}
915 narrowactions = {}
916 for m, actionsfortype in actions.iteritems():
916 for m, actionsfortype in actions.iteritems():
917 narrowactions[m] = []
917 narrowactions[m] = []
918 for (f, args, msg) in actionsfortype:
918 for (f, args, msg) in actionsfortype:
919 if narrowmatch(f):
919 if narrowmatch(f):
920 narrowactions[m].append((f, args, msg))
920 narrowactions[m].append((f, args, msg))
921 actions = narrowactions
921 actions = narrowactions
922
922
923 # build provisional merged manifest up
923 # build provisional merged manifest up
924 pmmf = set(wmf)
924 pmmf = set(wmf)
925
925
926 if actions:
926 if actions:
927 # KEEP and EXEC are no-op
927 # KEEP and EXEC are no-op
928 for m in (ACTION_ADD, ACTION_ADD_MODIFIED, ACTION_FORGET, ACTION_GET,
928 for m in (ACTION_ADD, ACTION_ADD_MODIFIED, ACTION_FORGET, ACTION_GET,
929 ACTION_CHANGED_DELETED, ACTION_DELETED_CHANGED):
929 ACTION_CHANGED_DELETED, ACTION_DELETED_CHANGED):
930 for f, args, msg in actions[m]:
930 for f, args, msg in actions[m]:
931 pmmf.add(f)
931 pmmf.add(f)
932 for f, args, msg in actions[ACTION_REMOVE]:
932 for f, args, msg in actions[ACTION_REMOVE]:
933 pmmf.discard(f)
933 pmmf.discard(f)
934 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
934 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
935 f2, flags = args
935 f2, flags = args
936 pmmf.discard(f2)
936 pmmf.discard(f2)
937 pmmf.add(f)
937 pmmf.add(f)
938 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
938 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
939 pmmf.add(f)
939 pmmf.add(f)
940 for f, args, msg in actions[ACTION_MERGE]:
940 for f, args, msg in actions[ACTION_MERGE]:
941 f1, f2, fa, move, anc = args
941 f1, f2, fa, move, anc = args
942 if move:
942 if move:
943 pmmf.discard(f1)
943 pmmf.discard(f1)
944 pmmf.add(f)
944 pmmf.add(f)
945
945
946 # check case-folding collision in provisional merged manifest
946 # check case-folding collision in provisional merged manifest
947 foldmap = {}
947 foldmap = {}
948 for f in pmmf:
948 for f in pmmf:
949 fold = util.normcase(f)
949 fold = util.normcase(f)
950 if fold in foldmap:
950 if fold in foldmap:
951 raise error.Abort(_("case-folding collision between %s and %s")
951 raise error.Abort(_("case-folding collision between %s and %s")
952 % (f, foldmap[fold]))
952 % (f, foldmap[fold]))
953 foldmap[fold] = f
953 foldmap[fold] = f
954
954
955 # check case-folding of directories
955 # check case-folding of directories
956 foldprefix = unfoldprefix = lastfull = ''
956 foldprefix = unfoldprefix = lastfull = ''
957 for fold, f in sorted(foldmap.items()):
957 for fold, f in sorted(foldmap.items()):
958 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
958 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
959 # the folded prefix matches but actual casing is different
959 # the folded prefix matches but actual casing is different
960 raise error.Abort(_("case-folding collision between "
960 raise error.Abort(_("case-folding collision between "
961 "%s and directory of %s") % (lastfull, f))
961 "%s and directory of %s") % (lastfull, f))
962 foldprefix = fold + '/'
962 foldprefix = fold + '/'
963 unfoldprefix = f + '/'
963 unfoldprefix = f + '/'
964 lastfull = f
964 lastfull = f
965
965
966 def driverpreprocess(repo, ms, wctx, labels=None):
966 def driverpreprocess(repo, ms, wctx, labels=None):
967 """run the preprocess step of the merge driver, if any
967 """run the preprocess step of the merge driver, if any
968
968
969 This is currently not implemented -- it's an extension point."""
969 This is currently not implemented -- it's an extension point."""
970 return True
970 return True
971
971
972 def driverconclude(repo, ms, wctx, labels=None):
972 def driverconclude(repo, ms, wctx, labels=None):
973 """run the conclude step of the merge driver, if any
973 """run the conclude step of the merge driver, if any
974
974
975 This is currently not implemented -- it's an extension point."""
975 This is currently not implemented -- it's an extension point."""
976 return True
976 return True
977
977
978 def _filesindirs(repo, manifest, dirs):
978 def _filesindirs(repo, manifest, dirs):
979 """
979 """
980 Generator that yields pairs of all the files in the manifest that are found
980 Generator that yields pairs of all the files in the manifest that are found
981 inside the directories listed in dirs, and which directory they are found
981 inside the directories listed in dirs, and which directory they are found
982 in.
982 in.
983 """
983 """
984 for f in manifest:
984 for f in manifest:
985 for p in util.finddirs(f):
985 for p in util.finddirs(f):
986 if p in dirs:
986 if p in dirs:
987 yield f, p
987 yield f, p
988 break
988 break
989
989
990 def checkpathconflicts(repo, wctx, mctx, actions):
990 def checkpathconflicts(repo, wctx, mctx, actions):
991 """
991 """
992 Check if any actions introduce path conflicts in the repository, updating
992 Check if any actions introduce path conflicts in the repository, updating
993 actions to record or handle the path conflict accordingly.
993 actions to record or handle the path conflict accordingly.
994 """
994 """
995 mf = wctx.manifest()
995 mf = wctx.manifest()
996
996
997 # The set of local files that conflict with a remote directory.
997 # The set of local files that conflict with a remote directory.
998 localconflicts = set()
998 localconflicts = set()
999
999
1000 # The set of directories that conflict with a remote file, and so may cause
1000 # The set of directories that conflict with a remote file, and so may cause
1001 # conflicts if they still contain any files after the merge.
1001 # conflicts if they still contain any files after the merge.
1002 remoteconflicts = set()
1002 remoteconflicts = set()
1003
1003
1004 # The set of directories that appear as both a file and a directory in the
1004 # The set of directories that appear as both a file and a directory in the
1005 # remote manifest. These indicate an invalid remote manifest, which
1005 # remote manifest. These indicate an invalid remote manifest, which
1006 # can't be updated to cleanly.
1006 # can't be updated to cleanly.
1007 invalidconflicts = set()
1007 invalidconflicts = set()
1008
1008
1009 # The set of directories that contain files that are being created.
1009 # The set of directories that contain files that are being created.
1010 createdfiledirs = set()
1010 createdfiledirs = set()
1011
1011
1012 # The set of files deleted by all the actions.
1012 # The set of files deleted by all the actions.
1013 deletedfiles = set()
1013 deletedfiles = set()
1014
1014
1015 for f, (m, args, msg) in actions.items():
1015 for f, (m, args, msg) in actions.items():
1016 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED, ACTION_MERGE,
1016 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED, ACTION_MERGE,
1017 ACTION_CREATED_MERGE):
1017 ACTION_CREATED_MERGE):
1018 # This action may create a new local file.
1018 # This action may create a new local file.
1019 createdfiledirs.update(util.finddirs(f))
1019 createdfiledirs.update(util.finddirs(f))
1020 if mf.hasdir(f):
1020 if mf.hasdir(f):
1021 # The file aliases a local directory. This might be ok if all
1021 # The file aliases a local directory. This might be ok if all
1022 # the files in the local directory are being deleted. This
1022 # the files in the local directory are being deleted. This
1023 # will be checked once we know what all the deleted files are.
1023 # will be checked once we know what all the deleted files are.
1024 remoteconflicts.add(f)
1024 remoteconflicts.add(f)
1025 # Track the names of all deleted files.
1025 # Track the names of all deleted files.
1026 if m == ACTION_REMOVE:
1026 if m == ACTION_REMOVE:
1027 deletedfiles.add(f)
1027 deletedfiles.add(f)
1028 if m == ACTION_MERGE:
1028 if m == ACTION_MERGE:
1029 f1, f2, fa, move, anc = args
1029 f1, f2, fa, move, anc = args
1030 if move:
1030 if move:
1031 deletedfiles.add(f1)
1031 deletedfiles.add(f1)
1032 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1032 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1033 f2, flags = args
1033 f2, flags = args
1034 deletedfiles.add(f2)
1034 deletedfiles.add(f2)
1035
1035
1036 # Check all directories that contain created files for path conflicts.
1036 # Check all directories that contain created files for path conflicts.
1037 for p in createdfiledirs:
1037 for p in createdfiledirs:
1038 if p in mf:
1038 if p in mf:
1039 if p in mctx:
1039 if p in mctx:
1040 # A file is in a directory which aliases both a local
1040 # A file is in a directory which aliases both a local
1041 # and a remote file. This is an internal inconsistency
1041 # and a remote file. This is an internal inconsistency
1042 # within the remote manifest.
1042 # within the remote manifest.
1043 invalidconflicts.add(p)
1043 invalidconflicts.add(p)
1044 else:
1044 else:
1045 # A file is in a directory which aliases a local file.
1045 # A file is in a directory which aliases a local file.
1046 # We will need to rename the local file.
1046 # We will need to rename the local file.
1047 localconflicts.add(p)
1047 localconflicts.add(p)
1048 if p in actions and actions[p][0] in (ACTION_CREATED,
1048 if p in actions and actions[p][0] in (ACTION_CREATED,
1049 ACTION_DELETED_CHANGED,
1049 ACTION_DELETED_CHANGED,
1050 ACTION_MERGE,
1050 ACTION_MERGE,
1051 ACTION_CREATED_MERGE):
1051 ACTION_CREATED_MERGE):
1052 # The file is in a directory which aliases a remote file.
1052 # The file is in a directory which aliases a remote file.
1053 # This is an internal inconsistency within the remote
1053 # This is an internal inconsistency within the remote
1054 # manifest.
1054 # manifest.
1055 invalidconflicts.add(p)
1055 invalidconflicts.add(p)
1056
1056
1057 # Rename all local conflicting files that have not been deleted.
1057 # Rename all local conflicting files that have not been deleted.
1058 for p in localconflicts:
1058 for p in localconflicts:
1059 if p not in deletedfiles:
1059 if p not in deletedfiles:
1060 ctxname = bytes(wctx).rstrip('+')
1060 ctxname = bytes(wctx).rstrip('+')
1061 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1061 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1062 actions[pnew] = (ACTION_PATH_CONFLICT_RESOLVE, (p,),
1062 actions[pnew] = (ACTION_PATH_CONFLICT_RESOLVE, (p,),
1063 'local path conflict')
1063 'local path conflict')
1064 actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'),
1064 actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'),
1065 'path conflict')
1065 'path conflict')
1066
1066
1067 if remoteconflicts:
1067 if remoteconflicts:
1068 # Check if all files in the conflicting directories have been removed.
1068 # Check if all files in the conflicting directories have been removed.
1069 ctxname = bytes(mctx).rstrip('+')
1069 ctxname = bytes(mctx).rstrip('+')
1070 for f, p in _filesindirs(repo, mf, remoteconflicts):
1070 for f, p in _filesindirs(repo, mf, remoteconflicts):
1071 if f not in deletedfiles:
1071 if f not in deletedfiles:
1072 m, args, msg = actions[p]
1072 m, args, msg = actions[p]
1073 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1073 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1074 if m in (ACTION_DELETED_CHANGED, ACTION_MERGE):
1074 if m in (ACTION_DELETED_CHANGED, ACTION_MERGE):
1075 # Action was merge, just update target.
1075 # Action was merge, just update target.
1076 actions[pnew] = (m, args, msg)
1076 actions[pnew] = (m, args, msg)
1077 else:
1077 else:
1078 # Action was create, change to renamed get action.
1078 # Action was create, change to renamed get action.
1079 fl = args[0]
1079 fl = args[0]
1080 actions[pnew] = (ACTION_LOCAL_DIR_RENAME_GET, (p, fl),
1080 actions[pnew] = (ACTION_LOCAL_DIR_RENAME_GET, (p, fl),
1081 'remote path conflict')
1081 'remote path conflict')
1082 actions[p] = (ACTION_PATH_CONFLICT, (pnew, ACTION_REMOVE),
1082 actions[p] = (ACTION_PATH_CONFLICT, (pnew, ACTION_REMOVE),
1083 'path conflict')
1083 'path conflict')
1084 remoteconflicts.remove(p)
1084 remoteconflicts.remove(p)
1085 break
1085 break
1086
1086
1087 if invalidconflicts:
1087 if invalidconflicts:
1088 for p in invalidconflicts:
1088 for p in invalidconflicts:
1089 repo.ui.warn(_("%s: is both a file and a directory\n") % p)
1089 repo.ui.warn(_("%s: is both a file and a directory\n") % p)
1090 raise error.Abort(_("destination manifest contains path conflicts"))
1090 raise error.Abort(_("destination manifest contains path conflicts"))
1091
1091
1092 def _filternarrowactions(narrowmatch, branchmerge, actions):
1092 def _filternarrowactions(narrowmatch, branchmerge, actions):
1093 """
1093 """
1094 Filters out actions that can ignored because the repo is narrowed.
1094 Filters out actions that can ignored because the repo is narrowed.
1095
1095
1096 Raise an exception if the merge cannot be completed because the repo is
1096 Raise an exception if the merge cannot be completed because the repo is
1097 narrowed.
1097 narrowed.
1098 """
1098 """
1099 nooptypes = set(['k']) # TODO: handle with nonconflicttypes
1099 nooptypes = set(['k']) # TODO: handle with nonconflicttypes
1100 nonconflicttypes = set('a am c cm f g r e'.split())
1100 nonconflicttypes = set('a am c cm f g r e'.split())
1101 # We mutate the items in the dict during iteration, so iterate
1101 # We mutate the items in the dict during iteration, so iterate
1102 # over a copy.
1102 # over a copy.
1103 for f, action in list(actions.items()):
1103 for f, action in list(actions.items()):
1104 if narrowmatch(f):
1104 if narrowmatch(f):
1105 pass
1105 pass
1106 elif not branchmerge:
1106 elif not branchmerge:
1107 del actions[f] # just updating, ignore changes outside clone
1107 del actions[f] # just updating, ignore changes outside clone
1108 elif action[0] in nooptypes:
1108 elif action[0] in nooptypes:
1109 del actions[f] # merge does not affect file
1109 del actions[f] # merge does not affect file
1110 elif action[0] in nonconflicttypes:
1110 elif action[0] in nonconflicttypes:
1111 raise error.Abort(_('merge affects file \'%s\' outside narrow, '
1111 raise error.Abort(_('merge affects file \'%s\' outside narrow, '
1112 'which is not yet supported') % f,
1112 'which is not yet supported') % f,
1113 hint=_('merging in the other direction '
1113 hint=_('merging in the other direction '
1114 'may work'))
1114 'may work'))
1115 else:
1115 else:
1116 raise error.Abort(_('conflict in file \'%s\' is outside '
1116 raise error.Abort(_('conflict in file \'%s\' is outside '
1117 'narrow clone') % f)
1117 'narrow clone') % f)
1118
1118
1119 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
1119 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
1120 acceptremote, followcopies, forcefulldiff=False):
1120 acceptremote, followcopies, forcefulldiff=False):
1121 """
1121 """
1122 Merge wctx and p2 with ancestor pa and generate merge action list
1122 Merge wctx and p2 with ancestor pa and generate merge action list
1123
1123
1124 branchmerge and force are as passed in to update
1124 branchmerge and force are as passed in to update
1125 matcher = matcher to filter file lists
1125 matcher = matcher to filter file lists
1126 acceptremote = accept the incoming changes without prompting
1126 acceptremote = accept the incoming changes without prompting
1127 """
1127 """
1128 if matcher is not None and matcher.always():
1128 if matcher is not None and matcher.always():
1129 matcher = None
1129 matcher = None
1130
1130
1131 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
1131 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
1132
1132
1133 # manifests fetched in order are going to be faster, so prime the caches
1133 # manifests fetched in order are going to be faster, so prime the caches
1134 [x.manifest() for x in
1134 [x.manifest() for x in
1135 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
1135 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
1136
1136
1137 if followcopies:
1137 if followcopies:
1138 ret = copies.mergecopies(repo, wctx, p2, pa)
1138 ret = copies.mergecopies(repo, wctx, p2, pa)
1139 copy, movewithdir, diverge, renamedelete, dirmove = ret
1139 copy, movewithdir, diverge, renamedelete, dirmove = ret
1140
1140
1141 boolbm = pycompat.bytestr(bool(branchmerge))
1141 boolbm = pycompat.bytestr(bool(branchmerge))
1142 boolf = pycompat.bytestr(bool(force))
1142 boolf = pycompat.bytestr(bool(force))
1143 boolm = pycompat.bytestr(bool(matcher))
1143 boolm = pycompat.bytestr(bool(matcher))
1144 repo.ui.note(_("resolving manifests\n"))
1144 repo.ui.note(_("resolving manifests\n"))
1145 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
1145 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
1146 % (boolbm, boolf, boolm))
1146 % (boolbm, boolf, boolm))
1147 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
1147 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
1148
1148
1149 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
1149 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
1150 copied = set(copy.values())
1150 copied = set(copy.values())
1151 copied.update(movewithdir.values())
1151 copied.update(movewithdir.values())
1152
1152
1153 if '.hgsubstate' in m1 and wctx.rev() is None:
1153 if '.hgsubstate' in m1 and wctx.rev() is None:
1154 # Check whether sub state is modified, and overwrite the manifest
1154 # Check whether sub state is modified, and overwrite the manifest
1155 # to flag the change. If wctx is a committed revision, we shouldn't
1155 # to flag the change. If wctx is a committed revision, we shouldn't
1156 # care for the dirty state of the working directory.
1156 # care for the dirty state of the working directory.
1157 if any(wctx.sub(s).dirty() for s in wctx.substate):
1157 if any(wctx.sub(s).dirty() for s in wctx.substate):
1158 m1['.hgsubstate'] = modifiednodeid
1158 m1['.hgsubstate'] = modifiednodeid
1159
1159
1160 # Don't use m2-vs-ma optimization if:
1160 # Don't use m2-vs-ma optimization if:
1161 # - ma is the same as m1 or m2, which we're just going to diff again later
1161 # - ma is the same as m1 or m2, which we're just going to diff again later
1162 # - The caller specifically asks for a full diff, which is useful during bid
1162 # - The caller specifically asks for a full diff, which is useful during bid
1163 # merge.
1163 # merge.
1164 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
1164 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
1165 # Identify which files are relevant to the merge, so we can limit the
1165 # Identify which files are relevant to the merge, so we can limit the
1166 # total m1-vs-m2 diff to just those files. This has significant
1166 # total m1-vs-m2 diff to just those files. This has significant
1167 # performance benefits in large repositories.
1167 # performance benefits in large repositories.
1168 relevantfiles = set(ma.diff(m2).keys())
1168 relevantfiles = set(ma.diff(m2).keys())
1169
1169
1170 # For copied and moved files, we need to add the source file too.
1170 # For copied and moved files, we need to add the source file too.
1171 for copykey, copyvalue in copy.iteritems():
1171 for copykey, copyvalue in copy.iteritems():
1172 if copyvalue in relevantfiles:
1172 if copyvalue in relevantfiles:
1173 relevantfiles.add(copykey)
1173 relevantfiles.add(copykey)
1174 for movedirkey in movewithdir:
1174 for movedirkey in movewithdir:
1175 relevantfiles.add(movedirkey)
1175 relevantfiles.add(movedirkey)
1176 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
1176 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
1177 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
1177 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
1178
1178
1179 diff = m1.diff(m2, match=matcher)
1179 diff = m1.diff(m2, match=matcher)
1180
1180
1181 if matcher is None:
1181 if matcher is None:
1182 matcher = matchmod.always('', '')
1182 matcher = matchmod.always('', '')
1183
1183
1184 actions = {}
1184 actions = {}
1185 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
1185 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
1186 if n1 and n2: # file exists on both local and remote side
1186 if n1 and n2: # file exists on both local and remote side
1187 if f not in ma:
1187 if f not in ma:
1188 fa = copy.get(f, None)
1188 fa = copy.get(f, None)
1189 if fa is not None:
1189 if fa is not None:
1190 actions[f] = (ACTION_MERGE, (f, f, fa, False, pa.node()),
1190 actions[f] = (ACTION_MERGE, (f, f, fa, False, pa.node()),
1191 'both renamed from %s' % fa)
1191 'both renamed from %s' % fa)
1192 else:
1192 else:
1193 actions[f] = (ACTION_MERGE, (f, f, None, False, pa.node()),
1193 actions[f] = (ACTION_MERGE, (f, f, None, False, pa.node()),
1194 'both created')
1194 'both created')
1195 else:
1195 else:
1196 a = ma[f]
1196 a = ma[f]
1197 fla = ma.flags(f)
1197 fla = ma.flags(f)
1198 nol = 'l' not in fl1 + fl2 + fla
1198 nol = 'l' not in fl1 + fl2 + fla
1199 if n2 == a and fl2 == fla:
1199 if n2 == a and fl2 == fla:
1200 actions[f] = (ACTION_KEEP, (), 'remote unchanged')
1200 actions[f] = (ACTION_KEEP, (), 'remote unchanged')
1201 elif n1 == a and fl1 == fla: # local unchanged - use remote
1201 elif n1 == a and fl1 == fla: # local unchanged - use remote
1202 if n1 == n2: # optimization: keep local content
1202 if n1 == n2: # optimization: keep local content
1203 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1203 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1204 else:
1204 else:
1205 actions[f] = (ACTION_GET, (fl2, False),
1205 actions[f] = (ACTION_GET, (fl2, False),
1206 'remote is newer')
1206 'remote is newer')
1207 elif nol and n2 == a: # remote only changed 'x'
1207 elif nol and n2 == a: # remote only changed 'x'
1208 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1208 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1209 elif nol and n1 == a: # local only changed 'x'
1209 elif nol and n1 == a: # local only changed 'x'
1210 actions[f] = (ACTION_GET, (fl1, False), 'remote is newer')
1210 actions[f] = (ACTION_GET, (fl1, False), 'remote is newer')
1211 else: # both changed something
1211 else: # both changed something
1212 actions[f] = (ACTION_MERGE, (f, f, f, False, pa.node()),
1212 actions[f] = (ACTION_MERGE, (f, f, f, False, pa.node()),
1213 'versions differ')
1213 'versions differ')
1214 elif n1: # file exists only on local side
1214 elif n1: # file exists only on local side
1215 if f in copied:
1215 if f in copied:
1216 pass # we'll deal with it on m2 side
1216 pass # we'll deal with it on m2 side
1217 elif f in movewithdir: # directory rename, move local
1217 elif f in movewithdir: # directory rename, move local
1218 f2 = movewithdir[f]
1218 f2 = movewithdir[f]
1219 if f2 in m2:
1219 if f2 in m2:
1220 actions[f2] = (ACTION_MERGE, (f, f2, None, True, pa.node()),
1220 actions[f2] = (ACTION_MERGE, (f, f2, None, True, pa.node()),
1221 'remote directory rename, both created')
1221 'remote directory rename, both created')
1222 else:
1222 else:
1223 actions[f2] = (ACTION_DIR_RENAME_MOVE_LOCAL, (f, fl1),
1223 actions[f2] = (ACTION_DIR_RENAME_MOVE_LOCAL, (f, fl1),
1224 'remote directory rename - move from %s' % f)
1224 'remote directory rename - move from %s' % f)
1225 elif f in copy:
1225 elif f in copy:
1226 f2 = copy[f]
1226 f2 = copy[f]
1227 actions[f] = (ACTION_MERGE, (f, f2, f2, False, pa.node()),
1227 actions[f] = (ACTION_MERGE, (f, f2, f2, False, pa.node()),
1228 'local copied/moved from %s' % f2)
1228 'local copied/moved from %s' % f2)
1229 elif f in ma: # clean, a different, no remote
1229 elif f in ma: # clean, a different, no remote
1230 if n1 != ma[f]:
1230 if n1 != ma[f]:
1231 if acceptremote:
1231 if acceptremote:
1232 actions[f] = (ACTION_REMOVE, None, 'remote delete')
1232 actions[f] = (ACTION_REMOVE, None, 'remote delete')
1233 else:
1233 else:
1234 actions[f] = (ACTION_CHANGED_DELETED,
1234 actions[f] = (ACTION_CHANGED_DELETED,
1235 (f, None, f, False, pa.node()),
1235 (f, None, f, False, pa.node()),
1236 'prompt changed/deleted')
1236 'prompt changed/deleted')
1237 elif n1 == addednodeid:
1237 elif n1 == addednodeid:
1238 # This extra 'a' is added by working copy manifest to mark
1238 # This extra 'a' is added by working copy manifest to mark
1239 # the file as locally added. We should forget it instead of
1239 # the file as locally added. We should forget it instead of
1240 # deleting it.
1240 # deleting it.
1241 actions[f] = (ACTION_FORGET, None, 'remote deleted')
1241 actions[f] = (ACTION_FORGET, None, 'remote deleted')
1242 else:
1242 else:
1243 actions[f] = (ACTION_REMOVE, None, 'other deleted')
1243 actions[f] = (ACTION_REMOVE, None, 'other deleted')
1244 elif n2: # file exists only on remote side
1244 elif n2: # file exists only on remote side
1245 if f in copied:
1245 if f in copied:
1246 pass # we'll deal with it on m1 side
1246 pass # we'll deal with it on m1 side
1247 elif f in movewithdir:
1247 elif f in movewithdir:
1248 f2 = movewithdir[f]
1248 f2 = movewithdir[f]
1249 if f2 in m1:
1249 if f2 in m1:
1250 actions[f2] = (ACTION_MERGE,
1250 actions[f2] = (ACTION_MERGE,
1251 (f2, f, None, False, pa.node()),
1251 (f2, f, None, False, pa.node()),
1252 'local directory rename, both created')
1252 'local directory rename, both created')
1253 else:
1253 else:
1254 actions[f2] = (ACTION_LOCAL_DIR_RENAME_GET, (f, fl2),
1254 actions[f2] = (ACTION_LOCAL_DIR_RENAME_GET, (f, fl2),
1255 'local directory rename - get from %s' % f)
1255 'local directory rename - get from %s' % f)
1256 elif f in copy:
1256 elif f in copy:
1257 f2 = copy[f]
1257 f2 = copy[f]
1258 if f2 in m2:
1258 if f2 in m2:
1259 actions[f] = (ACTION_MERGE, (f2, f, f2, False, pa.node()),
1259 actions[f] = (ACTION_MERGE, (f2, f, f2, False, pa.node()),
1260 'remote copied from %s' % f2)
1260 'remote copied from %s' % f2)
1261 else:
1261 else:
1262 actions[f] = (ACTION_MERGE, (f2, f, f2, True, pa.node()),
1262 actions[f] = (ACTION_MERGE, (f2, f, f2, True, pa.node()),
1263 'remote moved from %s' % f2)
1263 'remote moved from %s' % f2)
1264 elif f not in ma:
1264 elif f not in ma:
1265 # local unknown, remote created: the logic is described by the
1265 # local unknown, remote created: the logic is described by the
1266 # following table:
1266 # following table:
1267 #
1267 #
1268 # force branchmerge different | action
1268 # force branchmerge different | action
1269 # n * * | create
1269 # n * * | create
1270 # y n * | create
1270 # y n * | create
1271 # y y n | create
1271 # y y n | create
1272 # y y y | merge
1272 # y y y | merge
1273 #
1273 #
1274 # Checking whether the files are different is expensive, so we
1274 # Checking whether the files are different is expensive, so we
1275 # don't do that when we can avoid it.
1275 # don't do that when we can avoid it.
1276 if not force:
1276 if not force:
1277 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1277 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1278 elif not branchmerge:
1278 elif not branchmerge:
1279 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1279 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1280 else:
1280 else:
1281 actions[f] = (ACTION_CREATED_MERGE, (fl2, pa.node()),
1281 actions[f] = (ACTION_CREATED_MERGE, (fl2, pa.node()),
1282 'remote created, get or merge')
1282 'remote created, get or merge')
1283 elif n2 != ma[f]:
1283 elif n2 != ma[f]:
1284 df = None
1284 df = None
1285 for d in dirmove:
1285 for d in dirmove:
1286 if f.startswith(d):
1286 if f.startswith(d):
1287 # new file added in a directory that was moved
1287 # new file added in a directory that was moved
1288 df = dirmove[d] + f[len(d):]
1288 df = dirmove[d] + f[len(d):]
1289 break
1289 break
1290 if df is not None and df in m1:
1290 if df is not None and df in m1:
1291 actions[df] = (ACTION_MERGE, (df, f, f, False, pa.node()),
1291 actions[df] = (ACTION_MERGE, (df, f, f, False, pa.node()),
1292 'local directory rename - respect move '
1292 'local directory rename - respect move '
1293 'from %s' % f)
1293 'from %s' % f)
1294 elif acceptremote:
1294 elif acceptremote:
1295 actions[f] = (ACTION_CREATED, (fl2,), 'remote recreating')
1295 actions[f] = (ACTION_CREATED, (fl2,), 'remote recreating')
1296 else:
1296 else:
1297 actions[f] = (ACTION_DELETED_CHANGED,
1297 actions[f] = (ACTION_DELETED_CHANGED,
1298 (None, f, f, False, pa.node()),
1298 (None, f, f, False, pa.node()),
1299 'prompt deleted/changed')
1299 'prompt deleted/changed')
1300
1300
1301 if repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1301 if repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1302 # If we are merging, look for path conflicts.
1302 # If we are merging, look for path conflicts.
1303 checkpathconflicts(repo, wctx, p2, actions)
1303 checkpathconflicts(repo, wctx, p2, actions)
1304
1304
1305 narrowmatch = repo.narrowmatch()
1305 narrowmatch = repo.narrowmatch()
1306 if not narrowmatch.always():
1306 if not narrowmatch.always():
1307 # Updates "actions" in place
1307 # Updates "actions" in place
1308 _filternarrowactions(narrowmatch, branchmerge, actions)
1308 _filternarrowactions(narrowmatch, branchmerge, actions)
1309
1309
1310 return actions, diverge, renamedelete
1310 return actions, diverge, renamedelete
1311
1311
1312 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
1312 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
1313 """Resolves false conflicts where the nodeid changed but the content
1313 """Resolves false conflicts where the nodeid changed but the content
1314 remained the same."""
1314 remained the same."""
1315 # We force a copy of actions.items() because we're going to mutate
1315 # We force a copy of actions.items() because we're going to mutate
1316 # actions as we resolve trivial conflicts.
1316 # actions as we resolve trivial conflicts.
1317 for f, (m, args, msg) in list(actions.items()):
1317 for f, (m, args, msg) in list(actions.items()):
1318 if (m == ACTION_CHANGED_DELETED and f in ancestor
1318 if (m == ACTION_CHANGED_DELETED and f in ancestor
1319 and not wctx[f].cmp(ancestor[f])):
1319 and not wctx[f].cmp(ancestor[f])):
1320 # local did change but ended up with same content
1320 # local did change but ended up with same content
1321 actions[f] = ACTION_REMOVE, None, 'prompt same'
1321 actions[f] = ACTION_REMOVE, None, 'prompt same'
1322 elif (m == ACTION_DELETED_CHANGED and f in ancestor
1322 elif (m == ACTION_DELETED_CHANGED and f in ancestor
1323 and not mctx[f].cmp(ancestor[f])):
1323 and not mctx[f].cmp(ancestor[f])):
1324 # remote did change but ended up with same content
1324 # remote did change but ended up with same content
1325 del actions[f] # don't get = keep local deleted
1325 del actions[f] # don't get = keep local deleted
1326
1326
1327 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
1327 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
1328 acceptremote, followcopies, matcher=None,
1328 acceptremote, followcopies, matcher=None,
1329 mergeforce=False):
1329 mergeforce=False):
1330 """Calculate the actions needed to merge mctx into wctx using ancestors"""
1330 """Calculate the actions needed to merge mctx into wctx using ancestors"""
1331 # Avoid cycle.
1331 # Avoid cycle.
1332 from . import sparse
1332 from . import sparse
1333
1333
1334 if len(ancestors) == 1: # default
1334 if len(ancestors) == 1: # default
1335 actions, diverge, renamedelete = manifestmerge(
1335 actions, diverge, renamedelete = manifestmerge(
1336 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
1336 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
1337 acceptremote, followcopies)
1337 acceptremote, followcopies)
1338 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1338 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1339
1339
1340 else: # only when merge.preferancestor=* - the default
1340 else: # only when merge.preferancestor=* - the default
1341 repo.ui.note(
1341 repo.ui.note(
1342 _("note: merging %s and %s using bids from ancestors %s\n") %
1342 _("note: merging %s and %s using bids from ancestors %s\n") %
1343 (wctx, mctx, _(' and ').join(pycompat.bytestr(anc)
1343 (wctx, mctx, _(' and ').join(pycompat.bytestr(anc)
1344 for anc in ancestors)))
1344 for anc in ancestors)))
1345
1345
1346 # Call for bids
1346 # Call for bids
1347 fbids = {} # mapping filename to bids (action method to list af actions)
1347 fbids = {} # mapping filename to bids (action method to list af actions)
1348 diverge, renamedelete = None, None
1348 diverge, renamedelete = None, None
1349 for ancestor in ancestors:
1349 for ancestor in ancestors:
1350 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
1350 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
1351 actions, diverge1, renamedelete1 = manifestmerge(
1351 actions, diverge1, renamedelete1 = manifestmerge(
1352 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1352 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1353 acceptremote, followcopies, forcefulldiff=True)
1353 acceptremote, followcopies, forcefulldiff=True)
1354 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1354 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1355
1355
1356 # Track the shortest set of warning on the theory that bid
1356 # Track the shortest set of warning on the theory that bid
1357 # merge will correctly incorporate more information
1357 # merge will correctly incorporate more information
1358 if diverge is None or len(diverge1) < len(diverge):
1358 if diverge is None or len(diverge1) < len(diverge):
1359 diverge = diverge1
1359 diverge = diverge1
1360 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1360 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1361 renamedelete = renamedelete1
1361 renamedelete = renamedelete1
1362
1362
1363 for f, a in sorted(actions.iteritems()):
1363 for f, a in sorted(actions.iteritems()):
1364 m, args, msg = a
1364 m, args, msg = a
1365 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1365 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1366 if f in fbids:
1366 if f in fbids:
1367 d = fbids[f]
1367 d = fbids[f]
1368 if m in d:
1368 if m in d:
1369 d[m].append(a)
1369 d[m].append(a)
1370 else:
1370 else:
1371 d[m] = [a]
1371 d[m] = [a]
1372 else:
1372 else:
1373 fbids[f] = {m: [a]}
1373 fbids[f] = {m: [a]}
1374
1374
1375 # Pick the best bid for each file
1375 # Pick the best bid for each file
1376 repo.ui.note(_('\nauction for merging merge bids\n'))
1376 repo.ui.note(_('\nauction for merging merge bids\n'))
1377 actions = {}
1377 actions = {}
1378 dms = [] # filenames that have dm actions
1378 dms = [] # filenames that have dm actions
1379 for f, bids in sorted(fbids.items()):
1379 for f, bids in sorted(fbids.items()):
1380 # bids is a mapping from action method to list af actions
1380 # bids is a mapping from action method to list af actions
1381 # Consensus?
1381 # Consensus?
1382 if len(bids) == 1: # all bids are the same kind of method
1382 if len(bids) == 1: # all bids are the same kind of method
1383 m, l = list(bids.items())[0]
1383 m, l = list(bids.items())[0]
1384 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1384 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1385 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1385 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1386 actions[f] = l[0]
1386 actions[f] = l[0]
1387 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1387 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1388 dms.append(f)
1388 dms.append(f)
1389 continue
1389 continue
1390 # If keep is an option, just do it.
1390 # If keep is an option, just do it.
1391 if ACTION_KEEP in bids:
1391 if ACTION_KEEP in bids:
1392 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1392 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1393 actions[f] = bids[ACTION_KEEP][0]
1393 actions[f] = bids[ACTION_KEEP][0]
1394 continue
1394 continue
1395 # If there are gets and they all agree [how could they not?], do it.
1395 # If there are gets and they all agree [how could they not?], do it.
1396 if ACTION_GET in bids:
1396 if ACTION_GET in bids:
1397 ga0 = bids[ACTION_GET][0]
1397 ga0 = bids[ACTION_GET][0]
1398 if all(a == ga0 for a in bids[ACTION_GET][1:]):
1398 if all(a == ga0 for a in bids[ACTION_GET][1:]):
1399 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1399 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1400 actions[f] = ga0
1400 actions[f] = ga0
1401 continue
1401 continue
1402 # TODO: Consider other simple actions such as mode changes
1402 # TODO: Consider other simple actions such as mode changes
1403 # Handle inefficient democrazy.
1403 # Handle inefficient democrazy.
1404 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1404 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1405 for m, l in sorted(bids.items()):
1405 for m, l in sorted(bids.items()):
1406 for _f, args, msg in l:
1406 for _f, args, msg in l:
1407 repo.ui.note(' %s -> %s\n' % (msg, m))
1407 repo.ui.note(' %s -> %s\n' % (msg, m))
1408 # Pick random action. TODO: Instead, prompt user when resolving
1408 # Pick random action. TODO: Instead, prompt user when resolving
1409 m, l = list(bids.items())[0]
1409 m, l = list(bids.items())[0]
1410 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1410 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1411 (f, m))
1411 (f, m))
1412 actions[f] = l[0]
1412 actions[f] = l[0]
1413 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1413 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1414 dms.append(f)
1414 dms.append(f)
1415 continue
1415 continue
1416 # Work around 'dm' that can cause multiple actions for the same file
1416 # Work around 'dm' that can cause multiple actions for the same file
1417 for f in dms:
1417 for f in dms:
1418 dm, (f0, flags), msg = actions[f]
1418 dm, (f0, flags), msg = actions[f]
1419 assert dm == ACTION_DIR_RENAME_MOVE_LOCAL, dm
1419 assert dm == ACTION_DIR_RENAME_MOVE_LOCAL, dm
1420 if f0 in actions and actions[f0][0] == ACTION_REMOVE:
1420 if f0 in actions and actions[f0][0] == ACTION_REMOVE:
1421 # We have one bid for removing a file and another for moving it.
1421 # We have one bid for removing a file and another for moving it.
1422 # These two could be merged as first move and then delete ...
1422 # These two could be merged as first move and then delete ...
1423 # but instead drop moving and just delete.
1423 # but instead drop moving and just delete.
1424 del actions[f]
1424 del actions[f]
1425 repo.ui.note(_('end of auction\n\n'))
1425 repo.ui.note(_('end of auction\n\n'))
1426
1426
1427 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1428
1429 if wctx.rev() is None:
1427 if wctx.rev() is None:
1430 fractions = _forgetremoved(wctx, mctx, branchmerge)
1428 fractions = _forgetremoved(wctx, mctx, branchmerge)
1431 actions.update(fractions)
1429 actions.update(fractions)
1432
1430
1433 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1431 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1434 actions)
1432 actions)
1433 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1435
1434
1436 return prunedactions, diverge, renamedelete
1435 return prunedactions, diverge, renamedelete
1437
1436
1438 def _getcwd():
1437 def _getcwd():
1439 try:
1438 try:
1440 return pycompat.getcwd()
1439 return pycompat.getcwd()
1441 except OSError as err:
1440 except OSError as err:
1442 if err.errno == errno.ENOENT:
1441 if err.errno == errno.ENOENT:
1443 return None
1442 return None
1444 raise
1443 raise
1445
1444
1446 def batchremove(repo, wctx, actions):
1445 def batchremove(repo, wctx, actions):
1447 """apply removes to the working directory
1446 """apply removes to the working directory
1448
1447
1449 yields tuples for progress updates
1448 yields tuples for progress updates
1450 """
1449 """
1451 verbose = repo.ui.verbose
1450 verbose = repo.ui.verbose
1452 cwd = _getcwd()
1451 cwd = _getcwd()
1453 i = 0
1452 i = 0
1454 for f, args, msg in actions:
1453 for f, args, msg in actions:
1455 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1454 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1456 if verbose:
1455 if verbose:
1457 repo.ui.note(_("removing %s\n") % f)
1456 repo.ui.note(_("removing %s\n") % f)
1458 wctx[f].audit()
1457 wctx[f].audit()
1459 try:
1458 try:
1460 wctx[f].remove(ignoremissing=True)
1459 wctx[f].remove(ignoremissing=True)
1461 except OSError as inst:
1460 except OSError as inst:
1462 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1461 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1463 (f, inst.strerror))
1462 (f, inst.strerror))
1464 if i == 100:
1463 if i == 100:
1465 yield i, f
1464 yield i, f
1466 i = 0
1465 i = 0
1467 i += 1
1466 i += 1
1468 if i > 0:
1467 if i > 0:
1469 yield i, f
1468 yield i, f
1470
1469
1471 if cwd and not _getcwd():
1470 if cwd and not _getcwd():
1472 # cwd was removed in the course of removing files; print a helpful
1471 # cwd was removed in the course of removing files; print a helpful
1473 # warning.
1472 # warning.
1474 repo.ui.warn(_("current directory was removed\n"
1473 repo.ui.warn(_("current directory was removed\n"
1475 "(consider changing to repo root: %s)\n") % repo.root)
1474 "(consider changing to repo root: %s)\n") % repo.root)
1476
1475
1477 def batchget(repo, mctx, wctx, actions):
1476 def batchget(repo, mctx, wctx, actions):
1478 """apply gets to the working directory
1477 """apply gets to the working directory
1479
1478
1480 mctx is the context to get from
1479 mctx is the context to get from
1481
1480
1482 yields tuples for progress updates
1481 yields tuples for progress updates
1483 """
1482 """
1484 verbose = repo.ui.verbose
1483 verbose = repo.ui.verbose
1485 fctx = mctx.filectx
1484 fctx = mctx.filectx
1486 ui = repo.ui
1485 ui = repo.ui
1487 i = 0
1486 i = 0
1488 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1487 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1489 for f, (flags, backup), msg in actions:
1488 for f, (flags, backup), msg in actions:
1490 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1489 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1491 if verbose:
1490 if verbose:
1492 repo.ui.note(_("getting %s\n") % f)
1491 repo.ui.note(_("getting %s\n") % f)
1493
1492
1494 if backup:
1493 if backup:
1495 # If a file or directory exists with the same name, back that
1494 # If a file or directory exists with the same name, back that
1496 # up. Otherwise, look to see if there is a file that conflicts
1495 # up. Otherwise, look to see if there is a file that conflicts
1497 # with a directory this file is in, and if so, back that up.
1496 # with a directory this file is in, and if so, back that up.
1498 absf = repo.wjoin(f)
1497 absf = repo.wjoin(f)
1499 if not repo.wvfs.lexists(f):
1498 if not repo.wvfs.lexists(f):
1500 for p in util.finddirs(f):
1499 for p in util.finddirs(f):
1501 if repo.wvfs.isfileorlink(p):
1500 if repo.wvfs.isfileorlink(p):
1502 absf = repo.wjoin(p)
1501 absf = repo.wjoin(p)
1503 break
1502 break
1504 orig = scmutil.origpath(ui, repo, absf)
1503 orig = scmutil.origpath(ui, repo, absf)
1505 if repo.wvfs.lexists(absf):
1504 if repo.wvfs.lexists(absf):
1506 util.rename(absf, orig)
1505 util.rename(absf, orig)
1507 wctx[f].clearunknown()
1506 wctx[f].clearunknown()
1508 atomictemp = ui.configbool("experimental", "update.atomic-file")
1507 atomictemp = ui.configbool("experimental", "update.atomic-file")
1509 wctx[f].write(fctx(f).data(), flags, backgroundclose=True,
1508 wctx[f].write(fctx(f).data(), flags, backgroundclose=True,
1510 atomictemp=atomictemp)
1509 atomictemp=atomictemp)
1511 if i == 100:
1510 if i == 100:
1512 yield i, f
1511 yield i, f
1513 i = 0
1512 i = 0
1514 i += 1
1513 i += 1
1515 if i > 0:
1514 if i > 0:
1516 yield i, f
1515 yield i, f
1517
1516
1518 def _prefetchfiles(repo, ctx, actions):
1517 def _prefetchfiles(repo, ctx, actions):
1519 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1518 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1520 of merge actions. ``ctx`` is the context being merged in."""
1519 of merge actions. ``ctx`` is the context being merged in."""
1521
1520
1522 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1521 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1523 # don't touch the context to be merged in. 'cd' is skipped, because
1522 # don't touch the context to be merged in. 'cd' is skipped, because
1524 # changed/deleted never resolves to something from the remote side.
1523 # changed/deleted never resolves to something from the remote side.
1525 oplist = [actions[a] for a in (ACTION_GET, ACTION_DELETED_CHANGED,
1524 oplist = [actions[a] for a in (ACTION_GET, ACTION_DELETED_CHANGED,
1526 ACTION_LOCAL_DIR_RENAME_GET, ACTION_MERGE)]
1525 ACTION_LOCAL_DIR_RENAME_GET, ACTION_MERGE)]
1527 prefetch = scmutil.prefetchfiles
1526 prefetch = scmutil.prefetchfiles
1528 matchfiles = scmutil.matchfiles
1527 matchfiles = scmutil.matchfiles
1529 prefetch(repo, [ctx.rev()],
1528 prefetch(repo, [ctx.rev()],
1530 matchfiles(repo,
1529 matchfiles(repo,
1531 [f for sublist in oplist for f, args, msg in sublist]))
1530 [f for sublist in oplist for f, args, msg in sublist]))
1532
1531
1533 @attr.s(frozen=True)
1532 @attr.s(frozen=True)
1534 class updateresult(object):
1533 class updateresult(object):
1535 updatedcount = attr.ib()
1534 updatedcount = attr.ib()
1536 mergedcount = attr.ib()
1535 mergedcount = attr.ib()
1537 removedcount = attr.ib()
1536 removedcount = attr.ib()
1538 unresolvedcount = attr.ib()
1537 unresolvedcount = attr.ib()
1539
1538
1540 def isempty(self):
1539 def isempty(self):
1541 return (not self.updatedcount and not self.mergedcount
1540 return (not self.updatedcount and not self.mergedcount
1542 and not self.removedcount and not self.unresolvedcount)
1541 and not self.removedcount and not self.unresolvedcount)
1543
1542
1544 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1543 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1545 """apply the merge action list to the working directory
1544 """apply the merge action list to the working directory
1546
1545
1547 wctx is the working copy context
1546 wctx is the working copy context
1548 mctx is the context to be merged into the working copy
1547 mctx is the context to be merged into the working copy
1549
1548
1550 Return a tuple of counts (updated, merged, removed, unresolved) that
1549 Return a tuple of counts (updated, merged, removed, unresolved) that
1551 describes how many files were affected by the update.
1550 describes how many files were affected by the update.
1552 """
1551 """
1553
1552
1554 _prefetchfiles(repo, mctx, actions)
1553 _prefetchfiles(repo, mctx, actions)
1555
1554
1556 updated, merged, removed = 0, 0, 0
1555 updated, merged, removed = 0, 0, 0
1557 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1556 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1558 moves = []
1557 moves = []
1559 for m, l in actions.items():
1558 for m, l in actions.items():
1560 l.sort()
1559 l.sort()
1561
1560
1562 # 'cd' and 'dc' actions are treated like other merge conflicts
1561 # 'cd' and 'dc' actions are treated like other merge conflicts
1563 mergeactions = sorted(actions[ACTION_CHANGED_DELETED])
1562 mergeactions = sorted(actions[ACTION_CHANGED_DELETED])
1564 mergeactions.extend(sorted(actions[ACTION_DELETED_CHANGED]))
1563 mergeactions.extend(sorted(actions[ACTION_DELETED_CHANGED]))
1565 mergeactions.extend(actions[ACTION_MERGE])
1564 mergeactions.extend(actions[ACTION_MERGE])
1566 for f, args, msg in mergeactions:
1565 for f, args, msg in mergeactions:
1567 f1, f2, fa, move, anc = args
1566 f1, f2, fa, move, anc = args
1568 if f == '.hgsubstate': # merged internally
1567 if f == '.hgsubstate': # merged internally
1569 continue
1568 continue
1570 if f1 is None:
1569 if f1 is None:
1571 fcl = filemerge.absentfilectx(wctx, fa)
1570 fcl = filemerge.absentfilectx(wctx, fa)
1572 else:
1571 else:
1573 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1572 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1574 fcl = wctx[f1]
1573 fcl = wctx[f1]
1575 if f2 is None:
1574 if f2 is None:
1576 fco = filemerge.absentfilectx(mctx, fa)
1575 fco = filemerge.absentfilectx(mctx, fa)
1577 else:
1576 else:
1578 fco = mctx[f2]
1577 fco = mctx[f2]
1579 actx = repo[anc]
1578 actx = repo[anc]
1580 if fa in actx:
1579 if fa in actx:
1581 fca = actx[fa]
1580 fca = actx[fa]
1582 else:
1581 else:
1583 # TODO: move to absentfilectx
1582 # TODO: move to absentfilectx
1584 fca = repo.filectx(f1, fileid=nullrev)
1583 fca = repo.filectx(f1, fileid=nullrev)
1585 ms.add(fcl, fco, fca, f)
1584 ms.add(fcl, fco, fca, f)
1586 if f1 != f and move:
1585 if f1 != f and move:
1587 moves.append(f1)
1586 moves.append(f1)
1588
1587
1589 # remove renamed files after safely stored
1588 # remove renamed files after safely stored
1590 for f in moves:
1589 for f in moves:
1591 if wctx[f].lexists():
1590 if wctx[f].lexists():
1592 repo.ui.debug("removing %s\n" % f)
1591 repo.ui.debug("removing %s\n" % f)
1593 wctx[f].audit()
1592 wctx[f].audit()
1594 wctx[f].remove()
1593 wctx[f].remove()
1595
1594
1596 numupdates = sum(len(l) for m, l in actions.items()
1595 numupdates = sum(len(l) for m, l in actions.items()
1597 if m != ACTION_KEEP)
1596 if m != ACTION_KEEP)
1598 progress = repo.ui.makeprogress(_('updating'), unit=_('files'),
1597 progress = repo.ui.makeprogress(_('updating'), unit=_('files'),
1599 total=numupdates)
1598 total=numupdates)
1600
1599
1601 if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']:
1600 if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']:
1602 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1601 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1603
1602
1604 # record path conflicts
1603 # record path conflicts
1605 for f, args, msg in actions[ACTION_PATH_CONFLICT]:
1604 for f, args, msg in actions[ACTION_PATH_CONFLICT]:
1606 f1, fo = args
1605 f1, fo = args
1607 s = repo.ui.status
1606 s = repo.ui.status
1608 s(_("%s: path conflict - a file or link has the same name as a "
1607 s(_("%s: path conflict - a file or link has the same name as a "
1609 "directory\n") % f)
1608 "directory\n") % f)
1610 if fo == 'l':
1609 if fo == 'l':
1611 s(_("the local file has been renamed to %s\n") % f1)
1610 s(_("the local file has been renamed to %s\n") % f1)
1612 else:
1611 else:
1613 s(_("the remote file has been renamed to %s\n") % f1)
1612 s(_("the remote file has been renamed to %s\n") % f1)
1614 s(_("resolve manually then use 'hg resolve --mark %s'\n") % f)
1613 s(_("resolve manually then use 'hg resolve --mark %s'\n") % f)
1615 ms.addpath(f, f1, fo)
1614 ms.addpath(f, f1, fo)
1616 progress.increment(item=f)
1615 progress.increment(item=f)
1617
1616
1618 # When merging in-memory, we can't support worker processes, so set the
1617 # When merging in-memory, we can't support worker processes, so set the
1619 # per-item cost at 0 in that case.
1618 # per-item cost at 0 in that case.
1620 cost = 0 if wctx.isinmemory() else 0.001
1619 cost = 0 if wctx.isinmemory() else 0.001
1621
1620
1622 # remove in parallel (must come before resolving path conflicts and getting)
1621 # remove in parallel (must come before resolving path conflicts and getting)
1623 prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx),
1622 prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx),
1624 actions[ACTION_REMOVE])
1623 actions[ACTION_REMOVE])
1625 for i, item in prog:
1624 for i, item in prog:
1626 progress.increment(step=i, item=item)
1625 progress.increment(step=i, item=item)
1627 removed = len(actions[ACTION_REMOVE])
1626 removed = len(actions[ACTION_REMOVE])
1628
1627
1629 # resolve path conflicts (must come before getting)
1628 # resolve path conflicts (must come before getting)
1630 for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]:
1629 for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]:
1631 repo.ui.debug(" %s: %s -> pr\n" % (f, msg))
1630 repo.ui.debug(" %s: %s -> pr\n" % (f, msg))
1632 f0, = args
1631 f0, = args
1633 if wctx[f0].lexists():
1632 if wctx[f0].lexists():
1634 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1633 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1635 wctx[f].audit()
1634 wctx[f].audit()
1636 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1635 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1637 wctx[f0].remove()
1636 wctx[f0].remove()
1638 progress.increment(item=f)
1637 progress.increment(item=f)
1639
1638
1640 # get in parallel.
1639 # get in parallel.
1641 threadsafe = repo.ui.configbool('experimental',
1640 threadsafe = repo.ui.configbool('experimental',
1642 'worker.wdir-get-thread-safe')
1641 'worker.wdir-get-thread-safe')
1643 prog = worker.worker(repo.ui, cost, batchget, (repo, mctx, wctx),
1642 prog = worker.worker(repo.ui, cost, batchget, (repo, mctx, wctx),
1644 actions[ACTION_GET],
1643 actions[ACTION_GET],
1645 threadsafe=threadsafe)
1644 threadsafe=threadsafe)
1646 for i, item in prog:
1645 for i, item in prog:
1647 progress.increment(step=i, item=item)
1646 progress.increment(step=i, item=item)
1648 updated = len(actions[ACTION_GET])
1647 updated = len(actions[ACTION_GET])
1649
1648
1650 if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']:
1649 if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']:
1651 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1650 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1652
1651
1653 # forget (manifest only, just log it) (must come first)
1652 # forget (manifest only, just log it) (must come first)
1654 for f, args, msg in actions[ACTION_FORGET]:
1653 for f, args, msg in actions[ACTION_FORGET]:
1655 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1654 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1656 progress.increment(item=f)
1655 progress.increment(item=f)
1657
1656
1658 # re-add (manifest only, just log it)
1657 # re-add (manifest only, just log it)
1659 for f, args, msg in actions[ACTION_ADD]:
1658 for f, args, msg in actions[ACTION_ADD]:
1660 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1659 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1661 progress.increment(item=f)
1660 progress.increment(item=f)
1662
1661
1663 # re-add/mark as modified (manifest only, just log it)
1662 # re-add/mark as modified (manifest only, just log it)
1664 for f, args, msg in actions[ACTION_ADD_MODIFIED]:
1663 for f, args, msg in actions[ACTION_ADD_MODIFIED]:
1665 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1664 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1666 progress.increment(item=f)
1665 progress.increment(item=f)
1667
1666
1668 # keep (noop, just log it)
1667 # keep (noop, just log it)
1669 for f, args, msg in actions[ACTION_KEEP]:
1668 for f, args, msg in actions[ACTION_KEEP]:
1670 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1669 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1671 # no progress
1670 # no progress
1672
1671
1673 # directory rename, move local
1672 # directory rename, move local
1674 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
1673 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
1675 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1674 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1676 progress.increment(item=f)
1675 progress.increment(item=f)
1677 f0, flags = args
1676 f0, flags = args
1678 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1677 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1679 wctx[f].audit()
1678 wctx[f].audit()
1680 wctx[f].write(wctx.filectx(f0).data(), flags)
1679 wctx[f].write(wctx.filectx(f0).data(), flags)
1681 wctx[f0].remove()
1680 wctx[f0].remove()
1682 updated += 1
1681 updated += 1
1683
1682
1684 # local directory rename, get
1683 # local directory rename, get
1685 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
1684 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
1686 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1685 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1687 progress.increment(item=f)
1686 progress.increment(item=f)
1688 f0, flags = args
1687 f0, flags = args
1689 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1688 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1690 wctx[f].write(mctx.filectx(f0).data(), flags)
1689 wctx[f].write(mctx.filectx(f0).data(), flags)
1691 updated += 1
1690 updated += 1
1692
1691
1693 # exec
1692 # exec
1694 for f, args, msg in actions[ACTION_EXEC]:
1693 for f, args, msg in actions[ACTION_EXEC]:
1695 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1694 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1696 progress.increment(item=f)
1695 progress.increment(item=f)
1697 flags, = args
1696 flags, = args
1698 wctx[f].audit()
1697 wctx[f].audit()
1699 wctx[f].setflags('l' in flags, 'x' in flags)
1698 wctx[f].setflags('l' in flags, 'x' in flags)
1700 updated += 1
1699 updated += 1
1701
1700
1702 # the ordering is important here -- ms.mergedriver will raise if the merge
1701 # the ordering is important here -- ms.mergedriver will raise if the merge
1703 # driver has changed, and we want to be able to bypass it when overwrite is
1702 # driver has changed, and we want to be able to bypass it when overwrite is
1704 # True
1703 # True
1705 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1704 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1706
1705
1707 if usemergedriver:
1706 if usemergedriver:
1708 if wctx.isinmemory():
1707 if wctx.isinmemory():
1709 raise error.InMemoryMergeConflictsError("in-memory merge does not "
1708 raise error.InMemoryMergeConflictsError("in-memory merge does not "
1710 "support mergedriver")
1709 "support mergedriver")
1711 ms.commit()
1710 ms.commit()
1712 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1711 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1713 # the driver might leave some files unresolved
1712 # the driver might leave some files unresolved
1714 unresolvedf = set(ms.unresolved())
1713 unresolvedf = set(ms.unresolved())
1715 if not proceed:
1714 if not proceed:
1716 # XXX setting unresolved to at least 1 is a hack to make sure we
1715 # XXX setting unresolved to at least 1 is a hack to make sure we
1717 # error out
1716 # error out
1718 return updateresult(updated, merged, removed,
1717 return updateresult(updated, merged, removed,
1719 max(len(unresolvedf), 1))
1718 max(len(unresolvedf), 1))
1720 newactions = []
1719 newactions = []
1721 for f, args, msg in mergeactions:
1720 for f, args, msg in mergeactions:
1722 if f in unresolvedf:
1721 if f in unresolvedf:
1723 newactions.append((f, args, msg))
1722 newactions.append((f, args, msg))
1724 mergeactions = newactions
1723 mergeactions = newactions
1725
1724
1726 try:
1725 try:
1727 # premerge
1726 # premerge
1728 tocomplete = []
1727 tocomplete = []
1729 for f, args, msg in mergeactions:
1728 for f, args, msg in mergeactions:
1730 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1729 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1731 progress.increment(item=f)
1730 progress.increment(item=f)
1732 if f == '.hgsubstate': # subrepo states need updating
1731 if f == '.hgsubstate': # subrepo states need updating
1733 subrepoutil.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1732 subrepoutil.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1734 overwrite, labels)
1733 overwrite, labels)
1735 continue
1734 continue
1736 wctx[f].audit()
1735 wctx[f].audit()
1737 complete, r = ms.preresolve(f, wctx)
1736 complete, r = ms.preresolve(f, wctx)
1738 if not complete:
1737 if not complete:
1739 numupdates += 1
1738 numupdates += 1
1740 tocomplete.append((f, args, msg))
1739 tocomplete.append((f, args, msg))
1741
1740
1742 # merge
1741 # merge
1743 for f, args, msg in tocomplete:
1742 for f, args, msg in tocomplete:
1744 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1743 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1745 progress.increment(item=f, total=numupdates)
1744 progress.increment(item=f, total=numupdates)
1746 ms.resolve(f, wctx)
1745 ms.resolve(f, wctx)
1747
1746
1748 finally:
1747 finally:
1749 ms.commit()
1748 ms.commit()
1750
1749
1751 unresolved = ms.unresolvedcount()
1750 unresolved = ms.unresolvedcount()
1752
1751
1753 if (usemergedriver and not unresolved
1752 if (usemergedriver and not unresolved
1754 and ms.mdstate() != MERGE_DRIVER_STATE_SUCCESS):
1753 and ms.mdstate() != MERGE_DRIVER_STATE_SUCCESS):
1755 if not driverconclude(repo, ms, wctx, labels=labels):
1754 if not driverconclude(repo, ms, wctx, labels=labels):
1756 # XXX setting unresolved to at least 1 is a hack to make sure we
1755 # XXX setting unresolved to at least 1 is a hack to make sure we
1757 # error out
1756 # error out
1758 unresolved = max(unresolved, 1)
1757 unresolved = max(unresolved, 1)
1759
1758
1760 ms.commit()
1759 ms.commit()
1761
1760
1762 msupdated, msmerged, msremoved = ms.counts()
1761 msupdated, msmerged, msremoved = ms.counts()
1763 updated += msupdated
1762 updated += msupdated
1764 merged += msmerged
1763 merged += msmerged
1765 removed += msremoved
1764 removed += msremoved
1766
1765
1767 extraactions = ms.actions()
1766 extraactions = ms.actions()
1768 if extraactions:
1767 if extraactions:
1769 mfiles = set(a[0] for a in actions[ACTION_MERGE])
1768 mfiles = set(a[0] for a in actions[ACTION_MERGE])
1770 for k, acts in extraactions.iteritems():
1769 for k, acts in extraactions.iteritems():
1771 actions[k].extend(acts)
1770 actions[k].extend(acts)
1772 # Remove these files from actions[ACTION_MERGE] as well. This is
1771 # Remove these files from actions[ACTION_MERGE] as well. This is
1773 # important because in recordupdates, files in actions[ACTION_MERGE]
1772 # important because in recordupdates, files in actions[ACTION_MERGE]
1774 # are processed after files in other actions, and the merge driver
1773 # are processed after files in other actions, and the merge driver
1775 # might add files to those actions via extraactions above. This can
1774 # might add files to those actions via extraactions above. This can
1776 # lead to a file being recorded twice, with poor results. This is
1775 # lead to a file being recorded twice, with poor results. This is
1777 # especially problematic for actions[ACTION_REMOVE] (currently only
1776 # especially problematic for actions[ACTION_REMOVE] (currently only
1778 # possible with the merge driver in the initial merge process;
1777 # possible with the merge driver in the initial merge process;
1779 # interrupted merges don't go through this flow).
1778 # interrupted merges don't go through this flow).
1780 #
1779 #
1781 # The real fix here is to have indexes by both file and action so
1780 # The real fix here is to have indexes by both file and action so
1782 # that when the action for a file is changed it is automatically
1781 # that when the action for a file is changed it is automatically
1783 # reflected in the other action lists. But that involves a more
1782 # reflected in the other action lists. But that involves a more
1784 # complex data structure, so this will do for now.
1783 # complex data structure, so this will do for now.
1785 #
1784 #
1786 # We don't need to do the same operation for 'dc' and 'cd' because
1785 # We don't need to do the same operation for 'dc' and 'cd' because
1787 # those lists aren't consulted again.
1786 # those lists aren't consulted again.
1788 mfiles.difference_update(a[0] for a in acts)
1787 mfiles.difference_update(a[0] for a in acts)
1789
1788
1790 actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE]
1789 actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE]
1791 if a[0] in mfiles]
1790 if a[0] in mfiles]
1792
1791
1793 progress.complete()
1792 progress.complete()
1794 return updateresult(updated, merged, removed, unresolved)
1793 return updateresult(updated, merged, removed, unresolved)
1795
1794
1796 def recordupdates(repo, actions, branchmerge):
1795 def recordupdates(repo, actions, branchmerge):
1797 "record merge actions to the dirstate"
1796 "record merge actions to the dirstate"
1798 # remove (must come first)
1797 # remove (must come first)
1799 for f, args, msg in actions.get(ACTION_REMOVE, []):
1798 for f, args, msg in actions.get(ACTION_REMOVE, []):
1800 if branchmerge:
1799 if branchmerge:
1801 repo.dirstate.remove(f)
1800 repo.dirstate.remove(f)
1802 else:
1801 else:
1803 repo.dirstate.drop(f)
1802 repo.dirstate.drop(f)
1804
1803
1805 # forget (must come first)
1804 # forget (must come first)
1806 for f, args, msg in actions.get(ACTION_FORGET, []):
1805 for f, args, msg in actions.get(ACTION_FORGET, []):
1807 repo.dirstate.drop(f)
1806 repo.dirstate.drop(f)
1808
1807
1809 # resolve path conflicts
1808 # resolve path conflicts
1810 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
1809 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
1811 f0, = args
1810 f0, = args
1812 origf0 = repo.dirstate.copied(f0) or f0
1811 origf0 = repo.dirstate.copied(f0) or f0
1813 repo.dirstate.add(f)
1812 repo.dirstate.add(f)
1814 repo.dirstate.copy(origf0, f)
1813 repo.dirstate.copy(origf0, f)
1815 if f0 == origf0:
1814 if f0 == origf0:
1816 repo.dirstate.remove(f0)
1815 repo.dirstate.remove(f0)
1817 else:
1816 else:
1818 repo.dirstate.drop(f0)
1817 repo.dirstate.drop(f0)
1819
1818
1820 # re-add
1819 # re-add
1821 for f, args, msg in actions.get(ACTION_ADD, []):
1820 for f, args, msg in actions.get(ACTION_ADD, []):
1822 repo.dirstate.add(f)
1821 repo.dirstate.add(f)
1823
1822
1824 # re-add/mark as modified
1823 # re-add/mark as modified
1825 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
1824 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
1826 if branchmerge:
1825 if branchmerge:
1827 repo.dirstate.normallookup(f)
1826 repo.dirstate.normallookup(f)
1828 else:
1827 else:
1829 repo.dirstate.add(f)
1828 repo.dirstate.add(f)
1830
1829
1831 # exec change
1830 # exec change
1832 for f, args, msg in actions.get(ACTION_EXEC, []):
1831 for f, args, msg in actions.get(ACTION_EXEC, []):
1833 repo.dirstate.normallookup(f)
1832 repo.dirstate.normallookup(f)
1834
1833
1835 # keep
1834 # keep
1836 for f, args, msg in actions.get(ACTION_KEEP, []):
1835 for f, args, msg in actions.get(ACTION_KEEP, []):
1837 pass
1836 pass
1838
1837
1839 # get
1838 # get
1840 for f, args, msg in actions.get(ACTION_GET, []):
1839 for f, args, msg in actions.get(ACTION_GET, []):
1841 if branchmerge:
1840 if branchmerge:
1842 repo.dirstate.otherparent(f)
1841 repo.dirstate.otherparent(f)
1843 else:
1842 else:
1844 repo.dirstate.normal(f)
1843 repo.dirstate.normal(f)
1845
1844
1846 # merge
1845 # merge
1847 for f, args, msg in actions.get(ACTION_MERGE, []):
1846 for f, args, msg in actions.get(ACTION_MERGE, []):
1848 f1, f2, fa, move, anc = args
1847 f1, f2, fa, move, anc = args
1849 if branchmerge:
1848 if branchmerge:
1850 # We've done a branch merge, mark this file as merged
1849 # We've done a branch merge, mark this file as merged
1851 # so that we properly record the merger later
1850 # so that we properly record the merger later
1852 repo.dirstate.merge(f)
1851 repo.dirstate.merge(f)
1853 if f1 != f2: # copy/rename
1852 if f1 != f2: # copy/rename
1854 if move:
1853 if move:
1855 repo.dirstate.remove(f1)
1854 repo.dirstate.remove(f1)
1856 if f1 != f:
1855 if f1 != f:
1857 repo.dirstate.copy(f1, f)
1856 repo.dirstate.copy(f1, f)
1858 else:
1857 else:
1859 repo.dirstate.copy(f2, f)
1858 repo.dirstate.copy(f2, f)
1860 else:
1859 else:
1861 # We've update-merged a locally modified file, so
1860 # We've update-merged a locally modified file, so
1862 # we set the dirstate to emulate a normal checkout
1861 # we set the dirstate to emulate a normal checkout
1863 # of that file some time in the past. Thus our
1862 # of that file some time in the past. Thus our
1864 # merge will appear as a normal local file
1863 # merge will appear as a normal local file
1865 # modification.
1864 # modification.
1866 if f2 == f: # file not locally copied/moved
1865 if f2 == f: # file not locally copied/moved
1867 repo.dirstate.normallookup(f)
1866 repo.dirstate.normallookup(f)
1868 if move:
1867 if move:
1869 repo.dirstate.drop(f1)
1868 repo.dirstate.drop(f1)
1870
1869
1871 # directory rename, move local
1870 # directory rename, move local
1872 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
1871 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
1873 f0, flag = args
1872 f0, flag = args
1874 if branchmerge:
1873 if branchmerge:
1875 repo.dirstate.add(f)
1874 repo.dirstate.add(f)
1876 repo.dirstate.remove(f0)
1875 repo.dirstate.remove(f0)
1877 repo.dirstate.copy(f0, f)
1876 repo.dirstate.copy(f0, f)
1878 else:
1877 else:
1879 repo.dirstate.normal(f)
1878 repo.dirstate.normal(f)
1880 repo.dirstate.drop(f0)
1879 repo.dirstate.drop(f0)
1881
1880
1882 # directory rename, get
1881 # directory rename, get
1883 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
1882 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
1884 f0, flag = args
1883 f0, flag = args
1885 if branchmerge:
1884 if branchmerge:
1886 repo.dirstate.add(f)
1885 repo.dirstate.add(f)
1887 repo.dirstate.copy(f0, f)
1886 repo.dirstate.copy(f0, f)
1888 else:
1887 else:
1889 repo.dirstate.normal(f)
1888 repo.dirstate.normal(f)
1890
1889
1891 def update(repo, node, branchmerge, force, ancestor=None,
1890 def update(repo, node, branchmerge, force, ancestor=None,
1892 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1891 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1893 updatecheck=None, wc=None):
1892 updatecheck=None, wc=None):
1894 """
1893 """
1895 Perform a merge between the working directory and the given node
1894 Perform a merge between the working directory and the given node
1896
1895
1897 node = the node to update to
1896 node = the node to update to
1898 branchmerge = whether to merge between branches
1897 branchmerge = whether to merge between branches
1899 force = whether to force branch merging or file overwriting
1898 force = whether to force branch merging or file overwriting
1900 matcher = a matcher to filter file lists (dirstate not updated)
1899 matcher = a matcher to filter file lists (dirstate not updated)
1901 mergeancestor = whether it is merging with an ancestor. If true,
1900 mergeancestor = whether it is merging with an ancestor. If true,
1902 we should accept the incoming changes for any prompts that occur.
1901 we should accept the incoming changes for any prompts that occur.
1903 If false, merging with an ancestor (fast-forward) is only allowed
1902 If false, merging with an ancestor (fast-forward) is only allowed
1904 between different named branches. This flag is used by rebase extension
1903 between different named branches. This flag is used by rebase extension
1905 as a temporary fix and should be avoided in general.
1904 as a temporary fix and should be avoided in general.
1906 labels = labels to use for base, local and other
1905 labels = labels to use for base, local and other
1907 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1906 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1908 this is True, then 'force' should be True as well.
1907 this is True, then 'force' should be True as well.
1909
1908
1910 The table below shows all the behaviors of the update command given the
1909 The table below shows all the behaviors of the update command given the
1911 -c/--check and -C/--clean or no options, whether the working directory is
1910 -c/--check and -C/--clean or no options, whether the working directory is
1912 dirty, whether a revision is specified, and the relationship of the parent
1911 dirty, whether a revision is specified, and the relationship of the parent
1913 rev to the target rev (linear or not). Match from top first. The -n
1912 rev to the target rev (linear or not). Match from top first. The -n
1914 option doesn't exist on the command line, but represents the
1913 option doesn't exist on the command line, but represents the
1915 experimental.updatecheck=noconflict option.
1914 experimental.updatecheck=noconflict option.
1916
1915
1917 This logic is tested by test-update-branches.t.
1916 This logic is tested by test-update-branches.t.
1918
1917
1919 -c -C -n -m dirty rev linear | result
1918 -c -C -n -m dirty rev linear | result
1920 y y * * * * * | (1)
1919 y y * * * * * | (1)
1921 y * y * * * * | (1)
1920 y * y * * * * | (1)
1922 y * * y * * * | (1)
1921 y * * y * * * | (1)
1923 * y y * * * * | (1)
1922 * y y * * * * | (1)
1924 * y * y * * * | (1)
1923 * y * y * * * | (1)
1925 * * y y * * * | (1)
1924 * * y y * * * | (1)
1926 * * * * * n n | x
1925 * * * * * n n | x
1927 * * * * n * * | ok
1926 * * * * n * * | ok
1928 n n n n y * y | merge
1927 n n n n y * y | merge
1929 n n n n y y n | (2)
1928 n n n n y y n | (2)
1930 n n n y y * * | merge
1929 n n n y y * * | merge
1931 n n y n y * * | merge if no conflict
1930 n n y n y * * | merge if no conflict
1932 n y n n y * * | discard
1931 n y n n y * * | discard
1933 y n n n y * * | (3)
1932 y n n n y * * | (3)
1934
1933
1935 x = can't happen
1934 x = can't happen
1936 * = don't-care
1935 * = don't-care
1937 1 = incompatible options (checked in commands.py)
1936 1 = incompatible options (checked in commands.py)
1938 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1937 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1939 3 = abort: uncommitted changes (checked in commands.py)
1938 3 = abort: uncommitted changes (checked in commands.py)
1940
1939
1941 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1940 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1942 to repo[None] if None is passed.
1941 to repo[None] if None is passed.
1943
1942
1944 Return the same tuple as applyupdates().
1943 Return the same tuple as applyupdates().
1945 """
1944 """
1946 # Avoid cycle.
1945 # Avoid cycle.
1947 from . import sparse
1946 from . import sparse
1948
1947
1949 # This function used to find the default destination if node was None, but
1948 # This function used to find the default destination if node was None, but
1950 # that's now in destutil.py.
1949 # that's now in destutil.py.
1951 assert node is not None
1950 assert node is not None
1952 if not branchmerge and not force:
1951 if not branchmerge and not force:
1953 # TODO: remove the default once all callers that pass branchmerge=False
1952 # TODO: remove the default once all callers that pass branchmerge=False
1954 # and force=False pass a value for updatecheck. We may want to allow
1953 # and force=False pass a value for updatecheck. We may want to allow
1955 # updatecheck='abort' to better suppport some of these callers.
1954 # updatecheck='abort' to better suppport some of these callers.
1956 if updatecheck is None:
1955 if updatecheck is None:
1957 updatecheck = 'linear'
1956 updatecheck = 'linear'
1958 assert updatecheck in ('none', 'linear', 'noconflict')
1957 assert updatecheck in ('none', 'linear', 'noconflict')
1959 # If we're doing a partial update, we need to skip updating
1958 # If we're doing a partial update, we need to skip updating
1960 # the dirstate, so make a note of any partial-ness to the
1959 # the dirstate, so make a note of any partial-ness to the
1961 # update here.
1960 # update here.
1962 if matcher is None or matcher.always():
1961 if matcher is None or matcher.always():
1963 partial = False
1962 partial = False
1964 else:
1963 else:
1965 partial = True
1964 partial = True
1966 with repo.wlock():
1965 with repo.wlock():
1967 if wc is None:
1966 if wc is None:
1968 wc = repo[None]
1967 wc = repo[None]
1969 pl = wc.parents()
1968 pl = wc.parents()
1970 p1 = pl[0]
1969 p1 = pl[0]
1971 pas = [None]
1970 pas = [None]
1972 if ancestor is not None:
1971 if ancestor is not None:
1973 pas = [repo[ancestor]]
1972 pas = [repo[ancestor]]
1974
1973
1975 overwrite = force and not branchmerge
1974 overwrite = force and not branchmerge
1976
1975
1977 p2 = repo[node]
1976 p2 = repo[node]
1978 if pas[0] is None:
1977 if pas[0] is None:
1979 if repo.ui.configlist('merge', 'preferancestor') == ['*']:
1978 if repo.ui.configlist('merge', 'preferancestor') == ['*']:
1980 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1979 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1981 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1980 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1982 else:
1981 else:
1983 pas = [p1.ancestor(p2, warn=branchmerge)]
1982 pas = [p1.ancestor(p2, warn=branchmerge)]
1984
1983
1985 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
1984 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
1986
1985
1987 ### check phase
1986 ### check phase
1988 if not overwrite:
1987 if not overwrite:
1989 if len(pl) > 1:
1988 if len(pl) > 1:
1990 raise error.Abort(_("outstanding uncommitted merge"))
1989 raise error.Abort(_("outstanding uncommitted merge"))
1991 ms = mergestate.read(repo)
1990 ms = mergestate.read(repo)
1992 if list(ms.unresolved()):
1991 if list(ms.unresolved()):
1993 raise error.Abort(_("outstanding merge conflicts"))
1992 raise error.Abort(_("outstanding merge conflicts"))
1994 if branchmerge:
1993 if branchmerge:
1995 if pas == [p2]:
1994 if pas == [p2]:
1996 raise error.Abort(_("merging with a working directory ancestor"
1995 raise error.Abort(_("merging with a working directory ancestor"
1997 " has no effect"))
1996 " has no effect"))
1998 elif pas == [p1]:
1997 elif pas == [p1]:
1999 if not mergeancestor and wc.branch() == p2.branch():
1998 if not mergeancestor and wc.branch() == p2.branch():
2000 raise error.Abort(_("nothing to merge"),
1999 raise error.Abort(_("nothing to merge"),
2001 hint=_("use 'hg update' "
2000 hint=_("use 'hg update' "
2002 "or check 'hg heads'"))
2001 "or check 'hg heads'"))
2003 if not force and (wc.files() or wc.deleted()):
2002 if not force and (wc.files() or wc.deleted()):
2004 raise error.Abort(_("uncommitted changes"),
2003 raise error.Abort(_("uncommitted changes"),
2005 hint=_("use 'hg status' to list changes"))
2004 hint=_("use 'hg status' to list changes"))
2006 if not wc.isinmemory():
2005 if not wc.isinmemory():
2007 for s in sorted(wc.substate):
2006 for s in sorted(wc.substate):
2008 wc.sub(s).bailifchanged()
2007 wc.sub(s).bailifchanged()
2009
2008
2010 elif not overwrite:
2009 elif not overwrite:
2011 if p1 == p2: # no-op update
2010 if p1 == p2: # no-op update
2012 # call the hooks and exit early
2011 # call the hooks and exit early
2013 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
2012 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
2014 repo.hook('update', parent1=xp2, parent2='', error=0)
2013 repo.hook('update', parent1=xp2, parent2='', error=0)
2015 return updateresult(0, 0, 0, 0)
2014 return updateresult(0, 0, 0, 0)
2016
2015
2017 if (updatecheck == 'linear' and
2016 if (updatecheck == 'linear' and
2018 pas not in ([p1], [p2])): # nonlinear
2017 pas not in ([p1], [p2])): # nonlinear
2019 dirty = wc.dirty(missing=True)
2018 dirty = wc.dirty(missing=True)
2020 if dirty:
2019 if dirty:
2021 # Branching is a bit strange to ensure we do the minimal
2020 # Branching is a bit strange to ensure we do the minimal
2022 # amount of call to obsutil.foreground.
2021 # amount of call to obsutil.foreground.
2023 foreground = obsutil.foreground(repo, [p1.node()])
2022 foreground = obsutil.foreground(repo, [p1.node()])
2024 # note: the <node> variable contains a random identifier
2023 # note: the <node> variable contains a random identifier
2025 if repo[node].node() in foreground:
2024 if repo[node].node() in foreground:
2026 pass # allow updating to successors
2025 pass # allow updating to successors
2027 else:
2026 else:
2028 msg = _("uncommitted changes")
2027 msg = _("uncommitted changes")
2029 hint = _("commit or update --clean to discard changes")
2028 hint = _("commit or update --clean to discard changes")
2030 raise error.UpdateAbort(msg, hint=hint)
2029 raise error.UpdateAbort(msg, hint=hint)
2031 else:
2030 else:
2032 # Allow jumping branches if clean and specific rev given
2031 # Allow jumping branches if clean and specific rev given
2033 pass
2032 pass
2034
2033
2035 if overwrite:
2034 if overwrite:
2036 pas = [wc]
2035 pas = [wc]
2037 elif not branchmerge:
2036 elif not branchmerge:
2038 pas = [p1]
2037 pas = [p1]
2039
2038
2040 # deprecated config: merge.followcopies
2039 # deprecated config: merge.followcopies
2041 followcopies = repo.ui.configbool('merge', 'followcopies')
2040 followcopies = repo.ui.configbool('merge', 'followcopies')
2042 if overwrite:
2041 if overwrite:
2043 followcopies = False
2042 followcopies = False
2044 elif not pas[0]:
2043 elif not pas[0]:
2045 followcopies = False
2044 followcopies = False
2046 if not branchmerge and not wc.dirty(missing=True):
2045 if not branchmerge and not wc.dirty(missing=True):
2047 followcopies = False
2046 followcopies = False
2048
2047
2049 ### calculate phase
2048 ### calculate phase
2050 actionbyfile, diverge, renamedelete = calculateupdates(
2049 actionbyfile, diverge, renamedelete = calculateupdates(
2051 repo, wc, p2, pas, branchmerge, force, mergeancestor,
2050 repo, wc, p2, pas, branchmerge, force, mergeancestor,
2052 followcopies, matcher=matcher, mergeforce=mergeforce)
2051 followcopies, matcher=matcher, mergeforce=mergeforce)
2053
2052
2054 if updatecheck == 'noconflict':
2053 if updatecheck == 'noconflict':
2055 for f, (m, args, msg) in actionbyfile.iteritems():
2054 for f, (m, args, msg) in actionbyfile.iteritems():
2056 if m not in (ACTION_GET, ACTION_KEEP, ACTION_EXEC,
2055 if m not in (ACTION_GET, ACTION_KEEP, ACTION_EXEC,
2057 ACTION_REMOVE, ACTION_PATH_CONFLICT_RESOLVE):
2056 ACTION_REMOVE, ACTION_PATH_CONFLICT_RESOLVE):
2058 msg = _("conflicting changes")
2057 msg = _("conflicting changes")
2059 hint = _("commit or update --clean to discard changes")
2058 hint = _("commit or update --clean to discard changes")
2060 raise error.Abort(msg, hint=hint)
2059 raise error.Abort(msg, hint=hint)
2061
2060
2062 # Prompt and create actions. Most of this is in the resolve phase
2061 # Prompt and create actions. Most of this is in the resolve phase
2063 # already, but we can't handle .hgsubstate in filemerge or
2062 # already, but we can't handle .hgsubstate in filemerge or
2064 # subrepoutil.submerge yet so we have to keep prompting for it.
2063 # subrepoutil.submerge yet so we have to keep prompting for it.
2065 if '.hgsubstate' in actionbyfile:
2064 if '.hgsubstate' in actionbyfile:
2066 f = '.hgsubstate'
2065 f = '.hgsubstate'
2067 m, args, msg = actionbyfile[f]
2066 m, args, msg = actionbyfile[f]
2068 prompts = filemerge.partextras(labels)
2067 prompts = filemerge.partextras(labels)
2069 prompts['f'] = f
2068 prompts['f'] = f
2070 if m == ACTION_CHANGED_DELETED:
2069 if m == ACTION_CHANGED_DELETED:
2071 if repo.ui.promptchoice(
2070 if repo.ui.promptchoice(
2072 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
2071 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
2073 "use (c)hanged version or (d)elete?"
2072 "use (c)hanged version or (d)elete?"
2074 "$$ &Changed $$ &Delete") % prompts, 0):
2073 "$$ &Changed $$ &Delete") % prompts, 0):
2075 actionbyfile[f] = (ACTION_REMOVE, None, 'prompt delete')
2074 actionbyfile[f] = (ACTION_REMOVE, None, 'prompt delete')
2076 elif f in p1:
2075 elif f in p1:
2077 actionbyfile[f] = (ACTION_ADD_MODIFIED, None, 'prompt keep')
2076 actionbyfile[f] = (ACTION_ADD_MODIFIED, None, 'prompt keep')
2078 else:
2077 else:
2079 actionbyfile[f] = (ACTION_ADD, None, 'prompt keep')
2078 actionbyfile[f] = (ACTION_ADD, None, 'prompt keep')
2080 elif m == ACTION_DELETED_CHANGED:
2079 elif m == ACTION_DELETED_CHANGED:
2081 f1, f2, fa, move, anc = args
2080 f1, f2, fa, move, anc = args
2082 flags = p2[f2].flags()
2081 flags = p2[f2].flags()
2083 if repo.ui.promptchoice(
2082 if repo.ui.promptchoice(
2084 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
2083 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
2085 "use (c)hanged version or leave (d)eleted?"
2084 "use (c)hanged version or leave (d)eleted?"
2086 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
2085 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
2087 actionbyfile[f] = (ACTION_GET, (flags, False),
2086 actionbyfile[f] = (ACTION_GET, (flags, False),
2088 'prompt recreating')
2087 'prompt recreating')
2089 else:
2088 else:
2090 del actionbyfile[f]
2089 del actionbyfile[f]
2091
2090
2092 # Convert to dictionary-of-lists format
2091 # Convert to dictionary-of-lists format
2093 actions = dict((m, [])
2092 actions = dict((m, [])
2094 for m in (
2093 for m in (
2095 ACTION_ADD,
2094 ACTION_ADD,
2096 ACTION_ADD_MODIFIED,
2095 ACTION_ADD_MODIFIED,
2097 ACTION_FORGET,
2096 ACTION_FORGET,
2098 ACTION_GET,
2097 ACTION_GET,
2099 ACTION_CHANGED_DELETED,
2098 ACTION_CHANGED_DELETED,
2100 ACTION_DELETED_CHANGED,
2099 ACTION_DELETED_CHANGED,
2101 ACTION_REMOVE,
2100 ACTION_REMOVE,
2102 ACTION_DIR_RENAME_MOVE_LOCAL,
2101 ACTION_DIR_RENAME_MOVE_LOCAL,
2103 ACTION_LOCAL_DIR_RENAME_GET,
2102 ACTION_LOCAL_DIR_RENAME_GET,
2104 ACTION_MERGE,
2103 ACTION_MERGE,
2105 ACTION_EXEC,
2104 ACTION_EXEC,
2106 ACTION_KEEP,
2105 ACTION_KEEP,
2107 ACTION_PATH_CONFLICT,
2106 ACTION_PATH_CONFLICT,
2108 ACTION_PATH_CONFLICT_RESOLVE))
2107 ACTION_PATH_CONFLICT_RESOLVE))
2109 for f, (m, args, msg) in actionbyfile.iteritems():
2108 for f, (m, args, msg) in actionbyfile.iteritems():
2110 if m not in actions:
2109 if m not in actions:
2111 actions[m] = []
2110 actions[m] = []
2112 actions[m].append((f, args, msg))
2111 actions[m].append((f, args, msg))
2113
2112
2114 if not util.fscasesensitive(repo.path):
2113 if not util.fscasesensitive(repo.path):
2115 # check collision between files only in p2 for clean update
2114 # check collision between files only in p2 for clean update
2116 if (not branchmerge and
2115 if (not branchmerge and
2117 (force or not wc.dirty(missing=True, branch=False))):
2116 (force or not wc.dirty(missing=True, branch=False))):
2118 _checkcollision(repo, p2.manifest(), None)
2117 _checkcollision(repo, p2.manifest(), None)
2119 else:
2118 else:
2120 _checkcollision(repo, wc.manifest(), actions)
2119 _checkcollision(repo, wc.manifest(), actions)
2121
2120
2122 # divergent renames
2121 # divergent renames
2123 for f, fl in sorted(diverge.iteritems()):
2122 for f, fl in sorted(diverge.iteritems()):
2124 repo.ui.warn(_("note: possible conflict - %s was renamed "
2123 repo.ui.warn(_("note: possible conflict - %s was renamed "
2125 "multiple times to:\n") % f)
2124 "multiple times to:\n") % f)
2126 for nf in fl:
2125 for nf in fl:
2127 repo.ui.warn(" %s\n" % nf)
2126 repo.ui.warn(" %s\n" % nf)
2128
2127
2129 # rename and delete
2128 # rename and delete
2130 for f, fl in sorted(renamedelete.iteritems()):
2129 for f, fl in sorted(renamedelete.iteritems()):
2131 repo.ui.warn(_("note: possible conflict - %s was deleted "
2130 repo.ui.warn(_("note: possible conflict - %s was deleted "
2132 "and renamed to:\n") % f)
2131 "and renamed to:\n") % f)
2133 for nf in fl:
2132 for nf in fl:
2134 repo.ui.warn(" %s\n" % nf)
2133 repo.ui.warn(" %s\n" % nf)
2135
2134
2136 ### apply phase
2135 ### apply phase
2137 if not branchmerge: # just jump to the new rev
2136 if not branchmerge: # just jump to the new rev
2138 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
2137 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
2139 if not partial and not wc.isinmemory():
2138 if not partial and not wc.isinmemory():
2140 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
2139 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
2141 # note that we're in the middle of an update
2140 # note that we're in the middle of an update
2142 repo.vfs.write('updatestate', p2.hex())
2141 repo.vfs.write('updatestate', p2.hex())
2143
2142
2144 # Advertise fsmonitor when its presence could be useful.
2143 # Advertise fsmonitor when its presence could be useful.
2145 #
2144 #
2146 # We only advertise when performing an update from an empty working
2145 # We only advertise when performing an update from an empty working
2147 # directory. This typically only occurs during initial clone.
2146 # directory. This typically only occurs during initial clone.
2148 #
2147 #
2149 # We give users a mechanism to disable the warning in case it is
2148 # We give users a mechanism to disable the warning in case it is
2150 # annoying.
2149 # annoying.
2151 #
2150 #
2152 # We only allow on Linux and MacOS because that's where fsmonitor is
2151 # We only allow on Linux and MacOS because that's where fsmonitor is
2153 # considered stable.
2152 # considered stable.
2154 fsmonitorwarning = repo.ui.configbool('fsmonitor', 'warn_when_unused')
2153 fsmonitorwarning = repo.ui.configbool('fsmonitor', 'warn_when_unused')
2155 fsmonitorthreshold = repo.ui.configint('fsmonitor',
2154 fsmonitorthreshold = repo.ui.configint('fsmonitor',
2156 'warn_update_file_count')
2155 'warn_update_file_count')
2157 try:
2156 try:
2158 # avoid cycle: extensions -> cmdutil -> merge
2157 # avoid cycle: extensions -> cmdutil -> merge
2159 from . import extensions
2158 from . import extensions
2160 extensions.find('fsmonitor')
2159 extensions.find('fsmonitor')
2161 fsmonitorenabled = repo.ui.config('fsmonitor', 'mode') != 'off'
2160 fsmonitorenabled = repo.ui.config('fsmonitor', 'mode') != 'off'
2162 # We intentionally don't look at whether fsmonitor has disabled
2161 # We intentionally don't look at whether fsmonitor has disabled
2163 # itself because a) fsmonitor may have already printed a warning
2162 # itself because a) fsmonitor may have already printed a warning
2164 # b) we only care about the config state here.
2163 # b) we only care about the config state here.
2165 except KeyError:
2164 except KeyError:
2166 fsmonitorenabled = False
2165 fsmonitorenabled = False
2167
2166
2168 if (fsmonitorwarning
2167 if (fsmonitorwarning
2169 and not fsmonitorenabled
2168 and not fsmonitorenabled
2170 and p1.node() == nullid
2169 and p1.node() == nullid
2171 and len(actions[ACTION_GET]) >= fsmonitorthreshold
2170 and len(actions[ACTION_GET]) >= fsmonitorthreshold
2172 and pycompat.sysplatform.startswith(('linux', 'darwin'))):
2171 and pycompat.sysplatform.startswith(('linux', 'darwin'))):
2173 repo.ui.warn(
2172 repo.ui.warn(
2174 _('(warning: large working directory being used without '
2173 _('(warning: large working directory being used without '
2175 'fsmonitor enabled; enable fsmonitor to improve performance; '
2174 'fsmonitor enabled; enable fsmonitor to improve performance; '
2176 'see "hg help -e fsmonitor")\n'))
2175 'see "hg help -e fsmonitor")\n'))
2177
2176
2178 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
2177 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
2179
2178
2180 if not partial and not wc.isinmemory():
2179 if not partial and not wc.isinmemory():
2181 with repo.dirstate.parentchange():
2180 with repo.dirstate.parentchange():
2182 repo.setparents(fp1, fp2)
2181 repo.setparents(fp1, fp2)
2183 recordupdates(repo, actions, branchmerge)
2182 recordupdates(repo, actions, branchmerge)
2184 # update completed, clear state
2183 # update completed, clear state
2185 util.unlink(repo.vfs.join('updatestate'))
2184 util.unlink(repo.vfs.join('updatestate'))
2186
2185
2187 if not branchmerge:
2186 if not branchmerge:
2188 repo.dirstate.setbranch(p2.branch())
2187 repo.dirstate.setbranch(p2.branch())
2189
2188
2190 # If we're updating to a location, clean up any stale temporary includes
2189 # If we're updating to a location, clean up any stale temporary includes
2191 # (ex: this happens during hg rebase --abort).
2190 # (ex: this happens during hg rebase --abort).
2192 if not branchmerge:
2191 if not branchmerge:
2193 sparse.prunetemporaryincludes(repo)
2192 sparse.prunetemporaryincludes(repo)
2194
2193
2195 if not partial:
2194 if not partial:
2196 repo.hook('update', parent1=xp1, parent2=xp2,
2195 repo.hook('update', parent1=xp1, parent2=xp2,
2197 error=stats.unresolvedcount)
2196 error=stats.unresolvedcount)
2198 return stats
2197 return stats
2199
2198
2200 def graft(repo, ctx, pctx, labels, keepparent=False,
2199 def graft(repo, ctx, pctx, labels, keepparent=False,
2201 keepconflictparent=False):
2200 keepconflictparent=False):
2202 """Do a graft-like merge.
2201 """Do a graft-like merge.
2203
2202
2204 This is a merge where the merge ancestor is chosen such that one
2203 This is a merge where the merge ancestor is chosen such that one
2205 or more changesets are grafted onto the current changeset. In
2204 or more changesets are grafted onto the current changeset. In
2206 addition to the merge, this fixes up the dirstate to include only
2205 addition to the merge, this fixes up the dirstate to include only
2207 a single parent (if keepparent is False) and tries to duplicate any
2206 a single parent (if keepparent is False) and tries to duplicate any
2208 renames/copies appropriately.
2207 renames/copies appropriately.
2209
2208
2210 ctx - changeset to rebase
2209 ctx - changeset to rebase
2211 pctx - merge base, usually ctx.p1()
2210 pctx - merge base, usually ctx.p1()
2212 labels - merge labels eg ['local', 'graft']
2211 labels - merge labels eg ['local', 'graft']
2213 keepparent - keep second parent if any
2212 keepparent - keep second parent if any
2214 keepparent - if unresolved, keep parent used for the merge
2213 keepparent - if unresolved, keep parent used for the merge
2215
2214
2216 """
2215 """
2217 # If we're grafting a descendant onto an ancestor, be sure to pass
2216 # If we're grafting a descendant onto an ancestor, be sure to pass
2218 # mergeancestor=True to update. This does two things: 1) allows the merge if
2217 # mergeancestor=True to update. This does two things: 1) allows the merge if
2219 # the destination is the same as the parent of the ctx (so we can use graft
2218 # the destination is the same as the parent of the ctx (so we can use graft
2220 # to copy commits), and 2) informs update that the incoming changes are
2219 # to copy commits), and 2) informs update that the incoming changes are
2221 # newer than the destination so it doesn't prompt about "remote changed foo
2220 # newer than the destination so it doesn't prompt about "remote changed foo
2222 # which local deleted".
2221 # which local deleted".
2223 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
2222 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
2224
2223
2225 stats = update(repo, ctx.node(), True, True, pctx.node(),
2224 stats = update(repo, ctx.node(), True, True, pctx.node(),
2226 mergeancestor=mergeancestor, labels=labels)
2225 mergeancestor=mergeancestor, labels=labels)
2227
2226
2228
2227
2229 if keepconflictparent and stats.unresolvedcount:
2228 if keepconflictparent and stats.unresolvedcount:
2230 pother = ctx.node()
2229 pother = ctx.node()
2231 else:
2230 else:
2232 pother = nullid
2231 pother = nullid
2233 parents = ctx.parents()
2232 parents = ctx.parents()
2234 if keepparent and len(parents) == 2 and pctx in parents:
2233 if keepparent and len(parents) == 2 and pctx in parents:
2235 parents.remove(pctx)
2234 parents.remove(pctx)
2236 pother = parents[0].node()
2235 pother = parents[0].node()
2237
2236
2238 with repo.dirstate.parentchange():
2237 with repo.dirstate.parentchange():
2239 repo.setparents(repo['.'].node(), pother)
2238 repo.setparents(repo['.'].node(), pother)
2240 repo.dirstate.write(repo.currenttransaction())
2239 repo.dirstate.write(repo.currenttransaction())
2241 # fix up dirstate for copies and renames
2240 # fix up dirstate for copies and renames
2242 copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev())
2241 copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev())
2243 return stats
2242 return stats
@@ -1,116 +1,120 b''
1 test merging things outside of the sparse checkout
1 test merging things outside of the sparse checkout
2
2
3 $ hg init myrepo
3 $ hg init myrepo
4 $ cd myrepo
4 $ cd myrepo
5 $ cat > .hg/hgrc <<EOF
5 $ cat > .hg/hgrc <<EOF
6 > [extensions]
6 > [extensions]
7 > sparse=
7 > sparse=
8 > EOF
8 > EOF
9
9
10 $ echo foo > foo
10 $ echo foo > foo
11 $ echo bar > bar
11 $ echo bar > bar
12 $ hg add foo bar
12 $ hg add foo bar
13 $ hg commit -m initial
13 $ hg commit -m initial
14
14
15 $ hg branch feature
15 $ hg branch feature
16 marked working directory as branch feature
16 marked working directory as branch feature
17 (branches are permanent and global, did you want a bookmark?)
17 (branches are permanent and global, did you want a bookmark?)
18 $ echo bar2 >> bar
18 $ echo bar2 >> bar
19 $ hg commit -m 'feature - bar2'
19 $ hg commit -m 'feature - bar2'
20
20
21 $ hg update -q default
21 $ hg update -q default
22 $ hg debugsparse --exclude 'bar**'
22 $ hg debugsparse --exclude 'bar**'
23
23
24 $ hg merge feature
24 $ hg merge feature
25 temporarily included 1 file(s) in the sparse checkout for merging
25 temporarily included 1 file(s) in the sparse checkout for merging
26 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
27 (branch merge, don't forget to commit)
27 (branch merge, don't forget to commit)
28
28
29 Verify bar was merged temporarily
29 Verify bar was merged temporarily
30
30
31 $ ls
31 $ ls
32 bar
32 bar
33 foo
33 foo
34 $ hg status
34 $ hg status
35 M bar
35 M bar
36
36
37 Verify bar disappears automatically when the working copy becomes clean
37 Verify bar disappears automatically when the working copy becomes clean
38
38
39 $ hg commit -m "merged"
39 $ hg commit -m "merged"
40 cleaned up 1 temporarily added file(s) from the sparse checkout
40 cleaned up 1 temporarily added file(s) from the sparse checkout
41 $ hg status
41 $ hg status
42 $ ls
42 $ ls
43 foo
43 foo
44
44
45 $ hg cat -r . bar
45 $ hg cat -r . bar
46 bar
46 bar
47 bar2
47 bar2
48
48
49 Test merging things outside of the sparse checkout that are not in the working
49 Test merging things outside of the sparse checkout that are not in the working
50 copy
50 copy
51
51
52 $ hg strip -q -r . --config extensions.strip=
52 $ hg strip -q -r . --config extensions.strip=
53 $ hg up -q feature
53 $ hg up -q feature
54 $ touch branchonly
54 $ touch branchonly
55 $ hg ci -Aqm 'add branchonly'
55 $ hg ci -Aqm 'add branchonly'
56
56
57 $ hg up -q default
57 $ hg up -q default
58 $ hg debugsparse -X branchonly
58 $ hg debugsparse -X branchonly
59 $ hg merge feature
59 $ hg merge feature
60 temporarily included 2 file(s) in the sparse checkout for merging
60 temporarily included 2 file(s) in the sparse checkout for merging
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 (branch merge, don't forget to commit)
62 (branch merge, don't forget to commit)
63
63
64 $ cd ..
64 $ cd ..
65
65
66 Tests merging a file which is modified in one branch and deleted in another and
66 Tests merging a file which is modified in one branch and deleted in another and
67 file is excluded from sparse checkout
67 file is excluded from sparse checkout
68
68
69 $ hg init ytest
69 $ hg init ytest
70 $ cd ytest
70 $ cd ytest
71 $ echo "syntax: glob" >> .hgignore
71 $ echo "syntax: glob" >> .hgignore
72 $ echo "*.orig" >> .hgignore
72 $ echo "*.orig" >> .hgignore
73 $ hg ci -Aqm "added .hgignore"
73 $ hg ci -Aqm "added .hgignore"
74 $ for ch in a d; do echo foo > $ch; hg ci -Aqm "added "$ch; done;
74 $ for ch in a d; do echo foo > $ch; hg ci -Aqm "added "$ch; done;
75 $ cat >> .hg/hgrc <<EOF
75 $ cat >> .hg/hgrc <<EOF
76 > [alias]
76 > [alias]
77 > glog = log -GT "{rev}:{node|short} {desc}"
77 > glog = log -GT "{rev}:{node|short} {desc}"
78 > [extensions]
78 > [extensions]
79 > sparse =
79 > sparse =
80 > EOF
80 > EOF
81
81
82 $ hg glog
82 $ hg glog
83 @ 2:f29feff37cfc added d
83 @ 2:f29feff37cfc added d
84 |
84 |
85 o 1:617125d27d6b added a
85 o 1:617125d27d6b added a
86 |
86 |
87 o 0:53f3774ed939 added .hgignore
87 o 0:53f3774ed939 added .hgignore
88
88
89 $ hg rm d
89 $ hg rm d
90 $ hg ci -m "removed d"
90 $ hg ci -m "removed d"
91
91
92 $ hg up '.^'
92 $ hg up '.^'
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 $ hg debugsparse --reset
94 $ hg debugsparse --reset
95 $ echo bar >> d
95 $ echo bar >> d
96 $ hg ci -Am "added bar to d"
96 $ hg ci -Am "added bar to d"
97 created new head
97 created new head
98
98
99 $ hg glog
99 $ hg glog
100 @ 4:6527874a90e4 added bar to d
100 @ 4:6527874a90e4 added bar to d
101 |
101 |
102 | o 3:372c8558de45 removed d
102 | o 3:372c8558de45 removed d
103 |/
103 |/
104 o 2:f29feff37cfc added d
104 o 2:f29feff37cfc added d
105 |
105 |
106 o 1:617125d27d6b added a
106 o 1:617125d27d6b added a
107 |
107 |
108 o 0:53f3774ed939 added .hgignore
108 o 0:53f3774ed939 added .hgignore
109
109
110 $ hg debugsparse --exclude "d"
110 $ hg debugsparse --exclude "d"
111 $ ls
111 $ ls
112 a
112 a
113
113
114 $ hg merge
114 $ hg merge
115 abort: $ENOENT$: '$TESTTMP/ytest/d'
115 temporarily included 1 file(s) in the sparse checkout for merging
116 [255]
116 local [working copy] changed d which other [merge rev] deleted
117 use (c)hanged version, (d)elete, or leave (u)nresolved? u
118 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
119 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
120 [1]
General Comments 0
You need to be logged in to leave comments. Login now