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