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