提交 e6fefa92 编写于 作者: D David D. Kilzer 提交者: Eric Wong

git-svn info: implement info command

Implement "git-svn info" for files and directories based on the
"svn info" command.  Note that the -r/--revision argument is not
supported yet.

Added 18 tests in t/t9119-git-svn-info.sh.

[ew: small fix to work without arguments on all working directories]
Signed-off-by: NDavid D. Kilzer <ddkilzer@kilzer.net>
Acked-by: NEric Wong <normalperson@yhbt.net>
上级 b2b3ada7
......@@ -193,6 +193,11 @@ Any other arguments are passed directly to `git log'
repository (that has been init-ed with git-svn).
The -r<revision> option is required for this.
'info'::
Shows information about a file or directory similar to what
`svn info' provides. Does not currently support a -r/--revision
argument.
--
OPTIONS
......
......@@ -178,6 +178,10 @@ BEGIN
'file|F=s' => \$_file,
'revision|r=s' => \$_revision,
%cmt_opts } ],
'info' => [ \&cmd_info,
"Show info about the latest SVN revision
on the current branch",
{ } ],
);
my $cmd;
......@@ -586,12 +590,18 @@ sub cmd_create_ignore {
sub canonicalize_path {
my ($path) = @_;
my $dot_slash_added = 0;
if (substr($path, 0, 1) ne "/") {
$path = "./" . $path;
$dot_slash_added = 1;
}
# File::Spec->canonpath doesn't collapse x/../y into y (for a
# good reason), so let's do this manually.
$path =~ s#/+#/#g;
$path =~ s#/\.(?:/|$)#/#g;
$path =~ s#/[^/]+/\.\.##g;
$path =~ s#/$##g;
$path =~ s#^\./## if $dot_slash_added;
return $path;
}
......@@ -743,6 +753,104 @@ sub cmd_commit_diff {
}
}
sub cmd_info {
my $path = canonicalize_path(shift or ".");
unless (scalar(@_) == 0) {
die "Too many arguments specified\n";
}
my ($file_type, $diff_status) = find_file_type_and_diff_status($path);
if (!$file_type && !$diff_status) {
print STDERR "$path: (Not a versioned resource)\n\n";
return;
}
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
unless ($gs) {
die "Unable to determine upstream SVN information from ",
"working tree history\n";
}
my $full_url = $url . ($path eq "." ? "" : "/$path");
my $result = "Path: $path\n";
$result .= "Name: " . basename($path) . "\n" if $file_type ne "dir";
$result .= "URL: " . $full_url . "\n";
my $repos_root = $gs->ra->{repos_root};
Git::SVN::remove_username($repos_root);
$result .= "Repository Root: $repos_root\n";
$result .= "Repository UUID: $uuid\n" unless $diff_status eq "A";
$result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n";
$result .= "Node Kind: " .
($file_type eq "dir" ? "directory" : "file") . "\n";
my $schedule = $diff_status eq "A"
? "add"
: ($diff_status eq "D" ? "delete" : "normal");
$result .= "Schedule: $schedule\n";
if ($diff_status eq "A") {
print $result, "\n";
return;
}
my ($lc_author, $lc_rev, $lc_date_utc);
my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path);
my $log = command_output_pipe(@args);
my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
while (<$log>) {
if (/^${esc_color}author (.+) <[^>]+> (\d+) ([\-\+]?\d+)$/o) {
$lc_author = $1;
$lc_date_utc = Git::SVN::Log::parse_git_date($2, $3);
} elsif (/^${esc_color} (git-svn-id:.+)$/o) {
(undef, $lc_rev, undef) = ::extract_metadata($1);
}
}
close $log;
Git::SVN::Log::set_local_timezone();
$result .= "Last Changed Author: $lc_author\n";
$result .= "Last Changed Rev: $lc_rev\n";
$result .= "Last Changed Date: " .
Git::SVN::Log::format_svn_date($lc_date_utc) . "\n";
if ($file_type ne "dir") {
my $text_last_updated_date =
($diff_status eq "D" ? $lc_date_utc : (stat $path)[9]);
$result .=
"Text Last Updated: " .
Git::SVN::Log::format_svn_date($text_last_updated_date) .
"\n";
my $checksum;
if ($diff_status eq "D") {
my ($fh, $ctx) =
command_output_pipe(qw(cat-file blob), "HEAD:$path");
if ($file_type eq "link") {
my $file_name = <$fh>;
$checksum = Git::SVN::Util::md5sum("link $file_name");
} else {
$checksum = Git::SVN::Util::md5sum($fh);
}
command_close_pipe($fh, $ctx);
} elsif ($file_type eq "link") {
my $file_name =
command(qw(cat-file blob), "HEAD:$path");
$checksum =
Git::SVN::Util::md5sum("link " . $file_name);
} else {
open FILE, "<", $path or die $!;
$checksum = Git::SVN::Util::md5sum(\*FILE);
close FILE or die $!;
}
$result .= "Checksum: " . $checksum . "\n";
}
print $result, "\n";
}
########################### utility functions #########################
sub rebase_cmd {
......@@ -1050,6 +1158,31 @@ sub linearize_history {
(\@linear_refs, \%parents);
}
sub find_file_type_and_diff_status {
my ($path) = @_;
return ('dir', '') if $path eq '.';
my $diff_output =
command_oneline(qw(diff --cached --name-status --), $path) || "";
my $diff_status = (split(' ', $diff_output))[0] || "";
my $ls_tree = command_oneline(qw(ls-tree HEAD), $path) || "";
return (undef, undef) if !$diff_status && !$ls_tree;
if ($diff_status eq "A") {
return ("link", $diff_status) if -l $path;
return ("dir", $diff_status) if -d $path;
return ("file", $diff_status);
}
my $mode = (split(' ', $ls_tree))[0] || "";
return ("link", $diff_status) if $mode eq "120000";
return ("dir", $diff_status) if $mode eq "040000";
return ("file", $diff_status);
}
package Git::SVN::Util;
use strict;
use warnings;
......
#!/bin/sh
#
# Copyright (c) 2007 David D. Kilzer
test_description='git-svn info'
. ./lib-git-svn.sh
ptouch() {
perl -w -e '
use strict;
die "ptouch requires exactly 2 arguments" if @ARGV != 2;
die "$ARGV[0] does not exist" if ! -e $ARGV[0];
my @s = stat $ARGV[0];
utime $s[8], $s[9], $ARGV[1];
' "$1" "$2"
}
test_expect_success 'setup repository and import' "
mkdir info &&
cd info &&
echo FIRST > A &&
echo one > file &&
ln -s file symlink-file &&
mkdir directory &&
touch directory/.placeholder &&
ln -s directory symlink-directory &&
svn import -m 'initial' . $svnrepo &&
cd .. &&
mkdir gitwc &&
cd gitwc &&
git-svn init $svnrepo &&
git-svn fetch &&
cd .. &&
svn co $svnrepo svnwc &&
ptouch svnwc/file gitwc/file &&
ptouch svnwc/directory gitwc/directory &&
ptouch svnwc/symlink-file gitwc/symlink-file &&
ptouch svnwc/symlink-directory gitwc/symlink-directory
"
test_expect_success 'info' "
(cd svnwc; svn info) > expected.info &&
(cd gitwc; git-svn info) > actual.info &&
git-diff expected.info actual.info
"
test_expect_success 'info .' "
(cd svnwc; svn info .) > expected.info-dot &&
(cd gitwc; git-svn info .) > actual.info-dot &&
git-diff expected.info-dot actual.info-dot
"
test_expect_success 'info file' "
(cd svnwc; svn info file) > expected.info-file &&
(cd gitwc; git-svn info file) > actual.info-file &&
git-diff expected.info-file actual.info-file
"
test_expect_success 'info directory' "
(cd svnwc; svn info directory) > expected.info-directory &&
(cd gitwc; git-svn info directory) > actual.info-directory &&
git-diff expected.info-directory actual.info-directory
"
test_expect_success 'info symlink-file' "
(cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
(cd gitwc; git-svn info symlink-file) > actual.info-symlink-file &&
git-diff expected.info-symlink-file actual.info-symlink-file
"
test_expect_success 'info symlink-directory' "
(cd svnwc; svn info symlink-directory) \
> expected.info-symlink-directory &&
(cd gitwc; git-svn info symlink-directory) \
> actual.info-symlink-directory &&
git-diff expected.info-symlink-directory actual.info-symlink-directory
"
test_expect_success 'info added-file' "
echo two > gitwc/added-file &&
cd gitwc &&
git add added-file &&
cd .. &&
cp gitwc/added-file svnwc/added-file &&
ptouch gitwc/added-file svnwc/added-file &&
cd svnwc &&
svn add added-file > /dev/null &&
cd .. &&
(cd svnwc; svn info added-file) > expected.info-added-file &&
(cd gitwc; git-svn info added-file) > actual.info-added-file &&
git-diff expected.info-added-file actual.info-added-file
"
test_expect_success 'info added-directory' "
mkdir gitwc/added-directory svnwc/added-directory &&
ptouch gitwc/added-directory svnwc/added-directory &&
touch gitwc/added-directory/.placeholder &&
cd svnwc &&
svn add added-directory > /dev/null &&
cd .. &&
cd gitwc &&
git add added-directory &&
cd .. &&
(cd svnwc; svn info added-directory) \
> expected.info-added-directory &&
(cd gitwc; git-svn info added-directory) \
> actual.info-added-directory &&
git-diff expected.info-added-directory actual.info-added-directory
"
test_expect_success 'info added-symlink-file' "
cd gitwc &&
ln -s added-file added-symlink-file &&
git add added-symlink-file &&
cd .. &&
cd svnwc &&
ln -s added-file added-symlink-file &&
svn add added-symlink-file > /dev/null &&
cd .. &&
ptouch gitwc/added-symlink-file svnwc/added-symlink-file &&
(cd svnwc; svn info added-symlink-file) \
> expected.info-added-symlink-file &&
(cd gitwc; git-svn info added-symlink-file) \
> actual.info-added-symlink-file &&
git-diff expected.info-added-symlink-file \
actual.info-added-symlink-file
"
test_expect_success 'info added-symlink-directory' "
cd gitwc &&
ln -s added-directory added-symlink-directory &&
git add added-symlink-directory &&
cd .. &&
cd svnwc &&
ln -s added-directory added-symlink-directory &&
svn add added-symlink-directory > /dev/null &&
cd .. &&
ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory &&
(cd svnwc; svn info added-symlink-directory) \
> expected.info-added-symlink-directory &&
(cd gitwc; git-svn info added-symlink-directory) \
> actual.info-added-symlink-directory &&
git-diff expected.info-added-symlink-directory \
actual.info-added-symlink-directory
"
# The next few tests replace the "Text Last Updated" value with a
# placeholder since git doesn't have a way to know the date that a
# now-deleted file was last checked out locally. Internally it
# simply reuses the Last Changed Date.
test_expect_success 'info deleted-file' "
cd gitwc &&
git rm -f file > /dev/null &&
cd .. &&
cd svnwc &&
svn rm --force file > /dev/null &&
cd .. &&
(cd svnwc; svn info file) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> expected.info-deleted-file &&
(cd gitwc; git-svn info file) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> actual.info-deleted-file &&
git-diff expected.info-deleted-file actual.info-deleted-file
"
test_expect_success 'info deleted-directory' "
cd gitwc &&
git rm -r -f directory > /dev/null &&
cd .. &&
cd svnwc &&
svn rm --force directory > /dev/null &&
cd .. &&
(cd svnwc; svn info directory) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> expected.info-deleted-directory &&
(cd gitwc; git-svn info directory) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> actual.info-deleted-directory &&
git-diff expected.info-deleted-directory actual.info-deleted-directory
"
test_expect_success 'info deleted-symlink-file' "
cd gitwc &&
git rm -f symlink-file > /dev/null &&
cd .. &&
cd svnwc &&
svn rm --force symlink-file > /dev/null &&
cd .. &&
(cd svnwc; svn info symlink-file) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> expected.info-deleted-symlink-file &&
(cd gitwc; git-svn info symlink-file) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> actual.info-deleted-symlink-file &&
git-diff expected.info-deleted-symlink-file \
actual.info-deleted-symlink-file
"
test_expect_success 'info deleted-symlink-directory' "
cd gitwc &&
git rm -f symlink-directory > /dev/null &&
cd .. &&
cd svnwc &&
svn rm --force symlink-directory > /dev/null &&
cd .. &&
(cd svnwc; svn info symlink-directory) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> expected.info-deleted-symlink-directory &&
(cd gitwc; git-svn info symlink-directory) |
sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
> actual.info-deleted-symlink-directory &&
git-diff expected.info-deleted-symlink-directory \
actual.info-deleted-symlink-directory
"
# NOTE: git does not have the concept of replaced objects,
# so we can't test for files in that state.
test_expect_success 'info unknown-file' "
echo two > gitwc/unknown-file &&
cp gitwc/unknown-file svnwc/unknown-file &&
ptouch gitwc/unknown-file svnwc/unknown-file &&
(cd svnwc; svn info unknown-file) 2> expected.info-unknown-file &&
(cd gitwc; git-svn info unknown-file) 2> actual.info-unknown-file &&
git-diff expected.info-unknown-file actual.info-unknown-file
"
test_expect_success 'info unknown-directory' "
mkdir gitwc/unknown-directory svnwc/unknown-directory &&
ptouch gitwc/unknown-directory svnwc/unknown-directory &&
touch gitwc/unknown-directory/.placeholder &&
(cd svnwc; svn info unknown-directory) \
2> expected.info-unknown-directory &&
(cd gitwc; git-svn info unknown-directory) \
2> actual.info-unknown-directory &&
git-diff expected.info-unknown-directory actual.info-unknown-directory
"
test_expect_success 'info unknown-symlink-file' "
cd gitwc &&
ln -s unknown-file unknown-symlink-file &&
cd .. &&
cd svnwc &&
ln -s unknown-file unknown-symlink-file &&
cd .. &&
ptouch gitwc/unknown-symlink-file svnwc/unknown-symlink-file &&
(cd svnwc; svn info unknown-symlink-file) \
2> expected.info-unknown-symlink-file &&
(cd gitwc; git-svn info unknown-symlink-file) \
2> actual.info-unknown-symlink-file &&
git-diff expected.info-unknown-symlink-file \
actual.info-unknown-symlink-file
"
test_expect_success 'info unknown-symlink-directory' "
cd gitwc &&
ln -s unknown-directory unknown-symlink-directory &&
cd .. &&
cd svnwc &&
ln -s unknown-directory unknown-symlink-directory &&
cd .. &&
ptouch gitwc/unknown-symlink-directory \
svnwc/unknown-symlink-directory &&
(cd svnwc; svn info unknown-symlink-directory) \
2> expected.info-unknown-symlink-directory &&
(cd gitwc; git-svn info unknown-symlink-directory) \
2> actual.info-unknown-symlink-directory &&
git-diff expected.info-unknown-symlink-directory \
actual.info-unknown-symlink-directory
"
test_done
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册