Git development
 help / color / mirror / Atom feed
* Re: [PATCH, resent] fix openssl headers conflicting with custom SHA1 implementations
From: Shawn O. Pearce @ 2008-09-30 20:51 UTC (permalink / raw)
  To: Nicolas Pitre; +Cc: git
In-Reply-To: <alpine.LFD.2.00.0809301645340.3635@xanadu.home>

Nicolas Pitre <nico@cam.org> wrote:
> On Tue, 30 Sep 2008, Shawn O. Pearce wrote:
> > Nicolas Pitre <nico@cam.org> wrote:
> > > On ARM I have the following compilation errors:
> > ...
> > > This is a bit ugly but given the rat nest of system includes we have I 
> > > don't know how to solve this any better.
> > 
> > Hmm.  On Linux x86 with your change:
> > 
> > In file included from imap-send.c:32:
> > cache.h: In function 'create_ce_mode':
> > cache.h:186: error: 'S_IFLNK' undeclared (first use in this function)
> > 
> > I think that last hunk which moved the openssl includes to be before
> > cache.h is the problem.
> 
> With that, my build on ARM bombs out with:
> 
>     CC imap-send.o
> In file included from /usr/include/openssl/kssl.h:71,
>                  from /usr/include/openssl/ssl.h:191,
>                  from imap-send.c:30:
> /usr/include/ctype.h:102: error: expected expression before ']' token

*sigh*

Yea, its a bit ugly due to the rats nest of system includes.
Right now I don't see how we can include your patch, it breaks a
major platform for us.  But obviously my "fix" is also bogus and
won't get ARM working again.

Any other ideas we can try?  'cause I don't have any right now.  :-|

-- 
Shawn.

^ permalink raw reply

* Re: [PATCH 1/6] gitweb: action in path with use_pathinfo
From: Jakub Narebski @ 2008-09-30 21:00 UTC (permalink / raw)
  To: Giuseppe Bilotta; +Cc: git, Petr Baudis, Lea Wiemann
In-Reply-To: <cb7bb73a0809300553o7496f4c1me14ddf55b31fe4a6@mail.gmail.com>

On Tue, 30 September 2008, Giuseppe Bilotta wrote:
> On Tue, Sep 30, 2008 at 1:22 PM, Jakub Narebski <jnareb@gmail.com> wrote:
>> On Tue, 30 Sep 2008, Giuseppe "Oblomov" Bilotta wrote:
>>> On Tue, Sep 30, 2008 at 10:48 AM, Jakub Narebski <jnareb@gmail.com> wrote:

>>>> Or we could just scrap and revert adding href(..., -replay=>1).
>>>> There is much trouble with getting it right and performing well,
>>>> and it is less useful than I thought (at least now).
>>>
>>> Dunno, the idea in itself is not bad. We just have to get it right ;)
>>
>> It is not easy to get it right, especially that there are multivalued
>> parameters like @extra_options, see e.g. commit 7863c612
> 
> So we just let values be arrays. Or isn't this enough?

I don't think it would be that simple.


Let me elaborate a bit about complications and difficulties
within href() code.

First there are two names of parameters: the short name like 'a',
or 'h', or 'f' which is used in links to make them short (and
which is a bit historical legacy), and the long names like 'action',
'hash' or 'file_name' which are used for readability; then there are
also variables which hold values of parameters, like $action,
$hash and $file_name (which were source of long names for parameters).
href() has to provide translation between those two (well, three)
names; long names are used as names of "named parameters" to href(),
while short names are used when generating URL; %mapping provides
mapping between those two.

Second, href() must distinguish between gitweb options/parameters
like 'file_name'=>$file_name and extra options like '-full'=>1
or '-replay'=>1; additionally we want to have options output in
some definite order, with more important options first.  This
is provided by %mapping (to filter out) and @mapping (to sort).

Third, there are various sources of values of parameters, and
parameters used.  There are parameters specified explicitly in
href() call, and there are also implicit parameters: 'project'
parameter is implicitly added as it is needed in almost all
gitweb links (you can override it and generate projectless link by
using 'project'=>undef), and for '-replay'=>1 all parameters from
current URL are added.  Parameters specified explicitly have preference
over implicit or '-replay' ones.  

Now for '-replay' parameters might come from CGI query string, from
path_info part of URL, and perhaps in the future also from command
line.  To avoid duplicated code and other problems we should either
get parameters from variables (like in your code, but which doesn't
cover @extra_options well, but it could; or using "long name" to
variable ref mapping), or have some place where we save parameters
as we extract it from CGI query string, from path_info part of URL,
and in the future probably also from command line options/parameters.

Fourth, there is a little matter of _some_ parameters be multivalued;
currently it is only @extra_options / 'opt', but this may change in
the future, while _most_ are ordinary scalar values.  Printing arrayref
isn't something we want to do...


That is third and fourth which caused problems in the past with
href(..., -replay=>1)...

-- 
Jakub Narebski
Poland

^ permalink raw reply

* Re: [PATCH] parse-opt: migrate fmt-merge-msg.
From: Pierre Habouzit @ 2008-09-30 21:16 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git, gitster
In-Reply-To: <20080930191014.GH21310@spearce.org>

[-- Attachment #1: Type: text/plain, Size: 1829 bytes --]

On Tue, Sep 30, 2008 at 07:10:14PM +0000, Shawn O. Pearce wrote:
> Pierre Habouzit <madcoder@debian.org> wrote:
> > On Mon, Sep 29, 2008 at 04:35:23PM +0000, Shawn O. Pearce wrote:
> > > Pierre Habouzit <madcoder@debian.org> wrote:
> > > > Also fix an inefficient printf("%s", ...) where we can use write_in_full.
> > > 
> > > Near as I can tell, this is based upon a merge commit in next.
> > 
> > Hmm I've always sent my patches this way, and I believe you can git am
> > -3 them on top of master easily. I can send you the updated series if
> > you want.
> 
> I'd appreciate an updated series if you can send it.  am -3 isn't
> "easily" applying it.  Here I define "easy" as "the patch applies
> without me needing to resolve conflicts":
> 
>  $ git co -b ph/parseopt master
>  $ git am -3 -s X
>  Applying: parse-opt: migrate fmt-merge-msg.
>  error: patch failed: builtin-fmt-merge-msg.c:5
>  error: builtin-fmt-merge-msg.c: patch does not apply
>  Using index info to reconstruct a base tree...
>  Falling back to patching base and 3-way merge...
>  CONFLICT (content): Merge conflict in builtin-fmt-merge-msg.c
>  Recorded preimage for 'builtin-fmt-merge-msg.c'
>  Failed to merge in the changes.
>  Patch failed at 0001.
>  When you have resolved this problem run "git am -3 --resolved".
>  If you would prefer to skip this patch, instead run "git am -3 --skip".
>  To restore the original branch and stop patching run "git am -3 --abort".

Okay, I will then, but FWIW it means that when you'll try to merge this
in next it'll conflict at that time, so I'm not sure there's a huge win
for you at that point.

-- 
·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: [PATCH] doc: enhance git describe --tags help
From: Uwe Kleine-König @ 2008-09-30 22:14 UTC (permalink / raw)
  To: Shawn O. Pearce
  Cc: Pierre Habouzit, Erez Zilber, git@vger.kernel.org, open-iscsi,
	Junio C Hamano, Andreas Ericsson
In-Reply-To: <20080930190449.GG21310@spearce.org>

Hi Shawn,

On Tue, Sep 30, 2008 at 12:04:49PM -0700, Shawn O. Pearce wrote:
> Uwe Kleine-KKKnig <ukleinek@strlen.de> wrote:
> > On Mon, Sep 29, 2008 at 08:01:27AM -0700, Shawn O. Pearce wrote:
> > > --tags::
> > > 	If a lightweight tag exactly matches, output it.  If no
> > > 	annotated tag is found in the ancestry but a lightweight
> > > 	tag is found, output the lightweight tag.
> >
> > IMHO --tags should behave as Erez expected (because it's what I
> > expected, too).  As --tags currently behaves it's only usable in very
> > rare cases (most of the time it only makes a difference on repos without
> > any annotated tag).
> > 
> > When do you pass --tags?  Only if a lightweight tag is OK for an answer.
> > And then I would prefer a "near" lightweight tag to a "farer" annotated
> > one.
> 
> I don't disagree.  I've been tempted to write a patch to change the
> behavior of git-describe so that --tags and --all control what names
> are inserted into the candidate list, but don't control the ordering
> of their selection.
> 
> I think this is all that is needed to make the behavior do what you
> and Erez expected.  But its a pretty big change in the results if
> you are passing in --all or --tags today.
But it matches the documentation, and the expectations of Erez, me and
(at least initially) Pierre.

My POV is still:  If you pass --all or --tags you have to be able to
handle if a lw tag is used in the answer.

> -static int all;	/* Default to annotated tags only */
> -static int tags;	/* But allow any tags if --tags is specified */
> +static int all;	/* Any valid ref can be used */
> +static int tags;	/* Either lightweight or annotated tags */
Mmmh, IMHO the comment for tags is misleading, its either annotated only
or both.

Best regards and thanks,
Uwe

^ permalink raw reply

* Re: [PATCH] doc: enhance git describe --tags help
From: Shawn O. Pearce @ 2008-09-30 22:26 UTC (permalink / raw)
  To: Uwe Kleine-KKKnig
  Cc: Pierre Habouzit, Erez Zilber, git@vger.kernel.org, open-iscsi,
	Junio C Hamano, Andreas Ericsson
In-Reply-To: <20080930221453.GA13659@strlen.de>

Uwe Kleine-KKKnig <ukleinek@strlen.de> wrote:
> On Tue, Sep 30, 2008 at 12:04:49PM -0700, Shawn O. Pearce wrote:
> > Uwe Kleine-KKKnig <ukleinek@strlen.de> wrote:
> > >
> > > IMHO --tags should behave as Erez expected (because it's what I
> > > expected, too).
> > 
> > I don't disagree.  I've been tempted to write a patch to change the
> > behavior of git-describe so that --tags and --all control what names
> > are inserted into the candidate list, but don't control the ordering
> > of their selection.
> > 
> > I think this is all that is needed to make the behavior do what you
> > and Erez expected.  But its a pretty big change in the results if
> > you are passing in --all or --tags today.
>
> But it matches the documentation, and the expectations of Erez, me and
> (at least initially) Pierre.
> 
> My POV is still:  If you pass --all or --tags you have to be able to
> handle if a lw tag is used in the answer.

I was agreeing with you.  I've long felt that the --tags and --all
behavior of git-describe was wrong.  But something in the back of
my mind tells me Junio felt otherwise.

Its a change in behavior.  Today users are getting annotated tags
back from `git describe --tags` even if lightweight tags are closer.
Once this code change is in they'll start to get lightweight tags.

Previously `git describe --tags` never gave a lightweight tag if
there was at least one annotated tag in the history.  Now it will
start to give the lightweight tags.  Some users may see that as a
breakage.  Especially after the 1.6 "dashless" change...

> > -static int all;	/* Default to annotated tags only */
> > -static int tags;	/* But allow any tags if --tags is specified */
> > +static int all;	/* Any valid ref can be used */
> > +static int tags;	/* Either lightweight or annotated tags */
>
> Mmmh, IMHO the comment for tags is misleading, its either annotated only
> or both.

Oh, yes, right.  Thanks.  I'll clean it up.

-- 
Shawn.

^ permalink raw reply

* Re: [PATCH] parse-opt: migrate fmt-merge-msg.
From: Shawn O. Pearce @ 2008-09-30 22:46 UTC (permalink / raw)
  To: Pierre Habouzit; +Cc: git, gitster
In-Reply-To: <20080930211643.GA16879@artemis.corp>

Pierre Habouzit <madcoder@debian.org> wrote:
> On Tue, Sep 30, 2008 at 07:10:14PM +0000, Shawn O. Pearce wrote:
> > Pierre Habouzit <madcoder@debian.org> wrote:
> > > On Mon, Sep 29, 2008 at 04:35:23PM +0000, Shawn O. Pearce wrote:
> > > > Pierre Habouzit <madcoder@debian.org> wrote:
> > > > > Also fix an inefficient printf("%s", ...) where we can use write_in_full.
> > 
> > I'd appreciate an updated series if you can send it.  am -3 isn't
> > "easily" applying it.
> 
> Okay, I will then, but FWIW it means that when you'll try to merge this
> in next it'll conflict at that time, so I'm not sure there's a huge win
> for you at that point.

It may actually be a good idea to rebase this against master.

Reading Junio's notes for sg/merge-options (the branch this conflict
is coming out of) it sounds like we'd want to revert that anyway.
Its been around since April and Junio was talking about it needing
to be in a 1.7.0 release.  Its not going to graduate anytime soon.

IOW I'm quite tempted to revert sg/merge-options and cancel the
branch out of next.

-- 
Shawn.

^ permalink raw reply

* Re: [PATCH] git grep: Add "-Z/--null" option as in GNU's grep.
From: Shawn O. Pearce @ 2008-09-30 23:16 UTC (permalink / raw)
  To: Raphael Zimmerer; +Cc: git
In-Reply-To: <1222816390-9141-1-git-send-email-killekulla@rdrz.de>

Raphael Zimmerer <killekulla@rdrz.de> wrote:
> Here's a trivial patch that adds "-Z" and "--null" options to "git
> grep" equal to GNU's grep.
> So things like 'git grep -l -Z "$FOO" | xargs -0 sed -i "s/$FOO/$BOO/"'
> are more comfortable.

Elsewhere in Git we call this "-z", like "git ls-tree -z", "git
log -z".  Should we match grep or git convention here?
 
>  Documentation/git-grep.txt |    6 ++++++
>  builtin-grep.c             |    7 +++++++
>  grep.c                     |   14 +++++++++++---
>  grep.h                     |    1 +

-- 
Shawn.

^ permalink raw reply

* [PATCH] git grep: Add "-Z/--null" option as in GNU's grep.
From: Raphael Zimmerer @ 2008-09-30 23:13 UTC (permalink / raw)
  To: spearce; +Cc: git

Here's a trivial patch that adds "-Z" and "--null" options to "git
grep" equal to GNU's grep.
So things like 'git grep -l -Z "$FOO" | xargs -0 sed -i "s/$FOO/$BOO/"'
are more comfortable.

Signed-off-by: Raphael Zimmerer <killekulla@rdrz.de>
---
 Documentation/git-grep.txt |    6 ++++++
 builtin-grep.c             |    7 +++++++
 grep.c                     |   14 +++++++++++---
 grep.h                     |    1 +
 4 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index fa4d133..9317377 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -15,6 +15,7 @@ SYNOPSIS
 	   [-E | --extended-regexp] [-G | --basic-regexp]
 	   [-F | --fixed-strings] [-n]
 	   [-l | --files-with-matches] [-L | --files-without-match]
+	   [-Z | --null]
 	   [-c | --count] [--all-match]
 	   [-A <post-context>] [-B <pre-context>] [-C <context>]
 	   [-f <file>] [-e] <pattern>
@@ -94,6 +95,11 @@ OPTIONS
 	For better compatibility with 'git-diff', --name-only is a
 	synonym for --files-with-matches.
 
+-Z::
+--null::
+	Output \0 instead of the character that normally follows a
+	file name.
+
 -c::
 --count::
 	Instead of showing every matched line, show the number of
diff --git a/builtin-grep.c b/builtin-grep.c
index 3a51662..fb2abe4 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -295,6 +295,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 		push_arg("-l");
 	if (opt->unmatch_name_only)
 		push_arg("-L");
+	if (opt->null_following_name)
+		push_arg("-Z");
 	if (opt->count)
 		push_arg("-c");
 	if (opt->post_context || opt->pre_context) {
@@ -599,6 +601,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 			opt.unmatch_name_only = 1;
 			continue;
 		}
+		if (!strcmp("-Z", arg) ||
+		    !strcmp("--null", arg)) {
+			opt.null_following_name = 1;
+			continue;
+		}
 		if (!strcmp("-c", arg) ||
 		    !strcmp("--count", arg)) {
 			opt.count = 1;
diff --git a/grep.c b/grep.c
index 7063511..2619dbf 100644
--- a/grep.c
+++ b/grep.c
@@ -239,6 +239,8 @@ static int word_char(char ch)
 static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
 		      const char *name, unsigned lno, char sign)
 {
+	if (opt->null_following_name)
+		sign = 0;
 	if (opt->pathname)
 		printf("%s%c", name, sign);
 	if (opt->linenum)
@@ -246,6 +248,11 @@ static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
 	printf("%.*s\n", (int)(eol-bol), bol);
 }
 
+static void show_name(struct grep_opt *opt, const char *name)
+{
+	printf("%s%c", name, opt->null_following_name ? 0 : '\n');
+}
+
 static int fixmatch(const char *pattern, char *line, regmatch_t *match)
 {
 	char *hit = strstr(line, pattern);
@@ -489,7 +496,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 				return 1;
 			}
 			if (opt->name_only) {
-				printf("%s\n", name);
+				show_name(opt, name);
 				return 1;
 			}
 			/* Hit at this line.  If we haven't shown the
@@ -555,7 +562,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 		return 0;
 	if (opt->unmatch_name_only) {
 		/* We did not see any hit, so we want to show this */
-		printf("%s\n", name);
+		show_name(opt, name);
 		return 1;
 	}
 
@@ -565,7 +572,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 	 * make it another option?  For now suppress them.
 	 */
 	if (opt->count && count)
-		printf("%s:%u\n", name, count);
+		printf("%s%c%u\n", name,
+		       opt->null_following_name ? 0 : ':', count);
 	return !!last_hit;
 }
 
diff --git a/grep.h b/grep.h
index 59b3f87..45a222d 100644
--- a/grep.h
+++ b/grep.h
@@ -74,6 +74,7 @@ struct grep_opt {
 	unsigned extended:1;
 	unsigned relative:1;
 	unsigned pathname:1;
+	unsigned null_following_name:1;
 	int regflags;
 	unsigned pre_context;
 	unsigned post_context;
-- 
1.5.6.3

^ permalink raw reply related

* Re: [PATCH 1/6] gitweb: action in path with use_pathinfo
From: Jakub Narebski @ 2008-09-30 23:24 UTC (permalink / raw)
  To: Giuseppe Bilotta; +Cc: git, Petr Baudis, Lea Wiemann
In-Reply-To: <cb7bb73a0809300340t79a497fey4ededd960223fcdd@mail.gmail.com>

On Tue, 30 Sep 2008, Giuseppe Bilotta wrote:
> On Tue, Sep 30, 2008 at 10:48 AM, Jakub Narebski <jnareb@gmail.com> wrote:

>> I presume that you would want to replace for example $hash_base
>> everywhere by %input_params{'hash_base'}?
> 
> No. %input_params{'hash_base'} would only be the _input_ hash base.
> $hash_base would be kept if it's supposed to indicate the value of
> hash base that is being manipulated.

Ah, sorry, I misunderstood. Then your idea is the same as one of mine
(except perhaps some details).
 
>> I can think of yet another solution, namely to abstract getting
>> parameters from CGI query string, from path_info, and possibly in the
>> future also from command line options, and use this mechanism in
>> the getting parameters and validation part.
>>
>> The %params hash would be filled from CGI parameters by using simply
>> "%params = $cgi->Vars;", then added to in evaluate_path_info instead
>> of directly modifying global parameters variables.
> 
> So far I agree.

Using "%input_params = %cgi->Vars;" has consequence of using short
parameter names for keys (and also a bit strange syntax for multivalue
options, see CGI(3pm)).

>> The input validation
>> and dispatch part would be modified to use %params (taking care of
>> multivalued parameters as described in CGI(3pm)), like below:

This has the additional advantage of doing gitweb parameter validation
_once_, and not like it is now done for example first in the "input
validation and dispatch" section, and then in evaluate_path_info()
subroutine.

On the other hand $project is checked _already_ in evaluate_path_info(),
because it has to to find where project name ends, so this part would
get duplicated, unless something smart is done.

>>
>>  our $action = $params{'a'} || $params{'action'};
>
> Not too sure about that. The path_info (or whatever)-derived params
> should be converted to use the same name as the CGI params. Or
> conversely, CGI params should be mapped to the corresponding
> full-form.

After thinking about it a little, I agree with above paragraph.

>> That is just for consideration: each approach has its advantages and
>> disadvantages.  Your proposal, as I understand it, is similar to the
>> way described in "Storing options in a hash" subsection of
>> Getopt::Long(3pm) manpage.
> 
> I'll read that, although it probably is.

Perhaps gitweb should have implement something like GetOptions?

>> Or we could just scrap and revert adding href(..., -replay=>1).
>> There is much trouble with getting it right and performing well,
>> and it is less useful than I thought (at least now).
> 
> Dunno, the idea in itself is not bad. We just have to get it right ;)
> 
> In a way, I actually think that -replay=>1 should be the default, I
> suspect it makes sense in most cases.

PITA but useful. Hmmm....

-- 
Jakub Narebski
Poland

^ permalink raw reply

* Re: [PATCH] git grep: Add "-Z/--null" option as in GNU's grep.
From: Raphael Zimmerer @ 2008-09-30 23:41 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git
In-Reply-To: <20080930231619.GR21310@spearce.org>

On Tue, Sep 30, 2008 at 04:16:19PM -0700, Shawn O. Pearce wrote:
> Elsewhere in Git we call this "-z", like "git ls-tree -z", "git
> log -z".  Should we match grep or git convention here?

I'd tend to grep's convention, as most options of git-grep mimic those
of grep. grep uses "-z" for \0 on _input_, so that would be very
confusing for grep users...

- Raphael

^ permalink raw reply

* [PATCH v2] Teach git diff about Objective-C syntax
From: Jonathan del Strother @ 2008-09-30 23:46 UTC (permalink / raw)
  To: git
  Cc: Miklos Vajna, Johannes Schindelin, Junio C Hamano,
	Andreas Ericsson, Jonathan del Strother
In-Reply-To: <57518fd10809171630v97485aalcc5089f96082c0fc@mail.gmail.com>

Add support for recognition of Objective-C class & instance methods, C functions, and class implementation/interfaces.

Signed-off-by: Jonathan del Strother <jon.delStrother@bestbefore.tv>
---
This version is much the same, but rebuilt on top of 1883a0d3b to use the extended regexp stuff, and it doesn't attempt to tidy up other patterns.

I've been trying to make the objc-method matching line a bit more specific - I think I'm running into a bug (or more likely a misunderstanding) in the matching process.
Every pattern there uses either .*$ or [^;]*$ to match up to the end of a line.  But in trying to come up with a whitelist of characters to match up to the end of a line, I couldn't do it : there seems to be an invisible character at the end of the line that I can't match.
That is, a line containing just "FUNCNAME" (terminated by a newline) will be matched by the pattern "^(FUNCNAME.$)" but not "^(FUNCNAME$)".
Why is this?


 Documentation/gitattributes.txt |    2 ++
 diff.c                          |   10 ++++++++++
 2 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 2ae771f..2694559 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -315,6 +315,8 @@ patterns are available:
 
 - `java` suitable for source code in the Java language.
 
+- `objc` suitable for source code in the Objective-C language.
+
 - `pascal` suitable for source code in the Pascal/Delphi language.
 
 - `php` suitable for source code in the PHP language.
diff --git a/diff.c b/diff.c
index b001d7b..3694602 100644
--- a/diff.c
+++ b/diff.c
@@ -1429,6 +1429,16 @@ static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
 	  "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
 	  "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
 	  REG_EXTENDED },
+	{ "objc",
+	  /* Negate C statements that can look like functions */
+	  "!^[ \t]*(do|for|if|else|return|switch|while)\n"
+	  /* Objective-C methods */
+	  "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
+	  /* C functions */
+	  "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
+	  /* Objective-C class/protocol definitions */
+	  "^(@(implementation|interface|protocol)[ \t].*)$",
+	  REG_EXTENDED },
 	{ "pascal",
 	  "^((procedure|function|constructor|destructor|interface|"
 		"implementation|initialization|finalization)[ \t]*.*)$"
-- 
1.6.0.2.416.g8cab.dirty

^ permalink raw reply related

* Re: [PATCH 2/6] gitweb: use_pathinfo filenames start with /
From: Jakub Narebski @ 2008-09-30 23:49 UTC (permalink / raw)
  To: Giuseppe Bilotta; +Cc: git, Petr Baudis, Lea Wiemann
In-Reply-To: <cb7bb73a0809300048j7da35623m44ec9bfe7780fedb@mail.gmail.com>

On Tue, 30 Sep 2008, Giuseppe Bilotta wrote:
> On Tue, Sep 30, 2008 at 1:20 AM, Jakub Narebski <jnareb@gmail.com> wrote:
> >
> > Hn. Now I am not sure if it should be squashed, or should be separate.
> 
> Yeah. The fact that it's *specifically* to allow web docs to be used
> in raw view makes it count towarda a feature in itself, even if the
> patch by itself is trivial ...
> 
> So. Squashed or separate? :)

I'm slighty for separate, if only to avoid overly long commit message.
<commit-ish>:<filename> is understandable and expected because it is
what one uses for git-show; making <commit-ish>:/<filename> the default
because of relative links in 'blob_plain' view of HTML file requires
some further explanation.

But is is IMVHO finally your call here.
-- 
Jakub Narebski
Poland

^ permalink raw reply

* [EGIT PATCH 1/8] Set table row height for the glog JTable
From: Robin Rosenberg @ 2008-09-30 23:53 UTC (permalink / raw)
  To: spearce; +Cc: git, Robin Rosenberg

For some obscure reason JTable has a fixed default row size
of 16 pixels. This doesn't work well outside the default
look-and-feels shipped with the JRE, e.g. the GTK look and
feel for Linux.

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---
 .../org/spearce/jgit/awtui/CommitGraphPane.java    |   13 ++++++++++++-
 1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java b/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java
index 2be0e95..4ab2136 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java
@@ -52,6 +52,7 @@
 import javax.swing.table.AbstractTableModel;
 import javax.swing.table.DefaultTableCellRenderer;
 import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellRenderer;
 import javax.swing.table.TableColumn;
 import javax.swing.table.TableColumnModel;
 import javax.swing.table.TableModel;
