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