2008-06-19 22:32:49 +02:00
|
|
|
#!/usr/bin/perl
|
|
|
|
use lib (split(/:/, $ENV{GITPERLLIB}));
|
|
|
|
|
2010-09-24 22:00:52 +02:00
|
|
|
use 5.008;
|
2008-06-19 22:32:49 +02:00
|
|
|
use warnings;
|
|
|
|
use strict;
|
|
|
|
|
|
|
|
use Test::More qw(no_plan);
|
|
|
|
|
2010-06-24 19:44:46 +02:00
|
|
|
BEGIN {
|
|
|
|
# t9700-perl-git.sh kicks off our testing, so we have to go from
|
|
|
|
# there.
|
2010-06-29 00:51:02 +02:00
|
|
|
Test::More->builder->current_test(1);
|
|
|
|
Test::More->builder->no_ending(1);
|
2010-06-24 19:44:46 +02:00
|
|
|
}
|
|
|
|
|
2008-06-19 22:32:49 +02:00
|
|
|
use Cwd;
|
|
|
|
use File::Basename;
|
|
|
|
|
2016-03-04 12:43:21 +01:00
|
|
|
sub adjust_dirsep {
|
|
|
|
my $path = shift;
|
|
|
|
$path =~ s{\\}{/}g;
|
|
|
|
return $path;
|
|
|
|
}
|
|
|
|
|
2020-07-30 01:14:15 +02:00
|
|
|
my $oid_re = qr/^[0-9a-fA-F]{40}(?:[0-9a-fA-F]{24})?$/;
|
|
|
|
|
2008-06-19 22:32:49 +02:00
|
|
|
BEGIN { use_ok('Git') }
|
|
|
|
|
|
|
|
# set up
|
2009-11-19 19:41:20 +01:00
|
|
|
our $abs_repo_dir = cwd();
|
2008-06-19 22:32:49 +02:00
|
|
|
ok(our $r = Git->repository(Directory => "."), "open repository");
|
Git.pm: trust rev-parse to find bare repositories
When initializing a repository object, we run "git rev-parse --git-dir"
to let the C version of Git find the correct directory. But curiously,
if this fails we don't automatically say "not a git repository".
Instead, we do our own pure-perl check to see if we're in a bare
repository.
This makes little sense, as rev-parse will report both bare and non-bare
directories. This logic comes from d5c7721d58 (Git.pm: Add support for
subdirectories inside of working copies, 2006-06-24), but I don't see
any reason given why we can't just rely on rev-parse. Worse, because we
treat any non-error response from rev-parse as a non-bare repository,
we'll erroneously set the object's WorkingCopy, even in a bare
repository.
But it gets worse. Since 8959555cee (setup_git_directory(): add an owner
check for the top-level directory, 2022-03-02), it's actively wrong (and
dangerous). The perl code doesn't implement the same ownership checks.
And worse, after "finding" the bare repository, it sets GIT_DIR in the
environment, which tells any subsequent Git commands that we've
confirmed the directory is OK, and to trust us. I.e., it re-opens the
vulnerability plugged by 8959555cee when using Git.pm's repository
discovery code.
We can fix this by just relying on rev-parse to tell us when we're not
in a repository, which fixes the vulnerability. Furthermore, we'll ask
its --is-bare-repository function to tell us if we're bare or not, and
rely on that.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-23 00:08:59 +02:00
|
|
|
{
|
|
|
|
local $ENV{GIT_TEST_ASSUME_DIFFERENT_OWNER} = 1;
|
|
|
|
my $failed;
|
|
|
|
|
|
|
|
$failed = eval { Git->repository(Directory => $abs_repo_dir) };
|
|
|
|
ok(!$failed, "reject unsafe non-bare repository");
|
|
|
|
like($@, qr/not a git repository/i, "unsafe error message");
|
|
|
|
|
|
|
|
$failed = eval { Git->repository(Directory => "$abs_repo_dir/bare.git") };
|
|
|
|
ok(!$failed, "reject unsafe bare repository");
|
|
|
|
like($@, qr/not a git repository/i, "unsafe error message");
|
|
|
|
}
|
2008-06-19 22:32:49 +02:00
|
|
|
|
|
|
|
# config
|
|
|
|
is($r->config("test.string"), "value", "config scalar: string");
|
|
|
|
is_deeply([$r->config("test.dupstring")], ["value1", "value2"],
|
|
|
|
"config array: string");
|
|
|
|
is($r->config("test.nonexistent"), undef, "config scalar: nonexistent");
|
|
|
|
is_deeply([$r->config("test.nonexistent")], [], "config array: nonexistent");
|
|
|
|
is($r->config_int("test.int"), 2048, "config_int: integer");
|
|
|
|
is($r->config_int("test.nonexistent"), undef, "config_int: nonexistent");
|
|
|
|
ok($r->config_bool("test.booltrue"), "config_bool: true");
|
|
|
|
ok(!$r->config_bool("test.boolfalse"), "config_bool: false");
|
2016-03-04 12:43:21 +01:00
|
|
|
is(adjust_dirsep($r->config_path("test.path")), $r->config("test.pathexpanded"),
|
2011-10-21 20:42:44 +02:00
|
|
|
"config_path: ~/foo expansion");
|
|
|
|
is_deeply([$r->config_path("test.pathmulti")], ["foo", "bar"],
|
|
|
|
"config_path: multiple values");
|
2008-06-19 22:32:49 +02:00
|
|
|
our $ansi_green = "\x1b[32m";
|
|
|
|
is($r->get_color("color.test.slot1", "red"), $ansi_green, "get_color");
|
|
|
|
# Cannot test $r->get_colorbool("color.foo")) because we do not
|
|
|
|
# control whether our STDOUT is a terminal.
|
|
|
|
|
|
|
|
# Failure cases for config:
|
|
|
|
# Save and restore STDERR; we will probably extract this into a
|
|
|
|
# "dies_ok" method and possibly move the STDERR handling to Git.pm.
|
2013-04-04 22:41:42 +02:00
|
|
|
open our $tmpstderr, ">&STDERR" or die "cannot save STDERR";
|
|
|
|
open STDERR, ">", "/dev/null" or die "cannot redirect STDERR to /dev/null";
|
git-config: do not complain about duplicate entries
If git-config is asked for a single value, it will complain
and exit with an error if it finds multiple instances of
that value. This is unlike the usual internal config
parsing, however, which will generally overwrite previous
values, leaving only the final one. For example:
[set a multivar]
$ git config user.email one@example.com
$ git config --add user.email two@example.com
[use the internal parser to fetch it]
$ git var GIT_AUTHOR_IDENT
Your Name <two@example.com> ...
[use git-config to fetch it]
$ git config user.email
one@example.com
error: More than one value for the key user.email: two@example.com
This overwriting behavior is critical for the regular
parser, which starts with the lowest-priority file (e.g.,
/etc/gitconfig) and proceeds to the highest-priority file
($GIT_DIR/config). Overwriting yields the highest priority
value at the end.
Git-config solves this problem by implementing its own
parsing. It goes from highest to lowest priorty, but does
not proceed to the next file if it has seen a value.
So in practice, this distinction never mattered much,
because it only triggered for values in the same file. And
there was not much point in doing that; the real value is in
overwriting values from lower-priority files.
However, this changed with the implementation of config
include files. Now we might see an include overriding a
value from the parent file, which is a sensible thing to do,
but git-config will flag as a duplication.
This patch drops the duplicate detection for git-config and
switches to a pure-overwrite model (for the single case;
--get-all can still be used if callers want to do something
more fancy).
As is shown by the modifications to the test suite, this is
a user-visible change in behavior. An alternative would be
to just change the include case, but this is much cleaner
for a few reasons:
1. If you change the include case, then to what? If you
just stop parsing includes after getting a value, then
you will get a _different_ answer than the regular
config parser (you'll get the first value instead of
the last value). So you'd want to implement overwrite
semantics anyway.
2. Even though it is a change in behavior for git-config,
it is bringing us in line with what the internal
parsers already do.
3. The file-order reimplementation is the only thing
keeping us from sharing more code with the internal
config parser, which will help keep differences to a
minimum.
Going under the assumption that the primary purpose of
git-config is to behave identically to how git's internal
parsing works, this change can be seen as a bug-fix.
Signed-off-by: Jeff King <peff@peff.net>
2012-10-23 22:52:44 +02:00
|
|
|
is($r->config("test.dupstring"), "value2", "config: multivar");
|
2008-06-19 22:32:49 +02:00
|
|
|
eval { $r->config_bool("test.boolother") };
|
|
|
|
ok($@, "config_bool: non-boolean values fail");
|
|
|
|
open STDERR, ">&", $tmpstderr or die "cannot restore STDERR";
|
|
|
|
|
|
|
|
# ident
|
2020-07-09 22:35:50 +02:00
|
|
|
like($r->ident("aUthor"), qr/^A U Thor <author\@example.com> [0-9]+ [+-]\d{4}$/,
|
2008-06-19 22:32:49 +02:00
|
|
|
"ident scalar: author (type)");
|
2020-07-09 22:35:50 +02:00
|
|
|
like($r->ident("cOmmitter"), qr/^C O Mitter <committer\@example.com> [0-9]+ [+-]\d{4}$/,
|
2008-06-19 22:32:49 +02:00
|
|
|
"ident scalar: committer (type)");
|
|
|
|
is($r->ident("invalid"), "invalid", "ident scalar: invalid ident string (no parsing)");
|
|
|
|
my ($name, $email, $time_tz) = $r->ident('author');
|
|
|
|
is_deeply([$name, $email], ["A U Thor", "author\@example.com"],
|
|
|
|
"ident array: author");
|
2020-07-09 22:35:50 +02:00
|
|
|
like($time_tz, qr/[0-9]+ [+-]\d{4}/, "ident array: author");
|
2008-06-19 22:32:49 +02:00
|
|
|
is_deeply([$r->ident("Name <email> 123 +0000")], ["Name", "email", "123 +0000"],
|
|
|
|
"ident array: ident string");
|
|
|
|
is_deeply([$r->ident("invalid")], [], "ident array: invalid ident string");
|
|
|
|
|
|
|
|
# ident_person
|
|
|
|
is($r->ident_person("aUthor"), "A U Thor <author\@example.com>",
|
|
|
|
"ident_person: author (type)");
|
|
|
|
is($r->ident_person("Name <email> 123 +0000"), "Name <email>",
|
|
|
|
"ident_person: ident string");
|
|
|
|
is($r->ident_person("Name", "email", "123 +0000"), "Name <email>",
|
|
|
|
"ident_person: array");
|
|
|
|
|
|
|
|
# objects and hashes
|
|
|
|
ok(our $file1hash = $r->command_oneline('rev-parse', "HEAD:file1"), "(get file hash)");
|
2008-09-15 18:25:22 +02:00
|
|
|
my $tmpfile = "file.tmp";
|
|
|
|
open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
|
|
|
|
is($r->cat_blob($file1hash, \*TEMPFILE), 15, "cat_blob: size");
|
2008-06-19 22:32:49 +02:00
|
|
|
our $blobcontents;
|
2008-09-15 18:25:22 +02:00
|
|
|
{ local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
|
2008-06-19 22:32:49 +02:00
|
|
|
is($blobcontents, "changed file 1\n", "cat_blob: data");
|
2008-09-15 18:25:22 +02:00
|
|
|
close TEMPFILE or die "Failed writing to $tmpfile: $!";
|
2008-06-19 22:32:49 +02:00
|
|
|
is(Git::hash_object("blob", $tmpfile), $file1hash, "hash_object: roundtrip");
|
2008-09-15 18:25:22 +02:00
|
|
|
open TEMPFILE, ">$tmpfile" or die "Can't open $tmpfile: $!";
|
|
|
|
print TEMPFILE my $test_text = "test blob, to be inserted\n";
|
|
|
|
close TEMPFILE or die "Failed writing to $tmpfile: $!";
|
2020-07-30 01:14:15 +02:00
|
|
|
like(our $newhash = $r->hash_and_insert_object($tmpfile), $oid_re,
|
2008-06-19 22:32:49 +02:00
|
|
|
"hash_and_insert_object: returns hash");
|
2008-09-15 18:25:22 +02:00
|
|
|
open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
|
|
|
|
is($r->cat_blob($newhash, \*TEMPFILE), length $test_text, "cat_blob: roundtrip size");
|
|
|
|
{ local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
|
2008-06-19 22:32:49 +02:00
|
|
|
is($blobcontents, $test_text, "cat_blob: roundtrip data");
|
2008-09-15 18:25:22 +02:00
|
|
|
close TEMPFILE;
|
|
|
|
unlink $tmpfile;
|
2008-06-19 22:32:49 +02:00
|
|
|
|
|
|
|
# paths
|
2009-05-07 15:41:28 +02:00
|
|
|
is($r->repo_path, $abs_repo_dir . "/.git", "repo_path");
|
2008-06-19 22:32:49 +02:00
|
|
|
is($r->wc_path, $abs_repo_dir . "/", "wc_path");
|
|
|
|
is($r->wc_subdir, "", "wc_subdir initial");
|
|
|
|
$r->wc_chdir("directory1");
|
|
|
|
is($r->wc_subdir, "directory1", "wc_subdir after wc_chdir");
|
2009-05-07 15:41:28 +02:00
|
|
|
is($r->config("test.string"), "value", "config after wc_chdir");
|
2009-05-07 15:41:27 +02:00
|
|
|
|
|
|
|
# Object generation in sub directory
|
|
|
|
chdir("directory2");
|
|
|
|
my $r2 = Git->repository();
|
|
|
|
is($r2->repo_path, $abs_repo_dir . "/.git", "repo_path (2)");
|
|
|
|
is($r2->wc_path, $abs_repo_dir . "/", "wc_path (2)");
|
|
|
|
is($r2->wc_subdir, "directory2/", "wc_subdir initial (2)");
|
|
|
|
|
|
|
|
# commands in sub directory
|
|
|
|
my $last_commit = $r2->command_oneline(qw(rev-parse --verify HEAD));
|
2020-07-30 01:14:15 +02:00
|
|
|
like($last_commit, $oid_re, 'rev-parse returned hash');
|
2009-05-07 15:41:27 +02:00
|
|
|
my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.');
|
|
|
|
isnt($last_commit, $dir_commit, 'log . does not show last commit');
|
2010-06-24 19:44:46 +02:00
|
|
|
|
perl: command_bidi_pipe() method should set-up git environmens
When command_input_pipe and command_output_pipe are used as a
method of a Git::repository instance, they eventually call into
_cmd_exec method that sets up the execution environment such as
GIT_DIR, GIT_WORK_TREE environment variables and the current
working directory in the child process that interacts with the
repository.
command_bidi_pipe however didn't expect to be called as such, and
lacked all these set-up. Because of this, a program that did this
did not work as expected:
my $repo = Git->repository(Directory => '/some/where/else');
my ($pid, $in, $out, $ctx) =
$repo->command_bidi_pipe(qw(hash-object -w --stdin-paths));
This patch refactors the _cmd_exec into _setup_git_cmd_env that
sets up the execution environment, and makes _cmd_exec and
command_bidi_pipe to use it.
Note that unlike _cmd_exec that execv's a git command as an
external process, command_bidi_pipe is called from the main line
of control, and the execution environment needs to be restored
after open2() does its magic.
Signed-off-by: Masatake Osanai <unpush@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-02-14 23:13:04 +01:00
|
|
|
# commands outside working tree
|
|
|
|
chdir($abs_repo_dir . '/..');
|
|
|
|
my $r3 = Git->repository(Directory => $abs_repo_dir);
|
|
|
|
my $tmpfile3 = "$abs_repo_dir/file3.tmp";
|
|
|
|
open TEMPFILE3, "+>$tmpfile3" or die "Can't open $tmpfile3: $!";
|
|
|
|
is($r3->cat_blob($file1hash, \*TEMPFILE3), 15, "cat_blob(outside): size");
|
|
|
|
close TEMPFILE3;
|
|
|
|
unlink $tmpfile3;
|
|
|
|
chdir($abs_repo_dir);
|
|
|
|
|
2017-06-30 11:49:12 +02:00
|
|
|
# unquoting paths
|
|
|
|
is(Git::unquote_path('abc'), 'abc', 'unquote unquoted path');
|
|
|
|
is(Git::unquote_path('"abc def"'), 'abc def', 'unquote simple quoted path');
|
|
|
|
is(Git::unquote_path('"abc\"\\\\ \a\b\t\n\v\f\r\001\040"'),
|
|
|
|
"abc\"\\ \x07\x08\x09\x0a\x0b\x0c\x0d\x01 ",
|
|
|
|
'unquote escape sequences');
|
|
|
|
|
2010-06-29 00:51:02 +02:00
|
|
|
printf "1..%d\n", Test::More->builder->current_test;
|
2010-06-24 19:44:46 +02:00
|
|
|
|
2010-06-26 14:42:41 +02:00
|
|
|
my $is_passing = eval { Test::More->is_passing };
|
|
|
|
exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/;
|