Skip to content

Commit 2a97101

Browse files
committed
Merge branch 'mr/gitweb-snapshot'
* mr/gitweb-snapshot: t/gitweb-lib: Split HTTP response with non-GNU sed gitweb: Smarter snapshot names gitweb: Document current snapshot rules via new tests t/gitweb-lib.sh: Split gitweb output into headers and body gitweb: check given hash before trying to create snapshot
2 parents 783cfaf + f74a83f commit 2a97101

File tree

4 files changed

+234
-19
lines changed

4 files changed

+234
-19
lines changed

gitweb/gitweb.perl

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,16 +2020,27 @@ sub quote_command {
20202020

20212021
# get HEAD ref of given project as hash
20222022
sub git_get_head_hash {
2023-
my $project = shift;
2023+
return git_get_full_hash(shift, 'HEAD');
2024+
}
2025+
2026+
sub git_get_full_hash {
2027+
return git_get_hash(@_);
2028+
}
2029+
2030+
sub git_get_short_hash {
2031+
return git_get_hash(@_, '--short=7');
2032+
}
2033+
2034+
sub git_get_hash {
2035+
my ($project, $hash, @options) = @_;
20242036
my $o_git_dir = $git_dir;
20252037
my $retval = undef;
20262038
$git_dir = "$projectroot/$project";
2027-
if (open my $fd, "-|", git_cmd(), "rev-parse", "--verify", "HEAD") {
2028-
my $head = <$fd>;
2039+
if (open my $fd, '-|', git_cmd(), 'rev-parse',
2040+
'--verify', '-q', @options, $hash) {
2041+
$retval = <$fd>;
2042+
chomp $retval if defined $retval;
20292043
close $fd;
2030-
if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
2031-
$retval = $1;
2032-
}
20332044
}
20342045
if (defined $o_git_dir) {
20352046
$git_dir = $o_git_dir;
@@ -5285,6 +5296,43 @@ sub git_tree {
52855296
git_footer_html();
52865297
}
52875298

5299+
sub snapshot_name {
5300+
my ($project, $hash) = @_;
5301+
5302+
# path/to/project.git -> project
5303+
# path/to/project/.git -> project
5304+
my $name = to_utf8($project);
5305+
$name =~ s,([^/])/*\.git$,$1,;
5306+
$name = basename($name);
5307+
# sanitize name
5308+
$name =~ s/[[:cntrl:]]/?/g;
5309+
5310+
my $ver = $hash;
5311+
if ($hash =~ /^[0-9a-fA-F]+$/) {
5312+
# shorten SHA-1 hash
5313+
my $full_hash = git_get_full_hash($project, $hash);
5314+
if ($full_hash =~ /^$hash/ && length($hash) > 7) {
5315+
$ver = git_get_short_hash($project, $hash);
5316+
}
5317+
} elsif ($hash =~ m!^refs/tags/(.*)$!) {
5318+
# tags don't need shortened SHA-1 hash
5319+
$ver = $1;
5320+
} else {
5321+
# branches and other need shortened SHA-1 hash
5322+
if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) {
5323+
$ver = $1;
5324+
}
5325+
$ver .= '-' . git_get_short_hash($project, $hash);
5326+
}
5327+
# in case of hierarchical branch names
5328+
$ver =~ s!/!.!g;
5329+
5330+
# name = project-version_string
5331+
$name = "$name-$ver";
5332+
5333+
return wantarray ? ($name, $name) : $name;
5334+
}
5335+
52885336
sub git_snapshot {
52895337
my $format = $input_params{'snapshot_format'};
52905338
if (!@snapshot_fmts) {
@@ -5302,28 +5350,27 @@ sub git_snapshot {
53025350
die_error(403, "Unsupported snapshot format");
53035351
}
53045352

5305-
if (!defined $hash) {
5306-
$hash = git_get_head_hash($project);
5353+
my $type = git_get_type("$hash^{}");
5354+
if (!$type) {
5355+
die_error(404, 'Object does not exist');
5356+
} elsif ($type eq 'blob') {
5357+
die_error(400, 'Object is not a tree-ish');
53075358
}
53085359

5309-
my $name = $project;
5310-
$name =~ s,([^/])/*\.git$,$1,;
5311-
$name = basename($name);
5312-
my $filename = to_utf8($name);
5313-
$name =~ s/\047/\047\\\047\047/g;
5314-
my $cmd;
5315-
$filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}";
5316-
$cmd = quote_command(
5360+
my ($name, $prefix) = snapshot_name($project, $hash);
5361+
my $filename = "$name$known_snapshot_formats{$format}{'suffix'}";
5362+
my $cmd = quote_command(
53175363
git_cmd(), 'archive',
53185364
"--format=$known_snapshot_formats{$format}{'format'}",
5319-
"--prefix=$name/", $hash);
5365+
"--prefix=$prefix/", $hash);
53205366
if (exists $known_snapshot_formats{$format}{'compressor'}) {
53215367
$cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}});
53225368
}
53235369

5370+
$filename =~ s/(["\\])/\\$1/g;
53245371
print $cgi->header(
53255372
-type => $known_snapshot_formats{$format}{'type'},
5326-
-content_disposition => 'inline; filename="' . "$filename" . '"',
5373+
-content_disposition => 'inline; filename="' . $filename . '"',
53275374
-status => '200 OK');
53285375

53295376
open my $fd, "-|", $cmd

t/gitweb-lib.sh

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,24 @@ gitweb_run () {
5252
rm -f gitweb.log &&
5353
perl -- "$SCRIPT_NAME" \
5454
>gitweb.output 2>gitweb.log &&
55+
perl -w -e '
56+
open O, ">gitweb.headers";
57+
while (<>) {
58+
print O;
59+
last if (/^\r$/ || /^$/);
60+
}
61+
open O, ">gitweb.body";
62+
while (<>) {
63+
print O;
64+
}
65+
close O;
66+
' gitweb.output &&
5567
if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
5668

5769
# gitweb.log is left for debugging
58-
# gitweb.output is used to parse http output
70+
# gitweb.output is used to parse HTTP output
71+
# gitweb.headers contains only HTTP headers
72+
# gitweb.body contains body of message, without headers
5973
}
6074

6175
. ./test-lib.sh

t/t9501-gitweb-standalone-http-status.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,43 @@ test_expect_success \
7575
test_debug 'cat gitweb.output'
7676

7777

78+
# ----------------------------------------------------------------------
79+
# snapshot hash ids
80+
81+
test_expect_success 'snapshots: good tree-ish id' '
82+
gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
83+
grep "Status: 200 OK" gitweb.output
84+
'
85+
test_debug 'cat gitweb.output'
86+
87+
test_expect_success 'snapshots: bad tree-ish id' '
88+
gitweb_run "p=.git;a=snapshot;h=frizzumFrazzum;sf=tgz" &&
89+
grep "404 - Object does not exist" gitweb.output
90+
'
91+
test_debug 'cat gitweb.output'
92+
93+
test_expect_success 'snapshots: bad tree-ish id (tagged object)' '
94+
echo object > tag-object &&
95+
git add tag-object &&
96+
git commit -m "Object to be tagged" &&
97+
git tag tagged-object `git hash-object tag-object` &&
98+
gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" &&
99+
grep "400 - Object is not a tree-ish" gitweb.output
100+
'
101+
test_debug 'cat gitweb.output'
102+
103+
test_expect_success 'snapshots: good object id' '
104+
ID=`git rev-parse --verify HEAD` &&
105+
gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
106+
grep "Status: 200 OK" gitweb.output
107+
'
108+
test_debug 'cat gitweb.output'
109+
110+
test_expect_success 'snapshots: bad object id' '
111+
gitweb_run "p=.git;a=snapshot;h=abcdef01234;sf=tgz" &&
112+
grep "404 - Object does not exist" gitweb.output
113+
'
114+
test_debug 'cat gitweb.output'
115+
116+
78117
test_done
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2009 Mark Rada
4+
#
5+
6+
test_description='gitweb as standalone script (parsing script output).
7+
8+
This test runs gitweb (git web interface) as a CGI script from the
9+
commandline, and checks that it produces the correct output, either
10+
in the HTTP header or the actual script output.'
11+
12+
13+
. ./gitweb-lib.sh
14+
15+
# ----------------------------------------------------------------------
16+
# snapshot file name and prefix
17+
18+
cat >>gitweb_config.perl <<\EOF
19+
20+
$known_snapshot_formats{'tar'} = {
21+
'display' => 'tar',
22+
'type' => 'application/x-tar',
23+
'suffix' => '.tar',
24+
'format' => 'tar',
25+
};
26+
27+
$feature{'snapshot'}{'default'} = ['tar'];
28+
EOF
29+
30+
# Call check_snapshot with the arguments "<basename> [<prefix>]"
31+
#
32+
# This will check that gitweb HTTP header contains proposed filename
33+
# as <basename> with '.tar' suffix added, and that generated tarfile
34+
# (gitweb message body) has <prefix> as prefix for al files in tarfile
35+
#
36+
# <prefix> default to <basename>
37+
check_snapshot () {
38+
basename=$1
39+
prefix=${2:-"$1"}
40+
echo "basename=$basename"
41+
grep "filename=.*$basename.tar" gitweb.headers >/dev/null 2>&1 &&
42+
"$TAR" tf gitweb.body >file_list &&
43+
! grep -v "^$prefix/" file_list
44+
}
45+
46+
test_expect_success setup '
47+
test_commit first foo &&
48+
git branch xx/test &&
49+
FULL_ID=$(git rev-parse --verify HEAD) &&
50+
SHORT_ID=$(git rev-parse --verify --short=7 HEAD)
51+
'
52+
test_debug '
53+
echo "FULL_ID = $FULL_ID"
54+
echo "SHORT_ID = $SHORT_ID"
55+
'
56+
57+
test_expect_success 'snapshot: full sha1' '
58+
gitweb_run "p=.git;a=snapshot;h=$FULL_ID;sf=tar" &&
59+
check_snapshot ".git-$SHORT_ID"
60+
'
61+
test_debug 'cat gitweb.headers && cat file_list'
62+
63+
test_expect_success 'snapshot: shortened sha1' '
64+
gitweb_run "p=.git;a=snapshot;h=$SHORT_ID;sf=tar" &&
65+
check_snapshot ".git-$SHORT_ID"
66+
'
67+
test_debug 'cat gitweb.headers && cat file_list'
68+
69+
test_expect_success 'snapshot: almost full sha1' '
70+
ID=$(git rev-parse --short=30 HEAD) &&
71+
gitweb_run "p=.git;a=snapshot;h=$ID;sf=tar" &&
72+
check_snapshot ".git-$SHORT_ID"
73+
'
74+
test_debug 'cat gitweb.headers && cat file_list'
75+
76+
test_expect_success 'snapshot: HEAD' '
77+
gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tar" &&
78+
check_snapshot ".git-HEAD-$SHORT_ID"
79+
'
80+
test_debug 'cat gitweb.headers && cat file_list'
81+
82+
test_expect_success 'snapshot: short branch name (master)' '
83+
gitweb_run "p=.git;a=snapshot;h=master;sf=tar" &&
84+
ID=$(git rev-parse --verify --short=7 master) &&
85+
check_snapshot ".git-master-$ID"
86+
'
87+
test_debug 'cat gitweb.headers && cat file_list'
88+
89+
test_expect_success 'snapshot: short tag name (first)' '
90+
gitweb_run "p=.git;a=snapshot;h=first;sf=tar" &&
91+
ID=$(git rev-parse --verify --short=7 first) &&
92+
check_snapshot ".git-first-$ID"
93+
'
94+
test_debug 'cat gitweb.headers && cat file_list'
95+
96+
test_expect_success 'snapshot: full branch name (refs/heads/master)' '
97+
gitweb_run "p=.git;a=snapshot;h=refs/heads/master;sf=tar" &&
98+
ID=$(git rev-parse --verify --short=7 master) &&
99+
check_snapshot ".git-master-$ID"
100+
'
101+
test_debug 'cat gitweb.headers && cat file_list'
102+
103+
test_expect_success 'snapshot: full tag name (refs/tags/first)' '
104+
gitweb_run "p=.git;a=snapshot;h=refs/tags/first;sf=tar" &&
105+
check_snapshot ".git-first"
106+
'
107+
test_debug 'cat gitweb.headers && cat file_list'
108+
109+
test_expect_success 'snapshot: hierarchical branch name (xx/test)' '
110+
gitweb_run "p=.git;a=snapshot;h=xx/test;sf=tar" &&
111+
! grep "filename=.*/" gitweb.headers
112+
'
113+
test_debug 'cat gitweb.headers'
114+
115+
test_done

0 commit comments

Comments
 (0)