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

Re: detecting rename->commit->modify->commit

From
Jeff King <peff@peff.net>
Date
May 8, 2008, 18:17 UTC
Message-ID
<20080508181723.GA30449@sigill.intra.peff.net>
In-Reply-To
<20080501231427.GD21731@sigill.intra.peff.net>
On Thu, May 01, 2008 at 07:14:27PM -0400, Jeff King wrote:
>   1. write a proof-of-concept that shows directory renaming after the
>     fact (e.g., take a conflicted merge, scan the diff for directory
>     renames, and then fix up the files). That way it is available, but
>     doesn't impact git at all.

Here's a toy script that finds directory renames. I'm sure there are a ton of corner cases it doesn't handle (like directory renames inside of directory renames). My test case was the very trivial:

  mkdir repo && cd repo && git init
  mkdir subdir
  for i in 1 2 3; do
    echo content $i >subdir/file$i
  done
  git add subdir
  git commit -m initial
  git mv subdir new
  git commit -m move
  git checkout -b other HEAD^
  echo content 4 >subdir/file4
  git add subdir
  git commit -m new
  git merge --no-commit master
  perl ../find-dir-rename.pl
  git commit
At which point you should see the merged commit with new/file4.
Script is below.

-- >8 -- #!/usr/bin/perl # # Find renamed directories, and move any files in the "old" # directory into the "new". # # usage: # git merge --no-commit <whatever> # find-dir-rename # git commit

use strict;
foreach my $r (renamed_dirs()) {
  move_dir_contents($r->{from}, $r->{to});
}
exit 0;
sub renamed_dirs {
  my $base = `git merge-base HEAD MERGE_HEAD`;
  chomp $base;
  return grep {
    $_->{score} == 1
  } (renamed_dirs_between($base, 'HEAD'),
     renamed_dirs_between($base, 'MERGE_HEAD'));
}
sub renamed_dirs_between {
  my ($base, $commit) = @_;
  my %sources;
  foreach my $pair (renamed_files($base, $commit)) {
    my $d1 = dir_of($pair->[0]);
    my $d2 = dir_of($pair->[1]);
    next unless defined($d1) && defined($d2);
    $sources{$d1}->{total}++;
    $sources{$d1}->{dests}->{$d2}++;
  }
  return map {
    my $from = $_;
    map {
      {
        from => $from,
        to => $_,
        score => $sources{$from}->{dests}->{$_} / $sources{$from}->{total},
      }
    } keys(%{$sources{$from}->{dests}});
  } removed_directories($base, $commit);
}
sub dir_of {
  local $_ = shift;
  s{/[^/]+$}{} or return undef;
  return $_;
}
sub renamed_files {
  my ($from, $to) = @_;
  open(my $fh, '-|', qw(git diff-tree -r -M), $from, $to)
    or die "unable to open diff-tree: $!";
  return map {
    chomp;
    m/ R\d+\t([^\t]+)\t(.*)/ ? [$1 => $2] : ()
  } <$fh>;
}
sub removed_directories {
  my ($base, $commit) = @_;
  my %new_dirs = map { $_ => 1 } directories($commit);
  return grep { !exists $new_dirs{$_} } directories($base);
}
sub directories {
  my $commit = shift;
  return uniq(
    map {
      s{/[^/]+$}{} ? $_ : ()
    } files($commit)
  );
}
sub files {
  my $commit = shift;
  open(my $fh, '-|', qw(git ls-tree -r), $commit)
    or die "unable to open ls-tree: $!";
  return map {
    chomp;
    s/^[^\t]*\t//;
    $_
  } <$fh>;
}
sub uniq {
  my %seen;
  return grep { !$seen{$_}++ } @_;
}
sub move_dir_contents {
  my ($from, $to) = @_;
  my @files = glob("$from/*");
  return unless @files;
  system(qw(git mv), @files, "$to/")
    and die "unable to move $from/* to $to";
  rmdir($from); # ignore error since there may be untracked files
}
Previous: Linus TorvaldsNext: Sitaram Chamarty
Message 47 of 49 in “detecting rename->commit->modify->commit”
  1. Ittay DrorMay 1, 2008
  2. Jeff KingMay 1, 2008
  3. Ittay DrorMay 1, 2008
  4. Jeff KingMay 1, 2008
  5. Ittay DrorMay 1, 2008
  6. Jeff KingMay 1, 2008
  7. Jakub NarebskiMay 1, 2008
  8. Teemu LikonenMay 1, 2008
  9. Jeff KingMay 1, 2008
  10. Sitaram ChamartyMay 2, 2008
  11. Junio C HamanoMay 2, 2008
  12. Sitaram ChamartyMay 2, 2008
  13. Ittay DrorMay 1, 2008
  14. Jeff KingMay 1, 2008
  15. Ittay DrorMay 1, 2008
  16. Jeff KingMay 1, 2008
  17. Ittay DrorMay 1, 2008
  18. David TweedMay 1, 2008
  19. Avery PennarunMay 1, 2008
  20. Jeff KingMay 1, 2008
  21. Avery PennarunMay 1, 2008
  22. Jeff KingMay 1, 2008
  23. Avery PennarunMay 1, 2008
  24. Jeff KingMay 1, 2008
  25. Steven GrimmMay 1, 2008
  26. Jeff KingMay 1, 2008
  27. merge renamed files/directories? (was: Re: detecting rename->commit->modify->commit)Ittay Dror, May 3, 2008
  28. Avery PennarunMay 3, 2008
  29. Ittay DrorMay 4, 2008
  30. Jakub NarebskiMay 4, 2008
  31. Avery PennarunMay 5, 2008
  32. Robin RosenbergMay 5, 2008
  33. Linus TorvaldsMay 5, 2008
  34. Steven GrimmMay 5, 2008
  35. Linus TorvaldsMay 6, 2008
  36. Linus TorvaldsMay 6, 2008
  37. Theodore TsoMay 6, 2008
  38. Linus TorvaldsMay 6, 2008
  39. Linus TorvaldsMay 6, 2008
  40. Ittay DrorMay 6, 2008
  41. Linus TorvaldsMay 6, 2008
  42. Avery PennarunMay 6, 2008
  43. Shawn O. PearceMay 6, 2008
  44. Avery PennarunMay 6, 2008
  45. Shawn O. PearceMay 6, 2008
  46. Linus TorvaldsMay 6, 2008
  47. Jeff KingMay 8, 2008
  48. Sitaram ChamartyMay 1, 2008
  49. Ittay DrorMay 1, 2008

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.