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