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