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