##// END OF EJS Templates
merge: don't ignore conflicting file in remote renamed directory...
Martin von Zweigbergk -
r23475:67f1d688 default
parent child Browse files
Show More
@@ -1,1158 +1,1162 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import struct
8 import struct
9
9
10 from node import nullid, nullrev, hex, bin
10 from node import nullid, nullrev, hex, bin
11 from i18n import _
11 from i18n import _
12 from mercurial import obsolete
12 from mercurial import obsolete
13 import error as errormod, util, filemerge, copies, subrepo, worker
13 import error as errormod, util, filemerge, copies, subrepo, worker
14 import errno, os, shutil
14 import errno, os, shutil
15
15
16 _pack = struct.pack
16 _pack = struct.pack
17 _unpack = struct.unpack
17 _unpack = struct.unpack
18
18
19 def _droponode(data):
19 def _droponode(data):
20 # used for compatibility for v1
20 # used for compatibility for v1
21 bits = data.split('\0')
21 bits = data.split('\0')
22 bits = bits[:-2] + bits[-1:]
22 bits = bits[:-2] + bits[-1:]
23 return '\0'.join(bits)
23 return '\0'.join(bits)
24
24
25 class mergestate(object):
25 class mergestate(object):
26 '''track 3-way merge state of individual files
26 '''track 3-way merge state of individual files
27
27
28 it is stored on disk when needed. Two file are used, one with an old
28 it is stored on disk when needed. Two file are used, one with an old
29 format, one with a new format. Both contains similar data, but the new
29 format, one with a new format. Both contains similar data, but the new
30 format can store new kind of field.
30 format can store new kind of field.
31
31
32 Current new format is a list of arbitrary record of the form:
32 Current new format is a list of arbitrary record of the form:
33
33
34 [type][length][content]
34 [type][length][content]
35
35
36 Type is a single character, length is a 4 bytes integer, content is an
36 Type is a single character, length is a 4 bytes integer, content is an
37 arbitrary suites of bytes of length `length`.
37 arbitrary suites of bytes of length `length`.
38
38
39 Type should be a letter. Capital letter are mandatory record, Mercurial
39 Type should be a letter. Capital letter are mandatory record, Mercurial
40 should abort if they are unknown. lower case record can be safely ignored.
40 should abort if they are unknown. lower case record can be safely ignored.
41
41
42 Currently known record:
42 Currently known record:
43
43
44 L: the node of the "local" part of the merge (hexified version)
44 L: the node of the "local" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
46 F: a file to be merged entry
46 F: a file to be merged entry
47 '''
47 '''
48 statepathv1 = 'merge/state'
48 statepathv1 = 'merge/state'
49 statepathv2 = 'merge/state2'
49 statepathv2 = 'merge/state2'
50
50
51 def __init__(self, repo):
51 def __init__(self, repo):
52 self._repo = repo
52 self._repo = repo
53 self._dirty = False
53 self._dirty = False
54 self._read()
54 self._read()
55
55
56 def reset(self, node=None, other=None):
56 def reset(self, node=None, other=None):
57 self._state = {}
57 self._state = {}
58 self._local = None
58 self._local = None
59 self._other = None
59 self._other = None
60 if node:
60 if node:
61 self._local = node
61 self._local = node
62 self._other = other
62 self._other = other
63 shutil.rmtree(self._repo.join('merge'), True)
63 shutil.rmtree(self._repo.join('merge'), True)
64 self._dirty = False
64 self._dirty = False
65
65
66 def _read(self):
66 def _read(self):
67 """Analyse each record content to restore a serialized state from disk
67 """Analyse each record content to restore a serialized state from disk
68
68
69 This function process "record" entry produced by the de-serialization
69 This function process "record" entry produced by the de-serialization
70 of on disk file.
70 of on disk file.
71 """
71 """
72 self._state = {}
72 self._state = {}
73 self._local = None
73 self._local = None
74 self._other = None
74 self._other = None
75 records = self._readrecords()
75 records = self._readrecords()
76 for rtype, record in records:
76 for rtype, record in records:
77 if rtype == 'L':
77 if rtype == 'L':
78 self._local = bin(record)
78 self._local = bin(record)
79 elif rtype == 'O':
79 elif rtype == 'O':
80 self._other = bin(record)
80 self._other = bin(record)
81 elif rtype == 'F':
81 elif rtype == 'F':
82 bits = record.split('\0')
82 bits = record.split('\0')
83 self._state[bits[0]] = bits[1:]
83 self._state[bits[0]] = bits[1:]
84 elif not rtype.islower():
84 elif not rtype.islower():
85 raise util.Abort(_('unsupported merge state record: %s')
85 raise util.Abort(_('unsupported merge state record: %s')
86 % rtype)
86 % rtype)
87 self._dirty = False
87 self._dirty = False
88
88
89 def _readrecords(self):
89 def _readrecords(self):
90 """Read merge state from disk and return a list of record (TYPE, data)
90 """Read merge state from disk and return a list of record (TYPE, data)
91
91
92 We read data from both v1 and v2 files and decide which one to use.
92 We read data from both v1 and v2 files and decide which one to use.
93
93
94 V1 has been used by version prior to 2.9.1 and contains less data than
94 V1 has been used by version prior to 2.9.1 and contains less data than
95 v2. We read both versions and check if no data in v2 contradicts
95 v2. We read both versions and check if no data in v2 contradicts
96 v1. If there is not contradiction we can safely assume that both v1
96 v1. If there is not contradiction we can safely assume that both v1
97 and v2 were written at the same time and use the extract data in v2. If
97 and v2 were written at the same time and use the extract data in v2. If
98 there is contradiction we ignore v2 content as we assume an old version
98 there is contradiction we ignore v2 content as we assume an old version
99 of Mercurial has overwritten the mergestate file and left an old v2
99 of Mercurial has overwritten the mergestate file and left an old v2
100 file around.
100 file around.
101
101
102 returns list of record [(TYPE, data), ...]"""
102 returns list of record [(TYPE, data), ...]"""
103 v1records = self._readrecordsv1()
103 v1records = self._readrecordsv1()
104 v2records = self._readrecordsv2()
104 v2records = self._readrecordsv2()
105 oldv2 = set() # old format version of v2 record
105 oldv2 = set() # old format version of v2 record
106 for rec in v2records:
106 for rec in v2records:
107 if rec[0] == 'L':
107 if rec[0] == 'L':
108 oldv2.add(rec)
108 oldv2.add(rec)
109 elif rec[0] == 'F':
109 elif rec[0] == 'F':
110 # drop the onode data (not contained in v1)
110 # drop the onode data (not contained in v1)
111 oldv2.add(('F', _droponode(rec[1])))
111 oldv2.add(('F', _droponode(rec[1])))
112 for rec in v1records:
112 for rec in v1records:
113 if rec not in oldv2:
113 if rec not in oldv2:
114 # v1 file is newer than v2 file, use it
114 # v1 file is newer than v2 file, use it
115 # we have to infer the "other" changeset of the merge
115 # we have to infer the "other" changeset of the merge
116 # we cannot do better than that with v1 of the format
116 # we cannot do better than that with v1 of the format
117 mctx = self._repo[None].parents()[-1]
117 mctx = self._repo[None].parents()[-1]
118 v1records.append(('O', mctx.hex()))
118 v1records.append(('O', mctx.hex()))
119 # add place holder "other" file node information
119 # add place holder "other" file node information
120 # nobody is using it yet so we do no need to fetch the data
120 # nobody is using it yet so we do no need to fetch the data
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
122 for idx, r in enumerate(v1records):
122 for idx, r in enumerate(v1records):
123 if r[0] == 'F':
123 if r[0] == 'F':
124 bits = r[1].split('\0')
124 bits = r[1].split('\0')
125 bits.insert(-2, '')
125 bits.insert(-2, '')
126 v1records[idx] = (r[0], '\0'.join(bits))
126 v1records[idx] = (r[0], '\0'.join(bits))
127 return v1records
127 return v1records
128 else:
128 else:
129 return v2records
129 return v2records
130
130
131 def _readrecordsv1(self):
131 def _readrecordsv1(self):
132 """read on disk merge state for version 1 file
132 """read on disk merge state for version 1 file
133
133
134 returns list of record [(TYPE, data), ...]
134 returns list of record [(TYPE, data), ...]
135
135
136 Note: the "F" data from this file are one entry short
136 Note: the "F" data from this file are one entry short
137 (no "other file node" entry)
137 (no "other file node" entry)
138 """
138 """
139 records = []
139 records = []
140 try:
140 try:
141 f = self._repo.opener(self.statepathv1)
141 f = self._repo.opener(self.statepathv1)
142 for i, l in enumerate(f):
142 for i, l in enumerate(f):
143 if i == 0:
143 if i == 0:
144 records.append(('L', l[:-1]))
144 records.append(('L', l[:-1]))
145 else:
145 else:
146 records.append(('F', l[:-1]))
146 records.append(('F', l[:-1]))
147 f.close()
147 f.close()
148 except IOError, err:
148 except IOError, err:
149 if err.errno != errno.ENOENT:
149 if err.errno != errno.ENOENT:
150 raise
150 raise
151 return records
151 return records
152
152
153 def _readrecordsv2(self):
153 def _readrecordsv2(self):
154 """read on disk merge state for version 2 file
154 """read on disk merge state for version 2 file
155
155
156 returns list of record [(TYPE, data), ...]
156 returns list of record [(TYPE, data), ...]
157 """
157 """
158 records = []
158 records = []
159 try:
159 try:
160 f = self._repo.opener(self.statepathv2)
160 f = self._repo.opener(self.statepathv2)
161 data = f.read()
161 data = f.read()
162 off = 0
162 off = 0
163 end = len(data)
163 end = len(data)
164 while off < end:
164 while off < end:
165 rtype = data[off]
165 rtype = data[off]
166 off += 1
166 off += 1
167 length = _unpack('>I', data[off:(off + 4)])[0]
167 length = _unpack('>I', data[off:(off + 4)])[0]
168 off += 4
168 off += 4
169 record = data[off:(off + length)]
169 record = data[off:(off + length)]
170 off += length
170 off += length
171 records.append((rtype, record))
171 records.append((rtype, record))
172 f.close()
172 f.close()
173 except IOError, err:
173 except IOError, err:
174 if err.errno != errno.ENOENT:
174 if err.errno != errno.ENOENT:
175 raise
175 raise
176 return records
176 return records
177
177
178 def active(self):
178 def active(self):
179 """Whether mergestate is active.
179 """Whether mergestate is active.
180
180
181 Returns True if there appears to be mergestate. This is a rough proxy
181 Returns True if there appears to be mergestate. This is a rough proxy
182 for "is a merge in progress."
182 for "is a merge in progress."
183 """
183 """
184 # Check local variables before looking at filesystem for performance
184 # Check local variables before looking at filesystem for performance
185 # reasons.
185 # reasons.
186 return bool(self._local) or bool(self._state) or \
186 return bool(self._local) or bool(self._state) or \
187 self._repo.opener.exists(self.statepathv1) or \
187 self._repo.opener.exists(self.statepathv1) or \
188 self._repo.opener.exists(self.statepathv2)
188 self._repo.opener.exists(self.statepathv2)
189
189
190 def commit(self):
190 def commit(self):
191 """Write current state on disk (if necessary)"""
191 """Write current state on disk (if necessary)"""
192 if self._dirty:
192 if self._dirty:
193 records = []
193 records = []
194 records.append(('L', hex(self._local)))
194 records.append(('L', hex(self._local)))
195 records.append(('O', hex(self._other)))
195 records.append(('O', hex(self._other)))
196 for d, v in self._state.iteritems():
196 for d, v in self._state.iteritems():
197 records.append(('F', '\0'.join([d] + v)))
197 records.append(('F', '\0'.join([d] + v)))
198 self._writerecords(records)
198 self._writerecords(records)
199 self._dirty = False
199 self._dirty = False
200
200
201 def _writerecords(self, records):
201 def _writerecords(self, records):
202 """Write current state on disk (both v1 and v2)"""
202 """Write current state on disk (both v1 and v2)"""
203 self._writerecordsv1(records)
203 self._writerecordsv1(records)
204 self._writerecordsv2(records)
204 self._writerecordsv2(records)
205
205
206 def _writerecordsv1(self, records):
206 def _writerecordsv1(self, records):
207 """Write current state on disk in a version 1 file"""
207 """Write current state on disk in a version 1 file"""
208 f = self._repo.opener(self.statepathv1, 'w')
208 f = self._repo.opener(self.statepathv1, 'w')
209 irecords = iter(records)
209 irecords = iter(records)
210 lrecords = irecords.next()
210 lrecords = irecords.next()
211 assert lrecords[0] == 'L'
211 assert lrecords[0] == 'L'
212 f.write(hex(self._local) + '\n')
212 f.write(hex(self._local) + '\n')
213 for rtype, data in irecords:
213 for rtype, data in irecords:
214 if rtype == 'F':
214 if rtype == 'F':
215 f.write('%s\n' % _droponode(data))
215 f.write('%s\n' % _droponode(data))
216 f.close()
216 f.close()
217
217
218 def _writerecordsv2(self, records):
218 def _writerecordsv2(self, records):
219 """Write current state on disk in a version 2 file"""
219 """Write current state on disk in a version 2 file"""
220 f = self._repo.opener(self.statepathv2, 'w')
220 f = self._repo.opener(self.statepathv2, 'w')
221 for key, data in records:
221 for key, data in records:
222 assert len(key) == 1
222 assert len(key) == 1
223 format = '>sI%is' % len(data)
223 format = '>sI%is' % len(data)
224 f.write(_pack(format, key, len(data), data))
224 f.write(_pack(format, key, len(data), data))
225 f.close()
225 f.close()
226
226
227 def add(self, fcl, fco, fca, fd):
227 def add(self, fcl, fco, fca, fd):
228 """add a new (potentially?) conflicting file the merge state
228 """add a new (potentially?) conflicting file the merge state
229 fcl: file context for local,
229 fcl: file context for local,
230 fco: file context for remote,
230 fco: file context for remote,
231 fca: file context for ancestors,
231 fca: file context for ancestors,
232 fd: file path of the resulting merge.
232 fd: file path of the resulting merge.
233
233
234 note: also write the local version to the `.hg/merge` directory.
234 note: also write the local version to the `.hg/merge` directory.
235 """
235 """
236 hash = util.sha1(fcl.path()).hexdigest()
236 hash = util.sha1(fcl.path()).hexdigest()
237 self._repo.opener.write('merge/' + hash, fcl.data())
237 self._repo.opener.write('merge/' + hash, fcl.data())
238 self._state[fd] = ['u', hash, fcl.path(),
238 self._state[fd] = ['u', hash, fcl.path(),
239 fca.path(), hex(fca.filenode()),
239 fca.path(), hex(fca.filenode()),
240 fco.path(), hex(fco.filenode()),
240 fco.path(), hex(fco.filenode()),
241 fcl.flags()]
241 fcl.flags()]
242 self._dirty = True
242 self._dirty = True
243
243
244 def __contains__(self, dfile):
244 def __contains__(self, dfile):
245 return dfile in self._state
245 return dfile in self._state
246
246
247 def __getitem__(self, dfile):
247 def __getitem__(self, dfile):
248 return self._state[dfile][0]
248 return self._state[dfile][0]
249
249
250 def __iter__(self):
250 def __iter__(self):
251 return iter(sorted(self._state))
251 return iter(sorted(self._state))
252
252
253 def files(self):
253 def files(self):
254 return self._state.keys()
254 return self._state.keys()
255
255
256 def mark(self, dfile, state):
256 def mark(self, dfile, state):
257 self._state[dfile][0] = state
257 self._state[dfile][0] = state
258 self._dirty = True
258 self._dirty = True
259
259
260 def unresolved(self):
260 def unresolved(self):
261 """Obtain the paths of unresolved files."""
261 """Obtain the paths of unresolved files."""
262
262
263 for f, entry in self._state.items():
263 for f, entry in self._state.items():
264 if entry[0] == 'u':
264 if entry[0] == 'u':
265 yield f
265 yield f
266
266
267 def resolve(self, dfile, wctx, labels=None):
267 def resolve(self, dfile, wctx, labels=None):
268 """rerun merge process for file path `dfile`"""
268 """rerun merge process for file path `dfile`"""
269 if self[dfile] == 'r':
269 if self[dfile] == 'r':
270 return 0
270 return 0
271 stateentry = self._state[dfile]
271 stateentry = self._state[dfile]
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
273 octx = self._repo[self._other]
273 octx = self._repo[self._other]
274 fcd = wctx[dfile]
274 fcd = wctx[dfile]
275 fco = octx[ofile]
275 fco = octx[ofile]
276 fca = self._repo.filectx(afile, fileid=anode)
276 fca = self._repo.filectx(afile, fileid=anode)
277 # "premerge" x flags
277 # "premerge" x flags
278 flo = fco.flags()
278 flo = fco.flags()
279 fla = fca.flags()
279 fla = fca.flags()
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
281 if fca.node() == nullid:
281 if fca.node() == nullid:
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
283 afile)
283 afile)
284 elif flags == fla:
284 elif flags == fla:
285 flags = flo
285 flags = flo
286 # restore local
286 # restore local
287 f = self._repo.opener('merge/' + hash)
287 f = self._repo.opener('merge/' + hash)
288 self._repo.wwrite(dfile, f.read(), flags)
288 self._repo.wwrite(dfile, f.read(), flags)
289 f.close()
289 f.close()
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
291 labels=labels)
291 labels=labels)
292 if r is None:
292 if r is None:
293 # no real conflict
293 # no real conflict
294 del self._state[dfile]
294 del self._state[dfile]
295 self._dirty = True
295 self._dirty = True
296 elif not r:
296 elif not r:
297 self.mark(dfile, 'r')
297 self.mark(dfile, 'r')
298 return r
298 return r
299
299
300 def _checkunknownfile(repo, wctx, mctx, f):
300 def _checkunknownfile(repo, wctx, mctx, f):
301 return (not repo.dirstate._ignore(f)
301 return (not repo.dirstate._ignore(f)
302 and os.path.isfile(repo.wjoin(f))
302 and os.path.isfile(repo.wjoin(f))
303 and repo.wopener.audit.check(f)
303 and repo.wopener.audit.check(f)
304 and repo.dirstate.normalize(f) not in repo.dirstate
304 and repo.dirstate.normalize(f) not in repo.dirstate
305 and mctx[f].cmp(wctx[f]))
305 and mctx[f].cmp(wctx[f]))
306
306
307 def _forgetremoved(wctx, mctx, branchmerge):
307 def _forgetremoved(wctx, mctx, branchmerge):
308 """
308 """
309 Forget removed files
309 Forget removed files
310
310
311 If we're jumping between revisions (as opposed to merging), and if
311 If we're jumping between revisions (as opposed to merging), and if
312 neither the working directory nor the target rev has the file,
312 neither the working directory nor the target rev has the file,
313 then we need to remove it from the dirstate, to prevent the
313 then we need to remove it from the dirstate, to prevent the
314 dirstate from listing the file when it is no longer in the
314 dirstate from listing the file when it is no longer in the
315 manifest.
315 manifest.
316
316
317 If we're merging, and the other revision has removed a file
317 If we're merging, and the other revision has removed a file
318 that is not present in the working directory, we need to mark it
318 that is not present in the working directory, we need to mark it
319 as removed.
319 as removed.
320 """
320 """
321
321
322 ractions = []
322 ractions = []
323 factions = xactions = []
323 factions = xactions = []
324 if branchmerge:
324 if branchmerge:
325 xactions = ractions
325 xactions = ractions
326 for f in wctx.deleted():
326 for f in wctx.deleted():
327 if f not in mctx:
327 if f not in mctx:
328 xactions.append((f, None, "forget deleted"))
328 xactions.append((f, None, "forget deleted"))
329
329
330 if not branchmerge:
330 if not branchmerge:
331 for f in wctx.removed():
331 for f in wctx.removed():
332 if f not in mctx:
332 if f not in mctx:
333 factions.append((f, None, "forget removed"))
333 factions.append((f, None, "forget removed"))
334
334
335 return ractions, factions
335 return ractions, factions
336
336
337 def _checkcollision(repo, wmf, actions):
337 def _checkcollision(repo, wmf, actions):
338 # build provisional merged manifest up
338 # build provisional merged manifest up
339 pmmf = set(wmf)
339 pmmf = set(wmf)
340
340
341 if actions:
341 if actions:
342 # k, dr, e and rd are no-op
342 # k, dr, e and rd are no-op
343 for m in 'a', 'f', 'g', 'cd', 'dc':
343 for m in 'a', 'f', 'g', 'cd', 'dc':
344 for f, args, msg in actions[m]:
344 for f, args, msg in actions[m]:
345 pmmf.add(f)
345 pmmf.add(f)
346 for f, args, msg in actions['r']:
346 for f, args, msg in actions['r']:
347 pmmf.discard(f)
347 pmmf.discard(f)
348 for f, args, msg in actions['dm']:
348 for f, args, msg in actions['dm']:
349 f2, flags = args
349 f2, flags = args
350 pmmf.discard(f2)
350 pmmf.discard(f2)
351 pmmf.add(f)
351 pmmf.add(f)
352 for f, args, msg in actions['dg']:
352 for f, args, msg in actions['dg']:
353 pmmf.add(f)
353 pmmf.add(f)
354 for f, args, msg in actions['m']:
354 for f, args, msg in actions['m']:
355 f1, f2, fa, move, anc = args
355 f1, f2, fa, move, anc = args
356 if move:
356 if move:
357 pmmf.discard(f1)
357 pmmf.discard(f1)
358 pmmf.add(f)
358 pmmf.add(f)
359
359
360 # check case-folding collision in provisional merged manifest
360 # check case-folding collision in provisional merged manifest
361 foldmap = {}
361 foldmap = {}
362 for f in sorted(pmmf):
362 for f in sorted(pmmf):
363 fold = util.normcase(f)
363 fold = util.normcase(f)
364 if fold in foldmap:
364 if fold in foldmap:
365 raise util.Abort(_("case-folding collision between %s and %s")
365 raise util.Abort(_("case-folding collision between %s and %s")
366 % (f, foldmap[fold]))
366 % (f, foldmap[fold]))
367 foldmap[fold] = f
367 foldmap[fold] = f
368
368
369 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
369 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
370 acceptremote, followcopies):
370 acceptremote, followcopies):
371 """
371 """
372 Merge p1 and p2 with ancestor pa and generate merge action list
372 Merge p1 and p2 with ancestor pa and generate merge action list
373
373
374 branchmerge and force are as passed in to update
374 branchmerge and force are as passed in to update
375 partial = function to filter file lists
375 partial = function to filter file lists
376 acceptremote = accept the incoming changes without prompting
376 acceptremote = accept the incoming changes without prompting
377 """
377 """
378
378
379 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
379 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
380 copy, movewithdir = {}, {}
380 copy, movewithdir = {}, {}
381
381
382 # manifests fetched in order are going to be faster, so prime the caches
382 # manifests fetched in order are going to be faster, so prime the caches
383 [x.manifest() for x in
383 [x.manifest() for x in
384 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
384 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
385
385
386 if followcopies:
386 if followcopies:
387 ret = copies.mergecopies(repo, wctx, p2, pa)
387 ret = copies.mergecopies(repo, wctx, p2, pa)
388 copy, movewithdir, diverge, renamedelete = ret
388 copy, movewithdir, diverge, renamedelete = ret
389 for of, fl in diverge.iteritems():
389 for of, fl in diverge.iteritems():
390 actions['dr'].append((of, (fl,), "divergent renames"))
390 actions['dr'].append((of, (fl,), "divergent renames"))
391 for of, fl in renamedelete.iteritems():
391 for of, fl in renamedelete.iteritems():
392 actions['rd'].append((of, (fl,), "rename and delete"))
392 actions['rd'].append((of, (fl,), "rename and delete"))
393
393
394 repo.ui.note(_("resolving manifests\n"))
394 repo.ui.note(_("resolving manifests\n"))
395 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
395 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
396 % (bool(branchmerge), bool(force), bool(partial)))
396 % (bool(branchmerge), bool(force), bool(partial)))
397 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
397 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
398
398
399 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
399 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
400 copied = set(copy.values())
400 copied = set(copy.values())
401 copied.update(movewithdir.values())
401 copied.update(movewithdir.values())
402
402
403 if '.hgsubstate' in m1:
403 if '.hgsubstate' in m1:
404 # check whether sub state is modified
404 # check whether sub state is modified
405 for s in sorted(wctx.substate):
405 for s in sorted(wctx.substate):
406 if wctx.sub(s).dirty():
406 if wctx.sub(s).dirty():
407 m1['.hgsubstate'] += '+'
407 m1['.hgsubstate'] += '+'
408 break
408 break
409
409
410 aborts = []
410 aborts = []
411 # Compare manifests
411 # Compare manifests
412 diff = m1.diff(m2)
412 diff = m1.diff(m2)
413
413
414 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
414 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
415 if partial and not partial(f):
415 if partial and not partial(f):
416 continue
416 continue
417 if n1 and n2: # file exists on both local and remote side
417 if n1 and n2: # file exists on both local and remote side
418 if f not in ma:
418 if f not in ma:
419 fa = copy.get(f, None)
419 fa = copy.get(f, None)
420 if fa is not None:
420 if fa is not None:
421 actions['m'].append((f, (f, f, fa, False, pa.node()),
421 actions['m'].append((f, (f, f, fa, False, pa.node()),
422 "both renamed from " + fa))
422 "both renamed from " + fa))
423 else:
423 else:
424 actions['m'].append((f, (f, f, None, False, pa.node()),
424 actions['m'].append((f, (f, f, None, False, pa.node()),
425 "both created"))
425 "both created"))
426 else:
426 else:
427 a = ma[f]
427 a = ma[f]
428 fla = ma.flags(f)
428 fla = ma.flags(f)
429 nol = 'l' not in fl1 + fl2 + fla
429 nol = 'l' not in fl1 + fl2 + fla
430 if n2 == a and fl2 == fla:
430 if n2 == a and fl2 == fla:
431 actions['k'].append((f, (), "keep")) # remote unchanged
431 actions['k'].append((f, (), "keep")) # remote unchanged
432 elif n1 == a and fl1 == fla: # local unchanged - use remote
432 elif n1 == a and fl1 == fla: # local unchanged - use remote
433 if n1 == n2: # optimization: keep local content
433 if n1 == n2: # optimization: keep local content
434 actions['e'].append((f, (fl2,), "update permissions"))
434 actions['e'].append((f, (fl2,), "update permissions"))
435 else:
435 else:
436 actions['g'].append((f, (fl2,), "remote is newer"))
436 actions['g'].append((f, (fl2,), "remote is newer"))
437 elif nol and n2 == a: # remote only changed 'x'
437 elif nol and n2 == a: # remote only changed 'x'
438 actions['e'].append((f, (fl2,), "update permissions"))
438 actions['e'].append((f, (fl2,), "update permissions"))
439 elif nol and n1 == a: # local only changed 'x'
439 elif nol and n1 == a: # local only changed 'x'
440 actions['g'].append((f, (fl1,), "remote is newer"))
440 actions['g'].append((f, (fl1,), "remote is newer"))
441 else: # both changed something
441 else: # both changed something
442 actions['m'].append((f, (f, f, f, False, pa.node()),
442 actions['m'].append((f, (f, f, f, False, pa.node()),
443 "versions differ"))
443 "versions differ"))
444 elif n1: # file exists only on local side
444 elif n1: # file exists only on local side
445 if f in copied:
445 if f in copied:
446 pass # we'll deal with it on m2 side
446 pass # we'll deal with it on m2 side
447 elif f in movewithdir: # directory rename, move local
447 elif f in movewithdir: # directory rename, move local
448 f2 = movewithdir[f]
448 f2 = movewithdir[f]
449 actions['dm'].append((f2, (f, fl1),
449 if f2 in m2:
450 "remote directory rename - move from " + f))
450 actions['m'].append((f2, (f, f2, None, True, pa.node()),
451 "remote directory rename, both created"))
452 else:
453 actions['dm'].append((f2, (f, fl1),
454 "remote directory rename - move from " + f))
451 elif f in copy:
455 elif f in copy:
452 f2 = copy[f]
456 f2 = copy[f]
453 actions['m'].append((f, (f, f2, f2, False, pa.node()),
457 actions['m'].append((f, (f, f2, f2, False, pa.node()),
454 "local copied/moved from " + f2))
458 "local copied/moved from " + f2))
455 elif f in ma: # clean, a different, no remote
459 elif f in ma: # clean, a different, no remote
456 if n1 != ma[f]:
460 if n1 != ma[f]:
457 if acceptremote:
461 if acceptremote:
458 actions['r'].append((f, None, "remote delete"))
462 actions['r'].append((f, None, "remote delete"))
459 else:
463 else:
460 actions['cd'].append((f, None,
464 actions['cd'].append((f, None,
461 "prompt changed/deleted"))
465 "prompt changed/deleted"))
462 elif n1[20:] == 'a':
466 elif n1[20:] == 'a':
463 # This extra 'a' is added by working copy manifest to mark
467 # This extra 'a' is added by working copy manifest to mark
464 # the file as locally added. We should forget it instead of
468 # the file as locally added. We should forget it instead of
465 # deleting it.
469 # deleting it.
466 actions['f'].append((f, None, "remote deleted"))
470 actions['f'].append((f, None, "remote deleted"))
467 else:
471 else:
468 actions['r'].append((f, None, "other deleted"))
472 actions['r'].append((f, None, "other deleted"))
469 elif n2: # file exists only on remote side
473 elif n2: # file exists only on remote side
470 if f in copied:
474 if f in copied:
471 pass # we'll deal with it on m1 side
475 pass # we'll deal with it on m1 side
472 elif f in movewithdir:
476 elif f in movewithdir:
473 f2 = movewithdir[f]
477 f2 = movewithdir[f]
474 actions['dg'].append((f2, (f, fl2),
478 actions['dg'].append((f2, (f, fl2),
475 "local directory rename - get from " + f))
479 "local directory rename - get from " + f))
476 elif f in copy:
480 elif f in copy:
477 f2 = copy[f]
481 f2 = copy[f]
478 if f2 in m2:
482 if f2 in m2:
479 actions['m'].append((f, (f2, f, f2, False, pa.node()),
483 actions['m'].append((f, (f2, f, f2, False, pa.node()),
480 "remote copied from " + f2))
484 "remote copied from " + f2))
481 else:
485 else:
482 actions['m'].append((f, (f2, f, f2, True, pa.node()),
486 actions['m'].append((f, (f2, f, f2, True, pa.node()),
483 "remote moved from " + f2))
487 "remote moved from " + f2))
484 elif f not in ma:
488 elif f not in ma:
485 # local unknown, remote created: the logic is described by the
489 # local unknown, remote created: the logic is described by the
486 # following table:
490 # following table:
487 #
491 #
488 # force branchmerge different | action
492 # force branchmerge different | action
489 # n * n | get
493 # n * n | get
490 # n * y | abort
494 # n * y | abort
491 # y n * | get
495 # y n * | get
492 # y y n | get
496 # y y n | get
493 # y y y | merge
497 # y y y | merge
494 #
498 #
495 # Checking whether the files are different is expensive, so we
499 # Checking whether the files are different is expensive, so we
496 # don't do that when we can avoid it.
500 # don't do that when we can avoid it.
497 if force and not branchmerge:
501 if force and not branchmerge:
498 actions['g'].append((f, (fl2,), "remote created"))
502 actions['g'].append((f, (fl2,), "remote created"))
499 else:
503 else:
500 different = _checkunknownfile(repo, wctx, p2, f)
504 different = _checkunknownfile(repo, wctx, p2, f)
501 if force and branchmerge and different:
505 if force and branchmerge and different:
502 actions['m'].append((f, (f, f, None, False, pa.node()),
506 actions['m'].append((f, (f, f, None, False, pa.node()),
503 "remote differs from untracked local"))
507 "remote differs from untracked local"))
504 elif not force and different:
508 elif not force and different:
505 aborts.append((f, 'ud'))
509 aborts.append((f, 'ud'))
506 else:
510 else:
507 actions['g'].append((f, (fl2,), "remote created"))
511 actions['g'].append((f, (fl2,), "remote created"))
508 elif n2 != ma[f]:
512 elif n2 != ma[f]:
509 different = _checkunknownfile(repo, wctx, p2, f)
513 different = _checkunknownfile(repo, wctx, p2, f)
510 if not force and different:
514 if not force and different:
511 aborts.append((f, 'ud'))
515 aborts.append((f, 'ud'))
512 else:
516 else:
513 if acceptremote:
517 if acceptremote:
514 actions['g'].append((f, (fl2,), "remote recreating"))
518 actions['g'].append((f, (fl2,), "remote recreating"))
515 else:
519 else:
516 actions['dc'].append((f, (fl2,),
520 actions['dc'].append((f, (fl2,),
517 "prompt deleted/changed"))
521 "prompt deleted/changed"))
518
522
519 for f, m in sorted(aborts):
523 for f, m in sorted(aborts):
520 if m == 'ud':
524 if m == 'ud':
521 repo.ui.warn(_("%s: untracked file differs\n") % f)
525 repo.ui.warn(_("%s: untracked file differs\n") % f)
522 else: assert False, m
526 else: assert False, m
523 if aborts:
527 if aborts:
524 raise util.Abort(_("untracked files in working directory differ "
528 raise util.Abort(_("untracked files in working directory differ "
525 "from files in requested revision"))
529 "from files in requested revision"))
526
530
527 if not util.checkcase(repo.path):
531 if not util.checkcase(repo.path):
528 # check collision between files only in p2 for clean update
532 # check collision between files only in p2 for clean update
529 if (not branchmerge and
533 if (not branchmerge and
530 (force or not wctx.dirty(missing=True, branch=False))):
534 (force or not wctx.dirty(missing=True, branch=False))):
531 _checkcollision(repo, m2, None)
535 _checkcollision(repo, m2, None)
532 else:
536 else:
533 _checkcollision(repo, m1, actions)
537 _checkcollision(repo, m1, actions)
534
538
535 return actions
539 return actions
536
540
537 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
541 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
538 acceptremote, followcopies):
542 acceptremote, followcopies):
539 "Calculate the actions needed to merge mctx into wctx using ancestors"
543 "Calculate the actions needed to merge mctx into wctx using ancestors"
540
544
541 if len(ancestors) == 1: # default
545 if len(ancestors) == 1: # default
542 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
546 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
543 branchmerge, force,
547 branchmerge, force,
544 partial, acceptremote, followcopies)
548 partial, acceptremote, followcopies)
545
549
546 else: # only when merge.preferancestor=* - the default
550 else: # only when merge.preferancestor=* - the default
547 repo.ui.note(
551 repo.ui.note(
548 _("note: merging %s and %s using bids from ancestors %s\n") %
552 _("note: merging %s and %s using bids from ancestors %s\n") %
549 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
553 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
550
554
551 # Call for bids
555 # Call for bids
552 fbids = {} # mapping filename to bids (action method to list af actions)
556 fbids = {} # mapping filename to bids (action method to list af actions)
553 for ancestor in ancestors:
557 for ancestor in ancestors:
554 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
558 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
555 actions = manifestmerge(repo, wctx, mctx, ancestor,
559 actions = manifestmerge(repo, wctx, mctx, ancestor,
556 branchmerge, force,
560 branchmerge, force,
557 partial, acceptremote, followcopies)
561 partial, acceptremote, followcopies)
558 for m, l in sorted(actions.items()):
562 for m, l in sorted(actions.items()):
559 for a in l:
563 for a in l:
560 f, args, msg = a
564 f, args, msg = a
561 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
565 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
562 if f in fbids:
566 if f in fbids:
563 d = fbids[f]
567 d = fbids[f]
564 if m in d:
568 if m in d:
565 d[m].append(a)
569 d[m].append(a)
566 else:
570 else:
567 d[m] = [a]
571 d[m] = [a]
568 else:
572 else:
569 fbids[f] = {m: [a]}
573 fbids[f] = {m: [a]}
570
574
571 # Pick the best bid for each file
575 # Pick the best bid for each file
572 repo.ui.note(_('\nauction for merging merge bids\n'))
576 repo.ui.note(_('\nauction for merging merge bids\n'))
573 actions = dict((m, []) for m in actions.keys())
577 actions = dict((m, []) for m in actions.keys())
574 for f, bids in sorted(fbids.items()):
578 for f, bids in sorted(fbids.items()):
575 # bids is a mapping from action method to list af actions
579 # bids is a mapping from action method to list af actions
576 # Consensus?
580 # Consensus?
577 if len(bids) == 1: # all bids are the same kind of method
581 if len(bids) == 1: # all bids are the same kind of method
578 m, l = bids.items()[0]
582 m, l = bids.items()[0]
579 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
583 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
580 repo.ui.note(" %s: consensus for %s\n" % (f, m))
584 repo.ui.note(" %s: consensus for %s\n" % (f, m))
581 actions[m].append(l[0])
585 actions[m].append(l[0])
582 continue
586 continue
583 # If keep is an option, just do it.
587 # If keep is an option, just do it.
584 if 'k' in bids:
588 if 'k' in bids:
585 repo.ui.note(" %s: picking 'keep' action\n" % f)
589 repo.ui.note(" %s: picking 'keep' action\n" % f)
586 actions['k'].append(bids['k'][0])
590 actions['k'].append(bids['k'][0])
587 continue
591 continue
588 # If there are gets and they all agree [how could they not?], do it.
592 # If there are gets and they all agree [how could they not?], do it.
589 if 'g' in bids:
593 if 'g' in bids:
590 ga0 = bids['g'][0]
594 ga0 = bids['g'][0]
591 if util.all(a == ga0 for a in bids['g'][1:]):
595 if util.all(a == ga0 for a in bids['g'][1:]):
592 repo.ui.note(" %s: picking 'get' action\n" % f)
596 repo.ui.note(" %s: picking 'get' action\n" % f)
593 actions['g'].append(ga0)
597 actions['g'].append(ga0)
594 continue
598 continue
595 # TODO: Consider other simple actions such as mode changes
599 # TODO: Consider other simple actions such as mode changes
596 # Handle inefficient democrazy.
600 # Handle inefficient democrazy.
597 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
601 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
598 for m, l in sorted(bids.items()):
602 for m, l in sorted(bids.items()):
599 for _f, args, msg in l:
603 for _f, args, msg in l:
600 repo.ui.note(' %s -> %s\n' % (msg, m))
604 repo.ui.note(' %s -> %s\n' % (msg, m))
601 # Pick random action. TODO: Instead, prompt user when resolving
605 # Pick random action. TODO: Instead, prompt user when resolving
602 m, l = bids.items()[0]
606 m, l = bids.items()[0]
603 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
607 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
604 (f, m))
608 (f, m))
605 actions[m].append(l[0])
609 actions[m].append(l[0])
606 continue
610 continue
607 repo.ui.note(_('end of auction\n\n'))
611 repo.ui.note(_('end of auction\n\n'))
608
612
609 # Prompt and create actions. TODO: Move this towards resolve phase.
613 # Prompt and create actions. TODO: Move this towards resolve phase.
610 for f, args, msg in sorted(actions['cd']):
614 for f, args, msg in sorted(actions['cd']):
611 if f in ancestors[0] and not wctx[f].cmp(ancestors[0][f]):
615 if f in ancestors[0] and not wctx[f].cmp(ancestors[0][f]):
612 # local did change but ended up with same content
616 # local did change but ended up with same content
613 actions['r'].append((f, None, "prompt same"))
617 actions['r'].append((f, None, "prompt same"))
614 elif repo.ui.promptchoice(
618 elif repo.ui.promptchoice(
615 _("local changed %s which remote deleted\n"
619 _("local changed %s which remote deleted\n"
616 "use (c)hanged version or (d)elete?"
620 "use (c)hanged version or (d)elete?"
617 "$$ &Changed $$ &Delete") % f, 0):
621 "$$ &Changed $$ &Delete") % f, 0):
618 actions['r'].append((f, None, "prompt delete"))
622 actions['r'].append((f, None, "prompt delete"))
619 else:
623 else:
620 actions['a'].append((f, None, "prompt keep"))
624 actions['a'].append((f, None, "prompt keep"))
621 del actions['cd'][:]
625 del actions['cd'][:]
622
626
623 for f, args, msg in sorted(actions['dc']):
627 for f, args, msg in sorted(actions['dc']):
624 flags, = args
628 flags, = args
625 if f in ancestors[0] and not mctx[f].cmp(ancestors[0][f]):
629 if f in ancestors[0] and not mctx[f].cmp(ancestors[0][f]):
626 # remote did change but ended up with same content
630 # remote did change but ended up with same content
627 pass # don't get = keep local deleted
631 pass # don't get = keep local deleted
628 elif repo.ui.promptchoice(
632 elif repo.ui.promptchoice(
629 _("remote changed %s which local deleted\n"
633 _("remote changed %s which local deleted\n"
630 "use (c)hanged version or leave (d)eleted?"
634 "use (c)hanged version or leave (d)eleted?"
631 "$$ &Changed $$ &Deleted") % f, 0) == 0:
635 "$$ &Changed $$ &Deleted") % f, 0) == 0:
632 actions['g'].append((f, (flags,), "prompt recreating"))
636 actions['g'].append((f, (flags,), "prompt recreating"))
633 del actions['dc'][:]
637 del actions['dc'][:]
634
638
635 if wctx.rev() is None:
639 if wctx.rev() is None:
636 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
640 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
637 actions['r'].extend(ractions)
641 actions['r'].extend(ractions)
638 actions['f'].extend(factions)
642 actions['f'].extend(factions)
639
643
640 return actions
644 return actions
641
645
642 def batchremove(repo, actions):
646 def batchremove(repo, actions):
643 """apply removes to the working directory
647 """apply removes to the working directory
644
648
645 yields tuples for progress updates
649 yields tuples for progress updates
646 """
650 """
647 verbose = repo.ui.verbose
651 verbose = repo.ui.verbose
648 unlink = util.unlinkpath
652 unlink = util.unlinkpath
649 wjoin = repo.wjoin
653 wjoin = repo.wjoin
650 audit = repo.wopener.audit
654 audit = repo.wopener.audit
651 i = 0
655 i = 0
652 for f, args, msg in actions:
656 for f, args, msg in actions:
653 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
657 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
654 if verbose:
658 if verbose:
655 repo.ui.note(_("removing %s\n") % f)
659 repo.ui.note(_("removing %s\n") % f)
656 audit(f)
660 audit(f)
657 try:
661 try:
658 unlink(wjoin(f), ignoremissing=True)
662 unlink(wjoin(f), ignoremissing=True)
659 except OSError, inst:
663 except OSError, inst:
660 repo.ui.warn(_("update failed to remove %s: %s!\n") %
664 repo.ui.warn(_("update failed to remove %s: %s!\n") %
661 (f, inst.strerror))
665 (f, inst.strerror))
662 if i == 100:
666 if i == 100:
663 yield i, f
667 yield i, f
664 i = 0
668 i = 0
665 i += 1
669 i += 1
666 if i > 0:
670 if i > 0:
667 yield i, f
671 yield i, f
668
672
669 def batchget(repo, mctx, actions):
673 def batchget(repo, mctx, actions):
670 """apply gets to the working directory
674 """apply gets to the working directory
671
675
672 mctx is the context to get from
676 mctx is the context to get from
673
677
674 yields tuples for progress updates
678 yields tuples for progress updates
675 """
679 """
676 verbose = repo.ui.verbose
680 verbose = repo.ui.verbose
677 fctx = mctx.filectx
681 fctx = mctx.filectx
678 wwrite = repo.wwrite
682 wwrite = repo.wwrite
679 i = 0
683 i = 0
680 for f, args, msg in actions:
684 for f, args, msg in actions:
681 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
685 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
682 if verbose:
686 if verbose:
683 repo.ui.note(_("getting %s\n") % f)
687 repo.ui.note(_("getting %s\n") % f)
684 wwrite(f, fctx(f).data(), args[0])
688 wwrite(f, fctx(f).data(), args[0])
685 if i == 100:
689 if i == 100:
686 yield i, f
690 yield i, f
687 i = 0
691 i = 0
688 i += 1
692 i += 1
689 if i > 0:
693 if i > 0:
690 yield i, f
694 yield i, f
691
695
692 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
696 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
693 """apply the merge action list to the working directory
697 """apply the merge action list to the working directory
694
698
695 wctx is the working copy context
699 wctx is the working copy context
696 mctx is the context to be merged into the working copy
700 mctx is the context to be merged into the working copy
697
701
698 Return a tuple of counts (updated, merged, removed, unresolved) that
702 Return a tuple of counts (updated, merged, removed, unresolved) that
699 describes how many files were affected by the update.
703 describes how many files were affected by the update.
700 """
704 """
701
705
702 updated, merged, removed, unresolved = 0, 0, 0, 0
706 updated, merged, removed, unresolved = 0, 0, 0, 0
703 ms = mergestate(repo)
707 ms = mergestate(repo)
704 ms.reset(wctx.p1().node(), mctx.node())
708 ms.reset(wctx.p1().node(), mctx.node())
705 moves = []
709 moves = []
706 for m, l in actions.items():
710 for m, l in actions.items():
707 l.sort()
711 l.sort()
708
712
709 # prescan for merges
713 # prescan for merges
710 for f, args, msg in actions['m']:
714 for f, args, msg in actions['m']:
711 f1, f2, fa, move, anc = args
715 f1, f2, fa, move, anc = args
712 if f == '.hgsubstate': # merged internally
716 if f == '.hgsubstate': # merged internally
713 continue
717 continue
714 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))
715 fcl = wctx[f1]
719 fcl = wctx[f1]
716 fco = mctx[f2]
720 fco = mctx[f2]
717 actx = repo[anc]
721 actx = repo[anc]
718 if fa in actx:
722 if fa in actx:
719 fca = actx[fa]
723 fca = actx[fa]
720 else:
724 else:
721 fca = repo.filectx(f1, fileid=nullrev)
725 fca = repo.filectx(f1, fileid=nullrev)
722 ms.add(fcl, fco, fca, f)
726 ms.add(fcl, fco, fca, f)
723 if f1 != f and move:
727 if f1 != f and move:
724 moves.append(f1)
728 moves.append(f1)
725
729
726 audit = repo.wopener.audit
730 audit = repo.wopener.audit
727 _updating = _('updating')
731 _updating = _('updating')
728 _files = _('files')
732 _files = _('files')
729 progress = repo.ui.progress
733 progress = repo.ui.progress
730
734
731 # remove renamed files after safely stored
735 # remove renamed files after safely stored
732 for f in moves:
736 for f in moves:
733 if os.path.lexists(repo.wjoin(f)):
737 if os.path.lexists(repo.wjoin(f)):
734 repo.ui.debug("removing %s\n" % f)
738 repo.ui.debug("removing %s\n" % f)
735 audit(f)
739 audit(f)
736 util.unlinkpath(repo.wjoin(f))
740 util.unlinkpath(repo.wjoin(f))
737
741
738 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')
739
743
740 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
744 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
741 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
745 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
742
746
743 # remove in parallel (must come first)
747 # remove in parallel (must come first)
744 z = 0
748 z = 0
745 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
749 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
746 for i, item in prog:
750 for i, item in prog:
747 z += i
751 z += i
748 progress(_updating, z, item=item, total=numupdates, unit=_files)
752 progress(_updating, z, item=item, total=numupdates, unit=_files)
749 removed = len(actions['r'])
753 removed = len(actions['r'])
750
754
751 # get in parallel
755 # get in parallel
752 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'])
753 for i, item in prog:
757 for i, item in prog:
754 z += i
758 z += i
755 progress(_updating, z, item=item, total=numupdates, unit=_files)
759 progress(_updating, z, item=item, total=numupdates, unit=_files)
756 updated = len(actions['g'])
760 updated = len(actions['g'])
757
761
758 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
762 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
759 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
763 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
760
764
761 # forget (manifest only, just log it) (must come first)
765 # forget (manifest only, just log it) (must come first)
762 for f, args, msg in actions['f']:
766 for f, args, msg in actions['f']:
763 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
767 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
764 z += 1
768 z += 1
765 progress(_updating, z, item=f, total=numupdates, unit=_files)
769 progress(_updating, z, item=f, total=numupdates, unit=_files)
766
770
767 # re-add (manifest only, just log it)
771 # re-add (manifest only, just log it)
768 for f, args, msg in actions['a']:
772 for f, args, msg in actions['a']:
769 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
773 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
770 z += 1
774 z += 1
771 progress(_updating, z, item=f, total=numupdates, unit=_files)
775 progress(_updating, z, item=f, total=numupdates, unit=_files)
772
776
773 # keep (noop, just log it)
777 # keep (noop, just log it)
774 for f, args, msg in actions['k']:
778 for f, args, msg in actions['k']:
775 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
779 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
776 # no progress
780 # no progress
777
781
778 # merge
782 # merge
779 for f, args, msg in actions['m']:
783 for f, args, msg in actions['m']:
780 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
784 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
781 z += 1
785 z += 1
782 progress(_updating, z, item=f, total=numupdates, unit=_files)
786 progress(_updating, z, item=f, total=numupdates, unit=_files)
783 if f == '.hgsubstate': # subrepo states need updating
787 if f == '.hgsubstate': # subrepo states need updating
784 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
788 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
785 overwrite)
789 overwrite)
786 continue
790 continue
787 audit(f)
791 audit(f)
788 r = ms.resolve(f, wctx, labels=labels)
792 r = ms.resolve(f, wctx, labels=labels)
789 if r is not None and r > 0:
793 if r is not None and r > 0:
790 unresolved += 1
794 unresolved += 1
791 else:
795 else:
792 if r is None:
796 if r is None:
793 updated += 1
797 updated += 1
794 else:
798 else:
795 merged += 1
799 merged += 1
796
800
797 # directory rename, move local
801 # directory rename, move local
798 for f, args, msg in actions['dm']:
802 for f, args, msg in actions['dm']:
799 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
803 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
800 z += 1
804 z += 1
801 progress(_updating, z, item=f, total=numupdates, unit=_files)
805 progress(_updating, z, item=f, total=numupdates, unit=_files)
802 f0, flags = args
806 f0, flags = args
803 repo.ui.note(_("moving %s to %s\n") % (f0, f))
807 repo.ui.note(_("moving %s to %s\n") % (f0, f))
804 audit(f)
808 audit(f)
805 repo.wwrite(f, wctx.filectx(f0).data(), flags)
809 repo.wwrite(f, wctx.filectx(f0).data(), flags)
806 util.unlinkpath(repo.wjoin(f0))
810 util.unlinkpath(repo.wjoin(f0))
807 updated += 1
811 updated += 1
808
812
809 # local directory rename, get
813 # local directory rename, get
810 for f, args, msg in actions['dg']:
814 for f, args, msg in actions['dg']:
811 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
815 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
812 z += 1
816 z += 1
813 progress(_updating, z, item=f, total=numupdates, unit=_files)
817 progress(_updating, z, item=f, total=numupdates, unit=_files)
814 f0, flags = args
818 f0, flags = args
815 repo.ui.note(_("getting %s to %s\n") % (f0, f))
819 repo.ui.note(_("getting %s to %s\n") % (f0, f))
816 repo.wwrite(f, mctx.filectx(f0).data(), flags)
820 repo.wwrite(f, mctx.filectx(f0).data(), flags)
817 updated += 1
821 updated += 1
818
822
819 # divergent renames
823 # divergent renames
820 for f, args, msg in actions['dr']:
824 for f, args, msg in actions['dr']:
821 repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
825 repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
822 z += 1
826 z += 1
823 progress(_updating, z, item=f, total=numupdates, unit=_files)
827 progress(_updating, z, item=f, total=numupdates, unit=_files)
824 fl, = args
828 fl, = args
825 repo.ui.warn(_("note: possible conflict - %s was renamed "
829 repo.ui.warn(_("note: possible conflict - %s was renamed "
826 "multiple times to:\n") % f)
830 "multiple times to:\n") % f)
827 for nf in fl:
831 for nf in fl:
828 repo.ui.warn(" %s\n" % nf)
832 repo.ui.warn(" %s\n" % nf)
829
833
830 # rename and delete
834 # rename and delete
831 for f, args, msg in actions['rd']:
835 for f, args, msg in actions['rd']:
832 repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
836 repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
833 z += 1
837 z += 1
834 progress(_updating, z, item=f, total=numupdates, unit=_files)
838 progress(_updating, z, item=f, total=numupdates, unit=_files)
835 fl, = args
839 fl, = args
836 repo.ui.warn(_("note: possible conflict - %s was deleted "
840 repo.ui.warn(_("note: possible conflict - %s was deleted "
837 "and renamed to:\n") % f)
841 "and renamed to:\n") % f)
838 for nf in fl:
842 for nf in fl:
839 repo.ui.warn(" %s\n" % nf)
843 repo.ui.warn(" %s\n" % nf)
840
844
841 # exec
845 # exec
842 for f, args, msg in actions['e']:
846 for f, args, msg in actions['e']:
843 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
847 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
844 z += 1
848 z += 1
845 progress(_updating, z, item=f, total=numupdates, unit=_files)
849 progress(_updating, z, item=f, total=numupdates, unit=_files)
846 flags, = args
850 flags, = args
847 audit(f)
851 audit(f)
848 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
852 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
849 updated += 1
853 updated += 1
850
854
851 ms.commit()
855 ms.commit()
852 progress(_updating, None, total=numupdates, unit=_files)
856 progress(_updating, None, total=numupdates, unit=_files)
853
857
854 return updated, merged, removed, unresolved
858 return updated, merged, removed, unresolved
855
859
856 def recordupdates(repo, actions, branchmerge):
860 def recordupdates(repo, actions, branchmerge):
857 "record merge actions to the dirstate"
861 "record merge actions to the dirstate"
858 # remove (must come first)
862 # remove (must come first)
859 for f, args, msg in actions['r']:
863 for f, args, msg in actions['r']:
860 if branchmerge:
864 if branchmerge:
861 repo.dirstate.remove(f)
865 repo.dirstate.remove(f)
862 else:
866 else:
863 repo.dirstate.drop(f)
867 repo.dirstate.drop(f)
864
868
865 # forget (must come first)
869 # forget (must come first)
866 for f, args, msg in actions['f']:
870 for f, args, msg in actions['f']:
867 repo.dirstate.drop(f)
871 repo.dirstate.drop(f)
868
872
869 # re-add
873 # re-add
870 for f, args, msg in actions['a']:
874 for f, args, msg in actions['a']:
871 if not branchmerge:
875 if not branchmerge:
872 repo.dirstate.add(f)
876 repo.dirstate.add(f)
873
877
874 # exec change
878 # exec change
875 for f, args, msg in actions['e']:
879 for f, args, msg in actions['e']:
876 repo.dirstate.normallookup(f)
880 repo.dirstate.normallookup(f)
877
881
878 # keep
882 # keep
879 for f, args, msg in actions['k']:
883 for f, args, msg in actions['k']:
880 pass
884 pass
881
885
882 # get
886 # get
883 for f, args, msg in actions['g']:
887 for f, args, msg in actions['g']:
884 if branchmerge:
888 if branchmerge:
885 repo.dirstate.otherparent(f)
889 repo.dirstate.otherparent(f)
886 else:
890 else:
887 repo.dirstate.normal(f)
891 repo.dirstate.normal(f)
888
892
889 # merge
893 # merge
890 for f, args, msg in actions['m']:
894 for f, args, msg in actions['m']:
891 f1, f2, fa, move, anc = args
895 f1, f2, fa, move, anc = args
892 if branchmerge:
896 if branchmerge:
893 # We've done a branch merge, mark this file as merged
897 # We've done a branch merge, mark this file as merged
894 # so that we properly record the merger later
898 # so that we properly record the merger later
895 repo.dirstate.merge(f)
899 repo.dirstate.merge(f)
896 if f1 != f2: # copy/rename
900 if f1 != f2: # copy/rename
897 if move:
901 if move:
898 repo.dirstate.remove(f1)
902 repo.dirstate.remove(f1)
899 if f1 != f:
903 if f1 != f:
900 repo.dirstate.copy(f1, f)
904 repo.dirstate.copy(f1, f)
901 else:
905 else:
902 repo.dirstate.copy(f2, f)
906 repo.dirstate.copy(f2, f)
903 else:
907 else:
904 # We've update-merged a locally modified file, so
908 # We've update-merged a locally modified file, so
905 # we set the dirstate to emulate a normal checkout
909 # we set the dirstate to emulate a normal checkout
906 # of that file some time in the past. Thus our
910 # of that file some time in the past. Thus our
907 # merge will appear as a normal local file
911 # merge will appear as a normal local file
908 # modification.
912 # modification.
909 if f2 == f: # file not locally copied/moved
913 if f2 == f: # file not locally copied/moved
910 repo.dirstate.normallookup(f)
914 repo.dirstate.normallookup(f)
911 if move:
915 if move:
912 repo.dirstate.drop(f1)
916 repo.dirstate.drop(f1)
913
917
914 # directory rename, move local
918 # directory rename, move local
915 for f, args, msg in actions['dm']:
919 for f, args, msg in actions['dm']:
916 f0, flag = args
920 f0, flag = args
917 if branchmerge:
921 if branchmerge:
918 repo.dirstate.add(f)
922 repo.dirstate.add(f)
919 repo.dirstate.remove(f0)
923 repo.dirstate.remove(f0)
920 repo.dirstate.copy(f0, f)
924 repo.dirstate.copy(f0, f)
921 else:
925 else:
922 repo.dirstate.normal(f)
926 repo.dirstate.normal(f)
923 repo.dirstate.drop(f0)
927 repo.dirstate.drop(f0)
924
928
925 # directory rename, get
929 # directory rename, get
926 for f, args, msg in actions['dg']:
930 for f, args, msg in actions['dg']:
927 f0, flag = args
931 f0, flag = args
928 if branchmerge:
932 if branchmerge:
929 repo.dirstate.add(f)
933 repo.dirstate.add(f)
930 repo.dirstate.copy(f0, f)
934 repo.dirstate.copy(f0, f)
931 else:
935 else:
932 repo.dirstate.normal(f)
936 repo.dirstate.normal(f)
933
937
934 def update(repo, node, branchmerge, force, partial, ancestor=None,
938 def update(repo, node, branchmerge, force, partial, ancestor=None,
935 mergeancestor=False, labels=None):
939 mergeancestor=False, labels=None):
936 """
940 """
937 Perform a merge between the working directory and the given node
941 Perform a merge between the working directory and the given node
938
942
939 node = the node to update to, or None if unspecified
943 node = the node to update to, or None if unspecified
940 branchmerge = whether to merge between branches
944 branchmerge = whether to merge between branches
941 force = whether to force branch merging or file overwriting
945 force = whether to force branch merging or file overwriting
942 partial = a function to filter file lists (dirstate not updated)
946 partial = a function to filter file lists (dirstate not updated)
943 mergeancestor = whether it is merging with an ancestor. If true,
947 mergeancestor = whether it is merging with an ancestor. If true,
944 we should accept the incoming changes for any prompts that occur.
948 we should accept the incoming changes for any prompts that occur.
945 If false, merging with an ancestor (fast-forward) is only allowed
949 If false, merging with an ancestor (fast-forward) is only allowed
946 between different named branches. This flag is used by rebase extension
950 between different named branches. This flag is used by rebase extension
947 as a temporary fix and should be avoided in general.
951 as a temporary fix and should be avoided in general.
948
952
949 The table below shows all the behaviors of the update command
953 The table below shows all the behaviors of the update command
950 given the -c and -C or no options, whether the working directory
954 given the -c and -C or no options, whether the working directory
951 is dirty, whether a revision is specified, and the relationship of
955 is dirty, whether a revision is specified, and the relationship of
952 the parent rev to the target rev (linear, on the same named
956 the parent rev to the target rev (linear, on the same named
953 branch, or on another named branch).
957 branch, or on another named branch).
954
958
955 This logic is tested by test-update-branches.t.
959 This logic is tested by test-update-branches.t.
956
960
957 -c -C dirty rev | linear same cross
961 -c -C dirty rev | linear same cross
958 n n n n | ok (1) x
962 n n n n | ok (1) x
959 n n n y | ok ok ok
963 n n n y | ok ok ok
960 n n y n | merge (2) (2)
964 n n y n | merge (2) (2)
961 n n y y | merge (3) (3)
965 n n y y | merge (3) (3)
962 n y * * | --- discard ---
966 n y * * | --- discard ---
963 y n y * | --- (4) ---
967 y n y * | --- (4) ---
964 y n n * | --- ok ---
968 y n n * | --- ok ---
965 y y * * | --- (5) ---
969 y y * * | --- (5) ---
966
970
967 x = can't happen
971 x = can't happen
968 * = don't-care
972 * = don't-care
969 1 = abort: not a linear update (merge or update --check to force update)
973 1 = abort: not a linear update (merge or update --check to force update)
970 2 = abort: uncommitted changes (commit and merge, or update --clean to
974 2 = abort: uncommitted changes (commit and merge, or update --clean to
971 discard changes)
975 discard changes)
972 3 = abort: uncommitted changes (commit or update --clean to discard changes)
976 3 = abort: uncommitted changes (commit or update --clean to discard changes)
973 4 = abort: uncommitted changes (checked in commands.py)
977 4 = abort: uncommitted changes (checked in commands.py)
974 5 = incompatible options (checked in commands.py)
978 5 = incompatible options (checked in commands.py)
975
979
976 Return the same tuple as applyupdates().
980 Return the same tuple as applyupdates().
977 """
981 """
978
982
979 onode = node
983 onode = node
980 wlock = repo.wlock()
984 wlock = repo.wlock()
981 try:
985 try:
982 wc = repo[None]
986 wc = repo[None]
983 pl = wc.parents()
987 pl = wc.parents()
984 p1 = pl[0]
988 p1 = pl[0]
985 pas = [None]
989 pas = [None]
986 if ancestor is not None:
990 if ancestor is not None:
987 pas = [repo[ancestor]]
991 pas = [repo[ancestor]]
988
992
989 if node is None:
993 if node is None:
990 # Here is where we should consider bookmarks, divergent bookmarks,
994 # Here is where we should consider bookmarks, divergent bookmarks,
991 # foreground changesets (successors), and tip of current branch;
995 # foreground changesets (successors), and tip of current branch;
992 # but currently we are only checking the branch tips.
996 # but currently we are only checking the branch tips.
993 try:
997 try:
994 node = repo.branchtip(wc.branch())
998 node = repo.branchtip(wc.branch())
995 except errormod.RepoLookupError:
999 except errormod.RepoLookupError:
996 if wc.branch() == 'default': # no default branch!
1000 if wc.branch() == 'default': # no default branch!
997 node = repo.lookup('tip') # update to tip
1001 node = repo.lookup('tip') # update to tip
998 else:
1002 else:
999 raise util.Abort(_("branch %s not found") % wc.branch())
1003 raise util.Abort(_("branch %s not found") % wc.branch())
1000
1004
1001 if p1.obsolete() and not p1.children():
1005 if p1.obsolete() and not p1.children():
1002 # allow updating to successors
1006 # allow updating to successors
1003 successors = obsolete.successorssets(repo, p1.node())
1007 successors = obsolete.successorssets(repo, p1.node())
1004
1008
1005 # behavior of certain cases is as follows,
1009 # behavior of certain cases is as follows,
1006 #
1010 #
1007 # divergent changesets: update to highest rev, similar to what
1011 # divergent changesets: update to highest rev, similar to what
1008 # is currently done when there are more than one head
1012 # is currently done when there are more than one head
1009 # (i.e. 'tip')
1013 # (i.e. 'tip')
1010 #
1014 #
1011 # replaced changesets: same as divergent except we know there
1015 # replaced changesets: same as divergent except we know there
1012 # is no conflict
1016 # is no conflict
1013 #
1017 #
1014 # pruned changeset: no update is done; though, we could
1018 # pruned changeset: no update is done; though, we could
1015 # consider updating to the first non-obsolete parent,
1019 # consider updating to the first non-obsolete parent,
1016 # similar to what is current done for 'hg prune'
1020 # similar to what is current done for 'hg prune'
1017
1021
1018 if successors:
1022 if successors:
1019 # flatten the list here handles both divergent (len > 1)
1023 # flatten the list here handles both divergent (len > 1)
1020 # and the usual case (len = 1)
1024 # and the usual case (len = 1)
1021 successors = [n for sub in successors for n in sub]
1025 successors = [n for sub in successors for n in sub]
1022
1026
1023 # get the max revision for the given successors set,
1027 # get the max revision for the given successors set,
1024 # i.e. the 'tip' of a set
1028 # i.e. the 'tip' of a set
1025 node = repo.revs('max(%ln)', successors).first()
1029 node = repo.revs('max(%ln)', successors).first()
1026 pas = [p1]
1030 pas = [p1]
1027
1031
1028 overwrite = force and not branchmerge
1032 overwrite = force and not branchmerge
1029
1033
1030 p2 = repo[node]
1034 p2 = repo[node]
1031 if pas[0] is None:
1035 if pas[0] is None:
1032 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1036 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1033 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1037 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1034 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1038 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1035 else:
1039 else:
1036 pas = [p1.ancestor(p2, warn=branchmerge)]
1040 pas = [p1.ancestor(p2, warn=branchmerge)]
1037
1041
1038 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1042 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1039
1043
1040 ### check phase
1044 ### check phase
1041 if not overwrite and len(pl) > 1:
1045 if not overwrite and len(pl) > 1:
1042 raise util.Abort(_("outstanding uncommitted merge"))
1046 raise util.Abort(_("outstanding uncommitted merge"))
1043 if branchmerge:
1047 if branchmerge:
1044 if pas == [p2]:
1048 if pas == [p2]:
1045 raise util.Abort(_("merging with a working directory ancestor"
1049 raise util.Abort(_("merging with a working directory ancestor"
1046 " has no effect"))
1050 " has no effect"))
1047 elif pas == [p1]:
1051 elif pas == [p1]:
1048 if not mergeancestor and p1.branch() == p2.branch():
1052 if not mergeancestor and p1.branch() == p2.branch():
1049 raise util.Abort(_("nothing to merge"),
1053 raise util.Abort(_("nothing to merge"),
1050 hint=_("use 'hg update' "
1054 hint=_("use 'hg update' "
1051 "or check 'hg heads'"))
1055 "or check 'hg heads'"))
1052 if not force and (wc.files() or wc.deleted()):
1056 if not force and (wc.files() or wc.deleted()):
1053 raise util.Abort(_("uncommitted changes"),
1057 raise util.Abort(_("uncommitted changes"),
1054 hint=_("use 'hg status' to list changes"))
1058 hint=_("use 'hg status' to list changes"))
1055 for s in sorted(wc.substate):
1059 for s in sorted(wc.substate):
1056 if wc.sub(s).dirty():
1060 if wc.sub(s).dirty():
1057 raise util.Abort(_("uncommitted changes in "
1061 raise util.Abort(_("uncommitted changes in "
1058 "subrepository '%s'") % s)
1062 "subrepository '%s'") % s)
1059
1063
1060 elif not overwrite:
1064 elif not overwrite:
1061 if p1 == p2: # no-op update
1065 if p1 == p2: # no-op update
1062 # call the hooks and exit early
1066 # call the hooks and exit early
1063 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1067 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1064 repo.hook('update', parent1=xp2, parent2='', error=0)
1068 repo.hook('update', parent1=xp2, parent2='', error=0)
1065 return 0, 0, 0, 0
1069 return 0, 0, 0, 0
1066
1070
1067 if pas not in ([p1], [p2]): # nonlinear
1071 if pas not in ([p1], [p2]): # nonlinear
1068 dirty = wc.dirty(missing=True)
1072 dirty = wc.dirty(missing=True)
1069 if dirty or onode is None:
1073 if dirty or onode is None:
1070 # Branching is a bit strange to ensure we do the minimal
1074 # Branching is a bit strange to ensure we do the minimal
1071 # amount of call to obsolete.background.
1075 # amount of call to obsolete.background.
1072 foreground = obsolete.foreground(repo, [p1.node()])
1076 foreground = obsolete.foreground(repo, [p1.node()])
1073 # note: the <node> variable contains a random identifier
1077 # note: the <node> variable contains a random identifier
1074 if repo[node].node() in foreground:
1078 if repo[node].node() in foreground:
1075 pas = [p1] # allow updating to successors
1079 pas = [p1] # allow updating to successors
1076 elif dirty:
1080 elif dirty:
1077 msg = _("uncommitted changes")
1081 msg = _("uncommitted changes")
1078 if onode is None:
1082 if onode is None:
1079 hint = _("commit and merge, or update --clean to"
1083 hint = _("commit and merge, or update --clean to"
1080 " discard changes")
1084 " discard changes")
1081 else:
1085 else:
1082 hint = _("commit or update --clean to discard"
1086 hint = _("commit or update --clean to discard"
1083 " changes")
1087 " changes")
1084 raise util.Abort(msg, hint=hint)
1088 raise util.Abort(msg, hint=hint)
1085 else: # node is none
1089 else: # node is none
1086 msg = _("not a linear update")
1090 msg = _("not a linear update")
1087 hint = _("merge or update --check to force update")
1091 hint = _("merge or update --check to force update")
1088 raise util.Abort(msg, hint=hint)
1092 raise util.Abort(msg, hint=hint)
1089 else:
1093 else:
1090 # Allow jumping branches if clean and specific rev given
1094 # Allow jumping branches if clean and specific rev given
1091 pas = [p1]
1095 pas = [p1]
1092
1096
1093 followcopies = False
1097 followcopies = False
1094 if overwrite:
1098 if overwrite:
1095 pas = [wc]
1099 pas = [wc]
1096 elif pas == [p2]: # backwards
1100 elif pas == [p2]: # backwards
1097 pas = [wc.p1()]
1101 pas = [wc.p1()]
1098 elif not branchmerge and not wc.dirty(missing=True):
1102 elif not branchmerge and not wc.dirty(missing=True):
1099 pass
1103 pass
1100 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1104 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1101 followcopies = True
1105 followcopies = True
1102
1106
1103 ### calculate phase
1107 ### calculate phase
1104 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1108 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1105 partial, mergeancestor, followcopies)
1109 partial, mergeancestor, followcopies)
1106
1110
1107 ### apply phase
1111 ### apply phase
1108 if not branchmerge: # just jump to the new rev
1112 if not branchmerge: # just jump to the new rev
1109 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1113 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1110 if not partial:
1114 if not partial:
1111 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1115 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1112 # note that we're in the middle of an update
1116 # note that we're in the middle of an update
1113 repo.vfs.write('updatestate', p2.hex())
1117 repo.vfs.write('updatestate', p2.hex())
1114
1118
1115 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1119 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1116
1120
1117 if not partial:
1121 if not partial:
1118 repo.dirstate.beginparentchange()
1122 repo.dirstate.beginparentchange()
1119 repo.setparents(fp1, fp2)
1123 repo.setparents(fp1, fp2)
1120 recordupdates(repo, actions, branchmerge)
1124 recordupdates(repo, actions, branchmerge)
1121 # update completed, clear state
1125 # update completed, clear state
1122 util.unlink(repo.join('updatestate'))
1126 util.unlink(repo.join('updatestate'))
1123
1127
1124 if not branchmerge:
1128 if not branchmerge:
1125 repo.dirstate.setbranch(p2.branch())
1129 repo.dirstate.setbranch(p2.branch())
1126 repo.dirstate.endparentchange()
1130 repo.dirstate.endparentchange()
1127 finally:
1131 finally:
1128 wlock.release()
1132 wlock.release()
1129
1133
1130 if not partial:
1134 if not partial:
1131 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1135 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1132 return stats
1136 return stats
1133
1137
1134 def graft(repo, ctx, pctx, labels):
1138 def graft(repo, ctx, pctx, labels):
1135 """Do a graft-like merge.
1139 """Do a graft-like merge.
1136
1140
1137 This is a merge where the merge ancestor is chosen such that one
1141 This is a merge where the merge ancestor is chosen such that one
1138 or more changesets are grafted onto the current changeset. In
1142 or more changesets are grafted onto the current changeset. In
1139 addition to the merge, this fixes up the dirstate to include only
1143 addition to the merge, this fixes up the dirstate to include only
1140 a single parent and tries to duplicate any renames/copies
1144 a single parent and tries to duplicate any renames/copies
1141 appropriately.
1145 appropriately.
1142
1146
1143 ctx - changeset to rebase
1147 ctx - changeset to rebase
1144 pctx - merge base, usually ctx.p1()
1148 pctx - merge base, usually ctx.p1()
1145 labels - merge labels eg ['local', 'graft']
1149 labels - merge labels eg ['local', 'graft']
1146
1150
1147 """
1151 """
1148
1152
1149 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1153 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1150 labels=labels)
1154 labels=labels)
1151 # drop the second merge parent
1155 # drop the second merge parent
1152 repo.dirstate.beginparentchange()
1156 repo.dirstate.beginparentchange()
1153 repo.setparents(repo['.'].node(), nullid)
1157 repo.setparents(repo['.'].node(), nullid)
1154 repo.dirstate.write()
1158 repo.dirstate.write()
1155 # fix up dirstate for copies and renames
1159 # fix up dirstate for copies and renames
1156 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1160 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1157 repo.dirstate.endparentchange()
1161 repo.dirstate.endparentchange()
1158 return stats
1162 return stats
@@ -1,223 +1,230 b''
1 $ hg init t
1 $ hg init t
2 $ cd t
2 $ cd t
3
3
4 $ mkdir a
4 $ mkdir a
5 $ echo foo > a/a
5 $ echo foo > a/a
6 $ echo bar > a/b
6 $ echo bar > a/b
7 $ hg ci -Am "0"
7 $ hg ci -Am "0"
8 adding a/a
8 adding a/a
9 adding a/b
9 adding a/b
10
10
11 $ hg co -C 0
11 $ hg co -C 0
12 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 $ hg mv a b
13 $ hg mv a b
14 moving a/a to b/a (glob)
14 moving a/a to b/a (glob)
15 moving a/b to b/b (glob)
15 moving a/b to b/b (glob)
16 $ hg ci -m "1 mv a/ b/"
16 $ hg ci -m "1 mv a/ b/"
17
17
18 $ hg co -C 0
18 $ hg co -C 0
19 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
19 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
20 $ echo baz > a/c
20 $ echo baz > a/c
21 $ echo quux > a/d
21 $ echo quux > a/d
22 $ hg add a/c
22 $ hg add a/c
23 $ hg ci -m "2 add a/c"
23 $ hg ci -m "2 add a/c"
24 created new head
24 created new head
25
25
26 $ hg merge --debug 1
26 $ hg merge --debug 1
27 searching for copies back to rev 1
27 searching for copies back to rev 1
28 unmatched files in local:
28 unmatched files in local:
29 a/c
29 a/c
30 unmatched files in other:
30 unmatched files in other:
31 b/a
31 b/a
32 b/b
32 b/b
33 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
33 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
34 src: 'a/a' -> dst: 'b/a'
34 src: 'a/a' -> dst: 'b/a'
35 src: 'a/b' -> dst: 'b/b'
35 src: 'a/b' -> dst: 'b/b'
36 checking for directory renames
36 checking for directory renames
37 discovered dir src: 'a/' -> dst: 'b/'
37 discovered dir src: 'a/' -> dst: 'b/'
38 pending file src: 'a/c' -> dst: 'b/c'
38 pending file src: 'a/c' -> dst: 'b/c'
39 resolving manifests
39 resolving manifests
40 branchmerge: True, force: False, partial: False
40 branchmerge: True, force: False, partial: False
41 ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
41 ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
42 a/a: other deleted -> r
42 a/a: other deleted -> r
43 removing a/a
43 removing a/a
44 a/b: other deleted -> r
44 a/b: other deleted -> r
45 removing a/b
45 removing a/b
46 updating: a/b 2/5 files (40.00%)
46 updating: a/b 2/5 files (40.00%)
47 b/a: remote created -> g
47 b/a: remote created -> g
48 getting b/a
48 getting b/a
49 b/b: remote created -> g
49 b/b: remote created -> g
50 getting b/b
50 getting b/b
51 updating: b/b 4/5 files (80.00%)
51 updating: b/b 4/5 files (80.00%)
52 b/c: remote directory rename - move from a/c -> dm
52 b/c: remote directory rename - move from a/c -> dm
53 updating: b/c 5/5 files (100.00%)
53 updating: b/c 5/5 files (100.00%)
54 moving a/c to b/c (glob)
54 moving a/c to b/c (glob)
55 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
55 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
56 (branch merge, don't forget to commit)
56 (branch merge, don't forget to commit)
57
57
58 $ echo a/* b/*
58 $ echo a/* b/*
59 a/d b/a b/b b/c
59 a/d b/a b/b b/c
60 $ hg st -C
60 $ hg st -C
61 M b/a
61 M b/a
62 M b/b
62 M b/b
63 A b/c
63 A b/c
64 a/c
64 a/c
65 R a/a
65 R a/a
66 R a/b
66 R a/b
67 R a/c
67 R a/c
68 ? a/d
68 ? a/d
69 $ hg ci -m "3 merge 2+1"
69 $ hg ci -m "3 merge 2+1"
70 $ hg debugrename b/c
70 $ hg debugrename b/c
71 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
71 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
72
72
73 $ hg co -C 1
73 $ hg co -C 1
74 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
74 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
75 $ hg merge --debug 2
75 $ hg merge --debug 2
76 searching for copies back to rev 1
76 searching for copies back to rev 1
77 unmatched files in local:
77 unmatched files in local:
78 b/a
78 b/a
79 b/b
79 b/b
80 unmatched files in other:
80 unmatched files in other:
81 a/c
81 a/c
82 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
82 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
83 src: 'a/a' -> dst: 'b/a'
83 src: 'a/a' -> dst: 'b/a'
84 src: 'a/b' -> dst: 'b/b'
84 src: 'a/b' -> dst: 'b/b'
85 checking for directory renames
85 checking for directory renames
86 discovered dir src: 'a/' -> dst: 'b/'
86 discovered dir src: 'a/' -> dst: 'b/'
87 pending file src: 'a/c' -> dst: 'b/c'
87 pending file src: 'a/c' -> dst: 'b/c'
88 resolving manifests
88 resolving manifests
89 branchmerge: True, force: False, partial: False
89 branchmerge: True, force: False, partial: False
90 ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
90 ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
91 b/c: local directory rename - get from a/c -> dg
91 b/c: local directory rename - get from a/c -> dg
92 updating: b/c 1/1 files (100.00%)
92 updating: b/c 1/1 files (100.00%)
93 getting a/c to b/c
93 getting a/c to b/c
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 (branch merge, don't forget to commit)
95 (branch merge, don't forget to commit)
96
96
97 $ echo a/* b/*
97 $ echo a/* b/*
98 a/d b/a b/b b/c
98 a/d b/a b/b b/c
99 $ hg st -C
99 $ hg st -C
100 A b/c
100 A b/c
101 a/c
101 a/c
102 ? a/d
102 ? a/d
103 $ hg ci -m "4 merge 1+2"
103 $ hg ci -m "4 merge 1+2"
104 created new head
104 created new head
105 $ hg debugrename b/c
105 $ hg debugrename b/c
106 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
106 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
107
107
108 Local directory rename with conflicting file added in remote source directory
108 Local directory rename with conflicting file added in remote source directory
109 and untracked in local target directory.
109 and untracked in local target directory.
110
110
111 BROKEN: the uncommitted file is overwritten; we should abort
111 BROKEN: the uncommitted file is overwritten; we should abort
112
112
113 $ hg co -qC 1
113 $ hg co -qC 1
114 $ echo target > b/c
114 $ echo target > b/c
115 $ hg merge 2
115 $ hg merge 2
116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 (branch merge, don't forget to commit)
117 (branch merge, don't forget to commit)
118 $ hg st -A
118 $ hg st -A
119 A b/c
119 A b/c
120 a/c
120 a/c
121 ? a/d
121 ? a/d
122 C b/a
122 C b/a
123 C b/b
123 C b/b
124 $ cat b/c
124 $ cat b/c
125 baz
125 baz
126
126
127 Local directory rename with conflicting file added in remote source directory
127 Local directory rename with conflicting file added in remote source directory
128 and committed in local target directory.
128 and committed in local target directory.
129
129
130 BROKEN: the local file is overwritten; it should be merged
130 BROKEN: the local file is overwritten; it should be merged
131
131
132 $ hg co -qC 1
132 $ hg co -qC 1
133 $ echo target > b/c
133 $ echo target > b/c
134 $ hg add b/c
134 $ hg add b/c
135 $ hg commit -qm 'new file in target directory'
135 $ hg commit -qm 'new file in target directory'
136 $ hg merge 2
136 $ hg merge 2
137 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 (branch merge, don't forget to commit)
138 (branch merge, don't forget to commit)
139 $ hg st -A
139 $ hg st -A
140 A b/c
140 A b/c
141 a/c
141 a/c
142 ? a/d
142 ? a/d
143 C b/a
143 C b/a
144 C b/b
144 C b/b
145 $ cat b/c
145 $ cat b/c
146 baz
146 baz
147
147
148 Remote directory rename with conflicting file added in remote target directory
148 Remote directory rename with conflicting file added in remote target directory
149 and committed in local source directory.
149 and committed in local source directory.
150
150
151 BROKEN: the remote is ignored; it should be merged
152
153 $ hg co -qC 2
151 $ hg co -qC 2
154 $ rm b/c
152 $ rm b/c
155 $ hg st -A
153 $ hg st -A
156 ? a/d
154 ? a/d
157 C a/a
155 C a/a
158 C a/b
156 C a/b
159 C a/c
157 C a/c
160 $ hg merge 5
158 $ hg merge 5
161 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
159 merging a/c and b/c to b/c
162 (branch merge, don't forget to commit)
160 warning: conflicts during merge.
161 merging b/c incomplete! (edit conflicts, then use 'hg resolve --mark')
162 2 files updated, 0 files merged, 2 files removed, 1 files unresolved
163 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
164 [1]
163 $ hg st -A
165 $ hg st -A
164 M b/a
166 M b/a
165 M b/b
167 M b/b
166 A b/c
168 M b/c
167 a/c
169 a/c
168 R a/a
170 R a/a
169 R a/b
171 R a/b
170 R a/c
172 R a/c
171 ? a/d
173 ? a/d
174 ? b/c.orig
172 $ cat b/c
175 $ cat b/c
176 <<<<<<< local: ce36d17b18fb - test: 2 add a/c
173 baz
177 baz
178 =======
179 target
180 >>>>>>> other: f1c50ca4f127 - test: new file in target directory
174
181
175 Second scenario with two repos:
182 Second scenario with two repos:
176
183
177 $ cd ..
184 $ cd ..
178 $ hg init r1
185 $ hg init r1
179 $ cd r1
186 $ cd r1
180 $ mkdir a
187 $ mkdir a
181 $ echo foo > a/f
188 $ echo foo > a/f
182 $ hg add a
189 $ hg add a
183 adding a/f (glob)
190 adding a/f (glob)
184 $ hg ci -m "a/f == foo"
191 $ hg ci -m "a/f == foo"
185 $ cd ..
192 $ cd ..
186
193
187 $ hg clone r1 r2
194 $ hg clone r1 r2
188 updating to branch default
195 updating to branch default
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
196 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 $ cd r2
197 $ cd r2
191 $ hg mv a b
198 $ hg mv a b
192 moving a/f to b/f (glob)
199 moving a/f to b/f (glob)
193 $ echo foo1 > b/f
200 $ echo foo1 > b/f
194 $ hg ci -m" a -> b, b/f == foo1"
201 $ hg ci -m" a -> b, b/f == foo1"
195 $ cd ..
202 $ cd ..
196
203
197 $ cd r1
204 $ cd r1
198 $ mkdir a/aa
205 $ mkdir a/aa
199 $ echo bar > a/aa/g
206 $ echo bar > a/aa/g
200 $ hg add a/aa
207 $ hg add a/aa
201 adding a/aa/g (glob)
208 adding a/aa/g (glob)
202 $ hg ci -m "a/aa/g"
209 $ hg ci -m "a/aa/g"
203 $ hg pull ../r2
210 $ hg pull ../r2
204 pulling from ../r2
211 pulling from ../r2
205 searching for changes
212 searching for changes
206 adding changesets
213 adding changesets
207 adding manifests
214 adding manifests
208 adding file changes
215 adding file changes
209 added 1 changesets with 1 changes to 1 files (+1 heads)
216 added 1 changesets with 1 changes to 1 files (+1 heads)
210 (run 'hg heads' to see heads, 'hg merge' to merge)
217 (run 'hg heads' to see heads, 'hg merge' to merge)
211
218
212 $ hg merge
219 $ hg merge
213 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
220 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
214 (branch merge, don't forget to commit)
221 (branch merge, don't forget to commit)
215
222
216 $ hg st -C
223 $ hg st -C
217 M b/f
224 M b/f
218 A b/aa/g
225 A b/aa/g
219 a/aa/g
226 a/aa/g
220 R a/aa/g
227 R a/aa/g
221 R a/f
228 R a/f
222
229
223 $ cd ..
230 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now