@@ -83,8 +84,18 @@ public CommitGraphPane() {
 		allCommits = new SwingCommitList();
 		configureHeader();
 		setShowHorizontalLines(false);
-		setRowMargin(0);
 		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+		configureRowHeight();
+	}
+
+	private void configureRowHeight() {
+		int h = 0;
+		for (int i = 0; i<getColumnCount(); ++i) {
+			TableCellRenderer renderer = getDefaultRenderer(getColumnClass(i));
+			Component c = renderer.getTableCellRendererComponent(this, "Ã…Oj", false, false, 0, i);
+			h = Math.max(h, c.getPreferredSize().height);
+		}
+		setRowHeight(h + getRowMargin());
 	}
 
 	/**
-- 
1.6.0.1.310.gf789d0.dirty

^ permalink raw reply related

* [EGIT PATCH 3/8] Dispose of allocated colors on finalize()
From: Robin Rosenberg @ 2008-09-30 23:53 UTC (permalink / raw)
  To: spearce; +Cc: git, Robin Rosenberg
In-Reply-To: <1222818823-22780-2-git-send-email-robin.rosenberg@dewire.com>

---
 .../egit/ui/internal/history/SWTPlotRenderer.java  |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java
index 23ec255..a58b3bf 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java
@@ -45,6 +45,17 @@ SWTPlotRenderer(final Display d) {
 		sys_darkblue = d.getSystemColor(SWT.COLOR_DARK_BLUE);
 	}
 
+	@Override
+	protected void finalize() throws Throwable {
+		sys_black.dispose();
+		sys_blue.dispose();
+		sys_gray.dispose();
+		sys_darkblue.dispose();
+		sys_yellow.dispose();
+		sys_green.dispose();
+		sys_white.dispose();
+	}
+	
 	void paint(final Event event) {
 		g = event.gc;
 		cellX = event.x;
-- 
1.6.0.1.310.gf789d0.dirty

^ permalink raw reply related

* [EGIT PATCH 4/8] Align commit text properly in jgit glog
From: Robin Rosenberg @ 2008-09-30 23:53 UTC (permalink / raw)
  To: spearce; +Cc: git, Robin Rosenberg
In-Reply-To: <1222818823-22780-3-git-send-email-robin.rosenberg@dewire.com>

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---
 .../egit/ui/internal/history/SWTPlotRenderer.java  |    3 ---
 .../org/spearce/jgit/awtui/AWTPlotRenderer.java    |    7 +++----
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java
index a58b3bf..7c286a4 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/SWTPlotRenderer.java
@@ -51,9 +51,6 @@ protected void finalize() throws Throwable {
 		sys_blue.dispose();
 		sys_gray.dispose();
 		sys_darkblue.dispose();
-		sys_yellow.dispose();
-		sys_green.dispose();
-		sys_white.dispose();
 	}
 	
 	void paint(final Event event) {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java b/org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java
index a9933a4..5090ec9 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java
@@ -10,7 +10,6 @@
 
 import org.spearce.jgit.awtui.CommitGraphPane.GraphCellRender;
 import org.spearce.jgit.awtui.SwingCommitList.SwingLane;
-import org.spearce.jgit.lib.Ref;
 import org.spearce.jgit.revplot.AbstractPlotRenderer;
 import org.spearce.jgit.revplot.PlotCommit;
 
@@ -76,10 +75,10 @@ protected void drawBoundaryDot(final int x, final int y, final int w,
 
 	@Override
 	protected void drawText(final String msg, final int x, final int y) {
-		final int texty = g.getFontMetrics().getHeight()
-				- g.getFontMetrics().getDescent();
+		final int texth = g.getFontMetrics().getHeight();
+		final int y0 = y - texth/2 + (cell.getHeight() - texth)/2;
 		g.setColor(cell.getForeground());
-		g.drawString(msg, x, texty - (cell.getHeight() - y * 2));
+		g.drawString(msg, x, y0 + texth - g.getFontMetrics().getDescent());
 	}
 
 	@Override
-- 
1.6.0.1.310.gf789d0.dirty

^ permalink raw reply related

* [EGIT PATCH 2/8] Move AWTPlotRenderer to its own file.
From: Robin Rosenberg @ 2008-09-30 23:53 UTC (permalink / raw)
  To: spearce; +Cc: git, Robin Rosenberg
In-Reply-To: <1222818823-22780-1-git-send-email-robin.rosenberg@dewire.com>

This is mostly a convenience issue as it allows the
use of the JVM hotswap feature while debugging.

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---
 .../org/spearce/jgit/awtui/AWTPlotRenderer.java    |  104 ++++++++++++++++++++
 .../org/spearce/jgit/awtui/CommitGraphPane.java    |   92 -----------------
 2 files changed, 104 insertions(+), 92 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java b/org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java
new file mode 100644
index 0000000..a9933a4
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/awtui/AWTPlotRenderer.java
@@ -0,0 +1,104 @@
+/**
+ * 
+ */
+package org.spearce.jgit.awtui;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+
+import org.spearce.jgit.awtui.CommitGraphPane.GraphCellRender;
+import org.spearce.jgit.awtui.SwingCommitList.SwingLane;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.revplot.AbstractPlotRenderer;
+import org.spearce.jgit.revplot.PlotCommit;
+
+final class AWTPlotRenderer extends AbstractPlotRenderer<SwingLane, Color> {
+
+	final GraphCellRender cell;
+
+	Graphics2D g;
+
+	AWTPlotRenderer(final GraphCellRender c) {
+		cell = c;
+	}
+
+	void paint(final Graphics in, final PlotCommit<SwingLane> commit) {
+		g = (Graphics2D) in.create();
+		try {
+			final int h = cell.getHeight();
+			g.setColor(cell.getBackground());
+			g.fillRect(0, 0, cell.getWidth(), h);
+			if (commit != null)
+				paintCommit(commit, h);
+		} finally {
+			g.dispose();
+			g = null;
+		}
+	}
+
+	@Override
+	protected void drawLine(final Color color, int x1, int y1, int x2,
+			int y2, int width) {
+		if (y1 == y2) {
+			x1 -= width / 2;
+			x2 -= width / 2;
+		} else if (x1 == x2) {
+			y1 -= width / 2;
+			y2 -= width / 2;
+		}
+
+		g.setColor(color);
+		g.setStroke(CommitGraphPane.stroke(width));
+		g.drawLine(x1, y1, x2, y2);
+	}
+
+	@Override
+	protected void drawCommitDot(final int x, final int y, final int w,
+			final int h) {
+		g.setColor(Color.blue);
+		g.setStroke(CommitGraphPane.strokeCache[1]);
+		g.fillOval(x, y, w, h);
+		g.setColor(Color.black);
+		g.drawOval(x, y, w, h);
+	}
+
+	@Override
+	protected void drawBoundaryDot(final int x, final int y, final int w,
+			final int h) {
+		g.setColor(cell.getBackground());
+		g.setStroke(CommitGraphPane.strokeCache[1]);
+		g.fillOval(x, y, w, h);
+		g.setColor(Color.black);
+		g.drawOval(x, y, w, h);
+	}
+
+	@Override
+	protected void drawText(final String msg, final int x, final int y) {
+		final int texty = g.getFontMetrics().getHeight()
+				- g.getFontMetrics().getDescent();
+		g.setColor(cell.getForeground());
+		g.drawString(msg, x, texty - (cell.getHeight() - y * 2));
+	}
+
+	@Override
+	protected Color laneColor(final SwingLane myLane) {
+		return myLane != null ? myLane.color : Color.black;
+	}
+
+	void paintTriangleDown(final int cx, final int y, final int h) {
+		final int tipX = cx;
+		final int tipY = y + h;
+		final int baseX1 = cx - 10 / 2;
+		final int baseX2 = tipX + 10 / 2;
+		final int baseY = y;
+		final Polygon triangle = new Polygon();
+		triangle.addPoint(tipX, tipY);
+		triangle.addPoint(baseX1, baseY);
+		triangle.addPoint(baseX2, baseY);
+		g.fillPolygon(triangle);
+		g.drawPolygon(triangle);
+	}
+
+}
\ No newline at end of file
diff --git a/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java b/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java
index 4ab2136..d35bd5e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/awtui/CommitGraphPane.java
@@ -38,11 +38,8 @@
 package org.spearce.jgit.awtui;
 
 import java.awt.BasicStroke;
-import java.awt.Color;
 import java.awt.Component;
 import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Polygon;
 import java.awt.Stroke;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
@@ -59,7 +56,6 @@
 
 import org.spearce.jgit.awtui.SwingCommitList.SwingLane;
 import org.spearce.jgit.lib.PersonIdent;
-import org.spearce.jgit.revplot.AbstractPlotRenderer;
 import org.spearce.jgit.revplot.PlotCommit;
 import org.spearce.jgit.revplot.PlotCommitList;
 
@@ -251,92 +247,4 @@ static Stroke stroke(final int width) {
 		return new BasicStroke(width);
 	}
 
-	final class AWTPlotRenderer extends AbstractPlotRenderer<SwingLane, Color> {
-
-		final GraphCellRender cell;
-
-		Graphics2D g;
-
-		AWTPlotRenderer(final GraphCellRender c) {
-			cell = c;
-		}
-
-		void paint(final Graphics in, final PlotCommit<SwingLane> commit) {
-			g = (Graphics2D) in.create();
-			try {
-				final int h = cell.getHeight();
-				g.setColor(cell.getBackground());
-				g.fillRect(0, 0, cell.getWidth(), h);
-				if (commit != null)
-					paintCommit(commit, h);
-			} finally {
-				g.dispose();
-				g = null;
-			}
-		}
-
-		@Override
-		protected void drawLine(final Color color, int x1, int y1, int x2,
-				int y2, int width) {
-			if (y1 == y2) {
-				x1 -= width / 2;
-				x2 -= width / 2;
-			} else if (x1 == x2) {
-				y1 -= width / 2;
-				y2 -= width / 2;
-			}
-
-			g.setColor(color);
-			g.setStroke(stroke(width));
-			g.drawLine(x1, y1, x2, y2);
-		}
-
-		@Override
-		protected void drawCommitDot(final int x, final int y, final int w,
-				final int h) {
-			g.setColor(Color.blue);
-			g.setStroke(strokeCache[1]);
-			g.fillOval(x, y, w, h);
-			g.setColor(Color.black);
-			g.drawOval(x, y, w, h);
-		}
-
-		@Override
-		protected void drawBoundaryDot(final int x, final int y, final int w,
-				final int h) {
-			g.setColor(cell.getBackground());
-			g.setStroke(strokeCache[1]);
-			g.fillOval(x, y, w, h);
-			g.setColor(Color.black);
-			g.drawOval(x, y, w, h);
-		}
-
-		@Override
-		protected void drawText(final String msg, final int x, final int y) {
-			final int texty = g.getFontMetrics().getHeight()
-					- g.getFontMetrics().getDescent();
-			g.setColor(cell.getForeground());
-			g.drawString(msg, x, texty - (cell.getHeight() - y * 2));
-		}
-
-		@Override
-		protected Color laneColor(final SwingLane myLane) {
-			return myLane != null ? myLane.color : Color.black;
-		}
-
-		void paintTriangleDown(final int cx, final int y, final int h) {
-			final int tipX = cx;
-			final int tipY = y + h;
-			final int baseX1 = cx - 10 / 2;
-			final int baseX2 = tipX + 10 / 2;
-			final int baseY = y;
-			final Polygon triangle = new Polygon();
-			triangle.addPoint(tipX, tipY);
-			triangle.addPoint(baseX1, baseY);
-			triangle.addPoint(baseX2, baseY);
-			g.fillPolygon(triangle);
-			g.drawPolygon(triangle);
-		}
-	}
-
 }
-- 
1.6.0.1.310.gf789d0.dirty

^ permalink raw reply related

* Re: [EGIT PATCH 4/8] Align commit text properly in jgit glog
From: Robin Rosenberg @ 2008-10-01  0:02 UTC (permalink / raw)
  To: spearce; +Cc: git
In-Reply-To: <1222818823-22780-4-git-send-email-robin.rosenberg@dewire.com>


The Series actually ends here for now. The rest are not ready yet but can
be reviews if you are curious on my tagdecoration branch. These bug fixes
came out as a side effect.

-- robin

^ permalink raw reply

* Re: [PATCH v2] Teach git diff about Objective-C syntax
From: Brandon Casey @ 2008-10-01  0:52 UTC (permalink / raw)
  To: Jonathan del Strother
  Cc: git, Miklos Vajna, Johannes Schindelin, Junio C Hamano,
	Andreas Ericsson
In-Reply-To: <1222818394-11547-1-git-send-email-jon.delStrother@bestbefore.tv>

Jonathan del Strother wrote:
> Add support for recognition of Objective-C class & instance methods, C functions, and class implementation/interfaces.
> 
> Signed-off-by: Jonathan del Strother <jon.delStrother@bestbefore.tv>
> ---
> This version is much the same, but rebuilt on top of 1883a0d3b to use the extended regexp stuff, and it doesn't attempt to tidy up other patterns.
> 
> I've been trying to make the objc-method matching line a bit more specific - I think I'm running into a bug (or more likely a misunderstanding) in the matching process.
> Every pattern there uses either .*$ or [^;]*$ to match up to the end of a line.  But in trying to come up with a whitelist of characters to match up to the end of a line, I couldn't do it : there seems to be an invisible character at the end of the line that I can't match.
> That is, a line containing just "FUNCNAME" (terminated by a newline) will be matched by the pattern "^(FUNCNAME.$)" but not "^(FUNCNAME$)".
> Why is this?

I think it is the newline which git is not removing from the string passed to regexec.

See: http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_02

   "...In the regular expression processing described in IEEE Std 1003.1-2001,
    the <newline> is regarded as an ordinary character and both a period and a
    non-matching list can match one."

   "...Those utilities (like grep) that do not allow <newline>s to match are
    responsible for eliminating any <newline> from strings before matching against
    the RE."


