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