@@ -1223,7 +1223,173 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
1223
1223
struct diff_filepair * pair ,
1224
1224
char * new_path )
1225
1225
{
1226
- die ("Not yet implemented." );
1226
+ /*
1227
+ * The basic idea is to get the conflict_info from opt->priv->paths
1228
+ * at old path, and insert it into new_path; basically just this:
1229
+ * ci = strmap_get(&opt->priv->paths, old_path);
1230
+ * strmap_remove(&opt->priv->paths, old_path, 0);
1231
+ * strmap_put(&opt->priv->paths, new_path, ci);
1232
+ * However, there are some factors complicating this:
1233
+ * - opt->priv->paths may already have an entry at new_path
1234
+ * - Each ci tracks its containing directory, so we need to
1235
+ * update that
1236
+ * - If another ci has the same containing directory, then
1237
+ * the two char*'s MUST point to the same location. See the
1238
+ * comment in struct merged_info. strcmp equality is not
1239
+ * enough; we need pointer equality.
1240
+ * - opt->priv->paths must hold the parent directories of any
1241
+ * entries that are added. So, if this directory rename
1242
+ * causes entirely new directories, we must recursively add
1243
+ * parent directories.
1244
+ * - For each parent directory added to opt->priv->paths, we
1245
+ * also need to get its parent directory stored in its
1246
+ * conflict_info->merged.directory_name with all the same
1247
+ * requirements about pointer equality.
1248
+ */
1249
+ struct string_list dirs_to_insert = STRING_LIST_INIT_NODUP ;
1250
+ struct conflict_info * ci , * new_ci ;
1251
+ struct strmap_entry * entry ;
1252
+ const char * branch_with_new_path , * branch_with_dir_rename ;
1253
+ const char * old_path = pair -> two -> path ;
1254
+ const char * parent_name ;
1255
+ const char * cur_path ;
1256
+ int i , len ;
1257
+
1258
+ entry = strmap_get_entry (& opt -> priv -> paths , old_path );
1259
+ old_path = entry -> key ;
1260
+ ci = entry -> value ;
1261
+ VERIFY_CI (ci );
1262
+
1263
+ /* Find parent directories missing from opt->priv->paths */
1264
+ cur_path = new_path ;
1265
+ while (1 ) {
1266
+ /* Find the parent directory of cur_path */
1267
+ char * last_slash = strrchr (cur_path , '/' );
1268
+ if (last_slash ) {
1269
+ parent_name = xstrndup (cur_path , last_slash - cur_path );
1270
+ } else {
1271
+ parent_name = opt -> priv -> toplevel_dir ;
1272
+ break ;
1273
+ }
1274
+
1275
+ /* Look it up in opt->priv->paths */
1276
+ entry = strmap_get_entry (& opt -> priv -> paths , parent_name );
1277
+ if (entry ) {
1278
+ free ((char * )parent_name );
1279
+ parent_name = entry -> key ; /* reuse known pointer */
1280
+ break ;
1281
+ }
1282
+
1283
+ /* Record this is one of the directories we need to insert */
1284
+ string_list_append (& dirs_to_insert , parent_name );
1285
+ cur_path = parent_name ;
1286
+ }
1287
+
1288
+ /* Traverse dirs_to_insert and insert them into opt->priv->paths */
1289
+ for (i = dirs_to_insert .nr - 1 ; i >= 0 ; -- i ) {
1290
+ struct conflict_info * dir_ci ;
1291
+ char * cur_dir = dirs_to_insert .items [i ].string ;
1292
+
1293
+ dir_ci = xcalloc (1 , sizeof (* dir_ci ));
1294
+
1295
+ dir_ci -> merged .directory_name = parent_name ;
1296
+ len = strlen (parent_name );
1297
+ /* len+1 because of trailing '/' character */
1298
+ dir_ci -> merged .basename_offset = (len > 0 ? len + 1 : len );
1299
+ dir_ci -> dirmask = ci -> filemask ;
1300
+ strmap_put (& opt -> priv -> paths , cur_dir , dir_ci );
1301
+
1302
+ parent_name = cur_dir ;
1303
+ }
1304
+
1305
+ /*
1306
+ * We are removing old_path from opt->priv->paths. old_path also will
1307
+ * eventually need to be freed, but it may still be used by e.g.
1308
+ * ci->pathnames. So, store it in another string-list for now.
1309
+ */
1310
+ string_list_append (& opt -> priv -> paths_to_free , old_path );
1311
+
1312
+ assert (ci -> filemask == 2 || ci -> filemask == 4 );
1313
+ assert (ci -> dirmask == 0 );
1314
+ strmap_remove (& opt -> priv -> paths , old_path , 0 );
1315
+
1316
+ branch_with_new_path = (ci -> filemask == 2 ) ? opt -> branch1 : opt -> branch2 ;
1317
+ branch_with_dir_rename = (ci -> filemask == 2 ) ? opt -> branch2 : opt -> branch1 ;
1318
+
1319
+ /* Now, finally update ci and stick it into opt->priv->paths */
1320
+ ci -> merged .directory_name = parent_name ;
1321
+ len = strlen (parent_name );
1322
+ ci -> merged .basename_offset = (len > 0 ? len + 1 : len );
1323
+ new_ci = strmap_get (& opt -> priv -> paths , new_path );
1324
+ if (!new_ci ) {
1325
+ /* Place ci back into opt->priv->paths, but at new_path */
1326
+ strmap_put (& opt -> priv -> paths , new_path , ci );
1327
+ } else {
1328
+ int index ;
1329
+
1330
+ /* A few sanity checks */
1331
+ VERIFY_CI (new_ci );
1332
+ assert (ci -> filemask == 2 || ci -> filemask == 4 );
1333
+ assert ((new_ci -> filemask & ci -> filemask ) == 0 );
1334
+ assert (!new_ci -> merged .clean );
1335
+
1336
+ /* Copy stuff from ci into new_ci */
1337
+ new_ci -> filemask |= ci -> filemask ;
1338
+ if (new_ci -> dirmask )
1339
+ new_ci -> df_conflict = 1 ;
1340
+ index = (ci -> filemask >> 1 );
1341
+ new_ci -> pathnames [index ] = ci -> pathnames [index ];
1342
+ new_ci -> stages [index ].mode = ci -> stages [index ].mode ;
1343
+ oidcpy (& new_ci -> stages [index ].oid , & ci -> stages [index ].oid );
1344
+
1345
+ free (ci );
1346
+ ci = new_ci ;
1347
+ }
1348
+
1349
+ if (opt -> detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE ) {
1350
+ /* Notify user of updated path */
1351
+ if (pair -> status == 'A' )
1352
+ path_msg (opt , new_path , 1 ,
1353
+ _ ("Path updated: %s added in %s inside a "
1354
+ "directory that was renamed in %s; moving "
1355
+ "it to %s." ),
1356
+ old_path , branch_with_new_path ,
1357
+ branch_with_dir_rename , new_path );
1358
+ else
1359
+ path_msg (opt , new_path , 1 ,
1360
+ _ ("Path updated: %s renamed to %s in %s, "
1361
+ "inside a directory that was renamed in %s; "
1362
+ "moving it to %s." ),
1363
+ pair -> one -> path , old_path , branch_with_new_path ,
1364
+ branch_with_dir_rename , new_path );
1365
+ } else {
1366
+ /*
1367
+ * opt->detect_directory_renames has the value
1368
+ * MERGE_DIRECTORY_RENAMES_CONFLICT, so mark these as conflicts.
1369
+ */
1370
+ ci -> path_conflict = 1 ;
1371
+ if (pair -> status == 'A' )
1372
+ path_msg (opt , new_path , 0 ,
1373
+ _ ("CONFLICT (file location): %s added in %s "
1374
+ "inside a directory that was renamed in %s, "
1375
+ "suggesting it should perhaps be moved to "
1376
+ "%s." ),
1377
+ old_path , branch_with_new_path ,
1378
+ branch_with_dir_rename , new_path );
1379
+ else
1380
+ path_msg (opt , new_path , 0 ,
1381
+ _ ("CONFLICT (file location): %s renamed to %s "
1382
+ "in %s, inside a directory that was renamed "
1383
+ "in %s, suggesting it should perhaps be "
1384
+ "moved to %s." ),
1385
+ pair -> one -> path , old_path , branch_with_new_path ,
1386
+ branch_with_dir_rename , new_path );
1387
+ }
1388
+
1389
+ /*
1390
+ * Finally, record the new location.
1391
+ */
1392
+ pair -> two -> path = new_path ;
1227
1393
}
1228
1394
1229
1395
/*** Function Grouping: functions related to regular rename detection ***/
0 commit comments