##// END OF EJS Templates
merge: add "other" file node in the merge state file...
Pierre-Yves David -
r20593:3678707e stable
parent child Browse files
Show More
@@ -1,918 +1,941
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import struct
8 import struct
9
9
10 from node import nullid, nullrev, hex, bin
10 from node import nullid, nullrev, hex, bin
11 from i18n import _
11 from i18n import _
12 from mercurial import obsolete
12 from mercurial import obsolete
13 import error, util, filemerge, copies, subrepo, worker, dicthelpers
13 import error, util, filemerge, copies, subrepo, worker, dicthelpers
14 import errno, os, shutil
14 import errno, os, shutil
15
15
16 _pack = struct.pack
16 _pack = struct.pack
17 _unpack = struct.unpack
17 _unpack = struct.unpack
18
18
19 def _droponode(data):
20 # used for compatibility for v1
21 bits = data.split("\0")
22 bits = bits[:-2] + bits[-1:]
23 return "\0".join(bits)
24
19 class mergestate(object):
25 class mergestate(object):
20 '''track 3-way merge state of individual files
26 '''track 3-way merge state of individual files
21
27
22 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
23 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
24 format can store new kind of field.
30 format can store new kind of field.
25
31
26 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:
27
33
28 [type][length][content]
34 [type][length][content]
29
35
30 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
31 arbitrary suites of bytes of lenght `length`.
37 arbitrary suites of bytes of lenght `length`.
32
38
33 Type should be a letter. Capital letter are mandatory record, Mercurial
39 Type should be a letter. Capital letter are mandatory record, Mercurial
34 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.
35
41
36 Currently known record:
42 Currently known record:
37
43
38 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)
39 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)
40 F: a file to be merged entry
46 F: a file to be merged entry
41 '''
47 '''
42 statepathv1 = "merge/state"
48 statepathv1 = "merge/state"
43 statepathv2 = "merge/state2"
49 statepathv2 = "merge/state2"
44 def __init__(self, repo):
50 def __init__(self, repo):
45 self._repo = repo
51 self._repo = repo
46 self._dirty = False
52 self._dirty = False
47 self._read()
53 self._read()
48 def reset(self, node=None, other=None):
54 def reset(self, node=None, other=None):
49 self._state = {}
55 self._state = {}
50 if node:
56 if node:
51 self._local = node
57 self._local = node
52 self._other = other
58 self._other = other
53 shutil.rmtree(self._repo.join("merge"), True)
59 shutil.rmtree(self._repo.join("merge"), True)
54 self._dirty = False
60 self._dirty = False
55 def _read(self):
61 def _read(self):
56 self._state = {}
62 self._state = {}
57 records = self._readrecords()
63 records = self._readrecords()
58 for rtype, record in records:
64 for rtype, record in records:
59 if rtype == 'L':
65 if rtype == 'L':
60 self._local = bin(record)
66 self._local = bin(record)
61 elif rtype == 'O':
67 elif rtype == 'O':
62 self._other = bin(record)
68 self._other = bin(record)
63 elif rtype == "F":
69 elif rtype == "F":
64 bits = record.split("\0")
70 bits = record.split("\0")
65 self._state[bits[0]] = bits[1:]
71 self._state[bits[0]] = bits[1:]
66 elif not rtype.islower():
72 elif not rtype.islower():
67 raise util.Abort(_('unsupported merge state record:'
73 raise util.Abort(_('unsupported merge state record:'
68 % rtype))
74 % rtype))
69 self._dirty = False
75 self._dirty = False
70 def _readrecords(self):
76 def _readrecords(self):
71 v1records = self._readrecordsv1()
77 v1records = self._readrecordsv1()
72 v2records = self._readrecordsv2()
78 v2records = self._readrecordsv2()
73 allv2 = set(v2records)
79 oldv2 = set() # old format version of v2 record
74 for rev in v1records:
80 for rec in v2records:
75 if rev not in allv2:
81 if rec[0] == 'L':
82 oldv2.add(rec)
83 elif rec[0] == 'F':
84 # drop the onode data (not contained in v1)
85 oldv2.add(('F', _droponode(rec[1])))
86 for rec in v1records:
87 if rec not in oldv2:
76 # v1 file is newer than v2 file, use it
88 # v1 file is newer than v2 file, use it
77 # we have to infer the "other" changeset of the merge
89 # we have to infer the "other" changeset of the merge
78 # we cannot do better than that with v1 of the format
90 # we cannot do better than that with v1 of the format
79 mctx = self._repo[None].parents()[-1]
91 mctx = self._repo[None].parents()[-1]
80 v1records.append(('O', mctx.hex()))
92 v1records.append(('O', mctx.hex()))
93 # add place holder "other" file node information
94 # nobody is using it yet so we do no need to fetch the data
95 # if mctx was wrong `mctx[bits[-2]]` may fails.
96 for idx, r in enumerate(v1records):
97 if r[0] == 'F':
98 bits = r[1].split("\0")
99 bits.insert(-2, '')
100 v1records[idx] = (r[0], "\0".join(bits))
81 return v1records
101 return v1records
82 else:
102 else:
83 return v2records
103 return v2records
84 def _readrecordsv1(self):
104 def _readrecordsv1(self):
85 records = []
105 records = []
86 try:
106 try:
87 f = self._repo.opener(self.statepathv1)
107 f = self._repo.opener(self.statepathv1)
88 for i, l in enumerate(f):
108 for i, l in enumerate(f):
89 if i == 0:
109 if i == 0:
90 records.append(('L', l[:-1]))
110 records.append(('L', l[:-1]))
91 else:
111 else:
92 records.append(('F', l[:-1]))
112 records.append(('F', l[:-1]))
93 f.close()
113 f.close()
94 except IOError, err:
114 except IOError, err:
95 if err.errno != errno.ENOENT:
115 if err.errno != errno.ENOENT:
96 raise
116 raise
97 return records
117 return records
98 def _readrecordsv2(self):
118 def _readrecordsv2(self):
99 records = []
119 records = []
100 try:
120 try:
101 f = self._repo.opener(self.statepathv2)
121 f = self._repo.opener(self.statepathv2)
102 data = f.read()
122 data = f.read()
103 off = 0
123 off = 0
104 end = len(data)
124 end = len(data)
105 while off < end:
125 while off < end:
106 rtype = data[off]
126 rtype = data[off]
107 off += 1
127 off += 1
108 lenght = _unpack('>I', data[off:(off + 4)])[0]
128 lenght = _unpack('>I', data[off:(off + 4)])[0]
109 off += 4
129 off += 4
110 record = data[off:(off + lenght)]
130 record = data[off:(off + lenght)]
111 off += lenght
131 off += lenght
112 records.append((rtype, record))
132 records.append((rtype, record))
113 f.close()
133 f.close()
114 except IOError, err:
134 except IOError, err:
115 if err.errno != errno.ENOENT:
135 if err.errno != errno.ENOENT:
116 raise
136 raise
117 return records
137 return records
118 def commit(self):
138 def commit(self):
119 if self._dirty:
139 if self._dirty:
120 records = []
140 records = []
121 records.append(("L", hex(self._local)))
141 records.append(("L", hex(self._local)))
122 records.append(("O", hex(self._other)))
142 records.append(("O", hex(self._other)))
123 for d, v in self._state.iteritems():
143 for d, v in self._state.iteritems():
124 records.append(("F", "\0".join([d] + v)))
144 records.append(("F", "\0".join([d] + v)))
125 self._writerecords(records)
145 self._writerecords(records)
126 self._dirty = False
146 self._dirty = False
127 def _writerecords(self, records):
147 def _writerecords(self, records):
128 self._writerecordsv1(records)
148 self._writerecordsv1(records)
129 self._writerecordsv2(records)
149 self._writerecordsv2(records)
130 def _writerecordsv1(self, records):
150 def _writerecordsv1(self, records):
131 f = self._repo.opener(self.statepathv1, "w")
151 f = self._repo.opener(self.statepathv1, "w")
132 irecords = iter(records)
152 irecords = iter(records)
133 lrecords = irecords.next()
153 lrecords = irecords.next()
134 assert lrecords[0] == 'L'
154 assert lrecords[0] == 'L'
135 f.write(hex(self._local) + "\n")
155 f.write(hex(self._local) + "\n")
136 for rtype, data in irecords:
156 for rtype, data in irecords:
137 if rtype == "F":
157 if rtype == "F":
138 f.write("%s\n" % data)
158 f.write("%s\n" % _droponode(data))
139 f.close()
159 f.close()
140 def _writerecordsv2(self, records):
160 def _writerecordsv2(self, records):
141 f = self._repo.opener(self.statepathv2, "w")
161 f = self._repo.opener(self.statepathv2, "w")
142 for key, data in records:
162 for key, data in records:
143 assert len(key) == 1
163 assert len(key) == 1
144 format = ">sI%is" % len(data)
164 format = ">sI%is" % len(data)
145 f.write(_pack(format, key, len(data), data))
165 f.write(_pack(format, key, len(data), data))
146 f.close()
166 f.close()
147 def add(self, fcl, fco, fca, fd):
167 def add(self, fcl, fco, fca, fd):
148 hash = util.sha1(fcl.path()).hexdigest()
168 hash = util.sha1(fcl.path()).hexdigest()
149 self._repo.opener.write("merge/" + hash, fcl.data())
169 self._repo.opener.write("merge/" + hash, fcl.data())
150 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
170 self._state[fd] = ['u', hash, fcl.path(),
151 hex(fca.filenode()), fco.path(), fcl.flags()]
171 fca.path(), hex(fca.filenode()),
172 fco.path(), hex(fco.filenode()),
173 fcl.flags()]
152 self._dirty = True
174 self._dirty = True
153 def __contains__(self, dfile):
175 def __contains__(self, dfile):
154 return dfile in self._state
176 return dfile in self._state
155 def __getitem__(self, dfile):
177 def __getitem__(self, dfile):
156 return self._state[dfile][0]
178 return self._state[dfile][0]
157 def __iter__(self):
179 def __iter__(self):
158 l = self._state.keys()
180 l = self._state.keys()
159 l.sort()
181 l.sort()
160 for f in l:
182 for f in l:
161 yield f
183 yield f
162 def files(self):
184 def files(self):
163 return self._state.keys()
185 return self._state.keys()
164 def mark(self, dfile, state):
186 def mark(self, dfile, state):
165 self._state[dfile][0] = state
187 self._state[dfile][0] = state
166 self._dirty = True
188 self._dirty = True
167 def resolve(self, dfile, wctx, octx):
189 def resolve(self, dfile, wctx, octx):
168 if self[dfile] == 'r':
190 if self[dfile] == 'r':
169 return 0
191 return 0
170 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
192 stateentry = self._state[dfile]
193 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
171 fcd = wctx[dfile]
194 fcd = wctx[dfile]
172 fco = octx[ofile]
195 fco = octx[ofile]
173 fca = self._repo.filectx(afile, fileid=anode)
196 fca = self._repo.filectx(afile, fileid=anode)
174 # "premerge" x flags
197 # "premerge" x flags
175 flo = fco.flags()
198 flo = fco.flags()
176 fla = fca.flags()
199 fla = fca.flags()
177 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
200 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
178 if fca.node() == nullid:
201 if fca.node() == nullid:
179 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
202 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
180 afile)
203 afile)
181 elif flags == fla:
204 elif flags == fla:
182 flags = flo
205 flags = flo
183 # restore local
206 # restore local
184 f = self._repo.opener("merge/" + hash)
207 f = self._repo.opener("merge/" + hash)
185 self._repo.wwrite(dfile, f.read(), flags)
208 self._repo.wwrite(dfile, f.read(), flags)
186 f.close()
209 f.close()
187 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
210 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
188 if r is None:
211 if r is None:
189 # no real conflict
212 # no real conflict
190 del self._state[dfile]
213 del self._state[dfile]
191 elif not r:
214 elif not r:
192 self.mark(dfile, 'r')
215 self.mark(dfile, 'r')
193 return r
216 return r
194
217
195 def _checkunknownfile(repo, wctx, mctx, f):
218 def _checkunknownfile(repo, wctx, mctx, f):
196 return (not repo.dirstate._ignore(f)
219 return (not repo.dirstate._ignore(f)
197 and os.path.isfile(repo.wjoin(f))
220 and os.path.isfile(repo.wjoin(f))
198 and repo.wopener.audit.check(f)
221 and repo.wopener.audit.check(f)
199 and repo.dirstate.normalize(f) not in repo.dirstate
222 and repo.dirstate.normalize(f) not in repo.dirstate
200 and mctx[f].cmp(wctx[f]))
223 and mctx[f].cmp(wctx[f]))
201
224
202 def _checkunknown(repo, wctx, mctx):
225 def _checkunknown(repo, wctx, mctx):
203 "check for collisions between unknown files and files in mctx"
226 "check for collisions between unknown files and files in mctx"
204
227
205 error = False
228 error = False
206 for f in mctx:
229 for f in mctx:
207 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
230 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
208 error = True
231 error = True
209 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
232 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
210 if error:
233 if error:
211 raise util.Abort(_("untracked files in working directory differ "
234 raise util.Abort(_("untracked files in working directory differ "
212 "from files in requested revision"))
235 "from files in requested revision"))
213
236
214 def _forgetremoved(wctx, mctx, branchmerge):
237 def _forgetremoved(wctx, mctx, branchmerge):
215 """
238 """
216 Forget removed files
239 Forget removed files
217
240
218 If we're jumping between revisions (as opposed to merging), and if
241 If we're jumping between revisions (as opposed to merging), and if
219 neither the working directory nor the target rev has the file,
242 neither the working directory nor the target rev has the file,
220 then we need to remove it from the dirstate, to prevent the
243 then we need to remove it from the dirstate, to prevent the
221 dirstate from listing the file when it is no longer in the
244 dirstate from listing the file when it is no longer in the
222 manifest.
245 manifest.
223
246
224 If we're merging, and the other revision has removed a file
247 If we're merging, and the other revision has removed a file
225 that is not present in the working directory, we need to mark it
248 that is not present in the working directory, we need to mark it
226 as removed.
249 as removed.
227 """
250 """
228
251
229 actions = []
252 actions = []
230 state = branchmerge and 'r' or 'f'
253 state = branchmerge and 'r' or 'f'
231 for f in wctx.deleted():
254 for f in wctx.deleted():
232 if f not in mctx:
255 if f not in mctx:
233 actions.append((f, state, None, "forget deleted"))
256 actions.append((f, state, None, "forget deleted"))
234
257
235 if not branchmerge:
258 if not branchmerge:
236 for f in wctx.removed():
259 for f in wctx.removed():
237 if f not in mctx:
260 if f not in mctx:
238 actions.append((f, "f", None, "forget removed"))
261 actions.append((f, "f", None, "forget removed"))
239
262
240 return actions
263 return actions
241
264
242 def _checkcollision(repo, wmf, actions, prompts):
265 def _checkcollision(repo, wmf, actions, prompts):
243 # build provisional merged manifest up
266 # build provisional merged manifest up
244 pmmf = set(wmf)
267 pmmf = set(wmf)
245
268
246 def addop(f, args):
269 def addop(f, args):
247 pmmf.add(f)
270 pmmf.add(f)
248 def removeop(f, args):
271 def removeop(f, args):
249 pmmf.discard(f)
272 pmmf.discard(f)
250 def nop(f, args):
273 def nop(f, args):
251 pass
274 pass
252
275
253 def renameop(f, args):
276 def renameop(f, args):
254 f2, fd, flags = args
277 f2, fd, flags = args
255 if f:
278 if f:
256 pmmf.discard(f)
279 pmmf.discard(f)
257 pmmf.add(fd)
280 pmmf.add(fd)
258 def mergeop(f, args):
281 def mergeop(f, args):
259 f2, fd, move = args
282 f2, fd, move = args
260 if move:
283 if move:
261 pmmf.discard(f)
284 pmmf.discard(f)
262 pmmf.add(fd)
285 pmmf.add(fd)
263
286
264 opmap = {
287 opmap = {
265 "a": addop,
288 "a": addop,
266 "d": renameop,
289 "d": renameop,
267 "dr": nop,
290 "dr": nop,
268 "e": nop,
291 "e": nop,
269 "f": addop, # untracked file should be kept in working directory
292 "f": addop, # untracked file should be kept in working directory
270 "g": addop,
293 "g": addop,
271 "m": mergeop,
294 "m": mergeop,
272 "r": removeop,
295 "r": removeop,
273 "rd": nop,
296 "rd": nop,
274 }
297 }
275 for f, m, args, msg in actions:
298 for f, m, args, msg in actions:
276 op = opmap.get(m)
299 op = opmap.get(m)
277 assert op, m
300 assert op, m
278 op(f, args)
301 op(f, args)
279
302
280 opmap = {
303 opmap = {
281 "cd": addop,
304 "cd": addop,
282 "dc": addop,
305 "dc": addop,
283 }
306 }
284 for f, m in prompts:
307 for f, m in prompts:
285 op = opmap.get(m)
308 op = opmap.get(m)
286 assert op, m
309 assert op, m
287 op(f, None)
310 op(f, None)
288
311
289 # check case-folding collision in provisional merged manifest
312 # check case-folding collision in provisional merged manifest
290 foldmap = {}
313 foldmap = {}
291 for f in sorted(pmmf):
314 for f in sorted(pmmf):
292 fold = util.normcase(f)
315 fold = util.normcase(f)
293 if fold in foldmap:
316 if fold in foldmap:
294 raise util.Abort(_("case-folding collision between %s and %s")
317 raise util.Abort(_("case-folding collision between %s and %s")
295 % (f, foldmap[fold]))
318 % (f, foldmap[fold]))
296 foldmap[fold] = f
319 foldmap[fold] = f
297
320
298 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
321 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
299 acceptremote=False):
322 acceptremote=False):
300 """
323 """
301 Merge p1 and p2 with ancestor pa and generate merge action list
324 Merge p1 and p2 with ancestor pa and generate merge action list
302
325
303 branchmerge and force are as passed in to update
326 branchmerge and force are as passed in to update
304 partial = function to filter file lists
327 partial = function to filter file lists
305 acceptremote = accept the incoming changes without prompting
328 acceptremote = accept the incoming changes without prompting
306 """
329 """
307
330
308 overwrite = force and not branchmerge
331 overwrite = force and not branchmerge
309 actions, copy, movewithdir = [], {}, {}
332 actions, copy, movewithdir = [], {}, {}
310
333
311 followcopies = False
334 followcopies = False
312 if overwrite:
335 if overwrite:
313 pa = wctx
336 pa = wctx
314 elif pa == p2: # backwards
337 elif pa == p2: # backwards
315 pa = wctx.p1()
338 pa = wctx.p1()
316 elif not branchmerge and not wctx.dirty(missing=True):
339 elif not branchmerge and not wctx.dirty(missing=True):
317 pass
340 pass
318 elif pa and repo.ui.configbool("merge", "followcopies", True):
341 elif pa and repo.ui.configbool("merge", "followcopies", True):
319 followcopies = True
342 followcopies = True
320
343
321 # manifests fetched in order are going to be faster, so prime the caches
344 # manifests fetched in order are going to be faster, so prime the caches
322 [x.manifest() for x in
345 [x.manifest() for x in
323 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
346 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
324
347
325 if followcopies:
348 if followcopies:
326 ret = copies.mergecopies(repo, wctx, p2, pa)
349 ret = copies.mergecopies(repo, wctx, p2, pa)
327 copy, movewithdir, diverge, renamedelete = ret
350 copy, movewithdir, diverge, renamedelete = ret
328 for of, fl in diverge.iteritems():
351 for of, fl in diverge.iteritems():
329 actions.append((of, "dr", (fl,), "divergent renames"))
352 actions.append((of, "dr", (fl,), "divergent renames"))
330 for of, fl in renamedelete.iteritems():
353 for of, fl in renamedelete.iteritems():
331 actions.append((of, "rd", (fl,), "rename and delete"))
354 actions.append((of, "rd", (fl,), "rename and delete"))
332
355
333 repo.ui.note(_("resolving manifests\n"))
356 repo.ui.note(_("resolving manifests\n"))
334 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
357 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
335 % (bool(branchmerge), bool(force), bool(partial)))
358 % (bool(branchmerge), bool(force), bool(partial)))
336 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
359 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
337
360
338 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
361 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
339 copied = set(copy.values())
362 copied = set(copy.values())
340 copied.update(movewithdir.values())
363 copied.update(movewithdir.values())
341
364
342 if '.hgsubstate' in m1:
365 if '.hgsubstate' in m1:
343 # check whether sub state is modified
366 # check whether sub state is modified
344 for s in sorted(wctx.substate):
367 for s in sorted(wctx.substate):
345 if wctx.sub(s).dirty():
368 if wctx.sub(s).dirty():
346 m1['.hgsubstate'] += "+"
369 m1['.hgsubstate'] += "+"
347 break
370 break
348
371
349 aborts, prompts = [], []
372 aborts, prompts = [], []
350 # Compare manifests
373 # Compare manifests
351 fdiff = dicthelpers.diff(m1, m2)
374 fdiff = dicthelpers.diff(m1, m2)
352 flagsdiff = m1.flagsdiff(m2)
375 flagsdiff = m1.flagsdiff(m2)
353 diff12 = dicthelpers.join(fdiff, flagsdiff)
376 diff12 = dicthelpers.join(fdiff, flagsdiff)
354
377
355 for f, (n12, fl12) in diff12.iteritems():
378 for f, (n12, fl12) in diff12.iteritems():
356 if n12:
379 if n12:
357 n1, n2 = n12
380 n1, n2 = n12
358 else: # file contents didn't change, but flags did
381 else: # file contents didn't change, but flags did
359 n1 = n2 = m1.get(f, None)
382 n1 = n2 = m1.get(f, None)
360 if n1 is None:
383 if n1 is None:
361 # Since n1 == n2, the file isn't present in m2 either. This
384 # Since n1 == n2, the file isn't present in m2 either. This
362 # means that the file was removed or deleted locally and
385 # means that the file was removed or deleted locally and
363 # removed remotely, but that residual entries remain in flags.
386 # removed remotely, but that residual entries remain in flags.
364 # This can happen in manifests generated by workingctx.
387 # This can happen in manifests generated by workingctx.
365 continue
388 continue
366 if fl12:
389 if fl12:
367 fl1, fl2 = fl12
390 fl1, fl2 = fl12
368 else: # flags didn't change, file contents did
391 else: # flags didn't change, file contents did
369 fl1 = fl2 = m1.flags(f)
392 fl1 = fl2 = m1.flags(f)
370
393
371 if partial and not partial(f):
394 if partial and not partial(f):
372 continue
395 continue
373 if n1 and n2:
396 if n1 and n2:
374 fla = ma.flags(f)
397 fla = ma.flags(f)
375 nol = 'l' not in fl1 + fl2 + fla
398 nol = 'l' not in fl1 + fl2 + fla
376 a = ma.get(f, nullid)
399 a = ma.get(f, nullid)
377 if n2 == a and fl2 == fla:
400 if n2 == a and fl2 == fla:
378 pass # remote unchanged - keep local
401 pass # remote unchanged - keep local
379 elif n1 == a and fl1 == fla: # local unchanged - use remote
402 elif n1 == a and fl1 == fla: # local unchanged - use remote
380 if n1 == n2: # optimization: keep local content
403 if n1 == n2: # optimization: keep local content
381 actions.append((f, "e", (fl2,), "update permissions"))
404 actions.append((f, "e", (fl2,), "update permissions"))
382 else:
405 else:
383 actions.append((f, "g", (fl2,), "remote is newer"))
406 actions.append((f, "g", (fl2,), "remote is newer"))
384 elif nol and n2 == a: # remote only changed 'x'
407 elif nol and n2 == a: # remote only changed 'x'
385 actions.append((f, "e", (fl2,), "update permissions"))
408 actions.append((f, "e", (fl2,), "update permissions"))
386 elif nol and n1 == a: # local only changed 'x'
409 elif nol and n1 == a: # local only changed 'x'
387 actions.append((f, "g", (fl1,), "remote is newer"))
410 actions.append((f, "g", (fl1,), "remote is newer"))
388 else: # both changed something
411 else: # both changed something
389 actions.append((f, "m", (f, f, False), "versions differ"))
412 actions.append((f, "m", (f, f, False), "versions differ"))
390 elif f in copied: # files we'll deal with on m2 side
413 elif f in copied: # files we'll deal with on m2 side
391 pass
414 pass
392 elif n1 and f in movewithdir: # directory rename
415 elif n1 and f in movewithdir: # directory rename
393 f2 = movewithdir[f]
416 f2 = movewithdir[f]
394 actions.append((f, "d", (None, f2, fl1),
417 actions.append((f, "d", (None, f2, fl1),
395 "remote renamed directory to " + f2))
418 "remote renamed directory to " + f2))
396 elif n1 and f in copy:
419 elif n1 and f in copy:
397 f2 = copy[f]
420 f2 = copy[f]
398 actions.append((f, "m", (f2, f, False),
421 actions.append((f, "m", (f2, f, False),
399 "local copied/moved to " + f2))
422 "local copied/moved to " + f2))
400 elif n1 and f in ma: # clean, a different, no remote
423 elif n1 and f in ma: # clean, a different, no remote
401 if n1 != ma[f]:
424 if n1 != ma[f]:
402 prompts.append((f, "cd")) # prompt changed/deleted
425 prompts.append((f, "cd")) # prompt changed/deleted
403 elif n1[20:] == "a": # added, no remote
426 elif n1[20:] == "a": # added, no remote
404 actions.append((f, "f", None, "remote deleted"))
427 actions.append((f, "f", None, "remote deleted"))
405 else:
428 else:
406 actions.append((f, "r", None, "other deleted"))
429 actions.append((f, "r", None, "other deleted"))
407 elif n2 and f in movewithdir:
430 elif n2 and f in movewithdir:
408 f2 = movewithdir[f]
431 f2 = movewithdir[f]
409 actions.append((None, "d", (f, f2, fl2),
432 actions.append((None, "d", (f, f2, fl2),
410 "local renamed directory to " + f2))
433 "local renamed directory to " + f2))
411 elif n2 and f in copy:
434 elif n2 and f in copy:
412 f2 = copy[f]
435 f2 = copy[f]
413 if f2 in m2:
436 if f2 in m2:
414 actions.append((f2, "m", (f, f, False),
437 actions.append((f2, "m", (f, f, False),
415 "remote copied to " + f))
438 "remote copied to " + f))
416 else:
439 else:
417 actions.append((f2, "m", (f, f, True),
440 actions.append((f2, "m", (f, f, True),
418 "remote moved to " + f))
441 "remote moved to " + f))
419 elif n2 and f not in ma:
442 elif n2 and f not in ma:
420 # local unknown, remote created: the logic is described by the
443 # local unknown, remote created: the logic is described by the
421 # following table:
444 # following table:
422 #
445 #
423 # force branchmerge different | action
446 # force branchmerge different | action
424 # n * n | get
447 # n * n | get
425 # n * y | abort
448 # n * y | abort
426 # y n * | get
449 # y n * | get
427 # y y n | get
450 # y y n | get
428 # y y y | merge
451 # y y y | merge
429 #
452 #
430 # Checking whether the files are different is expensive, so we
453 # Checking whether the files are different is expensive, so we
431 # don't do that when we can avoid it.
454 # don't do that when we can avoid it.
432 if force and not branchmerge:
455 if force and not branchmerge:
433 actions.append((f, "g", (fl2,), "remote created"))
456 actions.append((f, "g", (fl2,), "remote created"))
434 else:
457 else:
435 different = _checkunknownfile(repo, wctx, p2, f)
458 different = _checkunknownfile(repo, wctx, p2, f)
436 if force and branchmerge and different:
459 if force and branchmerge and different:
437 actions.append((f, "m", (f, f, False),
460 actions.append((f, "m", (f, f, False),
438 "remote differs from untracked local"))
461 "remote differs from untracked local"))
439 elif not force and different:
462 elif not force and different:
440 aborts.append((f, "ud"))
463 aborts.append((f, "ud"))
441 else:
464 else:
442 actions.append((f, "g", (fl2,), "remote created"))
465 actions.append((f, "g", (fl2,), "remote created"))
443 elif n2 and n2 != ma[f]:
466 elif n2 and n2 != ma[f]:
444 different = _checkunknownfile(repo, wctx, p2, f)
467 different = _checkunknownfile(repo, wctx, p2, f)
445 if not force and different:
468 if not force and different:
446 aborts.append((f, "ud"))
469 aborts.append((f, "ud"))
447 else:
470 else:
448 # if different: old untracked f may be overwritten and lost
471 # if different: old untracked f may be overwritten and lost
449 prompts.append((f, "dc")) # prompt deleted/changed
472 prompts.append((f, "dc")) # prompt deleted/changed
450
473
451 for f, m in sorted(aborts):
474 for f, m in sorted(aborts):
452 if m == "ud":
475 if m == "ud":
453 repo.ui.warn(_("%s: untracked file differs\n") % f)
476 repo.ui.warn(_("%s: untracked file differs\n") % f)
454 else: assert False, m
477 else: assert False, m
455 if aborts:
478 if aborts:
456 raise util.Abort(_("untracked files in working directory differ "
479 raise util.Abort(_("untracked files in working directory differ "
457 "from files in requested revision"))
480 "from files in requested revision"))
458
481
459 if not util.checkcase(repo.path):
482 if not util.checkcase(repo.path):
460 # check collision between files only in p2 for clean update
483 # check collision between files only in p2 for clean update
461 if (not branchmerge and
484 if (not branchmerge and
462 (force or not wctx.dirty(missing=True, branch=False))):
485 (force or not wctx.dirty(missing=True, branch=False))):
463 _checkcollision(repo, m2, [], [])
486 _checkcollision(repo, m2, [], [])
464 else:
487 else:
465 _checkcollision(repo, m1, actions, prompts)
488 _checkcollision(repo, m1, actions, prompts)
466
489
467 for f, m in sorted(prompts):
490 for f, m in sorted(prompts):
468 if m == "cd":
491 if m == "cd":
469 if acceptremote:
492 if acceptremote:
470 actions.append((f, "r", None, "remote delete"))
493 actions.append((f, "r", None, "remote delete"))
471 elif repo.ui.promptchoice(
494 elif repo.ui.promptchoice(
472 _("local changed %s which remote deleted\n"
495 _("local changed %s which remote deleted\n"
473 "use (c)hanged version or (d)elete?"
496 "use (c)hanged version or (d)elete?"
474 "$$ &Changed $$ &Delete") % f, 0):
497 "$$ &Changed $$ &Delete") % f, 0):
475 actions.append((f, "r", None, "prompt delete"))
498 actions.append((f, "r", None, "prompt delete"))
476 else:
499 else:
477 actions.append((f, "a", None, "prompt keep"))
500 actions.append((f, "a", None, "prompt keep"))
478 elif m == "dc":
501 elif m == "dc":
479 if acceptremote:
502 if acceptremote:
480 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
503 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
481 elif repo.ui.promptchoice(
504 elif repo.ui.promptchoice(
482 _("remote changed %s which local deleted\n"
505 _("remote changed %s which local deleted\n"
483 "use (c)hanged version or leave (d)eleted?"
506 "use (c)hanged version or leave (d)eleted?"
484 "$$ &Changed $$ &Deleted") % f, 0) == 0:
507 "$$ &Changed $$ &Deleted") % f, 0) == 0:
485 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
508 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
486 else: assert False, m
509 else: assert False, m
487 return actions
510 return actions
488
511
489 def actionkey(a):
512 def actionkey(a):
490 return a[1] in "rf" and -1 or 0, a
513 return a[1] in "rf" and -1 or 0, a
491
514
492 def getremove(repo, mctx, overwrite, args):
515 def getremove(repo, mctx, overwrite, args):
493 """apply usually-non-interactive updates to the working directory
516 """apply usually-non-interactive updates to the working directory
494
517
495 mctx is the context to be merged into the working copy
518 mctx is the context to be merged into the working copy
496
519
497 yields tuples for progress updates
520 yields tuples for progress updates
498 """
521 """
499 verbose = repo.ui.verbose
522 verbose = repo.ui.verbose
500 unlink = util.unlinkpath
523 unlink = util.unlinkpath
501 wjoin = repo.wjoin
524 wjoin = repo.wjoin
502 fctx = mctx.filectx
525 fctx = mctx.filectx
503 wwrite = repo.wwrite
526 wwrite = repo.wwrite
504 audit = repo.wopener.audit
527 audit = repo.wopener.audit
505 i = 0
528 i = 0
506 for arg in args:
529 for arg in args:
507 f = arg[0]
530 f = arg[0]
508 if arg[1] == 'r':
531 if arg[1] == 'r':
509 if verbose:
532 if verbose:
510 repo.ui.note(_("removing %s\n") % f)
533 repo.ui.note(_("removing %s\n") % f)
511 audit(f)
534 audit(f)
512 try:
535 try:
513 unlink(wjoin(f), ignoremissing=True)
536 unlink(wjoin(f), ignoremissing=True)
514 except OSError, inst:
537 except OSError, inst:
515 repo.ui.warn(_("update failed to remove %s: %s!\n") %
538 repo.ui.warn(_("update failed to remove %s: %s!\n") %
516 (f, inst.strerror))
539 (f, inst.strerror))
517 else:
540 else:
518 if verbose:
541 if verbose:
519 repo.ui.note(_("getting %s\n") % f)
542 repo.ui.note(_("getting %s\n") % f)
520 wwrite(f, fctx(f).data(), arg[2][0])
543 wwrite(f, fctx(f).data(), arg[2][0])
521 if i == 100:
544 if i == 100:
522 yield i, f
545 yield i, f
523 i = 0
546 i = 0
524 i += 1
547 i += 1
525 if i > 0:
548 if i > 0:
526 yield i, f
549 yield i, f
527
550
528 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
551 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
529 """apply the merge action list to the working directory
552 """apply the merge action list to the working directory
530
553
531 wctx is the working copy context
554 wctx is the working copy context
532 mctx is the context to be merged into the working copy
555 mctx is the context to be merged into the working copy
533 actx is the context of the common ancestor
556 actx is the context of the common ancestor
534
557
535 Return a tuple of counts (updated, merged, removed, unresolved) that
558 Return a tuple of counts (updated, merged, removed, unresolved) that
536 describes how many files were affected by the update.
559 describes how many files were affected by the update.
537 """
560 """
538
561
539 updated, merged, removed, unresolved = 0, 0, 0, 0
562 updated, merged, removed, unresolved = 0, 0, 0, 0
540 ms = mergestate(repo)
563 ms = mergestate(repo)
541 ms.reset(wctx.p1().node(), mctx.node())
564 ms.reset(wctx.p1().node(), mctx.node())
542 moves = []
565 moves = []
543 actions.sort(key=actionkey)
566 actions.sort(key=actionkey)
544
567
545 # prescan for merges
568 # prescan for merges
546 for a in actions:
569 for a in actions:
547 f, m, args, msg = a
570 f, m, args, msg = a
548 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
571 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
549 if m == "m": # merge
572 if m == "m": # merge
550 f2, fd, move = args
573 f2, fd, move = args
551 if fd == '.hgsubstate': # merged internally
574 if fd == '.hgsubstate': # merged internally
552 continue
575 continue
553 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
576 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
554 fcl = wctx[f]
577 fcl = wctx[f]
555 fco = mctx[f2]
578 fco = mctx[f2]
556 if mctx == actx: # backwards, use working dir parent as ancestor
579 if mctx == actx: # backwards, use working dir parent as ancestor
557 if fcl.parents():
580 if fcl.parents():
558 fca = fcl.p1()
581 fca = fcl.p1()
559 else:
582 else:
560 fca = repo.filectx(f, fileid=nullrev)
583 fca = repo.filectx(f, fileid=nullrev)
561 else:
584 else:
562 fca = fcl.ancestor(fco, actx)
585 fca = fcl.ancestor(fco, actx)
563 if not fca:
586 if not fca:
564 fca = repo.filectx(f, fileid=nullrev)
587 fca = repo.filectx(f, fileid=nullrev)
565 ms.add(fcl, fco, fca, fd)
588 ms.add(fcl, fco, fca, fd)
566 if f != fd and move:
589 if f != fd and move:
567 moves.append(f)
590 moves.append(f)
568
591
569 audit = repo.wopener.audit
592 audit = repo.wopener.audit
570
593
571 # remove renamed files after safely stored
594 # remove renamed files after safely stored
572 for f in moves:
595 for f in moves:
573 if os.path.lexists(repo.wjoin(f)):
596 if os.path.lexists(repo.wjoin(f)):
574 repo.ui.debug("removing %s\n" % f)
597 repo.ui.debug("removing %s\n" % f)
575 audit(f)
598 audit(f)
576 util.unlinkpath(repo.wjoin(f))
599 util.unlinkpath(repo.wjoin(f))
577
600
578 numupdates = len(actions)
601 numupdates = len(actions)
579 workeractions = [a for a in actions if a[1] in 'gr']
602 workeractions = [a for a in actions if a[1] in 'gr']
580 updateactions = [a for a in workeractions if a[1] == 'g']
603 updateactions = [a for a in workeractions if a[1] == 'g']
581 updated = len(updateactions)
604 updated = len(updateactions)
582 removeactions = [a for a in workeractions if a[1] == 'r']
605 removeactions = [a for a in workeractions if a[1] == 'r']
583 removed = len(removeactions)
606 removed = len(removeactions)
584 actions = [a for a in actions if a[1] not in 'gr']
607 actions = [a for a in actions if a[1] not in 'gr']
585
608
586 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
609 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
587 if hgsub and hgsub[0] == 'r':
610 if hgsub and hgsub[0] == 'r':
588 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
611 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
589
612
590 z = 0
613 z = 0
591 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
614 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
592 removeactions)
615 removeactions)
593 for i, item in prog:
616 for i, item in prog:
594 z += i
617 z += i
595 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
618 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
596 unit=_('files'))
619 unit=_('files'))
597 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
620 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
598 updateactions)
621 updateactions)
599 for i, item in prog:
622 for i, item in prog:
600 z += i
623 z += i
601 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
624 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
602 unit=_('files'))
625 unit=_('files'))
603
626
604 if hgsub and hgsub[0] == 'g':
627 if hgsub and hgsub[0] == 'g':
605 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
628 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
606
629
607 _updating = _('updating')
630 _updating = _('updating')
608 _files = _('files')
631 _files = _('files')
609 progress = repo.ui.progress
632 progress = repo.ui.progress
610
633
611 for i, a in enumerate(actions):
634 for i, a in enumerate(actions):
612 f, m, args, msg = a
635 f, m, args, msg = a
613 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
636 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
614 if m == "m": # merge
637 if m == "m": # merge
615 f2, fd, move = args
638 f2, fd, move = args
616 if fd == '.hgsubstate': # subrepo states need updating
639 if fd == '.hgsubstate': # subrepo states need updating
617 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
640 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
618 overwrite)
641 overwrite)
619 continue
642 continue
620 audit(fd)
643 audit(fd)
621 r = ms.resolve(fd, wctx, mctx)
644 r = ms.resolve(fd, wctx, mctx)
622 if r is not None and r > 0:
645 if r is not None and r > 0:
623 unresolved += 1
646 unresolved += 1
624 else:
647 else:
625 if r is None:
648 if r is None:
626 updated += 1
649 updated += 1
627 else:
650 else:
628 merged += 1
651 merged += 1
629 elif m == "d": # directory rename
652 elif m == "d": # directory rename
630 f2, fd, flags = args
653 f2, fd, flags = args
631 if f:
654 if f:
632 repo.ui.note(_("moving %s to %s\n") % (f, fd))
655 repo.ui.note(_("moving %s to %s\n") % (f, fd))
633 audit(f)
656 audit(f)
634 repo.wwrite(fd, wctx.filectx(f).data(), flags)
657 repo.wwrite(fd, wctx.filectx(f).data(), flags)
635 util.unlinkpath(repo.wjoin(f))
658 util.unlinkpath(repo.wjoin(f))
636 if f2:
659 if f2:
637 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
660 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
638 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
661 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
639 updated += 1
662 updated += 1
640 elif m == "dr": # divergent renames
663 elif m == "dr": # divergent renames
641 fl, = args
664 fl, = args
642 repo.ui.warn(_("note: possible conflict - %s was renamed "
665 repo.ui.warn(_("note: possible conflict - %s was renamed "
643 "multiple times to:\n") % f)
666 "multiple times to:\n") % f)
644 for nf in fl:
667 for nf in fl:
645 repo.ui.warn(" %s\n" % nf)
668 repo.ui.warn(" %s\n" % nf)
646 elif m == "rd": # rename and delete
669 elif m == "rd": # rename and delete
647 fl, = args
670 fl, = args
648 repo.ui.warn(_("note: possible conflict - %s was deleted "
671 repo.ui.warn(_("note: possible conflict - %s was deleted "
649 "and renamed to:\n") % f)
672 "and renamed to:\n") % f)
650 for nf in fl:
673 for nf in fl:
651 repo.ui.warn(" %s\n" % nf)
674 repo.ui.warn(" %s\n" % nf)
652 elif m == "e": # exec
675 elif m == "e": # exec
653 flags, = args
676 flags, = args
654 audit(f)
677 audit(f)
655 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
678 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
656 updated += 1
679 updated += 1
657 ms.commit()
680 ms.commit()
658 progress(_updating, None, total=numupdates, unit=_files)
681 progress(_updating, None, total=numupdates, unit=_files)
659
682
660 return updated, merged, removed, unresolved
683 return updated, merged, removed, unresolved
661
684
662 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
685 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
663 acceptremote=False):
686 acceptremote=False):
664 "Calculate the actions needed to merge mctx into tctx"
687 "Calculate the actions needed to merge mctx into tctx"
665 actions = []
688 actions = []
666 actions += manifestmerge(repo, tctx, mctx,
689 actions += manifestmerge(repo, tctx, mctx,
667 ancestor,
690 ancestor,
668 branchmerge, force,
691 branchmerge, force,
669 partial, acceptremote)
692 partial, acceptremote)
670 if tctx.rev() is None:
693 if tctx.rev() is None:
671 actions += _forgetremoved(tctx, mctx, branchmerge)
694 actions += _forgetremoved(tctx, mctx, branchmerge)
672 return actions
695 return actions
673
696
674 def recordupdates(repo, actions, branchmerge):
697 def recordupdates(repo, actions, branchmerge):
675 "record merge actions to the dirstate"
698 "record merge actions to the dirstate"
676
699
677 for a in actions:
700 for a in actions:
678 f, m, args, msg = a
701 f, m, args, msg = a
679 if m == "r": # remove
702 if m == "r": # remove
680 if branchmerge:
703 if branchmerge:
681 repo.dirstate.remove(f)
704 repo.dirstate.remove(f)
682 else:
705 else:
683 repo.dirstate.drop(f)
706 repo.dirstate.drop(f)
684 elif m == "a": # re-add
707 elif m == "a": # re-add
685 if not branchmerge:
708 if not branchmerge:
686 repo.dirstate.add(f)
709 repo.dirstate.add(f)
687 elif m == "f": # forget
710 elif m == "f": # forget
688 repo.dirstate.drop(f)
711 repo.dirstate.drop(f)
689 elif m == "e": # exec change
712 elif m == "e": # exec change
690 repo.dirstate.normallookup(f)
713 repo.dirstate.normallookup(f)
691 elif m == "g": # get
714 elif m == "g": # get
692 if branchmerge:
715 if branchmerge:
693 repo.dirstate.otherparent(f)
716 repo.dirstate.otherparent(f)
694 else:
717 else:
695 repo.dirstate.normal(f)
718 repo.dirstate.normal(f)
696 elif m == "m": # merge
719 elif m == "m": # merge
697 f2, fd, move = args
720 f2, fd, move = args
698 if branchmerge:
721 if branchmerge:
699 # We've done a branch merge, mark this file as merged
722 # We've done a branch merge, mark this file as merged
700 # so that we properly record the merger later
723 # so that we properly record the merger later
701 repo.dirstate.merge(fd)
724 repo.dirstate.merge(fd)
702 if f != f2: # copy/rename
725 if f != f2: # copy/rename
703 if move:
726 if move:
704 repo.dirstate.remove(f)
727 repo.dirstate.remove(f)
705 if f != fd:
728 if f != fd:
706 repo.dirstate.copy(f, fd)
729 repo.dirstate.copy(f, fd)
707 else:
730 else:
708 repo.dirstate.copy(f2, fd)
731 repo.dirstate.copy(f2, fd)
709 else:
732 else:
710 # We've update-merged a locally modified file, so
733 # We've update-merged a locally modified file, so
711 # we set the dirstate to emulate a normal checkout
734 # we set the dirstate to emulate a normal checkout
712 # of that file some time in the past. Thus our
735 # of that file some time in the past. Thus our
713 # merge will appear as a normal local file
736 # merge will appear as a normal local file
714 # modification.
737 # modification.
715 if f2 == fd: # file not locally copied/moved
738 if f2 == fd: # file not locally copied/moved
716 repo.dirstate.normallookup(fd)
739 repo.dirstate.normallookup(fd)
717 if move:
740 if move:
718 repo.dirstate.drop(f)
741 repo.dirstate.drop(f)
719 elif m == "d": # directory rename
742 elif m == "d": # directory rename
720 f2, fd, flag = args
743 f2, fd, flag = args
721 if not f2 and f not in repo.dirstate:
744 if not f2 and f not in repo.dirstate:
722 # untracked file moved
745 # untracked file moved
723 continue
746 continue
724 if branchmerge:
747 if branchmerge:
725 repo.dirstate.add(fd)
748 repo.dirstate.add(fd)
726 if f:
749 if f:
727 repo.dirstate.remove(f)
750 repo.dirstate.remove(f)
728 repo.dirstate.copy(f, fd)
751 repo.dirstate.copy(f, fd)
729 if f2:
752 if f2:
730 repo.dirstate.copy(f2, fd)
753 repo.dirstate.copy(f2, fd)
731 else:
754 else:
732 repo.dirstate.normal(fd)
755 repo.dirstate.normal(fd)
733 if f:
756 if f:
734 repo.dirstate.drop(f)
757 repo.dirstate.drop(f)
735
758
736 def update(repo, node, branchmerge, force, partial, ancestor=None,
759 def update(repo, node, branchmerge, force, partial, ancestor=None,
737 mergeancestor=False):
760 mergeancestor=False):
738 """
761 """
739 Perform a merge between the working directory and the given node
762 Perform a merge between the working directory and the given node
740
763
741 node = the node to update to, or None if unspecified
764 node = the node to update to, or None if unspecified
742 branchmerge = whether to merge between branches
765 branchmerge = whether to merge between branches
743 force = whether to force branch merging or file overwriting
766 force = whether to force branch merging or file overwriting
744 partial = a function to filter file lists (dirstate not updated)
767 partial = a function to filter file lists (dirstate not updated)
745 mergeancestor = whether it is merging with an ancestor. If true,
768 mergeancestor = whether it is merging with an ancestor. If true,
746 we should accept the incoming changes for any prompts that occur.
769 we should accept the incoming changes for any prompts that occur.
747 If false, merging with an ancestor (fast-forward) is only allowed
770 If false, merging with an ancestor (fast-forward) is only allowed
748 between different named branches. This flag is used by rebase extension
771 between different named branches. This flag is used by rebase extension
749 as a temporary fix and should be avoided in general.
772 as a temporary fix and should be avoided in general.
750
773
751 The table below shows all the behaviors of the update command
774 The table below shows all the behaviors of the update command
752 given the -c and -C or no options, whether the working directory
775 given the -c and -C or no options, whether the working directory
753 is dirty, whether a revision is specified, and the relationship of
776 is dirty, whether a revision is specified, and the relationship of
754 the parent rev to the target rev (linear, on the same named
777 the parent rev to the target rev (linear, on the same named
755 branch, or on another named branch).
778 branch, or on another named branch).
756
779
757 This logic is tested by test-update-branches.t.
780 This logic is tested by test-update-branches.t.
758
781
759 -c -C dirty rev | linear same cross
782 -c -C dirty rev | linear same cross
760 n n n n | ok (1) x
783 n n n n | ok (1) x
761 n n n y | ok ok ok
784 n n n y | ok ok ok
762 n n y n | merge (2) (2)
785 n n y n | merge (2) (2)
763 n n y y | merge (3) (3)
786 n n y y | merge (3) (3)
764 n y * * | --- discard ---
787 n y * * | --- discard ---
765 y n y * | --- (4) ---
788 y n y * | --- (4) ---
766 y n n * | --- ok ---
789 y n n * | --- ok ---
767 y y * * | --- (5) ---
790 y y * * | --- (5) ---
768
791
769 x = can't happen
792 x = can't happen
770 * = don't-care
793 * = don't-care
771 1 = abort: not a linear update (merge or update --check to force update)
794 1 = abort: not a linear update (merge or update --check to force update)
772 2 = abort: uncommitted changes (commit and merge, or update --clean to
795 2 = abort: uncommitted changes (commit and merge, or update --clean to
773 discard changes)
796 discard changes)
774 3 = abort: uncommitted changes (commit or update --clean to discard changes)
797 3 = abort: uncommitted changes (commit or update --clean to discard changes)
775 4 = abort: uncommitted changes (checked in commands.py)
798 4 = abort: uncommitted changes (checked in commands.py)
776 5 = incompatible options (checked in commands.py)
799 5 = incompatible options (checked in commands.py)
777
800
778 Return the same tuple as applyupdates().
801 Return the same tuple as applyupdates().
779 """
802 """
780
803
781 onode = node
804 onode = node
782 wlock = repo.wlock()
805 wlock = repo.wlock()
783 try:
806 try:
784 wc = repo[None]
807 wc = repo[None]
785 pl = wc.parents()
808 pl = wc.parents()
786 p1 = pl[0]
809 p1 = pl[0]
787 pa = None
810 pa = None
788 if ancestor:
811 if ancestor:
789 pa = repo[ancestor]
812 pa = repo[ancestor]
790
813
791 if node is None:
814 if node is None:
792 # Here is where we should consider bookmarks, divergent bookmarks,
815 # Here is where we should consider bookmarks, divergent bookmarks,
793 # foreground changesets (successors), and tip of current branch;
816 # foreground changesets (successors), and tip of current branch;
794 # but currently we are only checking the branch tips.
817 # but currently we are only checking the branch tips.
795 try:
818 try:
796 node = repo.branchtip(wc.branch())
819 node = repo.branchtip(wc.branch())
797 except error.RepoLookupError:
820 except error.RepoLookupError:
798 if wc.branch() == "default": # no default branch!
821 if wc.branch() == "default": # no default branch!
799 node = repo.lookup("tip") # update to tip
822 node = repo.lookup("tip") # update to tip
800 else:
823 else:
801 raise util.Abort(_("branch %s not found") % wc.branch())
824 raise util.Abort(_("branch %s not found") % wc.branch())
802
825
803 if p1.obsolete() and not p1.children():
826 if p1.obsolete() and not p1.children():
804 # allow updating to successors
827 # allow updating to successors
805 successors = obsolete.successorssets(repo, p1.node())
828 successors = obsolete.successorssets(repo, p1.node())
806
829
807 # behavior of certain cases is as follows,
830 # behavior of certain cases is as follows,
808 #
831 #
809 # divergent changesets: update to highest rev, similar to what
832 # divergent changesets: update to highest rev, similar to what
810 # is currently done when there are more than one head
833 # is currently done when there are more than one head
811 # (i.e. 'tip')
834 # (i.e. 'tip')
812 #
835 #
813 # replaced changesets: same as divergent except we know there
836 # replaced changesets: same as divergent except we know there
814 # is no conflict
837 # is no conflict
815 #
838 #
816 # pruned changeset: no update is done; though, we could
839 # pruned changeset: no update is done; though, we could
817 # consider updating to the first non-obsolete parent,
840 # consider updating to the first non-obsolete parent,
818 # similar to what is current done for 'hg prune'
841 # similar to what is current done for 'hg prune'
819
842
820 if successors:
843 if successors:
821 # flatten the list here handles both divergent (len > 1)
844 # flatten the list here handles both divergent (len > 1)
822 # and the usual case (len = 1)
845 # and the usual case (len = 1)
823 successors = [n for sub in successors for n in sub]
846 successors = [n for sub in successors for n in sub]
824
847
825 # get the max revision for the given successors set,
848 # get the max revision for the given successors set,
826 # i.e. the 'tip' of a set
849 # i.e. the 'tip' of a set
827 node = repo.revs("max(%ln)", successors)[0]
850 node = repo.revs("max(%ln)", successors)[0]
828 pa = p1
851 pa = p1
829
852
830 overwrite = force and not branchmerge
853 overwrite = force and not branchmerge
831
854
832 p2 = repo[node]
855 p2 = repo[node]
833 if pa is None:
856 if pa is None:
834 pa = p1.ancestor(p2)
857 pa = p1.ancestor(p2)
835
858
836 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
859 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
837
860
838 ### check phase
861 ### check phase
839 if not overwrite and len(pl) > 1:
862 if not overwrite and len(pl) > 1:
840 raise util.Abort(_("outstanding uncommitted merges"))
863 raise util.Abort(_("outstanding uncommitted merges"))
841 if branchmerge:
864 if branchmerge:
842 if pa == p2:
865 if pa == p2:
843 raise util.Abort(_("merging with a working directory ancestor"
866 raise util.Abort(_("merging with a working directory ancestor"
844 " has no effect"))
867 " has no effect"))
845 elif pa == p1:
868 elif pa == p1:
846 if not mergeancestor and p1.branch() == p2.branch():
869 if not mergeancestor and p1.branch() == p2.branch():
847 raise util.Abort(_("nothing to merge"),
870 raise util.Abort(_("nothing to merge"),
848 hint=_("use 'hg update' "
871 hint=_("use 'hg update' "
849 "or check 'hg heads'"))
872 "or check 'hg heads'"))
850 if not force and (wc.files() or wc.deleted()):
873 if not force and (wc.files() or wc.deleted()):
851 raise util.Abort(_("uncommitted changes"),
874 raise util.Abort(_("uncommitted changes"),
852 hint=_("use 'hg status' to list changes"))
875 hint=_("use 'hg status' to list changes"))
853 for s in sorted(wc.substate):
876 for s in sorted(wc.substate):
854 if wc.sub(s).dirty():
877 if wc.sub(s).dirty():
855 raise util.Abort(_("uncommitted changes in "
878 raise util.Abort(_("uncommitted changes in "
856 "subrepository '%s'") % s)
879 "subrepository '%s'") % s)
857
880
858 elif not overwrite:
881 elif not overwrite:
859 if p1 == p2: # no-op update
882 if p1 == p2: # no-op update
860 # call the hooks and exit early
883 # call the hooks and exit early
861 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
884 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
862 repo.hook('update', parent1=xp2, parent2='', error=0)
885 repo.hook('update', parent1=xp2, parent2='', error=0)
863 return 0, 0, 0, 0
886 return 0, 0, 0, 0
864
887
865 if pa not in (p1, p2): # nonlinear
888 if pa not in (p1, p2): # nonlinear
866 dirty = wc.dirty(missing=True)
889 dirty = wc.dirty(missing=True)
867 if dirty or onode is None:
890 if dirty or onode is None:
868 # Branching is a bit strange to ensure we do the minimal
891 # Branching is a bit strange to ensure we do the minimal
869 # amount of call to obsolete.background.
892 # amount of call to obsolete.background.
870 foreground = obsolete.foreground(repo, [p1.node()])
893 foreground = obsolete.foreground(repo, [p1.node()])
871 # note: the <node> variable contains a random identifier
894 # note: the <node> variable contains a random identifier
872 if repo[node].node() in foreground:
895 if repo[node].node() in foreground:
873 pa = p1 # allow updating to successors
896 pa = p1 # allow updating to successors
874 elif dirty:
897 elif dirty:
875 msg = _("uncommitted changes")
898 msg = _("uncommitted changes")
876 if onode is None:
899 if onode is None:
877 hint = _("commit and merge, or update --clean to"
900 hint = _("commit and merge, or update --clean to"
878 " discard changes")
901 " discard changes")
879 else:
902 else:
880 hint = _("commit or update --clean to discard"
903 hint = _("commit or update --clean to discard"
881 " changes")
904 " changes")
882 raise util.Abort(msg, hint=hint)
905 raise util.Abort(msg, hint=hint)
883 else: # node is none
906 else: # node is none
884 msg = _("not a linear update")
907 msg = _("not a linear update")
885 hint = _("merge or update --check to force update")
908 hint = _("merge or update --check to force update")
886 raise util.Abort(msg, hint=hint)
909 raise util.Abort(msg, hint=hint)
887 else:
910 else:
888 # Allow jumping branches if clean and specific rev given
911 # Allow jumping branches if clean and specific rev given
889 pa = p1
912 pa = p1
890
913
891 ### calculate phase
914 ### calculate phase
892 actions = calculateupdates(repo, wc, p2, pa,
915 actions = calculateupdates(repo, wc, p2, pa,
893 branchmerge, force, partial, mergeancestor)
916 branchmerge, force, partial, mergeancestor)
894
917
895 ### apply phase
918 ### apply phase
896 if not branchmerge: # just jump to the new rev
919 if not branchmerge: # just jump to the new rev
897 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
920 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
898 if not partial:
921 if not partial:
899 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
922 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
900 # note that we're in the middle of an update
923 # note that we're in the middle of an update
901 repo.vfs.write('updatestate', p2.hex())
924 repo.vfs.write('updatestate', p2.hex())
902
925
903 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
926 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
904
927
905 if not partial:
928 if not partial:
906 repo.setparents(fp1, fp2)
929 repo.setparents(fp1, fp2)
907 recordupdates(repo, actions, branchmerge)
930 recordupdates(repo, actions, branchmerge)
908 # update completed, clear state
931 # update completed, clear state
909 util.unlink(repo.join('updatestate'))
932 util.unlink(repo.join('updatestate'))
910
933
911 if not branchmerge:
934 if not branchmerge:
912 repo.dirstate.setbranch(p2.branch())
935 repo.dirstate.setbranch(p2.branch())
913 finally:
936 finally:
914 wlock.release()
937 wlock.release()
915
938
916 if not partial:
939 if not partial:
917 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
940 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
918 return stats
941 return stats
General Comments 0
You need to be logged in to leave comments. Login now