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