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