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