git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 4/8] diffcore-rename: only compute dir_rename_count for relevant directories

From
Elijah Newren via GitGitGadget <gitgitgadget@gmail.com>
Date
Mar 13, 2021, 22:22 UTC
Message-ID
<9e30f4399abefc8af005a2de97eefe692fdb4848.1615674128.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.853.git.1615674128.gitgitgadget@gmail.com>
From: Elijah Newren <newren@gmail.com>

When one side adds files to a directory that the other side renamed, directory rename detection is used to either move the new paths to the newer directory or warn the user about the fact that another path location might be better.

If a parent of the given directory had new files added to it, any
renames in the current directory are also part of determining where the
parent directory is renamed to.  Thus, naively, we need to record each
rename N times for a path at depth N.  However, we can use the
additional information added to dirs_removed in the last commit to avoid
traversing all N parent directories in many cases.  Let's use an example
to explain how this works.  If we have a path named
   src/old_dir/a/b/file.c
and src/old_dir doesn't exist on one side of history, but the other
added a file named src/old_dir/newfile.c, then if one side renamed
   src/old_dir/a/b/file.c => source/new_dir/a/b/file.c
then this file would affect potential directory rename detection counts
for
   src/old_dir/a/b => source/new_dir/a/b
   src/old_dir/a   => source/new_dir/a
   src/old_dir     => source/new_dir
   src             => source
adding a weight of 1 to each in dir_rename_counts.  However, if src/
exists on both sides of history, then we don't need to track any entries
for it in dir_rename_counts.  That was implemented previously.  What we
are adding now, is that if no new files were added to src/old_dir/a or
src/old_dir/b, then we don't need to have counts in dir_rename_count
for those directories either.

In short, we only need to track counts in dir_rename_count for directories whose dirs_removed value is RELEVANT_FOR_SELF. And as soon as we reach a directory that isn't in dirs_removed (signalled by returning the default value of NOT_RELEVANT from strintmap_get()), we can stop looking any further up the directory hierarchy.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 diffcore-rename.c | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/diffcore-rename.c b/diffcore-rename.c
index fafec66b29e9..0a17abd46691 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -461,6 +461,8 @@ static void update_dir_rename_counts(struct dir_rename_info *info,
 		return;
 
 	while (1) {
+		int drd_flag = NOT_RELEVANT;
+
 		/* Get old_dir, skip if its directory isn't relevant. */
 		dirname_munge(old_dir);
 		if (info->relevant_source_dirs &&
@@ -509,16 +511,31 @@ static void update_dir_rename_counts(struct dir_rename_info *info,
 			}
 		}
 
-		if (strintmap_contains(dirs_removed, old_dir))
+		/*
+		 * Above we suggested that we'd keep recording renames for
+		 * all ancestor directories where the trailing directories
+		 * matched, i.e. for
+		 *   "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
+		 * we'd increment rename counts for each of
+		 *   a/b/c/d/e/ => a/b/some/thing/else/e/
+		 *   a/b/c/d/   => a/b/some/thing/else/
+		 * However, we only need the rename counts for directories
+		 * in dirs_removed whose value is RELEVANT_FOR_SELF.
+		 * However, we add one special case of also recording it for
+		 * first_time_in_loop because find_basename_matches() can
+		 * use that as a hint to find a good pairing.
+		 */
+		if (dirs_removed)
+			drd_flag = strintmap_get(dirs_removed, old_dir);
+		if (drd_flag == RELEVANT_FOR_SELF || first_time_in_loop)
 			increment_count(info, old_dir, new_dir);
-		else
-			break;
 
+		first_time_in_loop = 0;
+		if (drd_flag == NOT_RELEVANT)
+			break;
 		/* If we hit toplevel directory ("") for old or new dir, quit */
 		if (!*old_dir || !*new_dir)
 			break;
-
-		first_time_in_loop = 0;
 	}
 
 	/* Free resources we don't need anymore */
-- 
gitgitgadget
Previous: Elijah Newren via GitGitGadgetNext: Elijah Newren via GitGitGadget
Message 3 of 14 in “Optimization batch 10: avoid detecting even more irrelevant renames”
  1. 0/8 Optimization batch 10: avoid detecting even more irrelevant renamesElijah Newren via GitGitGadget, Mar 13, 2021
  2. 6/8 diffcore-rename: add computation of number of unknown renamesElijah Newren via GitGitGadget, Mar 13, 2021
  3. 4/8 diffcore-rename: only compute dir_rename_count for relevant directoriesElijah Newren via GitGitGadget, Mar 13, 2021
  4. 1/8 diffcore-rename: take advantage of "majority rules" to skip more renamesElijah Newren via GitGitGadget, Mar 13, 2021
  5. 5/8 diffcore-rename: check if we have enough renames for directories early onElijah Newren via GitGitGadget, Mar 13, 2021
  6. 2/8 merge-ort, diffcore-rename: tweak dirs_removed and relevant_source typeElijah Newren via GitGitGadget, Mar 13, 2021
  7. 3/8 merge-ort: record the reason that we want a rename for a directoryElijah Newren via GitGitGadget, Mar 13, 2021
  8. Derrick StoleeMar 15, 2021
  9. Elijah NewrenMar 15, 2021
  10. Junio C HamanoMar 28, 2021
  11. 8/8 diffcore-rename: determine which relevant_sources are no longer relevantElijah Newren via GitGitGadget, Mar 13, 2021
  12. 7/8 merge-ort: record the reason that we want a rename for a fileElijah Newren via GitGitGadget, Mar 13, 2021
  13. Derrick StoleeMar 15, 2021
  14. Elijah NewrenMar 15, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.