git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeff King <peff@peff.net>
To: Steven Grimm <koreth@midwinter.com>
Cc: Avery Pennarun <apenwarr@gmail.com>,
	Ittay Dror <ittayd@tikalk.com>,
	git@vger.kernel.org
Subject: Re: detecting rename->commit->modify->commit
Date: Thu, 8 May 2008 14:17:24 -0400	[thread overview]
Message-ID: <20080508181723.GA30449@sigill.intra.peff.net> (raw)
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
}

  parent reply	other threads:[~2008-05-08 18:18 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-05-01 14:10 detecting rename->commit->modify->commit Ittay Dror
2008-05-01 14:45 ` Jeff King
2008-05-01 15:08   ` Ittay Dror
2008-05-01 15:20     ` Jeff King
2008-05-01 15:30       ` Ittay Dror
2008-05-01 15:38         ` Jeff King
2008-05-01 15:47         ` Jakub Narebski
2008-05-01 20:39       ` Teemu Likonen
2008-05-01 23:09         ` Jeff King
2008-05-02  2:06         ` Sitaram Chamarty
2008-05-02  2:38           ` Junio C Hamano
2008-05-02 16:59             ` Sitaram Chamarty
2008-05-01 15:24     ` Ittay Dror
2008-05-01 15:28       ` Jeff King
2008-05-01 14:54 ` Ittay Dror
2008-05-01 15:09   ` Jeff King
2008-05-01 15:20     ` Ittay Dror
2008-05-01 15:30     ` David Tweed
2008-05-01 15:27   ` Avery Pennarun
2008-05-01 15:34     ` Jeff King
2008-05-01 15:50       ` Avery Pennarun
2008-05-01 16:48         ` Jeff King
2008-05-01 19:45           ` Avery Pennarun
2008-05-01 22:42             ` Jeff King
2008-05-01 19:12       ` Steven Grimm
2008-05-01 23:14         ` Jeff King
2008-05-03 17:56           ` merge renamed files/directories? (was: Re: detecting rename->commit->modify->commit) Ittay Dror
2008-05-03 18:11             ` Avery Pennarun
2008-05-04  6:08               ` merge renamed files/directories? Ittay Dror
2008-05-04  9:34                 ` Jakub Narebski
2008-05-05 16:40                 ` Avery Pennarun
2008-05-05 21:49                   ` Robin Rosenberg
2008-05-05 22:20                     ` Linus Torvalds
2008-05-05 23:07                       ` Steven Grimm
2008-05-06  0:29                         ` Linus Torvalds
2008-05-06  0:40                           ` Linus Torvalds
2008-05-06 15:47                           ` Theodore Tso
2008-05-06 16:10                             ` Linus Torvalds
2008-05-06 16:15                               ` Linus Torvalds
2008-05-06 16:32                               ` Ittay Dror
2008-05-06 16:39                                 ` Linus Torvalds
2008-05-06  1:38                       ` Avery Pennarun
2008-05-06  1:46                         ` Shawn O. Pearce
2008-05-06  1:58                           ` Avery Pennarun
2008-05-06  2:12                             ` Shawn O. Pearce
2008-05-06  2:19                         ` Linus Torvalds
2008-05-08 18:17           ` Jeff King [this message]
2008-05-01 16:39   ` detecting rename->commit->modify->commit Sitaram Chamarty
2008-05-01 18:58     ` Ittay Dror

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080508181723.GA30449@sigill.intra.peff.net \
    --to=peff@peff.net \
    --cc=apenwarr@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=ittayd@tikalk.com \
    --cc=koreth@midwinter.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).