* Re: [PATCH] cmd_reset: don't trash uncommitted changes unless told to
From: Johannes Schindelin @ 2008-06-26 17:49 UTC (permalink / raw)
To: Avery Pennarun
Cc: Björn Steinbrink, Junio C Hamano, Theodore Tso,
Johannes Sixt, Boaz Harrosh, Steven Walter, git, jeske
In-Reply-To: <32541b130806260855o691d444bpc0843e5f51639430@mail.gmail.com>
Hi,
On Thu, 26 Jun 2008, Avery Pennarun wrote:
> On 6/26/08, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> > While we are nit-picking: Ted's version does not respect autocrlf,
> > while Junio's does.
>
> Is it intentional that git-show doesn't respect autocrlf, or just an
> oversight?
Funny. I seem to have answered exactly the same question a few days ago.
"git show" is meant to show the contents of an object. It does not
operate on a working directory. It does not even _need_ a working
directory.
So, no, it is _not_ an oversight.
Hth,
Dscho
^ permalink raw reply
* Re: about c8af1de9 (git status uses pager)
From: Jeff King @ 2008-06-26 17:51 UTC (permalink / raw)
To: Wincent Colaiuta; +Cc: Dan McGee, git
In-Reply-To: <43234A8B-3201-4F4F-A9A8-3756FC95A422@wincent.com>
On Thu, Jun 26, 2008 at 12:17:53PM +0200, Wincent Colaiuta wrote:
> Follow-up discussion in early May, 31 messages:
>
> http://article.gmane.org/gmane.comp.version-control.git/80957/
And I posted several patches in that thread to make paging configuration
more fine-grained, but they got caught up in some setup_git_* bermuda
triangle. Dan (and others who dislike the new change), I would be very
happy if you wanted to take a look at them and see if you can address
some of the issues I raised.
-Peff
^ permalink raw reply
* Re: policy and mechanism for less-connected clients
From: David Jeske @ 2008-06-26 16:21 UTC (permalink / raw)
To: Theodore Tso; +Cc: Junio C Hamano, git
In-Reply-To: <20080626113710.GD8610@mit.edu>
Thanks for pointing out the issue with automatically committing and bisect.
You're right, if I'm going to automatically commit under the covers I should
use stash instead. However, I don't want users to keep a dirty tree, and now
they don't have to.
To use your two-months-without-checkins example.. one of the big problems I
have with cvs/p4 is this notion that I'm not supposed to record my work every
5-50 minutes. I checkin every time my code does something new and the tests
pass. In my own startup projects/companies this is fine, because it's my tree.
As soon as the group policy stops me from checking in every 5-50 minutes, I
painfully make my own branch so I can checkin on my schedule. I'm starting to
witness some users solving this problem in a very libertarian way, by using git
to manage their local changes even though they work in a code-review restricted
cvs/p4 environment.
-- Theodore Tso wrote:
> I'd suggest that you try using git straight for a bit longer, before
> you start drawing these conclusions. Trust me, the concepts of git
> really aren't that hard to explain to people; that's not what you need
> to hide from people coming from the CVS world. The hard part is the
> fact that git's UI has all sorts of non-linearities and that git's
> documentation and introductory tutorials are not as good as it should
> be. (Although it's gotten a LOT better than just a year or two ago.)
I agree 100%. I am using git straight. I think I have read more git
documentation and definitely read more git source-code in trying to use it over
a couple months, than I have read of cvs/p4 in decades - just to try to
understand which of the 3 ways to get from here-to-there is correct, and then
when I pull back the red curtain a little further I realize I was totally
wrong.
This started as a "cheat sheet" file with the combination of git commands I had
to execute to perform each task. However, they are only valid in the context of
a git-repo that's configured in certain ways. I realized it would be simpler
(even for just me) if I had something that grouped commands and did 'lint'
sanity checks, with helpful tutorial responses. Thus the wrapper.
> Exactly. So what I would ask you to consider is that you may find it
> personally useful to design this system,
I see where you're going with this, and I agree...
> but afterwards, before you inflict it on projects, and deal
> with some of the attendent side effects (like all of these trash
> commits causing "git bisect" to go down the drain), that you
> consider whether *now* that you understand how git works and
> why it does some of the things it does, and what the
> shortcomings of the git porcelain are from a UI perspective, whether
> CVS refugees really would be best served by this system you are
> designing, or whether a few wrapper scripts to hide some of the more
> pointy spikes in git's CLI, plus some better tutorials, might in the
> long run be much better for these CVS developers that you are trying
> to serve.
Absolutly. I hope that you can understand my goal of an 'interactive command
line/tutorial linear path from cvs/p4 to git'. One where they don't get stuck
and turn back, but also where they work in ways which are 'fairly reasonable'
in the git community. I also hope you'll help me evaluate whether I've succeed
or just made another confusing set of compromises that are no good. There is no
need for more of the latter.
I also have a group that's been using git and wants to switch back to cvs/p4.
They are willing to give up tracking their local changes (or do it with private
gits) in order to get a simpler model for 'shared head of tree' development. I
think they are a good test-case as well.
------
So far, 1/2 of the lines of my script merely transitional documentation from
p4/cvs to git. As I write more of this prose, I realize that it may be helpful
as transition documentation webpages. However, it is much more than passive
documentation, because if there are 3 steps from here to there, I can look at
the repository and see where the user is, and tell them what they need to do
next.
As one example, I have a command "pending" (like p4 pending) which shows local
changes in my branch (on my inaccessible firewalled machine) which are not on
my origin repo(s). Except that in order for this concept to even make sense, it
first:
- checks if I have an 'origin' for a public repo
- checks that my current branch is tracking an [some]origin
- if it is mapped to a 'myorigin' personal published repo
(because I'm firewalled), it checks that the name of the
branch matches the myorigin/branchname (because it's easier
to think straight if myorigin is a literal copy of my local
repo)
- shows what changes I have which are not submitted to myorigin
and/or origin
If at any step along that path something doesn't check out, it explains what
didn't check out, and has a helpful help-page about ways that I might configure
it so 'pending' can do something useful. Think of it like "git lint" and some
documentation.
That said, it's trickier than I thought, because git is capable of working in
so many ways. (all that complexity isn't there for nothing) Time will tell if I
can strike a useful balance.
^ permalink raw reply
* Re: policy and mechanism for less-connected clients
From: David Jeske @ 2008-06-26 16:21 UTC (permalink / raw)
To: Theodore Tso; +Cc: Junio C Hamano, git
In-Reply-To: <20080626113710.GD8610@mit.edu>
Thanks for pointing out the issue with automatically committing and bisect.
You're right, if I'm going to automatically commit under the covers I should
use stash instead. However, I don't want users to keep a dirty tree, and now
they don't have to.
To use your two-months-without-checkins example.. one of the big problems I
have with cvs/p4 is this notion that I'm not supposed to record my work every
5-50 minutes. I checkin every time my code does something new and the tests
pass. In my own startup projects/companies this is fine, because it's my tree.
As soon as the group policy stops me from checking in every 5-50 minutes, I
painfully make my own branch so I can checkin on my schedule. I'm starting to
witness some users solving this problem in a very libertarian way, by using git
to manage their local changes even though they work in a code-review restricted
cvs/p4 environment.
-- Theodore Tso wrote:
> I'd suggest that you try using git straight for a bit longer, before
> you start drawing these conclusions. Trust me, the concepts of git
> really aren't that hard to explain to people; that's not what you need
> to hide from people coming from the CVS world. The hard part is the
> fact that git's UI has all sorts of non-linearities and that git's
> documentation and introductory tutorials are not as good as it should
> be. (Although it's gotten a LOT better than just a year or two ago.)
I agree 100%. I am using git straight. I think I have read more git
documentation and definitely read more git source-code in trying to use it over
a couple months, than I have read of cvs/p4 in decades - just to try to
understand which of the 3 ways to get from here-to-there is correct, and then
when I pull back the red curtain a little further I realize I was totally
wrong.
This started as a "cheat sheet" file with the combination of git commands I had
to execute to perform each task. However, they are only valid in the context of
a git-repo that's configured in certain ways. I realized it would be simpler
(even for just me) if I had something that grouped commands and did 'lint'
sanity checks, with helpful tutorial responses. Thus the wrapper.
> Exactly. So what I would ask you to consider is that you may find it
> personally useful to design this system,
I see where you're going with this, and I agree...
> but afterwards, before you inflict it on projects, and deal
> with some of the attendent side effects (like all of these trash
> commits causing "git bisect" to go down the drain), that you
> consider whether *now* that you understand how git works and
> why it does some of the things it does, and what the
> shortcomings of the git porcelain are from a UI perspective, whether
> CVS refugees really would be best served by this system you are
> designing, or whether a few wrapper scripts to hide some of the more
> pointy spikes in git's CLI, plus some better tutorials, might in the
> long run be much better for these CVS developers that you are trying
> to serve.
Absolutly. I hope that you can understand my goal of an 'interactive command
line/tutorial linear path from cvs/p4 to git'. One where they don't get stuck
and turn back, but also where they work in ways which are 'fairly reasonable'
in the git community. I also hope you'll help me evaluate whether I've succeed
or just made another confusing set of compromises that are no good. There is no
need for more of the latter.
I also have a group that's been using git and wants to switch back to cvs/p4.
They are willing to give up tracking their local changes (or do it with private
gits) in order to get a simpler model for 'shared head of tree' development. I
think they are a good test-case as well.
------
So far, 1/2 of the lines of my script merely transitional documentation from
p4/cvs to git. As I write more of this prose, I realize that it may be helpful
as transition documentation webpages. However, it is much more than passive
documentation, because if there are 3 steps from here to there, I can look at
the repository and see where the user is, and tell them what they need to do
next.
As one example, I have a command "pending" (like p4 pending) which shows local
changes in my branch (on my inaccessible firewalled machine) which are not on
my origin repo(s). Except that in order for this concept to even make sense, it
first:
- checks if I have an 'origin' for a public repo
- checks that my current branch is tracking an [some]origin
- if it is mapped to a 'myorigin' personal published repo
(because I'm firewalled), it checks that the name of the
branch matches the myorigin/branchname (because it's easier
to think straight if myorigin is a literal copy of my local
repo)
- shows what changes I have which are not submitted to myorigin
and/or origin
If at any step along that path something doesn't check out, it explains what
didn't check out, and has a helpful help-page about ways that I might configure
it so 'pending' can do something useful. Think of it like "git lint" and some
documentation.
That said, it's trickier than I thought, because git is capable of working in
so many ways. (all that complexity isn't there for nothing) Time will tell if I
can strike a useful balance.
^ permalink raw reply
* pread() over NFS (again) [1.5.5.4]
From: Christian Holtje @ 2008-06-26 16:40 UTC (permalink / raw)
To: git
I have read all the threads on git having trouble with pread() and I
didn't see anything to help.
Situation:
git commands run on system "dev2" which is Linux 2.6.9-42.0.8.ELsmp
on an NFS mounted directory.
The NFS server is "dev1" which is Linux 2.4.28
I get the following output from git when doing a fetch:
remote: Counting objects: 406, done.
remote: Compressing objects: 100% (198/198), done.
remote: Total 253 (delta 127), reused 150 (delta 55)
Receiving objects: 100% (253/253), 5.27 MiB | 9136 KiB/s, done.
fatal: cannot pread pack file: No such file or directory
fatal: index-pack failed
The end of the strace looks like so:
pread(3, "", 205, 1373) = 0
write(2, "fatal: cannot pread pack file: N"..., 57) = 57
I have ran strace -o /somedir/q -ff git fetch and have the straces
available at:
http://docwhat.gerf.org/files/tmp/git-strace-20080626.tgz
I have worked around the problem by running the fetch from the nfs
server.
It looks like I can recreate this at will and I'm willing to help
figure this out.
Thanks!
Ciao!
^ permalink raw reply
* [PATCH] git-svn: follow revisions of svm-mirrored repositories
From: João Abecasis @ 2008-06-26 16:24 UTC (permalink / raw)
To: git
From a4cd092cd1aeae954378038d24f93af930610cc8 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Jo=C3=A3o=20Abecasis?= <joao@abecasis.name>
Date: Fri, 13 Jun 2008 20:32:57 -0500
Subject: [PATCH] git-svn: follow revisions of svm-mirrored repositories
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Although git-svn keeps track of source revisions in svm-mirrored
repositories, it doesn't use that information in all places.
This fixes working_head_info and Git::SVN::find_by_url to work with
mirrored repositories when useSvmProps is set. At least the commands
find-rev and rebase, which didn't work for me with svm-mirrored
repositories, are now working.
Signed-off-by: João Abecasis <joao@abecasis.name>
---
git-svn.perl | 36 ++++++++++++++++++++++++++++++++----
t/t9110-git-svn-use-svm-props.sh | 9 +++++++++
2 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/git-svn.perl b/git-svn.perl
index 4c9c59b..4df6c71 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -537,13 +537,13 @@ sub cmd_find_rev {
my $head = shift;
$head ||= 'HEAD';
my @refs;
- my (undef, undef, undef, $gs) = working_head_info($head, \@refs);
+ my (undef, undef, $uuid, $gs) = working_head_info($head, \@refs);
unless ($gs) {
die "Unable to determine upstream SVN information from ",
"$head history\n";
}
my $desired_revision = substr($revision_or_hash, 1);
- $result = $gs->rev_map_get($desired_revision);
+ $result = $gs->rev_map_get($desired_revision, $uuid);
} else {
my (undef, $rev, undef) = cmt_metadata($revision_or_hash);
$result = $rev;
@@ -1160,7 +1160,7 @@ sub working_head_info {
if (defined $url && defined $rev) {
next if $max{$url} and $max{$url} < $rev;
if (my $gs = Git::SVN->find_by_url($url)) {
- my $c = $gs->rev_map_get($rev);
+ my $c = $gs->rev_map_get($rev, $uuid);
if ($c && $c eq $hash) {
close $fh; # break the pipe
return ($url, $rev, $uuid, $gs);
@@ -1414,11 +1414,16 @@ sub fetch_all {
sub read_all_remotes {
my $r = {};
+ my $usesvmprops = eval {
+ command_oneline(qw/config --bool svn.useSvmProps/) } eq 'true';
foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
if (m!^(.+)\.fetch=\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!) {
my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
$local_ref =~ s{^/}{};
$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
+ $r->{$remote}->{svm} = {} if $usesvmprops;
+ } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
+ $r->{$1}->{svm} = {};
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
$r->{$1}->{url} = $2;
} elsif (m!^(.+)\.(branches|tags)=
@@ -1435,6 +1440,21 @@ sub read_all_remotes {
}
}
}
+
+ map {
+ if (defined $r->{$_}->{svm}) {
+ my $svm;
+ eval {
+ my $section = "svn-remote.$_";
+ $svm = {
+ source => tmp_config('--get', "$section.svm-source"),
+ replace => tmp_config('--get', "$section.svm-replace"),
+ }
+ };
+ $r->{$_}->{svm} = $svm;
+ }
+ } keys %$r;
+
$r;
}
@@ -1568,13 +1588,21 @@ sub find_by_url { # repos_root and, path are optional
}
my $p = $path;
my $rwr = rewrite_root({repo_id => $repo_id});
+ my $svm = $remotes->{$repo_id}->{svm}
+ if defined $remotes->{$repo_id}->{svm};
unless (defined $p) {
$p = $full_url;
my $z = $u;
+ my $prefix = '';
if ($rwr) {
$z = $rwr;
+ } elsif (defined $svm) {
+ $z = $svm->{source};
+ $prefix = $svm->{replace};
+ $prefix =~ s#^\Q$u\E(?:/|$)##;
+ $prefix =~ s#/$##;
}
- $p =~ s#^\Q$z\E(?:/|$)## or next;
+ $p =~ s#^\Q$z\E(?=/|$)#$prefix# or next;
}
foreach my $f (keys %$fetch) {
next if $f ne $p;
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
index 047659f..04d2a65 100755
--- a/t/t9110-git-svn-use-svm-props.sh
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -49,4 +49,13 @@ test_expect_success 'verify metadata for /dir' "
grep '^git-svn-id: $dir_url@1 $uuid$'
"
+test_expect_success 'find commit based on SVN revision number' "
+ git-svn find-rev r12 |
+ grep `git rev-parse HEAD`
+ "
+
+test_expect_success 'empty rebase' "
+ git-svn rebase
+ "
+
test_done
--
1.5.5.1
^ permalink raw reply related
* [PATCH] Don't use dash commands (git-foo) in tutorial-2
From: Ted Percival @ 2008-06-26 13:22 UTC (permalink / raw)
To: git
Signed-off-by: Ted Percival <ted@midg3t.net>
---
Documentation/gittutorial-2.txt | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Documentation/gittutorial-2.txt
b/Documentation/gittutorial-2.txt
index e3d5c1f..31e8a23 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -61,9 +61,9 @@ from your own version. Note that you can shorten it to
only a few
characters to save yourself typing all 40 hex digits:
------------------------------------------------
-$ git-cat-file -t 54196cc2
+$ git cat-file -t 54196cc2
commit
-$ git-cat-file commit 54196cc2
+$ git cat-file commit 54196cc2
tree 92b8b694ffb1675e5975148e1121810081dbdffe
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
@@ -166,7 +166,7 @@ hello world!
and the "parent" object refers to the previous commit:
------------------------------------------------
-$ git-cat-file commit 54196cc2
+$ git cat-file commit 54196cc2
tree 92b8b694ffb1675e5975148e1121810081dbdffe
author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
@@ -246,7 +246,7 @@ The last diff is empty, but no new commits have been
made, and the
head still doesn't contain the new line:
------------------------------------------------
-$ git-diff HEAD
+$ git diff HEAD
diff --git a/file.txt b/file.txt
index a042389..513feba 100644
--- a/file.txt
@@ -273,7 +273,7 @@ hello world, again
So what our "git add" did was store a new blob and then put
a reference to it in the index file. If we modify the file again,
-we'll see that the new modifications are reflected in the "git-diff"
+we'll see that the new modifications are reflected in the "git diff"
output:
------------------------------------------------
--
1.5.5.4
^ permalink raw reply related
* Re: [PATCH] cmd_reset: don't trash uncommitted changes unless told to
From: Jon Loeliger @ 2008-06-26 16:15 UTC (permalink / raw)
To: Andreas Ericsson; +Cc: Boaz Harrosh, Junio C Hamano, Steven Walter, git, jeske
In-Reply-To: <486329C9.8020801@op5.se>
Andreas Ericsson wrote:
> There is a "--hard" after one of them. It reads like this:
>
> git reset --hard ;# move current branch to random point in history
> # discarding working tree and index state
>
> git reset --mixed ;# move current branch to random point in history
> # discard the index but keep the working tree
>
> git reset --soft ;# move current branch to random point in history,
> # leaving index and working tree intact
I always thought that these would be best presented in
a linear ordering so that the effects were clearly
shown in an "increasing" way:
--soft
- touches one thing
--mixed
- touches one thing and a second
- this is the default
--hard
- touches one thing, a second and a third
Want a patch down that line?
jdl
^ permalink raw reply
* Re: git-svn branches creation question
From: Avery Pennarun @ 2008-06-26 16:08 UTC (permalink / raw)
To: Pascal Obry; +Cc: Git Mailing-List
In-Reply-To: <4863BBFB.8010305@obry.net>
On 6/26/08, Pascal Obry <pascal@obry.net> wrote:
> Is it possible to create and track new branches (or tags) using git-svn?
>
> I know how to track existing branches, but new ones?
>
> Some context. I'm tracking a CVS repository doing update from time to time
> on the master branch (using simply rsync and git-svn dcommit). Now I want to
> add a new tag for updates on the master. Currently I'm using svn copy. I'd
> like to do this directly from Git.
I don't think there's a way to do this. You can create branches in
git that *aren't* tracked in svn, of course, if you want to do that.
And creating branches in svn is a single command, even if you don't
have an svn checkout:
svn cp $SVN/trunk $SVN/branches/whatever
So it seems unnecessary to duplicate this feature in git.
Have fun,
Avery
^ permalink raw reply
* Re: [PATCH] cmd_reset: don't trash uncommitted changes unless told to
From: Avery Pennarun @ 2008-06-26 15:55 UTC (permalink / raw)
To: Johannes Schindelin
Cc: Björn Steinbrink, Junio C Hamano, Theodore Tso,
Johannes Sixt, Boaz Harrosh, Steven Walter, git, jeske
In-Reply-To: <alpine.DEB.1.00.0806261306060.9925@racer>
On 6/26/08, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> While we are nit-picking: Ted's version does not respect autocrlf, while
> Junio's does.
Is it intentional that git-show doesn't respect autocrlf, or just an oversight?
Avery
^ permalink raw reply
* Re: What's cooking in git.git (topics)
From: Jakub Narebski @ 2008-06-26 15:41 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Miklos Vajna, Pieter de Bie, Junio C Hamano, git
In-Reply-To: <alpine.DEB.1.00.0806242033570.9925@racer>
On Tue, 24 Jun 2008, Johannes Schindelin wrote:
> Hi,
>
> On Tue, 24 Jun 2008, Jakub Narebski wrote:
>
> > git-shell hackery won't solve problem, because not everybody is using
> > git-shell.
>
> The problem is not git-shell vs git potty.
>
> The problem is that not everybody magically updates their clients to ask
> for dash-less form.
With git-shell even if client uses dashed form it can find git commands
("hackery" is too strong a word for having git-shell search $GIT_EXEC_PATH).
But if one uses only SSH, server must have dashed form in a $PATH
--
Jakub Narebski
Poland
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: Brandon Casey @ 2008-06-26 15:13 UTC (permalink / raw)
To: Theodore Tso
Cc: Junio C Hamano, David Jeske, Jakub Narebski, Boaz Harrosh, git
In-Reply-To: <20080625022610.GB20361@mit.edu>
Theodore Tso wrote:
> On Tue, Jun 24, 2008 at 04:07:57PM -0700, Junio C Hamano wrote:
>>> Instead, I've just learned to be careful and my use of git reset
>>> --hard is mainly for historical reasons.
>> This makes it sound as if avoiding "reset --hard" is a good thing, but I
>> do not understand why.
>
> Well, it was Brandon Casey who was asserting that git reset --hard was
> evil, which I generally don't agree with.
I definitely don't think 'reset --hard' is evil. I _do_ think it is somewhat
of an advanced command. It should be used where it is appropriate. I think
it is a misuse of the command if it is used in place of checkout, which I got
the impression might be the case.
You described resetting an integration branch, Junio does a similar thing
with pu and these are both valid uses. This is what I was talking about
when I said that usually when I use reset I don't care about the state of
the branch I am resetting. I also agree there are many other valid uses for
'git reset --hard'.
-brandon
^ permalink raw reply
* git-svn branches creation question
From: Pascal Obry @ 2008-06-26 15:55 UTC (permalink / raw)
To: pascal, Git Mailing-List
Is it possible to create and track new branches (or tags) using git-svn?
I know how to track existing branches, but new ones?
Some context. I'm tracking a CVS repository doing update from time to
time on the master branch (using simply rsync and git-svn dcommit). Now
I want to add a new tag for updates on the master. Currently I'm using
svn copy. I'd like to do this directly from Git.
Thanks.
--
Pascal Obry
--
gpg --keyserver wwwkeys.pgp.net --recv-key C1082595
^ permalink raw reply
* Re: [q] git-diff --reverse 7def2be1..7def2be1^
From: Nicolas Pitre @ 2008-06-26 13:50 UTC (permalink / raw)
To: Jakub Narebski; +Cc: Ingo Molnar, Matthieu Moy, git
In-Reply-To: <200806261302.03952.jnareb@gmail.com>
On Thu, 26 Jun 2008, Jakub Narebski wrote:
> On Thu, 26. Jun 2008, Ingo Molnar wrote:
> > so please do not remove the git-* commands, they are really useful.
>
> Well, they are not removed, just moved aside (to address complaints
> of cluttering $PATH with 130+ programs), and I think that you can
> always install everything into /usr/bin/, as usual. Although it would
> be nice to be able to move aside only internal (*--*) commands, or
> perhaps even internal and plumbing.
Or simply add $gitexecdir to your search path.
Nicolas
^ permalink raw reply
* [PATCH v8] gitweb: add test suite with Test::WWW::Mechanize::CGI
From: Lea Wiemann @ 2008-06-26 13:48 UTC (permalink / raw)
To: git; +Cc: Lea Wiemann, Jakub Narebski
In-Reply-To: <1214273933-3925-1-git-send-email-LeWiemann@gmail.com>
This test uses Test::WWW::Mechanize::CGI to check gitweb's output. It
also uses HTML::Lint, XML::Parser, and Archive::Tar (if present, each)
to validate the HTML/XML/tgz output, and checks all links on the
tested pages if --long-tests is given.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Lea Wiemann <LeWiemann@gmail.com>
---
[Resent with correct subject.]
This patch applies on next.
Changes since v7:
- In Makefile, dump $PERL_PATH to GIT-BUILD-OPTIONS (which gets
source'd by test-lib.sh), and use it in t9710-perl-git-repo.sh.
- .git/description in the test repository no longer depends on $0
(this would e.g. cause 'cd t; make' to fail).
- Add test_link subroutine and use it everywhere in place of
ok(find_link...) so that links whose presence get tested get checked
and spidered in --long-tests mode.
- Add simple page caching (reduces execution time without --long-tests
by 25%). That's *really* helpful when you have to run those tests
on a regular basis. ;) (WWW::Mechanize::Cached won't work with
TWM::CGI, so we have to implement it ourselves; but it's easier
anyway.)
- Follow redirects rather than failing.
- Test subdirectories in tree view.
- Test error handling for non-existent hashes or hashes of wrong type.
- Test commitdiff_plain view.
- Expand test for history view.
- Test tag objects (not just symbolic tags) in tag list.
- Test a specific bug (under "diff formatting", marked TODO).
- Do not test correctness of line number fragments (#l[0-9]+); they're
broken too often right now.
- Probably some more minor improvements I've forgotten about. :)
Makefile | 1 +
t/t9503-gitweb-Mechanize.sh | 138 +++++++++++
t/t9503/test.pl | 553 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 692 insertions(+), 0 deletions(-)
create mode 100755 t/t9503-gitweb-Mechanize.sh
create mode 100755 t/t9503/test.pl
diff --git a/Makefile b/Makefile
index a129491..3bf4f14 100644
--- a/Makefile
+++ b/Makefile
@@ -1251,6 +1251,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
@echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@
+ @echo PERL_PATH=\''$(PERL_PATH_SQ)'\' >>$@
### Detect Tck/Tk interpreter path changes
ifndef NO_TCLTK
diff --git a/t/t9503-gitweb-Mechanize.sh b/t/t9503-gitweb-Mechanize.sh
new file mode 100755
index 0000000..4585cea
--- /dev/null
+++ b/t/t9503-gitweb-Mechanize.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Jakub Narebski
+# Copyright (c) 2008 Lea Wiemann
+#
+
+# This test supports the --long-tests option.
+
+# This test only runs on Perl 5.8 and later versions, since
+# Test::WWW::Mechanize::CGI requires Perl 5.8.
+
+test_description='gitweb tests (using WWW::Mechanize)
+
+This test uses Test::WWW::Mechanize::CGI to test gitweb.'
+
+# helper functions
+
+safe_chmod () {
+ chmod "$1" "$2" &&
+ if [ "$(git config --get core.filemode)" = false ]
+ then
+ git update-index --chmod="$1" "$2"
+ fi
+}
+
+. ./test-lib.sh
+
+# check if test can be run
+"$PERL_PATH" -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
+ test_expect_success \
+ 'skipping gitweb tests, perl version is too old' :
+ test_done
+ exit
+}
+
+"$PERL_PATH" -MTest::WWW::Mechanize::CGI -e '' >/dev/null 2>&1 || {
+ test_expect_success \
+ 'skipping gitweb tests, Test::WWW::Mechanize::CGI not found' :
+ test_done
+ exit
+}
+
+# set up test repository
+test_expect_success 'set up test repository' '
+
+ echo "Not an empty file." > file &&
+ git add file &&
+ test_tick && git commit -a -m "Initial commit." &&
+ git branch b &&
+
+ echo "New file" > new_file &&
+ git add new_file &&
+ test_tick && git commit -a -m "File added." &&
+
+ safe_chmod +x new_file &&
+ test_tick && git commit -a -m "Mode changed." &&
+
+ git mv new_file renamed_file &&
+ test_tick && git commit -a -m "File renamed." &&
+
+ rm renamed_file &&
+ ln -s file renamed_file &&
+ test_tick && git commit -a -m "File to symlink." &&
+ git tag with-symlink &&
+
+ git rm renamed_file &&
+ rm -f renamed_file &&
+ test_tick && git commit -a -m "File removed." &&
+
+ cp file file2 &&
+ git add file2 &&
+ test_tick && git commit -a -m "File copied." &&
+
+ echo "New line" >> file2 &&
+ safe_chmod +x file2 &&
+ test_tick && git commit -a -m "Mode change and modification." &&
+
+ mkdir dir1 &&
+ echo "New file" >> dir1/file1 &&
+ git add dir1/file1 &&
+ test_tick && git commit -a -m "File added in subdirectory." &&
+ git tag -m "creating a tag object" tag-object
+
+ git checkout b &&
+ echo "Branch" >> b &&
+ git add b &&
+ test_tick && git commit -a -m "On branch" &&
+ git checkout master &&
+ test_tick && git pull . b
+'
+
+# set up empty repository
+# TODO!
+
+# set up repositories for gitweb
+# TODO!
+
+# set up gitweb configuration
+safe_pwd="$("$PERL_PATH" -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
+cat >gitweb_config.perl <<EOF
+# gitweb configuration for tests
+
+our \$version = "current";
+our \$GIT = "$GIT_EXEC_PATH/git";
+our \$projectroot = "$safe_pwd";
+our \$project_maxdepth = 8;
+our \$home_link_str = "projects";
+our \$site_name = "[localhost]";
+our \$site_header = "";
+our \$site_footer = "";
+our \$home_text = "indextext.html";
+our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css");
+our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png";
+our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png";
+our \$projects_list = "";
+our \$export_ok = "";
+our \$strict_export = "";
+our %feature;
+\$feature{'blame'}{'default'} = [1];
+
+1;
+__END__
+EOF
+
+cat >.git/description <<EOF
+gitweb test repository
+EOF
+
+GITWEB_CONFIG="$(pwd)/gitweb_config.perl"
+export GITWEB_CONFIG
+
+# run tests
+
+test_external \
+ 'test gitweb output' \
+ "$PERL_PATH" ../t9503/test.pl
+
+test_done
diff --git a/t/t9503/test.pl b/t/t9503/test.pl
new file mode 100755
index 0000000..43ad062
--- /dev/null
+++ b/t/t9503/test.pl
@@ -0,0 +1,553 @@
+#!/usr/bin/perl
+use lib (split(/:/, $ENV{GITPERLLIB}));
+
+# This test supports the --long-tests option.
+
+use warnings;
+use strict;
+
+use Cwd qw( abs_path );
+use File::Spec;
+use File::Temp;
+use Storable;
+
+use Test::More qw(no_plan);
+
+die "this must be run by calling the t/t*.sh shell script(s)\n"
+ if Cwd->cwd !~ /trash directory$/;
+
+our $long_tests = $ENV{GIT_TEST_LONG}; # "our" so we can use "local $long_tests"
+
+eval { require Archive::Tar; };
+my $archive_tar_installed = !$@
+ or diag('Archive::Tar is not installed; no tests for valid snapshots');
+
+eval { require HTML::Lint; };
+my $html_lint_installed = !$@
+ or diag('HTML::Lint is not installed; no HTML validation tests');
+
+eval { require XML::Parser; };
+my $xml_parser_installed = !$@
+ or diag('XML::Parser is not installed; no tests for well-formed XML');
+
+sub rev_parse {
+ my $name = shift;
+ chomp(my $hash = `git rev-parse $name 2> /dev/null`);
+ $hash or undef;
+}
+
+sub get_type {
+ my $name = shift;
+ chomp(my $type = `git cat-file -t $name 2> /dev/null`);
+ $type or undef;
+}
+
+
+package OurMechanize;
+
+use base qw( Test::WWW::Mechanize::CGI );
+
+my %page_cache;
+# Cache requests.
+sub _make_request {
+ my ($self, $request) = (shift, shift);
+
+ my $response;
+ unless ($response = Storable::thaw($page_cache{$request->uri})) {
+ $response = $self->SUPER::_make_request($request, @_);
+ $page_cache{$request->uri} = Storable::freeze($response);
+ }
+ return $response;
+}
+
+# Fix whitespace problem.
+sub cgi_application {
+ my ($self, $application) = @_;
+
+ # This subroutine was copied (and modified) from
+ # WWW::Mechanize::CGI 0.3, which is licensed 'under the same
+ # terms as perl itself' and thus GPL compatible.
+ my $cgi = sub {
+ # Use exec, not the shell, to support embedded
+ # whitespace in the path to $application.
+ # http://rt.cpan.org/Ticket/Display.html?id=36654
+ my $status = system $application $application;
+ my $value = $status >> 8;
+
+ croak( qq/Failed to execute application '$application'. Reason: '$!'/ )
+ if ( $status == -1 );
+ croak( qq/Application '$application' exited with value: $value/ )
+ if ( $value > 0 );
+ };
+
+ $self->cgi($cgi);
+}
+
+package main;
+
+
+my @revisions = split /\s/, `git-rev-list --first-parent HEAD`;
+chomp(my @heads = map { (split('/', $_))[2] } `git-for-each-ref --sort=-committerdate refs/heads`);
+chomp(my @tags = map { (split('/', $_))[2] } `git-for-each-ref --sort=-committerdate refs/tags`);
+my @tag_objects = grep { get_type($_) eq 'tag' } @tags;
+chomp(my @root_entries = `git-ls-tree --name-only HEAD`);
+my @files = grep { get_type("HEAD:$_") eq 'blob' } @root_entries or die;
+my @directories = grep { get_type("HEAD:$_") eq 'tree' } @root_entries or die;
+# Only test one file and directory each.
+@files = $files[0] unless $long_tests;
+@directories = $directories[0] unless $long_tests;
+
+my $gitweb = abs_path(File::Spec->catfile('..', '..', 'gitweb', 'gitweb.cgi'));
+
+my $mech = OurMechanize->new;
+$mech->cgi_application($gitweb);
+# On some systems(?) it's necessary to have %ENV here, otherwise the
+# CGI process won't get *any* of the current environment variables
+# (not even PATH, etc.)
+$mech->env(%ENV,
+ GITWEB_CONFIG => $ENV{'GITWEB_CONFIG'},
+ SCRIPT_FILENAME => $gitweb,
+ $mech->env);
+
+# import config, predeclaring config variables
+our $site_name;
+require_ok($ENV{'GITWEB_CONFIG'})
+ or diag('Could not load gitweb config; some tests would fail');
+
+# Perform non-recursive checks on the current page, but do not check
+# the status code.
+my %verified_uris;
+sub _verify_page {
+ my ($uri, $fragment) = split '#', $mech->uri;
+ TODO: {
+ local $TODO = 'line number fragments can be broken for diffs and blames'
+ if $fragment && $fragment =~ /^l[0-9]+$/;
+ $mech->content_like(qr/(name|id)="$fragment"/,
+ "[auto] fragment #$fragment exists ($uri)")
+ if $fragment;
+ }
+
+ return 1 if $verified_uris{$uri};
+ $verified_uris{$uri} = 1;
+
+ # Internal errors yield 200 but cause gitweb.cgi to exit with
+ # non-zero exit code, which Mechanize::CGI translates to 500,
+ # so we don't really need to check for "Software error" here,
+ # provided that the test cases always check the status code.
+ #$mech->content_lacks('<h1>Software error:</h1>') or return 0;
+
+ # Validate. This is fast, so we can do it even without
+ # $long_tests.
+ $mech->html_lint_ok('[auto] validate HTML') or return 0
+ if $html_lint_installed && $mech->is_html;
+ my $content_type = $mech->response->header('Content-Type')
+ or die "$uri does not have a Content-Type header";
+ if ($xml_parser_installed && $content_type =~ /xml/) {
+ eval { XML::Parser->new->parse($mech->content); };
+ ok(!$@, "[auto] check for XML well-formedness ($uri)") or diag($@);
+ }
+ if ($archive_tar_installed && $uri =~ /sf=tgz/) {
+ my $snapshot_file = File::Temp->new;
+ print $snapshot_file $mech->content;
+ close $snapshot_file;
+ my $t = Archive::Tar->new;
+ $t->read($snapshot_file->filename, 1);
+ ok($t->get_files, "[auto] valid tgz snapshot ($uri)");
+ }
+ # WebService::Validator::Feed::W3C would be nice to
+ # use, but it doesn't support direct input (as opposed
+ # to URIs) long enough for our feeds.
+
+ return 1;
+}
+
+# Verify and spider the current page, the latter only if --long-tests
+# (-l) is given. Do not check the status code of the current page.
+my %spidered_uris; # pages whose links have been checked
+my %status_checked_uris; # verified pages whose status is known to be 2xx
+sub check_page {
+ _verify_page or return 0;
+ if ($long_tests && !$spidered_uris{$mech->uri} ) {
+ $spidered_uris{$mech->uri} = 1;
+ my $orig_url = $mech->uri;
+ TODO: {
+ local $TODO = "blame links can be broken sometimes"
+ if $orig_url =~ /a=blame/;
+ for my $url (map { $_->url_abs } $mech->followable_links) {
+ if (!$status_checked_uris{$url}) {
+ $status_checked_uris{$url} = 1;
+ local $long_tests = 0; # stop recursing
+ test_page($url, "[auto] check link ($url)")
+ or diag("broken link to $url on $orig_url");
+ $mech->back;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+my $baseurl = "http://localhost";
+my ($params, $url, $pagedesc, $status);
+
+# test_page ( <params>, <page_description>, <expected_status> )
+# Example:
+# if (test_page('?p=.git;a=summary', 'repository summary')) {
+# $mech->...;
+# $mech->...;
+# }
+#
+# Test that the page can be opened, call _verify_page on it, and
+# return true if there was no test failure. Also set the global
+# variables $params, $pagedesc, and $url for use in the if block.
+# Optionally pass a third parameter $status to test the HTTP status
+# code of the page (useful for error pages). You can also pass a full
+# URL instead of just parameters as the first parameter.
+sub test_page {
+ ($params, $pagedesc, $status) = @_;
+ # missing $pagedesc is usually accidental
+ die "$params: no pagedesc given" unless defined $pagedesc;
+ if($params =~ /^$baseurl/) {
+ $url = "$params";
+ } else {
+ $url = "$baseurl$params";
+ }
+ $mech->get($url);
+ like($mech->status, $status ? qr/$status/ : qr/^[23][0-9][0-9]$/,
+ "$pagedesc: $url" . ($status ? " -- yields $status" : ""))
+ or return 0;
+ if ($mech->status =~ /^3/) {
+ # Don't check 3xx, they tend to look funny.
+ my $location = $mech->response->header('Location');
+ $mech->back; # compensate for history
+ return test_page($location, "follow redirect from $url");
+ } else {
+ return check_page;
+ }
+}
+
+# follow_link ( \%parms, $pagedesc )
+# Example:
+# if (follow_link( { text => 'commit' }, 'first commit link')) {
+# $mech->...;
+# $mech->back;
+# }
+# Like test_page, but does not support status code testing, and
+# returns true if there was a link at all, regardless of whether it
+# was [23]xx or not.
+sub follow_link {
+ (my $parms, $pagedesc) = @_;
+ my $link = $mech->find_link(%$parms);
+ my $current_url = $mech->uri;
+ ok($link, "link exists: $pagedesc (on page $current_url)") or return 0;
+ test_page($link->url, "follow link: $pagedesc (on page $current_url)");
+ return 1;
+}
+
+# like follow_link, except that only checks and goes back immediately;
+# use this instead of ok(find_link...).
+sub test_link {
+ my ($parms, $pagedesc) = @_;
+ my $current_url = $mech->uri;
+ if($long_tests) {
+ # Check status, validate, spider.
+ return follow_link($parms, $pagedesc) && $mech->back;
+ } else {
+ # Only check presence of the link (much faster).
+ return ok($mech->find_link(%$parms),
+ "link exists: $pagedesc (on page $current_url)");
+ }
+}
+
+sub get_summary {
+ test_page('?p=.git', 'repository summary')
+}
+
+if (test_page '', 'project list (implicit)') {
+ $mech->title_like(qr!$site_name!,
+ "title contains $site_name");
+ $mech->content_contains('gitweb test repository',
+ 'lists test repository (by description)');
+}
+
+# Test repository summary: implicit, implicit with pathinfo, explicit.
+for my $sumparams ('?p=.git', '/.git', '?p=.git;a=summary') {
+ if (test_page $sumparams, 'repository summary') {
+ $mech->title_like(qr!$site_name.*\.git/summary!,
+ "title contains $site_name and \".git/summary\"");
+ }
+}
+
+# Search form (on summary page).
+if (get_summary && $mech->submit_form_ok(
+ { form_number => 1, fields => { 's' => 'Initial' } },
+ 'submit search form (default)')) {
+ check_page;
+ $mech->content_contains('Initial commit',
+ 'content contains commit we searched for');
+}
+
+test_page('?p=non-existent.git', 'non-existent project', 404);
+test_page('?p=.git;a=commit;h=non-existent', 'non-existent commit', 404);
+
+
+# Summary view
+
+get_summary or die 'summary page failed; no point in running the remaining tests';
+
+# Check short log. To do: Extract into separate test_short_log
+# function since the short log occurs on several pages.
+for my $revision (@revisions) {
+ for my $link_text qw( commit commitdiff tree snapshot ) {
+ test_link( { url_abs_regex => qr/h=$revision/, text => $link_text },
+ "$link_text link for $revision");
+ }
+}
+
+# Check that branches and tags are highlighted in green and yellow in
+# the shortlog. We assume here that we are on master, so it should be
+# at the top.
+$mech->content_like(qr{<span [^>]*class="head"[^>]*>master</span>},
+ 'master branch is highlighted in shortlog');
+$mech->content_like(qr{<span [^>]*class="tag"[^>]*>$tags[0]</span>},
+ "$tags[0] (most recent tag) is highlighted in shortlog");
+
+# Check heads. (This should be extracted as well.)
+for my $head (@heads) {
+ for my $link_text qw( shortlog log tree ) {
+ test_link( { url_abs_regex => qr{h=refs/heads/$head}, text => $link_text },
+ "$link_text link for head '$head'");
+ }
+}
+
+# Check tags (assume we only have tags referring to commits, not to
+# blobs or trees).
+for my $tag (@tags) {
+ my $commit = rev_parse("$tag^{commit}");
+ test_link( { url_abs_regex => qr{h=refs/tags/$tag}, text => 'shortlog' },
+ "shortlog link for tag '$tag'");
+ test_link( { url_abs_regex => qr{h=refs/tags/$tag}, text => 'log' },
+ "log link for tag '$tag'");
+ test_link( { url_abs_regex => qr{h=$commit}, text => 'commit' },
+ "commit link for tag '$tag'");
+ test_link( { url_abs_regex => qr{h=$commit}, text => $tag },
+ "'$tag' links to the commit as well");
+ # To do: Test tag link for tag objects.
+ # Why don't we have tree + snapshot links?
+}
+for my $tag (@tag_objects) {
+ my $tag_sha1 = rev_parse($tag);
+ test_link( { url_abs_regex => qr{h=$tag_sha1}, text => 'tag' },
+ "tag link for tag object '$tag'" );
+}
+
+
+# RSS/Atom/OPML view
+# Simply retrieve and verify well-formedness, but don't spider.
+$mech->get_ok('?p=.git;a=atom', 'Atom feed') and _verify_page;
+$mech->get_ok('?p=.git;a=rss', 'RSS feed') and _verify_page;
+TODO: {
+ # Now spider -- but there are broken links.
+ # http://mid.gmane.org/485EB333.5070108@gmail.com
+ local $TODO = "fix broken links in Atom/RSS feeds";
+ test_page('?p=.git;a=atom', 'Atom feed');
+ test_page('?p=.git;a=rss', 'RSS feed');
+}
+test_page('?a=opml', 'OPML outline');
+
+
+# Commit view
+if (test_page('?p=.git;a=commit;h=master', 'view HEAD commit')) {
+ my $tree_sha1 = rev_parse('master:');
+ test_link( { url_abs_regex => qr/a=tree/, text => rev_parse('master:') },
+ "SHA1 link to tree on commit page ($url)");
+ test_link( { url_abs_regex => qr/h=$tree_sha1/, text => 'tree' },
+ "'tree' link to tree on commit page ($url)");
+ $mech->content_like(qr/A U Thor/, "author mentioned on commit page ($url)");
+}
+
+
+# Commitdiff view
+if (get_summary &&
+ follow_link( { text_regex => qr/file added/i }, 'commit with added file') &&
+ follow_link( { text => 'commitdiff' }, 'commitdiff')) {
+ $mech->content_like(qr/new file with mode/, "commitdiff has diffstat ($url)");
+ $mech->content_like(qr/new file mode/, "commitdiff has diff ($url)");
+}
+test_page("?p=.git;a=commitdiff;h=$revisions[-1]",
+ 'commitdiff without parent');
+
+# Diff formattting problem.
+if (get_summary &&
+ follow_link( { text_regex => qr/renamed/ }, 'commit with rename') &&
+ follow_link( { text => 'commitdiff' }, 'commitdiff')) {
+ TODO: {
+ local $TODO = "bad a/* link in diff";
+ if (follow_link( { text_regex => qr!^a/! },
+ 'a/* link (probably wrong)')) {
+ # The page we land on here is broken already.
+ follow_link( { url_abs_regex => qr/a=blob_plain/ },
+ 'linked file name'); # bang
+ }
+ }
+}
+
+
+# Raw commitdiff (commitdiff_plain) view
+if (test_page('?p=.git;a=commit;h=refs/tags/tag-object',
+ 'commit view of tags/tag-object') &&
+ follow_link( { text => 'commitdiff' }, "'commitdiff'") &&
+ follow_link( { text => 'raw' }, "'raw' (commitdiff_plain)")) {
+ $mech->content_like(qr/^From: A U Thor <author\@example.com>$/m,
+ 'commitdiff_plain: From header');
+ TODO: {
+ local $TODO = 'date header mangles timezone';
+ $mech->content_like(qr/^Date: Thu, 7 Apr 2005 15:..:13 -0700$/m,
+ 'commitdiff_plain: Date header (correct)');
+ }
+ $mech->content_like(qr/^Date: Thu, 7 Apr 2005 22:..:13 \+0000 \(-0700\)$/m,
+ 'commitdiff_plain: Date header (UTC, wrong)');
+ $mech->content_like(qr/^Subject: .+$/m,
+ 'commitdiff_plain: Subject header');
+ # $ markers inexplicably don't work here if we use like(...)
+ # or $mech->content_like().
+ ok($mech->content =~ /^X-Git-Tag: tag-object\^0$/m,
+ 'commitdiff_plain: X-Git-Tag header');
+ ok($mech->content =~ /^X-Git-Url: $baseurl\?p=\.git;a=commitdiff_plain;h=refs%2Ftags%2Ftag-object$/m,
+ 'commitdiff_plain: X-Git-Url header');
+ ok($mech->content =~ /^---$/m, 'commitdiff_plain: separator$');
+ $mech->content_like(qr/^diff --git /m, 'commitdiff_plain: diff$');
+}
+
+
+# Tree view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files) {
+ my $sha1 = rev_parse("HEAD:$file");
+ test_link( { text => $file, url_abs_regex => qr/h=$sha1/ },
+ "'$file' is listed and linked");
+ test_link({ url_abs_regex => qr/f=$file/, text => $_ },
+ "'$_' link") foreach qw( blame blob history raw );
+ }
+ for my $directory (@directories) {
+ my $sha1 = rev_parse("HEAD:$directory");
+ test_link({ url_abs_regex => qr/f=$directory/, text => $_ },
+ "'$_' link") foreach qw( tree history );
+ if(follow_link( { text => $directory, url_abs_regex => qr/h=$sha1/ },
+ "'$directory is listed and linked" )) {
+ if(follow_link( { text => '..' }, 'parent directory')) {
+ test_link({ url_abs_regex => qr/h=$sha1/,
+ text => $directory },
+ 'back to original tree view');
+ $mech->back;
+ }
+ $mech->back;
+ }
+ }
+}
+
+
+# Blame view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $blame_link ($mech->find_all_links(text => 'blame')) {
+ my $url = $blame_link->url;
+ $mech->get_ok($url, "get $url -- blame link on tree view")
+ and _verify_page;
+ $mech->content_like(qr/A U Thor/,
+ "author mentioned on blame page");
+ TODO: {
+ # Now spider -- but there are broken links.
+ # http://mid.gmane.org/485EC621.7090101@gmail.com
+ local $TODO = "fix broken links in certain blame views";
+ check_page;
+ }
+ last unless $long_tests; # only test first blame link
+ }
+}
+
+
+# History view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files, @directories) {
+ my $type = get_type("HEAD:$file"); # blob or tree
+ if (follow_link( { text => 'history', url_abs_regex => qr/f=$file/ },
+ "history link for '$file'")) {
+ # There is at least one commit, so A U Thor is mentioned.
+ $mech->content_contains('A U Thor', 'A U Thor mentioned');
+ # The following tests test for at least *one*
+ # link of each type and are weak since we
+ # don't have any knowledge of commit hashes.
+ test_link( { text => $type, url_abs_regex => qr/f=$file/ },
+ "$type");
+ test_link( { text => 'commitdiff' },
+ "commitdiff");
+ test_link( { url_abs_regex => qr/a=commit;.*h=[a-f0-9]{40}/ },
+ "subject links to commit"); # weak, brittle
+ $mech->back;
+ }
+ }
+}
+
+
+# Blob view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files) {
+ if (follow_link( { text => $file, url_abs_regex => qr/a=blob/ },
+ "\"$file\" (blob) entry on tree view")) {
+ chomp(my $first_line_regex = (`cat "$file"`)[0]);
+ $first_line_regex =~ s/ / | /g;
+ # Hope that the first line doesn't contain any
+ # HTML-escapable character.
+ $mech->content_like(qr/$first_line_regex/,
+ "blob view contains first line of file ($url)");
+ $mech->back;
+ }
+ }
+}
+
+
+# Raw (blob_plain) view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files) {
+ if (follow_link( { text => 'raw', url_abs_regex => qr/f=$file/ },
+ "raw (blob_plain) entry for \"$file\" in tree view")) {
+ chomp(my $first_line = (`cat "$file"`)[0]);
+ $mech->content_contains(
+ $first_line, "blob_plain view contains first line of file");
+ $mech->back;
+ }
+ }
+}
+
+
+# Error handling
+# Pass valid and invalid paths to various file-based actions
+for my $action qw( blame blob blob_plain blame ) {
+ test_page("?p=.git;a=$action;f=$files[0];hb=HEAD",
+ "$action: look up existent file");
+ test_page("?p=.git;a=$action;f=does_not_exist;hb=HEAD",
+ "$action: look up non-existent file", 404);
+ TODO: {
+ local $TODO = 'wrong error code (but using Git::Repo will fix this)';
+ test_page("?p=.git;a=$action;f=$directories[0];hb=HEAD",
+ "$action: look up directory", 400);
+ }
+}
+TODO: {
+ local $TODO = 'wrong error code (but using Git::Repo will fix this)';
+ test_page("?p=.git;a=tree;f=$files[0];hb=HEAD",
+ 'tree: look up existent file', 400);
+}
+# Pass valid and invalid paths to tree action
+test_page("?p=.git;a=tree;f=does_not_exist;hb=HEAD",
+ 'tree: look up non-existent file', 404);
+test_page("?p=.git;a=tree;f=$directories[0];hb=HEAD",
+ 'tree: look up directory');
+TODO: {
+ local $TODO = 'cannot use f=/ or f= for trees';
+ test_page("?p=.git;a=tree;f=/;hb=HEAD", 'tree: look up directory');
+}
+
+
+1;
+__END__
--
1.5.6.109.g33ded.dirty
^ permalink raw reply related
* [PATCH] gitweb: add test suite with Test::WWW::Mechanize::CGI
From: Lea Wiemann @ 2008-06-26 13:47 UTC (permalink / raw)
To: git; +Cc: Lea Wiemann, Jakub Narebski
In-Reply-To: <1214273933-3925-1-git-send-email-LeWiemann@gmail.com>
This test uses Test::WWW::Mechanize::CGI to check gitweb's output. It
also uses HTML::Lint, XML::Parser, and Archive::Tar (if present, each)
to validate the HTML/XML/tgz output, and checks all links on the
tested pages if --long-tests is given.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Lea Wiemann <LeWiemann@gmail.com>
---
This patch applies on next.
Changes since v7:
- In Makefile, dump $PERL_PATH to GIT-BUILD-OPTIONS (which gets
source'd by test-lib.sh), and use it in t9710-perl-git-repo.sh.
- .git/description in the test repository no longer depends on $0
(this would e.g. cause 'cd t; make' to fail).
- Add test_link subroutine and use it everywhere in place of
ok(find_link...) so that links whose presence get tested get checked
and spidered in --long-tests mode.
- Add simple page caching (reduces execution time without --long-tests
by 25%). That's *really* helpful when you have to run those tests
on a regular basis. ;) (WWW::Mechanize::Cached won't work with
TWM::CGI, so we have to implement it ourselves; but it's easier
anyway.)
- Follow redirects rather than failing.
- Test subdirectories in tree view.
- Test error handling for non-existent hashes or hashes of wrong type.
- Test commitdiff_plain view.
- Expand test for history view.
- Test tag objects (not just symbolic tags) in tag list.
- Test a specific bug (under "diff formatting", marked TODO).
- Do not test correctness of line number fragments (#l[0-9]+); they're
broken too often right now.
- Probably some more minor improvements I've forgotten about. :)
Makefile | 1 +
t/t9503-gitweb-Mechanize.sh | 138 +++++++++++
t/t9503/test.pl | 553 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 692 insertions(+), 0 deletions(-)
create mode 100755 t/t9503-gitweb-Mechanize.sh
create mode 100755 t/t9503/test.pl
diff --git a/Makefile b/Makefile
index a129491..3bf4f14 100644
--- a/Makefile
+++ b/Makefile
@@ -1251,6 +1251,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
@echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@
+ @echo PERL_PATH=\''$(PERL_PATH_SQ)'\' >>$@
### Detect Tck/Tk interpreter path changes
ifndef NO_TCLTK
diff --git a/t/t9503-gitweb-Mechanize.sh b/t/t9503-gitweb-Mechanize.sh
new file mode 100755
index 0000000..4585cea
--- /dev/null
+++ b/t/t9503-gitweb-Mechanize.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Jakub Narebski
+# Copyright (c) 2008 Lea Wiemann
+#
+
+# This test supports the --long-tests option.
+
+# This test only runs on Perl 5.8 and later versions, since
+# Test::WWW::Mechanize::CGI requires Perl 5.8.
+
+test_description='gitweb tests (using WWW::Mechanize)
+
+This test uses Test::WWW::Mechanize::CGI to test gitweb.'
+
+# helper functions
+
+safe_chmod () {
+ chmod "$1" "$2" &&
+ if [ "$(git config --get core.filemode)" = false ]
+ then
+ git update-index --chmod="$1" "$2"
+ fi
+}
+
+. ./test-lib.sh
+
+# check if test can be run
+"$PERL_PATH" -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
+ test_expect_success \
+ 'skipping gitweb tests, perl version is too old' :
+ test_done
+ exit
+}
+
+"$PERL_PATH" -MTest::WWW::Mechanize::CGI -e '' >/dev/null 2>&1 || {
+ test_expect_success \
+ 'skipping gitweb tests, Test::WWW::Mechanize::CGI not found' :
+ test_done
+ exit
+}
+
+# set up test repository
+test_expect_success 'set up test repository' '
+
+ echo "Not an empty file." > file &&
+ git add file &&
+ test_tick && git commit -a -m "Initial commit." &&
+ git branch b &&
+
+ echo "New file" > new_file &&
+ git add new_file &&
+ test_tick && git commit -a -m "File added." &&
+
+ safe_chmod +x new_file &&
+ test_tick && git commit -a -m "Mode changed." &&
+
+ git mv new_file renamed_file &&
+ test_tick && git commit -a -m "File renamed." &&
+
+ rm renamed_file &&
+ ln -s file renamed_file &&
+ test_tick && git commit -a -m "File to symlink." &&
+ git tag with-symlink &&
+
+ git rm renamed_file &&
+ rm -f renamed_file &&
+ test_tick && git commit -a -m "File removed." &&
+
+ cp file file2 &&
+ git add file2 &&
+ test_tick && git commit -a -m "File copied." &&
+
+ echo "New line" >> file2 &&
+ safe_chmod +x file2 &&
+ test_tick && git commit -a -m "Mode change and modification." &&
+
+ mkdir dir1 &&
+ echo "New file" >> dir1/file1 &&
+ git add dir1/file1 &&
+ test_tick && git commit -a -m "File added in subdirectory." &&
+ git tag -m "creating a tag object" tag-object
+
+ git checkout b &&
+ echo "Branch" >> b &&
+ git add b &&
+ test_tick && git commit -a -m "On branch" &&
+ git checkout master &&
+ test_tick && git pull . b
+'
+
+# set up empty repository
+# TODO!
+
+# set up repositories for gitweb
+# TODO!
+
+# set up gitweb configuration
+safe_pwd="$("$PERL_PATH" -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
+cat >gitweb_config.perl <<EOF
+# gitweb configuration for tests
+
+our \$version = "current";
+our \$GIT = "$GIT_EXEC_PATH/git";
+our \$projectroot = "$safe_pwd";
+our \$project_maxdepth = 8;
+our \$home_link_str = "projects";
+our \$site_name = "[localhost]";
+our \$site_header = "";
+our \$site_footer = "";
+our \$home_text = "indextext.html";
+our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css");
+our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png";
+our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png";
+our \$projects_list = "";
+our \$export_ok = "";
+our \$strict_export = "";
+our %feature;
+\$feature{'blame'}{'default'} = [1];
+
+1;
+__END__
+EOF
+
+cat >.git/description <<EOF
+gitweb test repository
+EOF
+
+GITWEB_CONFIG="$(pwd)/gitweb_config.perl"
+export GITWEB_CONFIG
+
+# run tests
+
+test_external \
+ 'test gitweb output' \
+ "$PERL_PATH" ../t9503/test.pl
+
+test_done
diff --git a/t/t9503/test.pl b/t/t9503/test.pl
new file mode 100755
index 0000000..43ad062
--- /dev/null
+++ b/t/t9503/test.pl
@@ -0,0 +1,553 @@
+#!/usr/bin/perl
+use lib (split(/:/, $ENV{GITPERLLIB}));
+
+# This test supports the --long-tests option.
+
+use warnings;
+use strict;
+
+use Cwd qw( abs_path );
+use File::Spec;
+use File::Temp;
+use Storable;
+
+use Test::More qw(no_plan);
+
+die "this must be run by calling the t/t*.sh shell script(s)\n"
+ if Cwd->cwd !~ /trash directory$/;
+
+our $long_tests = $ENV{GIT_TEST_LONG}; # "our" so we can use "local $long_tests"
+
+eval { require Archive::Tar; };
+my $archive_tar_installed = !$@
+ or diag('Archive::Tar is not installed; no tests for valid snapshots');
+
+eval { require HTML::Lint; };
+my $html_lint_installed = !$@
+ or diag('HTML::Lint is not installed; no HTML validation tests');
+
+eval { require XML::Parser; };
+my $xml_parser_installed = !$@
+ or diag('XML::Parser is not installed; no tests for well-formed XML');
+
+sub rev_parse {
+ my $name = shift;
+ chomp(my $hash = `git rev-parse $name 2> /dev/null`);
+ $hash or undef;
+}
+
+sub get_type {
+ my $name = shift;
+ chomp(my $type = `git cat-file -t $name 2> /dev/null`);
+ $type or undef;
+}
+
+
+package OurMechanize;
+
+use base qw( Test::WWW::Mechanize::CGI );
+
+my %page_cache;
+# Cache requests.
+sub _make_request {
+ my ($self, $request) = (shift, shift);
+
+ my $response;
+ unless ($response = Storable::thaw($page_cache{$request->uri})) {
+ $response = $self->SUPER::_make_request($request, @_);
+ $page_cache{$request->uri} = Storable::freeze($response);
+ }
+ return $response;
+}
+
+# Fix whitespace problem.
+sub cgi_application {
+ my ($self, $application) = @_;
+
+ # This subroutine was copied (and modified) from
+ # WWW::Mechanize::CGI 0.3, which is licensed 'under the same
+ # terms as perl itself' and thus GPL compatible.
+ my $cgi = sub {
+ # Use exec, not the shell, to support embedded
+ # whitespace in the path to $application.
+ # http://rt.cpan.org/Ticket/Display.html?id=36654
+ my $status = system $application $application;
+ my $value = $status >> 8;
+
+ croak( qq/Failed to execute application '$application'. Reason: '$!'/ )
+ if ( $status == -1 );
+ croak( qq/Application '$application' exited with value: $value/ )
+ if ( $value > 0 );
+ };
+
+ $self->cgi($cgi);
+}
+
+package main;
+
+
+my @revisions = split /\s/, `git-rev-list --first-parent HEAD`;
+chomp(my @heads = map { (split('/', $_))[2] } `git-for-each-ref --sort=-committerdate refs/heads`);
+chomp(my @tags = map { (split('/', $_))[2] } `git-for-each-ref --sort=-committerdate refs/tags`);
+my @tag_objects = grep { get_type($_) eq 'tag' } @tags;
+chomp(my @root_entries = `git-ls-tree --name-only HEAD`);
+my @files = grep { get_type("HEAD:$_") eq 'blob' } @root_entries or die;
+my @directories = grep { get_type("HEAD:$_") eq 'tree' } @root_entries or die;
+# Only test one file and directory each.
+@files = $files[0] unless $long_tests;
+@directories = $directories[0] unless $long_tests;
+
+my $gitweb = abs_path(File::Spec->catfile('..', '..', 'gitweb', 'gitweb.cgi'));
+
+my $mech = OurMechanize->new;
+$mech->cgi_application($gitweb);
+# On some systems(?) it's necessary to have %ENV here, otherwise the
+# CGI process won't get *any* of the current environment variables
+# (not even PATH, etc.)
+$mech->env(%ENV,
+ GITWEB_CONFIG => $ENV{'GITWEB_CONFIG'},
+ SCRIPT_FILENAME => $gitweb,
+ $mech->env);
+
+# import config, predeclaring config variables
+our $site_name;
+require_ok($ENV{'GITWEB_CONFIG'})
+ or diag('Could not load gitweb config; some tests would fail');
+
+# Perform non-recursive checks on the current page, but do not check
+# the status code.
+my %verified_uris;
+sub _verify_page {
+ my ($uri, $fragment) = split '#', $mech->uri;
+ TODO: {
+ local $TODO = 'line number fragments can be broken for diffs and blames'
+ if $fragment && $fragment =~ /^l[0-9]+$/;
+ $mech->content_like(qr/(name|id)="$fragment"/,
+ "[auto] fragment #$fragment exists ($uri)")
+ if $fragment;
+ }
+
+ return 1 if $verified_uris{$uri};
+ $verified_uris{$uri} = 1;
+
+ # Internal errors yield 200 but cause gitweb.cgi to exit with
+ # non-zero exit code, which Mechanize::CGI translates to 500,
+ # so we don't really need to check for "Software error" here,
+ # provided that the test cases always check the status code.
+ #$mech->content_lacks('<h1>Software error:</h1>') or return 0;
+
+ # Validate. This is fast, so we can do it even without
+ # $long_tests.
+ $mech->html_lint_ok('[auto] validate HTML') or return 0
+ if $html_lint_installed && $mech->is_html;
+ my $content_type = $mech->response->header('Content-Type')
+ or die "$uri does not have a Content-Type header";
+ if ($xml_parser_installed && $content_type =~ /xml/) {
+ eval { XML::Parser->new->parse($mech->content); };
+ ok(!$@, "[auto] check for XML well-formedness ($uri)") or diag($@);
+ }
+ if ($archive_tar_installed && $uri =~ /sf=tgz/) {
+ my $snapshot_file = File::Temp->new;
+ print $snapshot_file $mech->content;
+ close $snapshot_file;
+ my $t = Archive::Tar->new;
+ $t->read($snapshot_file->filename, 1);
+ ok($t->get_files, "[auto] valid tgz snapshot ($uri)");
+ }
+ # WebService::Validator::Feed::W3C would be nice to
+ # use, but it doesn't support direct input (as opposed
+ # to URIs) long enough for our feeds.
+
+ return 1;
+}
+
+# Verify and spider the current page, the latter only if --long-tests
+# (-l) is given. Do not check the status code of the current page.
+my %spidered_uris; # pages whose links have been checked
+my %status_checked_uris; # verified pages whose status is known to be 2xx
+sub check_page {
+ _verify_page or return 0;
+ if ($long_tests && !$spidered_uris{$mech->uri} ) {
+ $spidered_uris{$mech->uri} = 1;
+ my $orig_url = $mech->uri;
+ TODO: {
+ local $TODO = "blame links can be broken sometimes"
+ if $orig_url =~ /a=blame/;
+ for my $url (map { $_->url_abs } $mech->followable_links) {
+ if (!$status_checked_uris{$url}) {
+ $status_checked_uris{$url} = 1;
+ local $long_tests = 0; # stop recursing
+ test_page($url, "[auto] check link ($url)")
+ or diag("broken link to $url on $orig_url");
+ $mech->back;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+my $baseurl = "http://localhost";
+my ($params, $url, $pagedesc, $status);
+
+# test_page ( <params>, <page_description>, <expected_status> )
+# Example:
+# if (test_page('?p=.git;a=summary', 'repository summary')) {
+# $mech->...;
+# $mech->...;
+# }
+#
+# Test that the page can be opened, call _verify_page on it, and
+# return true if there was no test failure. Also set the global
+# variables $params, $pagedesc, and $url for use in the if block.
+# Optionally pass a third parameter $status to test the HTTP status
+# code of the page (useful for error pages). You can also pass a full
+# URL instead of just parameters as the first parameter.
+sub test_page {
+ ($params, $pagedesc, $status) = @_;
+ # missing $pagedesc is usually accidental
+ die "$params: no pagedesc given" unless defined $pagedesc;
+ if($params =~ /^$baseurl/) {
+ $url = "$params";
+ } else {
+ $url = "$baseurl$params";
+ }
+ $mech->get($url);
+ like($mech->status, $status ? qr/$status/ : qr/^[23][0-9][0-9]$/,
+ "$pagedesc: $url" . ($status ? " -- yields $status" : ""))
+ or return 0;
+ if ($mech->status =~ /^3/) {
+ # Don't check 3xx, they tend to look funny.
+ my $location = $mech->response->header('Location');
+ $mech->back; # compensate for history
+ return test_page($location, "follow redirect from $url");
+ } else {
+ return check_page;
+ }
+}
+
+# follow_link ( \%parms, $pagedesc )
+# Example:
+# if (follow_link( { text => 'commit' }, 'first commit link')) {
+# $mech->...;
+# $mech->back;
+# }
+# Like test_page, but does not support status code testing, and
+# returns true if there was a link at all, regardless of whether it
+# was [23]xx or not.
+sub follow_link {
+ (my $parms, $pagedesc) = @_;
+ my $link = $mech->find_link(%$parms);
+ my $current_url = $mech->uri;
+ ok($link, "link exists: $pagedesc (on page $current_url)") or return 0;
+ test_page($link->url, "follow link: $pagedesc (on page $current_url)");
+ return 1;
+}
+
+# like follow_link, except that only checks and goes back immediately;
+# use this instead of ok(find_link...).
+sub test_link {
+ my ($parms, $pagedesc) = @_;
+ my $current_url = $mech->uri;
+ if($long_tests) {
+ # Check status, validate, spider.
+ return follow_link($parms, $pagedesc) && $mech->back;
+ } else {
+ # Only check presence of the link (much faster).
+ return ok($mech->find_link(%$parms),
+ "link exists: $pagedesc (on page $current_url)");
+ }
+}
+
+sub get_summary {
+ test_page('?p=.git', 'repository summary')
+}
+
+if (test_page '', 'project list (implicit)') {
+ $mech->title_like(qr!$site_name!,
+ "title contains $site_name");
+ $mech->content_contains('gitweb test repository',
+ 'lists test repository (by description)');
+}
+
+# Test repository summary: implicit, implicit with pathinfo, explicit.
+for my $sumparams ('?p=.git', '/.git', '?p=.git;a=summary') {
+ if (test_page $sumparams, 'repository summary') {
+ $mech->title_like(qr!$site_name.*\.git/summary!,
+ "title contains $site_name and \".git/summary\"");
+ }
+}
+
+# Search form (on summary page).
+if (get_summary && $mech->submit_form_ok(
+ { form_number => 1, fields => { 's' => 'Initial' } },
+ 'submit search form (default)')) {
+ check_page;
+ $mech->content_contains('Initial commit',
+ 'content contains commit we searched for');
+}
+
+test_page('?p=non-existent.git', 'non-existent project', 404);
+test_page('?p=.git;a=commit;h=non-existent', 'non-existent commit', 404);
+
+
+# Summary view
+
+get_summary or die 'summary page failed; no point in running the remaining tests';
+
+# Check short log. To do: Extract into separate test_short_log
+# function since the short log occurs on several pages.
+for my $revision (@revisions) {
+ for my $link_text qw( commit commitdiff tree snapshot ) {
+ test_link( { url_abs_regex => qr/h=$revision/, text => $link_text },
+ "$link_text link for $revision");
+ }
+}
+
+# Check that branches and tags are highlighted in green and yellow in
+# the shortlog. We assume here that we are on master, so it should be
+# at the top.
+$mech->content_like(qr{<span [^>]*class="head"[^>]*>master</span>},
+ 'master branch is highlighted in shortlog');
+$mech->content_like(qr{<span [^>]*class="tag"[^>]*>$tags[0]</span>},
+ "$tags[0] (most recent tag) is highlighted in shortlog");
+
+# Check heads. (This should be extracted as well.)
+for my $head (@heads) {
+ for my $link_text qw( shortlog log tree ) {
+ test_link( { url_abs_regex => qr{h=refs/heads/$head}, text => $link_text },
+ "$link_text link for head '$head'");
+ }
+}
+
+# Check tags (assume we only have tags referring to commits, not to
+# blobs or trees).
+for my $tag (@tags) {
+ my $commit = rev_parse("$tag^{commit}");
+ test_link( { url_abs_regex => qr{h=refs/tags/$tag}, text => 'shortlog' },
+ "shortlog link for tag '$tag'");
+ test_link( { url_abs_regex => qr{h=refs/tags/$tag}, text => 'log' },
+ "log link for tag '$tag'");
+ test_link( { url_abs_regex => qr{h=$commit}, text => 'commit' },
+ "commit link for tag '$tag'");
+ test_link( { url_abs_regex => qr{h=$commit}, text => $tag },
+ "'$tag' links to the commit as well");
+ # To do: Test tag link for tag objects.
+ # Why don't we have tree + snapshot links?
+}
+for my $tag (@tag_objects) {
+ my $tag_sha1 = rev_parse($tag);
+ test_link( { url_abs_regex => qr{h=$tag_sha1}, text => 'tag' },
+ "tag link for tag object '$tag'" );
+}
+
+
+# RSS/Atom/OPML view
+# Simply retrieve and verify well-formedness, but don't spider.
+$mech->get_ok('?p=.git;a=atom', 'Atom feed') and _verify_page;
+$mech->get_ok('?p=.git;a=rss', 'RSS feed') and _verify_page;
+TODO: {
+ # Now spider -- but there are broken links.
+ # http://mid.gmane.org/485EB333.5070108@gmail.com
+ local $TODO = "fix broken links in Atom/RSS feeds";
+ test_page('?p=.git;a=atom', 'Atom feed');
+ test_page('?p=.git;a=rss', 'RSS feed');
+}
+test_page('?a=opml', 'OPML outline');
+
+
+# Commit view
+if (test_page('?p=.git;a=commit;h=master', 'view HEAD commit')) {
+ my $tree_sha1 = rev_parse('master:');
+ test_link( { url_abs_regex => qr/a=tree/, text => rev_parse('master:') },
+ "SHA1 link to tree on commit page ($url)");
+ test_link( { url_abs_regex => qr/h=$tree_sha1/, text => 'tree' },
+ "'tree' link to tree on commit page ($url)");
+ $mech->content_like(qr/A U Thor/, "author mentioned on commit page ($url)");
+}
+
+
+# Commitdiff view
+if (get_summary &&
+ follow_link( { text_regex => qr/file added/i }, 'commit with added file') &&
+ follow_link( { text => 'commitdiff' }, 'commitdiff')) {
+ $mech->content_like(qr/new file with mode/, "commitdiff has diffstat ($url)");
+ $mech->content_like(qr/new file mode/, "commitdiff has diff ($url)");
+}
+test_page("?p=.git;a=commitdiff;h=$revisions[-1]",
+ 'commitdiff without parent');
+
+# Diff formattting problem.
+if (get_summary &&
+ follow_link( { text_regex => qr/renamed/ }, 'commit with rename') &&
+ follow_link( { text => 'commitdiff' }, 'commitdiff')) {
+ TODO: {
+ local $TODO = "bad a/* link in diff";
+ if (follow_link( { text_regex => qr!^a/! },
+ 'a/* link (probably wrong)')) {
+ # The page we land on here is broken already.
+ follow_link( { url_abs_regex => qr/a=blob_plain/ },
+ 'linked file name'); # bang
+ }
+ }
+}
+
+
+# Raw commitdiff (commitdiff_plain) view
+if (test_page('?p=.git;a=commit;h=refs/tags/tag-object',
+ 'commit view of tags/tag-object') &&
+ follow_link( { text => 'commitdiff' }, "'commitdiff'") &&
+ follow_link( { text => 'raw' }, "'raw' (commitdiff_plain)")) {
+ $mech->content_like(qr/^From: A U Thor <author\@example.com>$/m,
+ 'commitdiff_plain: From header');
+ TODO: {
+ local $TODO = 'date header mangles timezone';
+ $mech->content_like(qr/^Date: Thu, 7 Apr 2005 15:..:13 -0700$/m,
+ 'commitdiff_plain: Date header (correct)');
+ }
+ $mech->content_like(qr/^Date: Thu, 7 Apr 2005 22:..:13 \+0000 \(-0700\)$/m,
+ 'commitdiff_plain: Date header (UTC, wrong)');
+ $mech->content_like(qr/^Subject: .+$/m,
+ 'commitdiff_plain: Subject header');
+ # $ markers inexplicably don't work here if we use like(...)
+ # or $mech->content_like().
+ ok($mech->content =~ /^X-Git-Tag: tag-object\^0$/m,
+ 'commitdiff_plain: X-Git-Tag header');
+ ok($mech->content =~ /^X-Git-Url: $baseurl\?p=\.git;a=commitdiff_plain;h=refs%2Ftags%2Ftag-object$/m,
+ 'commitdiff_plain: X-Git-Url header');
+ ok($mech->content =~ /^---$/m, 'commitdiff_plain: separator$');
+ $mech->content_like(qr/^diff --git /m, 'commitdiff_plain: diff$');
+}
+
+
+# Tree view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files) {
+ my $sha1 = rev_parse("HEAD:$file");
+ test_link( { text => $file, url_abs_regex => qr/h=$sha1/ },
+ "'$file' is listed and linked");
+ test_link({ url_abs_regex => qr/f=$file/, text => $_ },
+ "'$_' link") foreach qw( blame blob history raw );
+ }
+ for my $directory (@directories) {
+ my $sha1 = rev_parse("HEAD:$directory");
+ test_link({ url_abs_regex => qr/f=$directory/, text => $_ },
+ "'$_' link") foreach qw( tree history );
+ if(follow_link( { text => $directory, url_abs_regex => qr/h=$sha1/ },
+ "'$directory is listed and linked" )) {
+ if(follow_link( { text => '..' }, 'parent directory')) {
+ test_link({ url_abs_regex => qr/h=$sha1/,
+ text => $directory },
+ 'back to original tree view');
+ $mech->back;
+ }
+ $mech->back;
+ }
+ }
+}
+
+
+# Blame view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $blame_link ($mech->find_all_links(text => 'blame')) {
+ my $url = $blame_link->url;
+ $mech->get_ok($url, "get $url -- blame link on tree view")
+ and _verify_page;
+ $mech->content_like(qr/A U Thor/,
+ "author mentioned on blame page");
+ TODO: {
+ # Now spider -- but there are broken links.
+ # http://mid.gmane.org/485EC621.7090101@gmail.com
+ local $TODO = "fix broken links in certain blame views";
+ check_page;
+ }
+ last unless $long_tests; # only test first blame link
+ }
+}
+
+
+# History view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files, @directories) {
+ my $type = get_type("HEAD:$file"); # blob or tree
+ if (follow_link( { text => 'history', url_abs_regex => qr/f=$file/ },
+ "history link for '$file'")) {
+ # There is at least one commit, so A U Thor is mentioned.
+ $mech->content_contains('A U Thor', 'A U Thor mentioned');
+ # The following tests test for at least *one*
+ # link of each type and are weak since we
+ # don't have any knowledge of commit hashes.
+ test_link( { text => $type, url_abs_regex => qr/f=$file/ },
+ "$type");
+ test_link( { text => 'commitdiff' },
+ "commitdiff");
+ test_link( { url_abs_regex => qr/a=commit;.*h=[a-f0-9]{40}/ },
+ "subject links to commit"); # weak, brittle
+ $mech->back;
+ }
+ }
+}
+
+
+# Blob view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files) {
+ if (follow_link( { text => $file, url_abs_regex => qr/a=blob/ },
+ "\"$file\" (blob) entry on tree view")) {
+ chomp(my $first_line_regex = (`cat "$file"`)[0]);
+ $first_line_regex =~ s/ / | /g;
+ # Hope that the first line doesn't contain any
+ # HTML-escapable character.
+ $mech->content_like(qr/$first_line_regex/,
+ "blob view contains first line of file ($url)");
+ $mech->back;
+ }
+ }
+}
+
+
+# Raw (blob_plain) view
+if (get_summary && follow_link( { text => 'tree' }, 'first tree link')) {
+ for my $file (@files) {
+ if (follow_link( { text => 'raw', url_abs_regex => qr/f=$file/ },
+ "raw (blob_plain) entry for \"$file\" in tree view")) {
+ chomp(my $first_line = (`cat "$file"`)[0]);
+ $mech->content_contains(
+ $first_line, "blob_plain view contains first line of file");
+ $mech->back;
+ }
+ }
+}
+
+
+# Error handling
+# Pass valid and invalid paths to various file-based actions
+for my $action qw( blame blob blob_plain blame ) {
+ test_page("?p=.git;a=$action;f=$files[0];hb=HEAD",
+ "$action: look up existent file");
+ test_page("?p=.git;a=$action;f=does_not_exist;hb=HEAD",
+ "$action: look up non-existent file", 404);
+ TODO: {
+ local $TODO = 'wrong error code (but using Git::Repo will fix this)';
+ test_page("?p=.git;a=$action;f=$directories[0];hb=HEAD",
+ "$action: look up directory", 400);
+ }
+}
+TODO: {
+ local $TODO = 'wrong error code (but using Git::Repo will fix this)';
+ test_page("?p=.git;a=tree;f=$files[0];hb=HEAD",
+ 'tree: look up existent file', 400);
+}
+# Pass valid and invalid paths to tree action
+test_page("?p=.git;a=tree;f=does_not_exist;hb=HEAD",
+ 'tree: look up non-existent file', 404);
+test_page("?p=.git;a=tree;f=$directories[0];hb=HEAD",
+ 'tree: look up directory');
+TODO: {
+ local $TODO = 'cannot use f=/ or f= for trees';
+ test_page("?p=.git;a=tree;f=/;hb=HEAD", 'tree: look up directory');
+}
+
+
+1;
+__END__
--
1.5.6.109.g33ded.dirty
^ permalink raw reply related
* Re: git-diff/git-format-patch safe for GNU (or POSIX) patch?
From: Petr Baudis @ 2008-06-26 13:44 UTC (permalink / raw)
To: Flavio Poletti; +Cc: git
In-Reply-To: <58284.213.203.159.164.1214480989.squirrel@upmail.polettix.it>
On Thu, Jun 26, 2008 at 01:49:49PM +0200, Flavio Poletti wrote:
> Thanks for the feedback! I wonder if this feature of patch "ignoring"
> stuff it doesn't understand is a GNU feature or a POSIX feature, but I'm
> abusing your patience here. (Incidentally, I also saw that POSIX has no
> "unified" format, but this "ignoring" feature might apply in a wider
> sense).
As you say, POSIX does not specify unified diffs at all. I'm not sure if
patch's policy is as relaxed for other formats and I didn't find
any mention of this relaxed policy in the SUS spec, but unified diffs
with extraneous information intertwined are extremely widespread; about
any VCSes out there append extra information to the diffs.
Incidentally, does anyone actually know about _any_ other patch tool in
the wild that is _not_ based on the original Larry Wall's patch?
--
Petr "Pasky" Baudis
The last good thing written in C++ was the Pachelbel Canon. -- J. Olson
^ permalink raw reply
* [PATCH v2] don't allow 'commit --amend' during rebase conflict resolution
From: Dmitry Potapov @ 2008-06-26 13:33 UTC (permalink / raw)
To: Theodore Tso; +Cc: git, Junio C Hamano
In-Reply-To: <20080626121303.GF8610@mit.edu>
Running 'commit --amend' during git rebase is almost certainly a mistake,
which causes that two consequent patches are squashed together. Moreover,
the commit message of the second commit is silently lost. It is almost
certainly not what the user expects. In that very unlikely case when you
really want to combine two patches during rebase conflict resolution,
you can do that using "git reset --soft HEAD^" followed by "git commit".
---
On Thu, Jun 26, 2008 at 08:13:03AM -0400, Theodore Tso wrote:
>
> In fact, we do this already for git-merge. Why not just do the same
> thing in the middle of a merge conflict with git-rebase?
Thank you for suggestion. I have corrected my patch to so the same as
we do in the case of git-merge conflict. MERGE_MSG is already removed
on successful commit, so the patch is very simple now.
builtin-commit.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/builtin-commit.c b/builtin-commit.c
index e3ad38b..6d1d955 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -725,6 +725,10 @@ static int parse_and_validate_options(int argc, const char *argv[],
die("You have nothing to amend.");
if (amend && in_merge)
die("You are in the middle of a merge -- cannot amend.");
+ /* no MERGE_HEAD but MERGE_MSG means a conflict during rebase */
+ if (amend && !access(git_path("MERGE_MSG"), F_OK))
+ die("You are in the middle of a rebase conflict -- "
+ "cannot amend.");
if (use_message)
f++;
--
1.5.6.60.gbc566
^ permalink raw reply related
* Re: [PATCH] git-send-pack: don't consider branch lagging behind as errors.
From: Pierre Habouzit @ 2008-06-26 13:28 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git
In-Reply-To: <7vfxr0sj61.fsf@gitster.siamese.dyndns.org>
[-- Attachment #1: Type: text/plain, Size: 2309 bytes --]
On Thu, Jun 26, 2008 at 08:19:02AM +0000, Junio C Hamano wrote:
> I however think what Pierre wanted to do is different, and you do not have
> to solve "force this strict rewind" problem to solve it.
>
> What we need is (with a configuration option) to change the meaning of
> "matching refs" from the current definition "branches with the same name
> between here and there" to "branches with the same name between here and
> there, but excluding the ones that do not fast forward (or strictly
> behind)". So if you have tons of stale branches you checked out but
> haven't touched most of them, your "matching push" won't even try to push
> what you haven't touched.
>
> I do not think we would ever want to allow a configuration that disables
> the exit status from push upon a push failure when somebody explicitly
> says "git push $there $this" and $this is not non-fast-forward. If you
> asked $this to be pushed, and if $this cannot be pushed, we _should_ error
> out.
>
> So...
>
> (1) when you say "git push" to trigger the "matching refs" push, with the
> new configuration, you do not error out nor even try to push out
> things that do not fast forward. Pretend that you did not even
> attempt to push them, and do not error out. This may result in not
> pushing anything out, but that is what the people who have such a
> configuration is asking for.
>
> If you have configured which branches are pushed when you are on your
> current branch, and that branch --- most likely it is that current
> branch --- does not fast forward, it is your problem.
>
> (2) even with such a configuration, you can "git push $there $this" and
> "git push $there +$this" to explicitly ask refs to be pushed. Such a
> push won't be interfered by the new configuration and correctly fail
> (or force).
Of course if I: git push $remote $branch, if it's stalled, I want it
to fail (exit with !0). *BUT* if I don't specify branches, I don't want
the stalled ones to generate noise. That's exactly the behaviour I'd
like to see.
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: git-diff/git-format-patch safe for GNU (or POSIX) patch?
From: Karl Hasselström @ 2008-06-26 12:49 UTC (permalink / raw)
To: Flavio Poletti; +Cc: git
In-Reply-To: <58284.213.203.159.164.1214480989.squirrel@upmail.polettix.it>
On 2008-06-26 13:49:49 +0200, Flavio Poletti wrote:
> Importing wasn't actually an issue, just a matter of
> init/add/commit. I was more scared about the patch production
> process, i.e. producing a patch that could be perfectly usable by
> the project's maintainers. But given what you kindly said, I think I
> can dismiss this as excess of caution by me.
Yes, patch is one of the most permissive programs around. It will eat
_anything_. (And throw up on the carpet if it was just too wrong.)
There are two things in git patches that patch won't grok: rename/copy
patches, and binary patches. Neither feature is on by default, IIRC --
you have to give command line flags -- precisely so that "git diff"
default output will be patch compatible.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
^ permalink raw reply
* Re: [PATCH] cmd_reset: don't trash uncommitted changes unless told to
From: Björn Steinbrink @ 2008-06-26 12:35 UTC (permalink / raw)
To: Johannes Schindelin
Cc: Junio C Hamano, Theodore Tso, Johannes Sixt, Boaz Harrosh,
Steven Walter, git, jeske
In-Reply-To: <alpine.DEB.1.00.0806261306060.9925@racer>
On 2008.06.26 13:07:40 +0100, Johannes Schindelin wrote:
> On Thu, 26 Jun 2008, Björn Steinbrink wrote:
>
> > On 2008.06.25 10:22:08 -0700, Junio C Hamano wrote:
> > > Theodore Tso <tytso@mit.edu> writes:
> > >
> > > > I used to argue for this, but gave up, because no one seemed to agree
> > > > with me. So now I just have the following in
> > > > /home/tytso/bin/git-revert-file and I am very happy:
> > > >
> > > > #!/bin/sh
> > > > #
> > > > prefix=$(git rev-parse --show-prefix)
> > > >
> > > > for i in $*
> > > > do
> > > > git show HEAD:$prefix$i > $i
> > > > done
> > >
> > > Isn't that this?
> > >
> > > #!/bin/sh
> > > exec git checkout HEAD -- "$@"
> >
> > I thought so at first, too, but there's one difference. Ted's version
> > doesn't affect the index, while yours does. Of course I cannot tell if
> > Ted actually intended not to touch the index ;-)
>
> While we are nit-picking: Ted's version does not respect autocrlf, while
> Junio's does.
>
> Oh, and Junio's version works with spaces and other funny stuff in file
> names, while Ted's does not.
>
> Oh, and error checking is correct in Junio's version.
>
> I am sure there are more differences.
I didn't intend to nit-pick, sorry if it looked like that. Not touching
the index might have been a conscious decision, but obviously I must
have missed some email that made it clear that it was intended to also
revert the index entry. Very sorry...
Thanks for the information on autocrlf though, didn't know that show
doesn't care about that.
Björn
^ permalink raw reply
* Re: [PATCH] cmd_reset: don't trash uncommitted changes unless told to
From: David Kastrup @ 2008-06-26 12:23 UTC (permalink / raw)
To: git
In-Reply-To: <alpine.DEB.1.00.0806261308420.9925@racer>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi,
>
> On Thu, 26 Jun 2008, Matthieu Moy wrote:
>
>> Theodore Tso <tytso@mit.edu> writes:
>>
>> > for i in $*
>>
>> Detail: you meant "$@", your version isn't whitespace-robust.
>
> In that case, you'd have to quote the variables in "$prefix$i" and "> $i",
> too.
Yes for the first, no for the second I think. But it would do no harm
in the second case, either. Certainly makes it less shell-dependent.
In fact, just tried it with bash and dash:
dak@lisa:/tmp$ zup="a b c"
dak@lisa:/tmp$ echo x > $zup
bash: $zup: ambiguous redirect
dak@lisa:/tmp$ echo x > "$zup"
dak@lisa:/tmp$ rm "$zup"
dak@lisa:/tmp$ dash
$ zup="a b c"
$ echo x > $zup
$ ls -l "$zup"
-rw-r--r-- 1 dak dak 2 2008-06-26 14:18 a b c
$
Dash gets it right. Bash just talks nonsense. "ambiguous redirect" is
not a useful message at all. There is nothing ambiguous here.
Anyway, bash is prevalent enough to warrant the quoting.
--
David Kastrup
^ permalink raw reply
* Re: how do I merge completely unrelated repositories ?
From: Miklos Vajna @ 2008-06-26 12:19 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Christian MICHON, Git Mailing List
In-Reply-To: <alpine.DEB.1.00.0806261224020.9925@racer>
[-- Attachment #1: Type: text/plain, Size: 456 bytes --]
On Thu, Jun 26, 2008 at 12:25:54PM +0100, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> FWIW this is how gitk got into git.git... See 5569bf9b(Do a cross-project
> merge of Paul Mackerras' gitk visualizer). This also was often referred
> to as the "coolest merge ever".
If we are at it, that's not unique, there are other root commits as
well: 2744b23 - git-tools, 161332a - gitweb, cb07fc2 - git-gui, 16d6b8a
- p4-fast-export. ;-)
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: git rebase interactive: usability issue
From: Theodore Tso @ 2008-06-26 12:13 UTC (permalink / raw)
To: Dmitry Potapov; +Cc: git
In-Reply-To: <20080625233208.GE5737@dpotapov.dyndns.org>
On Thu, Jun 26, 2008 at 03:32:08AM +0400, Dmitry Potapov wrote:
> Though the user realized his mistake after my explanation of how git
> rebase works, I still believe it is a serious usability issue, because
> the same command: "git commit --amend" produces drastically different
> results depends on whether the rebase process stopped due to conflict
> or on the "edit" mark. Moreover, the commit message of second patch is
> getting lost as result of using "git commit --amend" in the former case.
This is true whether it a conflict is getting addressed during a
git-rebase or a git-merge. The observation I would make is that git
has stopped a rebase or a merge with a conflict that the user needs to
fix up, a "git commit --amend" is almost always the wrong thing. So I
could imagine a safety where after git discovers a merge conflict, it
sets a flag (probably a file in the .git directory) which causes "git
commit --amend" stop withan error message "this probably wasn't what
you wanted", and telling the user to use a --force command if this is
what they wanted. This flag would be cleared on the next "git commit"
or "git reset".
In fact, we do this already for git-merge. Why not just do the same
thing in the middle of a merge conflict with git-rebase?
- Ted
^ permalink raw reply
* Re: [PATCH] cmd_reset: don't trash uncommitted changes unless told to
From: Johannes Schindelin @ 2008-06-26 12:09 UTC (permalink / raw)
To: Matthieu Moy; +Cc: Theodore Tso, git
In-Reply-To: <vpqy74scsln.fsf@bauges.imag.fr>
Hi,
On Thu, 26 Jun 2008, Matthieu Moy wrote:
> Theodore Tso <tytso@mit.edu> writes:
>
> > for i in $*
>
> Detail: you meant "$@", your version isn't whitespace-robust.
In that case, you'd have to quote the variables in "$prefix$i" and "> $i",
too.
Ciao,
Dscho
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox