##// END OF EJS Templates
merge: make debug output slightly more helpful by including message for action...
Mads Kiilerich -
r21269:fa601c4e default
parent child Browse files
Show More
@@ -1,1109 +1,1109 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 import struct
8 import struct
9
9
10 from node import nullid, nullrev, hex, bin
10 from node import nullid, nullrev, hex, bin
11 from i18n import _
11 from i18n import _
12 from mercurial import obsolete
12 from mercurial import obsolete
13 import error, util, filemerge, copies, subrepo, worker, dicthelpers
13 import error, util, filemerge, copies, subrepo, worker, dicthelpers
14 import errno, os, shutil
14 import errno, os, shutil
15
15
16 _pack = struct.pack
16 _pack = struct.pack
17 _unpack = struct.unpack
17 _unpack = struct.unpack
18
18
19 def _droponode(data):
19 def _droponode(data):
20 # used for compatibility for v1
20 # used for compatibility for v1
21 bits = data.split("\0")
21 bits = data.split("\0")
22 bits = bits[:-2] + bits[-1:]
22 bits = bits[:-2] + bits[-1:]
23 return "\0".join(bits)
23 return "\0".join(bits)
24
24
25 class mergestate(object):
25 class mergestate(object):
26 '''track 3-way merge state of individual files
26 '''track 3-way merge state of individual files
27
27
28 it is stored on disk when needed. Two file are used, one with an old
28 it is stored on disk when needed. Two file are used, one with an old
29 format, one with a new format. Both contains similar data, but the new
29 format, one with a new format. Both contains similar data, but the new
30 format can store new kind of field.
30 format can store new kind of field.
31
31
32 Current new format is a list of arbitrary record of the form:
32 Current new format is a list of arbitrary record of the form:
33
33
34 [type][length][content]
34 [type][length][content]
35
35
36 Type is a single character, length is a 4 bytes integer, content is an
36 Type is a single character, length is a 4 bytes integer, content is an
37 arbitrary suites of bytes of length `length`.
37 arbitrary suites of bytes of length `length`.
38
38
39 Type should be a letter. Capital letter are mandatory record, Mercurial
39 Type should be a letter. Capital letter are mandatory record, Mercurial
40 should abort if they are unknown. lower case record can be safely ignored.
40 should abort if they are unknown. lower case record can be safely ignored.
41
41
42 Currently known record:
42 Currently known record:
43
43
44 L: the node of the "local" part of the merge (hexified version)
44 L: the node of the "local" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
46 F: a file to be merged entry
46 F: a file to be merged entry
47 '''
47 '''
48 statepathv1 = "merge/state"
48 statepathv1 = "merge/state"
49 statepathv2 = "merge/state2"
49 statepathv2 = "merge/state2"
50
50
51 def __init__(self, repo):
51 def __init__(self, repo):
52 self._repo = repo
52 self._repo = repo
53 self._dirty = False
53 self._dirty = False
54 self._read()
54 self._read()
55
55
56 def reset(self, node=None, other=None):
56 def reset(self, node=None, other=None):
57 self._state = {}
57 self._state = {}
58 self._local = None
58 self._local = None
59 self._other = None
59 self._other = None
60 if node:
60 if node:
61 self._local = node
61 self._local = node
62 self._other = other
62 self._other = other
63 shutil.rmtree(self._repo.join("merge"), True)
63 shutil.rmtree(self._repo.join("merge"), True)
64 self._dirty = False
64 self._dirty = False
65
65
66 def _read(self):
66 def _read(self):
67 """Analyse each record content to restore a serialized state from disk
67 """Analyse each record content to restore a serialized state from disk
68
68
69 This function process "record" entry produced by the de-serialization
69 This function process "record" entry produced by the de-serialization
70 of on disk file.
70 of on disk file.
71 """
71 """
72 self._state = {}
72 self._state = {}
73 self._local = None
73 self._local = None
74 self._other = None
74 self._other = None
75 records = self._readrecords()
75 records = self._readrecords()
76 for rtype, record in records:
76 for rtype, record in records:
77 if rtype == 'L':
77 if rtype == 'L':
78 self._local = bin(record)
78 self._local = bin(record)
79 elif rtype == 'O':
79 elif rtype == 'O':
80 self._other = bin(record)
80 self._other = bin(record)
81 elif rtype == "F":
81 elif rtype == "F":
82 bits = record.split("\0")
82 bits = record.split("\0")
83 self._state[bits[0]] = bits[1:]
83 self._state[bits[0]] = bits[1:]
84 elif not rtype.islower():
84 elif not rtype.islower():
85 raise util.Abort(_('unsupported merge state record: %s')
85 raise util.Abort(_('unsupported merge state record: %s')
86 % rtype)
86 % rtype)
87 self._dirty = False
87 self._dirty = False
88
88
89 def _readrecords(self):
89 def _readrecords(self):
90 """Read merge state from disk and return a list of record (TYPE, data)
90 """Read merge state from disk and return a list of record (TYPE, data)
91
91
92 We read data from both v1 and v2 files and decide which one to use.
92 We read data from both v1 and v2 files and decide which one to use.
93
93
94 V1 has been used by version prior to 2.9.1 and contains less data than
94 V1 has been used by version prior to 2.9.1 and contains less data than
95 v2. We read both versions and check if no data in v2 contradicts
95 v2. We read both versions and check if no data in v2 contradicts
96 v1. If there is not contradiction we can safely assume that both v1
96 v1. If there is not contradiction we can safely assume that both v1
97 and v2 were written at the same time and use the extract data in v2. If
97 and v2 were written at the same time and use the extract data in v2. If
98 there is contradiction we ignore v2 content as we assume an old version
98 there is contradiction we ignore v2 content as we assume an old version
99 of Mercurial has overwritten the mergestate file and left an old v2
99 of Mercurial has overwritten the mergestate file and left an old v2
100 file around.
100 file around.
101
101
102 returns list of record [(TYPE, data), ...]"""
102 returns list of record [(TYPE, data), ...]"""
103 v1records = self._readrecordsv1()
103 v1records = self._readrecordsv1()
104 v2records = self._readrecordsv2()
104 v2records = self._readrecordsv2()
105 oldv2 = set() # old format version of v2 record
105 oldv2 = set() # old format version of v2 record
106 for rec in v2records:
106 for rec in v2records:
107 if rec[0] == 'L':
107 if rec[0] == 'L':
108 oldv2.add(rec)
108 oldv2.add(rec)
109 elif rec[0] == 'F':
109 elif rec[0] == 'F':
110 # drop the onode data (not contained in v1)
110 # drop the onode data (not contained in v1)
111 oldv2.add(('F', _droponode(rec[1])))
111 oldv2.add(('F', _droponode(rec[1])))
112 for rec in v1records:
112 for rec in v1records:
113 if rec not in oldv2:
113 if rec not in oldv2:
114 # v1 file is newer than v2 file, use it
114 # v1 file is newer than v2 file, use it
115 # we have to infer the "other" changeset of the merge
115 # we have to infer the "other" changeset of the merge
116 # we cannot do better than that with v1 of the format
116 # we cannot do better than that with v1 of the format
117 mctx = self._repo[None].parents()[-1]
117 mctx = self._repo[None].parents()[-1]
118 v1records.append(('O', mctx.hex()))
118 v1records.append(('O', mctx.hex()))
119 # add place holder "other" file node information
119 # add place holder "other" file node information
120 # nobody is using it yet so we do no need to fetch the data
120 # nobody is using it yet so we do no need to fetch the data
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
122 for idx, r in enumerate(v1records):
122 for idx, r in enumerate(v1records):
123 if r[0] == 'F':
123 if r[0] == 'F':
124 bits = r[1].split("\0")
124 bits = r[1].split("\0")
125 bits.insert(-2, '')
125 bits.insert(-2, '')
126 v1records[idx] = (r[0], "\0".join(bits))
126 v1records[idx] = (r[0], "\0".join(bits))
127 return v1records
127 return v1records
128 else:
128 else:
129 return v2records
129 return v2records
130
130
131 def _readrecordsv1(self):
131 def _readrecordsv1(self):
132 """read on disk merge state for version 1 file
132 """read on disk merge state for version 1 file
133
133
134 returns list of record [(TYPE, data), ...]
134 returns list of record [(TYPE, data), ...]
135
135
136 Note: the "F" data from this file are one entry short
136 Note: the "F" data from this file are one entry short
137 (no "other file node" entry)
137 (no "other file node" entry)
138 """
138 """
139 records = []
139 records = []
140 try:
140 try:
141 f = self._repo.opener(self.statepathv1)
141 f = self._repo.opener(self.statepathv1)
142 for i, l in enumerate(f):
142 for i, l in enumerate(f):
143 if i == 0:
143 if i == 0:
144 records.append(('L', l[:-1]))
144 records.append(('L', l[:-1]))
145 else:
145 else:
146 records.append(('F', l[:-1]))
146 records.append(('F', l[:-1]))
147 f.close()
147 f.close()
148 except IOError, err:
148 except IOError, err:
149 if err.errno != errno.ENOENT:
149 if err.errno != errno.ENOENT:
150 raise
150 raise
151 return records
151 return records
152
152
153 def _readrecordsv2(self):
153 def _readrecordsv2(self):
154 """read on disk merge state for version 2 file
154 """read on disk merge state for version 2 file
155
155
156 returns list of record [(TYPE, data), ...]
156 returns list of record [(TYPE, data), ...]
157 """
157 """
158 records = []
158 records = []
159 try:
159 try:
160 f = self._repo.opener(self.statepathv2)
160 f = self._repo.opener(self.statepathv2)
161 data = f.read()
161 data = f.read()
162 off = 0
162 off = 0
163 end = len(data)
163 end = len(data)
164 while off < end:
164 while off < end:
165 rtype = data[off]
165 rtype = data[off]
166 off += 1
166 off += 1
167 length = _unpack('>I', data[off:(off + 4)])[0]
167 length = _unpack('>I', data[off:(off + 4)])[0]
168 off += 4
168 off += 4
169 record = data[off:(off + length)]
169 record = data[off:(off + length)]
170 off += length
170 off += length
171 records.append((rtype, record))
171 records.append((rtype, record))
172 f.close()
172 f.close()
173 except IOError, err:
173 except IOError, err:
174 if err.errno != errno.ENOENT:
174 if err.errno != errno.ENOENT:
175 raise
175 raise
176 return records
176 return records
177
177
178 def active(self):
178 def active(self):
179 """Whether mergestate is active.
179 """Whether mergestate is active.
180
180
181 Returns True if there appears to be mergestate. This is a rough proxy
181 Returns True if there appears to be mergestate. This is a rough proxy
182 for "is a merge in progress."
182 for "is a merge in progress."
183 """
183 """
184 # Check local variables before looking at filesystem for performance
184 # Check local variables before looking at filesystem for performance
185 # reasons.
185 # reasons.
186 return bool(self._local) or bool(self._state) or \
186 return bool(self._local) or bool(self._state) or \
187 self._repo.opener.exists(self.statepathv1) or \
187 self._repo.opener.exists(self.statepathv1) or \
188 self._repo.opener.exists(self.statepathv2)
188 self._repo.opener.exists(self.statepathv2)
189
189
190 def commit(self):
190 def commit(self):
191 """Write current state on disk (if necessary)"""
191 """Write current state on disk (if necessary)"""
192 if self._dirty:
192 if self._dirty:
193 records = []
193 records = []
194 records.append(("L", hex(self._local)))
194 records.append(("L", hex(self._local)))
195 records.append(("O", hex(self._other)))
195 records.append(("O", hex(self._other)))
196 for d, v in self._state.iteritems():
196 for d, v in self._state.iteritems():
197 records.append(("F", "\0".join([d] + v)))
197 records.append(("F", "\0".join([d] + v)))
198 self._writerecords(records)
198 self._writerecords(records)
199 self._dirty = False
199 self._dirty = False
200
200
201 def _writerecords(self, records):
201 def _writerecords(self, records):
202 """Write current state on disk (both v1 and v2)"""
202 """Write current state on disk (both v1 and v2)"""
203 self._writerecordsv1(records)
203 self._writerecordsv1(records)
204 self._writerecordsv2(records)
204 self._writerecordsv2(records)
205
205
206 def _writerecordsv1(self, records):
206 def _writerecordsv1(self, records):
207 """Write current state on disk in a version 1 file"""
207 """Write current state on disk in a version 1 file"""
208 f = self._repo.opener(self.statepathv1, "w")
208 f = self._repo.opener(self.statepathv1, "w")
209 irecords = iter(records)
209 irecords = iter(records)
210 lrecords = irecords.next()
210 lrecords = irecords.next()
211 assert lrecords[0] == 'L'
211 assert lrecords[0] == 'L'
212 f.write(hex(self._local) + "\n")
212 f.write(hex(self._local) + "\n")
213 for rtype, data in irecords:
213 for rtype, data in irecords:
214 if rtype == "F":
214 if rtype == "F":
215 f.write("%s\n" % _droponode(data))
215 f.write("%s\n" % _droponode(data))
216 f.close()
216 f.close()
217
217
218 def _writerecordsv2(self, records):
218 def _writerecordsv2(self, records):
219 """Write current state on disk in a version 2 file"""
219 """Write current state on disk in a version 2 file"""
220 f = self._repo.opener(self.statepathv2, "w")
220 f = self._repo.opener(self.statepathv2, "w")
221 for key, data in records:
221 for key, data in records:
222 assert len(key) == 1
222 assert len(key) == 1
223 format = ">sI%is" % len(data)
223 format = ">sI%is" % len(data)
224 f.write(_pack(format, key, len(data), data))
224 f.write(_pack(format, key, len(data), data))
225 f.close()
225 f.close()
226
226
227 def add(self, fcl, fco, fca, fd):
227 def add(self, fcl, fco, fca, fd):
228 """add a new (potentially?) conflicting file the merge state
228 """add a new (potentially?) conflicting file the merge state
229 fcl: file context for local,
229 fcl: file context for local,
230 fco: file context for remote,
230 fco: file context for remote,
231 fca: file context for ancestors,
231 fca: file context for ancestors,
232 fd: file path of the resulting merge.
232 fd: file path of the resulting merge.
233
233
234 note: also write the local version to the `.hg/merge` directory.
234 note: also write the local version to the `.hg/merge` directory.
235 """
235 """
236 hash = util.sha1(fcl.path()).hexdigest()
236 hash = util.sha1(fcl.path()).hexdigest()
237 self._repo.opener.write("merge/" + hash, fcl.data())
237 self._repo.opener.write("merge/" + hash, fcl.data())
238 self._state[fd] = ['u', hash, fcl.path(),
238 self._state[fd] = ['u', hash, fcl.path(),
239 fca.path(), hex(fca.filenode()),
239 fca.path(), hex(fca.filenode()),
240 fco.path(), hex(fco.filenode()),
240 fco.path(), hex(fco.filenode()),
241 fcl.flags()]
241 fcl.flags()]
242 self._dirty = True
242 self._dirty = True
243
243
244 def __contains__(self, dfile):
244 def __contains__(self, dfile):
245 return dfile in self._state
245 return dfile in self._state
246
246
247 def __getitem__(self, dfile):
247 def __getitem__(self, dfile):
248 return self._state[dfile][0]
248 return self._state[dfile][0]
249
249
250 def __iter__(self):
250 def __iter__(self):
251 return iter(sorted(self._state))
251 return iter(sorted(self._state))
252
252
253 def files(self):
253 def files(self):
254 return self._state.keys()
254 return self._state.keys()
255
255
256 def mark(self, dfile, state):
256 def mark(self, dfile, state):
257 self._state[dfile][0] = state
257 self._state[dfile][0] = state
258 self._dirty = True
258 self._dirty = True
259
259
260 def unresolved(self):
260 def unresolved(self):
261 """Obtain the paths of unresolved files."""
261 """Obtain the paths of unresolved files."""
262
262
263 for f, entry in self._state.items():
263 for f, entry in self._state.items():
264 if entry[0] == 'u':
264 if entry[0] == 'u':
265 yield f
265 yield f
266
266
267 def resolve(self, dfile, wctx):
267 def resolve(self, dfile, wctx):
268 """rerun merge process for file path `dfile`"""
268 """rerun merge process for file path `dfile`"""
269 if self[dfile] == 'r':
269 if self[dfile] == 'r':
270 return 0
270 return 0
271 stateentry = self._state[dfile]
271 stateentry = self._state[dfile]
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
273 octx = self._repo[self._other]
273 octx = self._repo[self._other]
274 fcd = wctx[dfile]
274 fcd = wctx[dfile]
275 fco = octx[ofile]
275 fco = octx[ofile]
276 fca = self._repo.filectx(afile, fileid=anode)
276 fca = self._repo.filectx(afile, fileid=anode)
277 # "premerge" x flags
277 # "premerge" x flags
278 flo = fco.flags()
278 flo = fco.flags()
279 fla = fca.flags()
279 fla = fca.flags()
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
281 if fca.node() == nullid:
281 if fca.node() == nullid:
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
283 afile)
283 afile)
284 elif flags == fla:
284 elif flags == fla:
285 flags = flo
285 flags = flo
286 # restore local
286 # restore local
287 f = self._repo.opener("merge/" + hash)
287 f = self._repo.opener("merge/" + hash)
288 self._repo.wwrite(dfile, f.read(), flags)
288 self._repo.wwrite(dfile, f.read(), flags)
289 f.close()
289 f.close()
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
291 if r is None:
291 if r is None:
292 # no real conflict
292 # no real conflict
293 del self._state[dfile]
293 del self._state[dfile]
294 self._dirty = True
294 self._dirty = True
295 elif not r:
295 elif not r:
296 self.mark(dfile, 'r')
296 self.mark(dfile, 'r')
297 return r
297 return r
298
298
299 def _checkunknownfile(repo, wctx, mctx, f):
299 def _checkunknownfile(repo, wctx, mctx, f):
300 return (not repo.dirstate._ignore(f)
300 return (not repo.dirstate._ignore(f)
301 and os.path.isfile(repo.wjoin(f))
301 and os.path.isfile(repo.wjoin(f))
302 and repo.wopener.audit.check(f)
302 and repo.wopener.audit.check(f)
303 and repo.dirstate.normalize(f) not in repo.dirstate
303 and repo.dirstate.normalize(f) not in repo.dirstate
304 and mctx[f].cmp(wctx[f]))
304 and mctx[f].cmp(wctx[f]))
305
305
306 def _checkunknown(repo, wctx, mctx):
306 def _checkunknown(repo, wctx, mctx):
307 "check for collisions between unknown files and files in mctx"
307 "check for collisions between unknown files and files in mctx"
308
308
309 error = False
309 error = False
310 for f in mctx:
310 for f in mctx:
311 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
311 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
312 error = True
312 error = True
313 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
313 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
314 if error:
314 if error:
315 raise util.Abort(_("untracked files in working directory differ "
315 raise util.Abort(_("untracked files in working directory differ "
316 "from files in requested revision"))
316 "from files in requested revision"))
317
317
318 def _forgetremoved(wctx, mctx, branchmerge):
318 def _forgetremoved(wctx, mctx, branchmerge):
319 """
319 """
320 Forget removed files
320 Forget removed files
321
321
322 If we're jumping between revisions (as opposed to merging), and if
322 If we're jumping between revisions (as opposed to merging), and if
323 neither the working directory nor the target rev has the file,
323 neither the working directory nor the target rev has the file,
324 then we need to remove it from the dirstate, to prevent the
324 then we need to remove it from the dirstate, to prevent the
325 dirstate from listing the file when it is no longer in the
325 dirstate from listing the file when it is no longer in the
326 manifest.
326 manifest.
327
327
328 If we're merging, and the other revision has removed a file
328 If we're merging, and the other revision has removed a file
329 that is not present in the working directory, we need to mark it
329 that is not present in the working directory, we need to mark it
330 as removed.
330 as removed.
331 """
331 """
332
332
333 actions = []
333 actions = []
334 state = branchmerge and 'r' or 'f'
334 state = branchmerge and 'r' or 'f'
335 for f in wctx.deleted():
335 for f in wctx.deleted():
336 if f not in mctx:
336 if f not in mctx:
337 actions.append((f, state, None, "forget deleted"))
337 actions.append((f, state, None, "forget deleted"))
338
338
339 if not branchmerge:
339 if not branchmerge:
340 for f in wctx.removed():
340 for f in wctx.removed():
341 if f not in mctx:
341 if f not in mctx:
342 actions.append((f, "f", None, "forget removed"))
342 actions.append((f, "f", None, "forget removed"))
343
343
344 return actions
344 return actions
345
345
346 def _checkcollision(repo, wmf, actions):
346 def _checkcollision(repo, wmf, actions):
347 # build provisional merged manifest up
347 # build provisional merged manifest up
348 pmmf = set(wmf)
348 pmmf = set(wmf)
349
349
350 def addop(f, args):
350 def addop(f, args):
351 pmmf.add(f)
351 pmmf.add(f)
352 def removeop(f, args):
352 def removeop(f, args):
353 pmmf.discard(f)
353 pmmf.discard(f)
354 def nop(f, args):
354 def nop(f, args):
355 pass
355 pass
356
356
357 def renamemoveop(f, args):
357 def renamemoveop(f, args):
358 f2, flags = args
358 f2, flags = args
359 pmmf.discard(f2)
359 pmmf.discard(f2)
360 pmmf.add(f)
360 pmmf.add(f)
361 def renamegetop(f, args):
361 def renamegetop(f, args):
362 f2, flags = args
362 f2, flags = args
363 pmmf.add(f)
363 pmmf.add(f)
364 def mergeop(f, args):
364 def mergeop(f, args):
365 f1, f2, fa, move, anc = args
365 f1, f2, fa, move, anc = args
366 if move:
366 if move:
367 pmmf.discard(f1)
367 pmmf.discard(f1)
368 pmmf.add(f)
368 pmmf.add(f)
369
369
370 opmap = {
370 opmap = {
371 "a": addop,
371 "a": addop,
372 "dm": renamemoveop,
372 "dm": renamemoveop,
373 "dg": renamegetop,
373 "dg": renamegetop,
374 "dr": nop,
374 "dr": nop,
375 "e": nop,
375 "e": nop,
376 "k": nop,
376 "k": nop,
377 "f": addop, # untracked file should be kept in working directory
377 "f": addop, # untracked file should be kept in working directory
378 "g": addop,
378 "g": addop,
379 "m": mergeop,
379 "m": mergeop,
380 "r": removeop,
380 "r": removeop,
381 "rd": nop,
381 "rd": nop,
382 "cd": addop,
382 "cd": addop,
383 "dc": addop,
383 "dc": addop,
384 }
384 }
385 for f, m, args, msg in actions:
385 for f, m, args, msg in actions:
386 op = opmap.get(m)
386 op = opmap.get(m)
387 assert op, m
387 assert op, m
388 op(f, args)
388 op(f, args)
389
389
390 # check case-folding collision in provisional merged manifest
390 # check case-folding collision in provisional merged manifest
391 foldmap = {}
391 foldmap = {}
392 for f in sorted(pmmf):
392 for f in sorted(pmmf):
393 fold = util.normcase(f)
393 fold = util.normcase(f)
394 if fold in foldmap:
394 if fold in foldmap:
395 raise util.Abort(_("case-folding collision between %s and %s")
395 raise util.Abort(_("case-folding collision between %s and %s")
396 % (f, foldmap[fold]))
396 % (f, foldmap[fold]))
397 foldmap[fold] = f
397 foldmap[fold] = f
398
398
399 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
399 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
400 acceptremote, followcopies):
400 acceptremote, followcopies):
401 """
401 """
402 Merge p1 and p2 with ancestor pa and generate merge action list
402 Merge p1 and p2 with ancestor pa and generate merge action list
403
403
404 branchmerge and force are as passed in to update
404 branchmerge and force are as passed in to update
405 partial = function to filter file lists
405 partial = function to filter file lists
406 acceptremote = accept the incoming changes without prompting
406 acceptremote = accept the incoming changes without prompting
407 """
407 """
408
408
409 actions, copy, movewithdir = [], {}, {}
409 actions, copy, movewithdir = [], {}, {}
410
410
411 # manifests fetched in order are going to be faster, so prime the caches
411 # manifests fetched in order are going to be faster, so prime the caches
412 [x.manifest() for x in
412 [x.manifest() for x in
413 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
413 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
414
414
415 if followcopies:
415 if followcopies:
416 ret = copies.mergecopies(repo, wctx, p2, pa)
416 ret = copies.mergecopies(repo, wctx, p2, pa)
417 copy, movewithdir, diverge, renamedelete = ret
417 copy, movewithdir, diverge, renamedelete = ret
418 for of, fl in diverge.iteritems():
418 for of, fl in diverge.iteritems():
419 actions.append((of, "dr", (fl,), "divergent renames"))
419 actions.append((of, "dr", (fl,), "divergent renames"))
420 for of, fl in renamedelete.iteritems():
420 for of, fl in renamedelete.iteritems():
421 actions.append((of, "rd", (fl,), "rename and delete"))
421 actions.append((of, "rd", (fl,), "rename and delete"))
422
422
423 repo.ui.note(_("resolving manifests\n"))
423 repo.ui.note(_("resolving manifests\n"))
424 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
424 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
425 % (bool(branchmerge), bool(force), bool(partial)))
425 % (bool(branchmerge), bool(force), bool(partial)))
426 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
426 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
427
427
428 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
428 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
429 copied = set(copy.values())
429 copied = set(copy.values())
430 copied.update(movewithdir.values())
430 copied.update(movewithdir.values())
431
431
432 if '.hgsubstate' in m1:
432 if '.hgsubstate' in m1:
433 # check whether sub state is modified
433 # check whether sub state is modified
434 for s in sorted(wctx.substate):
434 for s in sorted(wctx.substate):
435 if wctx.sub(s).dirty():
435 if wctx.sub(s).dirty():
436 m1['.hgsubstate'] += "+"
436 m1['.hgsubstate'] += "+"
437 break
437 break
438
438
439 aborts = []
439 aborts = []
440 # Compare manifests
440 # Compare manifests
441 fdiff = dicthelpers.diff(m1, m2)
441 fdiff = dicthelpers.diff(m1, m2)
442 flagsdiff = m1.flagsdiff(m2)
442 flagsdiff = m1.flagsdiff(m2)
443 diff12 = dicthelpers.join(fdiff, flagsdiff)
443 diff12 = dicthelpers.join(fdiff, flagsdiff)
444
444
445 for f, (n12, fl12) in diff12.iteritems():
445 for f, (n12, fl12) in diff12.iteritems():
446 if n12:
446 if n12:
447 n1, n2 = n12
447 n1, n2 = n12
448 else: # file contents didn't change, but flags did
448 else: # file contents didn't change, but flags did
449 n1 = n2 = m1.get(f, None)
449 n1 = n2 = m1.get(f, None)
450 if n1 is None:
450 if n1 is None:
451 # Since n1 == n2, the file isn't present in m2 either. This
451 # Since n1 == n2, the file isn't present in m2 either. This
452 # means that the file was removed or deleted locally and
452 # means that the file was removed or deleted locally and
453 # removed remotely, but that residual entries remain in flags.
453 # removed remotely, but that residual entries remain in flags.
454 # This can happen in manifests generated by workingctx.
454 # This can happen in manifests generated by workingctx.
455 continue
455 continue
456 if fl12:
456 if fl12:
457 fl1, fl2 = fl12
457 fl1, fl2 = fl12
458 else: # flags didn't change, file contents did
458 else: # flags didn't change, file contents did
459 fl1 = fl2 = m1.flags(f)
459 fl1 = fl2 = m1.flags(f)
460
460
461 if partial and not partial(f):
461 if partial and not partial(f):
462 continue
462 continue
463 if n1 and n2:
463 if n1 and n2:
464 fa = f
464 fa = f
465 a = ma.get(f, nullid)
465 a = ma.get(f, nullid)
466 if a == nullid:
466 if a == nullid:
467 fa = copy.get(f, f)
467 fa = copy.get(f, f)
468 # Note: f as default is wrong - we can't really make a 3-way
468 # Note: f as default is wrong - we can't really make a 3-way
469 # merge without an ancestor file.
469 # merge without an ancestor file.
470 fla = ma.flags(fa)
470 fla = ma.flags(fa)
471 nol = 'l' not in fl1 + fl2 + fla
471 nol = 'l' not in fl1 + fl2 + fla
472 if n2 == a and fl2 == fla:
472 if n2 == a and fl2 == fla:
473 actions.append((f, "k", (), "keep")) # remote unchanged
473 actions.append((f, "k", (), "keep")) # remote unchanged
474 elif n1 == a and fl1 == fla: # local unchanged - use remote
474 elif n1 == a and fl1 == fla: # local unchanged - use remote
475 if n1 == n2: # optimization: keep local content
475 if n1 == n2: # optimization: keep local content
476 actions.append((f, "e", (fl2,), "update permissions"))
476 actions.append((f, "e", (fl2,), "update permissions"))
477 else:
477 else:
478 actions.append((f, "g", (fl2,), "remote is newer"))
478 actions.append((f, "g", (fl2,), "remote is newer"))
479 elif nol and n2 == a: # remote only changed 'x'
479 elif nol and n2 == a: # remote only changed 'x'
480 actions.append((f, "e", (fl2,), "update permissions"))
480 actions.append((f, "e", (fl2,), "update permissions"))
481 elif nol and n1 == a: # local only changed 'x'
481 elif nol and n1 == a: # local only changed 'x'
482 actions.append((f, "g", (fl1,), "remote is newer"))
482 actions.append((f, "g", (fl1,), "remote is newer"))
483 else: # both changed something
483 else: # both changed something
484 actions.append((f, "m", (f, f, fa, False, pa.node()),
484 actions.append((f, "m", (f, f, fa, False, pa.node()),
485 "versions differ"))
485 "versions differ"))
486 elif f in copied: # files we'll deal with on m2 side
486 elif f in copied: # files we'll deal with on m2 side
487 pass
487 pass
488 elif n1 and f in movewithdir: # directory rename, move local
488 elif n1 and f in movewithdir: # directory rename, move local
489 f2 = movewithdir[f]
489 f2 = movewithdir[f]
490 actions.append((f2, "dm", (f, fl1),
490 actions.append((f2, "dm", (f, fl1),
491 "remote directory rename - move from " + f))
491 "remote directory rename - move from " + f))
492 elif n1 and f in copy:
492 elif n1 and f in copy:
493 f2 = copy[f]
493 f2 = copy[f]
494 actions.append((f, "m", (f, f2, f2, False, pa.node()),
494 actions.append((f, "m", (f, f2, f2, False, pa.node()),
495 "local copied/moved from " + f2))
495 "local copied/moved from " + f2))
496 elif n1 and f in ma: # clean, a different, no remote
496 elif n1 and f in ma: # clean, a different, no remote
497 if n1 != ma[f]:
497 if n1 != ma[f]:
498 if acceptremote:
498 if acceptremote:
499 actions.append((f, "r", None, "remote delete"))
499 actions.append((f, "r", None, "remote delete"))
500 else:
500 else:
501 actions.append((f, "cd", None, "prompt changed/deleted"))
501 actions.append((f, "cd", None, "prompt changed/deleted"))
502 elif n1[20:] == "a": # added, no remote
502 elif n1[20:] == "a": # added, no remote
503 actions.append((f, "f", None, "remote deleted"))
503 actions.append((f, "f", None, "remote deleted"))
504 else:
504 else:
505 actions.append((f, "r", None, "other deleted"))
505 actions.append((f, "r", None, "other deleted"))
506 elif n2 and f in movewithdir:
506 elif n2 and f in movewithdir:
507 f2 = movewithdir[f]
507 f2 = movewithdir[f]
508 actions.append((f2, "dg", (f, fl2),
508 actions.append((f2, "dg", (f, fl2),
509 "local directory rename - get from " + f))
509 "local directory rename - get from " + f))
510 elif n2 and f in copy:
510 elif n2 and f in copy:
511 f2 = copy[f]
511 f2 = copy[f]
512 if f2 in m2:
512 if f2 in m2:
513 actions.append((f, "m", (f2, f, f2, False, pa.node()),
513 actions.append((f, "m", (f2, f, f2, False, pa.node()),
514 "remote copied from " + f2))
514 "remote copied from " + f2))
515 else:
515 else:
516 actions.append((f, "m", (f2, f, f2, True, pa.node()),
516 actions.append((f, "m", (f2, f, f2, True, pa.node()),
517 "remote moved from " + f2))
517 "remote moved from " + f2))
518 elif n2 and f not in ma:
518 elif n2 and f not in ma:
519 # local unknown, remote created: the logic is described by the
519 # local unknown, remote created: the logic is described by the
520 # following table:
520 # following table:
521 #
521 #
522 # force branchmerge different | action
522 # force branchmerge different | action
523 # n * n | get
523 # n * n | get
524 # n * y | abort
524 # n * y | abort
525 # y n * | get
525 # y n * | get
526 # y y n | get
526 # y y n | get
527 # y y y | merge
527 # y y y | merge
528 #
528 #
529 # Checking whether the files are different is expensive, so we
529 # Checking whether the files are different is expensive, so we
530 # don't do that when we can avoid it.
530 # don't do that when we can avoid it.
531 if force and not branchmerge:
531 if force and not branchmerge:
532 actions.append((f, "g", (fl2,), "remote created"))
532 actions.append((f, "g", (fl2,), "remote created"))
533 else:
533 else:
534 different = _checkunknownfile(repo, wctx, p2, f)
534 different = _checkunknownfile(repo, wctx, p2, f)
535 if force and branchmerge and different:
535 if force and branchmerge and different:
536 # FIXME: This is wrong - f is not in ma ...
536 # FIXME: This is wrong - f is not in ma ...
537 actions.append((f, "m", (f, f, f, False, pa.node()),
537 actions.append((f, "m", (f, f, f, False, pa.node()),
538 "remote differs from untracked local"))
538 "remote differs from untracked local"))
539 elif not force and different:
539 elif not force and different:
540 aborts.append((f, "ud"))
540 aborts.append((f, "ud"))
541 else:
541 else:
542 actions.append((f, "g", (fl2,), "remote created"))
542 actions.append((f, "g", (fl2,), "remote created"))
543 elif n2 and n2 != ma[f]:
543 elif n2 and n2 != ma[f]:
544 different = _checkunknownfile(repo, wctx, p2, f)
544 different = _checkunknownfile(repo, wctx, p2, f)
545 if not force and different:
545 if not force and different:
546 aborts.append((f, "ud"))
546 aborts.append((f, "ud"))
547 else:
547 else:
548 # if different: old untracked f may be overwritten and lost
548 # if different: old untracked f may be overwritten and lost
549 if acceptremote:
549 if acceptremote:
550 actions.append((f, "g", (m2.flags(f),),
550 actions.append((f, "g", (m2.flags(f),),
551 "remote recreating"))
551 "remote recreating"))
552 else:
552 else:
553 actions.append((f, "dc", (m2.flags(f),),
553 actions.append((f, "dc", (m2.flags(f),),
554 "prompt deleted/changed"))
554 "prompt deleted/changed"))
555
555
556 for f, m in sorted(aborts):
556 for f, m in sorted(aborts):
557 if m == "ud":
557 if m == "ud":
558 repo.ui.warn(_("%s: untracked file differs\n") % f)
558 repo.ui.warn(_("%s: untracked file differs\n") % f)
559 else: assert False, m
559 else: assert False, m
560 if aborts:
560 if aborts:
561 raise util.Abort(_("untracked files in working directory differ "
561 raise util.Abort(_("untracked files in working directory differ "
562 "from files in requested revision"))
562 "from files in requested revision"))
563
563
564 if not util.checkcase(repo.path):
564 if not util.checkcase(repo.path):
565 # check collision between files only in p2 for clean update
565 # check collision between files only in p2 for clean update
566 if (not branchmerge and
566 if (not branchmerge and
567 (force or not wctx.dirty(missing=True, branch=False))):
567 (force or not wctx.dirty(missing=True, branch=False))):
568 _checkcollision(repo, m2, [])
568 _checkcollision(repo, m2, [])
569 else:
569 else:
570 _checkcollision(repo, m1, actions)
570 _checkcollision(repo, m1, actions)
571
571
572 return actions
572 return actions
573
573
574 def actionkey(a):
574 def actionkey(a):
575 return a[1] in "rf" and -1 or 0, a
575 return a[1] in "rf" and -1 or 0, a
576
576
577 def getremove(repo, mctx, overwrite, args):
577 def getremove(repo, mctx, overwrite, args):
578 """apply usually-non-interactive updates to the working directory
578 """apply usually-non-interactive updates to the working directory
579
579
580 mctx is the context to be merged into the working copy
580 mctx is the context to be merged into the working copy
581
581
582 yields tuples for progress updates
582 yields tuples for progress updates
583 """
583 """
584 verbose = repo.ui.verbose
584 verbose = repo.ui.verbose
585 unlink = util.unlinkpath
585 unlink = util.unlinkpath
586 wjoin = repo.wjoin
586 wjoin = repo.wjoin
587 fctx = mctx.filectx
587 fctx = mctx.filectx
588 wwrite = repo.wwrite
588 wwrite = repo.wwrite
589 audit = repo.wopener.audit
589 audit = repo.wopener.audit
590 i = 0
590 i = 0
591 for arg in args:
591 for arg in args:
592 f = arg[0]
592 f = arg[0]
593 if arg[1] == 'r':
593 if arg[1] == 'r':
594 if verbose:
594 if verbose:
595 repo.ui.note(_("removing %s\n") % f)
595 repo.ui.note(_("removing %s\n") % f)
596 audit(f)
596 audit(f)
597 try:
597 try:
598 unlink(wjoin(f), ignoremissing=True)
598 unlink(wjoin(f), ignoremissing=True)
599 except OSError, inst:
599 except OSError, inst:
600 repo.ui.warn(_("update failed to remove %s: %s!\n") %
600 repo.ui.warn(_("update failed to remove %s: %s!\n") %
601 (f, inst.strerror))
601 (f, inst.strerror))
602 else:
602 else:
603 if verbose:
603 if verbose:
604 repo.ui.note(_("getting %s\n") % f)
604 repo.ui.note(_("getting %s\n") % f)
605 wwrite(f, fctx(f).data(), arg[2][0])
605 wwrite(f, fctx(f).data(), arg[2][0])
606 if i == 100:
606 if i == 100:
607 yield i, f
607 yield i, f
608 i = 0
608 i = 0
609 i += 1
609 i += 1
610 if i > 0:
610 if i > 0:
611 yield i, f
611 yield i, f
612
612
613 def applyupdates(repo, actions, wctx, mctx, overwrite):
613 def applyupdates(repo, actions, wctx, mctx, overwrite):
614 """apply the merge action list to the working directory
614 """apply the merge action list to the working directory
615
615
616 wctx is the working copy context
616 wctx is the working copy context
617 mctx is the context to be merged into the working copy
617 mctx is the context to be merged into the working copy
618
618
619 Return a tuple of counts (updated, merged, removed, unresolved) that
619 Return a tuple of counts (updated, merged, removed, unresolved) that
620 describes how many files were affected by the update.
620 describes how many files were affected by the update.
621 """
621 """
622
622
623 updated, merged, removed, unresolved = 0, 0, 0, 0
623 updated, merged, removed, unresolved = 0, 0, 0, 0
624 ms = mergestate(repo)
624 ms = mergestate(repo)
625 ms.reset(wctx.p1().node(), mctx.node())
625 ms.reset(wctx.p1().node(), mctx.node())
626 moves = []
626 moves = []
627 actions.sort(key=actionkey)
627 actions.sort(key=actionkey)
628
628
629 # prescan for merges
629 # prescan for merges
630 for a in actions:
630 for a in actions:
631 f, m, args, msg = a
631 f, m, args, msg = a
632 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
632 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
633 if m == "m": # merge
633 if m == "m": # merge
634 f1, f2, fa, move, anc = args
634 f1, f2, fa, move, anc = args
635 if f == '.hgsubstate': # merged internally
635 if f == '.hgsubstate': # merged internally
636 continue
636 continue
637 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
637 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
638 fcl = wctx[f1]
638 fcl = wctx[f1]
639 fco = mctx[f2]
639 fco = mctx[f2]
640 actx = repo[anc]
640 actx = repo[anc]
641 if fa in actx:
641 if fa in actx:
642 fca = actx[fa]
642 fca = actx[fa]
643 else:
643 else:
644 fca = repo.filectx(f1, fileid=nullrev)
644 fca = repo.filectx(f1, fileid=nullrev)
645 ms.add(fcl, fco, fca, f)
645 ms.add(fcl, fco, fca, f)
646 if f1 != f and move:
646 if f1 != f and move:
647 moves.append(f1)
647 moves.append(f1)
648
648
649 audit = repo.wopener.audit
649 audit = repo.wopener.audit
650
650
651 # remove renamed files after safely stored
651 # remove renamed files after safely stored
652 for f in moves:
652 for f in moves:
653 if os.path.lexists(repo.wjoin(f)):
653 if os.path.lexists(repo.wjoin(f)):
654 repo.ui.debug("removing %s\n" % f)
654 repo.ui.debug("removing %s\n" % f)
655 audit(f)
655 audit(f)
656 util.unlinkpath(repo.wjoin(f))
656 util.unlinkpath(repo.wjoin(f))
657
657
658 numupdates = len([a for a in actions if a[1] != 'k'])
658 numupdates = len([a for a in actions if a[1] != 'k'])
659 workeractions = [a for a in actions if a[1] in 'gr']
659 workeractions = [a for a in actions if a[1] in 'gr']
660 updateactions = [a for a in workeractions if a[1] == 'g']
660 updateactions = [a for a in workeractions if a[1] == 'g']
661 updated = len(updateactions)
661 updated = len(updateactions)
662 removeactions = [a for a in workeractions if a[1] == 'r']
662 removeactions = [a for a in workeractions if a[1] == 'r']
663 removed = len(removeactions)
663 removed = len(removeactions)
664 actions = [a for a in actions if a[1] not in 'grk']
664 actions = [a for a in actions if a[1] not in 'grk']
665
665
666 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
666 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
667 if hgsub and hgsub[0] == 'r':
667 if hgsub and hgsub[0] == 'r':
668 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
668 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
669
669
670 z = 0
670 z = 0
671 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
671 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
672 removeactions)
672 removeactions)
673 for i, item in prog:
673 for i, item in prog:
674 z += i
674 z += i
675 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
675 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
676 unit=_('files'))
676 unit=_('files'))
677 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
677 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
678 updateactions)
678 updateactions)
679 for i, item in prog:
679 for i, item in prog:
680 z += i
680 z += i
681 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
681 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
682 unit=_('files'))
682 unit=_('files'))
683
683
684 if hgsub and hgsub[0] == 'g':
684 if hgsub and hgsub[0] == 'g':
685 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
685 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
686
686
687 _updating = _('updating')
687 _updating = _('updating')
688 _files = _('files')
688 _files = _('files')
689 progress = repo.ui.progress
689 progress = repo.ui.progress
690
690
691 for i, a in enumerate(actions):
691 for i, a in enumerate(actions):
692 f, m, args, msg = a
692 f, m, args, msg = a
693 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
693 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
694 if m == "m": # merge
694 if m == "m": # merge
695 f1, f2, fa, move, anc = args
695 f1, f2, fa, move, anc = args
696 if f == '.hgsubstate': # subrepo states need updating
696 if f == '.hgsubstate': # subrepo states need updating
697 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
697 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
698 overwrite)
698 overwrite)
699 continue
699 continue
700 audit(f)
700 audit(f)
701 r = ms.resolve(f, wctx)
701 r = ms.resolve(f, wctx)
702 if r is not None and r > 0:
702 if r is not None and r > 0:
703 unresolved += 1
703 unresolved += 1
704 else:
704 else:
705 if r is None:
705 if r is None:
706 updated += 1
706 updated += 1
707 else:
707 else:
708 merged += 1
708 merged += 1
709 elif m == "dm": # directory rename, move local
709 elif m == "dm": # directory rename, move local
710 f0, flags = args
710 f0, flags = args
711 repo.ui.note(_("moving %s to %s\n") % (f0, f))
711 repo.ui.note(_("moving %s to %s\n") % (f0, f))
712 audit(f)
712 audit(f)
713 repo.wwrite(f, wctx.filectx(f0).data(), flags)
713 repo.wwrite(f, wctx.filectx(f0).data(), flags)
714 util.unlinkpath(repo.wjoin(f0))
714 util.unlinkpath(repo.wjoin(f0))
715 updated += 1
715 updated += 1
716 elif m == "dg": # local directory rename, get
716 elif m == "dg": # local directory rename, get
717 f0, flags = args
717 f0, flags = args
718 repo.ui.note(_("getting %s to %s\n") % (f0, f))
718 repo.ui.note(_("getting %s to %s\n") % (f0, f))
719 repo.wwrite(f, mctx.filectx(f0).data(), flags)
719 repo.wwrite(f, mctx.filectx(f0).data(), flags)
720 updated += 1
720 updated += 1
721 elif m == "dr": # divergent renames
721 elif m == "dr": # divergent renames
722 fl, = args
722 fl, = args
723 repo.ui.warn(_("note: possible conflict - %s was renamed "
723 repo.ui.warn(_("note: possible conflict - %s was renamed "
724 "multiple times to:\n") % f)
724 "multiple times to:\n") % f)
725 for nf in fl:
725 for nf in fl:
726 repo.ui.warn(" %s\n" % nf)
726 repo.ui.warn(" %s\n" % nf)
727 elif m == "rd": # rename and delete
727 elif m == "rd": # rename and delete
728 fl, = args
728 fl, = args
729 repo.ui.warn(_("note: possible conflict - %s was deleted "
729 repo.ui.warn(_("note: possible conflict - %s was deleted "
730 "and renamed to:\n") % f)
730 "and renamed to:\n") % f)
731 for nf in fl:
731 for nf in fl:
732 repo.ui.warn(" %s\n" % nf)
732 repo.ui.warn(" %s\n" % nf)
733 elif m == "e": # exec
733 elif m == "e": # exec
734 flags, = args
734 flags, = args
735 audit(f)
735 audit(f)
736 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
736 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
737 updated += 1
737 updated += 1
738 ms.commit()
738 ms.commit()
739 progress(_updating, None, total=numupdates, unit=_files)
739 progress(_updating, None, total=numupdates, unit=_files)
740
740
741 return updated, merged, removed, unresolved
741 return updated, merged, removed, unresolved
742
742
743 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
743 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
744 acceptremote, followcopies):
744 acceptremote, followcopies):
745 "Calculate the actions needed to merge mctx into wctx using ancestors"
745 "Calculate the actions needed to merge mctx into wctx using ancestors"
746
746
747 if len(ancestors) == 1: # default
747 if len(ancestors) == 1: # default
748 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
748 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
749 branchmerge, force,
749 branchmerge, force,
750 partial, acceptremote, followcopies)
750 partial, acceptremote, followcopies)
751
751
752 else: # only when merge.preferancestor=* - experimentalish code
752 else: # only when merge.preferancestor=* - experimentalish code
753 repo.ui.status(
753 repo.ui.status(
754 _("note: merging %s and %s using bids from ancestors %s\n") %
754 _("note: merging %s and %s using bids from ancestors %s\n") %
755 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
755 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
756
756
757 # Call for bids
757 # Call for bids
758 fbids = {} # mapping filename to list af action bids
758 fbids = {} # mapping filename to list af action bids
759 for ancestor in ancestors:
759 for ancestor in ancestors:
760 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
760 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
761 actions = manifestmerge(repo, wctx, mctx, ancestor,
761 actions = manifestmerge(repo, wctx, mctx, ancestor,
762 branchmerge, force,
762 branchmerge, force,
763 partial, acceptremote, followcopies)
763 partial, acceptremote, followcopies)
764 for a in sorted(actions):
764 for a in sorted(actions):
765 repo.ui.debug(' %s: %s\n' % (a[0], a[1]))
765 f, m, args, msg = a
766 f = a[0]
766 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
767 if f in fbids:
767 if f in fbids:
768 fbids[f].append(a)
768 fbids[f].append(a)
769 else:
769 else:
770 fbids[f] = [a]
770 fbids[f] = [a]
771
771
772 # Pick the best bid for each file
772 # Pick the best bid for each file
773 repo.ui.note(_('\nauction for merging merge bids\n'))
773 repo.ui.note(_('\nauction for merging merge bids\n'))
774 actions = []
774 actions = []
775 for f, bidsl in sorted(fbids.items()):
775 for f, bidsl in sorted(fbids.items()):
776 # Consensus?
776 # Consensus?
777 a0 = bidsl[0]
777 a0 = bidsl[0]
778 if util.all(a == a0 for a in bidsl[1:]): # len(bidsl) is > 1
778 if util.all(a == a0 for a in bidsl[1:]): # len(bidsl) is > 1
779 repo.ui.note(" %s: consensus for %s\n" % (f, a0[1]))
779 repo.ui.note(" %s: consensus for %s\n" % (f, a0[1]))
780 actions.append(a0)
780 actions.append(a0)
781 continue
781 continue
782 # Group bids by kind of action
782 # Group bids by kind of action
783 bids = {}
783 bids = {}
784 for a in bidsl:
784 for a in bidsl:
785 m = a[1]
785 m = a[1]
786 if m in bids:
786 if m in bids:
787 bids[m].append(a)
787 bids[m].append(a)
788 else:
788 else:
789 bids[m] = [a]
789 bids[m] = [a]
790 # If keep is an option, just do it.
790 # If keep is an option, just do it.
791 if "k" in bids:
791 if "k" in bids:
792 repo.ui.note(" %s: picking 'keep' action\n" % f)
792 repo.ui.note(" %s: picking 'keep' action\n" % f)
793 actions.append(bids["k"][0])
793 actions.append(bids["k"][0])
794 continue
794 continue
795 # If all gets agree [how could they not?], just do it.
795 # If all gets agree [how could they not?], just do it.
796 if "g" in bids:
796 if "g" in bids:
797 ga0 = bids["g"][0]
797 ga0 = bids["g"][0]
798 if util.all(a == ga0 for a in bids["g"][1:]):
798 if util.all(a == ga0 for a in bids["g"][1:]):
799 repo.ui.note(" %s: picking 'get' action\n" % f)
799 repo.ui.note(" %s: picking 'get' action\n" % f)
800 actions.append(ga0)
800 actions.append(ga0)
801 continue
801 continue
802 # TODO: Consider other simple actions such as mode changes
802 # TODO: Consider other simple actions such as mode changes
803 # Handle inefficient democrazy.
803 # Handle inefficient democrazy.
804 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
804 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
805 for _f, m, args, msg in bidsl:
805 for _f, m, args, msg in bidsl:
806 repo.ui.note(' %s -> %s\n' % (msg, m))
806 repo.ui.note(' %s -> %s\n' % (msg, m))
807 # Pick random action. TODO: Instead, prompt user when resolving
807 # Pick random action. TODO: Instead, prompt user when resolving
808 a0 = bidsl[0]
808 a0 = bidsl[0]
809 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
809 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
810 (f, a0[1]))
810 (f, a0[1]))
811 actions.append(a0)
811 actions.append(a0)
812 continue
812 continue
813 repo.ui.note(_('end of auction\n\n'))
813 repo.ui.note(_('end of auction\n\n'))
814
814
815 # Filter out prompts.
815 # Filter out prompts.
816 newactions, prompts = [], []
816 newactions, prompts = [], []
817 for a in actions:
817 for a in actions:
818 if a[1] in ("cd", "dc"):
818 if a[1] in ("cd", "dc"):
819 prompts.append(a)
819 prompts.append(a)
820 else:
820 else:
821 newactions.append(a)
821 newactions.append(a)
822 # Prompt and create actions. TODO: Move this towards resolve phase.
822 # Prompt and create actions. TODO: Move this towards resolve phase.
823 for f, m, args, msg in sorted(prompts):
823 for f, m, args, msg in sorted(prompts):
824 if m == "cd":
824 if m == "cd":
825 if repo.ui.promptchoice(
825 if repo.ui.promptchoice(
826 _("local changed %s which remote deleted\n"
826 _("local changed %s which remote deleted\n"
827 "use (c)hanged version or (d)elete?"
827 "use (c)hanged version or (d)elete?"
828 "$$ &Changed $$ &Delete") % f, 0):
828 "$$ &Changed $$ &Delete") % f, 0):
829 newactions.append((f, "r", None, "prompt delete"))
829 newactions.append((f, "r", None, "prompt delete"))
830 else:
830 else:
831 newactions.append((f, "a", None, "prompt keep"))
831 newactions.append((f, "a", None, "prompt keep"))
832 elif m == "dc":
832 elif m == "dc":
833 flags, = args
833 flags, = args
834 if repo.ui.promptchoice(
834 if repo.ui.promptchoice(
835 _("remote changed %s which local deleted\n"
835 _("remote changed %s which local deleted\n"
836 "use (c)hanged version or leave (d)eleted?"
836 "use (c)hanged version or leave (d)eleted?"
837 "$$ &Changed $$ &Deleted") % f, 0) == 0:
837 "$$ &Changed $$ &Deleted") % f, 0) == 0:
838 newactions.append((f, "g", (flags,), "prompt recreating"))
838 newactions.append((f, "g", (flags,), "prompt recreating"))
839 else: assert False, m
839 else: assert False, m
840
840
841 if wctx.rev() is None:
841 if wctx.rev() is None:
842 newactions += _forgetremoved(wctx, mctx, branchmerge)
842 newactions += _forgetremoved(wctx, mctx, branchmerge)
843
843
844 return newactions
844 return newactions
845
845
846 def recordupdates(repo, actions, branchmerge):
846 def recordupdates(repo, actions, branchmerge):
847 "record merge actions to the dirstate"
847 "record merge actions to the dirstate"
848
848
849 for a in actions:
849 for a in actions:
850 f, m, args, msg = a
850 f, m, args, msg = a
851 if m == "r": # remove
851 if m == "r": # remove
852 if branchmerge:
852 if branchmerge:
853 repo.dirstate.remove(f)
853 repo.dirstate.remove(f)
854 else:
854 else:
855 repo.dirstate.drop(f)
855 repo.dirstate.drop(f)
856 elif m == "a": # re-add
856 elif m == "a": # re-add
857 if not branchmerge:
857 if not branchmerge:
858 repo.dirstate.add(f)
858 repo.dirstate.add(f)
859 elif m == "f": # forget
859 elif m == "f": # forget
860 repo.dirstate.drop(f)
860 repo.dirstate.drop(f)
861 elif m == "e": # exec change
861 elif m == "e": # exec change
862 repo.dirstate.normallookup(f)
862 repo.dirstate.normallookup(f)
863 elif m == "k": # keep
863 elif m == "k": # keep
864 pass
864 pass
865 elif m == "g": # get
865 elif m == "g": # get
866 if branchmerge:
866 if branchmerge:
867 repo.dirstate.otherparent(f)
867 repo.dirstate.otherparent(f)
868 else:
868 else:
869 repo.dirstate.normal(f)
869 repo.dirstate.normal(f)
870 elif m == "m": # merge
870 elif m == "m": # merge
871 f1, f2, fa, move, anc = args
871 f1, f2, fa, move, anc = args
872 if branchmerge:
872 if branchmerge:
873 # We've done a branch merge, mark this file as merged
873 # We've done a branch merge, mark this file as merged
874 # so that we properly record the merger later
874 # so that we properly record the merger later
875 repo.dirstate.merge(f)
875 repo.dirstate.merge(f)
876 if f1 != f2: # copy/rename
876 if f1 != f2: # copy/rename
877 if move:
877 if move:
878 repo.dirstate.remove(f1)
878 repo.dirstate.remove(f1)
879 if f1 != f:
879 if f1 != f:
880 repo.dirstate.copy(f1, f)
880 repo.dirstate.copy(f1, f)
881 else:
881 else:
882 repo.dirstate.copy(f2, f)
882 repo.dirstate.copy(f2, f)
883 else:
883 else:
884 # We've update-merged a locally modified file, so
884 # We've update-merged a locally modified file, so
885 # we set the dirstate to emulate a normal checkout
885 # we set the dirstate to emulate a normal checkout
886 # of that file some time in the past. Thus our
886 # of that file some time in the past. Thus our
887 # merge will appear as a normal local file
887 # merge will appear as a normal local file
888 # modification.
888 # modification.
889 if f2 == f: # file not locally copied/moved
889 if f2 == f: # file not locally copied/moved
890 repo.dirstate.normallookup(f)
890 repo.dirstate.normallookup(f)
891 if move:
891 if move:
892 repo.dirstate.drop(f1)
892 repo.dirstate.drop(f1)
893 elif m == "dm": # directory rename, move local
893 elif m == "dm": # directory rename, move local
894 f0, flag = args
894 f0, flag = args
895 if f0 not in repo.dirstate:
895 if f0 not in repo.dirstate:
896 # untracked file moved
896 # untracked file moved
897 continue
897 continue
898 if branchmerge:
898 if branchmerge:
899 repo.dirstate.add(f)
899 repo.dirstate.add(f)
900 repo.dirstate.remove(f0)
900 repo.dirstate.remove(f0)
901 repo.dirstate.copy(f0, f)
901 repo.dirstate.copy(f0, f)
902 else:
902 else:
903 repo.dirstate.normal(f)
903 repo.dirstate.normal(f)
904 repo.dirstate.drop(f0)
904 repo.dirstate.drop(f0)
905 elif m == "dg": # directory rename, get
905 elif m == "dg": # directory rename, get
906 f0, flag = args
906 f0, flag = args
907 if branchmerge:
907 if branchmerge:
908 repo.dirstate.add(f)
908 repo.dirstate.add(f)
909 repo.dirstate.copy(f0, f)
909 repo.dirstate.copy(f0, f)
910 else:
910 else:
911 repo.dirstate.normal(f)
911 repo.dirstate.normal(f)
912
912
913 def update(repo, node, branchmerge, force, partial, ancestor=None,
913 def update(repo, node, branchmerge, force, partial, ancestor=None,
914 mergeancestor=False):
914 mergeancestor=False):
915 """
915 """
916 Perform a merge between the working directory and the given node
916 Perform a merge between the working directory and the given node
917
917
918 node = the node to update to, or None if unspecified
918 node = the node to update to, or None if unspecified
919 branchmerge = whether to merge between branches
919 branchmerge = whether to merge between branches
920 force = whether to force branch merging or file overwriting
920 force = whether to force branch merging or file overwriting
921 partial = a function to filter file lists (dirstate not updated)
921 partial = a function to filter file lists (dirstate not updated)
922 mergeancestor = whether it is merging with an ancestor. If true,
922 mergeancestor = whether it is merging with an ancestor. If true,
923 we should accept the incoming changes for any prompts that occur.
923 we should accept the incoming changes for any prompts that occur.
924 If false, merging with an ancestor (fast-forward) is only allowed
924 If false, merging with an ancestor (fast-forward) is only allowed
925 between different named branches. This flag is used by rebase extension
925 between different named branches. This flag is used by rebase extension
926 as a temporary fix and should be avoided in general.
926 as a temporary fix and should be avoided in general.
927
927
928 The table below shows all the behaviors of the update command
928 The table below shows all the behaviors of the update command
929 given the -c and -C or no options, whether the working directory
929 given the -c and -C or no options, whether the working directory
930 is dirty, whether a revision is specified, and the relationship of
930 is dirty, whether a revision is specified, and the relationship of
931 the parent rev to the target rev (linear, on the same named
931 the parent rev to the target rev (linear, on the same named
932 branch, or on another named branch).
932 branch, or on another named branch).
933
933
934 This logic is tested by test-update-branches.t.
934 This logic is tested by test-update-branches.t.
935
935
936 -c -C dirty rev | linear same cross
936 -c -C dirty rev | linear same cross
937 n n n n | ok (1) x
937 n n n n | ok (1) x
938 n n n y | ok ok ok
938 n n n y | ok ok ok
939 n n y n | merge (2) (2)
939 n n y n | merge (2) (2)
940 n n y y | merge (3) (3)
940 n n y y | merge (3) (3)
941 n y * * | --- discard ---
941 n y * * | --- discard ---
942 y n y * | --- (4) ---
942 y n y * | --- (4) ---
943 y n n * | --- ok ---
943 y n n * | --- ok ---
944 y y * * | --- (5) ---
944 y y * * | --- (5) ---
945
945
946 x = can't happen
946 x = can't happen
947 * = don't-care
947 * = don't-care
948 1 = abort: not a linear update (merge or update --check to force update)
948 1 = abort: not a linear update (merge or update --check to force update)
949 2 = abort: uncommitted changes (commit and merge, or update --clean to
949 2 = abort: uncommitted changes (commit and merge, or update --clean to
950 discard changes)
950 discard changes)
951 3 = abort: uncommitted changes (commit or update --clean to discard changes)
951 3 = abort: uncommitted changes (commit or update --clean to discard changes)
952 4 = abort: uncommitted changes (checked in commands.py)
952 4 = abort: uncommitted changes (checked in commands.py)
953 5 = incompatible options (checked in commands.py)
953 5 = incompatible options (checked in commands.py)
954
954
955 Return the same tuple as applyupdates().
955 Return the same tuple as applyupdates().
956 """
956 """
957
957
958 onode = node
958 onode = node
959 wlock = repo.wlock()
959 wlock = repo.wlock()
960 try:
960 try:
961 wc = repo[None]
961 wc = repo[None]
962 pl = wc.parents()
962 pl = wc.parents()
963 p1 = pl[0]
963 p1 = pl[0]
964 pas = [None]
964 pas = [None]
965 if ancestor:
965 if ancestor:
966 pas = [repo[ancestor]]
966 pas = [repo[ancestor]]
967
967
968 if node is None:
968 if node is None:
969 # Here is where we should consider bookmarks, divergent bookmarks,
969 # Here is where we should consider bookmarks, divergent bookmarks,
970 # foreground changesets (successors), and tip of current branch;
970 # foreground changesets (successors), and tip of current branch;
971 # but currently we are only checking the branch tips.
971 # but currently we are only checking the branch tips.
972 try:
972 try:
973 node = repo.branchtip(wc.branch())
973 node = repo.branchtip(wc.branch())
974 except error.RepoLookupError:
974 except error.RepoLookupError:
975 if wc.branch() == "default": # no default branch!
975 if wc.branch() == "default": # no default branch!
976 node = repo.lookup("tip") # update to tip
976 node = repo.lookup("tip") # update to tip
977 else:
977 else:
978 raise util.Abort(_("branch %s not found") % wc.branch())
978 raise util.Abort(_("branch %s not found") % wc.branch())
979
979
980 if p1.obsolete() and not p1.children():
980 if p1.obsolete() and not p1.children():
981 # allow updating to successors
981 # allow updating to successors
982 successors = obsolete.successorssets(repo, p1.node())
982 successors = obsolete.successorssets(repo, p1.node())
983
983
984 # behavior of certain cases is as follows,
984 # behavior of certain cases is as follows,
985 #
985 #
986 # divergent changesets: update to highest rev, similar to what
986 # divergent changesets: update to highest rev, similar to what
987 # is currently done when there are more than one head
987 # is currently done when there are more than one head
988 # (i.e. 'tip')
988 # (i.e. 'tip')
989 #
989 #
990 # replaced changesets: same as divergent except we know there
990 # replaced changesets: same as divergent except we know there
991 # is no conflict
991 # is no conflict
992 #
992 #
993 # pruned changeset: no update is done; though, we could
993 # pruned changeset: no update is done; though, we could
994 # consider updating to the first non-obsolete parent,
994 # consider updating to the first non-obsolete parent,
995 # similar to what is current done for 'hg prune'
995 # similar to what is current done for 'hg prune'
996
996
997 if successors:
997 if successors:
998 # flatten the list here handles both divergent (len > 1)
998 # flatten the list here handles both divergent (len > 1)
999 # and the usual case (len = 1)
999 # and the usual case (len = 1)
1000 successors = [n for sub in successors for n in sub]
1000 successors = [n for sub in successors for n in sub]
1001
1001
1002 # get the max revision for the given successors set,
1002 # get the max revision for the given successors set,
1003 # i.e. the 'tip' of a set
1003 # i.e. the 'tip' of a set
1004 node = repo.revs("max(%ln)", successors)[0]
1004 node = repo.revs("max(%ln)", successors)[0]
1005 pas = [p1]
1005 pas = [p1]
1006
1006
1007 overwrite = force and not branchmerge
1007 overwrite = force and not branchmerge
1008
1008
1009 p2 = repo[node]
1009 p2 = repo[node]
1010 if pas[0] is None:
1010 if pas[0] is None:
1011 if repo.ui.config("merge", "preferancestor") == '*':
1011 if repo.ui.config("merge", "preferancestor") == '*':
1012 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1012 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1013 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1013 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1014 else:
1014 else:
1015 pas = [p1.ancestor(p2, warn=True)]
1015 pas = [p1.ancestor(p2, warn=True)]
1016
1016
1017 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1017 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1018
1018
1019 ### check phase
1019 ### check phase
1020 if not overwrite and len(pl) > 1:
1020 if not overwrite and len(pl) > 1:
1021 raise util.Abort(_("outstanding uncommitted merges"))
1021 raise util.Abort(_("outstanding uncommitted merges"))
1022 if branchmerge:
1022 if branchmerge:
1023 if pas == [p2]:
1023 if pas == [p2]:
1024 raise util.Abort(_("merging with a working directory ancestor"
1024 raise util.Abort(_("merging with a working directory ancestor"
1025 " has no effect"))
1025 " has no effect"))
1026 elif pas == [p1]:
1026 elif pas == [p1]:
1027 if not mergeancestor and p1.branch() == p2.branch():
1027 if not mergeancestor and p1.branch() == p2.branch():
1028 raise util.Abort(_("nothing to merge"),
1028 raise util.Abort(_("nothing to merge"),
1029 hint=_("use 'hg update' "
1029 hint=_("use 'hg update' "
1030 "or check 'hg heads'"))
1030 "or check 'hg heads'"))
1031 if not force and (wc.files() or wc.deleted()):
1031 if not force and (wc.files() or wc.deleted()):
1032 raise util.Abort(_("uncommitted changes"),
1032 raise util.Abort(_("uncommitted changes"),
1033 hint=_("use 'hg status' to list changes"))
1033 hint=_("use 'hg status' to list changes"))
1034 for s in sorted(wc.substate):
1034 for s in sorted(wc.substate):
1035 if wc.sub(s).dirty():
1035 if wc.sub(s).dirty():
1036 raise util.Abort(_("uncommitted changes in "
1036 raise util.Abort(_("uncommitted changes in "
1037 "subrepository '%s'") % s)
1037 "subrepository '%s'") % s)
1038
1038
1039 elif not overwrite:
1039 elif not overwrite:
1040 if p1 == p2: # no-op update
1040 if p1 == p2: # no-op update
1041 # call the hooks and exit early
1041 # call the hooks and exit early
1042 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1042 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1043 repo.hook('update', parent1=xp2, parent2='', error=0)
1043 repo.hook('update', parent1=xp2, parent2='', error=0)
1044 return 0, 0, 0, 0
1044 return 0, 0, 0, 0
1045
1045
1046 if pas not in ([p1], [p2]): # nonlinear
1046 if pas not in ([p1], [p2]): # nonlinear
1047 dirty = wc.dirty(missing=True)
1047 dirty = wc.dirty(missing=True)
1048 if dirty or onode is None:
1048 if dirty or onode is None:
1049 # Branching is a bit strange to ensure we do the minimal
1049 # Branching is a bit strange to ensure we do the minimal
1050 # amount of call to obsolete.background.
1050 # amount of call to obsolete.background.
1051 foreground = obsolete.foreground(repo, [p1.node()])
1051 foreground = obsolete.foreground(repo, [p1.node()])
1052 # note: the <node> variable contains a random identifier
1052 # note: the <node> variable contains a random identifier
1053 if repo[node].node() in foreground:
1053 if repo[node].node() in foreground:
1054 pas = [p1] # allow updating to successors
1054 pas = [p1] # allow updating to successors
1055 elif dirty:
1055 elif dirty:
1056 msg = _("uncommitted changes")
1056 msg = _("uncommitted changes")
1057 if onode is None:
1057 if onode is None:
1058 hint = _("commit and merge, or update --clean to"
1058 hint = _("commit and merge, or update --clean to"
1059 " discard changes")
1059 " discard changes")
1060 else:
1060 else:
1061 hint = _("commit or update --clean to discard"
1061 hint = _("commit or update --clean to discard"
1062 " changes")
1062 " changes")
1063 raise util.Abort(msg, hint=hint)
1063 raise util.Abort(msg, hint=hint)
1064 else: # node is none
1064 else: # node is none
1065 msg = _("not a linear update")
1065 msg = _("not a linear update")
1066 hint = _("merge or update --check to force update")
1066 hint = _("merge or update --check to force update")
1067 raise util.Abort(msg, hint=hint)
1067 raise util.Abort(msg, hint=hint)
1068 else:
1068 else:
1069 # Allow jumping branches if clean and specific rev given
1069 # Allow jumping branches if clean and specific rev given
1070 pas = [p1]
1070 pas = [p1]
1071
1071
1072 followcopies = False
1072 followcopies = False
1073 if overwrite:
1073 if overwrite:
1074 pas = [wc]
1074 pas = [wc]
1075 elif pas == [p2]: # backwards
1075 elif pas == [p2]: # backwards
1076 pas = [wc.p1()]
1076 pas = [wc.p1()]
1077 elif not branchmerge and not wc.dirty(missing=True):
1077 elif not branchmerge and not wc.dirty(missing=True):
1078 pass
1078 pass
1079 elif pas[0] and repo.ui.configbool("merge", "followcopies", True):
1079 elif pas[0] and repo.ui.configbool("merge", "followcopies", True):
1080 followcopies = True
1080 followcopies = True
1081
1081
1082 ### calculate phase
1082 ### calculate phase
1083 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1083 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1084 partial, mergeancestor, followcopies)
1084 partial, mergeancestor, followcopies)
1085
1085
1086 ### apply phase
1086 ### apply phase
1087 if not branchmerge: # just jump to the new rev
1087 if not branchmerge: # just jump to the new rev
1088 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1088 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1089 if not partial:
1089 if not partial:
1090 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1090 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1091 # note that we're in the middle of an update
1091 # note that we're in the middle of an update
1092 repo.vfs.write('updatestate', p2.hex())
1092 repo.vfs.write('updatestate', p2.hex())
1093
1093
1094 stats = applyupdates(repo, actions, wc, p2, overwrite)
1094 stats = applyupdates(repo, actions, wc, p2, overwrite)
1095
1095
1096 if not partial:
1096 if not partial:
1097 repo.setparents(fp1, fp2)
1097 repo.setparents(fp1, fp2)
1098 recordupdates(repo, actions, branchmerge)
1098 recordupdates(repo, actions, branchmerge)
1099 # update completed, clear state
1099 # update completed, clear state
1100 util.unlink(repo.join('updatestate'))
1100 util.unlink(repo.join('updatestate'))
1101
1101
1102 if not branchmerge:
1102 if not branchmerge:
1103 repo.dirstate.setbranch(p2.branch())
1103 repo.dirstate.setbranch(p2.branch())
1104 finally:
1104 finally:
1105 wlock.release()
1105 wlock.release()
1106
1106
1107 if not partial:
1107 if not partial:
1108 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1108 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1109 return stats
1109 return stats
@@ -1,348 +1,348 b''
1 Criss cross merging
1 Criss cross merging
2
2
3 $ hg init criss-cross
3 $ hg init criss-cross
4 $ cd criss-cross
4 $ cd criss-cross
5 $ echo '0 base' > f1
5 $ echo '0 base' > f1
6 $ echo '0 base' > f2
6 $ echo '0 base' > f2
7 $ hg ci -Aqm '0 base'
7 $ hg ci -Aqm '0 base'
8
8
9 $ echo '1 first change' > f1
9 $ echo '1 first change' > f1
10 $ hg ci -m '1 first change f1'
10 $ hg ci -m '1 first change f1'
11
11
12 $ hg up -qr0
12 $ hg up -qr0
13 $ echo '2 first change' > f2
13 $ echo '2 first change' > f2
14 $ hg ci -qm '2 first change f2'
14 $ hg ci -qm '2 first change f2'
15
15
16 $ hg merge -qr 1
16 $ hg merge -qr 1
17 $ hg ci -m '3 merge'
17 $ hg ci -m '3 merge'
18
18
19 $ hg up -qr2
19 $ hg up -qr2
20 $ hg merge -qr1
20 $ hg merge -qr1
21 $ hg ci -qm '4 merge'
21 $ hg ci -qm '4 merge'
22
22
23 $ echo '5 second change' > f1
23 $ echo '5 second change' > f1
24 $ hg ci -m '5 second change f1'
24 $ hg ci -m '5 second change f1'
25
25
26 $ hg up -r3
26 $ hg up -r3
27 note: using 0f6b37dbe527 as ancestor of adfe50279922 and cf89f02107e5
27 note: using 0f6b37dbe527 as ancestor of adfe50279922 and cf89f02107e5
28 alternatively, use --config merge.preferancestor=40663881a6dd
28 alternatively, use --config merge.preferancestor=40663881a6dd
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 $ echo '6 second change' > f2
30 $ echo '6 second change' > f2
31 $ hg ci -m '6 second change f2'
31 $ hg ci -m '6 second change f2'
32
32
33 $ hg log -G
33 $ hg log -G
34 @ changeset: 6:3b08d01b0ab5
34 @ changeset: 6:3b08d01b0ab5
35 | tag: tip
35 | tag: tip
36 | parent: 3:cf89f02107e5
36 | parent: 3:cf89f02107e5
37 | user: test
37 | user: test
38 | date: Thu Jan 01 00:00:00 1970 +0000
38 | date: Thu Jan 01 00:00:00 1970 +0000
39 | summary: 6 second change f2
39 | summary: 6 second change f2
40 |
40 |
41 | o changeset: 5:adfe50279922
41 | o changeset: 5:adfe50279922
42 | | user: test
42 | | user: test
43 | | date: Thu Jan 01 00:00:00 1970 +0000
43 | | date: Thu Jan 01 00:00:00 1970 +0000
44 | | summary: 5 second change f1
44 | | summary: 5 second change f1
45 | |
45 | |
46 | o changeset: 4:7d3e55501ae6
46 | o changeset: 4:7d3e55501ae6
47 | |\ parent: 2:40663881a6dd
47 | |\ parent: 2:40663881a6dd
48 | | | parent: 1:0f6b37dbe527
48 | | | parent: 1:0f6b37dbe527
49 | | | user: test
49 | | | user: test
50 | | | date: Thu Jan 01 00:00:00 1970 +0000
50 | | | date: Thu Jan 01 00:00:00 1970 +0000
51 | | | summary: 4 merge
51 | | | summary: 4 merge
52 | | |
52 | | |
53 o---+ changeset: 3:cf89f02107e5
53 o---+ changeset: 3:cf89f02107e5
54 | | | parent: 2:40663881a6dd
54 | | | parent: 2:40663881a6dd
55 |/ / parent: 1:0f6b37dbe527
55 |/ / parent: 1:0f6b37dbe527
56 | | user: test
56 | | user: test
57 | | date: Thu Jan 01 00:00:00 1970 +0000
57 | | date: Thu Jan 01 00:00:00 1970 +0000
58 | | summary: 3 merge
58 | | summary: 3 merge
59 | |
59 | |
60 | o changeset: 2:40663881a6dd
60 | o changeset: 2:40663881a6dd
61 | | parent: 0:40494bf2444c
61 | | parent: 0:40494bf2444c
62 | | user: test
62 | | user: test
63 | | date: Thu Jan 01 00:00:00 1970 +0000
63 | | date: Thu Jan 01 00:00:00 1970 +0000
64 | | summary: 2 first change f2
64 | | summary: 2 first change f2
65 | |
65 | |
66 o | changeset: 1:0f6b37dbe527
66 o | changeset: 1:0f6b37dbe527
67 |/ user: test
67 |/ user: test
68 | date: Thu Jan 01 00:00:00 1970 +0000
68 | date: Thu Jan 01 00:00:00 1970 +0000
69 | summary: 1 first change f1
69 | summary: 1 first change f1
70 |
70 |
71 o changeset: 0:40494bf2444c
71 o changeset: 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: 0 base
74 summary: 0 base
75
75
76
76
77 $ hg merge -v --debug --tool internal:dump 5
77 $ hg merge -v --debug --tool internal:dump 5
78 note: using 0f6b37dbe527 as ancestor of 3b08d01b0ab5 and adfe50279922
78 note: using 0f6b37dbe527 as ancestor of 3b08d01b0ab5 and adfe50279922
79 alternatively, use --config merge.preferancestor=40663881a6dd
79 alternatively, use --config merge.preferancestor=40663881a6dd
80 searching for copies back to rev 3
80 searching for copies back to rev 3
81 resolving manifests
81 resolving manifests
82 branchmerge: True, force: False, partial: False
82 branchmerge: True, force: False, partial: False
83 ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
83 ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
84 f1: remote is newer -> g
84 f1: remote is newer -> g
85 f2: versions differ -> m
85 f2: versions differ -> m
86 preserving f2 for resolve of f2
86 preserving f2 for resolve of f2
87 getting f1
87 getting f1
88 updating: f1 1/2 files (50.00%)
88 updating: f1 1/2 files (50.00%)
89 updating: f2 2/2 files (100.00%)
89 updating: f2 2/2 files (100.00%)
90 picked tool 'internal:dump' for f2 (binary False symlink False)
90 picked tool 'internal:dump' for f2 (binary False symlink False)
91 merging f2
91 merging f2
92 my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@40494bf2444c
92 my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@40494bf2444c
93 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
93 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
94 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
94 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
95 [1]
95 [1]
96
96
97 $ head *
97 $ head *
98 ==> f1 <==
98 ==> f1 <==
99 5 second change
99 5 second change
100
100
101 ==> f2 <==
101 ==> f2 <==
102 6 second change
102 6 second change
103
103
104 ==> f2.base <==
104 ==> f2.base <==
105 0 base
105 0 base
106
106
107 ==> f2.local <==
107 ==> f2.local <==
108 6 second change
108 6 second change
109
109
110 ==> f2.orig <==
110 ==> f2.orig <==
111 6 second change
111 6 second change
112
112
113 ==> f2.other <==
113 ==> f2.other <==
114 2 first change
114 2 first change
115
115
116 $ hg up -qC .
116 $ hg up -qC .
117 $ hg merge -v --tool internal:dump 5 --config merge.preferancestor="null 40663881 3b08d"
117 $ hg merge -v --tool internal:dump 5 --config merge.preferancestor="null 40663881 3b08d"
118 note: using 40663881a6dd as ancestor of 3b08d01b0ab5 and adfe50279922
118 note: using 40663881a6dd as ancestor of 3b08d01b0ab5 and adfe50279922
119 alternatively, use --config merge.preferancestor=0f6b37dbe527
119 alternatively, use --config merge.preferancestor=0f6b37dbe527
120 resolving manifests
120 resolving manifests
121 merging f1
121 merging f1
122 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
122 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
123 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
123 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
124 [1]
124 [1]
125
125
126 Redo merge with merge.preferancestor="*" to enable bid merge
126 Redo merge with merge.preferancestor="*" to enable bid merge
127
127
128 $ rm f*
128 $ rm f*
129 $ hg up -qC .
129 $ hg up -qC .
130 $ hg merge -v --debug --tool internal:dump 5 --config merge.preferancestor="*"
130 $ hg merge -v --debug --tool internal:dump 5 --config merge.preferancestor="*"
131 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
131 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
132
132
133 calculating bids for ancestor 0f6b37dbe527
133 calculating bids for ancestor 0f6b37dbe527
134 searching for copies back to rev 3
134 searching for copies back to rev 3
135 resolving manifests
135 resolving manifests
136 branchmerge: True, force: False, partial: False
136 branchmerge: True, force: False, partial: False
137 ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
137 ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
138 f1: g
138 f1: remote is newer -> g
139 f2: m
139 f2: versions differ -> m
140
140
141 calculating bids for ancestor 40663881a6dd
141 calculating bids for ancestor 40663881a6dd
142 searching for copies back to rev 3
142 searching for copies back to rev 3
143 resolving manifests
143 resolving manifests
144 branchmerge: True, force: False, partial: False
144 branchmerge: True, force: False, partial: False
145 ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922
145 ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922
146 f1: m
146 f1: versions differ -> m
147 f2: k
147 f2: keep -> k
148
148
149 auction for merging merge bids
149 auction for merging merge bids
150 f1: picking 'get' action
150 f1: picking 'get' action
151 f2: picking 'keep' action
151 f2: picking 'keep' action
152 end of auction
152 end of auction
153
153
154 f1: remote is newer -> g
154 f1: remote is newer -> g
155 f2: keep -> k
155 f2: keep -> k
156 getting f1
156 getting f1
157 updating: f1 1/1 files (100.00%)
157 updating: f1 1/1 files (100.00%)
158 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 (branch merge, don't forget to commit)
159 (branch merge, don't forget to commit)
160
160
161 $ head *
161 $ head *
162 ==> f1 <==
162 ==> f1 <==
163 5 second change
163 5 second change
164
164
165 ==> f2 <==
165 ==> f2 <==
166 6 second change
166 6 second change
167
167
168
168
169 The other way around:
169 The other way around:
170
170
171 $ hg up -C -r5
171 $ hg up -C -r5
172 note: using 0f6b37dbe527 as ancestor of 3b08d01b0ab5 and adfe50279922
172 note: using 0f6b37dbe527 as ancestor of 3b08d01b0ab5 and adfe50279922
173 alternatively, use --config merge.preferancestor=40663881a6dd
173 alternatively, use --config merge.preferancestor=40663881a6dd
174 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 $ hg merge -v --debug --config merge.preferancestor="*"
175 $ hg merge -v --debug --config merge.preferancestor="*"
176 note: merging adfe50279922+ and 3b08d01b0ab5 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
176 note: merging adfe50279922+ and 3b08d01b0ab5 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
177
177
178 calculating bids for ancestor 0f6b37dbe527
178 calculating bids for ancestor 0f6b37dbe527
179 searching for copies back to rev 3
179 searching for copies back to rev 3
180 resolving manifests
180 resolving manifests
181 branchmerge: True, force: False, partial: False
181 branchmerge: True, force: False, partial: False
182 ancestor: 0f6b37dbe527, local: adfe50279922+, remote: 3b08d01b0ab5
182 ancestor: 0f6b37dbe527, local: adfe50279922+, remote: 3b08d01b0ab5
183 f1: k
183 f1: keep -> k
184 f2: m
184 f2: versions differ -> m
185
185
186 calculating bids for ancestor 40663881a6dd
186 calculating bids for ancestor 40663881a6dd
187 searching for copies back to rev 3
187 searching for copies back to rev 3
188 resolving manifests
188 resolving manifests
189 branchmerge: True, force: False, partial: False
189 branchmerge: True, force: False, partial: False
190 ancestor: 40663881a6dd, local: adfe50279922+, remote: 3b08d01b0ab5
190 ancestor: 40663881a6dd, local: adfe50279922+, remote: 3b08d01b0ab5
191 f1: m
191 f1: versions differ -> m
192 f2: g
192 f2: remote is newer -> g
193
193
194 auction for merging merge bids
194 auction for merging merge bids
195 f1: picking 'keep' action
195 f1: picking 'keep' action
196 f2: picking 'get' action
196 f2: picking 'get' action
197 end of auction
197 end of auction
198
198
199 f1: keep -> k
199 f1: keep -> k
200 f2: remote is newer -> g
200 f2: remote is newer -> g
201 getting f2
201 getting f2
202 updating: f2 1/1 files (100.00%)
202 updating: f2 1/1 files (100.00%)
203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 (branch merge, don't forget to commit)
204 (branch merge, don't forget to commit)
205
205
206 $ head *
206 $ head *
207 ==> f1 <==
207 ==> f1 <==
208 5 second change
208 5 second change
209
209
210 ==> f2 <==
210 ==> f2 <==
211 6 second change
211 6 second change
212
212
213 Verify how the output looks and and how verbose it is:
213 Verify how the output looks and and how verbose it is:
214
214
215 $ hg up -qC
215 $ hg up -qC
216 $ hg merge --config merge.preferancestor="*"
216 $ hg merge --config merge.preferancestor="*"
217 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
217 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
218 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
218 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
219 (branch merge, don't forget to commit)
219 (branch merge, don't forget to commit)
220
220
221 $ hg up -qC
221 $ hg up -qC
222 $ hg merge -v --config merge.preferancestor="*"
222 $ hg merge -v --config merge.preferancestor="*"
223 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
223 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
224
224
225 calculating bids for ancestor 0f6b37dbe527
225 calculating bids for ancestor 0f6b37dbe527
226 resolving manifests
226 resolving manifests
227
227
228 calculating bids for ancestor 40663881a6dd
228 calculating bids for ancestor 40663881a6dd
229 resolving manifests
229 resolving manifests
230
230
231 auction for merging merge bids
231 auction for merging merge bids
232 f1: picking 'get' action
232 f1: picking 'get' action
233 f2: picking 'keep' action
233 f2: picking 'keep' action
234 end of auction
234 end of auction
235
235
236 getting f1
236 getting f1
237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 (branch merge, don't forget to commit)
238 (branch merge, don't forget to commit)
239
239
240 $ hg up -qC
240 $ hg up -qC
241 $ hg merge -v --debug --config merge.preferancestor="*"
241 $ hg merge -v --debug --config merge.preferancestor="*"
242 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
242 note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
243
243
244 calculating bids for ancestor 0f6b37dbe527
244 calculating bids for ancestor 0f6b37dbe527
245 searching for copies back to rev 3
245 searching for copies back to rev 3
246 resolving manifests
246 resolving manifests
247 branchmerge: True, force: False, partial: False
247 branchmerge: True, force: False, partial: False
248 ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
248 ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
249 f1: g
249 f1: remote is newer -> g
250 f2: m
250 f2: versions differ -> m
251
251
252 calculating bids for ancestor 40663881a6dd
252 calculating bids for ancestor 40663881a6dd
253 searching for copies back to rev 3
253 searching for copies back to rev 3
254 resolving manifests
254 resolving manifests
255 branchmerge: True, force: False, partial: False
255 branchmerge: True, force: False, partial: False
256 ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922
256 ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922
257 f1: m
257 f1: versions differ -> m
258 f2: k
258 f2: keep -> k
259
259
260 auction for merging merge bids
260 auction for merging merge bids
261 f1: picking 'get' action
261 f1: picking 'get' action
262 f2: picking 'keep' action
262 f2: picking 'keep' action
263 end of auction
263 end of auction
264
264
265 f1: remote is newer -> g
265 f1: remote is newer -> g
266 f2: keep -> k
266 f2: keep -> k
267 getting f1
267 getting f1
268 updating: f1 1/1 files (100.00%)
268 updating: f1 1/1 files (100.00%)
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 (branch merge, don't forget to commit)
270 (branch merge, don't forget to commit)
271
271
272 $ cd ..
272 $ cd ..
273
273
274 http://stackoverflow.com/questions/9350005/how-do-i-specify-a-merge-base-to-use-in-a-hg-merge/9430810
274 http://stackoverflow.com/questions/9350005/how-do-i-specify-a-merge-base-to-use-in-a-hg-merge/9430810
275
275
276 $ hg init ancestor-merging
276 $ hg init ancestor-merging
277 $ cd ancestor-merging
277 $ cd ancestor-merging
278 $ echo a > x
278 $ echo a > x
279 $ hg commit -A -m a x
279 $ hg commit -A -m a x
280 $ hg update -q 0
280 $ hg update -q 0
281 $ echo b >> x
281 $ echo b >> x
282 $ hg commit -m b
282 $ hg commit -m b
283 $ hg update -q 0
283 $ hg update -q 0
284 $ echo c >> x
284 $ echo c >> x
285 $ hg commit -qm c
285 $ hg commit -qm c
286 $ hg update -q 1
286 $ hg update -q 1
287 $ hg merge -q --tool internal:local 2
287 $ hg merge -q --tool internal:local 2
288 $ echo c >> x
288 $ echo c >> x
289 $ hg commit -m bc
289 $ hg commit -m bc
290 $ hg update -q 2
290 $ hg update -q 2
291 $ hg merge -q --tool internal:local 1
291 $ hg merge -q --tool internal:local 1
292 $ echo b >> x
292 $ echo b >> x
293 $ hg commit -qm cb
293 $ hg commit -qm cb
294
294
295 $ hg merge
295 $ hg merge
296 note: using 70008a2163f6 as ancestor of 0d355fdef312 and 4b8b546a3eef
296 note: using 70008a2163f6 as ancestor of 0d355fdef312 and 4b8b546a3eef
297 alternatively, use --config merge.preferancestor=b211bbc6eb3c
297 alternatively, use --config merge.preferancestor=b211bbc6eb3c
298 merging x
298 merging x
299 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
299 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
300 (branch merge, don't forget to commit)
300 (branch merge, don't forget to commit)
301 $ cat x
301 $ cat x
302 a
302 a
303 c
303 c
304 b
304 b
305 c
305 c
306
306
307 $ hg up -qC .
307 $ hg up -qC .
308
308
309 $ hg merge --config merge.preferancestor=b211bbc6eb3c
309 $ hg merge --config merge.preferancestor=b211bbc6eb3c
310 note: using b211bbc6eb3c as ancestor of 0d355fdef312 and 4b8b546a3eef
310 note: using b211bbc6eb3c as ancestor of 0d355fdef312 and 4b8b546a3eef
311 alternatively, use --config merge.preferancestor=70008a2163f6
311 alternatively, use --config merge.preferancestor=70008a2163f6
312 merging x
312 merging x
313 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
313 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
314 (branch merge, don't forget to commit)
314 (branch merge, don't forget to commit)
315 $ cat x
315 $ cat x
316 a
316 a
317 b
317 b
318 c
318 c
319 b
319 b
320
320
321 $ hg up -qC .
321 $ hg up -qC .
322
322
323 $ hg merge -v --config merge.preferancestor="*"
323 $ hg merge -v --config merge.preferancestor="*"
324 note: merging 0d355fdef312+ and 4b8b546a3eef using bids from ancestors 70008a2163f6 and b211bbc6eb3c
324 note: merging 0d355fdef312+ and 4b8b546a3eef using bids from ancestors 70008a2163f6 and b211bbc6eb3c
325
325
326 calculating bids for ancestor 70008a2163f6
326 calculating bids for ancestor 70008a2163f6
327 resolving manifests
327 resolving manifests
328
328
329 calculating bids for ancestor b211bbc6eb3c
329 calculating bids for ancestor b211bbc6eb3c
330 resolving manifests
330 resolving manifests
331
331
332 auction for merging merge bids
332 auction for merging merge bids
333 x: multiple bids for merge action:
333 x: multiple bids for merge action:
334 versions differ -> m
334 versions differ -> m
335 versions differ -> m
335 versions differ -> m
336 x: ambiguous merge - picked m action
336 x: ambiguous merge - picked m action
337 end of auction
337 end of auction
338
338
339 merging x
339 merging x
340 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
340 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
341 (branch merge, don't forget to commit)
341 (branch merge, don't forget to commit)
342 $ cat x
342 $ cat x
343 a
343 a
344 c
344 c
345 b
345 b
346 c
346 c
347
347
348 $ cd ..
348 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now