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