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