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