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