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