##// END OF EJS Templates
merge: refuse update/merge if there are unresolved conflicts (BC)...
Martin von Zweigbergk -
r27316:777f668e default
parent child Browse files
Show More
@@ -1,1530 +1,1534 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 os
11 import os
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 bin,
17 bin,
18 hex,
18 hex,
19 nullhex,
19 nullhex,
20 nullid,
20 nullid,
21 nullrev,
21 nullrev,
22 )
22 )
23 from . import (
23 from . import (
24 copies,
24 copies,
25 destutil,
25 destutil,
26 error,
26 error,
27 filemerge,
27 filemerge,
28 obsolete,
28 obsolete,
29 subrepo,
29 subrepo,
30 util,
30 util,
31 worker,
31 worker,
32 )
32 )
33
33
34 _pack = struct.pack
34 _pack = struct.pack
35 _unpack = struct.unpack
35 _unpack = struct.unpack
36
36
37 def _droponode(data):
37 def _droponode(data):
38 # used for compatibility for v1
38 # used for compatibility for v1
39 bits = data.split('\0')
39 bits = data.split('\0')
40 bits = bits[:-2] + bits[-1:]
40 bits = bits[:-2] + bits[-1:]
41 return '\0'.join(bits)
41 return '\0'.join(bits)
42
42
43 class mergestate(object):
43 class mergestate(object):
44 '''track 3-way merge state of individual files
44 '''track 3-way merge state of individual files
45
45
46 The merge state is stored on disk when needed. Two files are used: one with
46 The merge state is stored on disk when needed. Two files are used: one with
47 an old format (version 1), and one with a new format (version 2). Version 2
47 an old format (version 1), and one with a new format (version 2). Version 2
48 stores a superset of the data in version 1, including new kinds of records
48 stores a superset of the data in version 1, including new kinds of records
49 in the future. For more about the new format, see the documentation for
49 in the future. For more about the new format, see the documentation for
50 `_readrecordsv2`.
50 `_readrecordsv2`.
51
51
52 Each record can contain arbitrary content, and has an associated type. This
52 Each record can contain arbitrary content, and has an associated type. This
53 `type` should be a letter. If `type` is uppercase, the record is mandatory:
53 `type` should be a letter. If `type` is uppercase, the record is mandatory:
54 versions of Mercurial that don't support it should abort. If `type` is
54 versions of Mercurial that don't support it should abort. If `type` is
55 lowercase, the record can be safely ignored.
55 lowercase, the record can be safely ignored.
56
56
57 Currently known records:
57 Currently known records:
58
58
59 L: the node of the "local" part of the merge (hexified version)
59 L: the node of the "local" part of the merge (hexified version)
60 O: the node of the "other" part of the merge (hexified version)
60 O: the node of the "other" part of the merge (hexified version)
61 F: a file to be merged entry
61 F: a file to be merged entry
62 C: a change/delete or delete/change conflict
62 C: a change/delete or delete/change conflict
63 D: a file that the external merge driver will merge internally
63 D: a file that the external merge driver will merge internally
64 (experimental)
64 (experimental)
65 m: the external merge driver defined for this merge plus its run state
65 m: the external merge driver defined for this merge plus its run state
66 (experimental)
66 (experimental)
67 X: unsupported mandatory record type (used in tests)
67 X: unsupported mandatory record type (used in tests)
68 x: unsupported advisory record type (used in tests)
68 x: unsupported advisory record type (used in tests)
69
69
70 Merge driver run states (experimental):
70 Merge driver run states (experimental):
71 u: driver-resolved files unmarked -- needs to be run next time we're about
71 u: driver-resolved files unmarked -- needs to be run next time we're about
72 to resolve or commit
72 to resolve or commit
73 m: driver-resolved files marked -- only needs to be run before commit
73 m: driver-resolved files marked -- only needs to be run before commit
74 s: success/skipped -- does not need to be run any more
74 s: success/skipped -- does not need to be run any more
75
75
76 '''
76 '''
77 statepathv1 = 'merge/state'
77 statepathv1 = 'merge/state'
78 statepathv2 = 'merge/state2'
78 statepathv2 = 'merge/state2'
79
79
80 @staticmethod
80 @staticmethod
81 def clean(repo, node=None, other=None):
81 def clean(repo, node=None, other=None):
82 """Initialize a brand new merge state, removing any existing state on
82 """Initialize a brand new merge state, removing any existing state on
83 disk."""
83 disk."""
84 ms = mergestate(repo)
84 ms = mergestate(repo)
85 ms.reset(node, other)
85 ms.reset(node, other)
86 return ms
86 return ms
87
87
88 @staticmethod
88 @staticmethod
89 def read(repo):
89 def read(repo):
90 """Initialize the merge state, reading it from disk."""
90 """Initialize the merge state, reading it from disk."""
91 ms = mergestate(repo)
91 ms = mergestate(repo)
92 ms._read()
92 ms._read()
93 return ms
93 return ms
94
94
95 def __init__(self, repo):
95 def __init__(self, repo):
96 """Initialize the merge state.
96 """Initialize the merge state.
97
97
98 Do not use this directly! Instead call read() or clean()."""
98 Do not use this directly! Instead call read() or clean()."""
99 self._repo = repo
99 self._repo = repo
100 self._dirty = False
100 self._dirty = False
101
101
102 def reset(self, node=None, other=None):
102 def reset(self, node=None, other=None):
103 self._state = {}
103 self._state = {}
104 self._local = None
104 self._local = None
105 self._other = None
105 self._other = None
106 for var in ('localctx', 'otherctx'):
106 for var in ('localctx', 'otherctx'):
107 if var in vars(self):
107 if var in vars(self):
108 delattr(self, var)
108 delattr(self, var)
109 if node:
109 if node:
110 self._local = node
110 self._local = node
111 self._other = other
111 self._other = other
112 self._readmergedriver = None
112 self._readmergedriver = None
113 if self.mergedriver:
113 if self.mergedriver:
114 self._mdstate = 's'
114 self._mdstate = 's'
115 else:
115 else:
116 self._mdstate = 'u'
116 self._mdstate = 'u'
117 shutil.rmtree(self._repo.join('merge'), True)
117 shutil.rmtree(self._repo.join('merge'), True)
118 self._results = {}
118 self._results = {}
119 self._dirty = False
119 self._dirty = False
120
120
121 def _read(self):
121 def _read(self):
122 """Analyse each record content to restore a serialized state from disk
122 """Analyse each record content to restore a serialized state from disk
123
123
124 This function process "record" entry produced by the de-serialization
124 This function process "record" entry produced by the de-serialization
125 of on disk file.
125 of on disk file.
126 """
126 """
127 self._state = {}
127 self._state = {}
128 self._local = None
128 self._local = None
129 self._other = None
129 self._other = None
130 for var in ('localctx', 'otherctx'):
130 for var in ('localctx', 'otherctx'):
131 if var in vars(self):
131 if var in vars(self):
132 delattr(self, var)
132 delattr(self, var)
133 self._readmergedriver = None
133 self._readmergedriver = None
134 self._mdstate = 's'
134 self._mdstate = 's'
135 unsupported = set()
135 unsupported = set()
136 records = self._readrecords()
136 records = self._readrecords()
137 for rtype, record in records:
137 for rtype, record in records:
138 if rtype == 'L':
138 if rtype == 'L':
139 self._local = bin(record)
139 self._local = bin(record)
140 elif rtype == 'O':
140 elif rtype == 'O':
141 self._other = bin(record)
141 self._other = bin(record)
142 elif rtype == 'm':
142 elif rtype == 'm':
143 bits = record.split('\0', 1)
143 bits = record.split('\0', 1)
144 mdstate = bits[1]
144 mdstate = bits[1]
145 if len(mdstate) != 1 or mdstate not in 'ums':
145 if len(mdstate) != 1 or mdstate not in 'ums':
146 # the merge driver should be idempotent, so just rerun it
146 # the merge driver should be idempotent, so just rerun it
147 mdstate = 'u'
147 mdstate = 'u'
148
148
149 self._readmergedriver = bits[0]
149 self._readmergedriver = bits[0]
150 self._mdstate = mdstate
150 self._mdstate = mdstate
151 elif rtype in 'FDC':
151 elif rtype in 'FDC':
152 bits = record.split('\0')
152 bits = record.split('\0')
153 self._state[bits[0]] = bits[1:]
153 self._state[bits[0]] = bits[1:]
154 elif not rtype.islower():
154 elif not rtype.islower():
155 unsupported.add(rtype)
155 unsupported.add(rtype)
156 self._results = {}
156 self._results = {}
157 self._dirty = False
157 self._dirty = False
158
158
159 if unsupported:
159 if unsupported:
160 raise error.UnsupportedMergeRecords(unsupported)
160 raise error.UnsupportedMergeRecords(unsupported)
161
161
162 def _readrecords(self):
162 def _readrecords(self):
163 """Read merge state from disk and return a list of record (TYPE, data)
163 """Read merge state from disk and return a list of record (TYPE, data)
164
164
165 We read data from both v1 and v2 files and decide which one to use.
165 We read data from both v1 and v2 files and decide which one to use.
166
166
167 V1 has been used by version prior to 2.9.1 and contains less data than
167 V1 has been used by version prior to 2.9.1 and contains less data than
168 v2. We read both versions and check if no data in v2 contradicts
168 v2. We read both versions and check if no data in v2 contradicts
169 v1. If there is not contradiction we can safely assume that both v1
169 v1. If there is not contradiction we can safely assume that both v1
170 and v2 were written at the same time and use the extract data in v2. If
170 and v2 were written at the same time and use the extract data in v2. If
171 there is contradiction we ignore v2 content as we assume an old version
171 there is contradiction we ignore v2 content as we assume an old version
172 of Mercurial has overwritten the mergestate file and left an old v2
172 of Mercurial has overwritten the mergestate file and left an old v2
173 file around.
173 file around.
174
174
175 returns list of record [(TYPE, data), ...]"""
175 returns list of record [(TYPE, data), ...]"""
176 v1records = self._readrecordsv1()
176 v1records = self._readrecordsv1()
177 v2records = self._readrecordsv2()
177 v2records = self._readrecordsv2()
178 if self._v1v2match(v1records, v2records):
178 if self._v1v2match(v1records, v2records):
179 return v2records
179 return v2records
180 else:
180 else:
181 # v1 file is newer than v2 file, use it
181 # v1 file is newer than v2 file, use it
182 # we have to infer the "other" changeset of the merge
182 # we have to infer the "other" changeset of the merge
183 # we cannot do better than that with v1 of the format
183 # we cannot do better than that with v1 of the format
184 mctx = self._repo[None].parents()[-1]
184 mctx = self._repo[None].parents()[-1]
185 v1records.append(('O', mctx.hex()))
185 v1records.append(('O', mctx.hex()))
186 # add place holder "other" file node information
186 # add place holder "other" file node information
187 # nobody is using it yet so we do no need to fetch the data
187 # nobody is using it yet so we do no need to fetch the data
188 # if mctx was wrong `mctx[bits[-2]]` may fails.
188 # if mctx was wrong `mctx[bits[-2]]` may fails.
189 for idx, r in enumerate(v1records):
189 for idx, r in enumerate(v1records):
190 if r[0] == 'F':
190 if r[0] == 'F':
191 bits = r[1].split('\0')
191 bits = r[1].split('\0')
192 bits.insert(-2, '')
192 bits.insert(-2, '')
193 v1records[idx] = (r[0], '\0'.join(bits))
193 v1records[idx] = (r[0], '\0'.join(bits))
194 return v1records
194 return v1records
195
195
196 def _v1v2match(self, v1records, v2records):
196 def _v1v2match(self, v1records, v2records):
197 oldv2 = set() # old format version of v2 record
197 oldv2 = set() # old format version of v2 record
198 for rec in v2records:
198 for rec in v2records:
199 if rec[0] == 'L':
199 if rec[0] == 'L':
200 oldv2.add(rec)
200 oldv2.add(rec)
201 elif rec[0] == 'F':
201 elif rec[0] == 'F':
202 # drop the onode data (not contained in v1)
202 # drop the onode data (not contained in v1)
203 oldv2.add(('F', _droponode(rec[1])))
203 oldv2.add(('F', _droponode(rec[1])))
204 for rec in v1records:
204 for rec in v1records:
205 if rec not in oldv2:
205 if rec not in oldv2:
206 return False
206 return False
207 else:
207 else:
208 return True
208 return True
209
209
210 def _readrecordsv1(self):
210 def _readrecordsv1(self):
211 """read on disk merge state for version 1 file
211 """read on disk merge state for version 1 file
212
212
213 returns list of record [(TYPE, data), ...]
213 returns list of record [(TYPE, data), ...]
214
214
215 Note: the "F" data from this file are one entry short
215 Note: the "F" data from this file are one entry short
216 (no "other file node" entry)
216 (no "other file node" entry)
217 """
217 """
218 records = []
218 records = []
219 try:
219 try:
220 f = self._repo.vfs(self.statepathv1)
220 f = self._repo.vfs(self.statepathv1)
221 for i, l in enumerate(f):
221 for i, l in enumerate(f):
222 if i == 0:
222 if i == 0:
223 records.append(('L', l[:-1]))
223 records.append(('L', l[:-1]))
224 else:
224 else:
225 records.append(('F', l[:-1]))
225 records.append(('F', l[:-1]))
226 f.close()
226 f.close()
227 except IOError as err:
227 except IOError as err:
228 if err.errno != errno.ENOENT:
228 if err.errno != errno.ENOENT:
229 raise
229 raise
230 return records
230 return records
231
231
232 def _readrecordsv2(self):
232 def _readrecordsv2(self):
233 """read on disk merge state for version 2 file
233 """read on disk merge state for version 2 file
234
234
235 This format is a list of arbitrary records of the form:
235 This format is a list of arbitrary records of the form:
236
236
237 [type][length][content]
237 [type][length][content]
238
238
239 `type` is a single character, `length` is a 4 byte integer, and
239 `type` is a single character, `length` is a 4 byte integer, and
240 `content` is an arbitrary byte sequence of length `length`.
240 `content` is an arbitrary byte sequence of length `length`.
241
241
242 Mercurial versions prior to 3.7 have a bug where if there are
242 Mercurial versions prior to 3.7 have a bug where if there are
243 unsupported mandatory merge records, attempting to clear out the merge
243 unsupported mandatory merge records, attempting to clear out the merge
244 state with hg update --clean or similar aborts. The 't' record type
244 state with hg update --clean or similar aborts. The 't' record type
245 works around that by writing out what those versions treat as an
245 works around that by writing out what those versions treat as an
246 advisory record, but later versions interpret as special: the first
246 advisory record, but later versions interpret as special: the first
247 character is the 'real' record type and everything onwards is the data.
247 character is the 'real' record type and everything onwards is the data.
248
248
249 Returns list of records [(TYPE, data), ...]."""
249 Returns list of records [(TYPE, data), ...]."""
250 records = []
250 records = []
251 try:
251 try:
252 f = self._repo.vfs(self.statepathv2)
252 f = self._repo.vfs(self.statepathv2)
253 data = f.read()
253 data = f.read()
254 off = 0
254 off = 0
255 end = len(data)
255 end = len(data)
256 while off < end:
256 while off < end:
257 rtype = data[off]
257 rtype = data[off]
258 off += 1
258 off += 1
259 length = _unpack('>I', data[off:(off + 4)])[0]
259 length = _unpack('>I', data[off:(off + 4)])[0]
260 off += 4
260 off += 4
261 record = data[off:(off + length)]
261 record = data[off:(off + length)]
262 off += length
262 off += length
263 if rtype == 't':
263 if rtype == 't':
264 rtype, record = record[0], record[1:]
264 rtype, record = record[0], record[1:]
265 records.append((rtype, record))
265 records.append((rtype, record))
266 f.close()
266 f.close()
267 except IOError as err:
267 except IOError as err:
268 if err.errno != errno.ENOENT:
268 if err.errno != errno.ENOENT:
269 raise
269 raise
270 return records
270 return records
271
271
272 @util.propertycache
272 @util.propertycache
273 def mergedriver(self):
273 def mergedriver(self):
274 # protect against the following:
274 # protect against the following:
275 # - A configures a malicious merge driver in their hgrc, then
275 # - A configures a malicious merge driver in their hgrc, then
276 # pauses the merge
276 # pauses the merge
277 # - A edits their hgrc to remove references to the merge driver
277 # - A edits their hgrc to remove references to the merge driver
278 # - A gives a copy of their entire repo, including .hg, to B
278 # - A gives a copy of their entire repo, including .hg, to B
279 # - B inspects .hgrc and finds it to be clean
279 # - B inspects .hgrc and finds it to be clean
280 # - B then continues the merge and the malicious merge driver
280 # - B then continues the merge and the malicious merge driver
281 # gets invoked
281 # gets invoked
282 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
282 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
283 if (self._readmergedriver is not None
283 if (self._readmergedriver is not None
284 and self._readmergedriver != configmergedriver):
284 and self._readmergedriver != configmergedriver):
285 raise error.ConfigError(
285 raise error.ConfigError(
286 _("merge driver changed since merge started"),
286 _("merge driver changed since merge started"),
287 hint=_("revert merge driver change or abort merge"))
287 hint=_("revert merge driver change or abort merge"))
288
288
289 return configmergedriver
289 return configmergedriver
290
290
291 @util.propertycache
291 @util.propertycache
292 def localctx(self):
292 def localctx(self):
293 if self._local is None:
293 if self._local is None:
294 raise RuntimeError("localctx accessed but self._local isn't set")
294 raise RuntimeError("localctx accessed but self._local isn't set")
295 return self._repo[self._local]
295 return self._repo[self._local]
296
296
297 @util.propertycache
297 @util.propertycache
298 def otherctx(self):
298 def otherctx(self):
299 if self._other is None:
299 if self._other is None:
300 raise RuntimeError("localctx accessed but self._local isn't set")
300 raise RuntimeError("localctx accessed but self._local isn't set")
301 return self._repo[self._other]
301 return self._repo[self._other]
302
302
303 def active(self):
303 def active(self):
304 """Whether mergestate is active.
304 """Whether mergestate is active.
305
305
306 Returns True if there appears to be mergestate. This is a rough proxy
306 Returns True if there appears to be mergestate. This is a rough proxy
307 for "is a merge in progress."
307 for "is a merge in progress."
308 """
308 """
309 # Check local variables before looking at filesystem for performance
309 # Check local variables before looking at filesystem for performance
310 # reasons.
310 # reasons.
311 return bool(self._local) or bool(self._state) or \
311 return bool(self._local) or bool(self._state) or \
312 self._repo.vfs.exists(self.statepathv1) or \
312 self._repo.vfs.exists(self.statepathv1) or \
313 self._repo.vfs.exists(self.statepathv2)
313 self._repo.vfs.exists(self.statepathv2)
314
314
315 def commit(self):
315 def commit(self):
316 """Write current state on disk (if necessary)"""
316 """Write current state on disk (if necessary)"""
317 if self._dirty:
317 if self._dirty:
318 records = self._makerecords()
318 records = self._makerecords()
319 self._writerecords(records)
319 self._writerecords(records)
320 self._dirty = False
320 self._dirty = False
321
321
322 def _makerecords(self):
322 def _makerecords(self):
323 records = []
323 records = []
324 records.append(('L', hex(self._local)))
324 records.append(('L', hex(self._local)))
325 records.append(('O', hex(self._other)))
325 records.append(('O', hex(self._other)))
326 if self.mergedriver:
326 if self.mergedriver:
327 records.append(('m', '\0'.join([
327 records.append(('m', '\0'.join([
328 self.mergedriver, self._mdstate])))
328 self.mergedriver, self._mdstate])))
329 for d, v in self._state.iteritems():
329 for d, v in self._state.iteritems():
330 if v[0] == 'd':
330 if v[0] == 'd':
331 records.append(('D', '\0'.join([d] + v)))
331 records.append(('D', '\0'.join([d] + v)))
332 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
332 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
333 # older versions of Mercurial
333 # older versions of Mercurial
334 elif v[1] == nullhex or v[6] == nullhex:
334 elif v[1] == nullhex or v[6] == nullhex:
335 records.append(('C', '\0'.join([d] + v)))
335 records.append(('C', '\0'.join([d] + v)))
336 else:
336 else:
337 records.append(('F', '\0'.join([d] + v)))
337 records.append(('F', '\0'.join([d] + v)))
338 return records
338 return records
339
339
340 def _writerecords(self, records):
340 def _writerecords(self, records):
341 """Write current state on disk (both v1 and v2)"""
341 """Write current state on disk (both v1 and v2)"""
342 self._writerecordsv1(records)
342 self._writerecordsv1(records)
343 self._writerecordsv2(records)
343 self._writerecordsv2(records)
344
344
345 def _writerecordsv1(self, records):
345 def _writerecordsv1(self, records):
346 """Write current state on disk in a version 1 file"""
346 """Write current state on disk in a version 1 file"""
347 f = self._repo.vfs(self.statepathv1, 'w')
347 f = self._repo.vfs(self.statepathv1, 'w')
348 irecords = iter(records)
348 irecords = iter(records)
349 lrecords = irecords.next()
349 lrecords = irecords.next()
350 assert lrecords[0] == 'L'
350 assert lrecords[0] == 'L'
351 f.write(hex(self._local) + '\n')
351 f.write(hex(self._local) + '\n')
352 for rtype, data in irecords:
352 for rtype, data in irecords:
353 if rtype == 'F':
353 if rtype == 'F':
354 f.write('%s\n' % _droponode(data))
354 f.write('%s\n' % _droponode(data))
355 f.close()
355 f.close()
356
356
357 def _writerecordsv2(self, records):
357 def _writerecordsv2(self, records):
358 """Write current state on disk in a version 2 file
358 """Write current state on disk in a version 2 file
359
359
360 See the docstring for _readrecordsv2 for why we use 't'."""
360 See the docstring for _readrecordsv2 for why we use 't'."""
361 # these are the records that all version 2 clients can read
361 # these are the records that all version 2 clients can read
362 whitelist = 'LOF'
362 whitelist = 'LOF'
363 f = self._repo.vfs(self.statepathv2, 'w')
363 f = self._repo.vfs(self.statepathv2, 'w')
364 for key, data in records:
364 for key, data in records:
365 assert len(key) == 1
365 assert len(key) == 1
366 if key not in whitelist:
366 if key not in whitelist:
367 key, data = 't', '%s%s' % (key, data)
367 key, data = 't', '%s%s' % (key, data)
368 format = '>sI%is' % len(data)
368 format = '>sI%is' % len(data)
369 f.write(_pack(format, key, len(data), data))
369 f.write(_pack(format, key, len(data), data))
370 f.close()
370 f.close()
371
371
372 def add(self, fcl, fco, fca, fd):
372 def add(self, fcl, fco, fca, fd):
373 """add a new (potentially?) conflicting file the merge state
373 """add a new (potentially?) conflicting file the merge state
374 fcl: file context for local,
374 fcl: file context for local,
375 fco: file context for remote,
375 fco: file context for remote,
376 fca: file context for ancestors,
376 fca: file context for ancestors,
377 fd: file path of the resulting merge.
377 fd: file path of the resulting merge.
378
378
379 note: also write the local version to the `.hg/merge` directory.
379 note: also write the local version to the `.hg/merge` directory.
380 """
380 """
381 if fcl.isabsent():
381 if fcl.isabsent():
382 hash = nullhex
382 hash = nullhex
383 else:
383 else:
384 hash = util.sha1(fcl.path()).hexdigest()
384 hash = util.sha1(fcl.path()).hexdigest()
385 self._repo.vfs.write('merge/' + hash, fcl.data())
385 self._repo.vfs.write('merge/' + hash, fcl.data())
386 self._state[fd] = ['u', hash, fcl.path(),
386 self._state[fd] = ['u', hash, fcl.path(),
387 fca.path(), hex(fca.filenode()),
387 fca.path(), hex(fca.filenode()),
388 fco.path(), hex(fco.filenode()),
388 fco.path(), hex(fco.filenode()),
389 fcl.flags()]
389 fcl.flags()]
390 self._dirty = True
390 self._dirty = True
391
391
392 def __contains__(self, dfile):
392 def __contains__(self, dfile):
393 return dfile in self._state
393 return dfile in self._state
394
394
395 def __getitem__(self, dfile):
395 def __getitem__(self, dfile):
396 return self._state[dfile][0]
396 return self._state[dfile][0]
397
397
398 def __iter__(self):
398 def __iter__(self):
399 return iter(sorted(self._state))
399 return iter(sorted(self._state))
400
400
401 def files(self):
401 def files(self):
402 return self._state.keys()
402 return self._state.keys()
403
403
404 def mark(self, dfile, state):
404 def mark(self, dfile, state):
405 self._state[dfile][0] = state
405 self._state[dfile][0] = state
406 self._dirty = True
406 self._dirty = True
407
407
408 def mdstate(self):
408 def mdstate(self):
409 return self._mdstate
409 return self._mdstate
410
410
411 def unresolved(self):
411 def unresolved(self):
412 """Obtain the paths of unresolved files."""
412 """Obtain the paths of unresolved files."""
413
413
414 for f, entry in self._state.items():
414 for f, entry in self._state.items():
415 if entry[0] == 'u':
415 if entry[0] == 'u':
416 yield f
416 yield f
417
417
418 def driverresolved(self):
418 def driverresolved(self):
419 """Obtain the paths of driver-resolved files."""
419 """Obtain the paths of driver-resolved files."""
420
420
421 for f, entry in self._state.items():
421 for f, entry in self._state.items():
422 if entry[0] == 'd':
422 if entry[0] == 'd':
423 yield f
423 yield f
424
424
425 def _resolve(self, preresolve, dfile, wctx, labels=None):
425 def _resolve(self, preresolve, dfile, wctx, labels=None):
426 """rerun merge process for file path `dfile`"""
426 """rerun merge process for file path `dfile`"""
427 if self[dfile] in 'rd':
427 if self[dfile] in 'rd':
428 return True, 0
428 return True, 0
429 stateentry = self._state[dfile]
429 stateentry = self._state[dfile]
430 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
430 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
431 octx = self._repo[self._other]
431 octx = self._repo[self._other]
432 fcd = self._filectxorabsent(hash, wctx, dfile)
432 fcd = self._filectxorabsent(hash, wctx, dfile)
433 fco = self._filectxorabsent(onode, octx, ofile)
433 fco = self._filectxorabsent(onode, octx, ofile)
434 # TODO: move this to filectxorabsent
434 # TODO: move this to filectxorabsent
435 fca = self._repo.filectx(afile, fileid=anode)
435 fca = self._repo.filectx(afile, fileid=anode)
436 # "premerge" x flags
436 # "premerge" x flags
437 flo = fco.flags()
437 flo = fco.flags()
438 fla = fca.flags()
438 fla = fca.flags()
439 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
439 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
440 if fca.node() == nullid:
440 if fca.node() == nullid:
441 if preresolve:
441 if preresolve:
442 self._repo.ui.warn(
442 self._repo.ui.warn(
443 _('warning: cannot merge flags for %s\n') % afile)
443 _('warning: cannot merge flags for %s\n') % afile)
444 elif flags == fla:
444 elif flags == fla:
445 flags = flo
445 flags = flo
446 if preresolve:
446 if preresolve:
447 # restore local
447 # restore local
448 if hash != nullhex:
448 if hash != nullhex:
449 f = self._repo.vfs('merge/' + hash)
449 f = self._repo.vfs('merge/' + hash)
450 self._repo.wwrite(dfile, f.read(), flags)
450 self._repo.wwrite(dfile, f.read(), flags)
451 f.close()
451 f.close()
452 else:
452 else:
453 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
453 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
454 complete, r, deleted = filemerge.premerge(self._repo, self._local,
454 complete, r, deleted = filemerge.premerge(self._repo, self._local,
455 lfile, fcd, fco, fca,
455 lfile, fcd, fco, fca,
456 labels=labels)
456 labels=labels)
457 else:
457 else:
458 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
458 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
459 lfile, fcd, fco, fca,
459 lfile, fcd, fco, fca,
460 labels=labels)
460 labels=labels)
461 if r is None:
461 if r is None:
462 # no real conflict
462 # no real conflict
463 del self._state[dfile]
463 del self._state[dfile]
464 self._dirty = True
464 self._dirty = True
465 elif not r:
465 elif not r:
466 self.mark(dfile, 'r')
466 self.mark(dfile, 'r')
467
467
468 if complete:
468 if complete:
469 action = None
469 action = None
470 if deleted:
470 if deleted:
471 if fcd.isabsent():
471 if fcd.isabsent():
472 # dc: local picked. Need to drop if present, which may
472 # dc: local picked. Need to drop if present, which may
473 # happen on re-resolves.
473 # happen on re-resolves.
474 action = 'f'
474 action = 'f'
475 else:
475 else:
476 # cd: remote picked (or otherwise deleted)
476 # cd: remote picked (or otherwise deleted)
477 action = 'r'
477 action = 'r'
478 else:
478 else:
479 if fcd.isabsent(): # dc: remote picked
479 if fcd.isabsent(): # dc: remote picked
480 action = 'g'
480 action = 'g'
481 elif fco.isabsent(): # cd: local picked
481 elif fco.isabsent(): # cd: local picked
482 if dfile in self.localctx:
482 if dfile in self.localctx:
483 action = 'am'
483 action = 'am'
484 else:
484 else:
485 action = 'a'
485 action = 'a'
486 # else: regular merges (no action necessary)
486 # else: regular merges (no action necessary)
487 self._results[dfile] = r, action
487 self._results[dfile] = r, action
488
488
489 return complete, r
489 return complete, r
490
490
491 def _filectxorabsent(self, hexnode, ctx, f):
491 def _filectxorabsent(self, hexnode, ctx, f):
492 if hexnode == nullhex:
492 if hexnode == nullhex:
493 return filemerge.absentfilectx(ctx, f)
493 return filemerge.absentfilectx(ctx, f)
494 else:
494 else:
495 return ctx[f]
495 return ctx[f]
496
496
497 def preresolve(self, dfile, wctx, labels=None):
497 def preresolve(self, dfile, wctx, labels=None):
498 """run premerge process for dfile
498 """run premerge process for dfile
499
499
500 Returns whether the merge is complete, and the exit code."""
500 Returns whether the merge is complete, and the exit code."""
501 return self._resolve(True, dfile, wctx, labels=labels)
501 return self._resolve(True, dfile, wctx, labels=labels)
502
502
503 def resolve(self, dfile, wctx, labels=None):
503 def resolve(self, dfile, wctx, labels=None):
504 """run merge process (assuming premerge was run) for dfile
504 """run merge process (assuming premerge was run) for dfile
505
505
506 Returns the exit code of the merge."""
506 Returns the exit code of the merge."""
507 return self._resolve(False, dfile, wctx, labels=labels)[1]
507 return self._resolve(False, dfile, wctx, labels=labels)[1]
508
508
509 def counts(self):
509 def counts(self):
510 """return counts for updated, merged and removed files in this
510 """return counts for updated, merged and removed files in this
511 session"""
511 session"""
512 updated, merged, removed = 0, 0, 0
512 updated, merged, removed = 0, 0, 0
513 for r, action in self._results.itervalues():
513 for r, action in self._results.itervalues():
514 if r is None:
514 if r is None:
515 updated += 1
515 updated += 1
516 elif r == 0:
516 elif r == 0:
517 if action == 'r':
517 if action == 'r':
518 removed += 1
518 removed += 1
519 else:
519 else:
520 merged += 1
520 merged += 1
521 return updated, merged, removed
521 return updated, merged, removed
522
522
523 def unresolvedcount(self):
523 def unresolvedcount(self):
524 """get unresolved count for this merge (persistent)"""
524 """get unresolved count for this merge (persistent)"""
525 return len([True for f, entry in self._state.iteritems()
525 return len([True for f, entry in self._state.iteritems()
526 if entry[0] == 'u'])
526 if entry[0] == 'u'])
527
527
528 def actions(self):
528 def actions(self):
529 """return lists of actions to perform on the dirstate"""
529 """return lists of actions to perform on the dirstate"""
530 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
530 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
531 for f, (r, action) in self._results.iteritems():
531 for f, (r, action) in self._results.iteritems():
532 if action is not None:
532 if action is not None:
533 actions[action].append((f, None, "merge result"))
533 actions[action].append((f, None, "merge result"))
534 return actions
534 return actions
535
535
536 def recordactions(self):
536 def recordactions(self):
537 """record remove/add/get actions in the dirstate"""
537 """record remove/add/get actions in the dirstate"""
538 branchmerge = self._repo.dirstate.p2() != nullid
538 branchmerge = self._repo.dirstate.p2() != nullid
539 recordupdates(self._repo, self.actions(), branchmerge)
539 recordupdates(self._repo, self.actions(), branchmerge)
540
540
541 def queueremove(self, f):
541 def queueremove(self, f):
542 """queues a file to be removed from the dirstate
542 """queues a file to be removed from the dirstate
543
543
544 Meant for use by custom merge drivers."""
544 Meant for use by custom merge drivers."""
545 self._results[f] = 0, 'r'
545 self._results[f] = 0, 'r'
546
546
547 def queueadd(self, f):
547 def queueadd(self, f):
548 """queues a file to be added to the dirstate
548 """queues a file to be added to the dirstate
549
549
550 Meant for use by custom merge drivers."""
550 Meant for use by custom merge drivers."""
551 self._results[f] = 0, 'a'
551 self._results[f] = 0, 'a'
552
552
553 def queueget(self, f):
553 def queueget(self, f):
554 """queues a file to be marked modified in the dirstate
554 """queues a file to be marked modified in the dirstate
555
555
556 Meant for use by custom merge drivers."""
556 Meant for use by custom merge drivers."""
557 self._results[f] = 0, 'g'
557 self._results[f] = 0, 'g'
558
558
559 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
559 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
560 if f2 is None:
560 if f2 is None:
561 f2 = f
561 f2 = f
562 return (os.path.isfile(repo.wjoin(f))
562 return (os.path.isfile(repo.wjoin(f))
563 and repo.wvfs.audit.check(f)
563 and repo.wvfs.audit.check(f)
564 and repo.dirstate.normalize(f) not in repo.dirstate
564 and repo.dirstate.normalize(f) not in repo.dirstate
565 and mctx[f2].cmp(wctx[f]))
565 and mctx[f2].cmp(wctx[f]))
566
566
567 def _checkunknownfiles(repo, wctx, mctx, force, actions):
567 def _checkunknownfiles(repo, wctx, mctx, force, actions):
568 """
568 """
569 Considers any actions that care about the presence of conflicting unknown
569 Considers any actions that care about the presence of conflicting unknown
570 files. For some actions, the result is to abort; for others, it is to
570 files. For some actions, the result is to abort; for others, it is to
571 choose a different action.
571 choose a different action.
572 """
572 """
573 aborts = []
573 aborts = []
574 if not force:
574 if not force:
575 for f, (m, args, msg) in actions.iteritems():
575 for f, (m, args, msg) in actions.iteritems():
576 if m in ('c', 'dc'):
576 if m in ('c', 'dc'):
577 if _checkunknownfile(repo, wctx, mctx, f):
577 if _checkunknownfile(repo, wctx, mctx, f):
578 aborts.append(f)
578 aborts.append(f)
579 elif m == 'dg':
579 elif m == 'dg':
580 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
580 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
581 aborts.append(f)
581 aborts.append(f)
582
582
583 for f in sorted(aborts):
583 for f in sorted(aborts):
584 repo.ui.warn(_("%s: untracked file differs\n") % f)
584 repo.ui.warn(_("%s: untracked file differs\n") % f)
585 if aborts:
585 if aborts:
586 raise error.Abort(_("untracked files in working directory differ "
586 raise error.Abort(_("untracked files in working directory differ "
587 "from files in requested revision"))
587 "from files in requested revision"))
588
588
589 for f, (m, args, msg) in actions.iteritems():
589 for f, (m, args, msg) in actions.iteritems():
590 if m == 'c':
590 if m == 'c':
591 actions[f] = ('g', args, msg)
591 actions[f] = ('g', args, msg)
592 elif m == 'cm':
592 elif m == 'cm':
593 fl2, anc = args
593 fl2, anc = args
594 different = _checkunknownfile(repo, wctx, mctx, f)
594 different = _checkunknownfile(repo, wctx, mctx, f)
595 if different:
595 if different:
596 actions[f] = ('m', (f, f, None, False, anc),
596 actions[f] = ('m', (f, f, None, False, anc),
597 "remote differs from untracked local")
597 "remote differs from untracked local")
598 else:
598 else:
599 actions[f] = ('g', (fl2,), "remote created")
599 actions[f] = ('g', (fl2,), "remote created")
600
600
601 def _forgetremoved(wctx, mctx, branchmerge):
601 def _forgetremoved(wctx, mctx, branchmerge):
602 """
602 """
603 Forget removed files
603 Forget removed files
604
604
605 If we're jumping between revisions (as opposed to merging), and if
605 If we're jumping between revisions (as opposed to merging), and if
606 neither the working directory nor the target rev has the file,
606 neither the working directory nor the target rev has the file,
607 then we need to remove it from the dirstate, to prevent the
607 then we need to remove it from the dirstate, to prevent the
608 dirstate from listing the file when it is no longer in the
608 dirstate from listing the file when it is no longer in the
609 manifest.
609 manifest.
610
610
611 If we're merging, and the other revision has removed a file
611 If we're merging, and the other revision has removed a file
612 that is not present in the working directory, we need to mark it
612 that is not present in the working directory, we need to mark it
613 as removed.
613 as removed.
614 """
614 """
615
615
616 actions = {}
616 actions = {}
617 m = 'f'
617 m = 'f'
618 if branchmerge:
618 if branchmerge:
619 m = 'r'
619 m = 'r'
620 for f in wctx.deleted():
620 for f in wctx.deleted():
621 if f not in mctx:
621 if f not in mctx:
622 actions[f] = m, None, "forget deleted"
622 actions[f] = m, None, "forget deleted"
623
623
624 if not branchmerge:
624 if not branchmerge:
625 for f in wctx.removed():
625 for f in wctx.removed():
626 if f not in mctx:
626 if f not in mctx:
627 actions[f] = 'f', None, "forget removed"
627 actions[f] = 'f', None, "forget removed"
628
628
629 return actions
629 return actions
630
630
631 def _checkcollision(repo, wmf, actions):
631 def _checkcollision(repo, wmf, actions):
632 # build provisional merged manifest up
632 # build provisional merged manifest up
633 pmmf = set(wmf)
633 pmmf = set(wmf)
634
634
635 if actions:
635 if actions:
636 # k, dr, e and rd are no-op
636 # k, dr, e and rd are no-op
637 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
637 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
638 for f, args, msg in actions[m]:
638 for f, args, msg in actions[m]:
639 pmmf.add(f)
639 pmmf.add(f)
640 for f, args, msg in actions['r']:
640 for f, args, msg in actions['r']:
641 pmmf.discard(f)
641 pmmf.discard(f)
642 for f, args, msg in actions['dm']:
642 for f, args, msg in actions['dm']:
643 f2, flags = args
643 f2, flags = args
644 pmmf.discard(f2)
644 pmmf.discard(f2)
645 pmmf.add(f)
645 pmmf.add(f)
646 for f, args, msg in actions['dg']:
646 for f, args, msg in actions['dg']:
647 pmmf.add(f)
647 pmmf.add(f)
648 for f, args, msg in actions['m']:
648 for f, args, msg in actions['m']:
649 f1, f2, fa, move, anc = args
649 f1, f2, fa, move, anc = args
650 if move:
650 if move:
651 pmmf.discard(f1)
651 pmmf.discard(f1)
652 pmmf.add(f)
652 pmmf.add(f)
653
653
654 # check case-folding collision in provisional merged manifest
654 # check case-folding collision in provisional merged manifest
655 foldmap = {}
655 foldmap = {}
656 for f in sorted(pmmf):
656 for f in sorted(pmmf):
657 fold = util.normcase(f)
657 fold = util.normcase(f)
658 if fold in foldmap:
658 if fold in foldmap:
659 raise error.Abort(_("case-folding collision between %s and %s")
659 raise error.Abort(_("case-folding collision between %s and %s")
660 % (f, foldmap[fold]))
660 % (f, foldmap[fold]))
661 foldmap[fold] = f
661 foldmap[fold] = f
662
662
663 # check case-folding of directories
663 # check case-folding of directories
664 foldprefix = unfoldprefix = lastfull = ''
664 foldprefix = unfoldprefix = lastfull = ''
665 for fold, f in sorted(foldmap.items()):
665 for fold, f in sorted(foldmap.items()):
666 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
666 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
667 # the folded prefix matches but actual casing is different
667 # the folded prefix matches but actual casing is different
668 raise error.Abort(_("case-folding collision between "
668 raise error.Abort(_("case-folding collision between "
669 "%s and directory of %s") % (lastfull, f))
669 "%s and directory of %s") % (lastfull, f))
670 foldprefix = fold + '/'
670 foldprefix = fold + '/'
671 unfoldprefix = f + '/'
671 unfoldprefix = f + '/'
672 lastfull = f
672 lastfull = f
673
673
674 def driverpreprocess(repo, ms, wctx, labels=None):
674 def driverpreprocess(repo, ms, wctx, labels=None):
675 """run the preprocess step of the merge driver, if any
675 """run the preprocess step of the merge driver, if any
676
676
677 This is currently not implemented -- it's an extension point."""
677 This is currently not implemented -- it's an extension point."""
678 return True
678 return True
679
679
680 def driverconclude(repo, ms, wctx, labels=None):
680 def driverconclude(repo, ms, wctx, labels=None):
681 """run the conclude step of the merge driver, if any
681 """run the conclude step of the merge driver, if any
682
682
683 This is currently not implemented -- it's an extension point."""
683 This is currently not implemented -- it's an extension point."""
684 return True
684 return True
685
685
686 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
686 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
687 acceptremote, followcopies):
687 acceptremote, followcopies):
688 """
688 """
689 Merge p1 and p2 with ancestor pa and generate merge action list
689 Merge p1 and p2 with ancestor pa and generate merge action list
690
690
691 branchmerge and force are as passed in to update
691 branchmerge and force are as passed in to update
692 partial = function to filter file lists
692 partial = function to filter file lists
693 acceptremote = accept the incoming changes without prompting
693 acceptremote = accept the incoming changes without prompting
694 """
694 """
695
695
696 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
696 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
697
697
698 # manifests fetched in order are going to be faster, so prime the caches
698 # manifests fetched in order are going to be faster, so prime the caches
699 [x.manifest() for x in
699 [x.manifest() for x in
700 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
700 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
701
701
702 if followcopies:
702 if followcopies:
703 ret = copies.mergecopies(repo, wctx, p2, pa)
703 ret = copies.mergecopies(repo, wctx, p2, pa)
704 copy, movewithdir, diverge, renamedelete = ret
704 copy, movewithdir, diverge, renamedelete = ret
705
705
706 repo.ui.note(_("resolving manifests\n"))
706 repo.ui.note(_("resolving manifests\n"))
707 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
707 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
708 % (bool(branchmerge), bool(force), bool(partial)))
708 % (bool(branchmerge), bool(force), bool(partial)))
709 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
709 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
710
710
711 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
711 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
712 copied = set(copy.values())
712 copied = set(copy.values())
713 copied.update(movewithdir.values())
713 copied.update(movewithdir.values())
714
714
715 if '.hgsubstate' in m1:
715 if '.hgsubstate' in m1:
716 # check whether sub state is modified
716 # check whether sub state is modified
717 for s in sorted(wctx.substate):
717 for s in sorted(wctx.substate):
718 if wctx.sub(s).dirty():
718 if wctx.sub(s).dirty():
719 m1['.hgsubstate'] += '+'
719 m1['.hgsubstate'] += '+'
720 break
720 break
721
721
722 # Compare manifests
722 # Compare manifests
723 diff = m1.diff(m2)
723 diff = m1.diff(m2)
724
724
725 actions = {}
725 actions = {}
726 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
726 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
727 if partial and not partial(f):
727 if partial and not partial(f):
728 continue
728 continue
729 if n1 and n2: # file exists on both local and remote side
729 if n1 and n2: # file exists on both local and remote side
730 if f not in ma:
730 if f not in ma:
731 fa = copy.get(f, None)
731 fa = copy.get(f, None)
732 if fa is not None:
732 if fa is not None:
733 actions[f] = ('m', (f, f, fa, False, pa.node()),
733 actions[f] = ('m', (f, f, fa, False, pa.node()),
734 "both renamed from " + fa)
734 "both renamed from " + fa)
735 else:
735 else:
736 actions[f] = ('m', (f, f, None, False, pa.node()),
736 actions[f] = ('m', (f, f, None, False, pa.node()),
737 "both created")
737 "both created")
738 else:
738 else:
739 a = ma[f]
739 a = ma[f]
740 fla = ma.flags(f)
740 fla = ma.flags(f)
741 nol = 'l' not in fl1 + fl2 + fla
741 nol = 'l' not in fl1 + fl2 + fla
742 if n2 == a and fl2 == fla:
742 if n2 == a and fl2 == fla:
743 actions[f] = ('k' , (), "remote unchanged")
743 actions[f] = ('k' , (), "remote unchanged")
744 elif n1 == a and fl1 == fla: # local unchanged - use remote
744 elif n1 == a and fl1 == fla: # local unchanged - use remote
745 if n1 == n2: # optimization: keep local content
745 if n1 == n2: # optimization: keep local content
746 actions[f] = ('e', (fl2,), "update permissions")
746 actions[f] = ('e', (fl2,), "update permissions")
747 else:
747 else:
748 actions[f] = ('g', (fl2,), "remote is newer")
748 actions[f] = ('g', (fl2,), "remote is newer")
749 elif nol and n2 == a: # remote only changed 'x'
749 elif nol and n2 == a: # remote only changed 'x'
750 actions[f] = ('e', (fl2,), "update permissions")
750 actions[f] = ('e', (fl2,), "update permissions")
751 elif nol and n1 == a: # local only changed 'x'
751 elif nol and n1 == a: # local only changed 'x'
752 actions[f] = ('g', (fl1,), "remote is newer")
752 actions[f] = ('g', (fl1,), "remote is newer")
753 else: # both changed something
753 else: # both changed something
754 actions[f] = ('m', (f, f, f, False, pa.node()),
754 actions[f] = ('m', (f, f, f, False, pa.node()),
755 "versions differ")
755 "versions differ")
756 elif n1: # file exists only on local side
756 elif n1: # file exists only on local side
757 if f in copied:
757 if f in copied:
758 pass # we'll deal with it on m2 side
758 pass # we'll deal with it on m2 side
759 elif f in movewithdir: # directory rename, move local
759 elif f in movewithdir: # directory rename, move local
760 f2 = movewithdir[f]
760 f2 = movewithdir[f]
761 if f2 in m2:
761 if f2 in m2:
762 actions[f2] = ('m', (f, f2, None, True, pa.node()),
762 actions[f2] = ('m', (f, f2, None, True, pa.node()),
763 "remote directory rename, both created")
763 "remote directory rename, both created")
764 else:
764 else:
765 actions[f2] = ('dm', (f, fl1),
765 actions[f2] = ('dm', (f, fl1),
766 "remote directory rename - move from " + f)
766 "remote directory rename - move from " + f)
767 elif f in copy:
767 elif f in copy:
768 f2 = copy[f]
768 f2 = copy[f]
769 actions[f] = ('m', (f, f2, f2, False, pa.node()),
769 actions[f] = ('m', (f, f2, f2, False, pa.node()),
770 "local copied/moved from " + f2)
770 "local copied/moved from " + f2)
771 elif f in ma: # clean, a different, no remote
771 elif f in ma: # clean, a different, no remote
772 if n1 != ma[f]:
772 if n1 != ma[f]:
773 if acceptremote:
773 if acceptremote:
774 actions[f] = ('r', None, "remote delete")
774 actions[f] = ('r', None, "remote delete")
775 else:
775 else:
776 actions[f] = ('cd', (f, None, f, False, pa.node()),
776 actions[f] = ('cd', (f, None, f, False, pa.node()),
777 "prompt changed/deleted")
777 "prompt changed/deleted")
778 elif n1[20:] == 'a':
778 elif n1[20:] == 'a':
779 # This extra 'a' is added by working copy manifest to mark
779 # This extra 'a' is added by working copy manifest to mark
780 # the file as locally added. We should forget it instead of
780 # the file as locally added. We should forget it instead of
781 # deleting it.
781 # deleting it.
782 actions[f] = ('f', None, "remote deleted")
782 actions[f] = ('f', None, "remote deleted")
783 else:
783 else:
784 actions[f] = ('r', None, "other deleted")
784 actions[f] = ('r', None, "other deleted")
785 elif n2: # file exists only on remote side
785 elif n2: # file exists only on remote side
786 if f in copied:
786 if f in copied:
787 pass # we'll deal with it on m1 side
787 pass # we'll deal with it on m1 side
788 elif f in movewithdir:
788 elif f in movewithdir:
789 f2 = movewithdir[f]
789 f2 = movewithdir[f]
790 if f2 in m1:
790 if f2 in m1:
791 actions[f2] = ('m', (f2, f, None, False, pa.node()),
791 actions[f2] = ('m', (f2, f, None, False, pa.node()),
792 "local directory rename, both created")
792 "local directory rename, both created")
793 else:
793 else:
794 actions[f2] = ('dg', (f, fl2),
794 actions[f2] = ('dg', (f, fl2),
795 "local directory rename - get from " + f)
795 "local directory rename - get from " + f)
796 elif f in copy:
796 elif f in copy:
797 f2 = copy[f]
797 f2 = copy[f]
798 if f2 in m2:
798 if f2 in m2:
799 actions[f] = ('m', (f2, f, f2, False, pa.node()),
799 actions[f] = ('m', (f2, f, f2, False, pa.node()),
800 "remote copied from " + f2)
800 "remote copied from " + f2)
801 else:
801 else:
802 actions[f] = ('m', (f2, f, f2, True, pa.node()),
802 actions[f] = ('m', (f2, f, f2, True, pa.node()),
803 "remote moved from " + f2)
803 "remote moved from " + f2)
804 elif f not in ma:
804 elif f not in ma:
805 # local unknown, remote created: the logic is described by the
805 # local unknown, remote created: the logic is described by the
806 # following table:
806 # following table:
807 #
807 #
808 # force branchmerge different | action
808 # force branchmerge different | action
809 # n * * | create
809 # n * * | create
810 # y n * | create
810 # y n * | create
811 # y y n | create
811 # y y n | create
812 # y y y | merge
812 # y y y | merge
813 #
813 #
814 # Checking whether the files are different is expensive, so we
814 # Checking whether the files are different is expensive, so we
815 # don't do that when we can avoid it.
815 # don't do that when we can avoid it.
816 if not force:
816 if not force:
817 actions[f] = ('c', (fl2,), "remote created")
817 actions[f] = ('c', (fl2,), "remote created")
818 elif not branchmerge:
818 elif not branchmerge:
819 actions[f] = ('c', (fl2,), "remote created")
819 actions[f] = ('c', (fl2,), "remote created")
820 else:
820 else:
821 actions[f] = ('cm', (fl2, pa.node()),
821 actions[f] = ('cm', (fl2, pa.node()),
822 "remote created, get or merge")
822 "remote created, get or merge")
823 elif n2 != ma[f]:
823 elif n2 != ma[f]:
824 if acceptremote:
824 if acceptremote:
825 actions[f] = ('c', (fl2,), "remote recreating")
825 actions[f] = ('c', (fl2,), "remote recreating")
826 else:
826 else:
827 actions[f] = ('dc', (None, f, f, False, pa.node()),
827 actions[f] = ('dc', (None, f, f, False, pa.node()),
828 "prompt deleted/changed")
828 "prompt deleted/changed")
829
829
830 return actions, diverge, renamedelete
830 return actions, diverge, renamedelete
831
831
832 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
832 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
833 """Resolves false conflicts where the nodeid changed but the content
833 """Resolves false conflicts where the nodeid changed but the content
834 remained the same."""
834 remained the same."""
835
835
836 for f, (m, args, msg) in actions.items():
836 for f, (m, args, msg) in actions.items():
837 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
837 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
838 # local did change but ended up with same content
838 # local did change but ended up with same content
839 actions[f] = 'r', None, "prompt same"
839 actions[f] = 'r', None, "prompt same"
840 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
840 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
841 # remote did change but ended up with same content
841 # remote did change but ended up with same content
842 del actions[f] # don't get = keep local deleted
842 del actions[f] # don't get = keep local deleted
843
843
844 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
844 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
845 acceptremote, followcopies):
845 acceptremote, followcopies):
846 "Calculate the actions needed to merge mctx into wctx using ancestors"
846 "Calculate the actions needed to merge mctx into wctx using ancestors"
847
847
848 if len(ancestors) == 1: # default
848 if len(ancestors) == 1: # default
849 actions, diverge, renamedelete = manifestmerge(
849 actions, diverge, renamedelete = manifestmerge(
850 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
850 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
851 acceptremote, followcopies)
851 acceptremote, followcopies)
852 _checkunknownfiles(repo, wctx, mctx, force, actions)
852 _checkunknownfiles(repo, wctx, mctx, force, actions)
853
853
854 else: # only when merge.preferancestor=* - the default
854 else: # only when merge.preferancestor=* - the default
855 repo.ui.note(
855 repo.ui.note(
856 _("note: merging %s and %s using bids from ancestors %s\n") %
856 _("note: merging %s and %s using bids from ancestors %s\n") %
857 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
857 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
858
858
859 # Call for bids
859 # Call for bids
860 fbids = {} # mapping filename to bids (action method to list af actions)
860 fbids = {} # mapping filename to bids (action method to list af actions)
861 diverge, renamedelete = None, None
861 diverge, renamedelete = None, None
862 for ancestor in ancestors:
862 for ancestor in ancestors:
863 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
863 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
864 actions, diverge1, renamedelete1 = manifestmerge(
864 actions, diverge1, renamedelete1 = manifestmerge(
865 repo, wctx, mctx, ancestor, branchmerge, force, partial,
865 repo, wctx, mctx, ancestor, branchmerge, force, partial,
866 acceptremote, followcopies)
866 acceptremote, followcopies)
867 _checkunknownfiles(repo, wctx, mctx, force, actions)
867 _checkunknownfiles(repo, wctx, mctx, force, actions)
868
868
869 # Track the shortest set of warning on the theory that bid
869 # Track the shortest set of warning on the theory that bid
870 # merge will correctly incorporate more information
870 # merge will correctly incorporate more information
871 if diverge is None or len(diverge1) < len(diverge):
871 if diverge is None or len(diverge1) < len(diverge):
872 diverge = diverge1
872 diverge = diverge1
873 if renamedelete is None or len(renamedelete) < len(renamedelete1):
873 if renamedelete is None or len(renamedelete) < len(renamedelete1):
874 renamedelete = renamedelete1
874 renamedelete = renamedelete1
875
875
876 for f, a in sorted(actions.iteritems()):
876 for f, a in sorted(actions.iteritems()):
877 m, args, msg = a
877 m, args, msg = a
878 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
878 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
879 if f in fbids:
879 if f in fbids:
880 d = fbids[f]
880 d = fbids[f]
881 if m in d:
881 if m in d:
882 d[m].append(a)
882 d[m].append(a)
883 else:
883 else:
884 d[m] = [a]
884 d[m] = [a]
885 else:
885 else:
886 fbids[f] = {m: [a]}
886 fbids[f] = {m: [a]}
887
887
888 # Pick the best bid for each file
888 # Pick the best bid for each file
889 repo.ui.note(_('\nauction for merging merge bids\n'))
889 repo.ui.note(_('\nauction for merging merge bids\n'))
890 actions = {}
890 actions = {}
891 for f, bids in sorted(fbids.items()):
891 for f, bids in sorted(fbids.items()):
892 # bids is a mapping from action method to list af actions
892 # bids is a mapping from action method to list af actions
893 # Consensus?
893 # Consensus?
894 if len(bids) == 1: # all bids are the same kind of method
894 if len(bids) == 1: # all bids are the same kind of method
895 m, l = bids.items()[0]
895 m, l = bids.items()[0]
896 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
896 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
897 repo.ui.note(" %s: consensus for %s\n" % (f, m))
897 repo.ui.note(" %s: consensus for %s\n" % (f, m))
898 actions[f] = l[0]
898 actions[f] = l[0]
899 continue
899 continue
900 # If keep is an option, just do it.
900 # If keep is an option, just do it.
901 if 'k' in bids:
901 if 'k' in bids:
902 repo.ui.note(" %s: picking 'keep' action\n" % f)
902 repo.ui.note(" %s: picking 'keep' action\n" % f)
903 actions[f] = bids['k'][0]
903 actions[f] = bids['k'][0]
904 continue
904 continue
905 # If there are gets and they all agree [how could they not?], do it.
905 # If there are gets and they all agree [how could they not?], do it.
906 if 'g' in bids:
906 if 'g' in bids:
907 ga0 = bids['g'][0]
907 ga0 = bids['g'][0]
908 if all(a == ga0 for a in bids['g'][1:]):
908 if all(a == ga0 for a in bids['g'][1:]):
909 repo.ui.note(" %s: picking 'get' action\n" % f)
909 repo.ui.note(" %s: picking 'get' action\n" % f)
910 actions[f] = ga0
910 actions[f] = ga0
911 continue
911 continue
912 # TODO: Consider other simple actions such as mode changes
912 # TODO: Consider other simple actions such as mode changes
913 # Handle inefficient democrazy.
913 # Handle inefficient democrazy.
914 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
914 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
915 for m, l in sorted(bids.items()):
915 for m, l in sorted(bids.items()):
916 for _f, args, msg in l:
916 for _f, args, msg in l:
917 repo.ui.note(' %s -> %s\n' % (msg, m))
917 repo.ui.note(' %s -> %s\n' % (msg, m))
918 # Pick random action. TODO: Instead, prompt user when resolving
918 # Pick random action. TODO: Instead, prompt user when resolving
919 m, l = bids.items()[0]
919 m, l = bids.items()[0]
920 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
920 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
921 (f, m))
921 (f, m))
922 actions[f] = l[0]
922 actions[f] = l[0]
923 continue
923 continue
924 repo.ui.note(_('end of auction\n\n'))
924 repo.ui.note(_('end of auction\n\n'))
925
925
926 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
926 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
927
927
928 if wctx.rev() is None:
928 if wctx.rev() is None:
929 fractions = _forgetremoved(wctx, mctx, branchmerge)
929 fractions = _forgetremoved(wctx, mctx, branchmerge)
930 actions.update(fractions)
930 actions.update(fractions)
931
931
932 return actions, diverge, renamedelete
932 return actions, diverge, renamedelete
933
933
934 def batchremove(repo, actions):
934 def batchremove(repo, actions):
935 """apply removes to the working directory
935 """apply removes to the working directory
936
936
937 yields tuples for progress updates
937 yields tuples for progress updates
938 """
938 """
939 verbose = repo.ui.verbose
939 verbose = repo.ui.verbose
940 unlink = util.unlinkpath
940 unlink = util.unlinkpath
941 wjoin = repo.wjoin
941 wjoin = repo.wjoin
942 audit = repo.wvfs.audit
942 audit = repo.wvfs.audit
943 i = 0
943 i = 0
944 for f, args, msg in actions:
944 for f, args, msg in actions:
945 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
945 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
946 if verbose:
946 if verbose:
947 repo.ui.note(_("removing %s\n") % f)
947 repo.ui.note(_("removing %s\n") % f)
948 audit(f)
948 audit(f)
949 try:
949 try:
950 unlink(wjoin(f), ignoremissing=True)
950 unlink(wjoin(f), ignoremissing=True)
951 except OSError as inst:
951 except OSError as inst:
952 repo.ui.warn(_("update failed to remove %s: %s!\n") %
952 repo.ui.warn(_("update failed to remove %s: %s!\n") %
953 (f, inst.strerror))
953 (f, inst.strerror))
954 if i == 100:
954 if i == 100:
955 yield i, f
955 yield i, f
956 i = 0
956 i = 0
957 i += 1
957 i += 1
958 if i > 0:
958 if i > 0:
959 yield i, f
959 yield i, f
960
960
961 def batchget(repo, mctx, actions):
961 def batchget(repo, mctx, actions):
962 """apply gets to the working directory
962 """apply gets to the working directory
963
963
964 mctx is the context to get from
964 mctx is the context to get from
965
965
966 yields tuples for progress updates
966 yields tuples for progress updates
967 """
967 """
968 verbose = repo.ui.verbose
968 verbose = repo.ui.verbose
969 fctx = mctx.filectx
969 fctx = mctx.filectx
970 wwrite = repo.wwrite
970 wwrite = repo.wwrite
971 i = 0
971 i = 0
972 for f, args, msg in actions:
972 for f, args, msg in actions:
973 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
973 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
974 if verbose:
974 if verbose:
975 repo.ui.note(_("getting %s\n") % f)
975 repo.ui.note(_("getting %s\n") % f)
976 wwrite(f, fctx(f).data(), args[0])
976 wwrite(f, fctx(f).data(), args[0])
977 if i == 100:
977 if i == 100:
978 yield i, f
978 yield i, f
979 i = 0
979 i = 0
980 i += 1
980 i += 1
981 if i > 0:
981 if i > 0:
982 yield i, f
982 yield i, f
983
983
984 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
984 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
985 """apply the merge action list to the working directory
985 """apply the merge action list to the working directory
986
986
987 wctx is the working copy context
987 wctx is the working copy context
988 mctx is the context to be merged into the working copy
988 mctx is the context to be merged into the working copy
989
989
990 Return a tuple of counts (updated, merged, removed, unresolved) that
990 Return a tuple of counts (updated, merged, removed, unresolved) that
991 describes how many files were affected by the update.
991 describes how many files were affected by the update.
992 """
992 """
993
993
994 updated, merged, removed = 0, 0, 0
994 updated, merged, removed = 0, 0, 0
995 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
995 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
996 moves = []
996 moves = []
997 for m, l in actions.items():
997 for m, l in actions.items():
998 l.sort()
998 l.sort()
999
999
1000 # 'cd' and 'dc' actions are treated like other merge conflicts
1000 # 'cd' and 'dc' actions are treated like other merge conflicts
1001 mergeactions = sorted(actions['cd'])
1001 mergeactions = sorted(actions['cd'])
1002 mergeactions.extend(sorted(actions['dc']))
1002 mergeactions.extend(sorted(actions['dc']))
1003 mergeactions.extend(actions['m'])
1003 mergeactions.extend(actions['m'])
1004 for f, args, msg in mergeactions:
1004 for f, args, msg in mergeactions:
1005 f1, f2, fa, move, anc = args
1005 f1, f2, fa, move, anc = args
1006 if f == '.hgsubstate': # merged internally
1006 if f == '.hgsubstate': # merged internally
1007 continue
1007 continue
1008 if f1 is None:
1008 if f1 is None:
1009 fcl = filemerge.absentfilectx(wctx, fa)
1009 fcl = filemerge.absentfilectx(wctx, fa)
1010 else:
1010 else:
1011 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1011 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1012 fcl = wctx[f1]
1012 fcl = wctx[f1]
1013 if f2 is None:
1013 if f2 is None:
1014 fco = filemerge.absentfilectx(mctx, fa)
1014 fco = filemerge.absentfilectx(mctx, fa)
1015 else:
1015 else:
1016 fco = mctx[f2]
1016 fco = mctx[f2]
1017 actx = repo[anc]
1017 actx = repo[anc]
1018 if fa in actx:
1018 if fa in actx:
1019 fca = actx[fa]
1019 fca = actx[fa]
1020 else:
1020 else:
1021 # TODO: move to absentfilectx
1021 # TODO: move to absentfilectx
1022 fca = repo.filectx(f1, fileid=nullrev)
1022 fca = repo.filectx(f1, fileid=nullrev)
1023 ms.add(fcl, fco, fca, f)
1023 ms.add(fcl, fco, fca, f)
1024 if f1 != f and move:
1024 if f1 != f and move:
1025 moves.append(f1)
1025 moves.append(f1)
1026
1026
1027 audit = repo.wvfs.audit
1027 audit = repo.wvfs.audit
1028 _updating = _('updating')
1028 _updating = _('updating')
1029 _files = _('files')
1029 _files = _('files')
1030 progress = repo.ui.progress
1030 progress = repo.ui.progress
1031
1031
1032 # remove renamed files after safely stored
1032 # remove renamed files after safely stored
1033 for f in moves:
1033 for f in moves:
1034 if os.path.lexists(repo.wjoin(f)):
1034 if os.path.lexists(repo.wjoin(f)):
1035 repo.ui.debug("removing %s\n" % f)
1035 repo.ui.debug("removing %s\n" % f)
1036 audit(f)
1036 audit(f)
1037 util.unlinkpath(repo.wjoin(f))
1037 util.unlinkpath(repo.wjoin(f))
1038
1038
1039 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1039 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1040
1040
1041 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1041 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1042 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1042 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1043
1043
1044 # remove in parallel (must come first)
1044 # remove in parallel (must come first)
1045 z = 0
1045 z = 0
1046 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1046 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1047 for i, item in prog:
1047 for i, item in prog:
1048 z += i
1048 z += i
1049 progress(_updating, z, item=item, total=numupdates, unit=_files)
1049 progress(_updating, z, item=item, total=numupdates, unit=_files)
1050 removed = len(actions['r'])
1050 removed = len(actions['r'])
1051
1051
1052 # get in parallel
1052 # get in parallel
1053 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1053 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1054 for i, item in prog:
1054 for i, item in prog:
1055 z += i
1055 z += i
1056 progress(_updating, z, item=item, total=numupdates, unit=_files)
1056 progress(_updating, z, item=item, total=numupdates, unit=_files)
1057 updated = len(actions['g'])
1057 updated = len(actions['g'])
1058
1058
1059 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1059 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1060 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1060 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1061
1061
1062 # forget (manifest only, just log it) (must come first)
1062 # forget (manifest only, just log it) (must come first)
1063 for f, args, msg in actions['f']:
1063 for f, args, msg in actions['f']:
1064 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1064 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1065 z += 1
1065 z += 1
1066 progress(_updating, z, item=f, total=numupdates, unit=_files)
1066 progress(_updating, z, item=f, total=numupdates, unit=_files)
1067
1067
1068 # re-add (manifest only, just log it)
1068 # re-add (manifest only, just log it)
1069 for f, args, msg in actions['a']:
1069 for f, args, msg in actions['a']:
1070 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1070 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1071 z += 1
1071 z += 1
1072 progress(_updating, z, item=f, total=numupdates, unit=_files)
1072 progress(_updating, z, item=f, total=numupdates, unit=_files)
1073
1073
1074 # re-add/mark as modified (manifest only, just log it)
1074 # re-add/mark as modified (manifest only, just log it)
1075 for f, args, msg in actions['am']:
1075 for f, args, msg in actions['am']:
1076 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1076 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1077 z += 1
1077 z += 1
1078 progress(_updating, z, item=f, total=numupdates, unit=_files)
1078 progress(_updating, z, item=f, total=numupdates, unit=_files)
1079
1079
1080 # keep (noop, just log it)
1080 # keep (noop, just log it)
1081 for f, args, msg in actions['k']:
1081 for f, args, msg in actions['k']:
1082 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1082 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1083 # no progress
1083 # no progress
1084
1084
1085 # directory rename, move local
1085 # directory rename, move local
1086 for f, args, msg in actions['dm']:
1086 for f, args, msg in actions['dm']:
1087 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1087 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1088 z += 1
1088 z += 1
1089 progress(_updating, z, item=f, total=numupdates, unit=_files)
1089 progress(_updating, z, item=f, total=numupdates, unit=_files)
1090 f0, flags = args
1090 f0, flags = args
1091 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1091 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1092 audit(f)
1092 audit(f)
1093 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1093 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1094 util.unlinkpath(repo.wjoin(f0))
1094 util.unlinkpath(repo.wjoin(f0))
1095 updated += 1
1095 updated += 1
1096
1096
1097 # local directory rename, get
1097 # local directory rename, get
1098 for f, args, msg in actions['dg']:
1098 for f, args, msg in actions['dg']:
1099 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1099 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1100 z += 1
1100 z += 1
1101 progress(_updating, z, item=f, total=numupdates, unit=_files)
1101 progress(_updating, z, item=f, total=numupdates, unit=_files)
1102 f0, flags = args
1102 f0, flags = args
1103 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1103 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1104 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1104 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1105 updated += 1
1105 updated += 1
1106
1106
1107 # exec
1107 # exec
1108 for f, args, msg in actions['e']:
1108 for f, args, msg in actions['e']:
1109 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1109 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1110 z += 1
1110 z += 1
1111 progress(_updating, z, item=f, total=numupdates, unit=_files)
1111 progress(_updating, z, item=f, total=numupdates, unit=_files)
1112 flags, = args
1112 flags, = args
1113 audit(f)
1113 audit(f)
1114 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1114 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1115 updated += 1
1115 updated += 1
1116
1116
1117 # the ordering is important here -- ms.mergedriver will raise if the merge
1117 # the ordering is important here -- ms.mergedriver will raise if the merge
1118 # driver has changed, and we want to be able to bypass it when overwrite is
1118 # driver has changed, and we want to be able to bypass it when overwrite is
1119 # True
1119 # True
1120 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1120 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1121
1121
1122 if usemergedriver:
1122 if usemergedriver:
1123 ms.commit()
1123 ms.commit()
1124 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1124 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1125 # the driver might leave some files unresolved
1125 # the driver might leave some files unresolved
1126 unresolvedf = set(ms.unresolved())
1126 unresolvedf = set(ms.unresolved())
1127 if not proceed:
1127 if not proceed:
1128 # XXX setting unresolved to at least 1 is a hack to make sure we
1128 # XXX setting unresolved to at least 1 is a hack to make sure we
1129 # error out
1129 # error out
1130 return updated, merged, removed, max(len(unresolvedf), 1)
1130 return updated, merged, removed, max(len(unresolvedf), 1)
1131 newactions = []
1131 newactions = []
1132 for f, args, msg in mergeactions:
1132 for f, args, msg in mergeactions:
1133 if f in unresolvedf:
1133 if f in unresolvedf:
1134 newactions.append((f, args, msg))
1134 newactions.append((f, args, msg))
1135 mergeactions = newactions
1135 mergeactions = newactions
1136
1136
1137 # premerge
1137 # premerge
1138 tocomplete = []
1138 tocomplete = []
1139 for f, args, msg in mergeactions:
1139 for f, args, msg in mergeactions:
1140 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1140 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1141 z += 1
1141 z += 1
1142 progress(_updating, z, item=f, total=numupdates, unit=_files)
1142 progress(_updating, z, item=f, total=numupdates, unit=_files)
1143 if f == '.hgsubstate': # subrepo states need updating
1143 if f == '.hgsubstate': # subrepo states need updating
1144 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1144 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1145 overwrite)
1145 overwrite)
1146 continue
1146 continue
1147 audit(f)
1147 audit(f)
1148 complete, r = ms.preresolve(f, wctx, labels=labels)
1148 complete, r = ms.preresolve(f, wctx, labels=labels)
1149 if not complete:
1149 if not complete:
1150 numupdates += 1
1150 numupdates += 1
1151 tocomplete.append((f, args, msg))
1151 tocomplete.append((f, args, msg))
1152
1152
1153 # merge
1153 # merge
1154 for f, args, msg in tocomplete:
1154 for f, args, msg in tocomplete:
1155 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1155 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1156 z += 1
1156 z += 1
1157 progress(_updating, z, item=f, total=numupdates, unit=_files)
1157 progress(_updating, z, item=f, total=numupdates, unit=_files)
1158 ms.resolve(f, wctx, labels=labels)
1158 ms.resolve(f, wctx, labels=labels)
1159
1159
1160 ms.commit()
1160 ms.commit()
1161
1161
1162 unresolved = ms.unresolvedcount()
1162 unresolved = ms.unresolvedcount()
1163
1163
1164 if usemergedriver and not unresolved and ms.mdstate() != 's':
1164 if usemergedriver and not unresolved and ms.mdstate() != 's':
1165 if not driverconclude(repo, ms, wctx, labels=labels):
1165 if not driverconclude(repo, ms, wctx, labels=labels):
1166 # XXX setting unresolved to at least 1 is a hack to make sure we
1166 # XXX setting unresolved to at least 1 is a hack to make sure we
1167 # error out
1167 # error out
1168 unresolved = max(unresolved, 1)
1168 unresolved = max(unresolved, 1)
1169
1169
1170 ms.commit()
1170 ms.commit()
1171
1171
1172 msupdated, msmerged, msremoved = ms.counts()
1172 msupdated, msmerged, msremoved = ms.counts()
1173 updated += msupdated
1173 updated += msupdated
1174 merged += msmerged
1174 merged += msmerged
1175 removed += msremoved
1175 removed += msremoved
1176
1176
1177 extraactions = ms.actions()
1177 extraactions = ms.actions()
1178 for k, acts in extraactions.iteritems():
1178 for k, acts in extraactions.iteritems():
1179 actions[k].extend(acts)
1179 actions[k].extend(acts)
1180
1180
1181 progress(_updating, None, total=numupdates, unit=_files)
1181 progress(_updating, None, total=numupdates, unit=_files)
1182
1182
1183 return updated, merged, removed, unresolved
1183 return updated, merged, removed, unresolved
1184
1184
1185 def recordupdates(repo, actions, branchmerge):
1185 def recordupdates(repo, actions, branchmerge):
1186 "record merge actions to the dirstate"
1186 "record merge actions to the dirstate"
1187 # remove (must come first)
1187 # remove (must come first)
1188 for f, args, msg in actions.get('r', []):
1188 for f, args, msg in actions.get('r', []):
1189 if branchmerge:
1189 if branchmerge:
1190 repo.dirstate.remove(f)
1190 repo.dirstate.remove(f)
1191 else:
1191 else:
1192 repo.dirstate.drop(f)
1192 repo.dirstate.drop(f)
1193
1193
1194 # forget (must come first)
1194 # forget (must come first)
1195 for f, args, msg in actions.get('f', []):
1195 for f, args, msg in actions.get('f', []):
1196 repo.dirstate.drop(f)
1196 repo.dirstate.drop(f)
1197
1197
1198 # re-add
1198 # re-add
1199 for f, args, msg in actions.get('a', []):
1199 for f, args, msg in actions.get('a', []):
1200 repo.dirstate.add(f)
1200 repo.dirstate.add(f)
1201
1201
1202 # re-add/mark as modified
1202 # re-add/mark as modified
1203 for f, args, msg in actions.get('am', []):
1203 for f, args, msg in actions.get('am', []):
1204 if branchmerge:
1204 if branchmerge:
1205 repo.dirstate.normallookup(f)
1205 repo.dirstate.normallookup(f)
1206 else:
1206 else:
1207 repo.dirstate.add(f)
1207 repo.dirstate.add(f)
1208
1208
1209 # exec change
1209 # exec change
1210 for f, args, msg in actions.get('e', []):
1210 for f, args, msg in actions.get('e', []):
1211 repo.dirstate.normallookup(f)
1211 repo.dirstate.normallookup(f)
1212
1212
1213 # keep
1213 # keep
1214 for f, args, msg in actions.get('k', []):
1214 for f, args, msg in actions.get('k', []):
1215 pass
1215 pass
1216
1216
1217 # get
1217 # get
1218 for f, args, msg in actions.get('g', []):
1218 for f, args, msg in actions.get('g', []):
1219 if branchmerge:
1219 if branchmerge:
1220 repo.dirstate.otherparent(f)
1220 repo.dirstate.otherparent(f)
1221 else:
1221 else:
1222 repo.dirstate.normal(f)
1222 repo.dirstate.normal(f)
1223
1223
1224 # merge
1224 # merge
1225 for f, args, msg in actions.get('m', []):
1225 for f, args, msg in actions.get('m', []):
1226 f1, f2, fa, move, anc = args
1226 f1, f2, fa, move, anc = args
1227 if branchmerge:
1227 if branchmerge:
1228 # We've done a branch merge, mark this file as merged
1228 # We've done a branch merge, mark this file as merged
1229 # so that we properly record the merger later
1229 # so that we properly record the merger later
1230 repo.dirstate.merge(f)
1230 repo.dirstate.merge(f)
1231 if f1 != f2: # copy/rename
1231 if f1 != f2: # copy/rename
1232 if move:
1232 if move:
1233 repo.dirstate.remove(f1)
1233 repo.dirstate.remove(f1)
1234 if f1 != f:
1234 if f1 != f:
1235 repo.dirstate.copy(f1, f)
1235 repo.dirstate.copy(f1, f)
1236 else:
1236 else:
1237 repo.dirstate.copy(f2, f)
1237 repo.dirstate.copy(f2, f)
1238 else:
1238 else:
1239 # We've update-merged a locally modified file, so
1239 # We've update-merged a locally modified file, so
1240 # we set the dirstate to emulate a normal checkout
1240 # we set the dirstate to emulate a normal checkout
1241 # of that file some time in the past. Thus our
1241 # of that file some time in the past. Thus our
1242 # merge will appear as a normal local file
1242 # merge will appear as a normal local file
1243 # modification.
1243 # modification.
1244 if f2 == f: # file not locally copied/moved
1244 if f2 == f: # file not locally copied/moved
1245 repo.dirstate.normallookup(f)
1245 repo.dirstate.normallookup(f)
1246 if move:
1246 if move:
1247 repo.dirstate.drop(f1)
1247 repo.dirstate.drop(f1)
1248
1248
1249 # directory rename, move local
1249 # directory rename, move local
1250 for f, args, msg in actions.get('dm', []):
1250 for f, args, msg in actions.get('dm', []):
1251 f0, flag = args
1251 f0, flag = args
1252 if branchmerge:
1252 if branchmerge:
1253 repo.dirstate.add(f)
1253 repo.dirstate.add(f)
1254 repo.dirstate.remove(f0)
1254 repo.dirstate.remove(f0)
1255 repo.dirstate.copy(f0, f)
1255 repo.dirstate.copy(f0, f)
1256 else:
1256 else:
1257 repo.dirstate.normal(f)
1257 repo.dirstate.normal(f)
1258 repo.dirstate.drop(f0)
1258 repo.dirstate.drop(f0)
1259
1259
1260 # directory rename, get
1260 # directory rename, get
1261 for f, args, msg in actions.get('dg', []):
1261 for f, args, msg in actions.get('dg', []):
1262 f0, flag = args
1262 f0, flag = args
1263 if branchmerge:
1263 if branchmerge:
1264 repo.dirstate.add(f)
1264 repo.dirstate.add(f)
1265 repo.dirstate.copy(f0, f)
1265 repo.dirstate.copy(f0, f)
1266 else:
1266 else:
1267 repo.dirstate.normal(f)
1267 repo.dirstate.normal(f)
1268
1268
1269 def update(repo, node, branchmerge, force, partial, ancestor=None,
1269 def update(repo, node, branchmerge, force, partial, ancestor=None,
1270 mergeancestor=False, labels=None):
1270 mergeancestor=False, labels=None):
1271 """
1271 """
1272 Perform a merge between the working directory and the given node
1272 Perform a merge between the working directory and the given node
1273
1273
1274 node = the node to update to, or None if unspecified
1274 node = the node to update to, or None if unspecified
1275 branchmerge = whether to merge between branches
1275 branchmerge = whether to merge between branches
1276 force = whether to force branch merging or file overwriting
1276 force = whether to force branch merging or file overwriting
1277 partial = a function to filter file lists (dirstate not updated)
1277 partial = a function to filter file lists (dirstate not updated)
1278 mergeancestor = whether it is merging with an ancestor. If true,
1278 mergeancestor = whether it is merging with an ancestor. If true,
1279 we should accept the incoming changes for any prompts that occur.
1279 we should accept the incoming changes for any prompts that occur.
1280 If false, merging with an ancestor (fast-forward) is only allowed
1280 If false, merging with an ancestor (fast-forward) is only allowed
1281 between different named branches. This flag is used by rebase extension
1281 between different named branches. This flag is used by rebase extension
1282 as a temporary fix and should be avoided in general.
1282 as a temporary fix and should be avoided in general.
1283
1283
1284 The table below shows all the behaviors of the update command
1284 The table below shows all the behaviors of the update command
1285 given the -c and -C or no options, whether the working directory
1285 given the -c and -C or no options, whether the working directory
1286 is dirty, whether a revision is specified, and the relationship of
1286 is dirty, whether a revision is specified, and the relationship of
1287 the parent rev to the target rev (linear, on the same named
1287 the parent rev to the target rev (linear, on the same named
1288 branch, or on another named branch).
1288 branch, or on another named branch).
1289
1289
1290 This logic is tested by test-update-branches.t.
1290 This logic is tested by test-update-branches.t.
1291
1291
1292 -c -C dirty rev | linear same cross
1292 -c -C dirty rev | linear same cross
1293 n n n n | ok (1) x
1293 n n n n | ok (1) x
1294 n n n y | ok ok ok
1294 n n n y | ok ok ok
1295 n n y n | merge (2) (2)
1295 n n y n | merge (2) (2)
1296 n n y y | merge (3) (3)
1296 n n y y | merge (3) (3)
1297 n y * * | discard discard discard
1297 n y * * | discard discard discard
1298 y n y * | (4) (4) (4)
1298 y n y * | (4) (4) (4)
1299 y n n * | ok ok ok
1299 y n n * | ok ok ok
1300 y y * * | (5) (5) (5)
1300 y y * * | (5) (5) (5)
1301
1301
1302 x = can't happen
1302 x = can't happen
1303 * = don't-care
1303 * = don't-care
1304 1 = abort: not a linear update (merge or update --check to force update)
1304 1 = abort: not a linear update (merge or update --check to force update)
1305 2 = abort: uncommitted changes (commit and merge, or update --clean to
1305 2 = abort: uncommitted changes (commit and merge, or update --clean to
1306 discard changes)
1306 discard changes)
1307 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1307 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1308 4 = abort: uncommitted changes (checked in commands.py)
1308 4 = abort: uncommitted changes (checked in commands.py)
1309 5 = incompatible options (checked in commands.py)
1309 5 = incompatible options (checked in commands.py)
1310
1310
1311 Return the same tuple as applyupdates().
1311 Return the same tuple as applyupdates().
1312 """
1312 """
1313
1313
1314 onode = node
1314 onode = node
1315 wlock = repo.wlock()
1315 wlock = repo.wlock()
1316 try:
1316 try:
1317 wc = repo[None]
1317 wc = repo[None]
1318 pl = wc.parents()
1318 pl = wc.parents()
1319 p1 = pl[0]
1319 p1 = pl[0]
1320 pas = [None]
1320 pas = [None]
1321 if ancestor is not None:
1321 if ancestor is not None:
1322 pas = [repo[ancestor]]
1322 pas = [repo[ancestor]]
1323
1323
1324 if node is None:
1324 if node is None:
1325 if (repo.ui.configbool('devel', 'all-warnings')
1325 if (repo.ui.configbool('devel', 'all-warnings')
1326 or repo.ui.configbool('devel', 'oldapi')):
1326 or repo.ui.configbool('devel', 'oldapi')):
1327 repo.ui.develwarn('update with no target')
1327 repo.ui.develwarn('update with no target')
1328 rev, _mark, _act = destutil.destupdate(repo)
1328 rev, _mark, _act = destutil.destupdate(repo)
1329 node = repo[rev].node()
1329 node = repo[rev].node()
1330
1330
1331 overwrite = force and not branchmerge
1331 overwrite = force and not branchmerge
1332
1332
1333 p2 = repo[node]
1333 p2 = repo[node]
1334 if pas[0] is None:
1334 if pas[0] is None:
1335 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1335 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1336 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1336 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1337 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1337 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1338 else:
1338 else:
1339 pas = [p1.ancestor(p2, warn=branchmerge)]
1339 pas = [p1.ancestor(p2, warn=branchmerge)]
1340
1340
1341 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1341 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1342
1342
1343 ### check phase
1343 ### check phase
1344 if not overwrite and len(pl) > 1:
1344 if not overwrite:
1345 raise error.Abort(_("outstanding uncommitted merge"))
1345 if len(pl) > 1:
1346 raise error.Abort(_("outstanding uncommitted merge"))
1347 ms = mergestate.read(repo)
1348 if list(ms.unresolved()):
1349 raise error.Abort(_("outstanding merge conflicts"))
1346 if branchmerge:
1350 if branchmerge:
1347 if pas == [p2]:
1351 if pas == [p2]:
1348 raise error.Abort(_("merging with a working directory ancestor"
1352 raise error.Abort(_("merging with a working directory ancestor"
1349 " has no effect"))
1353 " has no effect"))
1350 elif pas == [p1]:
1354 elif pas == [p1]:
1351 if not mergeancestor and p1.branch() == p2.branch():
1355 if not mergeancestor and p1.branch() == p2.branch():
1352 raise error.Abort(_("nothing to merge"),
1356 raise error.Abort(_("nothing to merge"),
1353 hint=_("use 'hg update' "
1357 hint=_("use 'hg update' "
1354 "or check 'hg heads'"))
1358 "or check 'hg heads'"))
1355 if not force and (wc.files() or wc.deleted()):
1359 if not force and (wc.files() or wc.deleted()):
1356 raise error.Abort(_("uncommitted changes"),
1360 raise error.Abort(_("uncommitted changes"),
1357 hint=_("use 'hg status' to list changes"))
1361 hint=_("use 'hg status' to list changes"))
1358 for s in sorted(wc.substate):
1362 for s in sorted(wc.substate):
1359 wc.sub(s).bailifchanged()
1363 wc.sub(s).bailifchanged()
1360
1364
1361 elif not overwrite:
1365 elif not overwrite:
1362 if p1 == p2: # no-op update
1366 if p1 == p2: # no-op update
1363 # call the hooks and exit early
1367 # call the hooks and exit early
1364 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1368 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1365 repo.hook('update', parent1=xp2, parent2='', error=0)
1369 repo.hook('update', parent1=xp2, parent2='', error=0)
1366 return 0, 0, 0, 0
1370 return 0, 0, 0, 0
1367
1371
1368 if pas not in ([p1], [p2]): # nonlinear
1372 if pas not in ([p1], [p2]): # nonlinear
1369 dirty = wc.dirty(missing=True)
1373 dirty = wc.dirty(missing=True)
1370 if dirty or onode is None:
1374 if dirty or onode is None:
1371 # Branching is a bit strange to ensure we do the minimal
1375 # Branching is a bit strange to ensure we do the minimal
1372 # amount of call to obsolete.background.
1376 # amount of call to obsolete.background.
1373 foreground = obsolete.foreground(repo, [p1.node()])
1377 foreground = obsolete.foreground(repo, [p1.node()])
1374 # note: the <node> variable contains a random identifier
1378 # note: the <node> variable contains a random identifier
1375 if repo[node].node() in foreground:
1379 if repo[node].node() in foreground:
1376 pas = [p1] # allow updating to successors
1380 pas = [p1] # allow updating to successors
1377 elif dirty:
1381 elif dirty:
1378 msg = _("uncommitted changes")
1382 msg = _("uncommitted changes")
1379 if onode is None:
1383 if onode is None:
1380 hint = _("commit and merge, or update --clean to"
1384 hint = _("commit and merge, or update --clean to"
1381 " discard changes")
1385 " discard changes")
1382 else:
1386 else:
1383 hint = _("commit or update --clean to discard"
1387 hint = _("commit or update --clean to discard"
1384 " changes")
1388 " changes")
1385 raise error.Abort(msg, hint=hint)
1389 raise error.Abort(msg, hint=hint)
1386 else: # node is none
1390 else: # node is none
1387 msg = _("not a linear update")
1391 msg = _("not a linear update")
1388 hint = _("merge or update --check to force update")
1392 hint = _("merge or update --check to force update")
1389 raise error.Abort(msg, hint=hint)
1393 raise error.Abort(msg, hint=hint)
1390 else:
1394 else:
1391 # Allow jumping branches if clean and specific rev given
1395 # Allow jumping branches if clean and specific rev given
1392 pas = [p1]
1396 pas = [p1]
1393
1397
1394 # deprecated config: merge.followcopies
1398 # deprecated config: merge.followcopies
1395 followcopies = False
1399 followcopies = False
1396 if overwrite:
1400 if overwrite:
1397 pas = [wc]
1401 pas = [wc]
1398 elif pas == [p2]: # backwards
1402 elif pas == [p2]: # backwards
1399 pas = [wc.p1()]
1403 pas = [wc.p1()]
1400 elif not branchmerge and not wc.dirty(missing=True):
1404 elif not branchmerge and not wc.dirty(missing=True):
1401 pass
1405 pass
1402 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1406 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1403 followcopies = True
1407 followcopies = True
1404
1408
1405 ### calculate phase
1409 ### calculate phase
1406 actionbyfile, diverge, renamedelete = calculateupdates(
1410 actionbyfile, diverge, renamedelete = calculateupdates(
1407 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1411 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1408 followcopies)
1412 followcopies)
1409 # Convert to dictionary-of-lists format
1413 # Convert to dictionary-of-lists format
1410 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1414 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1411 for f, (m, args, msg) in actionbyfile.iteritems():
1415 for f, (m, args, msg) in actionbyfile.iteritems():
1412 if m not in actions:
1416 if m not in actions:
1413 actions[m] = []
1417 actions[m] = []
1414 actions[m].append((f, args, msg))
1418 actions[m].append((f, args, msg))
1415
1419
1416 if not util.checkcase(repo.path):
1420 if not util.checkcase(repo.path):
1417 # check collision between files only in p2 for clean update
1421 # check collision between files only in p2 for clean update
1418 if (not branchmerge and
1422 if (not branchmerge and
1419 (force or not wc.dirty(missing=True, branch=False))):
1423 (force or not wc.dirty(missing=True, branch=False))):
1420 _checkcollision(repo, p2.manifest(), None)
1424 _checkcollision(repo, p2.manifest(), None)
1421 else:
1425 else:
1422 _checkcollision(repo, wc.manifest(), actions)
1426 _checkcollision(repo, wc.manifest(), actions)
1423
1427
1424 # Prompt and create actions. Most of this is in the resolve phase
1428 # Prompt and create actions. Most of this is in the resolve phase
1425 # already, but we can't handle .hgsubstate in filemerge or
1429 # already, but we can't handle .hgsubstate in filemerge or
1426 # subrepo.submerge yet so we have to keep prompting for it.
1430 # subrepo.submerge yet so we have to keep prompting for it.
1427 for f, args, msg in sorted(actions['cd']):
1431 for f, args, msg in sorted(actions['cd']):
1428 if f != '.hgsubstate':
1432 if f != '.hgsubstate':
1429 continue
1433 continue
1430 if repo.ui.promptchoice(
1434 if repo.ui.promptchoice(
1431 _("local changed %s which remote deleted\n"
1435 _("local changed %s which remote deleted\n"
1432 "use (c)hanged version or (d)elete?"
1436 "use (c)hanged version or (d)elete?"
1433 "$$ &Changed $$ &Delete") % f, 0):
1437 "$$ &Changed $$ &Delete") % f, 0):
1434 actions['r'].append((f, None, "prompt delete"))
1438 actions['r'].append((f, None, "prompt delete"))
1435 elif f in p1:
1439 elif f in p1:
1436 actions['am'].append((f, None, "prompt keep"))
1440 actions['am'].append((f, None, "prompt keep"))
1437 else:
1441 else:
1438 actions['a'].append((f, None, "prompt keep"))
1442 actions['a'].append((f, None, "prompt keep"))
1439
1443
1440 for f, args, msg in sorted(actions['dc']):
1444 for f, args, msg in sorted(actions['dc']):
1441 if f != '.hgsubstate':
1445 if f != '.hgsubstate':
1442 continue
1446 continue
1443 f1, f2, fa, move, anc = args
1447 f1, f2, fa, move, anc = args
1444 flags = p2[f2].flags()
1448 flags = p2[f2].flags()
1445 if repo.ui.promptchoice(
1449 if repo.ui.promptchoice(
1446 _("remote changed %s which local deleted\n"
1450 _("remote changed %s which local deleted\n"
1447 "use (c)hanged version or leave (d)eleted?"
1451 "use (c)hanged version or leave (d)eleted?"
1448 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1452 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1449 actions['g'].append((f, (flags,), "prompt recreating"))
1453 actions['g'].append((f, (flags,), "prompt recreating"))
1450
1454
1451 # divergent renames
1455 # divergent renames
1452 for f, fl in sorted(diverge.iteritems()):
1456 for f, fl in sorted(diverge.iteritems()):
1453 repo.ui.warn(_("note: possible conflict - %s was renamed "
1457 repo.ui.warn(_("note: possible conflict - %s was renamed "
1454 "multiple times to:\n") % f)
1458 "multiple times to:\n") % f)
1455 for nf in fl:
1459 for nf in fl:
1456 repo.ui.warn(" %s\n" % nf)
1460 repo.ui.warn(" %s\n" % nf)
1457
1461
1458 # rename and delete
1462 # rename and delete
1459 for f, fl in sorted(renamedelete.iteritems()):
1463 for f, fl in sorted(renamedelete.iteritems()):
1460 repo.ui.warn(_("note: possible conflict - %s was deleted "
1464 repo.ui.warn(_("note: possible conflict - %s was deleted "
1461 "and renamed to:\n") % f)
1465 "and renamed to:\n") % f)
1462 for nf in fl:
1466 for nf in fl:
1463 repo.ui.warn(" %s\n" % nf)
1467 repo.ui.warn(" %s\n" % nf)
1464
1468
1465 ### apply phase
1469 ### apply phase
1466 if not branchmerge: # just jump to the new rev
1470 if not branchmerge: # just jump to the new rev
1467 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1471 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1468 if not partial:
1472 if not partial:
1469 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1473 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1470 # note that we're in the middle of an update
1474 # note that we're in the middle of an update
1471 repo.vfs.write('updatestate', p2.hex())
1475 repo.vfs.write('updatestate', p2.hex())
1472
1476
1473 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1477 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1474
1478
1475 if not partial:
1479 if not partial:
1476 repo.dirstate.beginparentchange()
1480 repo.dirstate.beginparentchange()
1477 repo.setparents(fp1, fp2)
1481 repo.setparents(fp1, fp2)
1478 recordupdates(repo, actions, branchmerge)
1482 recordupdates(repo, actions, branchmerge)
1479 # update completed, clear state
1483 # update completed, clear state
1480 util.unlink(repo.join('updatestate'))
1484 util.unlink(repo.join('updatestate'))
1481
1485
1482 if not branchmerge:
1486 if not branchmerge:
1483 repo.dirstate.setbranch(p2.branch())
1487 repo.dirstate.setbranch(p2.branch())
1484 repo.dirstate.endparentchange()
1488 repo.dirstate.endparentchange()
1485 finally:
1489 finally:
1486 wlock.release()
1490 wlock.release()
1487
1491
1488 if not partial:
1492 if not partial:
1489 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1493 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1490 return stats
1494 return stats
1491
1495
1492 def graft(repo, ctx, pctx, labels, keepparent=False):
1496 def graft(repo, ctx, pctx, labels, keepparent=False):
1493 """Do a graft-like merge.
1497 """Do a graft-like merge.
1494
1498
1495 This is a merge where the merge ancestor is chosen such that one
1499 This is a merge where the merge ancestor is chosen such that one
1496 or more changesets are grafted onto the current changeset. In
1500 or more changesets are grafted onto the current changeset. In
1497 addition to the merge, this fixes up the dirstate to include only
1501 addition to the merge, this fixes up the dirstate to include only
1498 a single parent (if keepparent is False) and tries to duplicate any
1502 a single parent (if keepparent is False) and tries to duplicate any
1499 renames/copies appropriately.
1503 renames/copies appropriately.
1500
1504
1501 ctx - changeset to rebase
1505 ctx - changeset to rebase
1502 pctx - merge base, usually ctx.p1()
1506 pctx - merge base, usually ctx.p1()
1503 labels - merge labels eg ['local', 'graft']
1507 labels - merge labels eg ['local', 'graft']
1504 keepparent - keep second parent if any
1508 keepparent - keep second parent if any
1505
1509
1506 """
1510 """
1507 # If we're grafting a descendant onto an ancestor, be sure to pass
1511 # If we're grafting a descendant onto an ancestor, be sure to pass
1508 # mergeancestor=True to update. This does two things: 1) allows the merge if
1512 # mergeancestor=True to update. This does two things: 1) allows the merge if
1509 # the destination is the same as the parent of the ctx (so we can use graft
1513 # the destination is the same as the parent of the ctx (so we can use graft
1510 # to copy commits), and 2) informs update that the incoming changes are
1514 # to copy commits), and 2) informs update that the incoming changes are
1511 # newer than the destination so it doesn't prompt about "remote changed foo
1515 # newer than the destination so it doesn't prompt about "remote changed foo
1512 # which local deleted".
1516 # which local deleted".
1513 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1517 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1514
1518
1515 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1519 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1516 mergeancestor=mergeancestor, labels=labels)
1520 mergeancestor=mergeancestor, labels=labels)
1517
1521
1518 pother = nullid
1522 pother = nullid
1519 parents = ctx.parents()
1523 parents = ctx.parents()
1520 if keepparent and len(parents) == 2 and pctx in parents:
1524 if keepparent and len(parents) == 2 and pctx in parents:
1521 parents.remove(pctx)
1525 parents.remove(pctx)
1522 pother = parents[0].node()
1526 pother = parents[0].node()
1523
1527
1524 repo.dirstate.beginparentchange()
1528 repo.dirstate.beginparentchange()
1525 repo.setparents(repo['.'].node(), pother)
1529 repo.setparents(repo['.'].node(), pother)
1526 repo.dirstate.write(repo.currenttransaction())
1530 repo.dirstate.write(repo.currenttransaction())
1527 # fix up dirstate for copies and renames
1531 # fix up dirstate for copies and renames
1528 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1532 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1529 repo.dirstate.endparentchange()
1533 repo.dirstate.endparentchange()
1530 return stats
1534 return stats
@@ -1,139 +1,151 b''
1 $ hg init
1 $ hg init
2
2
3 Revision 0:
3 Revision 0:
4
4
5 $ echo "unchanged" > unchanged
5 $ echo "unchanged" > unchanged
6 $ echo "remove me" > remove
6 $ echo "remove me" > remove
7 $ echo "copy me" > copy
7 $ echo "copy me" > copy
8 $ echo "move me" > move
8 $ echo "move me" > move
9 $ for i in 1 2 3 4 5 6 7 8 9; do
9 $ for i in 1 2 3 4 5 6 7 8 9; do
10 > echo "merge ok $i" >> zzz1_merge_ok
10 > echo "merge ok $i" >> zzz1_merge_ok
11 > done
11 > done
12 $ echo "merge bad" > zzz2_merge_bad
12 $ echo "merge bad" > zzz2_merge_bad
13 $ hg ci -Am "revision 0"
13 $ hg ci -Am "revision 0"
14 adding copy
14 adding copy
15 adding move
15 adding move
16 adding remove
16 adding remove
17 adding unchanged
17 adding unchanged
18 adding zzz1_merge_ok
18 adding zzz1_merge_ok
19 adding zzz2_merge_bad
19 adding zzz2_merge_bad
20
20
21 Revision 1:
21 Revision 1:
22
22
23 $ hg rm remove
23 $ hg rm remove
24 $ hg mv move moved
24 $ hg mv move moved
25 $ hg cp copy copied
25 $ hg cp copy copied
26 $ echo "added" > added
26 $ echo "added" > added
27 $ hg add added
27 $ hg add added
28 $ echo "new first line" > zzz1_merge_ok
28 $ echo "new first line" > zzz1_merge_ok
29 $ hg cat zzz1_merge_ok >> zzz1_merge_ok
29 $ hg cat zzz1_merge_ok >> zzz1_merge_ok
30 $ echo "new last line" >> zzz2_merge_bad
30 $ echo "new last line" >> zzz2_merge_bad
31 $ hg ci -m "revision 1"
31 $ hg ci -m "revision 1"
32
32
33 Local changes to revision 0:
33 Local changes to revision 0:
34
34
35 $ hg co 0
35 $ hg co 0
36 4 files updated, 0 files merged, 3 files removed, 0 files unresolved
36 4 files updated, 0 files merged, 3 files removed, 0 files unresolved
37 $ echo "new last line" >> zzz1_merge_ok
37 $ echo "new last line" >> zzz1_merge_ok
38 $ echo "another last line" >> zzz2_merge_bad
38 $ echo "another last line" >> zzz2_merge_bad
39
39
40 $ hg diff --nodates | grep "^[+-][^<>]"
40 $ hg diff --nodates | grep "^[+-][^<>]"
41 --- a/zzz1_merge_ok
41 --- a/zzz1_merge_ok
42 +++ b/zzz1_merge_ok
42 +++ b/zzz1_merge_ok
43 +new last line
43 +new last line
44 --- a/zzz2_merge_bad
44 --- a/zzz2_merge_bad
45 +++ b/zzz2_merge_bad
45 +++ b/zzz2_merge_bad
46 +another last line
46 +another last line
47
47
48 $ hg st
48 $ hg st
49 M zzz1_merge_ok
49 M zzz1_merge_ok
50 M zzz2_merge_bad
50 M zzz2_merge_bad
51
51
52 Local merge with bad merge tool:
52 Local merge with bad merge tool:
53
53
54 $ HGMERGE=false hg co
54 $ HGMERGE=false hg co
55 merging zzz1_merge_ok
55 merging zzz1_merge_ok
56 merging zzz2_merge_bad
56 merging zzz2_merge_bad
57 merging zzz2_merge_bad failed!
57 merging zzz2_merge_bad failed!
58 3 files updated, 1 files merged, 2 files removed, 1 files unresolved
58 3 files updated, 1 files merged, 2 files removed, 1 files unresolved
59 use 'hg resolve' to retry unresolved file merges
59 use 'hg resolve' to retry unresolved file merges
60 [1]
60 [1]
61
61
62 $ hg resolve -m
63 (no more unresolved files)
64
62 $ hg co 0
65 $ hg co 0
63 merging zzz1_merge_ok
66 merging zzz1_merge_ok
64 merging zzz2_merge_bad
67 merging zzz2_merge_bad
65 warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
68 warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
66 2 files updated, 1 files merged, 3 files removed, 1 files unresolved
69 2 files updated, 1 files merged, 3 files removed, 1 files unresolved
67 use 'hg resolve' to retry unresolved file merges
70 use 'hg resolve' to retry unresolved file merges
68 [1]
71 [1]
69
72
70 $ hg diff --nodates | grep "^[+-][^<>]"
73 $ hg diff --nodates | grep "^[+-][^<>]"
71 --- a/zzz1_merge_ok
74 --- a/zzz1_merge_ok
72 +++ b/zzz1_merge_ok
75 +++ b/zzz1_merge_ok
73 +new last line
76 +new last line
74 --- a/zzz2_merge_bad
77 --- a/zzz2_merge_bad
75 +++ b/zzz2_merge_bad
78 +++ b/zzz2_merge_bad
76 +another last line
79 +another last line
77 +=======
80 +=======
78
81
79 $ hg st
82 $ hg st
80 M zzz1_merge_ok
83 M zzz1_merge_ok
81 M zzz2_merge_bad
84 M zzz2_merge_bad
82 ? zzz2_merge_bad.orig
85 ? zzz2_merge_bad.orig
83
86
84 Local merge with conflicts:
87 Local merge with conflicts:
85
88
89 $ hg resolve -m
90 (no more unresolved files)
91
86 $ hg co
92 $ hg co
87 merging zzz1_merge_ok
93 merging zzz1_merge_ok
88 merging zzz2_merge_bad
94 merging zzz2_merge_bad
89 warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
95 warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
90 3 files updated, 1 files merged, 2 files removed, 1 files unresolved
96 3 files updated, 1 files merged, 2 files removed, 1 files unresolved
91 use 'hg resolve' to retry unresolved file merges
97 use 'hg resolve' to retry unresolved file merges
92 [1]
98 [1]
93
99
100 $ hg resolve -m
101 (no more unresolved files)
102
94 $ hg co 0 --config 'ui.origbackuppath=.hg/origbackups'
103 $ hg co 0 --config 'ui.origbackuppath=.hg/origbackups'
95 merging zzz1_merge_ok
104 merging zzz1_merge_ok
96 merging zzz2_merge_bad
105 merging zzz2_merge_bad
97 warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
106 warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark')
98 2 files updated, 1 files merged, 3 files removed, 1 files unresolved
107 2 files updated, 1 files merged, 3 files removed, 1 files unresolved
99 use 'hg resolve' to retry unresolved file merges
108 use 'hg resolve' to retry unresolved file merges
100 [1]
109 [1]
101
110
102 Are orig files from the last commit where we want them?
111 Are orig files from the last commit where we want them?
103 $ ls .hg/origbackups
112 $ ls .hg/origbackups
104 zzz2_merge_bad.orig
113 zzz2_merge_bad.orig
105
114
106 $ hg diff --nodates | grep "^[+-][^<>]"
115 $ hg diff --nodates | grep "^[+-][^<>]"
107 --- a/zzz1_merge_ok
116 --- a/zzz1_merge_ok
108 +++ b/zzz1_merge_ok
117 +++ b/zzz1_merge_ok
109 +new last line
118 +new last line
110 --- a/zzz2_merge_bad
119 --- a/zzz2_merge_bad
111 +++ b/zzz2_merge_bad
120 +++ b/zzz2_merge_bad
112 +another last line
121 +another last line
113 +=======
122 +=======
114 +=======
123 +=======
115 +new last line
124 +new last line
116 +=======
125 +=======
117
126
118 $ hg st
127 $ hg st
119 M zzz1_merge_ok
128 M zzz1_merge_ok
120 M zzz2_merge_bad
129 M zzz2_merge_bad
121 ? zzz2_merge_bad.orig
130 ? zzz2_merge_bad.orig
122
131
123 Local merge without conflicts:
132 Local merge without conflicts:
124
133
125 $ hg revert zzz2_merge_bad
134 $ hg revert zzz2_merge_bad
126
135
136 $ hg resolve -m
137 (no more unresolved files)
138
127 $ hg co
139 $ hg co
128 merging zzz1_merge_ok
140 merging zzz1_merge_ok
129 4 files updated, 1 files merged, 2 files removed, 0 files unresolved
141 4 files updated, 1 files merged, 2 files removed, 0 files unresolved
130
142
131 $ hg diff --nodates | grep "^[+-][^<>]"
143 $ hg diff --nodates | grep "^[+-][^<>]"
132 --- a/zzz1_merge_ok
144 --- a/zzz1_merge_ok
133 +++ b/zzz1_merge_ok
145 +++ b/zzz1_merge_ok
134 +new last line
146 +new last line
135
147
136 $ hg st
148 $ hg st
137 M zzz1_merge_ok
149 M zzz1_merge_ok
138 ? zzz2_merge_bad.orig
150 ? zzz2_merge_bad.orig
139
151
@@ -1,308 +1,328 b''
1 test that a commit clears the merge state.
1 test that a commit clears the merge state.
2
2
3 $ hg init repo
3 $ hg init repo
4 $ cd repo
4 $ cd repo
5
5
6 $ echo foo > file1
6 $ echo foo > file1
7 $ echo foo > file2
7 $ echo foo > file2
8 $ hg commit -Am 'add files'
8 $ hg commit -Am 'add files'
9 adding file1
9 adding file1
10 adding file2
10 adding file2
11
11
12 $ echo bar >> file1
12 $ echo bar >> file1
13 $ echo bar >> file2
13 $ echo bar >> file2
14 $ hg commit -Am 'append bar to files'
14 $ hg commit -Am 'append bar to files'
15
15
16 create a second head with conflicting edits
16 create a second head with conflicting edits
17
17
18 $ hg up -C 0
18 $ hg up -C 0
19 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 $ echo baz >> file1
20 $ echo baz >> file1
21 $ echo baz >> file2
21 $ echo baz >> file2
22 $ hg commit -Am 'append baz to files'
22 $ hg commit -Am 'append baz to files'
23 created new head
23 created new head
24
24
25 create a third head with no conflicting edits
25 create a third head with no conflicting edits
26 $ hg up -qC 0
26 $ hg up -qC 0
27 $ echo foo > file3
27 $ echo foo > file3
28 $ hg commit -Am 'add non-conflicting file'
28 $ hg commit -Am 'add non-conflicting file'
29 adding file3
29 adding file3
30 created new head
30 created new head
31
31
32 failing merge
32 failing merge
33
33
34 $ hg up -qC 2
34 $ hg up -qC 2
35 $ hg merge --tool=internal:fail 1
35 $ hg merge --tool=internal:fail 1
36 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
36 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
37 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
37 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
38 [1]
38 [1]
39
39
40 resolve -l should contain unresolved entries
40 resolve -l should contain unresolved entries
41
41
42 $ hg resolve -l
42 $ hg resolve -l
43 U file1
43 U file1
44 U file2
44 U file2
45
45
46 $ hg resolve -l --no-status
46 $ hg resolve -l --no-status
47 file1
47 file1
48 file2
48 file2
49
49
50 resolving an unknown path should emit a warning, but not for -l
50 resolving an unknown path should emit a warning, but not for -l
51
51
52 $ hg resolve -m does-not-exist
52 $ hg resolve -m does-not-exist
53 arguments do not match paths that need resolving
53 arguments do not match paths that need resolving
54 $ hg resolve -l does-not-exist
54 $ hg resolve -l does-not-exist
55
55
56 don't allow marking or unmarking driver-resolved files
56 don't allow marking or unmarking driver-resolved files
57
57
58 $ cat > $TESTTMP/markdriver.py << EOF
58 $ cat > $TESTTMP/markdriver.py << EOF
59 > '''mark and unmark files as driver-resolved'''
59 > '''mark and unmark files as driver-resolved'''
60 > from mercurial import cmdutil, merge, scmutil
60 > from mercurial import cmdutil, merge, scmutil
61 > cmdtable = {}
61 > cmdtable = {}
62 > command = cmdutil.command(cmdtable)
62 > command = cmdutil.command(cmdtable)
63 > @command('markdriver',
63 > @command('markdriver',
64 > [('u', 'unmark', None, '')],
64 > [('u', 'unmark', None, '')],
65 > 'FILE...')
65 > 'FILE...')
66 > def markdriver(ui, repo, *pats, **opts):
66 > def markdriver(ui, repo, *pats, **opts):
67 > wlock = repo.wlock()
67 > wlock = repo.wlock()
68 > try:
68 > try:
69 > ms = merge.mergestate.read(repo)
69 > ms = merge.mergestate.read(repo)
70 > m = scmutil.match(repo[None], pats, opts)
70 > m = scmutil.match(repo[None], pats, opts)
71 > for f in ms:
71 > for f in ms:
72 > if not m(f):
72 > if not m(f):
73 > continue
73 > continue
74 > if not opts['unmark']:
74 > if not opts['unmark']:
75 > ms.mark(f, 'd')
75 > ms.mark(f, 'd')
76 > else:
76 > else:
77 > ms.mark(f, 'u')
77 > ms.mark(f, 'u')
78 > ms.commit()
78 > ms.commit()
79 > finally:
79 > finally:
80 > wlock.release()
80 > wlock.release()
81 > EOF
81 > EOF
82 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver file1
82 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver file1
83 $ hg resolve --list
83 $ hg resolve --list
84 D file1
84 D file1
85 U file2
85 U file2
86 $ hg resolve --mark file1
86 $ hg resolve --mark file1
87 not marking file1 as it is driver-resolved
87 not marking file1 as it is driver-resolved
88 this should not print out file1
88 this should not print out file1
89 $ hg resolve --mark --all
89 $ hg resolve --mark --all
90 (no more unresolved files -- run "hg resolve --all" to conclude)
90 (no more unresolved files -- run "hg resolve --all" to conclude)
91 $ hg resolve --mark 'glob:file*'
91 $ hg resolve --mark 'glob:file*'
92 (no more unresolved files -- run "hg resolve --all" to conclude)
92 (no more unresolved files -- run "hg resolve --all" to conclude)
93 $ hg resolve --list
93 $ hg resolve --list
94 D file1
94 D file1
95 R file2
95 R file2
96 $ hg resolve --unmark file1
96 $ hg resolve --unmark file1
97 not unmarking file1 as it is driver-resolved
97 not unmarking file1 as it is driver-resolved
98 (no more unresolved files -- run "hg resolve --all" to conclude)
98 (no more unresolved files -- run "hg resolve --all" to conclude)
99 $ hg resolve --unmark --all
99 $ hg resolve --unmark --all
100 $ hg resolve --list
100 $ hg resolve --list
101 D file1
101 D file1
102 U file2
102 U file2
103 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver --unmark file1
103 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver --unmark file1
104 $ hg resolve --list
104 $ hg resolve --list
105 U file1
105 U file1
106 U file2
106 U file2
107
107
108 resolve the failure
108 resolve the failure
109
109
110 $ echo resolved > file1
110 $ echo resolved > file1
111 $ hg resolve -m file1
111 $ hg resolve -m file1
112
112
113 resolve -l should show resolved file as resolved
113 resolve -l should show resolved file as resolved
114
114
115 $ hg resolve -l
115 $ hg resolve -l
116 R file1
116 R file1
117 U file2
117 U file2
118
118
119 $ hg resolve -l -Tjson
119 $ hg resolve -l -Tjson
120 [
120 [
121 {
121 {
122 "path": "file1",
122 "path": "file1",
123 "status": "R"
123 "status": "R"
124 },
124 },
125 {
125 {
126 "path": "file2",
126 "path": "file2",
127 "status": "U"
127 "status": "U"
128 }
128 }
129 ]
129 ]
130
130
131 resolve -m without paths should mark all resolved
131 resolve -m without paths should mark all resolved
132
132
133 $ hg resolve -m
133 $ hg resolve -m
134 (no more unresolved files)
134 (no more unresolved files)
135 $ hg commit -m 'resolved'
135 $ hg commit -m 'resolved'
136
136
137 resolve -l should be empty after commit
137 resolve -l should be empty after commit
138
138
139 $ hg resolve -l
139 $ hg resolve -l
140
140
141 $ hg resolve -l -Tjson
141 $ hg resolve -l -Tjson
142 [
142 [
143 ]
143 ]
144
144
145 resolve --all should abort when no merge in progress
145 resolve --all should abort when no merge in progress
146
146
147 $ hg resolve --all
147 $ hg resolve --all
148 abort: resolve command not applicable when not merging
148 abort: resolve command not applicable when not merging
149 [255]
149 [255]
150
150
151 resolve -m should abort when no merge in progress
151 resolve -m should abort when no merge in progress
152
152
153 $ hg resolve -m
153 $ hg resolve -m
154 abort: resolve command not applicable when not merging
154 abort: resolve command not applicable when not merging
155 [255]
155 [255]
156
156
157 can not update or merge when there are unresolved conflicts
158
159 $ hg up -qC 0
160 $ echo quux >> file1
161 $ hg up 1
162 merging file1
163 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
164 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
165 use 'hg resolve' to retry unresolved file merges
166 [1]
167 $ hg up 0
168 abort: outstanding merge conflicts
169 [255]
170 $ hg merge 2
171 abort: outstanding merge conflicts
172 [255]
173 $ hg merge --force 2
174 abort: outstanding merge conflicts
175 [255]
176
157 set up conflict-free merge
177 set up conflict-free merge
158
178
159 $ hg up -qC 3
179 $ hg up -qC 3
160 $ hg merge 1
180 $ hg merge 1
161 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 (branch merge, don't forget to commit)
182 (branch merge, don't forget to commit)
163
183
164 resolve --all should do nothing in merge without conflicts
184 resolve --all should do nothing in merge without conflicts
165 $ hg resolve --all
185 $ hg resolve --all
166 (no more unresolved files)
186 (no more unresolved files)
167
187
168 resolve -m should do nothing in merge without conflicts
188 resolve -m should do nothing in merge without conflicts
169
189
170 $ hg resolve -m
190 $ hg resolve -m
171 (no more unresolved files)
191 (no more unresolved files)
172
192
173 get back to conflicting state
193 get back to conflicting state
174
194
175 $ hg up -qC 2
195 $ hg up -qC 2
176 $ hg merge --tool=internal:fail 1
196 $ hg merge --tool=internal:fail 1
177 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
197 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
178 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
198 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
179 [1]
199 [1]
180
200
181 resolve without arguments should suggest --all
201 resolve without arguments should suggest --all
182 $ hg resolve
202 $ hg resolve
183 abort: no files or directories specified
203 abort: no files or directories specified
184 (use --all to re-merge all unresolved files)
204 (use --all to re-merge all unresolved files)
185 [255]
205 [255]
186
206
187 resolve --all should re-merge all unresolved files
207 resolve --all should re-merge all unresolved files
188 $ hg resolve --all
208 $ hg resolve --all
189 merging file1
209 merging file1
190 merging file2
210 merging file2
191 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
211 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
192 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
212 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
193 [1]
213 [1]
194 $ cat file1.orig
214 $ cat file1.orig
195 foo
215 foo
196 baz
216 baz
197 $ cat file2.orig
217 $ cat file2.orig
198 foo
218 foo
199 baz
219 baz
200
220
201 .orig files should exists where specified
221 .orig files should exists where specified
202 $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
222 $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
203 merging file1
223 merging file1
204 creating directory: $TESTTMP/repo/.hg/origbackups (glob)
224 creating directory: $TESTTMP/repo/.hg/origbackups (glob)
205 merging file2
225 merging file2
206 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
226 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
207 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
227 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
208 [1]
228 [1]
209 $ ls .hg/origbackups
229 $ ls .hg/origbackups
210 file1.orig
230 file1.orig
211 file2.orig
231 file2.orig
212 $ grep '<<<' file1 > /dev/null
232 $ grep '<<<' file1 > /dev/null
213 $ grep '<<<' file2 > /dev/null
233 $ grep '<<<' file2 > /dev/null
214
234
215 resolve <file> should re-merge file
235 resolve <file> should re-merge file
216 $ echo resolved > file1
236 $ echo resolved > file1
217 $ hg resolve -q file1
237 $ hg resolve -q file1
218 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
238 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
219 [1]
239 [1]
220 $ grep '<<<' file1 > /dev/null
240 $ grep '<<<' file1 > /dev/null
221
241
222 test .orig behavior with resolve
242 test .orig behavior with resolve
223
243
224 $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
244 $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
225 $TESTTMP/repo/file1.orig: (glob)
245 $TESTTMP/repo/file1.orig: (glob)
226 >>>
246 >>>
227 foo
247 foo
228 baz
248 baz
229 <<<
249 <<<
230
250
231 resolve <file> should do nothing if 'file' was marked resolved
251 resolve <file> should do nothing if 'file' was marked resolved
232 $ echo resolved > file1
252 $ echo resolved > file1
233 $ hg resolve -m file1
253 $ hg resolve -m file1
234 $ hg resolve -q file1
254 $ hg resolve -q file1
235 $ cat file1
255 $ cat file1
236 resolved
256 resolved
237
257
238 insert unsupported advisory merge record
258 insert unsupported advisory merge record
239
259
240 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
260 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
241 $ hg debugmergestate
261 $ hg debugmergestate
242 * version 2 records
262 * version 2 records
243 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
263 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
244 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
264 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
245 unrecognized entry: x advisory record
265 unrecognized entry: x advisory record
246 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
266 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
247 local path: file1 (flags "")
267 local path: file1 (flags "")
248 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
268 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
249 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
269 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
250 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
270 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
251 local path: file2 (flags "")
271 local path: file2 (flags "")
252 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
272 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
253 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
273 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
254 $ hg resolve -l
274 $ hg resolve -l
255 R file1
275 R file1
256 U file2
276 U file2
257
277
258 insert unsupported mandatory merge record
278 insert unsupported mandatory merge record
259
279
260 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
280 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
261 $ hg debugmergestate
281 $ hg debugmergestate
262 * version 2 records
282 * version 2 records
263 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
283 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
264 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
284 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
265 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
285 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
266 local path: file1 (flags "")
286 local path: file1 (flags "")
267 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
287 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
268 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
288 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
269 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
289 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
270 local path: file2 (flags "")
290 local path: file2 (flags "")
271 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
291 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
272 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
292 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
273 unrecognized entry: X mandatory record
293 unrecognized entry: X mandatory record
274 $ hg resolve -l
294 $ hg resolve -l
275 abort: unsupported merge state records: X
295 abort: unsupported merge state records: X
276 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
296 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
277 [255]
297 [255]
278 $ hg resolve -ma
298 $ hg resolve -ma
279 abort: unsupported merge state records: X
299 abort: unsupported merge state records: X
280 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
300 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
281 [255]
301 [255]
282 $ hg summary
302 $ hg summary
283 parent: 2:57653b9f834a
303 parent: 2:57653b9f834a
284 append baz to files
304 append baz to files
285 parent: 1:dc77451844e3
305 parent: 1:dc77451844e3
286 append bar to files
306 append bar to files
287 branch: default
307 branch: default
288 warning: merge state has unsupported record types: X
308 warning: merge state has unsupported record types: X
289 commit: 2 modified, 2 unknown (merge)
309 commit: 2 modified, 2 unknown (merge)
290 update: 2 new changesets (update)
310 update: 2 new changesets (update)
291 phases: 5 draft
311 phases: 5 draft
292
312
293 update --clean shouldn't abort on unsupported records
313 update --clean shouldn't abort on unsupported records
294
314
295 $ hg up -qC 1
315 $ hg up -qC 1
296 $ hg debugmergestate
316 $ hg debugmergestate
297 no merge state found
317 no merge state found
298
318
299 test crashed merge with empty mergestate
319 test crashed merge with empty mergestate
300
320
301 $ mkdir .hg/merge
321 $ mkdir .hg/merge
302 $ touch .hg/merge/state
322 $ touch .hg/merge/state
303
323
304 resolve -l should be empty
324 resolve -l should be empty
305
325
306 $ hg resolve -l
326 $ hg resolve -l
307
327
308 $ cd ..
328 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now