comparison mercurial/merge.py @ 44275:fa9ad1da2e77

merge: start using the per-side copy dicts The point of this patch is mostly to clarify `manifestmerge()`. I find it much easier to reason about now. Differential Revision: https://phab.mercurial-scm.org/D7990
author Martin von Zweigbergk <martinvonz@google.com>
date Thu, 23 Jan 2020 15:44:30 -0800
parents 7f8bdee0034e
children fc7175df6359
comparison
equal deleted inserted replaced
44274:7f8bdee0034e 44275:fa9ad1da2e77
1260 [ 1260 [
1261 x.manifest() 1261 x.manifest()
1262 for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev) 1262 for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)
1263 ] 1263 ]
1264 1264
1265 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {} 1265 branch_copies1 = copies.branch_copies()
1266 branch_copies2 = copies.branch_copies()
1267 diverge = {}
1266 if followcopies: 1268 if followcopies:
1267 branch_copies, diverge = copies.mergecopies(repo, wctx, p2, pa) 1269 branch_copies1, branch_copies2, diverge = copies.mergecopies(
1268 copy = branch_copies.copy 1270 repo, wctx, p2, pa
1269 renamedelete = branch_copies.renamedelete 1271 )
1270 dirmove = branch_copies.dirmove
1271 movewithdir = branch_copies.movewithdir
1272 1272
1273 boolbm = pycompat.bytestr(bool(branchmerge)) 1273 boolbm = pycompat.bytestr(bool(branchmerge))
1274 boolf = pycompat.bytestr(bool(force)) 1274 boolf = pycompat.bytestr(bool(force))
1275 boolm = pycompat.bytestr(bool(matcher)) 1275 boolm = pycompat.bytestr(bool(matcher))
1276 repo.ui.note(_(b"resolving manifests\n")) 1276 repo.ui.note(_(b"resolving manifests\n"))
1278 b" branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm) 1278 b" branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm)
1279 ) 1279 )
1280 repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2)) 1280 repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
1281 1281
1282 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest() 1282 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
1283 copied = set(copy.values()) 1283 copied1 = set(branch_copies1.copy.values())
1284 copied.update(movewithdir.values()) 1284 copied1.update(branch_copies1.movewithdir.values())
1285 copied2 = set(branch_copies2.copy.values())
1286 copied2.update(branch_copies2.movewithdir.values())
1285 1287
1286 if b'.hgsubstate' in m1 and wctx.rev() is None: 1288 if b'.hgsubstate' in m1 and wctx.rev() is None:
1287 # Check whether sub state is modified, and overwrite the manifest 1289 # Check whether sub state is modified, and overwrite the manifest
1288 # to flag the change. If wctx is a committed revision, we shouldn't 1290 # to flag the change. If wctx is a committed revision, we shouldn't
1289 # care for the dirty state of the working directory. 1291 # care for the dirty state of the working directory.
1299 # total m1-vs-m2 diff to just those files. This has significant 1301 # total m1-vs-m2 diff to just those files. This has significant
1300 # performance benefits in large repositories. 1302 # performance benefits in large repositories.
1301 relevantfiles = set(ma.diff(m2).keys()) 1303 relevantfiles = set(ma.diff(m2).keys())
1302 1304
1303 # For copied and moved files, we need to add the source file too. 1305 # For copied and moved files, we need to add the source file too.
1304 for copykey, copyvalue in pycompat.iteritems(copy): 1306 for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy):
1305 if copyvalue in relevantfiles: 1307 if copyvalue in relevantfiles:
1306 relevantfiles.add(copykey) 1308 relevantfiles.add(copykey)
1307 for movedirkey in movewithdir: 1309 for movedirkey in branch_copies1.movewithdir:
1308 relevantfiles.add(movedirkey) 1310 relevantfiles.add(movedirkey)
1309 filesmatcher = scmutil.matchfiles(repo, relevantfiles) 1311 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
1310 matcher = matchmod.intersectmatchers(matcher, filesmatcher) 1312 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
1311 1313
1312 diff = m1.diff(m2, match=matcher) 1314 diff = m1.diff(m2, match=matcher)
1313 1315
1314 actions = {} 1316 actions = {}
1315 for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff): 1317 for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff):
1316 if n1 and n2: # file exists on both local and remote side 1318 if n1 and n2: # file exists on both local and remote side
1317 if f not in ma: 1319 if f not in ma:
1318 fa = copy.get(f, None) 1320 # TODO: what if they're renamed from different sources?
1321 fa = branch_copies1.copy.get(
1322 f, None
1323 ) or branch_copies2.copy.get(f, None)
1319 if fa is not None: 1324 if fa is not None:
1320 actions[f] = ( 1325 actions[f] = (
1321 ACTION_MERGE, 1326 ACTION_MERGE,
1322 (f, f, fa, False, pa.node()), 1327 (f, f, fa, False, pa.node()),
1323 b'both renamed from %s' % fa, 1328 b'both renamed from %s' % fa,
1356 ACTION_MERGE, 1361 ACTION_MERGE,
1357 (f, f, f, False, pa.node()), 1362 (f, f, f, False, pa.node()),
1358 b'versions differ', 1363 b'versions differ',
1359 ) 1364 )
1360 elif n1: # file exists only on local side 1365 elif n1: # file exists only on local side
1361 if f in copied: 1366 if f in copied2:
1362 pass # we'll deal with it on m2 side 1367 pass # we'll deal with it on m2 side
1363 elif f in movewithdir: # directory rename, move local 1368 elif (
1364 f2 = movewithdir[f] 1369 f in branch_copies1.movewithdir
1370 ): # directory rename, move local
1371 f2 = branch_copies1.movewithdir[f]
1365 if f2 in m2: 1372 if f2 in m2:
1366 actions[f2] = ( 1373 actions[f2] = (
1367 ACTION_MERGE, 1374 ACTION_MERGE,
1368 (f, f2, None, True, pa.node()), 1375 (f, f2, None, True, pa.node()),
1369 b'remote directory rename, both created', 1376 b'remote directory rename, both created',
1372 actions[f2] = ( 1379 actions[f2] = (
1373 ACTION_DIR_RENAME_MOVE_LOCAL, 1380 ACTION_DIR_RENAME_MOVE_LOCAL,
1374 (f, fl1), 1381 (f, fl1),
1375 b'remote directory rename - move from %s' % f, 1382 b'remote directory rename - move from %s' % f,
1376 ) 1383 )
1377 elif f in copy: 1384 elif f in branch_copies1.copy:
1378 f2 = copy[f] 1385 f2 = branch_copies1.copy[f]
1379 actions[f] = ( 1386 actions[f] = (
1380 ACTION_MERGE, 1387 ACTION_MERGE,
1381 (f, f2, f2, False, pa.node()), 1388 (f, f2, f2, False, pa.node()),
1382 b'local copied/moved from %s' % f2, 1389 b'local copied/moved from %s' % f2,
1383 ) 1390 )
1397 # deleting it. 1404 # deleting it.
1398 actions[f] = (ACTION_FORGET, None, b'remote deleted') 1405 actions[f] = (ACTION_FORGET, None, b'remote deleted')
1399 else: 1406 else:
1400 actions[f] = (ACTION_REMOVE, None, b'other deleted') 1407 actions[f] = (ACTION_REMOVE, None, b'other deleted')
1401 elif n2: # file exists only on remote side 1408 elif n2: # file exists only on remote side
1402 if f in copied: 1409 if f in copied1:
1403 pass # we'll deal with it on m1 side 1410 pass # we'll deal with it on m1 side
1404 elif f in movewithdir: 1411 elif f in branch_copies2.movewithdir:
1405 f2 = movewithdir[f] 1412 f2 = branch_copies2.movewithdir[f]
1406 if f2 in m1: 1413 if f2 in m1:
1407 actions[f2] = ( 1414 actions[f2] = (
1408 ACTION_MERGE, 1415 ACTION_MERGE,
1409 (f2, f, None, False, pa.node()), 1416 (f2, f, None, False, pa.node()),
1410 b'local directory rename, both created', 1417 b'local directory rename, both created',
1413 actions[f2] = ( 1420 actions[f2] = (
1414 ACTION_LOCAL_DIR_RENAME_GET, 1421 ACTION_LOCAL_DIR_RENAME_GET,
1415 (f, fl2), 1422 (f, fl2),
1416 b'local directory rename - get from %s' % f, 1423 b'local directory rename - get from %s' % f,
1417 ) 1424 )
1418 elif f in copy: 1425 elif f in branch_copies2.copy:
1419 f2 = copy[f] 1426 f2 = branch_copies2.copy[f]
1420 if f2 in m2: 1427 if f2 in m2:
1421 actions[f] = ( 1428 actions[f] = (
1422 ACTION_MERGE, 1429 ACTION_MERGE,
1423 (f2, f, f2, False, pa.node()), 1430 (f2, f, f2, False, pa.node()),
1424 b'remote copied from %s' % f2, 1431 b'remote copied from %s' % f2,
1451 (fl2, pa.node()), 1458 (fl2, pa.node()),
1452 b'remote created, get or merge', 1459 b'remote created, get or merge',
1453 ) 1460 )
1454 elif n2 != ma[f]: 1461 elif n2 != ma[f]:
1455 df = None 1462 df = None
1456 for d in dirmove: 1463 for d in branch_copies1.dirmove:
1457 if f.startswith(d): 1464 if f.startswith(d):
1458 # new file added in a directory that was moved 1465 # new file added in a directory that was moved
1459 df = dirmove[d] + f[len(d) :] 1466 df = branch_copies1.dirmove[d] + f[len(d) :]
1460 break 1467 break
1461 if df is not None and df in m1: 1468 if df is not None and df in m1:
1462 actions[df] = ( 1469 actions[df] = (
1463 ACTION_MERGE, 1470 ACTION_MERGE,
1464 (df, f, f, False, pa.node()), 1471 (df, f, f, False, pa.node()),
1480 1487
1481 narrowmatch = repo.narrowmatch() 1488 narrowmatch = repo.narrowmatch()
1482 if not narrowmatch.always(): 1489 if not narrowmatch.always():
1483 # Updates "actions" in place 1490 # Updates "actions" in place
1484 _filternarrowactions(narrowmatch, branchmerge, actions) 1491 _filternarrowactions(narrowmatch, branchmerge, actions)
1492
1493 renamedelete = branch_copies1.renamedelete
1494 renamedelete.update(branch_copies2.renamedelete)
1485 1495
1486 return actions, diverge, renamedelete 1496 return actions, diverge, renamedelete
1487 1497
1488 1498
1489 def _resolvetrivial(repo, wctx, mctx, ancestor, actions): 1499 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):