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