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