Possibly something like:

diff --git a/xdiff-interface.c b/xdiff-interface.c
index 8bab82e..f471c7c 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -191,7 +191,7 @@ struct ff_regs {
 static long ff_regexp(const char *line, long len,
		char *buffer, long buffer_size, void *priv)
 {
-	char *line_buffer = xstrndup(line, len); /* make NUL terminated */
+	char *line_buffer = xstrndup(line, len-1); /* make NUL terminated */
 	struct ff_regs *regs = priv;
 	regmatch_t pmatch[2];
 	int i;

^ permalink raw reply related

* [JGIT PATCH 0/5] Support receive.fsckobjects
From: Shawn O. Pearce @ 2008-10-01  1:31 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

This series adds support for receive.fsckobjects, but on the fetch
side of the connection.  Perhaps it should be transfer.fsckobjects
or fetch.fsckobjects, but git.git doesn't support either of those
right now.

I mainly need this series because I'm fetching out of untrusted
bundles.  The content of the bundle has to pass git-fsck for it
to be considered safe.

The ObjectChecker class covers the same rules as git-fsck does, and
is perhaps even stricter on some of the things git-fsck lets slide.
I think git-fsck is too lenient in some areas, and I'd like to try
and improve the rules more in git.git, but I don't have time for
it right now.

Shawn O. Pearce (5):
  Expose RawParseUtils.match to application callers
  Fix UnpackedObjectLoader.getBytes to return a copy
  Object validation tests for "jgit fsck"
  Expose the critical receive configuration options to JGit
  Honor receive.fsckobjects during any fetch connection

 .../org/spearce/jgit/lib/ObjectCheckerTest.java    | 1294 ++++++++++++++++++++
 .../src/org/spearce/jgit/lib/ObjectChecker.java    |  352 ++++++
 .../src/org/spearce/jgit/lib/ObjectLoader.java     |    7 +-
 .../org/spearce/jgit/lib/PackedObjectLoader.java   |    7 -
 .../src/org/spearce/jgit/lib/RepositoryConfig.java |   10 +
 .../src/org/spearce/jgit/lib/TransferConfig.java   |   56 +
 .../org/spearce/jgit/lib/UnpackedObjectLoader.java |    4 -
 .../jgit/transport/BasePackFetchConnection.java    |    1 +
 .../src/org/spearce/jgit/transport/IndexPack.java  |   60 +-
 .../src/org/spearce/jgit/transport/Transport.java  |   24 +
 .../spearce/jgit/transport/TransportBundle.java    |   10 +-
 .../jgit/transport/WalkFetchConnection.java        |   26 +-
 .../src/org/spearce/jgit/util/RawParseUtils.java   |   23 +-
 13 files changed, 1842 insertions(+), 32 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java

^ permalink raw reply

* [JGIT PATCH 2/5] Fix UnpackedObjectLoader.getBytes to return a copy
From: Shawn O. Pearce @ 2008-10-01  1:31 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1222824690-7632-2-git-send-email-spearce@spearce.org>

The contract for ObjectLoader.getBytes() says the caller can modify
the returned array.  UnpackedObjectLoader must copy the data and not
return its internal cached byte array.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/lib/ObjectLoader.java     |    7 ++++++-
 .../org/spearce/jgit/lib/PackedObjectLoader.java   |    7 -------
 .../org/spearce/jgit/lib/UnpackedObjectLoader.java |    4 ----
 3 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
index 5282491..87e861f 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java
@@ -105,7 +105,12 @@ protected void setId(final ObjectId id) {
 	 * @throws IOException
 	 *             the object cannot be read.
 	 */
-	public abstract byte[] getBytes() throws IOException;
+	public final byte[] getBytes() throws IOException {
+		final byte[] data = getCachedBytes();
+		final byte[] copy = new byte[data.length];
+		System.arraycopy(data, 0, copy, 0, data.length);
+		return data;
+	}
 
 	/**
 	 * Obtain a reference to the (possibly cached) bytes of this object.
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java
index fa414d6..35983fe 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java
@@ -80,13 +80,6 @@ public long getDataOffset() {
 		return dataOffset;
 	}
 
-	public final byte[] getBytes() throws IOException {
-		final byte[] data = getCachedBytes();
-		final byte[] copy = new byte[data.length];
-		System.arraycopy(data, 0, copy, 0, data.length);
-		return data;
-	}
-
 	/**
 	 * Copy raw object representation from storage to provided output stream.
 	 * <p>
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java
index 3c61254..3ad273f 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java
@@ -200,10 +200,6 @@ public long getSize() {
 		return objectSize;
 	}
 
-	public byte[] getBytes() {
-		return bytes;
-	}
-
 	@Override
 	public byte[] getCachedBytes() throws IOException {
 		return bytes;
-- 
1.6.0.2.569.g798a2a

^ permalink raw reply related

* [JGIT PATCH 1/5] Expose RawParseUtils.match to application callers
From: Shawn O. Pearce @ 2008-10-01  1:31 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1222824690-7632-1-git-send-email-spearce@spearce.org>

This utility can be useful when parsing a buffer directly, such as
for a commit or tag object's header lines.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/util/RawParseUtils.java   |   13 ++++++++++++-
 1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
index dbc2e83..2ab3bfe 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
@@ -61,7 +61,18 @@
 			digits[i] = (byte) (i - '0');
 	}
 
-	private static final int match(final byte[] b, int ptr, final byte[] src) {
+	/**
+	 * Determine if b[ptr] matches src.
+	 * 
+	 * @param b
+	 *            the buffer to scan.
+	 * @param ptr
+	 *            first position within b, this should match src[0].
+	 * @param src
+	 *            the buffer to test for equality with b.
+	 * @return ptr += src.length if b[ptr..src.length] == src; else -1.
+	 */
+	public static final int match(final byte[] b, int ptr, final byte[] src) {
 		if (ptr + src.length >= b.length)
 			return -1;
 		for (int i = 0; i < src.length; i++, ptr++)
-- 
1.6.0.2.569.g798a2a

^ permalink raw reply related

* [JGIT PATCH 4/5] Expose the critical receive configuration options to JGit
From: Shawn O. Pearce @ 2008-10-01  1:31 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1222824690-7632-4-git-send-email-spearce@spearce.org>

The TransferConfig object keeps track of basic transfer related options
from the transfer, receive and fetch groups within a git config file.

Right now we only care about receive.fsckobjects.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/lib/RepositoryConfig.java |   10 ++++
 .../src/org/spearce/jgit/lib/TransferConfig.java   |   56 ++++++++++++++++++++
 2 files changed, 66 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
index d3c29ac..d8fd2fa 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
@@ -92,6 +92,8 @@ public static RepositoryConfig openUserConfig() {
 
 	private CoreConfig core;
 
+	private TransferConfig transfer;
+
 	private List<Entry> entries;
 
 	private Map<String, Object> byName;
@@ -126,6 +128,13 @@ public CoreConfig getCore() {
 	}
 
 	/**
+	 * @return transfer, fetch and receive configuration values
+	 */
+	public TransferConfig getTransfer() {
+		return transfer;
+	}
+
+	/**
 	 * Obtain an integer value from the configuration.
 	 *
 	 * @param section
@@ -667,6 +676,7 @@ public void load() throws IOException {
 		}
 
 		core = new CoreConfig(this);
+		transfer = new TransferConfig(this);
 	}
 
 	private void clear() {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java
new file mode 100644
index 0000000..ff3a5eb
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/TransferConfig.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+/**
+ * The standard "transfer", "fetch" and "receive" configuration parameters.
+ */
+public class TransferConfig {
+	private final boolean fsckObjects;
+
+	TransferConfig(final RepositoryConfig rc) {
+		fsckObjects = rc.getBoolean("receive", "fsckobjects", false);
+	}
+
+	/**
+	 * @return strictly verify received objects?
+	 */
+	public boolean isFsckObjects() {
+		return fsckObjects;
+	}
+}
-- 
1.6.0.2.569.g798a2a

^ permalink raw reply related

* [JGIT PATCH 5/5] Honor receive.fsckobjects during any fetch connection
From: Shawn O. Pearce @ 2008-10-01  1:31 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1222824690-7632-5-git-send-email-spearce@spearce.org>

If the configuration option receive.fsckobjects is true or if the
application requests it on the Transport each object received over
the wire is validated to pass "git fsck" style rules.  This can be
useful when fetching data from an untrusted source, to ensure that
the incoming objects comply with parsing standards.

The optional checking does require extra CPU on the client side. A
test against git.git (69601 objects 49719 deltas) showed:

    receive.fsckobjects       average time
    --------------------------------------
    false (default)           0m17.588s
    true                      0m18.465s

So the additional checking costs about 5% more in client side time.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../jgit/transport/BasePackFetchConnection.java    |    1 +
 .../src/org/spearce/jgit/transport/IndexPack.java  |   60 ++++++++++++++++++--
 .../src/org/spearce/jgit/transport/Transport.java  |   24 ++++++++
 .../spearce/jgit/transport/TransportBundle.java    |   10 +++-
 .../jgit/transport/WalkFetchConnection.java        |   26 +++++++--
 5 files changed, 108 insertions(+), 13 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
index a22b33d..a542eb7 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
@@ -452,6 +452,7 @@ private void receivePack(final ProgressMonitor monitor) throws IOException {
 
 		ip = IndexPack.create(local, sideband ? pckIn.sideband(monitor) : in);
 		ip.setFixThin(thinPack);
+		ip.setObjectChecking(transport.isCheckFetchedObjects());
 		ip.index(monitor);
 		ip.renameAndOpenPack();
 	}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java
index 8a66ec9..ef1ee52 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/IndexPack.java
@@ -56,10 +56,12 @@
 
 import org.spearce.jgit.errors.CorruptObjectException;
 import org.spearce.jgit.errors.MissingObjectException;
+import org.spearce.jgit.lib.AnyObjectId;
 import org.spearce.jgit.lib.BinaryDelta;
 import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.InflaterCache;
 import org.spearce.jgit.lib.MutableObjectId;
+import org.spearce.jgit.lib.ObjectChecker;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.ObjectIdMap;
 import org.spearce.jgit.lib.ObjectLoader;
@@ -136,6 +138,8 @@ public static IndexPack create(final Repository db, final InputStream is)
 
 	private int bAvail;
 
+	private ObjectChecker objCheck;
+
 	private boolean fixThin;
 
 	private int outputVersion;
@@ -231,6 +235,40 @@ public void setFixThin(final boolean fix) {
 	}
 
 	/**
+	 * Configure the checker used to validate received objects.
+	 * <p>
+	 * Usually object checking isn't necessary, as Git implementations only
+	 * create valid objects in pack files. However, additional checking may be
+	 * useful if processing data from an untrusted source.
+	 * 
+	 * @param oc
+	 *            the checker instance; null to disable object checking.
+	 */
+	public void setObjectChecker(final ObjectChecker oc) {
+		objCheck = oc;
+	}
+
+	/**
+	 * Configure the checker used to validate received objects.
+	 * <p>
+	 * Usually object checking isn't necessary, as Git implementations only
+	 * create valid objects in pack files. However, additional checking may be
+	 * useful if processing data from an untrusted source.
+	 * <p>
+	 * This is shorthand for:
+	 * 
+	 * <pre>
+	 * setObjectChecker(on ? new ObjectChecker() : null);
+	 * </pre>
+	 * 
+	 * @param on
+	 *            true to enable the default checker; false to disable it.
+	 */
+	public void setObjectChecking(final boolean on) {
+		setObjectChecker(on ? new ObjectChecker() : null);
+	}
+
+	/**
 	 * Consume data from the input stream until the packfile is indexed.
 	 * 
 	 * @param progress
@@ -375,7 +413,7 @@ private void resolveDeltas(final long pos, final int oldCRC, int type,
 			objectDigest.update(data);
 			tempObjectId.fromRaw(objectDigest.digest(), 0);
 
-			verifyNoCollision(type, data);
+			verifySafeObject(tempObjectId, type, data);
 			oe = new PackedObjectInfo(pos, crc32, tempObjectId);
 			entries[entryCount++] = oe;
 		}
@@ -658,18 +696,28 @@ private void whole(final int type, final long pos, final long sz)
 		objectDigest.update(data);
 		tempObjectId.fromRaw(objectDigest.digest(), 0);
 
-		verifyNoCollision(type, data);
+		verifySafeObject(tempObjectId, type, data);
 		final int crc32 = (int) crc.getValue();
 		entries[entryCount++] = new PackedObjectInfo(pos, crc32, tempObjectId);
 	}
 
-	private void verifyNoCollision(final int type, final byte[] data)
-			throws IOException {
-		final ObjectLoader ldr = repo.openObject(tempObjectId);
+	private void verifySafeObject(final AnyObjectId id, final int type,
+			final byte[] data) throws IOException {
+		if (objCheck != null) {
+			try {
+				objCheck.check(type, data);
+			} catch (CorruptObjectException e) {
+				throw new IOException("Invalid "
+						+ Constants.encodedTypeString(type) + " " + id.name()
+						+ ":" + e.getMessage());
+			}
+		}
+
+		final ObjectLoader ldr = repo.openObject(id);
 		if (ldr != null) {
 			final byte[] existingData = ldr.getCachedBytes();
 			if (ldr.getType() != type || !Arrays.equals(data, existingData)) {
-				throw new IOException("collision in " + tempObjectId.name());
+				throw new IOException("Collision on " + id.name());
 			}
 		}
 	}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
index 7284b28..28700b7 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
@@ -55,6 +55,7 @@
 import org.spearce.jgit.lib.ProgressMonitor;
 import org.spearce.jgit.lib.Ref;
 import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.TransferConfig;
 
 /**
  * Connects two Git repositories together and copies objects between them.
@@ -351,6 +352,9 @@ private static String findTrackingRefName(final String remoteName,
 	/** Should push just check for operation result, not really push. */
 	private boolean dryRun;
 
+	/** Should an incoming (fetch) transfer validate objects? */
+	private boolean checkFetchedObjects;
+
 	/**
 	 * Create a new transport instance.
 	 * 
@@ -363,8 +367,10 @@ private static String findTrackingRefName(final String remoteName,
 	 *            URI passed to {@link #open(Repository, URIish)}.
 	 */
 	protected Transport(final Repository local, final URIish uri) {
+		final TransferConfig tc = local.getConfig().getTransfer();
 		this.local = local;
 		this.uri = uri;
+		this.checkFetchedObjects = tc.isFsckObjects();
 	}
 
 	/**
@@ -444,6 +450,24 @@ public void setFetchThin(final boolean fetchThin) {
 	}
 
 	/**
+	 * @return true if fetch will verify received objects are formatted
+	 *         correctly. Validating objects requires more CPU time on the
+	 *         client side of the connection.
+	 */
+	public boolean isCheckFetchedObjects() {
+		return checkFetchedObjects;
+	}
+
+	/**
+	 * @param check
+	 *            true to enable checking received objects; false to assume all
+	 *            received objects are valid.
+	 */
+	public void setCheckFetchedObjects(final boolean check) {
+		checkFetchedObjects = check;
+	}
+
+	/**
 	 * Default setting is: {@value RemoteConfig#DEFAULT_RECEIVE_PACK}
 	 *
 	 * @return remote executable providing receive-pack service for pack
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
index d43a18f..5b321a0 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java
@@ -170,8 +170,7 @@ protected void doFetch(final ProgressMonitor monitor,
 				final Collection<Ref> want) throws TransportException {
 			verifyPrerequisites();
 			try {
-				final IndexPack ip = IndexPack.create(local, bin);
-				ip.setFixThin(true);
+				final IndexPack ip = newIndexPack();
 				ip.index(monitor);
 				ip.renameAndOpenPack();
 			} catch (IOException err) {
@@ -183,6 +182,13 @@ protected void doFetch(final ProgressMonitor monitor,
 			}
 		}
 
+		private IndexPack newIndexPack() throws IOException {
+			final IndexPack ip = IndexPack.create(local, bin);
+			ip.setFixThin(true);
+			ip.setObjectChecking(TransportBundle.this.isCheckFetchedObjects());
+			return ip;
+		}
+
 		private void verifyPrerequisites() throws TransportException {
 			if (prereqs.isEmpty())
 				return;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
index f309fe1..5638454 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
@@ -59,6 +59,7 @@
 import org.spearce.jgit.lib.AnyObjectId;
 import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.FileMode;
+import org.spearce.jgit.lib.ObjectChecker;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.PackIndex;
 import org.spearce.jgit.lib.ProgressMonitor;
@@ -99,6 +100,9 @@
 	/** The repository this transport fetches into, or pushes out of. */
 	private final Repository local;
 
+	/** If not null the validator for received objects. */
+	private final ObjectChecker objCheck;
+
 	/**
 	 * List of all remote repositories we may need to get objects out of.
 	 * <p>
@@ -157,9 +161,9 @@
 	 */
 	private final HashMap<ObjectId, List<Throwable>> fetchErrors;
 
-	WalkFetchConnection(final WalkTransport walkTransport,
-			final WalkRemoteObjectDatabase w) {
-		local = walkTransport.local;
+	WalkFetchConnection(final WalkTransport wt, final WalkRemoteObjectDatabase w) {
+		local = wt.local;
+		objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
 
 		remotes = new ArrayList<WalkRemoteObjectDatabase>();
 		remotes.add(w);
@@ -560,10 +564,21 @@ private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
 			throw e;
 		}
 
-		if (!AnyObjectId.equals(id, uol.getId()))
+		if (!AnyObjectId.equals(id, uol.getId())) {
 			throw new TransportException("Incorrect hash for " + id.name()
 					+ "; computed " + uol.getId().name() + " as a "
-					+ uol.getType() + " from " + compressed.length + " bytes.");
+					+ Constants.encodedTypeString(uol.getType()) + " from "
+					+ compressed.length + " bytes.");
+		}
+		if (objCheck != null) {
+			try {
+				objCheck.check(uol.getType(), uol.getCachedBytes());
+			} catch (CorruptObjectException e) {
+				throw new TransportException("Invalid "
+						+ Constants.encodedTypeString(uol.getType()) + " "
+						+ id.name() + ":" + e.getMessage());
+			}
+		}
 	}
 
 	private void saveLooseObject(final AnyObjectId id, final byte[] compressed)
@@ -811,6 +826,7 @@ void downloadPack(final ProgressMonitor monitor) throws IOException {
 			s = connection.open("pack/" + packName);
 			ip = IndexPack.create(local, s.in);
 			ip.setFixThin(false);
+			ip.setObjectChecker(objCheck);
 			ip.index(monitor);
 			ip.renameAndOpenPack();
 		}
-- 
1.6.0.2.569.g798a2a

^ permalink raw reply related

* [JGIT PATCH 3/5] Object validation tests for "jgit fsck"
From: Shawn O. Pearce @ 2008-10-01  1:31 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1222824690-7632-3-git-send-email-spearce@spearce.org>

The ObjectChecker provides strict validation rules for objects.  If
any commit, tag or tree object is malformed in a way that might cause
a Git implementation to misinterpret the data CorruptObjectException
is thrown back to the caller for error handling.

Due to the shear size of the validation code this change provides
only the validation code and its unit tests.  It is at least as
paranoid as git.git's "git-fsck" is on the same object types, but
is actually a bit stricter about the commit and tag objects having
the well known header fields populated correctly.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../org/spearce/jgit/lib/ObjectCheckerTest.java    | 1294 ++++++++++++++++++++
 .../src/org/spearce/jgit/lib/ObjectChecker.java    |  352 ++++++
 .../src/org/spearce/jgit/util/RawParseUtils.java   |   10 +-
 3 files changed, 1650 insertions(+), 6 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
new file mode 100644
index 0000000..fa37fb5
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ObjectCheckerTest.java
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+import junit.framework.TestCase;
+
+import org.spearce.jgit.errors.CorruptObjectException;
+
+public class ObjectCheckerTest extends TestCase {
+	private ObjectChecker checker;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		checker = new ObjectChecker();
+	}
+
+	public void testInvalidType() {
+		try {
+			checker.check(Constants.OBJ_BAD, new byte[0]);
+			fail("Did not throw CorruptObjectException");
+		} catch (CorruptObjectException e) {
+			final String m = e.getMessage();
+			assertEquals("Invalid object type: " + Constants.OBJ_BAD, m);
+		}
+	}
+
+	public void testCheckBlob() throws CorruptObjectException {
+		// Any blob should pass...
+		checker.checkBlob(new byte[0]);
+		checker.checkBlob(new byte[1]);
+
+		checker.check(Constants.OBJ_BLOB, new byte[0]);
+		checker.check(Constants.OBJ_BLOB, new byte[1]);
+	}
+
+	public void testValidCommitNoParent() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkCommit(data);
+		checker.check(Constants.OBJ_COMMIT, data);
+	}
+
+	public void testValidCommitBlankAuthor() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author <> 0 +0000\n");
+		b.append("committer <> 0 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkCommit(data);
+		checker.check(Constants.OBJ_COMMIT, data);
+	}
+
+	public void testValidCommit1Parent() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkCommit(data);
+		checker.check(Constants.OBJ_COMMIT, data);
+	}
+
+	public void testValidCommit2Parent() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkCommit(data);
+		checker.check(Constants.OBJ_COMMIT, data);
+	}
+
+	public void testValidCommit128Parent() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		for (int i = 0; i < 128; i++) {
+			b.append("parent ");
+			b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+			b.append('\n');
+		}
+
+		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkCommit(data);
+		checker.check(Constants.OBJ_COMMIT, data);
+	}
+
+	public void testValidCommitNormalTime() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		final String when = "1222757360 -0730";
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor <author@localhost> " + when + "\n");
+		b.append("committer A. U. Thor <author@localhost> " + when + "\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkCommit(data);
+		checker.check(Constants.OBJ_COMMIT, data);
+	}
+
+	public void testInvalidCommitNoTree1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("parent ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tree header", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitNoTree2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("trie ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tree header", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitNoTree3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tree header", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitNoTree4() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree\t");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tree header", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidTree1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid tree", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidTree2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append("z\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid tree", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidTree3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9b");
+		b.append("\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid tree", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidTree4() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree  ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid tree", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidParent1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent ");
+		b.append("\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid parent", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidParent2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent ");
+		b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append("\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid parent", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidParent3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent  ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append("\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid parent", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidParent4() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent  ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append("z\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid parent", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidParent5() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("parent\t");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append("\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("no author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitNoAuthor() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("no author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitNoCommitter1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("no committer", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitNoCommitter2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
+		b.append("\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("no committer", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidAuthor1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor <foo 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidAuthor2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author A. U. Thor foo> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidAuthor3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidAuthor4() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author a <b> +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidAuthor5() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author a <b>\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidAuthor6() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author a <b> z");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidAuthor7() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author a <b> 1 z");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid author", e.getMessage());
+		}
+	}
+
+	public void testInvalidCommitInvalidCommitter() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("tree ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("author a <b> 1 +0000\n");
+		b.append("committer a <");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkCommit(data);
+			fail("Did not catch corrupt object");
+		} catch (CorruptObjectException e) {
+			// Yes, really, we complain about author not being
+			// found as the invalid parent line wasn't consumed.
+			assertEquals("invalid committer", e.getMessage());
+		}
+	}
+
+	public void testValidTag() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+		b.append("tag test-tag\n");
+		b.append("tagger A. U. Thor <author@localhost> 1 +0000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTag(data);
+		checker.check(Constants.OBJ_TAG, data);
+	}
+
+	public void testInvalidTagNoObject1() {
+		final StringBuilder b = new StringBuilder();
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no object header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoObject2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object\t");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no object header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoObject3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("obejct ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no object header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoObject4() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("zz9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid object", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoObject5() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append(" \n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid object", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoObject6() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid object", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoType1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no type header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoType2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type\tcommit\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no type header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoType3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("tpye commit\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no type header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoType4() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tag header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoTagHeader1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tag header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoTagHeader2() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+		b.append("tag\tfoo\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tag header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoTagHeader3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+		b.append("tga foo\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tag header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoTagHeader4() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+		b.append("tag foo");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tagger header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagNoTaggerHeader1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+		b.append("tag foo\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("no tagger header", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagInvalidTaggerHeader1() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+		b.append("tag foo\n");
+		b.append("tagger \n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid tagger", e.getMessage());
+		}
+	}
+
+	public void testInvalidTagInvalidTaggerHeader3() {
+		final StringBuilder b = new StringBuilder();
+
+		b.append("object ");
+		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
+		b.append('\n');
+
+		b.append("type commit\n");
+		b.append("tag foo\n");
+		b.append("tagger a < 1 +000\n");
+
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTag(data);
+			fail("incorrectly accepted invalid tag");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid tagger", e.getMessage());
+		}
+	}
+
+	public void testValidEmptyTree() throws CorruptObjectException {
+		checker.checkTree(new byte[0]);
+		checker.check(Constants.OBJ_TREE, new byte[0]);
+	}
+
+	public void testValidTree1() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 regular-file");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTree2() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100755 executable");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTree3() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "40000 tree");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTree4() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "120000 symlink");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTree5() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "160000 git link");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting1() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 fooaaa");
+		entry(b, "100755 foobar");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting2() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100755 fooaaa");
+		entry(b, "100644 foobar");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting3() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "40000 a");
+		entry(b, "100644 b");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting4() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a");
+		entry(b, "40000 b");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting5() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a.c");
+		entry(b, "40000 a");
+		entry(b, "100644 a0c");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting6() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "40000 a");
+		entry(b, "100644 apple");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting7() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "40000 an orang");
+		entry(b, "40000 an orange");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testValidTreeSorting8() throws CorruptObjectException {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a");
+		entry(b, "100644 a0c");
+		entry(b, "100644 b");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		checker.checkTree(data);
+	}
+
+	public void testInvalidTreeModeStartsWithZero1() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "0 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("mode starts with '0'", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeModeStartsWithZero2() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "0100644 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("mode starts with '0'", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeModeStartsWithZero3() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "040000 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("mode starts with '0'", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeModeNotOctal1() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "8 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid mode character", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeModeNotOctal2() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "Z a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid mode character", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeModeNotSupportedMode1() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "1 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid mode 1", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeModeNotSupportedMode2() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "170000 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("invalid mode " + 0170000, e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeModeMissingName() {
+		final StringBuilder b = new StringBuilder();
+		b.append("100644");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("truncated in mode", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeNameContainsSlash() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a/b");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("name contains '/'", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeNameIsEmpty() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 ");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("zero length name", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeTruncatedInName() {
+		final StringBuilder b = new StringBuilder();
+		b.append("100644 b");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("truncated in name", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeTruncatedInObjectId() {
+		final StringBuilder b = new StringBuilder();
+		b.append("100644 b\0\1\2");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("truncated in object id", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeBadSorting1() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 foobar");
+		entry(b, "100644 fooaaa");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("incorrectly sorted", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeBadSorting2() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "40000 a");
+		entry(b, "100644 a.c");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("incorrectly sorted", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeBadSorting3() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a0c");
+		entry(b, "40000 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("incorrectly sorted", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeDuplicateNames1() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a");
+		entry(b, "100644 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("duplicate entry names", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeDuplicateNames2() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a");
+		entry(b, "100755 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("duplicate entry names", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeDuplicateNames3() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a");
+		entry(b, "40000 a");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("duplicate entry names", e.getMessage());
+		}
+	}
+
+	public void testInvalidTreeDuplicateNames4() {
+		final StringBuilder b = new StringBuilder();
+		entry(b, "100644 a");
+		entry(b, "100644 a.c");
+		entry(b, "100644 a.d");
+		entry(b, "100644 a.e");
+		entry(b, "40000 a");
+		entry(b, "100644 zoo");
+		final byte[] data = Constants.encodeASCII(b.toString());
+		try {
+			checker.checkTree(data);
+			fail("incorrectly accepted an invalid tree");
+		} catch (CorruptObjectException e) {
+			assertEquals("duplicate entry names", e.getMessage());
+		}
+	}
+
+	private static void entry(final StringBuilder b, final String modeName) {
+		b.append(modeName);
+		b.append('\0');
+		for (int i = 0; i < Constants.OBJECT_ID_LENGTH; i++)
+			b.append((char) i);
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java
new file mode 100644
index 0000000..0d165af
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectChecker.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+import static org.spearce.jgit.util.RawParseUtils.match;
+import static org.spearce.jgit.util.RawParseUtils.nextLF;
+import static org.spearce.jgit.util.RawParseUtils.parseBase10;
+
+import org.spearce.jgit.errors.CorruptObjectException;
+import org.spearce.jgit.util.MutableInteger;
+
+/**
+ * Verifies that an object is formatted correctly.
+ * <p>
+ * Verifications made by this class only check that the fields of an object are
+ * formatted correctly. The ObjectId checksum of the object is not verified, and
+ * connectivity links between objects are also not verified. Its assumed that
+ * the caller can provide both of these validations on its own.
+ * <p>
+ * Instances of this class are not thread safe, but they may be reused to
+ * perform multiple object validations.
+ */
+public class ObjectChecker {
+	/** Header "tree " */
+	public static final byte[] tree = Constants.encodeASCII("tree ");
+
+	/** Header "parent " */
+	public static final byte[] parent = Constants.encodeASCII("parent ");
+
+	/** Header "author " */
+	public static final byte[] author = Constants.encodeASCII("author ");
+
+	/** Header "committer " */
+	public static final byte[] committer = Constants.encodeASCII("committer ");
+
+	/** Header "encoding " */
+	public static final byte[] encoding = Constants.encodeASCII("encoding ");
+
+	/** Header "object " */
+	public static final byte[] object = Constants.encodeASCII("object ");
+
+	/** Header "type " */
+	public static final byte[] type = Constants.encodeASCII("type ");
+
+	/** Header "tag " */
+	public static final byte[] tag = Constants.encodeASCII("tag ");
+
+	/** Header "tagger " */
+	public static final byte[] tagger = Constants.encodeASCII("tagger ");
+
+	private final MutableObjectId tempId = new MutableObjectId();
+
+	private final MutableInteger ptrout = new MutableInteger();
+
+	/**
+	 * Check an object for parsing errors.
+	 * 
+	 * @param objType
+	 *            type of the object. Must be a valid object type code in
+	 *            {@link Constants}.
+	 * @param raw
+	 *            the raw data which comprises the object. This should be in the
+	 *            canonical format (that is the format used to generate the
+	 *            ObjectId of the object). The array is never modified.
+	 * @throws CorruptObjectException
+	 *             if an error is identified.
+	 */
+	public void check(final int objType, final byte[] raw)
+			throws CorruptObjectException {
+		switch (objType) {
+		case Constants.OBJ_COMMIT:
+			checkCommit(raw);
+			break;
+		case Constants.OBJ_TAG:
+			checkTag(raw);
+			break;
+		case Constants.OBJ_TREE:
+			checkTree(raw);
+			break;
+		case Constants.OBJ_BLOB:
+			checkBlob(raw);
+			break;
+		default:
+			throw new CorruptObjectException("Invalid object type: " + objType);
+		}
+	}
+
+	private int id(final byte[] raw, final int ptr) {
+		try {
+			tempId.fromString(raw, ptr);
+			return ptr + AnyObjectId.STR_LEN;
+		} catch (IllegalArgumentException e) {
+			return -1;
+		}
+	}
+
+	private int personIdent(final byte[] raw, int ptr) {
+		final int emailB = nextLF(raw, ptr, '<');
+		if (emailB == ptr || raw[emailB - 1] != '<')
+			return -1;
+
+		final int emailE = nextLF(raw, emailB, '>');
+		if (emailE == emailB || raw[emailE - 1] != '>')
+			return -1;
+		if (emailE == raw.length || raw[emailE] != ' ')
+			return -1;
+
+		parseBase10(raw, emailE + 1, ptrout); // when
+		ptr = ptrout.value;
+		if (emailE + 1 == ptr)
+			return -1;
+		if (ptr == raw.length || raw[ptr] != ' ')
+			return -1;
+
+		parseBase10(raw, ptr + 1, ptrout); // tz offset
+		if (ptr + 1 == ptrout.value)
+			return -1;
+		return ptrout.value;
+	}
+
+	/**
+	 * Check a commit for errors.
+	 * 
+	 * @param raw
+	 *            the commit data. The array is never modified.
+	 * @throws CorruptObjectException
+	 *             if any error was detected.
+	 */
+	public void checkCommit(final byte[] raw) throws CorruptObjectException {
+		int ptr = 0;
+
+		if ((ptr = match(raw, ptr, tree)) < 0)
+			throw new CorruptObjectException("no tree header");
+		if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
+			throw new CorruptObjectException("invalid tree");
+
+		while (match(raw, ptr, parent) >= 0) {
+			ptr += parent.length;
+			if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
+				throw new CorruptObjectException("invalid parent");
+		}
+
+		if ((ptr = match(raw, ptr, author)) < 0)
+			throw new CorruptObjectException("no author");
+		if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
+			throw new CorruptObjectException("invalid author");
+
+		if ((ptr = match(raw, ptr, committer)) < 0)
+			throw new CorruptObjectException("no committer");
+		if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
+			throw new CorruptObjectException("invalid committer");
+	}
+
+	/**
+	 * Check an annotated tag for errors.
+	 * 
+	 * @param raw
+	 *            the tag data. The array is never modified.
+	 * @throws CorruptObjectException
+	 *             if any error was detected.
+	 */
+	public void checkTag(final byte[] raw) throws CorruptObjectException {
+		int ptr = 0;
+
+		if ((ptr = match(raw, ptr, object)) < 0)
+			throw new CorruptObjectException("no object header");
+		if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
+			throw new CorruptObjectException("invalid object");
+
+		if ((ptr = match(raw, ptr, type)) < 0)
+			throw new CorruptObjectException("no type header");
+		ptr = nextLF(raw, ptr, '\n');
+
+		if ((ptr = match(raw, ptr, tag)) < 0)
+			throw new CorruptObjectException("no tag header");
+		ptr = nextLF(raw, ptr, '\n');
+
+		if ((ptr = match(raw, ptr, tagger)) < 0)
+			throw new CorruptObjectException("no tagger header");
+		if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
+			throw new CorruptObjectException("invalid tagger");
+	}
+
+	private static int lastPathChar(final int mode) {
+		return FileMode.TREE.equals(mode) ? '/' : '\0';
+	}
+
+	private static int pathCompare(final byte[] raw, int aPos, final int aEnd,
+			final int aMode, int bPos, final int bEnd, final int bMode) {
+		while (aPos < aEnd && bPos < bEnd) {
+			final int cmp = (raw[aPos++] & 0xff) - (raw[bPos++] & 0xff);
+			if (cmp != 0)
+				return cmp;
+		}
+
+		if (aPos < aEnd)
+			return (raw[aPos] & 0xff) - lastPathChar(bMode);
+		if (bPos < bEnd)
+			return lastPathChar(aMode) - (raw[bPos] & 0xff);
+		return 0;
+	}
+
+	private static boolean duplicateName(final byte[] raw,
+			final int thisNamePos, final int thisNameEnd) {
+		final int sz = raw.length;
+		int nextPtr = thisNameEnd + 1 + Constants.OBJECT_ID_LENGTH;
+		for (;;) {
+			int nextMode = 0;
+			for (;;) {
+				if (nextPtr >= sz)
+					return false;
+				final byte c = raw[nextPtr++];
+				if (' ' == c)
+					break;
+				nextMode <<= 3;
+				nextMode += c - '0';
+			}
+
+			final int nextNamePos = nextPtr;
+			for (;;) {
+				if (nextPtr == sz)
+					return false;
+				final byte c = raw[nextPtr++];
+				if (c == 0)
+					break;
+			}
+			if (nextNamePos + 1 == nextPtr)
+				return false;
+
+			final int cmp = pathCompare(raw, thisNamePos, thisNameEnd,
+					FileMode.TREE.getBits(), nextNamePos, nextPtr - 1, nextMode);
+			if (cmp < 0)
+				return false;
+			else if (cmp == 0)
+				return true;
+
+			nextPtr += Constants.OBJECT_ID_LENGTH;
+		}
+	}
+
+	/**
+	 * Check a canonical formatted tree for errors.
+	 * 
+	 * @param raw
+	 *            the raw tree data. The array is never modified.
+	 * @throws CorruptObjectException
+	 *             if any error was detected.
+	 */
+	public void checkTree(final byte[] raw) throws CorruptObjectException {
+		final int sz = raw.length;
+		int ptr = 0;
+		int lastNameB = 0, lastNameE = 0, lastMode = 0;
+
+		while (ptr < sz) {
+			int thisMode = 0;
+			for (;;) {
+				if (ptr == sz)
+					throw new CorruptObjectException("truncated in mode");
+				final byte c = raw[ptr++];
+				if (' ' == c)
+					break;
+				if (c < '0' || c > '7')
+					throw new CorruptObjectException("invalid mode character");
+				if (thisMode == 0 && c == '0')
+					throw new CorruptObjectException("mode starts with '0'");
+				thisMode <<= 3;
+				thisMode += c - '0';
+			}
+
+			if (FileMode.fromBits(thisMode).getObjectType() == Constants.OBJ_BAD)
+				throw new CorruptObjectException("invalid mode " + thisMode);
+
+			final int thisNameB = ptr;
+			for (;;) {
+				if (ptr == sz)
+					throw new CorruptObjectException("truncated in name");
+				final byte c = raw[ptr++];
+				if (c == 0)
+					break;
+				if (c == '/')
+					throw new CorruptObjectException("name contains '/'");
+			}
+			if (thisNameB + 1 == ptr)
+				throw new CorruptObjectException("zero length name");
+			if (duplicateName(raw, thisNameB, ptr - 1))
+				throw new CorruptObjectException("duplicate entry names");
+
+			if (lastNameB != 0) {
+				final int cmp = pathCompare(raw, lastNameB, lastNameE,
+						lastMode, thisNameB, ptr - 1, thisMode);
+				if (cmp > 0)
+					throw new CorruptObjectException("incorrectly sorted");
+			}
+
+			lastNameB = thisNameB;
+			lastNameE = ptr - 1;
+			lastMode = thisMode;
+
+			ptr += Constants.OBJECT_ID_LENGTH;
+			if (ptr > sz)
+				throw new CorruptObjectException("truncated in object id");
+		}
+	}
+
+	/**
+	 * Check a blob for errors.
+	 * 
+	 * @param raw
+	 *            the blob data. The array is never modified.
+	 * @throws CorruptObjectException
+	 *             if any error was detected.
+	 */
+	public void checkBlob(final byte[] raw) throws CorruptObjectException {
+		// We can always assume the blob is valid.
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
index 2ab3bfe..c2c9b00 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java
@@ -37,6 +37,10 @@
 
 package org.spearce.jgit.util;
 
+import static org.spearce.jgit.lib.ObjectChecker.author;
+import static org.spearce.jgit.lib.ObjectChecker.committer;
+import static org.spearce.jgit.lib.ObjectChecker.encoding;
+
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.util.Arrays;
@@ -46,12 +50,6 @@
 
 /** Handy utility functions to parse raw object contents. */
 public final class RawParseUtils {
-	private static final byte[] author = Constants.encodeASCII("author ");
-
-	private static final byte[] committer = Constants.encodeASCII("committer ");
-
-	private static final byte[] encoding = Constants.encodeASCII("encoding ");
-
 	private static final byte[] digits;
 
 	static {
-- 
1.6.0.2.569.g798a2a

^ permalink raw reply related

* Re: [GUILT] Use git_editor
From: Josef 'Jeff' Sipek @ 2008-10-01  2:31 UTC (permalink / raw)
  To: Alan Jenkins; +Cc: git
In-Reply-To: <48E27366.3080503@tuffmail.co.uk>

On Tue, Sep 30, 2008 at 07:43:50PM +0100, Alan Jenkins wrote:
> Signed-off-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk>

Applied.

Thanks,

Josef 'Jeff' Sipek.

-- 
Don't drink and derive. Alcohol and algebra don't mix.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox