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