##// END OF EJS Templates
update: "deprecate" call to 'merge.update' without a destination...
Pierre-Yves David -
r26682:08b06824 default
parent child Browse files
Show More
@@ -1,1274 +1,1277
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
481 # check case-folding of directories
482 foldprefix = unfoldprefix = lastfull = ''
482 foldprefix = unfoldprefix = lastfull = ''
483 for fold, f in sorted(foldmap.items()):
483 for fold, f in sorted(foldmap.items()):
484 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
484 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
485 # the folded prefix matches but actual casing is different
485 # the folded prefix matches but actual casing is different
486 raise error.Abort(_("case-folding collision between "
486 raise error.Abort(_("case-folding collision between "
487 "%s and directory of %s") % (lastfull, f))
487 "%s and directory of %s") % (lastfull, f))
488 foldprefix = fold + '/'
488 foldprefix = fold + '/'
489 unfoldprefix = f + '/'
489 unfoldprefix = f + '/'
490 lastfull = f
490 lastfull = f
491
491
492 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
492 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
493 acceptremote, followcopies):
493 acceptremote, followcopies):
494 """
494 """
495 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
496
496
497 branchmerge and force are as passed in to update
497 branchmerge and force are as passed in to update
498 partial = function to filter file lists
498 partial = function to filter file lists
499 acceptremote = accept the incoming changes without prompting
499 acceptremote = accept the incoming changes without prompting
500 """
500 """
501
501
502 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
502 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
503
503
504 # 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
505 [x.manifest() for x in
505 [x.manifest() for x in
506 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
506 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
507
507
508 if followcopies:
508 if followcopies:
509 ret = copies.mergecopies(repo, wctx, p2, pa)
509 ret = copies.mergecopies(repo, wctx, p2, pa)
510 copy, movewithdir, diverge, renamedelete = ret
510 copy, movewithdir, diverge, renamedelete = ret
511
511
512 repo.ui.note(_("resolving manifests\n"))
512 repo.ui.note(_("resolving manifests\n"))
513 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
513 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
514 % (bool(branchmerge), bool(force), bool(partial)))
514 % (bool(branchmerge), bool(force), bool(partial)))
515 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))
516
516
517 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
517 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
518 copied = set(copy.values())
518 copied = set(copy.values())
519 copied.update(movewithdir.values())
519 copied.update(movewithdir.values())
520
520
521 if '.hgsubstate' in m1:
521 if '.hgsubstate' in m1:
522 # check whether sub state is modified
522 # check whether sub state is modified
523 for s in sorted(wctx.substate):
523 for s in sorted(wctx.substate):
524 if wctx.sub(s).dirty():
524 if wctx.sub(s).dirty():
525 m1['.hgsubstate'] += '+'
525 m1['.hgsubstate'] += '+'
526 break
526 break
527
527
528 # Compare manifests
528 # Compare manifests
529 diff = m1.diff(m2)
529 diff = m1.diff(m2)
530
530
531 actions = {}
531 actions = {}
532 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
532 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
533 if partial and not partial(f):
533 if partial and not partial(f):
534 continue
534 continue
535 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
536 if f not in ma:
536 if f not in ma:
537 fa = copy.get(f, None)
537 fa = copy.get(f, None)
538 if fa is not None:
538 if fa is not None:
539 actions[f] = ('m', (f, f, fa, False, pa.node()),
539 actions[f] = ('m', (f, f, fa, False, pa.node()),
540 "both renamed from " + fa)
540 "both renamed from " + fa)
541 else:
541 else:
542 actions[f] = ('m', (f, f, None, False, pa.node()),
542 actions[f] = ('m', (f, f, None, False, pa.node()),
543 "both created")
543 "both created")
544 else:
544 else:
545 a = ma[f]
545 a = ma[f]
546 fla = ma.flags(f)
546 fla = ma.flags(f)
547 nol = 'l' not in fl1 + fl2 + fla
547 nol = 'l' not in fl1 + fl2 + fla
548 if n2 == a and fl2 == fla:
548 if n2 == a and fl2 == fla:
549 actions[f] = ('k' , (), "remote unchanged")
549 actions[f] = ('k' , (), "remote unchanged")
550 elif n1 == a and fl1 == fla: # local unchanged - use remote
550 elif n1 == a and fl1 == fla: # local unchanged - use remote
551 if n1 == n2: # optimization: keep local content
551 if n1 == n2: # optimization: keep local content
552 actions[f] = ('e', (fl2,), "update permissions")
552 actions[f] = ('e', (fl2,), "update permissions")
553 else:
553 else:
554 actions[f] = ('g', (fl2,), "remote is newer")
554 actions[f] = ('g', (fl2,), "remote is newer")
555 elif nol and n2 == a: # remote only changed 'x'
555 elif nol and n2 == a: # remote only changed 'x'
556 actions[f] = ('e', (fl2,), "update permissions")
556 actions[f] = ('e', (fl2,), "update permissions")
557 elif nol and n1 == a: # local only changed 'x'
557 elif nol and n1 == a: # local only changed 'x'
558 actions[f] = ('g', (fl1,), "remote is newer")
558 actions[f] = ('g', (fl1,), "remote is newer")
559 else: # both changed something
559 else: # both changed something
560 actions[f] = ('m', (f, f, f, False, pa.node()),
560 actions[f] = ('m', (f, f, f, False, pa.node()),
561 "versions differ")
561 "versions differ")
562 elif n1: # file exists only on local side
562 elif n1: # file exists only on local side
563 if f in copied:
563 if f in copied:
564 pass # we'll deal with it on m2 side
564 pass # we'll deal with it on m2 side
565 elif f in movewithdir: # directory rename, move local
565 elif f in movewithdir: # directory rename, move local
566 f2 = movewithdir[f]
566 f2 = movewithdir[f]
567 if f2 in m2:
567 if f2 in m2:
568 actions[f2] = ('m', (f, f2, None, True, pa.node()),
568 actions[f2] = ('m', (f, f2, None, True, pa.node()),
569 "remote directory rename, both created")
569 "remote directory rename, both created")
570 else:
570 else:
571 actions[f2] = ('dm', (f, fl1),
571 actions[f2] = ('dm', (f, fl1),
572 "remote directory rename - move from " + f)
572 "remote directory rename - move from " + f)
573 elif f in copy:
573 elif f in copy:
574 f2 = copy[f]
574 f2 = copy[f]
575 actions[f] = ('m', (f, f2, f2, False, pa.node()),
575 actions[f] = ('m', (f, f2, f2, False, pa.node()),
576 "local copied/moved from " + f2)
576 "local copied/moved from " + f2)
577 elif f in ma: # clean, a different, no remote
577 elif f in ma: # clean, a different, no remote
578 if n1 != ma[f]:
578 if n1 != ma[f]:
579 if acceptremote:
579 if acceptremote:
580 actions[f] = ('r', None, "remote delete")
580 actions[f] = ('r', None, "remote delete")
581 else:
581 else:
582 actions[f] = ('cd', None, "prompt changed/deleted")
582 actions[f] = ('cd', None, "prompt changed/deleted")
583 elif n1[20:] == 'a':
583 elif n1[20:] == 'a':
584 # This extra 'a' is added by working copy manifest to mark
584 # This extra 'a' is added by working copy manifest to mark
585 # the file as locally added. We should forget it instead of
585 # the file as locally added. We should forget it instead of
586 # deleting it.
586 # deleting it.
587 actions[f] = ('f', None, "remote deleted")
587 actions[f] = ('f', None, "remote deleted")
588 else:
588 else:
589 actions[f] = ('r', None, "other deleted")
589 actions[f] = ('r', None, "other deleted")
590 elif n2: # file exists only on remote side
590 elif n2: # file exists only on remote side
591 if f in copied:
591 if f in copied:
592 pass # we'll deal with it on m1 side
592 pass # we'll deal with it on m1 side
593 elif f in movewithdir:
593 elif f in movewithdir:
594 f2 = movewithdir[f]
594 f2 = movewithdir[f]
595 if f2 in m1:
595 if f2 in m1:
596 actions[f2] = ('m', (f2, f, None, False, pa.node()),
596 actions[f2] = ('m', (f2, f, None, False, pa.node()),
597 "local directory rename, both created")
597 "local directory rename, both created")
598 else:
598 else:
599 actions[f2] = ('dg', (f, fl2),
599 actions[f2] = ('dg', (f, fl2),
600 "local directory rename - get from " + f)
600 "local directory rename - get from " + f)
601 elif f in copy:
601 elif f in copy:
602 f2 = copy[f]
602 f2 = copy[f]
603 if f2 in m2:
603 if f2 in m2:
604 actions[f] = ('m', (f2, f, f2, False, pa.node()),
604 actions[f] = ('m', (f2, f, f2, False, pa.node()),
605 "remote copied from " + f2)
605 "remote copied from " + f2)
606 else:
606 else:
607 actions[f] = ('m', (f2, f, f2, True, pa.node()),
607 actions[f] = ('m', (f2, f, f2, True, pa.node()),
608 "remote moved from " + f2)
608 "remote moved from " + f2)
609 elif f not in ma:
609 elif f not in ma:
610 # local unknown, remote created: the logic is described by the
610 # local unknown, remote created: the logic is described by the
611 # following table:
611 # following table:
612 #
612 #
613 # force branchmerge different | action
613 # force branchmerge different | action
614 # n * * | create
614 # n * * | create
615 # y n * | create
615 # y n * | create
616 # y y n | create
616 # y y n | create
617 # y y y | merge
617 # y y y | merge
618 #
618 #
619 # Checking whether the files are different is expensive, so we
619 # Checking whether the files are different is expensive, so we
620 # don't do that when we can avoid it.
620 # don't do that when we can avoid it.
621 if not force:
621 if not force:
622 actions[f] = ('c', (fl2,), "remote created")
622 actions[f] = ('c', (fl2,), "remote created")
623 elif not branchmerge:
623 elif not branchmerge:
624 actions[f] = ('c', (fl2,), "remote created")
624 actions[f] = ('c', (fl2,), "remote created")
625 else:
625 else:
626 actions[f] = ('cm', (fl2, pa.node()),
626 actions[f] = ('cm', (fl2, pa.node()),
627 "remote created, get or merge")
627 "remote created, get or merge")
628 elif n2 != ma[f]:
628 elif n2 != ma[f]:
629 if acceptremote:
629 if acceptremote:
630 actions[f] = ('c', (fl2,), "remote recreating")
630 actions[f] = ('c', (fl2,), "remote recreating")
631 else:
631 else:
632 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
632 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
633
633
634 return actions, diverge, renamedelete
634 return actions, diverge, renamedelete
635
635
636 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
636 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
637 """Resolves false conflicts where the nodeid changed but the content
637 """Resolves false conflicts where the nodeid changed but the content
638 remained the same."""
638 remained the same."""
639
639
640 for f, (m, args, msg) in actions.items():
640 for f, (m, args, msg) in actions.items():
641 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]):
642 # local did change but ended up with same content
642 # local did change but ended up with same content
643 actions[f] = 'r', None, "prompt same"
643 actions[f] = 'r', None, "prompt same"
644 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]):
645 # remote did change but ended up with same content
645 # remote did change but ended up with same content
646 del actions[f] # don't get = keep local deleted
646 del actions[f] # don't get = keep local deleted
647
647
648 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
648 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
649 acceptremote, followcopies):
649 acceptremote, followcopies):
650 "Calculate the actions needed to merge mctx into wctx using ancestors"
650 "Calculate the actions needed to merge mctx into wctx using ancestors"
651
651
652 if len(ancestors) == 1: # default
652 if len(ancestors) == 1: # default
653 actions, diverge, renamedelete = manifestmerge(
653 actions, diverge, renamedelete = manifestmerge(
654 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
654 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
655 acceptremote, followcopies)
655 acceptremote, followcopies)
656 _checkunknownfiles(repo, wctx, mctx, force, actions)
656 _checkunknownfiles(repo, wctx, mctx, force, actions)
657
657
658 else: # only when merge.preferancestor=* - the default
658 else: # only when merge.preferancestor=* - the default
659 repo.ui.note(
659 repo.ui.note(
660 _("note: merging %s and %s using bids from ancestors %s\n") %
660 _("note: merging %s and %s using bids from ancestors %s\n") %
661 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
661 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
662
662
663 # Call for bids
663 # Call for bids
664 fbids = {} # mapping filename to bids (action method to list af actions)
664 fbids = {} # mapping filename to bids (action method to list af actions)
665 diverge, renamedelete = None, None
665 diverge, renamedelete = None, None
666 for ancestor in ancestors:
666 for ancestor in ancestors:
667 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
667 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
668 actions, diverge1, renamedelete1 = manifestmerge(
668 actions, diverge1, renamedelete1 = manifestmerge(
669 repo, wctx, mctx, ancestor, branchmerge, force, partial,
669 repo, wctx, mctx, ancestor, branchmerge, force, partial,
670 acceptremote, followcopies)
670 acceptremote, followcopies)
671 _checkunknownfiles(repo, wctx, mctx, force, actions)
671 _checkunknownfiles(repo, wctx, mctx, force, actions)
672
672
673 # Track the shortest set of warning on the theory that bid
673 # Track the shortest set of warning on the theory that bid
674 # merge will correctly incorporate more information
674 # merge will correctly incorporate more information
675 if diverge is None or len(diverge1) < len(diverge):
675 if diverge is None or len(diverge1) < len(diverge):
676 diverge = diverge1
676 diverge = diverge1
677 if renamedelete is None or len(renamedelete) < len(renamedelete1):
677 if renamedelete is None or len(renamedelete) < len(renamedelete1):
678 renamedelete = renamedelete1
678 renamedelete = renamedelete1
679
679
680 for f, a in sorted(actions.iteritems()):
680 for f, a in sorted(actions.iteritems()):
681 m, args, msg = a
681 m, args, msg = a
682 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
682 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
683 if f in fbids:
683 if f in fbids:
684 d = fbids[f]
684 d = fbids[f]
685 if m in d:
685 if m in d:
686 d[m].append(a)
686 d[m].append(a)
687 else:
687 else:
688 d[m] = [a]
688 d[m] = [a]
689 else:
689 else:
690 fbids[f] = {m: [a]}
690 fbids[f] = {m: [a]}
691
691
692 # Pick the best bid for each file
692 # Pick the best bid for each file
693 repo.ui.note(_('\nauction for merging merge bids\n'))
693 repo.ui.note(_('\nauction for merging merge bids\n'))
694 actions = {}
694 actions = {}
695 for f, bids in sorted(fbids.items()):
695 for f, bids in sorted(fbids.items()):
696 # bids is a mapping from action method to list af actions
696 # bids is a mapping from action method to list af actions
697 # Consensus?
697 # Consensus?
698 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
699 m, l = bids.items()[0]
699 m, l = bids.items()[0]
700 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
701 repo.ui.note(" %s: consensus for %s\n" % (f, m))
701 repo.ui.note(" %s: consensus for %s\n" % (f, m))
702 actions[f] = l[0]
702 actions[f] = l[0]
703 continue
703 continue
704 # If keep is an option, just do it.
704 # If keep is an option, just do it.
705 if 'k' in bids:
705 if 'k' in bids:
706 repo.ui.note(" %s: picking 'keep' action\n" % f)
706 repo.ui.note(" %s: picking 'keep' action\n" % f)
707 actions[f] = bids['k'][0]
707 actions[f] = bids['k'][0]
708 continue
708 continue
709 # 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.
710 if 'g' in bids:
710 if 'g' in bids:
711 ga0 = bids['g'][0]
711 ga0 = bids['g'][0]
712 if all(a == ga0 for a in bids['g'][1:]):
712 if all(a == ga0 for a in bids['g'][1:]):
713 repo.ui.note(" %s: picking 'get' action\n" % f)
713 repo.ui.note(" %s: picking 'get' action\n" % f)
714 actions[f] = ga0
714 actions[f] = ga0
715 continue
715 continue
716 # TODO: Consider other simple actions such as mode changes
716 # TODO: Consider other simple actions such as mode changes
717 # Handle inefficient democrazy.
717 # Handle inefficient democrazy.
718 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
718 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
719 for m, l in sorted(bids.items()):
719 for m, l in sorted(bids.items()):
720 for _f, args, msg in l:
720 for _f, args, msg in l:
721 repo.ui.note(' %s -> %s\n' % (msg, m))
721 repo.ui.note(' %s -> %s\n' % (msg, m))
722 # Pick random action. TODO: Instead, prompt user when resolving
722 # Pick random action. TODO: Instead, prompt user when resolving
723 m, l = bids.items()[0]
723 m, l = bids.items()[0]
724 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
724 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
725 (f, m))
725 (f, m))
726 actions[f] = l[0]
726 actions[f] = l[0]
727 continue
727 continue
728 repo.ui.note(_('end of auction\n\n'))
728 repo.ui.note(_('end of auction\n\n'))
729
729
730 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
730 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
731
731
732 if wctx.rev() is None:
732 if wctx.rev() is None:
733 fractions = _forgetremoved(wctx, mctx, branchmerge)
733 fractions = _forgetremoved(wctx, mctx, branchmerge)
734 actions.update(fractions)
734 actions.update(fractions)
735
735
736 return actions, diverge, renamedelete
736 return actions, diverge, renamedelete
737
737
738 def batchremove(repo, actions):
738 def batchremove(repo, actions):
739 """apply removes to the working directory
739 """apply removes to the working directory
740
740
741 yields tuples for progress updates
741 yields tuples for progress updates
742 """
742 """
743 verbose = repo.ui.verbose
743 verbose = repo.ui.verbose
744 unlink = util.unlinkpath
744 unlink = util.unlinkpath
745 wjoin = repo.wjoin
745 wjoin = repo.wjoin
746 audit = repo.wvfs.audit
746 audit = repo.wvfs.audit
747 i = 0
747 i = 0
748 for f, args, msg in actions:
748 for f, args, msg in actions:
749 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
749 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
750 if verbose:
750 if verbose:
751 repo.ui.note(_("removing %s\n") % f)
751 repo.ui.note(_("removing %s\n") % f)
752 audit(f)
752 audit(f)
753 try:
753 try:
754 unlink(wjoin(f), ignoremissing=True)
754 unlink(wjoin(f), ignoremissing=True)
755 except OSError as inst:
755 except OSError as inst:
756 repo.ui.warn(_("update failed to remove %s: %s!\n") %
756 repo.ui.warn(_("update failed to remove %s: %s!\n") %
757 (f, inst.strerror))
757 (f, inst.strerror))
758 if i == 100:
758 if i == 100:
759 yield i, f
759 yield i, f
760 i = 0
760 i = 0
761 i += 1
761 i += 1
762 if i > 0:
762 if i > 0:
763 yield i, f
763 yield i, f
764
764
765 def batchget(repo, mctx, actions):
765 def batchget(repo, mctx, actions):
766 """apply gets to the working directory
766 """apply gets to the working directory
767
767
768 mctx is the context to get from
768 mctx is the context to get from
769
769
770 yields tuples for progress updates
770 yields tuples for progress updates
771 """
771 """
772 verbose = repo.ui.verbose
772 verbose = repo.ui.verbose
773 fctx = mctx.filectx
773 fctx = mctx.filectx
774 wwrite = repo.wwrite
774 wwrite = repo.wwrite
775 i = 0
775 i = 0
776 for f, args, msg in actions:
776 for f, args, msg in actions:
777 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
777 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
778 if verbose:
778 if verbose:
779 repo.ui.note(_("getting %s\n") % f)
779 repo.ui.note(_("getting %s\n") % f)
780 wwrite(f, fctx(f).data(), args[0])
780 wwrite(f, fctx(f).data(), args[0])
781 if i == 100:
781 if i == 100:
782 yield i, f
782 yield i, f
783 i = 0
783 i = 0
784 i += 1
784 i += 1
785 if i > 0:
785 if i > 0:
786 yield i, f
786 yield i, f
787
787
788 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
788 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
789 """apply the merge action list to the working directory
789 """apply the merge action list to the working directory
790
790
791 wctx is the working copy context
791 wctx is the working copy context
792 mctx is the context to be merged into the working copy
792 mctx is the context to be merged into the working copy
793
793
794 Return a tuple of counts (updated, merged, removed, unresolved) that
794 Return a tuple of counts (updated, merged, removed, unresolved) that
795 describes how many files were affected by the update.
795 describes how many files were affected by the update.
796 """
796 """
797
797
798 updated, merged, removed, unresolved = 0, 0, 0, 0
798 updated, merged, removed, unresolved = 0, 0, 0, 0
799 ms = mergestate(repo)
799 ms = mergestate(repo)
800 ms.reset(wctx.p1().node(), mctx.node())
800 ms.reset(wctx.p1().node(), mctx.node())
801 moves = []
801 moves = []
802 for m, l in actions.items():
802 for m, l in actions.items():
803 l.sort()
803 l.sort()
804
804
805 # prescan for merges
805 # prescan for merges
806 for f, args, msg in actions['m']:
806 for f, args, msg in actions['m']:
807 f1, f2, fa, move, anc = args
807 f1, f2, fa, move, anc = args
808 if f == '.hgsubstate': # merged internally
808 if f == '.hgsubstate': # merged internally
809 continue
809 continue
810 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))
811 fcl = wctx[f1]
811 fcl = wctx[f1]
812 fco = mctx[f2]
812 fco = mctx[f2]
813 actx = repo[anc]
813 actx = repo[anc]
814 if fa in actx:
814 if fa in actx:
815 fca = actx[fa]
815 fca = actx[fa]
816 else:
816 else:
817 fca = repo.filectx(f1, fileid=nullrev)
817 fca = repo.filectx(f1, fileid=nullrev)
818 ms.add(fcl, fco, fca, f)
818 ms.add(fcl, fco, fca, f)
819 if f1 != f and move:
819 if f1 != f and move:
820 moves.append(f1)
820 moves.append(f1)
821
821
822 audit = repo.wvfs.audit
822 audit = repo.wvfs.audit
823 _updating = _('updating')
823 _updating = _('updating')
824 _files = _('files')
824 _files = _('files')
825 progress = repo.ui.progress
825 progress = repo.ui.progress
826
826
827 # remove renamed files after safely stored
827 # remove renamed files after safely stored
828 for f in moves:
828 for f in moves:
829 if os.path.lexists(repo.wjoin(f)):
829 if os.path.lexists(repo.wjoin(f)):
830 repo.ui.debug("removing %s\n" % f)
830 repo.ui.debug("removing %s\n" % f)
831 audit(f)
831 audit(f)
832 util.unlinkpath(repo.wjoin(f))
832 util.unlinkpath(repo.wjoin(f))
833
833
834 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')
835
835
836 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
836 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
837 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
837 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
838
838
839 # remove in parallel (must come first)
839 # remove in parallel (must come first)
840 z = 0
840 z = 0
841 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
841 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
842 for i, item in prog:
842 for i, item in prog:
843 z += i
843 z += i
844 progress(_updating, z, item=item, total=numupdates, unit=_files)
844 progress(_updating, z, item=item, total=numupdates, unit=_files)
845 removed = len(actions['r'])
845 removed = len(actions['r'])
846
846
847 # get in parallel
847 # get in parallel
848 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'])
849 for i, item in prog:
849 for i, item in prog:
850 z += i
850 z += i
851 progress(_updating, z, item=item, total=numupdates, unit=_files)
851 progress(_updating, z, item=item, total=numupdates, unit=_files)
852 updated = len(actions['g'])
852 updated = len(actions['g'])
853
853
854 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
854 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
855 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
855 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
856
856
857 # forget (manifest only, just log it) (must come first)
857 # forget (manifest only, just log it) (must come first)
858 for f, args, msg in actions['f']:
858 for f, args, msg in actions['f']:
859 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
859 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
860 z += 1
860 z += 1
861 progress(_updating, z, item=f, total=numupdates, unit=_files)
861 progress(_updating, z, item=f, total=numupdates, unit=_files)
862
862
863 # re-add (manifest only, just log it)
863 # re-add (manifest only, just log it)
864 for f, args, msg in actions['a']:
864 for f, args, msg in actions['a']:
865 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
865 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
866 z += 1
866 z += 1
867 progress(_updating, z, item=f, total=numupdates, unit=_files)
867 progress(_updating, z, item=f, total=numupdates, unit=_files)
868
868
869 # keep (noop, just log it)
869 # keep (noop, just log it)
870 for f, args, msg in actions['k']:
870 for f, args, msg in actions['k']:
871 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
871 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
872 # no progress
872 # no progress
873
873
874 # directory rename, move local
874 # directory rename, move local
875 for f, args, msg in actions['dm']:
875 for f, args, msg in actions['dm']:
876 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
876 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
877 z += 1
877 z += 1
878 progress(_updating, z, item=f, total=numupdates, unit=_files)
878 progress(_updating, z, item=f, total=numupdates, unit=_files)
879 f0, flags = args
879 f0, flags = args
880 repo.ui.note(_("moving %s to %s\n") % (f0, f))
880 repo.ui.note(_("moving %s to %s\n") % (f0, f))
881 audit(f)
881 audit(f)
882 repo.wwrite(f, wctx.filectx(f0).data(), flags)
882 repo.wwrite(f, wctx.filectx(f0).data(), flags)
883 util.unlinkpath(repo.wjoin(f0))
883 util.unlinkpath(repo.wjoin(f0))
884 updated += 1
884 updated += 1
885
885
886 # local directory rename, get
886 # local directory rename, get
887 for f, args, msg in actions['dg']:
887 for f, args, msg in actions['dg']:
888 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
888 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
889 z += 1
889 z += 1
890 progress(_updating, z, item=f, total=numupdates, unit=_files)
890 progress(_updating, z, item=f, total=numupdates, unit=_files)
891 f0, flags = args
891 f0, flags = args
892 repo.ui.note(_("getting %s to %s\n") % (f0, f))
892 repo.ui.note(_("getting %s to %s\n") % (f0, f))
893 repo.wwrite(f, mctx.filectx(f0).data(), flags)
893 repo.wwrite(f, mctx.filectx(f0).data(), flags)
894 updated += 1
894 updated += 1
895
895
896 # exec
896 # exec
897 for f, args, msg in actions['e']:
897 for f, args, msg in actions['e']:
898 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
898 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
899 z += 1
899 z += 1
900 progress(_updating, z, item=f, total=numupdates, unit=_files)
900 progress(_updating, z, item=f, total=numupdates, unit=_files)
901 flags, = args
901 flags, = args
902 audit(f)
902 audit(f)
903 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
903 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
904 updated += 1
904 updated += 1
905
905
906 # premerge
906 # premerge
907 tocomplete = []
907 tocomplete = []
908 for f, args, msg in actions['m']:
908 for f, args, msg in actions['m']:
909 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
909 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
910 z += 1
910 z += 1
911 progress(_updating, z, item=f, total=numupdates, unit=_files)
911 progress(_updating, z, item=f, total=numupdates, unit=_files)
912 if f == '.hgsubstate': # subrepo states need updating
912 if f == '.hgsubstate': # subrepo states need updating
913 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
913 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
914 overwrite)
914 overwrite)
915 continue
915 continue
916 audit(f)
916 audit(f)
917 complete, r = ms.preresolve(f, wctx, labels=labels)
917 complete, r = ms.preresolve(f, wctx, labels=labels)
918 if complete:
918 if complete:
919 if r is not None and r > 0:
919 if r is not None and r > 0:
920 unresolved += 1
920 unresolved += 1
921 else:
921 else:
922 if r is None:
922 if r is None:
923 updated += 1
923 updated += 1
924 else:
924 else:
925 merged += 1
925 merged += 1
926 else:
926 else:
927 numupdates += 1
927 numupdates += 1
928 tocomplete.append((f, args, msg))
928 tocomplete.append((f, args, msg))
929
929
930 # merge
930 # merge
931 for f, args, msg in tocomplete:
931 for f, args, msg in tocomplete:
932 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
932 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
933 z += 1
933 z += 1
934 progress(_updating, z, item=f, total=numupdates, unit=_files)
934 progress(_updating, z, item=f, total=numupdates, unit=_files)
935 r = ms.resolve(f, wctx, labels=labels)
935 r = ms.resolve(f, wctx, labels=labels)
936 if r is not None and r > 0:
936 if r is not None and r > 0:
937 unresolved += 1
937 unresolved += 1
938 else:
938 else:
939 if r is None:
939 if r is None:
940 updated += 1
940 updated += 1
941 else:
941 else:
942 merged += 1
942 merged += 1
943
943
944 ms.commit()
944 ms.commit()
945 progress(_updating, None, total=numupdates, unit=_files)
945 progress(_updating, None, total=numupdates, unit=_files)
946
946
947 return updated, merged, removed, unresolved
947 return updated, merged, removed, unresolved
948
948
949 def recordupdates(repo, actions, branchmerge):
949 def recordupdates(repo, actions, branchmerge):
950 "record merge actions to the dirstate"
950 "record merge actions to the dirstate"
951 # remove (must come first)
951 # remove (must come first)
952 for f, args, msg in actions['r']:
952 for f, args, msg in actions['r']:
953 if branchmerge:
953 if branchmerge:
954 repo.dirstate.remove(f)
954 repo.dirstate.remove(f)
955 else:
955 else:
956 repo.dirstate.drop(f)
956 repo.dirstate.drop(f)
957
957
958 # forget (must come first)
958 # forget (must come first)
959 for f, args, msg in actions['f']:
959 for f, args, msg in actions['f']:
960 repo.dirstate.drop(f)
960 repo.dirstate.drop(f)
961
961
962 # re-add
962 # re-add
963 for f, args, msg in actions['a']:
963 for f, args, msg in actions['a']:
964 if not branchmerge:
964 if not branchmerge:
965 repo.dirstate.add(f)
965 repo.dirstate.add(f)
966
966
967 # exec change
967 # exec change
968 for f, args, msg in actions['e']:
968 for f, args, msg in actions['e']:
969 repo.dirstate.normallookup(f)
969 repo.dirstate.normallookup(f)
970
970
971 # keep
971 # keep
972 for f, args, msg in actions['k']:
972 for f, args, msg in actions['k']:
973 pass
973 pass
974
974
975 # get
975 # get
976 for f, args, msg in actions['g']:
976 for f, args, msg in actions['g']:
977 if branchmerge:
977 if branchmerge:
978 repo.dirstate.otherparent(f)
978 repo.dirstate.otherparent(f)
979 else:
979 else:
980 repo.dirstate.normal(f)
980 repo.dirstate.normal(f)
981
981
982 # merge
982 # merge
983 for f, args, msg in actions['m']:
983 for f, args, msg in actions['m']:
984 f1, f2, fa, move, anc = args
984 f1, f2, fa, move, anc = args
985 if branchmerge:
985 if branchmerge:
986 # We've done a branch merge, mark this file as merged
986 # We've done a branch merge, mark this file as merged
987 # so that we properly record the merger later
987 # so that we properly record the merger later
988 repo.dirstate.merge(f)
988 repo.dirstate.merge(f)
989 if f1 != f2: # copy/rename
989 if f1 != f2: # copy/rename
990 if move:
990 if move:
991 repo.dirstate.remove(f1)
991 repo.dirstate.remove(f1)
992 if f1 != f:
992 if f1 != f:
993 repo.dirstate.copy(f1, f)
993 repo.dirstate.copy(f1, f)
994 else:
994 else:
995 repo.dirstate.copy(f2, f)
995 repo.dirstate.copy(f2, f)
996 else:
996 else:
997 # We've update-merged a locally modified file, so
997 # We've update-merged a locally modified file, so
998 # we set the dirstate to emulate a normal checkout
998 # we set the dirstate to emulate a normal checkout
999 # of that file some time in the past. Thus our
999 # of that file some time in the past. Thus our
1000 # merge will appear as a normal local file
1000 # merge will appear as a normal local file
1001 # modification.
1001 # modification.
1002 if f2 == f: # file not locally copied/moved
1002 if f2 == f: # file not locally copied/moved
1003 repo.dirstate.normallookup(f)
1003 repo.dirstate.normallookup(f)
1004 if move:
1004 if move:
1005 repo.dirstate.drop(f1)
1005 repo.dirstate.drop(f1)
1006
1006
1007 # directory rename, move local
1007 # directory rename, move local
1008 for f, args, msg in actions['dm']:
1008 for f, args, msg in actions['dm']:
1009 f0, flag = args
1009 f0, flag = args
1010 if branchmerge:
1010 if branchmerge:
1011 repo.dirstate.add(f)
1011 repo.dirstate.add(f)
1012 repo.dirstate.remove(f0)
1012 repo.dirstate.remove(f0)
1013 repo.dirstate.copy(f0, f)
1013 repo.dirstate.copy(f0, f)
1014 else:
1014 else:
1015 repo.dirstate.normal(f)
1015 repo.dirstate.normal(f)
1016 repo.dirstate.drop(f0)
1016 repo.dirstate.drop(f0)
1017
1017
1018 # directory rename, get
1018 # directory rename, get
1019 for f, args, msg in actions['dg']:
1019 for f, args, msg in actions['dg']:
1020 f0, flag = args
1020 f0, flag = args
1021 if branchmerge:
1021 if branchmerge:
1022 repo.dirstate.add(f)
1022 repo.dirstate.add(f)
1023 repo.dirstate.copy(f0, f)
1023 repo.dirstate.copy(f0, f)
1024 else:
1024 else:
1025 repo.dirstate.normal(f)
1025 repo.dirstate.normal(f)
1026
1026
1027 def update(repo, node, branchmerge, force, partial, ancestor=None,
1027 def update(repo, node, branchmerge, force, partial, ancestor=None,
1028 mergeancestor=False, labels=None):
1028 mergeancestor=False, labels=None):
1029 """
1029 """
1030 Perform a merge between the working directory and the given node
1030 Perform a merge between the working directory and the given node
1031
1031
1032 node = the node to update to, or None if unspecified
1032 node = the node to update to, or None if unspecified
1033 branchmerge = whether to merge between branches
1033 branchmerge = whether to merge between branches
1034 force = whether to force branch merging or file overwriting
1034 force = whether to force branch merging or file overwriting
1035 partial = a function to filter file lists (dirstate not updated)
1035 partial = a function to filter file lists (dirstate not updated)
1036 mergeancestor = whether it is merging with an ancestor. If true,
1036 mergeancestor = whether it is merging with an ancestor. If true,
1037 we should accept the incoming changes for any prompts that occur.
1037 we should accept the incoming changes for any prompts that occur.
1038 If false, merging with an ancestor (fast-forward) is only allowed
1038 If false, merging with an ancestor (fast-forward) is only allowed
1039 between different named branches. This flag is used by rebase extension
1039 between different named branches. This flag is used by rebase extension
1040 as a temporary fix and should be avoided in general.
1040 as a temporary fix and should be avoided in general.
1041
1041
1042 The table below shows all the behaviors of the update command
1042 The table below shows all the behaviors of the update command
1043 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
1044 is dirty, whether a revision is specified, and the relationship of
1044 is dirty, whether a revision is specified, and the relationship of
1045 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
1046 branch, or on another named branch).
1046 branch, or on another named branch).
1047
1047
1048 This logic is tested by test-update-branches.t.
1048 This logic is tested by test-update-branches.t.
1049
1049
1050 -c -C dirty rev | linear same cross
1050 -c -C dirty rev | linear same cross
1051 n n n n | ok (1) x
1051 n n n n | ok (1) x
1052 n n n y | ok ok ok
1052 n n n y | ok ok ok
1053 n n y n | merge (2) (2)
1053 n n y n | merge (2) (2)
1054 n n y y | merge (3) (3)
1054 n n y y | merge (3) (3)
1055 n y * * | --- discard ---
1055 n y * * | --- discard ---
1056 y n y * | --- (4) ---
1056 y n y * | --- (4) ---
1057 y n n * | --- ok ---
1057 y n n * | --- ok ---
1058 y y * * | --- (5) ---
1058 y y * * | --- (5) ---
1059
1059
1060 x = can't happen
1060 x = can't happen
1061 * = don't-care
1061 * = don't-care
1062 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)
1063 2 = abort: uncommitted changes (commit and merge, or update --clean to
1063 2 = abort: uncommitted changes (commit and merge, or update --clean to
1064 discard changes)
1064 discard changes)
1065 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1065 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1066 4 = abort: uncommitted changes (checked in commands.py)
1066 4 = abort: uncommitted changes (checked in commands.py)
1067 5 = incompatible options (checked in commands.py)
1067 5 = incompatible options (checked in commands.py)
1068
1068
1069 Return the same tuple as applyupdates().
1069 Return the same tuple as applyupdates().
1070 """
1070 """
1071
1071
1072 onode = node
1072 onode = node
1073 wlock = repo.wlock()
1073 wlock = repo.wlock()
1074 try:
1074 try:
1075 wc = repo[None]
1075 wc = repo[None]
1076 pl = wc.parents()
1076 pl = wc.parents()
1077 p1 = pl[0]
1077 p1 = pl[0]
1078 pas = [None]
1078 pas = [None]
1079 if ancestor is not None:
1079 if ancestor is not None:
1080 pas = [repo[ancestor]]
1080 pas = [repo[ancestor]]
1081
1081
1082 if node is None:
1082 if node is None:
1083 if (repo.ui.configbool('devel', 'all-warnings')
1084 or repo.ui.configbool('devel', 'oldapi')):
1085 repo.ui.develwarn('update with no target')
1083 rev, _mark, _act = destutil.destupdate(repo)
1086 rev, _mark, _act = destutil.destupdate(repo)
1084 node = repo[rev].node()
1087 node = repo[rev].node()
1085
1088
1086 overwrite = force and not branchmerge
1089 overwrite = force and not branchmerge
1087
1090
1088 p2 = repo[node]
1091 p2 = repo[node]
1089 if pas[0] is None:
1092 if pas[0] is None:
1090 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1093 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1091 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1094 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1092 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1095 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1093 else:
1096 else:
1094 pas = [p1.ancestor(p2, warn=branchmerge)]
1097 pas = [p1.ancestor(p2, warn=branchmerge)]
1095
1098
1096 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1099 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1097
1100
1098 ### check phase
1101 ### check phase
1099 if not overwrite and len(pl) > 1:
1102 if not overwrite and len(pl) > 1:
1100 raise error.Abort(_("outstanding uncommitted merge"))
1103 raise error.Abort(_("outstanding uncommitted merge"))
1101 if branchmerge:
1104 if branchmerge:
1102 if pas == [p2]:
1105 if pas == [p2]:
1103 raise error.Abort(_("merging with a working directory ancestor"
1106 raise error.Abort(_("merging with a working directory ancestor"
1104 " has no effect"))
1107 " has no effect"))
1105 elif pas == [p1]:
1108 elif pas == [p1]:
1106 if not mergeancestor and p1.branch() == p2.branch():
1109 if not mergeancestor and p1.branch() == p2.branch():
1107 raise error.Abort(_("nothing to merge"),
1110 raise error.Abort(_("nothing to merge"),
1108 hint=_("use 'hg update' "
1111 hint=_("use 'hg update' "
1109 "or check 'hg heads'"))
1112 "or check 'hg heads'"))
1110 if not force and (wc.files() or wc.deleted()):
1113 if not force and (wc.files() or wc.deleted()):
1111 raise error.Abort(_("uncommitted changes"),
1114 raise error.Abort(_("uncommitted changes"),
1112 hint=_("use 'hg status' to list changes"))
1115 hint=_("use 'hg status' to list changes"))
1113 for s in sorted(wc.substate):
1116 for s in sorted(wc.substate):
1114 wc.sub(s).bailifchanged()
1117 wc.sub(s).bailifchanged()
1115
1118
1116 elif not overwrite:
1119 elif not overwrite:
1117 if p1 == p2: # no-op update
1120 if p1 == p2: # no-op update
1118 # call the hooks and exit early
1121 # call the hooks and exit early
1119 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1122 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1120 repo.hook('update', parent1=xp2, parent2='', error=0)
1123 repo.hook('update', parent1=xp2, parent2='', error=0)
1121 return 0, 0, 0, 0
1124 return 0, 0, 0, 0
1122
1125
1123 if pas not in ([p1], [p2]): # nonlinear
1126 if pas not in ([p1], [p2]): # nonlinear
1124 dirty = wc.dirty(missing=True)
1127 dirty = wc.dirty(missing=True)
1125 if dirty or onode is None:
1128 if dirty or onode is None:
1126 # Branching is a bit strange to ensure we do the minimal
1129 # Branching is a bit strange to ensure we do the minimal
1127 # amount of call to obsolete.background.
1130 # amount of call to obsolete.background.
1128 foreground = obsolete.foreground(repo, [p1.node()])
1131 foreground = obsolete.foreground(repo, [p1.node()])
1129 # note: the <node> variable contains a random identifier
1132 # note: the <node> variable contains a random identifier
1130 if repo[node].node() in foreground:
1133 if repo[node].node() in foreground:
1131 pas = [p1] # allow updating to successors
1134 pas = [p1] # allow updating to successors
1132 elif dirty:
1135 elif dirty:
1133 msg = _("uncommitted changes")
1136 msg = _("uncommitted changes")
1134 if onode is None:
1137 if onode is None:
1135 hint = _("commit and merge, or update --clean to"
1138 hint = _("commit and merge, or update --clean to"
1136 " discard changes")
1139 " discard changes")
1137 else:
1140 else:
1138 hint = _("commit or update --clean to discard"
1141 hint = _("commit or update --clean to discard"
1139 " changes")
1142 " changes")
1140 raise error.Abort(msg, hint=hint)
1143 raise error.Abort(msg, hint=hint)
1141 else: # node is none
1144 else: # node is none
1142 msg = _("not a linear update")
1145 msg = _("not a linear update")
1143 hint = _("merge or update --check to force update")
1146 hint = _("merge or update --check to force update")
1144 raise error.Abort(msg, hint=hint)
1147 raise error.Abort(msg, hint=hint)
1145 else:
1148 else:
1146 # Allow jumping branches if clean and specific rev given
1149 # Allow jumping branches if clean and specific rev given
1147 pas = [p1]
1150 pas = [p1]
1148
1151
1149 # deprecated config: merge.followcopies
1152 # deprecated config: merge.followcopies
1150 followcopies = False
1153 followcopies = False
1151 if overwrite:
1154 if overwrite:
1152 pas = [wc]
1155 pas = [wc]
1153 elif pas == [p2]: # backwards
1156 elif pas == [p2]: # backwards
1154 pas = [wc.p1()]
1157 pas = [wc.p1()]
1155 elif not branchmerge and not wc.dirty(missing=True):
1158 elif not branchmerge and not wc.dirty(missing=True):
1156 pass
1159 pass
1157 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1160 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1158 followcopies = True
1161 followcopies = True
1159
1162
1160 ### calculate phase
1163 ### calculate phase
1161 actionbyfile, diverge, renamedelete = calculateupdates(
1164 actionbyfile, diverge, renamedelete = calculateupdates(
1162 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1165 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1163 followcopies)
1166 followcopies)
1164 # Convert to dictionary-of-lists format
1167 # Convert to dictionary-of-lists format
1165 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1168 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1166 for f, (m, args, msg) in actionbyfile.iteritems():
1169 for f, (m, args, msg) in actionbyfile.iteritems():
1167 if m not in actions:
1170 if m not in actions:
1168 actions[m] = []
1171 actions[m] = []
1169 actions[m].append((f, args, msg))
1172 actions[m].append((f, args, msg))
1170
1173
1171 if not util.checkcase(repo.path):
1174 if not util.checkcase(repo.path):
1172 # check collision between files only in p2 for clean update
1175 # check collision between files only in p2 for clean update
1173 if (not branchmerge and
1176 if (not branchmerge and
1174 (force or not wc.dirty(missing=True, branch=False))):
1177 (force or not wc.dirty(missing=True, branch=False))):
1175 _checkcollision(repo, p2.manifest(), None)
1178 _checkcollision(repo, p2.manifest(), None)
1176 else:
1179 else:
1177 _checkcollision(repo, wc.manifest(), actions)
1180 _checkcollision(repo, wc.manifest(), actions)
1178
1181
1179 # Prompt and create actions. TODO: Move this towards resolve phase.
1182 # Prompt and create actions. TODO: Move this towards resolve phase.
1180 for f, args, msg in sorted(actions['cd']):
1183 for f, args, msg in sorted(actions['cd']):
1181 if repo.ui.promptchoice(
1184 if repo.ui.promptchoice(
1182 _("local changed %s which remote deleted\n"
1185 _("local changed %s which remote deleted\n"
1183 "use (c)hanged version or (d)elete?"
1186 "use (c)hanged version or (d)elete?"
1184 "$$ &Changed $$ &Delete") % f, 0):
1187 "$$ &Changed $$ &Delete") % f, 0):
1185 actions['r'].append((f, None, "prompt delete"))
1188 actions['r'].append((f, None, "prompt delete"))
1186 else:
1189 else:
1187 actions['a'].append((f, None, "prompt keep"))
1190 actions['a'].append((f, None, "prompt keep"))
1188 del actions['cd'][:]
1191 del actions['cd'][:]
1189
1192
1190 for f, args, msg in sorted(actions['dc']):
1193 for f, args, msg in sorted(actions['dc']):
1191 flags, = args
1194 flags, = args
1192 if repo.ui.promptchoice(
1195 if repo.ui.promptchoice(
1193 _("remote changed %s which local deleted\n"
1196 _("remote changed %s which local deleted\n"
1194 "use (c)hanged version or leave (d)eleted?"
1197 "use (c)hanged version or leave (d)eleted?"
1195 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1198 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1196 actions['g'].append((f, (flags,), "prompt recreating"))
1199 actions['g'].append((f, (flags,), "prompt recreating"))
1197 del actions['dc'][:]
1200 del actions['dc'][:]
1198
1201
1199 ### apply phase
1202 ### apply phase
1200 if not branchmerge: # just jump to the new rev
1203 if not branchmerge: # just jump to the new rev
1201 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1204 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1202 if not partial:
1205 if not partial:
1203 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1206 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1204 # note that we're in the middle of an update
1207 # note that we're in the middle of an update
1205 repo.vfs.write('updatestate', p2.hex())
1208 repo.vfs.write('updatestate', p2.hex())
1206
1209
1207 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1210 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1208
1211
1209 # divergent renames
1212 # divergent renames
1210 for f, fl in sorted(diverge.iteritems()):
1213 for f, fl in sorted(diverge.iteritems()):
1211 repo.ui.warn(_("note: possible conflict - %s was renamed "
1214 repo.ui.warn(_("note: possible conflict - %s was renamed "
1212 "multiple times to:\n") % f)
1215 "multiple times to:\n") % f)
1213 for nf in fl:
1216 for nf in fl:
1214 repo.ui.warn(" %s\n" % nf)
1217 repo.ui.warn(" %s\n" % nf)
1215
1218
1216 # rename and delete
1219 # rename and delete
1217 for f, fl in sorted(renamedelete.iteritems()):
1220 for f, fl in sorted(renamedelete.iteritems()):
1218 repo.ui.warn(_("note: possible conflict - %s was deleted "
1221 repo.ui.warn(_("note: possible conflict - %s was deleted "
1219 "and renamed to:\n") % f)
1222 "and renamed to:\n") % f)
1220 for nf in fl:
1223 for nf in fl:
1221 repo.ui.warn(" %s\n" % nf)
1224 repo.ui.warn(" %s\n" % nf)
1222
1225
1223 if not partial:
1226 if not partial:
1224 repo.dirstate.beginparentchange()
1227 repo.dirstate.beginparentchange()
1225 repo.setparents(fp1, fp2)
1228 repo.setparents(fp1, fp2)
1226 recordupdates(repo, actions, branchmerge)
1229 recordupdates(repo, actions, branchmerge)
1227 # update completed, clear state
1230 # update completed, clear state
1228 util.unlink(repo.join('updatestate'))
1231 util.unlink(repo.join('updatestate'))
1229
1232
1230 if not branchmerge:
1233 if not branchmerge:
1231 repo.dirstate.setbranch(p2.branch())
1234 repo.dirstate.setbranch(p2.branch())
1232 repo.dirstate.endparentchange()
1235 repo.dirstate.endparentchange()
1233 finally:
1236 finally:
1234 wlock.release()
1237 wlock.release()
1235
1238
1236 if not partial:
1239 if not partial:
1237 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1240 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1238 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1241 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1239 repo._afterlock(updatehook)
1242 repo._afterlock(updatehook)
1240 return stats
1243 return stats
1241
1244
1242 def graft(repo, ctx, pctx, labels):
1245 def graft(repo, ctx, pctx, labels):
1243 """Do a graft-like merge.
1246 """Do a graft-like merge.
1244
1247
1245 This is a merge where the merge ancestor is chosen such that one
1248 This is a merge where the merge ancestor is chosen such that one
1246 or more changesets are grafted onto the current changeset. In
1249 or more changesets are grafted onto the current changeset. In
1247 addition to the merge, this fixes up the dirstate to include only
1250 addition to the merge, this fixes up the dirstate to include only
1248 a single parent and tries to duplicate any renames/copies
1251 a single parent and tries to duplicate any renames/copies
1249 appropriately.
1252 appropriately.
1250
1253
1251 ctx - changeset to rebase
1254 ctx - changeset to rebase
1252 pctx - merge base, usually ctx.p1()
1255 pctx - merge base, usually ctx.p1()
1253 labels - merge labels eg ['local', 'graft']
1256 labels - merge labels eg ['local', 'graft']
1254
1257
1255 """
1258 """
1256 # If we're grafting a descendant onto an ancestor, be sure to pass
1259 # If we're grafting a descendant onto an ancestor, be sure to pass
1257 # mergeancestor=True to update. This does two things: 1) allows the merge if
1260 # mergeancestor=True to update. This does two things: 1) allows the merge if
1258 # the destination is the same as the parent of the ctx (so we can use graft
1261 # the destination is the same as the parent of the ctx (so we can use graft
1259 # to copy commits), and 2) informs update that the incoming changes are
1262 # to copy commits), and 2) informs update that the incoming changes are
1260 # newer than the destination so it doesn't prompt about "remote changed foo
1263 # newer than the destination so it doesn't prompt about "remote changed foo
1261 # which local deleted".
1264 # which local deleted".
1262 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1265 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1263
1266
1264 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1267 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1265 mergeancestor=mergeancestor, labels=labels)
1268 mergeancestor=mergeancestor, labels=labels)
1266
1269
1267 # drop the second merge parent
1270 # drop the second merge parent
1268 repo.dirstate.beginparentchange()
1271 repo.dirstate.beginparentchange()
1269 repo.setparents(repo['.'].node(), nullid)
1272 repo.setparents(repo['.'].node(), nullid)
1270 repo.dirstate.write()
1273 repo.dirstate.write()
1271 # fix up dirstate for copies and renames
1274 # fix up dirstate for copies and renames
1272 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1275 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1273 repo.dirstate.endparentchange()
1276 repo.dirstate.endparentchange()
1274 return stats
1277 return stats
General Comments 0
You need to be logged in to leave comments. Login now