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