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