##// END OF EJS Templates
merge: abort on file/directory case folding collisions (issue4892)...
Mads Kiilerich -
r26661:2b955fec default
parent child Browse files
Show More
@@ -1,1263 +1,1274 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import shutil
12 import shutil
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from . import (
22 from . import (
23 copies,
23 copies,
24 destutil,
24 destutil,
25 error,
25 error,
26 filemerge,
26 filemerge,
27 obsolete,
27 obsolete,
28 subrepo,
28 subrepo,
29 util,
29 util,
30 worker,
30 worker,
31 )
31 )
32
32
33 _pack = struct.pack
33 _pack = struct.pack
34 _unpack = struct.unpack
34 _unpack = struct.unpack
35
35
36 def _droponode(data):
36 def _droponode(data):
37 # used for compatibility for v1
37 # used for compatibility for v1
38 bits = data.split('\0')
38 bits = data.split('\0')
39 bits = bits[:-2] + bits[-1:]
39 bits = bits[:-2] + bits[-1:]
40 return '\0'.join(bits)
40 return '\0'.join(bits)
41
41
42 class mergestate(object):
42 class mergestate(object):
43 '''track 3-way merge state of individual files
43 '''track 3-way merge state of individual files
44
44
45 it is stored on disk when needed. Two file are used, one with an old
45 it is stored on disk when needed. Two file are used, one with an old
46 format, one with a new format. Both contains similar data, but the new
46 format, one with a new format. Both contains similar data, but the new
47 format can store new kinds of field.
47 format can store new kinds of field.
48
48
49 Current new format is a list of arbitrary record of the form:
49 Current new format is a list of arbitrary record of the form:
50
50
51 [type][length][content]
51 [type][length][content]
52
52
53 Type is a single character, length is a 4 bytes integer, content is an
53 Type is a single character, length is a 4 bytes integer, content is an
54 arbitrary suites of bytes of length `length`.
54 arbitrary suites of bytes of length `length`.
55
55
56 Type should be a letter. Capital letter are mandatory record, Mercurial
56 Type should be a letter. Capital letter are mandatory record, Mercurial
57 should abort if they are unknown. lower case record can be safely ignored.
57 should abort if they are unknown. lower case record can be safely ignored.
58
58
59 Currently known record:
59 Currently known record:
60
60
61 L: the node of the "local" part of the merge (hexified version)
61 L: the node of the "local" part of the merge (hexified version)
62 O: the node of the "other" part of the merge (hexified version)
62 O: the node of the "other" part of the merge (hexified version)
63 F: a file to be merged entry
63 F: a file to be merged entry
64 D: a file that the external merge driver will merge internally
64 D: a file that the external merge driver will merge internally
65 (experimental)
65 (experimental)
66 m: the external merge driver defined for this merge plus its run state
66 m: the external merge driver defined for this merge plus its run state
67 (experimental)
67 (experimental)
68
68
69 Merge driver run states (experimental):
69 Merge driver run states (experimental):
70 u: driver-resolved files unmarked -- needs to be run next time we're about
70 u: driver-resolved files unmarked -- needs to be run next time we're about
71 to resolve or commit
71 to resolve or commit
72 m: driver-resolved files marked -- only needs to be run before commit
72 m: driver-resolved files marked -- only needs to be run before commit
73 s: success/skipped -- does not need to be run any more
73 s: success/skipped -- does not need to be run any more
74 '''
74 '''
75 statepathv1 = 'merge/state'
75 statepathv1 = 'merge/state'
76 statepathv2 = 'merge/state2'
76 statepathv2 = 'merge/state2'
77
77
78 def __init__(self, repo):
78 def __init__(self, repo):
79 self._repo = repo
79 self._repo = repo
80 self._dirty = False
80 self._dirty = False
81 self._read()
81 self._read()
82
82
83 def reset(self, node=None, other=None):
83 def reset(self, node=None, other=None):
84 self._state = {}
84 self._state = {}
85 self._local = None
85 self._local = None
86 self._other = None
86 self._other = None
87 if node:
87 if node:
88 self._local = node
88 self._local = node
89 self._other = other
89 self._other = other
90 self._mdstate = 'u'
90 self._mdstate = 'u'
91 shutil.rmtree(self._repo.join('merge'), True)
91 shutil.rmtree(self._repo.join('merge'), True)
92 self._dirty = False
92 self._dirty = False
93
93
94 def _read(self):
94 def _read(self):
95 """Analyse each record content to restore a serialized state from disk
95 """Analyse each record content to restore a serialized state from disk
96
96
97 This function process "record" entry produced by the de-serialization
97 This function process "record" entry produced by the de-serialization
98 of on disk file.
98 of on disk file.
99 """
99 """
100 self._state = {}
100 self._state = {}
101 self._local = None
101 self._local = None
102 self._other = None
102 self._other = None
103 self._mdstate = 'u'
103 self._mdstate = 'u'
104 records = self._readrecords()
104 records = self._readrecords()
105 for rtype, record in records:
105 for rtype, record in records:
106 if rtype == 'L':
106 if rtype == 'L':
107 self._local = bin(record)
107 self._local = bin(record)
108 elif rtype == 'O':
108 elif rtype == 'O':
109 self._other = bin(record)
109 self._other = bin(record)
110 elif rtype == 'm':
110 elif rtype == 'm':
111 bits = record.split('\0', 1)
111 bits = record.split('\0', 1)
112 mdstate = bits[1]
112 mdstate = bits[1]
113 if len(mdstate) != 1 or mdstate not in 'ums':
113 if len(mdstate) != 1 or mdstate not in 'ums':
114 # the merge driver should be idempotent, so just rerun it
114 # the merge driver should be idempotent, so just rerun it
115 mdstate = 'u'
115 mdstate = 'u'
116
116
117 # protect against the following:
117 # protect against the following:
118 # - A configures a malicious merge driver in their hgrc, then
118 # - A configures a malicious merge driver in their hgrc, then
119 # pauses the merge
119 # pauses the merge
120 # - A edits their hgrc to remove references to the merge driver
120 # - A edits their hgrc to remove references to the merge driver
121 # - A gives a copy of their entire repo, including .hg, to B
121 # - A gives a copy of their entire repo, including .hg, to B
122 # - B inspects .hgrc and finds it to be clean
122 # - B inspects .hgrc and finds it to be clean
123 # - B then continues the merge and the malicious merge driver
123 # - B then continues the merge and the malicious merge driver
124 # gets invoked
124 # gets invoked
125 if self.mergedriver != bits[0]:
125 if self.mergedriver != bits[0]:
126 raise error.ConfigError(
126 raise error.ConfigError(
127 _("merge driver changed since merge started"),
127 _("merge driver changed since merge started"),
128 hint=_("revert merge driver change or abort merge"))
128 hint=_("revert merge driver change or abort merge"))
129 self._mdstate = mdstate
129 self._mdstate = mdstate
130 elif rtype in 'FD':
130 elif rtype in 'FD':
131 bits = record.split('\0')
131 bits = record.split('\0')
132 self._state[bits[0]] = bits[1:]
132 self._state[bits[0]] = bits[1:]
133 elif not rtype.islower():
133 elif not rtype.islower():
134 raise error.Abort(_('unsupported merge state record: %s')
134 raise error.Abort(_('unsupported merge state record: %s')
135 % rtype)
135 % rtype)
136 self._dirty = False
136 self._dirty = False
137
137
138 def _readrecords(self):
138 def _readrecords(self):
139 """Read merge state from disk and return a list of record (TYPE, data)
139 """Read merge state from disk and return a list of record (TYPE, data)
140
140
141 We read data from both v1 and v2 files and decide which one to use.
141 We read data from both v1 and v2 files and decide which one to use.
142
142
143 V1 has been used by version prior to 2.9.1 and contains less data than
143 V1 has been used by version prior to 2.9.1 and contains less data than
144 v2. We read both versions and check if no data in v2 contradicts
144 v2. We read both versions and check if no data in v2 contradicts
145 v1. If there is not contradiction we can safely assume that both v1
145 v1. If there is not contradiction we can safely assume that both v1
146 and v2 were written at the same time and use the extract data in v2. If
146 and v2 were written at the same time and use the extract data in v2. If
147 there is contradiction we ignore v2 content as we assume an old version
147 there is contradiction we ignore v2 content as we assume an old version
148 of Mercurial has overwritten the mergestate file and left an old v2
148 of Mercurial has overwritten the mergestate file and left an old v2
149 file around.
149 file around.
150
150
151 returns list of record [(TYPE, data), ...]"""
151 returns list of record [(TYPE, data), ...]"""
152 v1records = self._readrecordsv1()
152 v1records = self._readrecordsv1()
153 v2records = self._readrecordsv2()
153 v2records = self._readrecordsv2()
154 if self._v1v2match(v1records, v2records):
154 if self._v1v2match(v1records, v2records):
155 return v2records
155 return v2records
156 else:
156 else:
157 # v1 file is newer than v2 file, use it
157 # v1 file is newer than v2 file, use it
158 # we have to infer the "other" changeset of the merge
158 # we have to infer the "other" changeset of the merge
159 # we cannot do better than that with v1 of the format
159 # we cannot do better than that with v1 of the format
160 mctx = self._repo[None].parents()[-1]
160 mctx = self._repo[None].parents()[-1]
161 v1records.append(('O', mctx.hex()))
161 v1records.append(('O', mctx.hex()))
162 # add place holder "other" file node information
162 # add place holder "other" file node information
163 # nobody is using it yet so we do no need to fetch the data
163 # nobody is using it yet so we do no need to fetch the data
164 # if mctx was wrong `mctx[bits[-2]]` may fails.
164 # if mctx was wrong `mctx[bits[-2]]` may fails.
165 for idx, r in enumerate(v1records):
165 for idx, r in enumerate(v1records):
166 if r[0] == 'F':
166 if r[0] == 'F':
167 bits = r[1].split('\0')
167 bits = r[1].split('\0')
168 bits.insert(-2, '')
168 bits.insert(-2, '')
169 v1records[idx] = (r[0], '\0'.join(bits))
169 v1records[idx] = (r[0], '\0'.join(bits))
170 return v1records
170 return v1records
171
171
172 def _v1v2match(self, v1records, v2records):
172 def _v1v2match(self, v1records, v2records):
173 oldv2 = set() # old format version of v2 record
173 oldv2 = set() # old format version of v2 record
174 for rec in v2records:
174 for rec in v2records:
175 if rec[0] == 'L':
175 if rec[0] == 'L':
176 oldv2.add(rec)
176 oldv2.add(rec)
177 elif rec[0] == 'F':
177 elif rec[0] == 'F':
178 # drop the onode data (not contained in v1)
178 # drop the onode data (not contained in v1)
179 oldv2.add(('F', _droponode(rec[1])))
179 oldv2.add(('F', _droponode(rec[1])))
180 for rec in v1records:
180 for rec in v1records:
181 if rec not in oldv2:
181 if rec not in oldv2:
182 return False
182 return False
183 else:
183 else:
184 return True
184 return True
185
185
186 def _readrecordsv1(self):
186 def _readrecordsv1(self):
187 """read on disk merge state for version 1 file
187 """read on disk merge state for version 1 file
188
188
189 returns list of record [(TYPE, data), ...]
189 returns list of record [(TYPE, data), ...]
190
190
191 Note: the "F" data from this file are one entry short
191 Note: the "F" data from this file are one entry short
192 (no "other file node" entry)
192 (no "other file node" entry)
193 """
193 """
194 records = []
194 records = []
195 try:
195 try:
196 f = self._repo.vfs(self.statepathv1)
196 f = self._repo.vfs(self.statepathv1)
197 for i, l in enumerate(f):
197 for i, l in enumerate(f):
198 if i == 0:
198 if i == 0:
199 records.append(('L', l[:-1]))
199 records.append(('L', l[:-1]))
200 else:
200 else:
201 records.append(('F', l[:-1]))
201 records.append(('F', l[:-1]))
202 f.close()
202 f.close()
203 except IOError as err:
203 except IOError as err:
204 if err.errno != errno.ENOENT:
204 if err.errno != errno.ENOENT:
205 raise
205 raise
206 return records
206 return records
207
207
208 def _readrecordsv2(self):
208 def _readrecordsv2(self):
209 """read on disk merge state for version 2 file
209 """read on disk merge state for version 2 file
210
210
211 returns list of record [(TYPE, data), ...]
211 returns list of record [(TYPE, data), ...]
212 """
212 """
213 records = []
213 records = []
214 try:
214 try:
215 f = self._repo.vfs(self.statepathv2)
215 f = self._repo.vfs(self.statepathv2)
216 data = f.read()
216 data = f.read()
217 off = 0
217 off = 0
218 end = len(data)
218 end = len(data)
219 while off < end:
219 while off < end:
220 rtype = data[off]
220 rtype = data[off]
221 off += 1
221 off += 1
222 length = _unpack('>I', data[off:(off + 4)])[0]
222 length = _unpack('>I', data[off:(off + 4)])[0]
223 off += 4
223 off += 4
224 record = data[off:(off + length)]
224 record = data[off:(off + length)]
225 off += length
225 off += length
226 records.append((rtype, record))
226 records.append((rtype, record))
227 f.close()
227 f.close()
228 except IOError as err:
228 except IOError as err:
229 if err.errno != errno.ENOENT:
229 if err.errno != errno.ENOENT:
230 raise
230 raise
231 return records
231 return records
232
232
233 @util.propertycache
233 @util.propertycache
234 def mergedriver(self):
234 def mergedriver(self):
235 return self._repo.ui.config('experimental', 'mergedriver')
235 return self._repo.ui.config('experimental', 'mergedriver')
236
236
237 def active(self):
237 def active(self):
238 """Whether mergestate is active.
238 """Whether mergestate is active.
239
239
240 Returns True if there appears to be mergestate. This is a rough proxy
240 Returns True if there appears to be mergestate. This is a rough proxy
241 for "is a merge in progress."
241 for "is a merge in progress."
242 """
242 """
243 # Check local variables before looking at filesystem for performance
243 # Check local variables before looking at filesystem for performance
244 # reasons.
244 # reasons.
245 return bool(self._local) or bool(self._state) or \
245 return bool(self._local) or bool(self._state) or \
246 self._repo.vfs.exists(self.statepathv1) or \
246 self._repo.vfs.exists(self.statepathv1) or \
247 self._repo.vfs.exists(self.statepathv2)
247 self._repo.vfs.exists(self.statepathv2)
248
248
249 def commit(self):
249 def commit(self):
250 """Write current state on disk (if necessary)"""
250 """Write current state on disk (if necessary)"""
251 if self._dirty:
251 if self._dirty:
252 records = []
252 records = []
253 records.append(('L', hex(self._local)))
253 records.append(('L', hex(self._local)))
254 records.append(('O', hex(self._other)))
254 records.append(('O', hex(self._other)))
255 if self.mergedriver:
255 if self.mergedriver:
256 records.append(('m', '\0'.join([
256 records.append(('m', '\0'.join([
257 self.mergedriver, self._mdstate])))
257 self.mergedriver, self._mdstate])))
258 for d, v in self._state.iteritems():
258 for d, v in self._state.iteritems():
259 if v[0] == 'd':
259 if v[0] == 'd':
260 records.append(('D', '\0'.join([d] + v)))
260 records.append(('D', '\0'.join([d] + v)))
261 else:
261 else:
262 records.append(('F', '\0'.join([d] + v)))
262 records.append(('F', '\0'.join([d] + v)))
263 self._writerecords(records)
263 self._writerecords(records)
264 self._dirty = False
264 self._dirty = False
265
265
266 def _writerecords(self, records):
266 def _writerecords(self, records):
267 """Write current state on disk (both v1 and v2)"""
267 """Write current state on disk (both v1 and v2)"""
268 self._writerecordsv1(records)
268 self._writerecordsv1(records)
269 self._writerecordsv2(records)
269 self._writerecordsv2(records)
270
270
271 def _writerecordsv1(self, records):
271 def _writerecordsv1(self, records):
272 """Write current state on disk in a version 1 file"""
272 """Write current state on disk in a version 1 file"""
273 f = self._repo.vfs(self.statepathv1, 'w')
273 f = self._repo.vfs(self.statepathv1, 'w')
274 irecords = iter(records)
274 irecords = iter(records)
275 lrecords = irecords.next()
275 lrecords = irecords.next()
276 assert lrecords[0] == 'L'
276 assert lrecords[0] == 'L'
277 f.write(hex(self._local) + '\n')
277 f.write(hex(self._local) + '\n')
278 for rtype, data in irecords:
278 for rtype, data in irecords:
279 if rtype == 'F':
279 if rtype == 'F':
280 f.write('%s\n' % _droponode(data))
280 f.write('%s\n' % _droponode(data))
281 f.close()
281 f.close()
282
282
283 def _writerecordsv2(self, records):
283 def _writerecordsv2(self, records):
284 """Write current state on disk in a version 2 file"""
284 """Write current state on disk in a version 2 file"""
285 f = self._repo.vfs(self.statepathv2, 'w')
285 f = self._repo.vfs(self.statepathv2, 'w')
286 for key, data in records:
286 for key, data in records:
287 assert len(key) == 1
287 assert len(key) == 1
288 format = '>sI%is' % len(data)
288 format = '>sI%is' % len(data)
289 f.write(_pack(format, key, len(data), data))
289 f.write(_pack(format, key, len(data), data))
290 f.close()
290 f.close()
291
291
292 def add(self, fcl, fco, fca, fd):
292 def add(self, fcl, fco, fca, fd):
293 """add a new (potentially?) conflicting file the merge state
293 """add a new (potentially?) conflicting file the merge state
294 fcl: file context for local,
294 fcl: file context for local,
295 fco: file context for remote,
295 fco: file context for remote,
296 fca: file context for ancestors,
296 fca: file context for ancestors,
297 fd: file path of the resulting merge.
297 fd: file path of the resulting merge.
298
298
299 note: also write the local version to the `.hg/merge` directory.
299 note: also write the local version to the `.hg/merge` directory.
300 """
300 """
301 hash = util.sha1(fcl.path()).hexdigest()
301 hash = util.sha1(fcl.path()).hexdigest()
302 self._repo.vfs.write('merge/' + hash, fcl.data())
302 self._repo.vfs.write('merge/' + hash, fcl.data())
303 self._state[fd] = ['u', hash, fcl.path(),
303 self._state[fd] = ['u', hash, fcl.path(),
304 fca.path(), hex(fca.filenode()),
304 fca.path(), hex(fca.filenode()),
305 fco.path(), hex(fco.filenode()),
305 fco.path(), hex(fco.filenode()),
306 fcl.flags()]
306 fcl.flags()]
307 self._dirty = True
307 self._dirty = True
308
308
309 def __contains__(self, dfile):
309 def __contains__(self, dfile):
310 return dfile in self._state
310 return dfile in self._state
311
311
312 def __getitem__(self, dfile):
312 def __getitem__(self, dfile):
313 return self._state[dfile][0]
313 return self._state[dfile][0]
314
314
315 def __iter__(self):
315 def __iter__(self):
316 return iter(sorted(self._state))
316 return iter(sorted(self._state))
317
317
318 def files(self):
318 def files(self):
319 return self._state.keys()
319 return self._state.keys()
320
320
321 def mark(self, dfile, state):
321 def mark(self, dfile, state):
322 self._state[dfile][0] = state
322 self._state[dfile][0] = state
323 self._dirty = True
323 self._dirty = True
324
324
325 def unresolved(self):
325 def unresolved(self):
326 """Obtain the paths of unresolved files."""
326 """Obtain the paths of unresolved files."""
327
327
328 for f, entry in self._state.items():
328 for f, entry in self._state.items():
329 if entry[0] == 'u':
329 if entry[0] == 'u':
330 yield f
330 yield f
331
331
332 def _resolve(self, preresolve, dfile, wctx, labels=None):
332 def _resolve(self, preresolve, dfile, wctx, labels=None):
333 """rerun merge process for file path `dfile`"""
333 """rerun merge process for file path `dfile`"""
334 if self[dfile] in 'rd':
334 if self[dfile] in 'rd':
335 return True, 0
335 return True, 0
336 stateentry = self._state[dfile]
336 stateentry = self._state[dfile]
337 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
337 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
338 octx = self._repo[self._other]
338 octx = self._repo[self._other]
339 fcd = wctx[dfile]
339 fcd = wctx[dfile]
340 fco = octx[ofile]
340 fco = octx[ofile]
341 fca = self._repo.filectx(afile, fileid=anode)
341 fca = self._repo.filectx(afile, fileid=anode)
342 # "premerge" x flags
342 # "premerge" x flags
343 flo = fco.flags()
343 flo = fco.flags()
344 fla = fca.flags()
344 fla = fca.flags()
345 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
345 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
346 if fca.node() == nullid:
346 if fca.node() == nullid:
347 if preresolve:
347 if preresolve:
348 self._repo.ui.warn(
348 self._repo.ui.warn(
349 _('warning: cannot merge flags for %s\n') % afile)
349 _('warning: cannot merge flags for %s\n') % afile)
350 elif flags == fla:
350 elif flags == fla:
351 flags = flo
351 flags = flo
352 if preresolve:
352 if preresolve:
353 # restore local
353 # restore local
354 f = self._repo.vfs('merge/' + hash)
354 f = self._repo.vfs('merge/' + hash)
355 self._repo.wwrite(dfile, f.read(), flags)
355 self._repo.wwrite(dfile, f.read(), flags)
356 f.close()
356 f.close()
357 complete, r = filemerge.premerge(self._repo, self._local, lfile,
357 complete, r = filemerge.premerge(self._repo, self._local, lfile,
358 fcd, fco, fca, labels=labels)
358 fcd, fco, fca, labels=labels)
359 else:
359 else:
360 complete, r = filemerge.filemerge(self._repo, self._local, lfile,
360 complete, r = filemerge.filemerge(self._repo, self._local, lfile,
361 fcd, fco, fca, labels=labels)
361 fcd, fco, fca, labels=labels)
362 if r is None:
362 if r is None:
363 # no real conflict
363 # no real conflict
364 del self._state[dfile]
364 del self._state[dfile]
365 self._dirty = True
365 self._dirty = True
366 elif not r:
366 elif not r:
367 self.mark(dfile, 'r')
367 self.mark(dfile, 'r')
368 return complete, r
368 return complete, r
369
369
370 def preresolve(self, dfile, wctx, labels=None):
370 def preresolve(self, dfile, wctx, labels=None):
371 return self._resolve(True, dfile, wctx, labels=labels)
371 return self._resolve(True, dfile, wctx, labels=labels)
372
372
373 def resolve(self, dfile, wctx, labels=None):
373 def resolve(self, dfile, wctx, labels=None):
374 """rerun merge process for file path `dfile`"""
374 """rerun merge process for file path `dfile`"""
375 return self._resolve(False, dfile, wctx, labels=labels)[1]
375 return self._resolve(False, dfile, wctx, labels=labels)[1]
376
376
377 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
377 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
378 if f2 is None:
378 if f2 is None:
379 f2 = f
379 f2 = f
380 return (os.path.isfile(repo.wjoin(f))
380 return (os.path.isfile(repo.wjoin(f))
381 and repo.wvfs.audit.check(f)
381 and repo.wvfs.audit.check(f)
382 and repo.dirstate.normalize(f) not in repo.dirstate
382 and repo.dirstate.normalize(f) not in repo.dirstate
383 and mctx[f2].cmp(wctx[f]))
383 and mctx[f2].cmp(wctx[f]))
384
384
385 def _checkunknownfiles(repo, wctx, mctx, force, actions):
385 def _checkunknownfiles(repo, wctx, mctx, force, actions):
386 """
386 """
387 Considers any actions that care about the presence of conflicting unknown
387 Considers any actions that care about the presence of conflicting unknown
388 files. For some actions, the result is to abort; for others, it is to
388 files. For some actions, the result is to abort; for others, it is to
389 choose a different action.
389 choose a different action.
390 """
390 """
391 aborts = []
391 aborts = []
392 if not force:
392 if not force:
393 for f, (m, args, msg) in actions.iteritems():
393 for f, (m, args, msg) in actions.iteritems():
394 if m in ('c', 'dc'):
394 if m in ('c', 'dc'):
395 if _checkunknownfile(repo, wctx, mctx, f):
395 if _checkunknownfile(repo, wctx, mctx, f):
396 aborts.append(f)
396 aborts.append(f)
397 elif m == 'dg':
397 elif m == 'dg':
398 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
398 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
399 aborts.append(f)
399 aborts.append(f)
400
400
401 for f in sorted(aborts):
401 for f in sorted(aborts):
402 repo.ui.warn(_("%s: untracked file differs\n") % f)
402 repo.ui.warn(_("%s: untracked file differs\n") % f)
403 if aborts:
403 if aborts:
404 raise error.Abort(_("untracked files in working directory differ "
404 raise error.Abort(_("untracked files in working directory differ "
405 "from files in requested revision"))
405 "from files in requested revision"))
406
406
407 for f, (m, args, msg) in actions.iteritems():
407 for f, (m, args, msg) in actions.iteritems():
408 if m == 'c':
408 if m == 'c':
409 actions[f] = ('g', args, msg)
409 actions[f] = ('g', args, msg)
410 elif m == 'cm':
410 elif m == 'cm':
411 fl2, anc = args
411 fl2, anc = args
412 different = _checkunknownfile(repo, wctx, mctx, f)
412 different = _checkunknownfile(repo, wctx, mctx, f)
413 if different:
413 if different:
414 actions[f] = ('m', (f, f, None, False, anc),
414 actions[f] = ('m', (f, f, None, False, anc),
415 "remote differs from untracked local")
415 "remote differs from untracked local")
416 else:
416 else:
417 actions[f] = ('g', (fl2,), "remote created")
417 actions[f] = ('g', (fl2,), "remote created")
418
418
419 def _forgetremoved(wctx, mctx, branchmerge):
419 def _forgetremoved(wctx, mctx, branchmerge):
420 """
420 """
421 Forget removed files
421 Forget removed files
422
422
423 If we're jumping between revisions (as opposed to merging), and if
423 If we're jumping between revisions (as opposed to merging), and if
424 neither the working directory nor the target rev has the file,
424 neither the working directory nor the target rev has the file,
425 then we need to remove it from the dirstate, to prevent the
425 then we need to remove it from the dirstate, to prevent the
426 dirstate from listing the file when it is no longer in the
426 dirstate from listing the file when it is no longer in the
427 manifest.
427 manifest.
428
428
429 If we're merging, and the other revision has removed a file
429 If we're merging, and the other revision has removed a file
430 that is not present in the working directory, we need to mark it
430 that is not present in the working directory, we need to mark it
431 as removed.
431 as removed.
432 """
432 """
433
433
434 actions = {}
434 actions = {}
435 m = 'f'
435 m = 'f'
436 if branchmerge:
436 if branchmerge:
437 m = 'r'
437 m = 'r'
438 for f in wctx.deleted():
438 for f in wctx.deleted():
439 if f not in mctx:
439 if f not in mctx:
440 actions[f] = m, None, "forget deleted"
440 actions[f] = m, None, "forget deleted"
441
441
442 if not branchmerge:
442 if not branchmerge:
443 for f in wctx.removed():
443 for f in wctx.removed():
444 if f not in mctx:
444 if f not in mctx:
445 actions[f] = 'f', None, "forget removed"
445 actions[f] = 'f', None, "forget removed"
446
446
447 return actions
447 return actions
448
448
449 def _checkcollision(repo, wmf, actions):
449 def _checkcollision(repo, wmf, actions):
450 # build provisional merged manifest up
450 # build provisional merged manifest up
451 pmmf = set(wmf)
451 pmmf = set(wmf)
452
452
453 if actions:
453 if actions:
454 # k, dr, e and rd are no-op
454 # k, dr, e and rd are no-op
455 for m in 'a', 'f', 'g', 'cd', 'dc':
455 for m in 'a', 'f', 'g', 'cd', 'dc':
456 for f, args, msg in actions[m]:
456 for f, args, msg in actions[m]:
457 pmmf.add(f)
457 pmmf.add(f)
458 for f, args, msg in actions['r']:
458 for f, args, msg in actions['r']:
459 pmmf.discard(f)
459 pmmf.discard(f)
460 for f, args, msg in actions['dm']:
460 for f, args, msg in actions['dm']:
461 f2, flags = args
461 f2, flags = args
462 pmmf.discard(f2)
462 pmmf.discard(f2)
463 pmmf.add(f)
463 pmmf.add(f)
464 for f, args, msg in actions['dg']:
464 for f, args, msg in actions['dg']:
465 pmmf.add(f)
465 pmmf.add(f)
466 for f, args, msg in actions['m']:
466 for f, args, msg in actions['m']:
467 f1, f2, fa, move, anc = args
467 f1, f2, fa, move, anc = args
468 if move:
468 if move:
469 pmmf.discard(f1)
469 pmmf.discard(f1)
470 pmmf.add(f)
470 pmmf.add(f)
471
471
472 # check case-folding collision in provisional merged manifest
472 # check case-folding collision in provisional merged manifest
473 foldmap = {}
473 foldmap = {}
474 for f in sorted(pmmf):
474 for f in sorted(pmmf):
475 fold = util.normcase(f)
475 fold = util.normcase(f)
476 if fold in foldmap:
476 if fold in foldmap:
477 raise error.Abort(_("case-folding collision between %s and %s")
477 raise error.Abort(_("case-folding collision between %s and %s")
478 % (f, foldmap[fold]))
478 % (f, foldmap[fold]))
479 foldmap[fold] = f
479 foldmap[fold] = f
480
480
481 # check case-folding of directories
482 foldprefix = unfoldprefix = lastfull = ''
483 for fold, f in sorted(foldmap.items()):
484 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
485 # the folded prefix matches but actual casing is different
486 raise error.Abort(_("case-folding collision between "
487 "%s and directory of %s") % (lastfull, f))
488 foldprefix = fold + '/'
489 unfoldprefix = f + '/'
490 lastfull = f
491
481 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
492 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
482 acceptremote, followcopies):
493 acceptremote, followcopies):
483 """
494 """
484 Merge p1 and p2 with ancestor pa and generate merge action list
495 Merge p1 and p2 with ancestor pa and generate merge action list
485
496
486 branchmerge and force are as passed in to update
497 branchmerge and force are as passed in to update
487 partial = function to filter file lists
498 partial = function to filter file lists
488 acceptremote = accept the incoming changes without prompting
499 acceptremote = accept the incoming changes without prompting
489 """
500 """
490
501
491 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
502 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
492
503
493 # manifests fetched in order are going to be faster, so prime the caches
504 # manifests fetched in order are going to be faster, so prime the caches
494 [x.manifest() for x in
505 [x.manifest() for x in
495 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
506 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
496
507
497 if followcopies:
508 if followcopies:
498 ret = copies.mergecopies(repo, wctx, p2, pa)
509 ret = copies.mergecopies(repo, wctx, p2, pa)
499 copy, movewithdir, diverge, renamedelete = ret
510 copy, movewithdir, diverge, renamedelete = ret
500
511
501 repo.ui.note(_("resolving manifests\n"))
512 repo.ui.note(_("resolving manifests\n"))
502 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
513 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
503 % (bool(branchmerge), bool(force), bool(partial)))
514 % (bool(branchmerge), bool(force), bool(partial)))
504 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
515 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
505
516
506 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
517 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
507 copied = set(copy.values())
518 copied = set(copy.values())
508 copied.update(movewithdir.values())
519 copied.update(movewithdir.values())
509
520
510 if '.hgsubstate' in m1:
521 if '.hgsubstate' in m1:
511 # check whether sub state is modified
522 # check whether sub state is modified
512 for s in sorted(wctx.substate):
523 for s in sorted(wctx.substate):
513 if wctx.sub(s).dirty():
524 if wctx.sub(s).dirty():
514 m1['.hgsubstate'] += '+'
525 m1['.hgsubstate'] += '+'
515 break
526 break
516
527
517 # Compare manifests
528 # Compare manifests
518 diff = m1.diff(m2)
529 diff = m1.diff(m2)
519
530
520 actions = {}
531 actions = {}
521 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
532 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
522 if partial and not partial(f):
533 if partial and not partial(f):
523 continue
534 continue
524 if n1 and n2: # file exists on both local and remote side
535 if n1 and n2: # file exists on both local and remote side
525 if f not in ma:
536 if f not in ma:
526 fa = copy.get(f, None)
537 fa = copy.get(f, None)
527 if fa is not None:
538 if fa is not None:
528 actions[f] = ('m', (f, f, fa, False, pa.node()),
539 actions[f] = ('m', (f, f, fa, False, pa.node()),
529 "both renamed from " + fa)
540 "both renamed from " + fa)
530 else:
541 else:
531 actions[f] = ('m', (f, f, None, False, pa.node()),
542 actions[f] = ('m', (f, f, None, False, pa.node()),
532 "both created")
543 "both created")
533 else:
544 else:
534 a = ma[f]
545 a = ma[f]
535 fla = ma.flags(f)
546 fla = ma.flags(f)
536 nol = 'l' not in fl1 + fl2 + fla
547 nol = 'l' not in fl1 + fl2 + fla
537 if n2 == a and fl2 == fla:
548 if n2 == a and fl2 == fla:
538 actions[f] = ('k' , (), "remote unchanged")
549 actions[f] = ('k' , (), "remote unchanged")
539 elif n1 == a and fl1 == fla: # local unchanged - use remote
550 elif n1 == a and fl1 == fla: # local unchanged - use remote
540 if n1 == n2: # optimization: keep local content
551 if n1 == n2: # optimization: keep local content
541 actions[f] = ('e', (fl2,), "update permissions")
552 actions[f] = ('e', (fl2,), "update permissions")
542 else:
553 else:
543 actions[f] = ('g', (fl2,), "remote is newer")
554 actions[f] = ('g', (fl2,), "remote is newer")
544 elif nol and n2 == a: # remote only changed 'x'
555 elif nol and n2 == a: # remote only changed 'x'
545 actions[f] = ('e', (fl2,), "update permissions")
556 actions[f] = ('e', (fl2,), "update permissions")
546 elif nol and n1 == a: # local only changed 'x'
557 elif nol and n1 == a: # local only changed 'x'
547 actions[f] = ('g', (fl1,), "remote is newer")
558 actions[f] = ('g', (fl1,), "remote is newer")
548 else: # both changed something
559 else: # both changed something
549 actions[f] = ('m', (f, f, f, False, pa.node()),
560 actions[f] = ('m', (f, f, f, False, pa.node()),
550 "versions differ")
561 "versions differ")
551 elif n1: # file exists only on local side
562 elif n1: # file exists only on local side
552 if f in copied:
563 if f in copied:
553 pass # we'll deal with it on m2 side
564 pass # we'll deal with it on m2 side
554 elif f in movewithdir: # directory rename, move local
565 elif f in movewithdir: # directory rename, move local
555 f2 = movewithdir[f]
566 f2 = movewithdir[f]
556 if f2 in m2:
567 if f2 in m2:
557 actions[f2] = ('m', (f, f2, None, True, pa.node()),
568 actions[f2] = ('m', (f, f2, None, True, pa.node()),
558 "remote directory rename, both created")
569 "remote directory rename, both created")
559 else:
570 else:
560 actions[f2] = ('dm', (f, fl1),
571 actions[f2] = ('dm', (f, fl1),
561 "remote directory rename - move from " + f)
572 "remote directory rename - move from " + f)
562 elif f in copy:
573 elif f in copy:
563 f2 = copy[f]
574 f2 = copy[f]
564 actions[f] = ('m', (f, f2, f2, False, pa.node()),
575 actions[f] = ('m', (f, f2, f2, False, pa.node()),
565 "local copied/moved from " + f2)
576 "local copied/moved from " + f2)
566 elif f in ma: # clean, a different, no remote
577 elif f in ma: # clean, a different, no remote
567 if n1 != ma[f]:
578 if n1 != ma[f]:
568 if acceptremote:
579 if acceptremote:
569 actions[f] = ('r', None, "remote delete")
580 actions[f] = ('r', None, "remote delete")
570 else:
581 else:
571 actions[f] = ('cd', None, "prompt changed/deleted")
582 actions[f] = ('cd', None, "prompt changed/deleted")
572 elif n1[20:] == 'a':
583 elif n1[20:] == 'a':
573 # This extra 'a' is added by working copy manifest to mark
584 # This extra 'a' is added by working copy manifest to mark
574 # the file as locally added. We should forget it instead of
585 # the file as locally added. We should forget it instead of
575 # deleting it.
586 # deleting it.
576 actions[f] = ('f', None, "remote deleted")
587 actions[f] = ('f', None, "remote deleted")
577 else:
588 else:
578 actions[f] = ('r', None, "other deleted")
589 actions[f] = ('r', None, "other deleted")
579 elif n2: # file exists only on remote side
590 elif n2: # file exists only on remote side
580 if f in copied:
591 if f in copied:
581 pass # we'll deal with it on m1 side
592 pass # we'll deal with it on m1 side
582 elif f in movewithdir:
593 elif f in movewithdir:
583 f2 = movewithdir[f]
594 f2 = movewithdir[f]
584 if f2 in m1:
595 if f2 in m1:
585 actions[f2] = ('m', (f2, f, None, False, pa.node()),
596 actions[f2] = ('m', (f2, f, None, False, pa.node()),
586 "local directory rename, both created")
597 "local directory rename, both created")
587 else:
598 else:
588 actions[f2] = ('dg', (f, fl2),
599 actions[f2] = ('dg', (f, fl2),
589 "local directory rename - get from " + f)
600 "local directory rename - get from " + f)
590 elif f in copy:
601 elif f in copy:
591 f2 = copy[f]
602 f2 = copy[f]
592 if f2 in m2:
603 if f2 in m2:
593 actions[f] = ('m', (f2, f, f2, False, pa.node()),
604 actions[f] = ('m', (f2, f, f2, False, pa.node()),
594 "remote copied from " + f2)
605 "remote copied from " + f2)
595 else:
606 else:
596 actions[f] = ('m', (f2, f, f2, True, pa.node()),
607 actions[f] = ('m', (f2, f, f2, True, pa.node()),
597 "remote moved from " + f2)
608 "remote moved from " + f2)
598 elif f not in ma:
609 elif f not in ma:
599 # local unknown, remote created: the logic is described by the
610 # local unknown, remote created: the logic is described by the
600 # following table:
611 # following table:
601 #
612 #
602 # force branchmerge different | action
613 # force branchmerge different | action
603 # n * * | create
614 # n * * | create
604 # y n * | create
615 # y n * | create
605 # y y n | create
616 # y y n | create
606 # y y y | merge
617 # y y y | merge
607 #
618 #
608 # Checking whether the files are different is expensive, so we
619 # Checking whether the files are different is expensive, so we
609 # don't do that when we can avoid it.
620 # don't do that when we can avoid it.
610 if not force:
621 if not force:
611 actions[f] = ('c', (fl2,), "remote created")
622 actions[f] = ('c', (fl2,), "remote created")
612 elif not branchmerge:
623 elif not branchmerge:
613 actions[f] = ('c', (fl2,), "remote created")
624 actions[f] = ('c', (fl2,), "remote created")
614 else:
625 else:
615 actions[f] = ('cm', (fl2, pa.node()),
626 actions[f] = ('cm', (fl2, pa.node()),
616 "remote created, get or merge")
627 "remote created, get or merge")
617 elif n2 != ma[f]:
628 elif n2 != ma[f]:
618 if acceptremote:
629 if acceptremote:
619 actions[f] = ('c', (fl2,), "remote recreating")
630 actions[f] = ('c', (fl2,), "remote recreating")
620 else:
631 else:
621 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
632 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
622
633
623 return actions, diverge, renamedelete
634 return actions, diverge, renamedelete
624
635
625 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
636 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
626 """Resolves false conflicts where the nodeid changed but the content
637 """Resolves false conflicts where the nodeid changed but the content
627 remained the same."""
638 remained the same."""
628
639
629 for f, (m, args, msg) in actions.items():
640 for f, (m, args, msg) in actions.items():
630 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
641 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
631 # local did change but ended up with same content
642 # local did change but ended up with same content
632 actions[f] = 'r', None, "prompt same"
643 actions[f] = 'r', None, "prompt same"
633 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
644 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
634 # remote did change but ended up with same content
645 # remote did change but ended up with same content
635 del actions[f] # don't get = keep local deleted
646 del actions[f] # don't get = keep local deleted
636
647
637 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
648 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
638 acceptremote, followcopies):
649 acceptremote, followcopies):
639 "Calculate the actions needed to merge mctx into wctx using ancestors"
650 "Calculate the actions needed to merge mctx into wctx using ancestors"
640
651
641 if len(ancestors) == 1: # default
652 if len(ancestors) == 1: # default
642 actions, diverge, renamedelete = manifestmerge(
653 actions, diverge, renamedelete = manifestmerge(
643 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
654 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
644 acceptremote, followcopies)
655 acceptremote, followcopies)
645 _checkunknownfiles(repo, wctx, mctx, force, actions)
656 _checkunknownfiles(repo, wctx, mctx, force, actions)
646
657
647 else: # only when merge.preferancestor=* - the default
658 else: # only when merge.preferancestor=* - the default
648 repo.ui.note(
659 repo.ui.note(
649 _("note: merging %s and %s using bids from ancestors %s\n") %
660 _("note: merging %s and %s using bids from ancestors %s\n") %
650 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
661 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
651
662
652 # Call for bids
663 # Call for bids
653 fbids = {} # mapping filename to bids (action method to list af actions)
664 fbids = {} # mapping filename to bids (action method to list af actions)
654 diverge, renamedelete = None, None
665 diverge, renamedelete = None, None
655 for ancestor in ancestors:
666 for ancestor in ancestors:
656 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
667 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
657 actions, diverge1, renamedelete1 = manifestmerge(
668 actions, diverge1, renamedelete1 = manifestmerge(
658 repo, wctx, mctx, ancestor, branchmerge, force, partial,
669 repo, wctx, mctx, ancestor, branchmerge, force, partial,
659 acceptremote, followcopies)
670 acceptremote, followcopies)
660 _checkunknownfiles(repo, wctx, mctx, force, actions)
671 _checkunknownfiles(repo, wctx, mctx, force, actions)
661
672
662 # Track the shortest set of warning on the theory that bid
673 # Track the shortest set of warning on the theory that bid
663 # merge will correctly incorporate more information
674 # merge will correctly incorporate more information
664 if diverge is None or len(diverge1) < len(diverge):
675 if diverge is None or len(diverge1) < len(diverge):
665 diverge = diverge1
676 diverge = diverge1
666 if renamedelete is None or len(renamedelete) < len(renamedelete1):
677 if renamedelete is None or len(renamedelete) < len(renamedelete1):
667 renamedelete = renamedelete1
678 renamedelete = renamedelete1
668
679
669 for f, a in sorted(actions.iteritems()):
680 for f, a in sorted(actions.iteritems()):
670 m, args, msg = a
681 m, args, msg = a
671 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
682 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
672 if f in fbids:
683 if f in fbids:
673 d = fbids[f]
684 d = fbids[f]
674 if m in d:
685 if m in d:
675 d[m].append(a)
686 d[m].append(a)
676 else:
687 else:
677 d[m] = [a]
688 d[m] = [a]
678 else:
689 else:
679 fbids[f] = {m: [a]}
690 fbids[f] = {m: [a]}
680
691
681 # Pick the best bid for each file
692 # Pick the best bid for each file
682 repo.ui.note(_('\nauction for merging merge bids\n'))
693 repo.ui.note(_('\nauction for merging merge bids\n'))
683 actions = {}
694 actions = {}
684 for f, bids in sorted(fbids.items()):
695 for f, bids in sorted(fbids.items()):
685 # bids is a mapping from action method to list af actions
696 # bids is a mapping from action method to list af actions
686 # Consensus?
697 # Consensus?
687 if len(bids) == 1: # all bids are the same kind of method
698 if len(bids) == 1: # all bids are the same kind of method
688 m, l = bids.items()[0]
699 m, l = bids.items()[0]
689 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
700 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
690 repo.ui.note(" %s: consensus for %s\n" % (f, m))
701 repo.ui.note(" %s: consensus for %s\n" % (f, m))
691 actions[f] = l[0]
702 actions[f] = l[0]
692 continue
703 continue
693 # If keep is an option, just do it.
704 # If keep is an option, just do it.
694 if 'k' in bids:
705 if 'k' in bids:
695 repo.ui.note(" %s: picking 'keep' action\n" % f)
706 repo.ui.note(" %s: picking 'keep' action\n" % f)
696 actions[f] = bids['k'][0]
707 actions[f] = bids['k'][0]
697 continue
708 continue
698 # If there are gets and they all agree [how could they not?], do it.
709 # If there are gets and they all agree [how could they not?], do it.
699 if 'g' in bids:
710 if 'g' in bids:
700 ga0 = bids['g'][0]
711 ga0 = bids['g'][0]
701 if all(a == ga0 for a in bids['g'][1:]):
712 if all(a == ga0 for a in bids['g'][1:]):
702 repo.ui.note(" %s: picking 'get' action\n" % f)
713 repo.ui.note(" %s: picking 'get' action\n" % f)
703 actions[f] = ga0
714 actions[f] = ga0
704 continue
715 continue
705 # TODO: Consider other simple actions such as mode changes
716 # TODO: Consider other simple actions such as mode changes
706 # Handle inefficient democrazy.
717 # Handle inefficient democrazy.
707 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
718 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
708 for m, l in sorted(bids.items()):
719 for m, l in sorted(bids.items()):
709 for _f, args, msg in l:
720 for _f, args, msg in l:
710 repo.ui.note(' %s -> %s\n' % (msg, m))
721 repo.ui.note(' %s -> %s\n' % (msg, m))
711 # Pick random action. TODO: Instead, prompt user when resolving
722 # Pick random action. TODO: Instead, prompt user when resolving
712 m, l = bids.items()[0]
723 m, l = bids.items()[0]
713 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
724 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
714 (f, m))
725 (f, m))
715 actions[f] = l[0]
726 actions[f] = l[0]
716 continue
727 continue
717 repo.ui.note(_('end of auction\n\n'))
728 repo.ui.note(_('end of auction\n\n'))
718
729
719 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
730 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
720
731
721 if wctx.rev() is None:
732 if wctx.rev() is None:
722 fractions = _forgetremoved(wctx, mctx, branchmerge)
733 fractions = _forgetremoved(wctx, mctx, branchmerge)
723 actions.update(fractions)
734 actions.update(fractions)
724
735
725 return actions, diverge, renamedelete
736 return actions, diverge, renamedelete
726
737
727 def batchremove(repo, actions):
738 def batchremove(repo, actions):
728 """apply removes to the working directory
739 """apply removes to the working directory
729
740
730 yields tuples for progress updates
741 yields tuples for progress updates
731 """
742 """
732 verbose = repo.ui.verbose
743 verbose = repo.ui.verbose
733 unlink = util.unlinkpath
744 unlink = util.unlinkpath
734 wjoin = repo.wjoin
745 wjoin = repo.wjoin
735 audit = repo.wvfs.audit
746 audit = repo.wvfs.audit
736 i = 0
747 i = 0
737 for f, args, msg in actions:
748 for f, args, msg in actions:
738 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
749 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
739 if verbose:
750 if verbose:
740 repo.ui.note(_("removing %s\n") % f)
751 repo.ui.note(_("removing %s\n") % f)
741 audit(f)
752 audit(f)
742 try:
753 try:
743 unlink(wjoin(f), ignoremissing=True)
754 unlink(wjoin(f), ignoremissing=True)
744 except OSError as inst:
755 except OSError as inst:
745 repo.ui.warn(_("update failed to remove %s: %s!\n") %
756 repo.ui.warn(_("update failed to remove %s: %s!\n") %
746 (f, inst.strerror))
757 (f, inst.strerror))
747 if i == 100:
758 if i == 100:
748 yield i, f
759 yield i, f
749 i = 0
760 i = 0
750 i += 1
761 i += 1
751 if i > 0:
762 if i > 0:
752 yield i, f
763 yield i, f
753
764
754 def batchget(repo, mctx, actions):
765 def batchget(repo, mctx, actions):
755 """apply gets to the working directory
766 """apply gets to the working directory
756
767
757 mctx is the context to get from
768 mctx is the context to get from
758
769
759 yields tuples for progress updates
770 yields tuples for progress updates
760 """
771 """
761 verbose = repo.ui.verbose
772 verbose = repo.ui.verbose
762 fctx = mctx.filectx
773 fctx = mctx.filectx
763 wwrite = repo.wwrite
774 wwrite = repo.wwrite
764 i = 0
775 i = 0
765 for f, args, msg in actions:
776 for f, args, msg in actions:
766 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
777 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
767 if verbose:
778 if verbose:
768 repo.ui.note(_("getting %s\n") % f)
779 repo.ui.note(_("getting %s\n") % f)
769 wwrite(f, fctx(f).data(), args[0])
780 wwrite(f, fctx(f).data(), args[0])
770 if i == 100:
781 if i == 100:
771 yield i, f
782 yield i, f
772 i = 0
783 i = 0
773 i += 1
784 i += 1
774 if i > 0:
785 if i > 0:
775 yield i, f
786 yield i, f
776
787
777 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
788 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
778 """apply the merge action list to the working directory
789 """apply the merge action list to the working directory
779
790
780 wctx is the working copy context
791 wctx is the working copy context
781 mctx is the context to be merged into the working copy
792 mctx is the context to be merged into the working copy
782
793
783 Return a tuple of counts (updated, merged, removed, unresolved) that
794 Return a tuple of counts (updated, merged, removed, unresolved) that
784 describes how many files were affected by the update.
795 describes how many files were affected by the update.
785 """
796 """
786
797
787 updated, merged, removed, unresolved = 0, 0, 0, 0
798 updated, merged, removed, unresolved = 0, 0, 0, 0
788 ms = mergestate(repo)
799 ms = mergestate(repo)
789 ms.reset(wctx.p1().node(), mctx.node())
800 ms.reset(wctx.p1().node(), mctx.node())
790 moves = []
801 moves = []
791 for m, l in actions.items():
802 for m, l in actions.items():
792 l.sort()
803 l.sort()
793
804
794 # prescan for merges
805 # prescan for merges
795 for f, args, msg in actions['m']:
806 for f, args, msg in actions['m']:
796 f1, f2, fa, move, anc = args
807 f1, f2, fa, move, anc = args
797 if f == '.hgsubstate': # merged internally
808 if f == '.hgsubstate': # merged internally
798 continue
809 continue
799 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
810 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
800 fcl = wctx[f1]
811 fcl = wctx[f1]
801 fco = mctx[f2]
812 fco = mctx[f2]
802 actx = repo[anc]
813 actx = repo[anc]
803 if fa in actx:
814 if fa in actx:
804 fca = actx[fa]
815 fca = actx[fa]
805 else:
816 else:
806 fca = repo.filectx(f1, fileid=nullrev)
817 fca = repo.filectx(f1, fileid=nullrev)
807 ms.add(fcl, fco, fca, f)
818 ms.add(fcl, fco, fca, f)
808 if f1 != f and move:
819 if f1 != f and move:
809 moves.append(f1)
820 moves.append(f1)
810
821
811 audit = repo.wvfs.audit
822 audit = repo.wvfs.audit
812 _updating = _('updating')
823 _updating = _('updating')
813 _files = _('files')
824 _files = _('files')
814 progress = repo.ui.progress
825 progress = repo.ui.progress
815
826
816 # remove renamed files after safely stored
827 # remove renamed files after safely stored
817 for f in moves:
828 for f in moves:
818 if os.path.lexists(repo.wjoin(f)):
829 if os.path.lexists(repo.wjoin(f)):
819 repo.ui.debug("removing %s\n" % f)
830 repo.ui.debug("removing %s\n" % f)
820 audit(f)
831 audit(f)
821 util.unlinkpath(repo.wjoin(f))
832 util.unlinkpath(repo.wjoin(f))
822
833
823 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
834 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
824
835
825 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
836 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
826 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
837 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
827
838
828 # remove in parallel (must come first)
839 # remove in parallel (must come first)
829 z = 0
840 z = 0
830 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
841 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
831 for i, item in prog:
842 for i, item in prog:
832 z += i
843 z += i
833 progress(_updating, z, item=item, total=numupdates, unit=_files)
844 progress(_updating, z, item=item, total=numupdates, unit=_files)
834 removed = len(actions['r'])
845 removed = len(actions['r'])
835
846
836 # get in parallel
847 # get in parallel
837 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
848 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
838 for i, item in prog:
849 for i, item in prog:
839 z += i
850 z += i
840 progress(_updating, z, item=item, total=numupdates, unit=_files)
851 progress(_updating, z, item=item, total=numupdates, unit=_files)
841 updated = len(actions['g'])
852 updated = len(actions['g'])
842
853
843 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
854 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
844 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
855 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
845
856
846 # forget (manifest only, just log it) (must come first)
857 # forget (manifest only, just log it) (must come first)
847 for f, args, msg in actions['f']:
858 for f, args, msg in actions['f']:
848 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
859 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
849 z += 1
860 z += 1
850 progress(_updating, z, item=f, total=numupdates, unit=_files)
861 progress(_updating, z, item=f, total=numupdates, unit=_files)
851
862
852 # re-add (manifest only, just log it)
863 # re-add (manifest only, just log it)
853 for f, args, msg in actions['a']:
864 for f, args, msg in actions['a']:
854 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
865 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
855 z += 1
866 z += 1
856 progress(_updating, z, item=f, total=numupdates, unit=_files)
867 progress(_updating, z, item=f, total=numupdates, unit=_files)
857
868
858 # keep (noop, just log it)
869 # keep (noop, just log it)
859 for f, args, msg in actions['k']:
870 for f, args, msg in actions['k']:
860 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
871 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
861 # no progress
872 # no progress
862
873
863 # directory rename, move local
874 # directory rename, move local
864 for f, args, msg in actions['dm']:
875 for f, args, msg in actions['dm']:
865 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
876 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
866 z += 1
877 z += 1
867 progress(_updating, z, item=f, total=numupdates, unit=_files)
878 progress(_updating, z, item=f, total=numupdates, unit=_files)
868 f0, flags = args
879 f0, flags = args
869 repo.ui.note(_("moving %s to %s\n") % (f0, f))
880 repo.ui.note(_("moving %s to %s\n") % (f0, f))
870 audit(f)
881 audit(f)
871 repo.wwrite(f, wctx.filectx(f0).data(), flags)
882 repo.wwrite(f, wctx.filectx(f0).data(), flags)
872 util.unlinkpath(repo.wjoin(f0))
883 util.unlinkpath(repo.wjoin(f0))
873 updated += 1
884 updated += 1
874
885
875 # local directory rename, get
886 # local directory rename, get
876 for f, args, msg in actions['dg']:
887 for f, args, msg in actions['dg']:
877 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
888 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
878 z += 1
889 z += 1
879 progress(_updating, z, item=f, total=numupdates, unit=_files)
890 progress(_updating, z, item=f, total=numupdates, unit=_files)
880 f0, flags = args
891 f0, flags = args
881 repo.ui.note(_("getting %s to %s\n") % (f0, f))
892 repo.ui.note(_("getting %s to %s\n") % (f0, f))
882 repo.wwrite(f, mctx.filectx(f0).data(), flags)
893 repo.wwrite(f, mctx.filectx(f0).data(), flags)
883 updated += 1
894 updated += 1
884
895
885 # exec
896 # exec
886 for f, args, msg in actions['e']:
897 for f, args, msg in actions['e']:
887 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
898 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
888 z += 1
899 z += 1
889 progress(_updating, z, item=f, total=numupdates, unit=_files)
900 progress(_updating, z, item=f, total=numupdates, unit=_files)
890 flags, = args
901 flags, = args
891 audit(f)
902 audit(f)
892 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
903 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
893 updated += 1
904 updated += 1
894
905
895 # premerge
906 # premerge
896 tocomplete = []
907 tocomplete = []
897 for f, args, msg in actions['m']:
908 for f, args, msg in actions['m']:
898 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
909 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
899 z += 1
910 z += 1
900 progress(_updating, z, item=f, total=numupdates, unit=_files)
911 progress(_updating, z, item=f, total=numupdates, unit=_files)
901 if f == '.hgsubstate': # subrepo states need updating
912 if f == '.hgsubstate': # subrepo states need updating
902 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
913 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
903 overwrite)
914 overwrite)
904 continue
915 continue
905 audit(f)
916 audit(f)
906 complete, r = ms.preresolve(f, wctx, labels=labels)
917 complete, r = ms.preresolve(f, wctx, labels=labels)
907 if complete:
918 if complete:
908 if r is not None and r > 0:
919 if r is not None and r > 0:
909 unresolved += 1
920 unresolved += 1
910 else:
921 else:
911 if r is None:
922 if r is None:
912 updated += 1
923 updated += 1
913 else:
924 else:
914 merged += 1
925 merged += 1
915 else:
926 else:
916 numupdates += 1
927 numupdates += 1
917 tocomplete.append((f, args, msg))
928 tocomplete.append((f, args, msg))
918
929
919 # merge
930 # merge
920 for f, args, msg in tocomplete:
931 for f, args, msg in tocomplete:
921 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
932 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
922 z += 1
933 z += 1
923 progress(_updating, z, item=f, total=numupdates, unit=_files)
934 progress(_updating, z, item=f, total=numupdates, unit=_files)
924 r = ms.resolve(f, wctx, labels=labels)
935 r = ms.resolve(f, wctx, labels=labels)
925 if r is not None and r > 0:
936 if r is not None and r > 0:
926 unresolved += 1
937 unresolved += 1
927 else:
938 else:
928 if r is None:
939 if r is None:
929 updated += 1
940 updated += 1
930 else:
941 else:
931 merged += 1
942 merged += 1
932
943
933 ms.commit()
944 ms.commit()
934 progress(_updating, None, total=numupdates, unit=_files)
945 progress(_updating, None, total=numupdates, unit=_files)
935
946
936 return updated, merged, removed, unresolved
947 return updated, merged, removed, unresolved
937
948
938 def recordupdates(repo, actions, branchmerge):
949 def recordupdates(repo, actions, branchmerge):
939 "record merge actions to the dirstate"
950 "record merge actions to the dirstate"
940 # remove (must come first)
951 # remove (must come first)
941 for f, args, msg in actions['r']:
952 for f, args, msg in actions['r']:
942 if branchmerge:
953 if branchmerge:
943 repo.dirstate.remove(f)
954 repo.dirstate.remove(f)
944 else:
955 else:
945 repo.dirstate.drop(f)
956 repo.dirstate.drop(f)
946
957
947 # forget (must come first)
958 # forget (must come first)
948 for f, args, msg in actions['f']:
959 for f, args, msg in actions['f']:
949 repo.dirstate.drop(f)
960 repo.dirstate.drop(f)
950
961
951 # re-add
962 # re-add
952 for f, args, msg in actions['a']:
963 for f, args, msg in actions['a']:
953 if not branchmerge:
964 if not branchmerge:
954 repo.dirstate.add(f)
965 repo.dirstate.add(f)
955
966
956 # exec change
967 # exec change
957 for f, args, msg in actions['e']:
968 for f, args, msg in actions['e']:
958 repo.dirstate.normallookup(f)
969 repo.dirstate.normallookup(f)
959
970
960 # keep
971 # keep
961 for f, args, msg in actions['k']:
972 for f, args, msg in actions['k']:
962 pass
973 pass
963
974
964 # get
975 # get
965 for f, args, msg in actions['g']:
976 for f, args, msg in actions['g']:
966 if branchmerge:
977 if branchmerge:
967 repo.dirstate.otherparent(f)
978 repo.dirstate.otherparent(f)
968 else:
979 else:
969 repo.dirstate.normal(f)
980 repo.dirstate.normal(f)
970
981
971 # merge
982 # merge
972 for f, args, msg in actions['m']:
983 for f, args, msg in actions['m']:
973 f1, f2, fa, move, anc = args
984 f1, f2, fa, move, anc = args
974 if branchmerge:
985 if branchmerge:
975 # We've done a branch merge, mark this file as merged
986 # We've done a branch merge, mark this file as merged
976 # so that we properly record the merger later
987 # so that we properly record the merger later
977 repo.dirstate.merge(f)
988 repo.dirstate.merge(f)
978 if f1 != f2: # copy/rename
989 if f1 != f2: # copy/rename
979 if move:
990 if move:
980 repo.dirstate.remove(f1)
991 repo.dirstate.remove(f1)
981 if f1 != f:
992 if f1 != f:
982 repo.dirstate.copy(f1, f)
993 repo.dirstate.copy(f1, f)
983 else:
994 else:
984 repo.dirstate.copy(f2, f)
995 repo.dirstate.copy(f2, f)
985 else:
996 else:
986 # We've update-merged a locally modified file, so
997 # We've update-merged a locally modified file, so
987 # we set the dirstate to emulate a normal checkout
998 # we set the dirstate to emulate a normal checkout
988 # of that file some time in the past. Thus our
999 # of that file some time in the past. Thus our
989 # merge will appear as a normal local file
1000 # merge will appear as a normal local file
990 # modification.
1001 # modification.
991 if f2 == f: # file not locally copied/moved
1002 if f2 == f: # file not locally copied/moved
992 repo.dirstate.normallookup(f)
1003 repo.dirstate.normallookup(f)
993 if move:
1004 if move:
994 repo.dirstate.drop(f1)
1005 repo.dirstate.drop(f1)
995
1006
996 # directory rename, move local
1007 # directory rename, move local
997 for f, args, msg in actions['dm']:
1008 for f, args, msg in actions['dm']:
998 f0, flag = args
1009 f0, flag = args
999 if branchmerge:
1010 if branchmerge:
1000 repo.dirstate.add(f)
1011 repo.dirstate.add(f)
1001 repo.dirstate.remove(f0)
1012 repo.dirstate.remove(f0)
1002 repo.dirstate.copy(f0, f)
1013 repo.dirstate.copy(f0, f)
1003 else:
1014 else:
1004 repo.dirstate.normal(f)
1015 repo.dirstate.normal(f)
1005 repo.dirstate.drop(f0)
1016 repo.dirstate.drop(f0)
1006
1017
1007 # directory rename, get
1018 # directory rename, get
1008 for f, args, msg in actions['dg']:
1019 for f, args, msg in actions['dg']:
1009 f0, flag = args
1020 f0, flag = args
1010 if branchmerge:
1021 if branchmerge:
1011 repo.dirstate.add(f)
1022 repo.dirstate.add(f)
1012 repo.dirstate.copy(f0, f)
1023 repo.dirstate.copy(f0, f)
1013 else:
1024 else:
1014 repo.dirstate.normal(f)
1025 repo.dirstate.normal(f)
1015
1026
1016 def update(repo, node, branchmerge, force, partial, ancestor=None,
1027 def update(repo, node, branchmerge, force, partial, ancestor=None,
1017 mergeancestor=False, labels=None):
1028 mergeancestor=False, labels=None):
1018 """
1029 """
1019 Perform a merge between the working directory and the given node
1030 Perform a merge between the working directory and the given node
1020
1031
1021 node = the node to update to, or None if unspecified
1032 node = the node to update to, or None if unspecified
1022 branchmerge = whether to merge between branches
1033 branchmerge = whether to merge between branches
1023 force = whether to force branch merging or file overwriting
1034 force = whether to force branch merging or file overwriting
1024 partial = a function to filter file lists (dirstate not updated)
1035 partial = a function to filter file lists (dirstate not updated)
1025 mergeancestor = whether it is merging with an ancestor. If true,
1036 mergeancestor = whether it is merging with an ancestor. If true,
1026 we should accept the incoming changes for any prompts that occur.
1037 we should accept the incoming changes for any prompts that occur.
1027 If false, merging with an ancestor (fast-forward) is only allowed
1038 If false, merging with an ancestor (fast-forward) is only allowed
1028 between different named branches. This flag is used by rebase extension
1039 between different named branches. This flag is used by rebase extension
1029 as a temporary fix and should be avoided in general.
1040 as a temporary fix and should be avoided in general.
1030
1041
1031 The table below shows all the behaviors of the update command
1042 The table below shows all the behaviors of the update command
1032 given the -c and -C or no options, whether the working directory
1043 given the -c and -C or no options, whether the working directory
1033 is dirty, whether a revision is specified, and the relationship of
1044 is dirty, whether a revision is specified, and the relationship of
1034 the parent rev to the target rev (linear, on the same named
1045 the parent rev to the target rev (linear, on the same named
1035 branch, or on another named branch).
1046 branch, or on another named branch).
1036
1047
1037 This logic is tested by test-update-branches.t.
1048 This logic is tested by test-update-branches.t.
1038
1049
1039 -c -C dirty rev | linear same cross
1050 -c -C dirty rev | linear same cross
1040 n n n n | ok (1) x
1051 n n n n | ok (1) x
1041 n n n y | ok ok ok
1052 n n n y | ok ok ok
1042 n n y n | merge (2) (2)
1053 n n y n | merge (2) (2)
1043 n n y y | merge (3) (3)
1054 n n y y | merge (3) (3)
1044 n y * * | --- discard ---
1055 n y * * | --- discard ---
1045 y n y * | --- (4) ---
1056 y n y * | --- (4) ---
1046 y n n * | --- ok ---
1057 y n n * | --- ok ---
1047 y y * * | --- (5) ---
1058 y y * * | --- (5) ---
1048
1059
1049 x = can't happen
1060 x = can't happen
1050 * = don't-care
1061 * = don't-care
1051 1 = abort: not a linear update (merge or update --check to force update)
1062 1 = abort: not a linear update (merge or update --check to force update)
1052 2 = abort: uncommitted changes (commit and merge, or update --clean to
1063 2 = abort: uncommitted changes (commit and merge, or update --clean to
1053 discard changes)
1064 discard changes)
1054 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1065 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1055 4 = abort: uncommitted changes (checked in commands.py)
1066 4 = abort: uncommitted changes (checked in commands.py)
1056 5 = incompatible options (checked in commands.py)
1067 5 = incompatible options (checked in commands.py)
1057
1068
1058 Return the same tuple as applyupdates().
1069 Return the same tuple as applyupdates().
1059 """
1070 """
1060
1071
1061 onode = node
1072 onode = node
1062 wlock = repo.wlock()
1073 wlock = repo.wlock()
1063 try:
1074 try:
1064 wc = repo[None]
1075 wc = repo[None]
1065 pl = wc.parents()
1076 pl = wc.parents()
1066 p1 = pl[0]
1077 p1 = pl[0]
1067 pas = [None]
1078 pas = [None]
1068 if ancestor is not None:
1079 if ancestor is not None:
1069 pas = [repo[ancestor]]
1080 pas = [repo[ancestor]]
1070
1081
1071 if node is None:
1082 if node is None:
1072 rev, _mark, _act = destutil.destupdate(repo)
1083 rev, _mark, _act = destutil.destupdate(repo)
1073 node = repo[rev].node()
1084 node = repo[rev].node()
1074
1085
1075 overwrite = force and not branchmerge
1086 overwrite = force and not branchmerge
1076
1087
1077 p2 = repo[node]
1088 p2 = repo[node]
1078 if pas[0] is None:
1089 if pas[0] is None:
1079 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1090 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1080 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1091 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1081 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1092 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1082 else:
1093 else:
1083 pas = [p1.ancestor(p2, warn=branchmerge)]
1094 pas = [p1.ancestor(p2, warn=branchmerge)]
1084
1095
1085 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1096 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1086
1097
1087 ### check phase
1098 ### check phase
1088 if not overwrite and len(pl) > 1:
1099 if not overwrite and len(pl) > 1:
1089 raise error.Abort(_("outstanding uncommitted merge"))
1100 raise error.Abort(_("outstanding uncommitted merge"))
1090 if branchmerge:
1101 if branchmerge:
1091 if pas == [p2]:
1102 if pas == [p2]:
1092 raise error.Abort(_("merging with a working directory ancestor"
1103 raise error.Abort(_("merging with a working directory ancestor"
1093 " has no effect"))
1104 " has no effect"))
1094 elif pas == [p1]:
1105 elif pas == [p1]:
1095 if not mergeancestor and p1.branch() == p2.branch():
1106 if not mergeancestor and p1.branch() == p2.branch():
1096 raise error.Abort(_("nothing to merge"),
1107 raise error.Abort(_("nothing to merge"),
1097 hint=_("use 'hg update' "
1108 hint=_("use 'hg update' "
1098 "or check 'hg heads'"))
1109 "or check 'hg heads'"))
1099 if not force and (wc.files() or wc.deleted()):
1110 if not force and (wc.files() or wc.deleted()):
1100 raise error.Abort(_("uncommitted changes"),
1111 raise error.Abort(_("uncommitted changes"),
1101 hint=_("use 'hg status' to list changes"))
1112 hint=_("use 'hg status' to list changes"))
1102 for s in sorted(wc.substate):
1113 for s in sorted(wc.substate):
1103 wc.sub(s).bailifchanged()
1114 wc.sub(s).bailifchanged()
1104
1115
1105 elif not overwrite:
1116 elif not overwrite:
1106 if p1 == p2: # no-op update
1117 if p1 == p2: # no-op update
1107 # call the hooks and exit early
1118 # call the hooks and exit early
1108 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1119 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1109 repo.hook('update', parent1=xp2, parent2='', error=0)
1120 repo.hook('update', parent1=xp2, parent2='', error=0)
1110 return 0, 0, 0, 0
1121 return 0, 0, 0, 0
1111
1122
1112 if pas not in ([p1], [p2]): # nonlinear
1123 if pas not in ([p1], [p2]): # nonlinear
1113 dirty = wc.dirty(missing=True)
1124 dirty = wc.dirty(missing=True)
1114 if dirty or onode is None:
1125 if dirty or onode is None:
1115 # Branching is a bit strange to ensure we do the minimal
1126 # Branching is a bit strange to ensure we do the minimal
1116 # amount of call to obsolete.background.
1127 # amount of call to obsolete.background.
1117 foreground = obsolete.foreground(repo, [p1.node()])
1128 foreground = obsolete.foreground(repo, [p1.node()])
1118 # note: the <node> variable contains a random identifier
1129 # note: the <node> variable contains a random identifier
1119 if repo[node].node() in foreground:
1130 if repo[node].node() in foreground:
1120 pas = [p1] # allow updating to successors
1131 pas = [p1] # allow updating to successors
1121 elif dirty:
1132 elif dirty:
1122 msg = _("uncommitted changes")
1133 msg = _("uncommitted changes")
1123 if onode is None:
1134 if onode is None:
1124 hint = _("commit and merge, or update --clean to"
1135 hint = _("commit and merge, or update --clean to"
1125 " discard changes")
1136 " discard changes")
1126 else:
1137 else:
1127 hint = _("commit or update --clean to discard"
1138 hint = _("commit or update --clean to discard"
1128 " changes")
1139 " changes")
1129 raise error.Abort(msg, hint=hint)
1140 raise error.Abort(msg, hint=hint)
1130 else: # node is none
1141 else: # node is none
1131 msg = _("not a linear update")
1142 msg = _("not a linear update")
1132 hint = _("merge or update --check to force update")
1143 hint = _("merge or update --check to force update")
1133 raise error.Abort(msg, hint=hint)
1144 raise error.Abort(msg, hint=hint)
1134 else:
1145 else:
1135 # Allow jumping branches if clean and specific rev given
1146 # Allow jumping branches if clean and specific rev given
1136 pas = [p1]
1147 pas = [p1]
1137
1148
1138 # deprecated config: merge.followcopies
1149 # deprecated config: merge.followcopies
1139 followcopies = False
1150 followcopies = False
1140 if overwrite:
1151 if overwrite:
1141 pas = [wc]
1152 pas = [wc]
1142 elif pas == [p2]: # backwards
1153 elif pas == [p2]: # backwards
1143 pas = [wc.p1()]
1154 pas = [wc.p1()]
1144 elif not branchmerge and not wc.dirty(missing=True):
1155 elif not branchmerge and not wc.dirty(missing=True):
1145 pass
1156 pass
1146 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1157 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1147 followcopies = True
1158 followcopies = True
1148
1159
1149 ### calculate phase
1160 ### calculate phase
1150 actionbyfile, diverge, renamedelete = calculateupdates(
1161 actionbyfile, diverge, renamedelete = calculateupdates(
1151 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1162 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1152 followcopies)
1163 followcopies)
1153 # Convert to dictionary-of-lists format
1164 # Convert to dictionary-of-lists format
1154 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1165 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1155 for f, (m, args, msg) in actionbyfile.iteritems():
1166 for f, (m, args, msg) in actionbyfile.iteritems():
1156 if m not in actions:
1167 if m not in actions:
1157 actions[m] = []
1168 actions[m] = []
1158 actions[m].append((f, args, msg))
1169 actions[m].append((f, args, msg))
1159
1170
1160 if not util.checkcase(repo.path):
1171 if not util.checkcase(repo.path):
1161 # check collision between files only in p2 for clean update
1172 # check collision between files only in p2 for clean update
1162 if (not branchmerge and
1173 if (not branchmerge and
1163 (force or not wc.dirty(missing=True, branch=False))):
1174 (force or not wc.dirty(missing=True, branch=False))):
1164 _checkcollision(repo, p2.manifest(), None)
1175 _checkcollision(repo, p2.manifest(), None)
1165 else:
1176 else:
1166 _checkcollision(repo, wc.manifest(), actions)
1177 _checkcollision(repo, wc.manifest(), actions)
1167
1178
1168 # Prompt and create actions. TODO: Move this towards resolve phase.
1179 # Prompt and create actions. TODO: Move this towards resolve phase.
1169 for f, args, msg in sorted(actions['cd']):
1180 for f, args, msg in sorted(actions['cd']):
1170 if repo.ui.promptchoice(
1181 if repo.ui.promptchoice(
1171 _("local changed %s which remote deleted\n"
1182 _("local changed %s which remote deleted\n"
1172 "use (c)hanged version or (d)elete?"
1183 "use (c)hanged version or (d)elete?"
1173 "$$ &Changed $$ &Delete") % f, 0):
1184 "$$ &Changed $$ &Delete") % f, 0):
1174 actions['r'].append((f, None, "prompt delete"))
1185 actions['r'].append((f, None, "prompt delete"))
1175 else:
1186 else:
1176 actions['a'].append((f, None, "prompt keep"))
1187 actions['a'].append((f, None, "prompt keep"))
1177 del actions['cd'][:]
1188 del actions['cd'][:]
1178
1189
1179 for f, args, msg in sorted(actions['dc']):
1190 for f, args, msg in sorted(actions['dc']):
1180 flags, = args
1191 flags, = args
1181 if repo.ui.promptchoice(
1192 if repo.ui.promptchoice(
1182 _("remote changed %s which local deleted\n"
1193 _("remote changed %s which local deleted\n"
1183 "use (c)hanged version or leave (d)eleted?"
1194 "use (c)hanged version or leave (d)eleted?"
1184 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1195 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1185 actions['g'].append((f, (flags,), "prompt recreating"))
1196 actions['g'].append((f, (flags,), "prompt recreating"))
1186 del actions['dc'][:]
1197 del actions['dc'][:]
1187
1198
1188 ### apply phase
1199 ### apply phase
1189 if not branchmerge: # just jump to the new rev
1200 if not branchmerge: # just jump to the new rev
1190 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1201 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1191 if not partial:
1202 if not partial:
1192 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1203 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1193 # note that we're in the middle of an update
1204 # note that we're in the middle of an update
1194 repo.vfs.write('updatestate', p2.hex())
1205 repo.vfs.write('updatestate', p2.hex())
1195
1206
1196 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1207 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1197
1208
1198 # divergent renames
1209 # divergent renames
1199 for f, fl in sorted(diverge.iteritems()):
1210 for f, fl in sorted(diverge.iteritems()):
1200 repo.ui.warn(_("note: possible conflict - %s was renamed "
1211 repo.ui.warn(_("note: possible conflict - %s was renamed "
1201 "multiple times to:\n") % f)
1212 "multiple times to:\n") % f)
1202 for nf in fl:
1213 for nf in fl:
1203 repo.ui.warn(" %s\n" % nf)
1214 repo.ui.warn(" %s\n" % nf)
1204
1215
1205 # rename and delete
1216 # rename and delete
1206 for f, fl in sorted(renamedelete.iteritems()):
1217 for f, fl in sorted(renamedelete.iteritems()):
1207 repo.ui.warn(_("note: possible conflict - %s was deleted "
1218 repo.ui.warn(_("note: possible conflict - %s was deleted "
1208 "and renamed to:\n") % f)
1219 "and renamed to:\n") % f)
1209 for nf in fl:
1220 for nf in fl:
1210 repo.ui.warn(" %s\n" % nf)
1221 repo.ui.warn(" %s\n" % nf)
1211
1222
1212 if not partial:
1223 if not partial:
1213 repo.dirstate.beginparentchange()
1224 repo.dirstate.beginparentchange()
1214 repo.setparents(fp1, fp2)
1225 repo.setparents(fp1, fp2)
1215 recordupdates(repo, actions, branchmerge)
1226 recordupdates(repo, actions, branchmerge)
1216 # update completed, clear state
1227 # update completed, clear state
1217 util.unlink(repo.join('updatestate'))
1228 util.unlink(repo.join('updatestate'))
1218
1229
1219 if not branchmerge:
1230 if not branchmerge:
1220 repo.dirstate.setbranch(p2.branch())
1231 repo.dirstate.setbranch(p2.branch())
1221 repo.dirstate.endparentchange()
1232 repo.dirstate.endparentchange()
1222 finally:
1233 finally:
1223 wlock.release()
1234 wlock.release()
1224
1235
1225 if not partial:
1236 if not partial:
1226 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1237 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1227 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1238 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1228 repo._afterlock(updatehook)
1239 repo._afterlock(updatehook)
1229 return stats
1240 return stats
1230
1241
1231 def graft(repo, ctx, pctx, labels):
1242 def graft(repo, ctx, pctx, labels):
1232 """Do a graft-like merge.
1243 """Do a graft-like merge.
1233
1244
1234 This is a merge where the merge ancestor is chosen such that one
1245 This is a merge where the merge ancestor is chosen such that one
1235 or more changesets are grafted onto the current changeset. In
1246 or more changesets are grafted onto the current changeset. In
1236 addition to the merge, this fixes up the dirstate to include only
1247 addition to the merge, this fixes up the dirstate to include only
1237 a single parent and tries to duplicate any renames/copies
1248 a single parent and tries to duplicate any renames/copies
1238 appropriately.
1249 appropriately.
1239
1250
1240 ctx - changeset to rebase
1251 ctx - changeset to rebase
1241 pctx - merge base, usually ctx.p1()
1252 pctx - merge base, usually ctx.p1()
1242 labels - merge labels eg ['local', 'graft']
1253 labels - merge labels eg ['local', 'graft']
1243
1254
1244 """
1255 """
1245 # If we're grafting a descendant onto an ancestor, be sure to pass
1256 # If we're grafting a descendant onto an ancestor, be sure to pass
1246 # mergeancestor=True to update. This does two things: 1) allows the merge if
1257 # mergeancestor=True to update. This does two things: 1) allows the merge if
1247 # the destination is the same as the parent of the ctx (so we can use graft
1258 # the destination is the same as the parent of the ctx (so we can use graft
1248 # to copy commits), and 2) informs update that the incoming changes are
1259 # to copy commits), and 2) informs update that the incoming changes are
1249 # newer than the destination so it doesn't prompt about "remote changed foo
1260 # newer than the destination so it doesn't prompt about "remote changed foo
1250 # which local deleted".
1261 # which local deleted".
1251 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1262 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1252
1263
1253 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1264 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1254 mergeancestor=mergeancestor, labels=labels)
1265 mergeancestor=mergeancestor, labels=labels)
1255
1266
1256 # drop the second merge parent
1267 # drop the second merge parent
1257 repo.dirstate.beginparentchange()
1268 repo.dirstate.beginparentchange()
1258 repo.setparents(repo['.'].node(), nullid)
1269 repo.setparents(repo['.'].node(), nullid)
1259 repo.dirstate.write()
1270 repo.dirstate.write()
1260 # fix up dirstate for copies and renames
1271 # fix up dirstate for copies and renames
1261 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1272 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1262 repo.dirstate.endparentchange()
1273 repo.dirstate.endparentchange()
1263 return stats
1274 return stats
@@ -1,346 +1,346 b''
1 #require icasefs
1 #require icasefs
2
2
3 ################################
3 ################################
4 test for branch merging
4 test for branch merging
5 ################################
5 ################################
6
6
7 test for rename awareness of case-folding collision check:
7 test for rename awareness of case-folding collision check:
8
8
9 (1) colliding file is one renamed from collided file:
9 (1) colliding file is one renamed from collided file:
10 this is also case for issue3370.
10 this is also case for issue3370.
11
11
12 $ hg init branch_merge_renaming
12 $ hg init branch_merge_renaming
13 $ cd branch_merge_renaming
13 $ cd branch_merge_renaming
14
14
15 $ echo a > a
15 $ echo a > a
16 $ hg add a
16 $ hg add a
17 $ echo b > b
17 $ echo b > b
18 $ hg add b
18 $ hg add b
19 $ hg commit -m '#0'
19 $ hg commit -m '#0'
20 $ hg tag -l A
20 $ hg tag -l A
21 $ hg rename a tmp
21 $ hg rename a tmp
22 $ hg rename tmp A
22 $ hg rename tmp A
23 $ hg commit -m '#1'
23 $ hg commit -m '#1'
24 $ hg tag -l B
24 $ hg tag -l B
25 $ hg update -q 0
25 $ hg update -q 0
26 $ touch x
26 $ touch x
27 $ hg add x
27 $ hg add x
28 $ hg commit -m '#2'
28 $ hg commit -m '#2'
29 created new head
29 created new head
30 $ hg tag -l C
30 $ hg tag -l C
31
31
32 $ hg merge -q
32 $ hg merge -q
33 $ hg status -A
33 $ hg status -A
34 M A
34 M A
35 R a
35 R a
36 C b
36 C b
37 C x
37 C x
38
38
39 $ hg update -q --clean 1
39 $ hg update -q --clean 1
40 $ hg merge -q
40 $ hg merge -q
41 $ hg status -A
41 $ hg status -A
42 M x
42 M x
43 C A
43 C A
44 C b
44 C b
45 $ hg commit -m '(D)'
45 $ hg commit -m '(D)'
46 $ hg tag -l D
46 $ hg tag -l D
47
47
48 additional test for issue3452:
48 additional test for issue3452:
49
49
50 | this assumes the history below.
50 | this assumes the history below.
51 |
51 |
52 | (A) -- (C) -- (E) -------
52 | (A) -- (C) -- (E) -------
53 | \ \ \
53 | \ \ \
54 | \ \ \
54 | \ \ \
55 | (B) -- (D) -- (F) -- (G)
55 | (B) -- (D) -- (F) -- (G)
56 |
56 |
57 | A: add file 'a'
57 | A: add file 'a'
58 | B: rename from 'a' to 'A'
58 | B: rename from 'a' to 'A'
59 | C: add 'x' (or operation other than modification of 'a')
59 | C: add 'x' (or operation other than modification of 'a')
60 | D: merge C into B
60 | D: merge C into B
61 | E: modify 'a'
61 | E: modify 'a'
62 | F: modify 'A'
62 | F: modify 'A'
63 | G: merge E into F
63 | G: merge E into F
64 |
64 |
65 | issue3452 occurs when (B) is recorded before (C)
65 | issue3452 occurs when (B) is recorded before (C)
66
66
67 $ hg update -q --clean C
67 $ hg update -q --clean C
68 $ echo "modify 'a' at (E)" > a
68 $ echo "modify 'a' at (E)" > a
69 $ echo "modify 'b' at (E)" > b
69 $ echo "modify 'b' at (E)" > b
70 $ hg commit -m '(E)'
70 $ hg commit -m '(E)'
71 created new head
71 created new head
72 $ hg tag -l E
72 $ hg tag -l E
73
73
74 $ hg update -q --clean D
74 $ hg update -q --clean D
75 $ echo "modify 'A' at (F)" > A
75 $ echo "modify 'A' at (F)" > A
76 $ hg commit -m '(F)'
76 $ hg commit -m '(F)'
77 $ hg tag -l F
77 $ hg tag -l F
78
78
79 $ hg merge -q --tool internal:other E
79 $ hg merge -q --tool internal:other E
80 $ hg status -A
80 $ hg status -A
81 M A
81 M A
82 a
82 a
83 M b
83 M b
84 C x
84 C x
85 $ cat A
85 $ cat A
86 modify 'a' at (E)
86 modify 'a' at (E)
87
87
88 test also the case that (B) is recorded after (C), to prevent
88 test also the case that (B) is recorded after (C), to prevent
89 regression by changes in the future.
89 regression by changes in the future.
90
90
91 to avoid unexpected (successful) behavior by filelog unification,
91 to avoid unexpected (successful) behavior by filelog unification,
92 target file is not 'a'/'A' but 'b'/'B' in this case.
92 target file is not 'a'/'A' but 'b'/'B' in this case.
93
93
94 $ hg update -q --clean A
94 $ hg update -q --clean A
95 $ hg rename b tmp
95 $ hg rename b tmp
96 $ hg rename tmp B
96 $ hg rename tmp B
97 $ hg commit -m '(B1)'
97 $ hg commit -m '(B1)'
98 created new head
98 created new head
99 $ hg tag -l B1
99 $ hg tag -l B1
100
100
101 $ hg merge -q C
101 $ hg merge -q C
102 $ hg status -A
102 $ hg status -A
103 M x
103 M x
104 C B
104 C B
105 C a
105 C a
106 $ hg commit -m '(D1)'
106 $ hg commit -m '(D1)'
107 $ hg tag -l D1
107 $ hg tag -l D1
108
108
109 $ echo "modify 'B' at (F1)" > B
109 $ echo "modify 'B' at (F1)" > B
110 $ hg commit -m '(F1)'
110 $ hg commit -m '(F1)'
111 $ hg tag -l F1
111 $ hg tag -l F1
112
112
113 $ hg merge -q --tool internal:other E
113 $ hg merge -q --tool internal:other E
114 $ hg status -A
114 $ hg status -A
115 M B
115 M B
116 b
116 b
117 M a
117 M a
118 C x
118 C x
119 $ cat B
119 $ cat B
120 modify 'b' at (E)
120 modify 'b' at (E)
121
121
122 $ cd ..
122 $ cd ..
123
123
124 (2) colliding file is not related to collided file
124 (2) colliding file is not related to collided file
125
125
126 $ hg init branch_merge_collding
126 $ hg init branch_merge_collding
127 $ cd branch_merge_collding
127 $ cd branch_merge_collding
128
128
129 $ echo a > a
129 $ echo a > a
130 $ hg add a
130 $ hg add a
131 $ hg commit -m '#0'
131 $ hg commit -m '#0'
132 $ hg remove a
132 $ hg remove a
133 $ hg commit -m '#1'
133 $ hg commit -m '#1'
134 $ echo A > A
134 $ echo A > A
135 $ hg add A
135 $ hg add A
136 $ hg commit -m '#2'
136 $ hg commit -m '#2'
137 $ hg update --clean 0
137 $ hg update --clean 0
138 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
138 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
139 $ echo x > x
139 $ echo x > x
140 $ hg add x
140 $ hg add x
141 $ hg commit -m '#3'
141 $ hg commit -m '#3'
142 created new head
142 created new head
143 $ echo 'modified at #4' > a
143 $ echo 'modified at #4' > a
144 $ hg commit -m '#4'
144 $ hg commit -m '#4'
145
145
146 $ hg merge
146 $ hg merge
147 abort: case-folding collision between a and A
147 abort: case-folding collision between a and A
148 [255]
148 [255]
149 $ hg parents --template '{rev}\n'
149 $ hg parents --template '{rev}\n'
150 4
150 4
151 $ hg status -A
151 $ hg status -A
152 C a
152 C a
153 C x
153 C x
154 $ cat a
154 $ cat a
155 modified at #4
155 modified at #4
156
156
157 $ hg update --clean 2
157 $ hg update --clean 2
158 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
158 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
159 $ hg merge
159 $ hg merge
160 abort: case-folding collision between a and A
160 abort: case-folding collision between a and A
161 [255]
161 [255]
162 $ hg parents --template '{rev}\n'
162 $ hg parents --template '{rev}\n'
163 2
163 2
164 $ hg status -A
164 $ hg status -A
165 C A
165 C A
166 $ cat A
166 $ cat A
167 A
167 A
168
168
169 test for deletion awareness of case-folding collision check (issue3648):
169 test for deletion awareness of case-folding collision check (issue3648):
170 revision '#3' doesn't change 'a', so 'a' should be recognized as
170 revision '#3' doesn't change 'a', so 'a' should be recognized as
171 safely removed in merging between #2 and #3.
171 safely removed in merging between #2 and #3.
172
172
173 $ hg update --clean 3
173 $ hg update --clean 3
174 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
174 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
175 $ hg merge 2
175 $ hg merge 2
176 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
176 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
177 (branch merge, don't forget to commit)
177 (branch merge, don't forget to commit)
178 $ hg status -A
178 $ hg status -A
179 M A
179 M A
180 R a
180 R a
181 C x
181 C x
182
182
183 $ hg update --clean 2
183 $ hg update --clean 2
184 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
184 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
185 $ hg merge 3
185 $ hg merge 3
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 (branch merge, don't forget to commit)
187 (branch merge, don't forget to commit)
188 $ hg status -A
188 $ hg status -A
189 M x
189 M x
190 C A
190 C A
191
191
192 $ cd ..
192 $ cd ..
193
193
194 Prepare for tests of directory case-folding collisions
194 Prepare for tests of directory case-folding collisions
195
195
196 $ hg init directory-casing
196 $ hg init directory-casing
197 $ cd directory-casing
197 $ cd directory-casing
198 $ touch 0 # test: file without directory
198 $ touch 0 # test: file without directory
199 $ mkdir 0a
199 $ mkdir 0a
200 $ touch 0a/f
200 $ touch 0a/f
201 $ mkdir aA
201 $ mkdir aA
202 $ touch aA/a
202 $ touch aA/a
203 $ hg ci -Aqm0
203 $ hg ci -Aqm0
204
204
205 Directory/file case-folding collision:
205 Directory/file case-folding collision:
206
206
207 $ hg up -q null
207 $ hg up -q null
208 $ touch 00 # test: starts as '0'
208 $ touch 00 # test: starts as '0'
209 $ mkdir 000 # test: starts as '0'
209 $ mkdir 000 # test: starts as '0'
210 $ touch 000/f
210 $ touch 000/f
211 $ touch Aa # test: collision with 'aA/a'
211 $ touch Aa # test: collision with 'aA/a'
212 $ hg ci -Aqm1
212 $ hg ci -Aqm1
213
213
214 $ hg merge 0
214 $ hg merge 0
215 abort: Not a directory: '$TESTTMP/directory-casing/aA/a'
215 abort: case-folding collision between Aa and directory of aA/a
216 [255]
216 [255]
217 (note: no collision between 0 and 00 or 000/f)
217 (note: no collision between 0 and 00 or 000/f)
218
218
219 Directory case-folding collision:
219 Directory case-folding collision:
220
220
221 $ hg up -qC null
221 $ hg up -qC null
222 $ hg --config extensions.purge= purge
222 $ hg --config extensions.purge= purge
223 $ mkdir 0A0
223 $ mkdir 0A0
224 $ touch 0A0/f # test: starts as '0a'
224 $ touch 0A0/f # test: starts as '0a'
225 $ mkdir Aa
225 $ mkdir Aa
226 $ touch Aa/b # test: collision with 'aA/a'
226 $ touch Aa/b # test: collision with 'aA/a'
227 $ hg ci -Aqm2
227 $ hg ci -Aqm2
228
228
229 $ hg merge 0
229 $ hg merge 0
230 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 (branch merge, don't forget to commit)
231 (branch merge, don't forget to commit)
232
232
233 $ cd ..
233 $ cd ..
234
234
235 ################################
235 ################################
236 test for linear updates
236 test for linear updates
237 ################################
237 ################################
238
238
239 test for rename awareness of case-folding collision check:
239 test for rename awareness of case-folding collision check:
240
240
241 (1) colliding file is one renamed from collided file
241 (1) colliding file is one renamed from collided file
242
242
243 $ hg init linearupdate_renameaware_1
243 $ hg init linearupdate_renameaware_1
244 $ cd linearupdate_renameaware_1
244 $ cd linearupdate_renameaware_1
245
245
246 $ echo a > a
246 $ echo a > a
247 $ hg add a
247 $ hg add a
248 $ hg commit -m '#0'
248 $ hg commit -m '#0'
249 $ hg rename a tmp
249 $ hg rename a tmp
250 $ hg rename tmp A
250 $ hg rename tmp A
251 $ hg commit -m '#1'
251 $ hg commit -m '#1'
252
252
253 $ hg update 0
253 $ hg update 0
254 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
254 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
255
255
256 $ echo 'this is added line' >> a
256 $ echo 'this is added line' >> a
257 $ hg update 1
257 $ hg update 1
258 merging a and A to A
258 merging a and A to A
259 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
259 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
260 $ hg status -A
260 $ hg status -A
261 M A
261 M A
262 $ cat A
262 $ cat A
263 a
263 a
264 this is added line
264 this is added line
265
265
266 $ cd ..
266 $ cd ..
267
267
268 (2) colliding file is not related to collided file
268 (2) colliding file is not related to collided file
269
269
270 $ hg init linearupdate_renameaware_2
270 $ hg init linearupdate_renameaware_2
271 $ cd linearupdate_renameaware_2
271 $ cd linearupdate_renameaware_2
272
272
273 $ echo a > a
273 $ echo a > a
274 $ hg add a
274 $ hg add a
275 $ hg commit -m '#0'
275 $ hg commit -m '#0'
276 $ hg remove a
276 $ hg remove a
277 $ hg commit -m '#1'
277 $ hg commit -m '#1'
278 $ echo A > A
278 $ echo A > A
279 $ hg add A
279 $ hg add A
280 $ hg commit -m '#2'
280 $ hg commit -m '#2'
281
281
282 $ hg update 0
282 $ hg update 0
283 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
283 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
284 $ hg parents --template '{rev}\n'
284 $ hg parents --template '{rev}\n'
285 0
285 0
286 $ hg status -A
286 $ hg status -A
287 C a
287 C a
288 $ cat A
288 $ cat A
289 a
289 a
290 $ hg up -qC 2
290 $ hg up -qC 2
291
291
292 $ hg update --check 0
292 $ hg update --check 0
293 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
293 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
294 $ hg parents --template '{rev}\n'
294 $ hg parents --template '{rev}\n'
295 0
295 0
296 $ hg status -A
296 $ hg status -A
297 C a
297 C a
298 $ cat a
298 $ cat a
299 a
299 a
300
300
301 $ hg update --clean 2
301 $ hg update --clean 2
302 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
302 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
303 $ hg parents --template '{rev}\n'
303 $ hg parents --template '{rev}\n'
304 2
304 2
305 $ hg status -A
305 $ hg status -A
306 C A
306 C A
307 $ cat A
307 $ cat A
308 A
308 A
309
309
310 $ cd ..
310 $ cd ..
311
311
312 (3) colliding file is not related to collided file: added in working dir
312 (3) colliding file is not related to collided file: added in working dir
313
313
314 $ hg init linearupdate_renameaware_3
314 $ hg init linearupdate_renameaware_3
315 $ cd linearupdate_renameaware_3
315 $ cd linearupdate_renameaware_3
316
316
317 $ echo a > a
317 $ echo a > a
318 $ hg add a
318 $ hg add a
319 $ hg commit -m '#0'
319 $ hg commit -m '#0'
320 $ hg rename a b
320 $ hg rename a b
321 $ hg commit -m '#1'
321 $ hg commit -m '#1'
322 $ hg update 0
322 $ hg update 0
323 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
323 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
324
324
325 $ echo B > B
325 $ echo B > B
326 $ hg add B
326 $ hg add B
327 $ hg status
327 $ hg status
328 A B
328 A B
329 $ hg update
329 $ hg update
330 abort: case-folding collision between b and B
330 abort: case-folding collision between b and B
331 [255]
331 [255]
332
332
333 $ hg update --check
333 $ hg update --check
334 abort: uncommitted changes
334 abort: uncommitted changes
335 [255]
335 [255]
336
336
337 $ hg update --clean
337 $ hg update --clean
338 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
338 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
339 $ hg parents --template '{rev}\n'
339 $ hg parents --template '{rev}\n'
340 1
340 1
341 $ hg status -A
341 $ hg status -A
342 C b
342 C b
343 $ cat b
343 $ cat b
344 a
344 a
345
345
346 $ cd ..
346 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now