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