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

Re: [PATCH 1/2] merge-recursive: clean up get_renamed_dir_portion()

From
Johannes Schindelin <johannes.schindelin@gmx.de>
Date
Oct 12, 2019, 19:47 UTC
Message-ID
<nycvar.QRO.7.76.6.1910122147070.3272@tvgsbejvaqbjf.bet>
In-Reply-To
<8ae78679c9066a206610465e1da91a5f575fced2.1570826543.git.gitgitgadget@gmail.com>
Hi Elijah,
On Fri, 11 Oct 2019, Elijah Newren via GitGitGadget wrote:
Show 21 quoted lines
> From: Elijah Newren <newren@gmail.com>
>
> Dscho noted a few things making this function hard to follow.
> Restructure it a bit and add comments to make it easier to follow.  The
> restructurings include:
>
>   * There was a special case if-check at the end of the function
>     checking whether someone just renamed a file within its original
>     directory, meaning that there could be no directory rename involved.
>     That check was slightly convoluted; it could be done in a more
>     straightforward fashion earlier in the function, and can be done
>     more cheaply too (no call to strncmp).
>
>   * The conditions for advancing end_of_old and end_of_new before
>     calling strchr were both confusing and unnecessary.  If either
>     points at a '/', then they need to be advanced in order to find the
>     next '/'.  If either doesn't point at a '/', then advancing them one
>     char before calling strchr() doesn't hurt.  So, just rip out the
>     if conditions and advance both before calling strchr().
>
> Signed-off-by: Elijah Newren <newren@gmail.com>

This commit message, as well as the patch, make a lot of sense to me. Thank you for doing this!

Ciao, Dscho

Show 101 quoted lines
> ---
>  merge-recursive.c | 60 ++++++++++++++++++++++++++++-------------------
>  1 file changed, 36 insertions(+), 24 deletions(-)
>
> diff --git a/merge-recursive.c b/merge-recursive.c
> index 22a12cfeba..f80e48f623 100644
> --- a/merge-recursive.c
> +++ b/merge-recursive.c
> @@ -1943,8 +1943,8 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
>  				    char **old_dir, char **new_dir)
>  {
>  	char *end_of_old, *end_of_new;
> -	int old_len, new_len;
>
> +	/* Default return values: NULL, meaning no rename */
>  	*old_dir = NULL;
>  	*new_dir = NULL;
>
> @@ -1955,43 +1955,55 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
>  	 *    "a/b/c/d" was renamed to "a/b/some/thing/else"
>  	 * so, for this example, this function returns "a/b/c/d" in
>  	 * *old_dir and "a/b/some/thing/else" in *new_dir.
> -	 *
> -	 * Also, if the basename of the file changed, we don't care.  We
> -	 * want to know which portion of the directory, if any, changed.
> +	 */
> +
> +	/*
> +	 * If the basename of the file changed, we don't care.  We want
> +	 * to know which portion of the directory, if any, changed.
>  	 */
>  	end_of_old = strrchr(old_path, '/');
>  	end_of_new = strrchr(new_path, '/');
> -
>  	if (end_of_old == NULL || end_of_new == NULL)
> -		return;
> +		return; /* We haven't modified *old_dir or *new_dir yet. */
> +
> +	/* Find the first non-matching character traversing backwards */
>  	while (*--end_of_new == *--end_of_old &&
>  	       end_of_old != old_path &&
>  	       end_of_new != new_path)
>  		; /* Do nothing; all in the while loop */
> +
>  	/*
> -	 * We've found the first non-matching character in the directory
> -	 * paths.  That means the current directory we were comparing
> -	 * represents the rename.  Move end_of_old and end_of_new back
> -	 * to the full directory name.
> +	 * If both got back to the beginning of their strings, then the
> +	 * directory didn't change at all, only the basename did.
>  	 */
> -	if (*end_of_old == '/')
> -		end_of_old++;
> -	if (*end_of_old != '/')
> -		end_of_new++;
> -	end_of_old = strchr(end_of_old, '/');
> -	end_of_new = strchr(end_of_new, '/');
> +	if (end_of_old == old_path && end_of_new == new_path &&
> +	    *end_of_old == *end_of_new)
> +		return; /* We haven't modified *old_dir or *new_dir yet. */
>
>  	/*
> -	 * It may have been the case that old_path and new_path were the same
> -	 * directory all along.  Don't claim a rename if they're the same.
> +	 * We've found the first non-matching character in the directory
> +	 * paths.  That means the current characters we were looking at
> +	 * were part of the first non-matching subdir name going back from
> +	 * the end of the strings.  Get the whole name by advancing both
> +	 * end_of_old and end_of_new to the NEXT '/' character.  That will
> +	 * represent the entire directory rename.
> +	 *
> +	 * The reason for the increment is cases like
> +	 *    a/b/star/foo/whatever.c -> a/b/tar/foo/random.c
> +	 * After dropping the basename and going back to the first
> +	 * non-matching character, we're now comparing:
> +	 *    a/b/s          and         a/b/
> +	 * and we want to be comparing:
> +	 *    a/b/star/      and         a/b/tar/
> +	 * but without the pre-increment, the one on the right would stay
> +	 * a/b/.
>  	 */
> -	old_len = end_of_old - old_path;
> -	new_len = end_of_new - new_path;
> +	end_of_old = strchr(++end_of_old, '/');
> +	end_of_new = strchr(++end_of_new, '/');
>
> -	if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
> -		*old_dir = xstrndup(old_path, old_len);
> -		*new_dir = xstrndup(new_path, new_len);
> -	}
> +	/* Copy the old and new directories into *old_dir and *new_dir. */
> +	*old_dir = xstrndup(old_path, end_of_old - old_path);
> +	*new_dir = xstrndup(new_path, end_of_new - new_path);
>  }
>
>  static void remove_hashmap_entries(struct hashmap *dir_renames,
> --
> gitgitgadget
>
>
Previous: Elijah Newren via GitGitGadgetNext: Elijah Newren via GitGitGadget
Message 3 of 21 in “Dir rename fixes”
  1. 0/2 Dir rename fixesElijah Newren via GitGitGadget, Oct 11, 2019
  2. 1/2 merge-recursive: clean up get_renamed_dir_portion()Elijah Newren via GitGitGadget, Oct 11, 2019
  3. Johannes SchindelinOct 12, 2019
  4. 2/2 merge-recursive: fix merging a subdirectory into the root directoryElijah Newren via GitGitGadget, Oct 11, 2019
  5. Johannes SchindelinOct 12, 2019
  6. Elijah NewrenOct 13, 2019
  7. Johannes SchindelinOct 14, 2019
  8. Elijah NewrenOct 22, 2019
  9. Johannes SchindelinOct 24, 2019
  10. Elijah NewrenOct 25, 2019
  11. Johannes SchindelinOct 25, 2019
  12. Junio C HamanoOct 29, 2019
  13. Elijah NewrenOct 30, 2019
  14. Johannes SchindelinOct 30, 2019
  15. Elijah NewrenOct 30, 2019
  16. Johannes SchindelinOct 31, 2019
  17. Johannes SchindelinOct 12, 2019
  18. 0/3 Dir rename fixesElijah Newren via GitGitGadget, Oct 22, 2019
  19. 1/3 merge-recursive: clean up get_renamed_dir_portion()Elijah Newren via GitGitGadget, Oct 22, 2019
  20. 2/3 merge-recursive: fix merging a subdirectory into the root directoryElijah Newren via GitGitGadget, Oct 22, 2019
  21. 3/3 t604[236]: do not run setup in separate testsElijah Newren via GitGitGadget, Oct 22, 2019

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.