Mercurial > public > mercurial-scm > hg-stable
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): |