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