Git development
 help / color / mirror / Atom feed
* Re: Mozilla .git tree
From: Jon Smirl @ 2006-08-30  3:27 UTC (permalink / raw)
  To: Shawn Pearce; +Cc: git
In-Reply-To: <20060830031029.GA23967@spearce.org>

On 8/29/06, Shawn Pearce <spearce@spearce.org> wrote:
> Jon Smirl <jonsmirl@gmail.com> wrote:
> > sha1s are effectively 20 byte pointer addresses into the pack. With 2M
> > objects you can easily get away with 4 byte address and a mapping
> > table. Another idea would be to replace the 20 byte sha1 in tree
> > objects with 32b file offsets - requiring that anything the tree
> > refers to has to already be in the pack before the tree entry can be
> > written.
>
> I've thought of that, but when you transfer a "thin" pack over the
> wire the base object may not even be in the pack.  Thus you can't
> use an offset to reference it.  Otherwise there's probably little
> reason why the base couldn't be referenced by its 4 byte offset
> rather than its full 20 byte object ID.  Added up over all deltas
> in the mozilla pack it saves a whopping 23 MiB.

Every time an object goes on the wire these 'pack internal'
optimizations need to be undone. If you are sending the whole pack
everything can be sent as is.

These intense compression schemes are meant for archival level data.
Everybody should end up with a copy of the entire archive and that
will be the end of those objects moving on the wire.

> From what I was able to gather I don't think Clucene stores the
> documents themselves as the tokenized compressed data.  Or if it
> does you lose everything between the tokens.  There's a number of
> things we want to preserve in the original "document" like whitespace
> that would be likely stripped when constructing tokens.

I can't remember if the Clucene code includes the ability to compress
using the dictionary. I had thought that the code was in there but
maybe not. Things that aren't in the dictionary use an escape code and
are copied intact. I have the Lucene book on my desk, I'll flip
through it and see what it says.

Might be worthwhile to poke around on the net and see if you can come
up with an existing dictionary based compressor. There has got to be
one out there, this is a 30 year old concept.

> But it shouldn't be that difficult to produce a rough estimate of
> what that storage size would be.


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [PATCH] gitweb: Add local time and timezone to git_print_authorship
From: Junio C Hamano @ 2006-08-30  4:26 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <ed142v$fbi$1@sea.gmane.org>

Jakub Narebski <jnareb@gmail.com> writes:

> Junio C Hamano wrote:
>
>>  (3) I'd like to eventually get rid of the abbreviated commit
>>      object name from blame output, so the setting in gitweb.css
>>      for table.blame td.age[012] (different colors and font
>>      styles) is not appropriate for what I am shooting at.
>
> What do you want to replace it with? Link can be to "commit" or "commitdiff"
> view, but some marker for commit (perhaps 'git-name-rev --tags'?) is
> needed.

I was hoping I can get away with a narrow single column of solid
color that says nothing.  8 hexadecimal digits do not mean much
to humans and it is taking 8-column or so of screen real estate
that could otherwise be used to show the source lines instead.

I've tried doing that (just set $rev to a single space, and make
the "Commit" column narrower in sub blame2 {}).  One drawback is
that while 8 hexadecimal digits do not mean anything they do
help to match lines that came from the same rev (i.e. "I do not
know what this 8fad7343 mean but this group of lines and that
group are tagged together with that same 8fad7343 so they must
come from the same revision").

^ permalink raw reply

* Re: Mozilla .git tree
From: Nicolas Pitre @ 2006-08-30  5:53 UTC (permalink / raw)
  To: Shawn Pearce; +Cc: Jon Smirl, git
In-Reply-To: <20060830031029.GA23967@spearce.org>

On Tue, 29 Aug 2006, Shawn Pearce wrote:
> Jon Smirl <jonsmirl@gmail.com> wrote:
> > The git tools can be modified to set the compression level to 0 before
> > compressing tree deltas. There is no need to change the decoding code.
> > Even with compression level 0 they still get slightly larger because
> > zlib tacks on a header.
> 
> See my followup email to myself; I think we're talking a zlib
> overhead of 9.2 bytes on average per tree delta.  That's with a
> compression level of -1 (default, which is 6).

In fact, the bulk of a tree delta is most likely to contain the 
literal sha1 of one or more directory entries that changed, and this is 
hardly compressible.  There is nothing to gain by forcing zlib level to 
0 for tree deltas since it never makes the deflated stream smaller from 
the tests I've performed in the past.  It seems that zlib is smart 
enough not to attempt any compression when there is no gain.  That 
leaves the zlib header as the only overhead.

And the zlib header contains a CRC which we're about to use for 
validating the data when doing delta data reuse in order to prevent pack 
corruption propagation like the one recently posted on the list.  
Without that a pack corruption (from a bad disk sector for example) is 
likely to go unnoticed when doing a repack.  The data could be validated 
by expanding deltas and verifying the sha1 on the end result but this is 
a really expensive operation if performed on all deltas which is best 
left to git-fsck-objects --full. So I think the small overhead relative 
to total pack size might be worth it for better data integrity.

Using an offset instead of a sha1 to reference a delta base object is 
certainly a good idea though.  But I'd use the same variable encoding as 
the object size to avoid the 32-bit limit issue.  When generating a thin 
pack the real sha1 of the delta object could be substituted for the 
offset quite easily if the base object is not sent a part of the same 
pack.


Nicolas

^ permalink raw reply

* Re: [PATCH] gitweb: Add local time and timezone to git_print_authorship
From: Jakub Narebski @ 2006-08-30  9:47 UTC (permalink / raw)
  To: git
In-Reply-To: <7vlkp6kfwh.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano wrote:

> Jakub Narebski <jnareb@gmail.com> writes:
> 
>> Junio C Hamano wrote:
>>
>>>  (3) I'd like to eventually get rid of the abbreviated commit
>>>      object name from blame output, so the setting in gitweb.css
>>>      for table.blame td.age[012] (different colors and font
>>>      styles) is not appropriate for what I am shooting at.
>>
>> What do you want to replace it with? Link can be to "commit" 
>> or "commitdiff" view, but some marker for commit 
>> (perhaps 'git-name-rev --tags'?) is needed.
> 
> I was hoping I can get away with a narrow single column of solid
> color that says nothing.  8 hexadecimal digits do not mean much
> to humans and it is taking 8-column or so of screen real estate
> that could otherwise be used to show the source lines instead.

I don't think that is much an issue. Source code is usually (read:
should be) 80-columns wide, and with default font size there is
certainly place for 8-column revision number. Anything more, and
much more (like e.g. default git_blame a.k.a. git_annotate output)
and it is less readable.

> I've tried doing that (just set $rev to a single space, and make
> the "Commit" column narrower in sub blame2 {}).  One drawback is
> that while 8 hexadecimal digits do not mean anything they do
> help to match lines that came from the same rev (i.e. "I do not
> know what this 8fad7343 mean but this group of lines and that
> group are tagged together with that same 8fad7343 so they must
> come from the same revision").

So what you need is to solve graph coloring problem (which is not
map coloring, as the same revisions needs the same color) for 
revisions ;-) 

I have an idea to use first character of commit hash together with
dark/light (odd/even) class to color blocks of lines in the same 
revisions, 16 colors (we could reduce it to e.g. 6 or 8 colors)
with darker/lighter version.

The problem is to reduce hash to 16 or 8 bits with as small number of
collisions as possible (in average).


Yet another blame improvement idea would be to "highlight" whole _block_
on hover (on mouseover), but it needs changing blame output format from
table to divs (like "blob" view). BTW. table view should be I think
reserved for tabular data, i.e. when sorting by at least some of columns
have sense.
-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* Re: Mozilla .git tree
From: Junio C Hamano @ 2006-08-30 11:42 UTC (permalink / raw)
  To: Nicolas Pitre; +Cc: git, Linus Torvalds
In-Reply-To: <Pine.LNX.4.64.0608300124550.9796@xanadu.home>

Nicolas Pitre <nico@cam.org> writes:

> And the zlib header contains a CRC which we're about to use for 
> validating the data when doing delta data reuse in order to prevent pack 
> corruption propagation like the one recently posted on the list.  

Ah, never thought of using the CRC directly.  I was thinking
about inflating into void and see if it succeeds, which as you
say is perhaps quite expensive.

This brings me back to my pet-peeve, though.  I do not think
zlib stream seeks back and leaves some clue at the beginning to
tell me the deflated length, so it is quite hard to find where
each deflated stream ends in a packfile cheaply.  Loose objects
(with new or legacy style header) are easy (st.st_size is
available), but I do not think of a way short of building a
reverse index of pack .idx file, which means I am already
talking about not so cheap way X-<.

It might be a reason to define a new .idx format.  We could lift
32-bit offset limitation while we are at it.  Each entry could
have 20-byte hash, 64-bit offset into the corresponding .pack,
and 32-bit deflated length (heh, why not make it 64-bit while we
are at it).  Luckily, .idx _is_ a local matter so we can even
have a flag day and tell people to run the updated index-pack on
existing packfiles to regenerate .idx.

> Using an offset instead of a sha1 to reference a delta base object is 
> certainly a good idea though.  But I'd use the same variable encoding as 
> the object size to avoid the 32-bit limit issue.  When generating a thin 
> pack the real sha1 of the delta object could be substituted for the 
> offset quite easily if the base object is not sent a part of the same 
> pack.

That sounds quite a reasonable suggestion.  I love this kind of
moment when I find us very fortunate to have bright people on
the list ;-).

^ permalink raw reply

* problem with git-cvsserver
From: aonghus @ 2006-08-30 15:45 UTC (permalink / raw)
  To: git

Hi,

I am trying to use git-cvsserver and I have come across this error:

    $ cvs co -d project-master master

    install_driver(SQLite) failed: Can't locate DBD/SQLite.pm in @INC (@INC contains: /etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl /usr/local/lib/perl/5.8.7 /usr/local/share/perl/5.8.7 .) at (eval 7) line 3, <STDIN> line 14.

    Perhaps the DBD::SQLite perl module hasn't been fully installed,

    or perhaps the capitalisation of 'SQLite' isn't right.

    Available drivers: DBM, ExampleP, File, Pg, Proxy, Sponge, mysql.

     at /usr/bin/git-cvsserver line 2055

    cvs [checkout aborted]: end of file from server (consult above messages if any)


This is a debian testing/unstable machine with git, git-core, git-cvs 
etc installed (with no problems):

    $ dpkg --list git-cvs

    ||/ Name                            Version                         Description

    +++-===============================-===============================-

    ii  git-cvs                         1.4.1.1-1                       content addressable filesystem (cvs interoperability)


Also, the perl DBI modules are installed:

    $ dpkg --list libdbd-sqlite3-perl

    ||/ Name                            Version                         Description

    +++-===============================-===============================-

    ii  libdbd-sqlite3-perl             1.12-1                          Perl DBI driver with a self-contained RDBMS

    $ dpkg -S SQLite

    libdbd-sqlite3-perl: /usr/share/man/man3/DBD::SQLite.3pm.gz

    libdbd-sqlite3-perl: /usr/share/perl5/DBD/SQLite.pm

    libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite

    libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite/SQLite.bs

    libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite/SQLite.so


Anyone know whats wrong? If its a problem somewhere else (eg. with 
debian packaging), please point me there,

thanks

a

^ permalink raw reply

* Re: problem with git-cvsserver
From: Johannes Schindelin @ 2006-08-30 17:07 UTC (permalink / raw)
  To: aonghus; +Cc: git
In-Reply-To: <44F5B2A7.8070501@gmail.com>

Hi,

On Wed, 30 Aug 2006, aonghus wrote:

>    install_driver(SQLite) failed: Can't locate DBD/SQLite.pm in @INC (@INC
> contains: /etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8
> /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8
> /usr/local/lib/site_perl /usr/local/lib/perl/5.8.7 /usr/local/share/perl/5.8.7
> .) at (eval 7) line 3, <STDIN> line 14.
>
> [...]
> 
>    libdbd-sqlite3-perl: /usr/share/perl5/DBD/SQLite.pm

Have you checked that /usr/share/perl5/DBD/SQLite.pm is actually there? 
Because your perl does not think so.

OTOH it could be that it only works when SQLite.pm is in 
/usr/lib/perl5/DBD/ (note the "lib" instead of "share"), because in my 
(working) setup, both the .pm and the .so are under /usr/lib/perl5/...

Hth,
Dscho

^ permalink raw reply

* Re: Problematic git pack
From: Linus Torvalds @ 2006-08-30 18:11 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Sergio Callegari, Git Mailing List
In-Reply-To: <7v7j0qihwl.fsf@assigned-by-dhcp.cox.net>


[ git list cc'd, just because maybe somebody was interested in seeing what 
  looks very much like the resolution of this issue..

  For people who haven't followed (the private) email exhanges with Sergio 
  about his git corruption, what was going on is that we initially were 
  able to re-generate all but one object (and the objects that were 
  dependent on it through deltas - there were three deltas against it, but 
  two of the deltas happened to delete the corruption and thus only one 
  of the dependent objects actually ended up having the wrong data, in 
  addition to the primary corrupted object, of course)..

  Junio pinpointed what the filesystem name of that primary object was, 
  and Sergio actually had the original object in his working tree, so 
  Junio could then generate a new pack with the one corrupted object 
  fixed, which obviously meant that all the deltas now worked too.

  This is my (probably final) analysis of the resulting differences.. ]

On Wed, 30 Aug 2006, Junio C Hamano wrote:
> 
> Ok, I was going to attach the resurrected pack that should
> contain everything your corrupt pack had, but it is a bit too
> large, so I'll place it here [*1*].  Drop me a note when you
> retrieved it, so that I can remove it.

VERY interesting.

The pack you generated looks very different from the original corrupt 
pack, but when I compare it to one that I generated _from_ that corrupted 
original by forcing a full repack, they are actually very very similar.

[ Side note: the explanation for the difference between my repacked 
  version and the original corrupt pack in turn seems to be fairly 
  straightforward: the older objects in the original pack were created 
  with an older version of git that defaulted to maximum zlib compression, 
  while the newer parts of the pack were done with the current default of 
  "default compression".

  So when Junio re-created the packfile from scratch with a modern git 
  version, it would now re-compress everything with the modern compression 
  value, and thus the byte-stream of Junio's pack would look very 
  different from Sergio's original one for the older objects. So what I 
  did was to repack the _corrupt_ data with a modern git, to be able to 
  compare the two sanely. ]

In fact, doing a hexdump with 'od', and diffing the results, here's the 
differences:

	--- od.unfixed  2006-08-30 10:11:36.000000000 -0700
	+++ od.fixed    2006-08-30 09:33:02.000000000 -0700
	@@ -3358,7 +3358,7 @@
	 0150720 2c0c bc8b 2f07 3733 767d 35bb 6bb6 4b28
	 0150740 0e3c 0ddc 955b eb2d 57e0 754b b1ec b9f7
	 0150760 8ac8 87bf 9d44 fd11 a041 c4ea cd1d 26de
	-0151000 94ea 9cf3 7dcd b596 13a3 61bb 48db e69e
	+0151000 96ea 9cf3 7dcd b596 13a3 61bb 48db e69e
	 0151020 21e9 1288 6294 8dd4 8b3c 6cc6 e4e7 518f
	 0151040 6166 3e2d d635 b631 9ec6 0613 aee3 caab
	 0151060 d4fd 3ab3 6e18 c43a 8dee 15b9 bc6d 0748
	@@ -8455,7 +8455,7 @@
	 0410140 9f30 2514 f447 0c0d 477e 27c7 a0e1 c9ec
	 0410160 c0b1 f352 76dd 4ff6 24b1 03c2 0ed2 363a
	 0410200 034f 637c 8e11 0c86 e2db 2625 75c4 508b
	-0410220 6fdb bf01 af2d b23b fbb7 0128 49bd 2bd8
	+0410220 6fdb bf01 54b6 b23d fbb7 0128 49bd 2bd8
	 0410240 a76b bca3 7df2 a4c8 e8b9 9081 1d01 a778
	 0410260 9cad 564d 6f1b 4518 7605 e563 c501 0995
	 0410300 231a 0151 5255 f6ce ccee ec07 8a22 25eb
	@@ -394626,7 +394626,7 @@
	 30054020 4951 6272 665e 7a6e 6272 517e b1ad d1e4
	 30054040 a55c 9a40 fd62 a2a9 c925 99f9 79b5 d5aa
	 30054060 5c0a 4010 03b4 d6a1 a064 b323 f74a c6cd
	-30054100 df78 a219 27c7 f205 0200 8970 3307 ae17
	-30054120 5e11 8f8c e015 00f8 5d51 9152 0d07 79b2
	-30054140 45dd
	+30054100 df78 a219 27c7 f205 0200 8970 3307 4599
	+30054120 3854 bacb 704e 7312 11e1 60e3 c14b 0a90
	+30054140 e22b
	 30054142

You can ignore the last hunk, that's just the SHA1 of the pack itself, and 
that will obviously differ in all 20 bytes, so that difference is not 
interesting. 

So the _real_ difference is literally just the one byte at offset 0151000 
(decimal 53760) which in the fixed pack is 0x96, and in the corrupt pack 
it is 0x94. That's a single-bit difference (bit #1 has been cleared).

The other difference is at 0410224 (decimal 135316), where the sequence 
"af2d b23b" should have been "54b6 b23d", and that's just final zlib CRC 
of that one object (the corrupted object is fairly large: it's 127905 
bytes uncompressed, and I think it was ~100kB compressed too, because most 
of it is the data for the image inside of it).

The way I know that: doing "git-show-index" (and sorting by object offset) 
on the index gives you:

	...
	13830 2849bd2bd8a76bbca37df2a4c8e8b990811d01a7
	135320 42abdeecbf1b49c8354ca9639bd19c378be6d7d4
	...

which means that the one-bit error was in the middle of that (known 
corrupt) object 2849bd2bd8, and the four-byte difference is exactly the 
four last bytes of that same object - which is obviously where you'd 
expect to find the crc32 for the compressed data.

[ NOTE! The only reason the crc32 has changed is exactly the fact that I 
  forced a full repack of the corrupt pack. In the _original_ pack the 
  object 2849bd2bd8a76bbca37df2a4c8e8b990811d01a7 was at:

	...
	13369 2849bd2bd8a76bbca37df2a4c8e8b990811d01a7
	134859 42abdeecbf1b49c8354ca9639bd19c378be6d7d4
	...

  and thus the crc32 in the original is the four bytes at 134855, and 
  indeed in the original corrupt pack we see:

	...
	0407260 d236 3a03 4f63 7c8e 110c 86e2 db26 2575
	0407300 c450 8b6f dbbf 0154 b6b2 3dfb b701 2849
				 ^^ ^^^^ ^^
	0407320 bd2b d8a7 6bbc a37d f2a4 c8e8 b990 811d
	...

  so note how that contains the _original_ CRC, because in the original 
  corrupt pack, the CRC was the one that had been computed with the 
  original (uncorrupted) zlib data, but obviously didn't actually _match_ 
  the actual corrupted data itself - which is why we got a zlib error on 
  unpacking it ]

So I think this is pretty damn conclusive. Sergio had a single-bit error 
in a pack-file, and that error got propagated because "git repack" didn't 
notice, and because he used unison to synchronize between two different 
machines, and that obviously happily transferred the corruption.

Now, that makes me feel happy on one level, because it's almost certainly 
a hardware problem - subtle memory corruption, or disk corruption that 
happened when either reading or writing the image. Sergio may not be that 
happy about it, of course.

It _could_ be something else (hey, it could be the kernel or git itself 
that has a wild pointer and corrupted a single bit), but I'd say that the 
hardware is the primary suspect.

Now, what to take away from this:

 - git _did_ find the error, but it would have been easier for everybody 
   if it had noticed it a bit earlier. Ergo:

 - we should make the repacking verify the object even when it just 
   blindly copies it, so that we do _not_ end up in the situation that a 
   pack-file has a "valid" SHA1, even though the contents are actually 
   corrupt.

   This should be easy enough, since zlib already has the crc (actually, 
   we should probably do a full unpack of that object, even if it's 
   expensive: if it's a delta, we'd need that to verify that the SHA1 of 
   the base object is valid).

 - git pack-files are extremely dense (we knew that already, and mostly 
   consider it to be a really good thing), and a single-bit error can be 
   absolutely devastating. For important data, always keep a copy on 
   another machine (that's obviously true regardless of whether you use 
   git or not ;), and _always_ create the copy with git itself, or at 
   least verify it with "git-fsck-objects --full" before you overwrite the 
   previous version.

   The point being, if you have even a single-bit error (and for all we 
   know, it could have been introduced by the transfer itself, and then 
   been re-introduced in the original place when transferring the data 
   back), you absolutely do _not_ want to transfer that to your backup 
   location too.

Finally, this also points out that the corrupted packs _can_ be fixed, but 
I think Sergio was a bit lucky (to offset all the bad luck). Sergio still 
had access to the original file that had had its object corrupted. And it 
took a fair amount of work, and some git hacking by somebody who really 
understood git (Junio).

Maybe we'll end up having some of that effort being useful and checked in, 
and we'll eventually have more infrastructure for fixing these things, but 
I suspect that in most cases, even a _single_ bit of corruption will 
generally result in so much havoc that nobody should depend on that. It's 
a lot better to have backups.

			Linus

^ permalink raw reply

* Re: problem with git-cvsserver
From: aonghus @ 2006-08-30 18:20 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0608301904360.28360@wbgn013.biozentrum.uni-wuerzburg.de>

Johannes Schindelin wrote:
> Hi,
>
> On Wed, 30 Aug 2006, aonghus wrote:
>
>   
>>    install_driver(SQLite) failed: Can't locate DBD/SQLite.pm in @INC (@INC
>> contains: /etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8
>> /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8
>> /usr/local/lib/site_perl /usr/local/lib/perl/5.8.7 /usr/local/share/perl/5.8.7
>> .) at (eval 7) line 3, <STDIN> line 14.
>>
>> [...]
>>
>>    libdbd-sqlite3-perl: /usr/share/perl5/DBD/SQLite.pm
>>     
>
> Have you checked that /usr/share/perl5/DBD/SQLite.pm is actually there? 
> Because your perl does not think so.
>
> OTOH it could be that it only works when SQLite.pm is in 
> /usr/lib/perl5/DBD/ (note the "lib" instead of "share"), because in my 
> (working) setup, both the .pm and the .so are under /usr/lib/perl5/...
>
> Hth,
> Dscho
>   

Hi,

Thanks for the reply, but I think the files are in the right place- here 
is what I have:

    $ dpkg -S SQLite
    libdbd-sqlite3-perl: /usr/share/man/man3/DBD::SQLite.3pm.gz
    libdbd-sqlite3-perl: /usr/share/perl5/DBD/SQLite.pm
    libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite
    libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite/SQLite.bs
    libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite/SQLite.so


and it really is there:

    $ ll /usr/share/perl5/DBD/SQLite.pm
    -rw-r--r-- 1 root root 16K Apr 18 16:20 /usr/share/perl5/DBD/SQLite.pm


I don't know much about the perl SQLite package, but it seems that 
git-cvsserver is not loading the correct module. The line 'use DBI;' 
seems to load only this module:

    'DBI.pm' => '1.51 from /usr/lib/perl5/DBI.pm'

Does it need something else to load the SQLite module?

a

^ permalink raw reply

* Re: problem with git-cvsserver
From: Junio C Hamano @ 2006-08-30 19:24 UTC (permalink / raw)
  To: aonghus; +Cc: git, Johannes Schindelin
In-Reply-To: <44F5D6F8.50307@gmail.com>

aonghus <thecolourblue@gmail.com> writes:

> I don't know much about the perl SQLite package, but it seems that
> git-cvsserver is not loading the correct module. The line 'use DBI;'
> seems to load only this module:
>
>    'DBI.pm' => '1.51 from /usr/lib/perl5/DBI.pm'
>
> Does it need something else to load the SQLite module?

When things are properly installed, it should be enough to say
'use DBI' upfront and then 'DBI->connect("dbi:Foo:...")' should
be enough to use DBD::Foo backend of the DBI interface.

I am on Debian etch plus some from testing and have these:

        perl (5.8.8-6.1)
        perl-base (5.8.8-6.1)
        libdbi-perl (1.51-2)
        libsqlite3-0 (3.3.7-1)
        libdbd-sqlite3-perl (1.12-1)

Does this work for you?

-- >8 -- cut here -- >8 --
#!/usr/bin/perl -w
use DBI;
my $dsn = 'dbi:SQLite:dbname=foo';
my $dbh = DBI->connect($dsn, '', '');
-- 8< -- cut here -- 8< --

If not, does it work for you if you substitute $dsn with
'dbi:mysql:dbname=test'?  The error message you showed us in
your earlier message said mysql was one of the backend your DBI
knows about while SQLite is not.

This did not work for me before installing libdbd-sqlite3-perl
and gave a very similar error message as you had.

What's puzzling is that you said you have these:

   $ dpkg -S SQLite
   libdbd-sqlite3-perl: /usr/share/man/man3/DBD::SQLite.3pm.gz
   libdbd-sqlite3-perl: /usr/share/perl5/DBD/SQLite.pm
   libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite
   libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite/SQLite.bs
   libdbd-sqlite3-perl: /usr/lib/perl5/auto/DBD/SQLite/SQLite.so

but that is exactly what I am seeing _after_ installing
libdbd-sqlite3-perl.

^ permalink raw reply

* Re: problem with git-cvsserver
From: Martin Langhoff @ 2006-08-30 20:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: aonghus, git, Johannes Schindelin
In-Reply-To: <7vlkp6gh6e.fsf@assigned-by-dhcp.cox.net>

On 8/31/06, Junio C Hamano <junkio@cox.net> wrote:

> I am on Debian etch plus some from testing and have these:
>
>         perl (5.8.8-6.1)
>         perl-base (5.8.8-6.1)
>         libdbi-perl (1.51-2)
>         libsqlite3-0 (3.3.7-1)
>         libdbd-sqlite3-perl (1.12-1)
>
> Does this work for you?
>
> -- >8 -- cut here -- >8 --
> #!/usr/bin/perl -w
> use DBI;
> my $dsn = 'dbi:SQLite:dbname=foo';
> my $dbh = DBI->connect($dsn, '', '');
> -- 8< -- cut here -- 8< --

Hi! all this seems to have happened during NZ's night, so I'm only
catching up. +1 on all the diagnosis Junio is proposing. Can't think
of anything more relevant to add. The code was developed mainly on a
bunch of debian sarge/etch boxes using all the standard
libdbd-sqlite-perl libs, and a gentoo box.

Actually, just looking at my etch dev box, libdbd-sqlite-perl is
0.29-1 and sqlite is 2.8.16-1. Not sure if the difference is
significant. Perhaps SQLite v3 has a different invocation / driver
name?

BTW, I just doublechecked, cvsserver isn't mangling the lib path in
any way. However, the environment it's running under may have a
strange PERL5LIB.

cheers,


martin

^ permalink raw reply

* [PATCH 1/4] gitweb: Move git-ls-tree output parsing to parse_ls_tree_line
From: Jakub Narebski @ 2006-08-30 22:32 UTC (permalink / raw)
  To: git
In-Reply-To: <200608310030.33512.jnareb@gmail.com>

Add new subroutine parse_ls_tree_line and use it in git_tree.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This patch could be applied regardless of the fact that this 
"tree_blame" view series is proof-of-concept series.

 gitweb/gitweb.perl |   62 ++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index fa7f62a..84a13fd 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1021,6 +1021,27 @@ sub parse_difftree_raw_line {
 	return wantarray ? %res : \%res;
 }
 
+# parse line of git-ls-tree output
+sub parse_ls_tree_line ($;%) {
+	my $line = shift;
+	my %opts = @_;
+	my %res;
+
+	#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
+	$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
+
+	$res{'mode'} = $1;
+	$res{'type'} = $2;
+	$res{'hash'} = $3;
+	if ($opts{'-z'}) {
+		$res{'name'} = $4;
+	} else {
+		$res{'name'} = unquote($4);
+	}
+
+	return wantarray ? %res : \%res;
+}
+
 ## ......................................................................
 ## parse to array of hashes functions
 
@@ -2498,51 +2519,54 @@ sub git_tree {
 	print "<table cellspacing=\"0\">\n";
 	my $alternate = 0;
 	foreach my $line (@entries) {
-		#'100644	blob	0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
-		$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
-		my $t_mode = $1;
-		my $t_type = $2;
-		my $t_hash = $3;
-		my $t_name = validate_input($4);
+		my %t = parse_ls_tree_line($line, -z => 1);
+
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
 			print "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
-		print "<td class=\"mode\">" . mode_str($t_mode) . "</td>\n";
-		if ($t_type eq "blob") {
+
+		print "<td class=\"mode\">" . mode_str($t{'mode'}) . "</td>\n";
+		if ($t{'type'} eq "blob") {
 			print "<td class=\"list\">" .
-			      $cgi->a({-href => href(action=>"blob", hash=>$t_hash, file_name=>"$base$t_name", %base_key),
-			              -class => "list"}, esc_html($t_name)) .
+			      $cgi->a({-href => href(action=>"blob", hash=>$t{'hash'},
+			                             file_name=>"$base$t{'name'}", %base_key),
+			              -class => "list"}, esc_html($t{'name'})) .
 			      "</td>\n" .
 			      "<td class=\"link\">" .
-			      $cgi->a({-href => href(action=>"blob", hash=>$t_hash, file_name=>"$base$t_name", %base_key)},
+			      $cgi->a({-href => href(action=>"blob", hash=>$t{'hash'},
+			                             file_name=>"$base$t{'name'}", %base_key)},
 			              "blob");
 			if ($have_blame) {
 				print " | " .
-					$cgi->a({-href => href(action=>"blame", hash=>$t_hash, file_name=>"$base$t_name", %base_key)},
+					$cgi->a({-href => href(action=>"blame", hash=>$t{'hash'},
+					                       file_name=>"$base$t{'name'}", %base_key)},
 					        "blame");
 			}
 			print " | " .
 			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
-			                             hash=>$t_hash, file_name=>"$base$t_name")},
+			                             hash=>$t{'hash'}, file_name=>"$base$t{'name'}")},
 			              "history") .
 			      " | " .
 			      $cgi->a({-href => href(action=>"blob_plain",
-			                             hash=>$t_hash, file_name=>"$base$t_name")},
+			                             hash=>$t{'hash'}, file_name=>"$base$t{'name'}")},
 			              "raw") .
 			      "</td>\n";
-		} elsif ($t_type eq "tree") {
+		} elsif ($t{'type'} eq "tree") {
 			print "<td class=\"list\">" .
-			      $cgi->a({-href => href(action=>"tree", hash=>$t_hash, file_name=>"$base$t_name", %base_key)},
-			              esc_html($t_name)) .
+			      $cgi->a({-href => href(action=>"tree", hash=>$t{'hash'},
+			                             file_name=>"$base$t{'name'}", %base_key)},
+			              esc_html($t{'name'})) .
 			      "</td>\n" .
 			      "<td class=\"link\">" .
-			      $cgi->a({-href => href(action=>"tree", hash=>$t_hash, file_name=>"$base$t_name", %base_key)},
+			      $cgi->a({-href => href(action=>"tree", hash=>$t{'hash'},
+			                             file_name=>"$base$t{'name'}", %base_key)},
 			              "tree") .
 			      " | " .
-			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, file_name=>"$base$t_name")},
+			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+			                             file_name=>"$base$t{'name'}")},
 			              "history") .
 			      "</td>\n";
 		}
-- 
1.4.1.1

^ permalink raw reply related

* [PATCH 2/4] gitweb: Separate printing of git_tree row into git_print_tree_entry
From: Jakub Narebski @ 2006-08-30 22:35 UTC (permalink / raw)
  To: git
In-Reply-To: <200608310030.33512.jnareb@gmail.com>

This is preparation for "tree blame" (similar to what ViewVC shows)
output, i.e. for each entry give commit where it was changed.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This could be applied even though it is proof-of-concept series,
but I think it would be better to separate git_tree_body rather,
although perhaps something like git_print_tree_entry subroutine
is a good idea.

 gitweb/gitweb.perl |  103 ++++++++++++++++++++++++++++++----------------------
 1 files changed, 59 insertions(+), 44 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 84a13fd..d8b94a1 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1462,6 +1462,62 @@ sub git_print_simplified_log {
 		-remove_title => $remove_title);
 }
 
+# print tree entry (row of git_tree), but without encompassing <tr> element
+sub git_print_tree_entry {
+	my ($t, $basedir, $hash_base, $have_blame) = @_;
+
+	my %base_key = ();
+	$base_key{hash_base} = $hash_base if defined $hash_base;
+
+	print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
+	if ($t->{'type'} eq "blob") {
+		print "<td class=\"list\">" .
+		      $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+		                             file_name=>"$basedir$t->{'name'}", %base_key),
+		              -class => "list"}, esc_html($t->{'name'})) .
+		      "</td>\n" .
+		      "<td class=\"link\">" .
+		      $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+		                             file_name=>"$basedir$t->{'name'}", %base_key)},
+		              "blob");
+		if ($have_blame) {
+			print " | " .
+				$cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
+				                       file_name=>"$basedir$t->{'name'}", %base_key)},
+				        "blame");
+		}
+		if (defined $hash_base) {
+			print " | " .
+			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+			                             hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
+			              "history");
+		}
+		print " | " .
+		      $cgi->a({-href => href(action=>"blob_plain",
+		                             hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
+		              "raw") .
+		      "</td>\n";
+
+	} elsif ($t->{'type'} eq "tree") {
+		print "<td class=\"list\">" .
+		      $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+		                             file_name=>"$basedir$t->{'name'}", %base_key)},
+		              esc_html($t->{'name'})) .
+		      "</td>\n" .
+		      "<td class=\"link\">" .
+		      $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+		                             file_name=>"$basedir$t->{'name'}", %base_key)},
+		              "tree");
+		if (defined $hash_base) {
+			print " | " .
+			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+			                             file_name=>"$basedir$t->{'name'}")},
+			              "history");
+		}
+		print "</td>\n";
+	}
+}
+
 ## ......................................................................
 ## functions printing large fragments of HTML
 
@@ -2499,14 +2555,13 @@ sub git_tree {
 	my $refs = git_get_references();
 	my $ref = format_ref_marker($refs, $hash_base);
 	git_header_html();
-	my %base_key = ();
 	my $base = "";
 	my $have_blame = gitweb_check_feature('blame');
 	if (defined $hash_base && (my %co = parse_commit($hash_base))) {
-		$base_key{hash_base} = $hash_base;
 		git_print_page_nav('tree','', $hash_base);
 		git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
 	} else {
+		undef $hash_base;
 		print "<div class=\"page_nav\">\n";
 		print "<br/><br/></div>\n";
 		print "<div class=\"title\">$hash</div>\n";
@@ -2528,48 +2583,8 @@ sub git_tree {
 		}
 		$alternate ^= 1;
 
-		print "<td class=\"mode\">" . mode_str($t{'mode'}) . "</td>\n";
-		if ($t{'type'} eq "blob") {
-			print "<td class=\"list\">" .
-			      $cgi->a({-href => href(action=>"blob", hash=>$t{'hash'},
-			                             file_name=>"$base$t{'name'}", %base_key),
-			              -class => "list"}, esc_html($t{'name'})) .
-			      "</td>\n" .
-			      "<td class=\"link\">" .
-			      $cgi->a({-href => href(action=>"blob", hash=>$t{'hash'},
-			                             file_name=>"$base$t{'name'}", %base_key)},
-			              "blob");
-			if ($have_blame) {
-				print " | " .
-					$cgi->a({-href => href(action=>"blame", hash=>$t{'hash'},
-					                       file_name=>"$base$t{'name'}", %base_key)},
-					        "blame");
-			}
-			print " | " .
-			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
-			                             hash=>$t{'hash'}, file_name=>"$base$t{'name'}")},
-			              "history") .
-			      " | " .
-			      $cgi->a({-href => href(action=>"blob_plain",
-			                             hash=>$t{'hash'}, file_name=>"$base$t{'name'}")},
-			              "raw") .
-			      "</td>\n";
-		} elsif ($t{'type'} eq "tree") {
-			print "<td class=\"list\">" .
-			      $cgi->a({-href => href(action=>"tree", hash=>$t{'hash'},
-			                             file_name=>"$base$t{'name'}", %base_key)},
-			              esc_html($t{'name'})) .
-			      "</td>\n" .
-			      "<td class=\"link\">" .
-			      $cgi->a({-href => href(action=>"tree", hash=>$t{'hash'},
-			                             file_name=>"$base$t{'name'}", %base_key)},
-			              "tree") .
-			      " | " .
-			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
-			                             file_name=>"$base$t{'name'}")},
-			              "history") .
-			      "</td>\n";
-		}
+		git_print_tree_entry(\%t, $base, $hash_base, $have_blame);
+
 		print "</tr>\n";
 	}
 	print "</table>\n" .
-- 
1.4.1.1

^ permalink raw reply related

* [PATCH 3/4] gitweb: Extend parse_difftree_raw_line to save commit info
From: Jakub Narebski @ 2006-08-30 22:36 UTC (permalink / raw)
  To: git
In-Reply-To: <200608310030.33512.jnareb@gmail.com>

Extend parse_difftree_raw_line to save commit info from when
git-diff-tree is given only one <tree-ish>, for example when fed
from git-rev-list using --stdin option.

git-diff-tree outputs a line with the commit ID when applicable.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This can be applied regardless that it is proof-of-concept series.
Need checking if it doesn't break anything.

 gitweb/gitweb.perl |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index d8b94a1..b193bc6 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1014,9 +1014,9 @@ sub parse_difftree_raw_line {
 		}
 	}
 	# 'c512b523472485aef4fff9e57b229d9d243c967f'
-	#elsif ($line =~ m/^([0-9a-fA-F]{40})$/) {
-	#	$res{'commit'} = $1;
-	#}
+	elsif ($line =~ m/^([0-9a-fA-F]{40})$/) {
+		$res{'commit'} = $1;
+	}
 
 	return wantarray ? %res : \%res;
 }
-- 
1.4.1.1

^ permalink raw reply related

* [PATCH 4/4] gitweb: Add "tree_blame" view (WIP)
From: Jakub Narebski @ 2006-08-30 22:36 UTC (permalink / raw)
  To: git
In-Reply-To: <200608310030.33512.jnareb@gmail.com>

Adds git_tree_blame subroutine, for the "tree_blame" view, similar to
the ViewVC output.  It means having the commit, date and author of
last change to the file (blob) or directory (tree) in given tree in
addition to the default "tree" view info.

The git_tree_blame subroutine contains 3 implementations: one using
git-diff-tree to detect when file was changed (works only on blobs,
and not on trees/directories), one comparing output of git-ls-tree to
check what changed between revisions, and one (default used) looking
at two first commits in the blob or tree history.

While at it, add 'year' field to the parse_date output; add "blame"
link to the list of links for tree entries in directory listing in
git_print_tree_entry; add support for 'order' parameter to href().

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
 gitweb/gitweb.perl |  344 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 344 insertions(+), 0 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index b193bc6..130609b 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -264,6 +264,7 @@ my %actions = (
 	"tag" => \&git_tag,
 	"tags" => \&git_tags,
 	"tree" => \&git_tree,
+	"tree_blame" => \&git_tree_blame,
 	"snapshot" => \&git_snapshot,
 	# those below don't need $project
 	"opml" => \&git_opml,
@@ -298,6 +299,7 @@ sub href(%) {
 		hash_parent_base => "hpb",
 		page => "pg",
 		searchtext => "s",
+		order => "o",
 	);
 	my %mapping = @mapping;
 
@@ -804,6 +806,7 @@ sub parse_date {
 	$date{'hour'} = $hour;
 	$date{'minute'} = $min;
 	$date{'mday'} = $mday;
+	$date{'year'} = 1900+$year;
 	$date{'day'} = $days[$wday];
 	$date{'month'} = $months[$mon];
 	$date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000",
@@ -1508,6 +1511,12 @@ sub git_print_tree_entry {
 		      $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
 		                             file_name=>"$basedir$t->{'name'}", %base_key)},
 		              "tree");
+		if ($have_blame) {
+			print " | " .
+				$cgi->a({-href => href(action=>"tree_blame", hash=>$t->{'hash'},
+				                       file_name=>"$basedir$t->{'name'}", %base_key)},
+				        "blame");
+		}
 		if (defined $hash_base) {
 			print " | " .
 			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
@@ -2592,6 +2601,341 @@ sub git_tree {
 	git_footer_html();
 }
 
+sub git_tree_blame {
+	my $version = shift || 'rev-list';
+
+	# check permission
+	my $have_blame = gitweb_check_feature('blame');
+	if (!$have_blame) {
+		die_error('403 Permission denied', "Permission denied");
+	}
+	# get sort order parameter
+	my $order = $cgi->param('o');
+	if (defined $order && $order !~ m/[+-]?age|[+-]?author|[+-]?mode|[+-]?name/) {
+		die_error(undef, "Unknown order parameter");
+	}
+	# check hash
+	if (!defined $hash) {
+		$hash = git_get_head_hash($project);
+		if (defined $file_name) {
+			my $base = $hash_base || $hash;
+			$hash = git_get_hash_by_path($base, $file_name, "tree");
+		}
+	}
+	# check hash_base
+	if (!defined $hash_base) {
+		$hash_base = $hash;
+	}
+	# check if we have starting (base) commit
+	my %co = parse_commit($hash_base);
+	if (!%co) {
+		die_error(undef, "Base commit not defined");
+	}
+	# set basedir (dirname for entries)
+	my $basedir;
+	if (defined $file_name) {
+		$basedir = "$file_name/";
+	}
+
+	# read tree
+	$/ = "\0";
+	open my $fd, "-|", $GIT, "ls-tree", '-z', $hash
+		or die_error(undef, "Open git-ls-tree failed");
+	my @entries = map { chomp; $_ } <$fd>;
+	close $fd
+		or die_error(undef, "Reading git-ls-tree failed");
+	$/ = "\n";
+
+	# parse tree
+	@entries = map {
+		scalar parse_ls_tree_line($_, -z => 1);
+	} @entries;
+
+	## !!! two versions: one using git-diff-tree, without directories,
+	## !!! second using git-ls-files, with directories, slower.
+
+	# blame
+	my $tofind = scalar @entries;
+	my %idx;
+	for (my $i = 0; $i < @entries; $i++) {
+		$idx{"$basedir$entries[$i]{'name'}"} = $i;
+	}
+	my $commit = $hash_base;
+
+	if ($version eq 'diff-tree') {
+		open $fd, "-|", "$GIT rev-list $commit -- $basedir/" .
+			"| $GIT diff-tree -r -- $basedir/"
+			or die_error(undef, "Open git-rev-list or git-diff-tree failed");
+
+		while ($tofind > 0 && (my $line = <$fd>)) {
+			chomp $line;
+			my %diffinfo = parse_difftree_raw_line($line);
+			if ($diffinfo{'commit'}) {
+				$commit = $diffinfo{'commit'};
+			} else {
+				my $name = $diffinfo{'to_name'} || $diffinfo{'name'};
+				my $fullname = $basedir . $name;
+				if (!exists $entries[$idx{$fullname}]{'commit'}) {
+					$entries[$idx{$fullname}]{'commit'} = $commit;
+					$entries[$idx{$fullname}]{'added'} =
+						($diffinfo{'status'} eq 'A');
+					$tofind--;
+				}
+			}
+		}
+
+	} elsif ($version eq 'ls-tree') {
+
+	TOFIND:
+		while ($tofind > 0) {
+			# workaround the lack of git-ls-tree --stdin
+			open $fd, "-|", $GIT, "rev-list", "--max-count=100", "--remove-empty",
+				"$commit^", "--", $basedir
+					or die_error(undef, "Open git-rev-list failed");
+			my @revlist = map { chomp; $_ } <$fd>;
+			close $fd
+				or die_error(undef, "Reading git-rev-list failed");
+
+			# exit if no revisions found
+			last unless @revlist;
+
+			$/ = "\0";
+			my $prev_commit = $commit;
+		COMMIT:
+			foreach $commit (@revlist) {
+				# read tree
+				open my $fd, "-|", $GIT, "ls-tree", '-z', $commit, "--", $basedir
+					or die_error(undef, "Open git-ls-tree failed");
+				my @ls_tree = map {	chomp; $_ } <$fd>;
+				close $fd
+					or die_error(undef, "Reading git-ls-tree failed");
+				# parse tree
+				@ls_tree = map {
+					scalar parse_ls_tree_line($_, -z => 1);
+				} @ls_tree;
+
+				# find unchanged
+				my %unchanged;
+				my %changed;
+				foreach my $entry (@ls_tree) {
+					if (exists $idx{"$entry->{'name'}"} &&
+					    $entry->{'hash'} eq $entries[$idx{"$entry->{'name'}"}]{'hash'}) {
+						$unchanged{"$entry->{'name'}"} = 1;
+					}
+					if (exists $idx{"$entry->{'name'}"} &&
+					    $entry->{'hash'} ne $entries[$idx{"$entry->{'name'}"}]{'hash'}) {
+						$changed{"$entry->{'name'}"} = 1;
+					}
+				}
+
+				# find those with changed id, and those that vanished
+				foreach my $entry (@entries) {
+					my $fullname = "$basedir$entry->{'name'}";
+					if (!$unchanged{$fullname} &&
+					    !exists $entry->{'commit'}) {
+						$entry->{'commit'} = $prev_commit;
+						$entry->{'added'} =
+							!$changed{$fullname};
+						$tofind--;
+					}
+				}
+				last TOFIND if $tofind <= 0;
+
+			} continue {
+				$prev_commit = $commit;
+			}
+			$/ = "\n";
+
+			$commit = $revlist[-1];
+		}
+
+		# 'import' case: we attribute 'not found yet' to the last commit
+		if ($tofind > 0) {
+			foreach my $entry (@entries) {
+				if (!exists $entry->{'commit'}) {
+					$entry->{'commit'} = $commit;
+					$entry->{'added'} = 1;
+				}
+			}
+		}
+
+	} elsif ($version eq 'rev-list') {
+
+		foreach my $entry (@entries) {
+			my $fullname = "$basedir$entry->{'name'}";
+
+			open $fd, "-|", $GIT, "rev-list", "--max-count=2",
+				$hash_base, "--", $fullname
+				or die_error(undef, "Open git-rev-list failed");
+			my @revlist = map { chomp; $_ } <$fd>;
+			close $fd
+				or die_error(undef, "Reading git-rev-list failed");
+
+			if ($#revlist >= 0) {
+				$entry->{'commit'} = $revlist[0];
+				$entry->{'added'} = 1
+					if (@revlist == 1);
+			}
+		}
+
+	} else {
+		die_error(undef, "Unknown tree_blame version parameter");
+	}
+
+	# blame postprocessing
+	foreach my $entry (@entries) {
+		if (my %entry_co = parse_commit($entry->{'commit'})) {
+			$entry->{'title'}  = $entry_co{'title'};
+			$entry->{'epoch'}  = $entry_co{'author_epoch'};
+			$entry->{'author'} = $entry_co{'author_name'};
+			if ($entry_co{'author'} =~ m/<([^@>]+)\@/) {
+				$entry->{'author'} = $1;
+			}
+		}
+	}
+
+
+	### ----------------------------------------------
+	# page header
+	my $refs = git_get_references();
+	my $ref = format_ref_marker($refs, $hash_base);
+	git_header_html();
+	git_print_page_nav('tree_blame','', $hash_base, $hash, $hash_base);
+	git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
+	git_print_page_path($file_name, 'tree', $hash_base);
+	print "<div class=\"page_body\">\n";
+	print "<table class=\"tree blame\" cellspacing=\"0\">\n";
+
+	# sort and print table header
+	print "<thead>\n" .
+	      "<tr>\n";
+	print "<th>Commit</th>\n";
+	if ($order =~ m/^([-+]?)age$/) {
+		my $sign = "$1" ? "${1}1" : -1;
+		@entries = sort {
+			if (exists $a->{'commit'} && exists $b->{'commit'}) {
+				$sign*($a->{'epoch'} <=> $b->{'epoch'});
+			} else {
+				0;
+			}
+		} @entries;
+		print "<th>Date</th>\n";
+	} else {
+		print "<th>" .
+		      $cgi->a({-href => href(action=>$action, hash=>$hash,
+		                             hash_base=>$hash_base, file_name=>$file_name,
+		                             order=>'age'),
+		              -class => "header"}, "Date") .
+		      "</th>\n";
+	}
+	if ($order =~ m/^([-+]?)author$/) {
+		my $sign = "${1}1";
+		@entries = sort {
+			if (exists $a->{'commit'} && exists $b->{'commit'}) {
+				$sign*($a->{'author'} cmp $b->{'author'});
+			} else {
+				0;
+			}
+		} @entries;
+		print "<th>Author</th>\n";
+	} else {
+		print "<th>" .
+		      $cgi->a({-href => href(action=>$action, hash=>$hash,
+		                             hash_base=>$hash_base, file_name=>$file_name,
+		                             order=>'author'),
+		              -class => "header"}, "Author") .
+		      "</th>\n";
+	}
+	if ($order =~ m/^([-+]?)mode$/) {
+		my $sign = "${1}1";
+		@entries = sort {
+			if (exists $a->{'commit'} && exists $b->{'commit'}) {
+				$sign*($a->{'mode'} <=> $b->{'mode'});
+			} else {
+				0;
+			}
+		} @entries;
+		print "<th>Mode</th>\n";
+	} else {
+		print "<th>" .
+		      $cgi->a({-href => href(action=>$action, hash=>$hash,
+		                             hash_base=>$hash_base, file_name=>$file_name,
+		                             order=>'mode'),
+		              -class => "header"}, "Mode") .
+		      "</th>\n";
+	}
+	if ($order =~ m/^([-+]?)name$/) {
+		my $sign = "${1}1";
+		@entries = sort {
+			if (exists $a->{'commit'} && exists $b->{'commit'}) {
+				$sign*($a->{'name'} cmp $b->{'name'});
+			} else {
+				0;
+			}
+		} @entries;
+		print "<th>Filename</th>\n";
+	} else {
+		print "<th>" .
+		      $cgi->a({-href => href(action=>$action, hash=>$hash,
+		                             hash_base=>$hash_base, file_name=>$file_name,
+		                             order=>'name'),
+		              -class => "header"}, "Filename") .
+		      "</th>\n";
+	}
+	print <<HTML;
+<th>Link</th>
+</tr>
+</thead>
+<tbody>
+HTML
+
+	# print tree
+	my $alternate = 0;
+	foreach my $entry (@entries) {
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+
+		if ($entry->{'commit'}) {
+			print $cgi->start_td({-class => "sha1", -title => $entry->{'title'}}) .
+			      ($entry->{'added'} ? "<strike>" : "") .
+			      $cgi->a({-href => href(action=>"commit", hash=>$entry->{'commit'},
+			                             file_name=>$entry->{'name'})},
+			              esc_html(substr($entry->{'commit'}, 0, 8))) .
+			      ($entry->{'added'} ? "</strike>" : "") .
+			      "</td>";
+			my %ad = parse_date($entry->{'epoch'});
+			my (undef,undef,undef,undef,undef,$current_year) = gmtime();
+			my $date_str;
+			if ($ad{'year'} == 1900+$current_year) {
+				$date_str = sprintf "%3s % 2u %02d:%02d",
+					$ad{'month'}, $ad{'mday'}, $ad{'hour'}, $ad{'minute'};
+			} else {
+				$date_str = sprintf "%3s % 2u % 5d",
+					$ad{'month'}, $ad{'mday'}, $ad{'year'};
+			}
+			$date_str =~ s/ /&nbsp;/g;
+			print "<td>$date_str</td>\n";
+			print "<td>$entry->{'author'}</td>\n";
+		} else {
+			print "<td></td>\n" .
+			      "<td></td>\n" .
+			      "<td></td>\n";
+		}
+		git_print_tree_entry($entry, $basedir, $hash_base,
+		                     $have_blame && defined $hash_base);
+
+		print "</tr>\n";
+	}
+	print "</tbody>\n" .
+	      "</table>\n" .
+	      "</div>";
+	git_footer_html();
+}
+
 sub git_snapshot {
 
 	my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
-- 
1.4.1.1

^ permalink raw reply related

* [PATCH/RFC 0/4] gitweb: ViewVC-like "tree_blame" view
From: Jakub Narebski @ 2006-08-30 22:43 UTC (permalink / raw)
  To: git

In response to Thomas Glanzmann (or rather a friend of his) request, 
here is series of patches which adds to tree view information about 
when last time and by whom was given file or directory touched.  As git 
records "snapshot history" and commits are whole tree, and not have per 
file history, it means that one wants to know last commit counting from 
the top which has the same version of file or the same tree.  This is 
something similar to blame (aka. annotate) for file, hence naming such 
view "tree_blame".

Current implementation is done inefficiently directly in gitweb, and 
serves rather as a proof of concept than as a serious implementation to 
use.

This series of patches is based on 'next' (v1.4.2-g39ee2f6) equivalent, 
but with the commit 25691fbe6d02135d55dfc3a5180e29890dce1521
   gitweb: Use --git-dir parameter instead of setting $ENV{'GIT_DIR'}
reverted, as it needs git version post v1.4.2-rc2, while I use git 
version 1.4.1.1, as there are no i386 RPMS for 1.4.2 yet.

ViewCVS/ViewVC prints the last touch of a a file in a directory and you 
can tell it to sort after the date criteria:
http://faumachine.informatik.uni-erlangen.de/cgi-bin/viewcvs.cgi/mutt/?sortby=date#dirlist

-- 
Jakub Narebski
ShadeHawk on #git
Poland

^ permalink raw reply

* Re: [PATCH/RFC 0/4] gitweb: ViewVC-like "tree_blame" view
From: Jakub Narebski @ 2006-08-30 23:08 UTC (permalink / raw)
  To: git
In-Reply-To: <200608310043.12606.jnareb@gmail.com>

At the below URL (links of course _won't_ work)
  http://front.fuw.edu.pl/jnareb/tree_blame.html
is output of  "tree_blame" view. One can sort by Date (age), Author, Mode
and Filename. The date view was modelled after 'ls -l' view; the order of
columns probably should be changed to match it more closely.

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* Re: [PATCH/RFC 0/4] gitweb: ViewVC-like "tree_blame" view
From: Junio C Hamano @ 2006-08-31  0:00 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <ed55nu$62k$1@sea.gmane.org>

Jakub Narebski <jnareb@gmail.com> writes:

> At the below URL (links of course _won't_ work)
>   http://front.fuw.edu.pl/jnareb/tree_blame.html
> is output of  "tree_blame" view. One can sort by Date (age), Author, Mode
> and Filename. The date view was modelled after 'ls -l' view; the order of
> columns probably should be changed to match it more closely.

Just a quick impression.

 - "Mode" looks too wide for the information it conveys.  Path
   being a link already tells whether it is a tree, so the only
   information you would need is a bit and half (regular file,
   executable regular file, or symbolic link).

 - Spelling out Date in human readable form is fine but I suspect
   it would be nicer if it used YYYY-MM-DD / MM-DD HH:MM

 - I personally do not like blaming the last person who touched
   the file, but that is probably just me so it is fine.

 - "Commit" column does not seem to convey much useful
   information; I would suggest perhaps making Date and/or
   Author clickable to take the browser to the commit, but I do
   not understand what the strike-through is about...

 - Since the rightmost links have quite a lot of information
   these days, it _might_ be reasonable to make the main part
   2 lines, like so:

	date    author   mode    path
        short-commit-log-message

	date    author   mode    path
        short-commit-log-message

        ...

 - I wonder how expensive it is to generate this.

^ permalink raw reply

* Re: [PATCH/RFC 0/4] gitweb: ViewVC-like "tree_blame" view
From: Jakub Narebski @ 2006-08-31  0:18 UTC (permalink / raw)
  To: git
In-Reply-To: <7vfyfdg4fw.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano wrote:

> Jakub Narebski <jnareb@gmail.com> writes:
> 
>> At the below URL (links of course _won't_ work)
>>   http://front.fuw.edu.pl/jnareb/tree_blame.html
>> is output of  "tree_blame" view. One can sort by Date (age), Author, Mode
>> and Filename. The date view was modelled after 'ls -l' view; the order of
>> columns probably should be changed to match it more closely.
> 
> Just a quick impression.
> 
>  - "Mode" looks too wide for the information it conveys.  Path
>    being a link already tells whether it is a tree, so the only
>    information you would need is a bit and half (regular file,
>    executable regular file, or symbolic link).
> 
>  - Spelling out Date in human readable form is fine but I suspect
>    it would be nicer if it used YYYY-MM-DD / MM-DD HH:MM

Good idea.

>  - I personally do not like blaming the last person who touched
>    the file, but that is probably just me so it is fine.

I was going for something similar to the 'ls -l' look, with symbolic mode,
user ("blame" i.e. last person who touched the file for gitweb), date in
'ls -l' format but in UTC and in C locale, file name. Preceded by commit,
followed by related links (tree/blob, blame, history, raw).

>  - "Commit" column does not seem to convey much useful
>    information; I would suggest perhaps making Date and/or
>    Author clickable to take the browser to the commit, but I do
>    not understand what the strike-through is about...

Ooops, I forgot to explain this. Strike-through, which actually should be
using different color like red for example means that the file has 1-commit
long history, i.e. it just appeared in given commit.

BTW. commit link should show commit title on mouseover.

>  - Since the rightmost links have quite a lot of information
>    these days, it _might_ be reasonable to make the main part
>    2 lines, like so:
> 
>       date    author   mode    path
>         short-commit-log-message
> 
>       date    author   mode    path
>         short-commit-log-message
> 
>         ...

I'd rather have 1 line per entry, especially considering that one of key
features is ability to sort the "tree_blame" table by date.

>  - I wonder how expensive it is to generate this.

Quite expensive, especially for directories (trees) containing larger number
of files, but if I remember correctly not much longer than for example
summary or tags page. Speed is constrained by the forking, one per
file/tree in given directory, at least for now.

There are three implementations in git_tree_blame...

Of course the engine should be rewritten in C, made into git-blame-tree
(similar to git-blame).

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* Re: [PATCH/RFC 0/4] gitweb: ViewVC-like "tree_blame" view
From: Josef Weidendorfer @ 2006-08-31  0:47 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <ed59rk$hk0$1@sea.gmane.org>

On Thursday 31 August 2006 02:18, Jakub Narebski wrote:
> Ooops, I forgot to explain this. Strike-through, which actually should be
> using different color like red for example means that the file has 1-commit
> long history, i.e. it just appeared in given commit.

Seems useful. Perhaps better green. And red for...
(As you talk about "just appeared") What's about deleted files, similar
to attic in CVS? It could be quite useful to provide an alternate
tree-blame mode including deleted files; where the given commit
would specify the deletion.

Josef

^ permalink raw reply

* Re: [PATCH/RFC 0/4] gitweb: ViewVC-like "tree_blame" view
From: Jakub Narebski @ 2006-08-31  1:05 UTC (permalink / raw)
  To: git
In-Reply-To: <200608310247.05385.Josef.Weidendorfer@gmx.de>

Josef Weidendorfer wrote:

> On Thursday 31 August 2006 02:18, Jakub Narebski wrote:
>> Ooops, I forgot to explain this. Strike-through, which actually should be
>> using different color like red for example means that the file has
>> 1-commit long history, i.e. it just appeared in given commit.
> 
> Seems useful. Perhaps better green. And red for...
> (As you talk about "just appeared") What's about deleted files, similar
> to attic in CVS? It could be quite useful to provide an alternate
> tree-blame mode including deleted files; where the given commit
> would specify the deletion.

Deletion, or renaming.

Actually the currently used implementation (2-items max length history for
each file) doesn't allow for that. The other two implementations (you can
choose other 2 implementations by changing 'rev-list' in "my $version =
shift || 'rev-list'" to 'diff-tree' or 'ls-tree') can do this. I think it
would be perhaps easiest with git-ls-tree, if git-ls-tree would acquire
--stdin option, similar to git-diff-tree...

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* [PATCH] Trace into open fd and refactor tracing code.
From: Christian Couder @ 2006-08-31  6:42 UTC (permalink / raw)
  To: Junio Hamano; +Cc: git

Now if GIT_TRACE is set to an integer value greater than 1
and lower than 10, we interpret this as an open fd value
and we trace into it. Note that this behavior is not
compatible with the previous one.

We also trace whole messages using one write(2) call to
make sure messages from processes do net get mixed up in
the middle.

It's now possible to run the tests like this:

	GIT_TRACE=9 make test 9>/var/tmp/trace.log

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>

---
 Makefile       |    2 -
 cache.h        |    6 +++
 exec_cmd.c     |   18 +-------
 git.c          |   25 ++---------
 imap-send.c    |   25 -----------
 quote.c        |   32 ++++++++++++++
 quote.h        |    1 
 t/test-lib.sh  |    1 
 trace.c        |  125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 write_or_die.c |   25 +++++++++++
 10 files changed, 196 insertions(+), 64 deletions(-)

diff --git a/Makefile b/Makefile
index 48eb5f5..8c9de6b 100644
--- a/Makefile
+++ b/Makefile
@@ -260,7 +260,7 @@ LIB_OBJS = \
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
 	fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
-	write_or_die.o \
+	write_or_die.o trace.o \
 	alloc.o merge-file.o path-list.o unpack-trees.o help.o $(DIFF_OBJS)
 
 BUILTIN_OBJS = \
diff --git a/cache.h b/cache.h
index e26ab7d..9a700ee 100644
--- a/cache.h
+++ b/cache.h
@@ -401,6 +401,7 @@ extern char git_commit_encoding[MAX_ENCO
 
 extern int copy_fd(int ifd, int ofd);
 extern void write_or_die(int fd, const void *buf, size_t count);
+extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 
 /* Finish off pack transfer receiving end */
 extern int receive_unpack_pack(int fd[2], const char *me, int quiet, int);
@@ -426,4 +427,9 @@ extern struct commit *alloc_commit_node(
 extern struct tag *alloc_tag_node(void);
 extern void alloc_report(void);
 
+/* trace.c */
+extern int nfvasprintf(char **str, const char *fmt, va_list va);
+extern void trace_printf(const char *format, ...);
+extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
+
 #endif /* CACHE_H */
diff --git a/exec_cmd.c b/exec_cmd.c
index e30936d..5d6a124 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -97,26 +97,12 @@ int execv_git_cmd(const char **argv)
 		tmp = argv[0];
 		argv[0] = git_command;
 
-		if (getenv("GIT_TRACE")) {
-			const char **p = argv;
-			fputs("trace: exec:", stderr);
-			while (*p) {
-				fputc(' ', stderr);
-				sq_quote_print(stderr, *p);
-				++p;
-			}
-			putc('\n', stderr);
-			fflush(stderr);
-		}
+		trace_argv_printf(argv, -1, "trace: exec:");
 
 		/* execve() can only ever return if it fails */
 		execve(git_command, (char **)argv, environ);
 
-		if (getenv("GIT_TRACE")) {
-			fprintf(stderr, "trace: exec failed: %s\n",
-				strerror(errno));
-			fflush(stderr);
-		}
+		trace_printf("trace: exec failed: %s\n", strerror(errno));
 
 		argv[0] = tmp;
 	}
diff --git a/git.c b/git.c
index a01d195..0b78697 100644
--- a/git.c
+++ b/git.c
@@ -179,17 +179,9 @@ static int handle_alias(int *argcp, cons
 		if (!strcmp(alias_command, new_argv[0]))
 			die("recursive alias: %s", alias_command);
 
-		if (getenv("GIT_TRACE")) {
-			int i;
-			fprintf(stderr, "trace: alias expansion: %s =>",
-				alias_command);
-			for (i = 0; i < count; ++i) {
-				fputc(' ', stderr);
-				sq_quote_print(stderr, new_argv[i]);
-			}
-			fputc('\n', stderr);
-			fflush(stderr);
-		}
+		trace_argv_printf(new_argv, count,
+				  "trace: alias expansion: %s =>",
+				  alias_command);
 
 		new_argv = realloc(new_argv, sizeof(char*) *
 				   (count + *argcp + 1));
@@ -291,16 +283,7 @@ static void handle_internal_command(int 
 			prefix = setup_git_directory();
 		if (p->option & USE_PAGER)
 			setup_pager();
-		if (getenv("GIT_TRACE")) {
-			int j;
-			fprintf(stderr, "trace: built-in: git");
-			for (j = 0; j < argc; ++j) {
-				fputc(' ', stderr);
-				sq_quote_print(stderr, argv[j]);
-			}
-			putc('\n', stderr);
-			fflush(stderr);
-		}
+		trace_argv_printf(argv, argc, "trace: built-in: git");
 
 		exit(p->fn(argc, argv, prefix));
 	}
diff --git a/imap-send.c b/imap-send.c
index 65c71c6..6a52dbd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -110,7 +110,6 @@ static char *next_arg( char ** );
 
 static void free_generic_messages( message_t * );
 
-static int nfvasprintf( char **str, const char *fmt, va_list va );
 static int nfsnprintf( char *buf, int blen, const char *fmt, ... );
 
 
@@ -372,21 +371,6 @@ free_generic_messages( message_t *msgs )
 }
 
 static int
-git_vasprintf( char **strp, const char *fmt, va_list ap )
-{
-	int len;
-	char tmp[1024];
-
-	if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap )) < 0 || !(*strp = xmalloc( len + 1 )))
-		return -1;
-	if (len >= (int)sizeof(tmp))
-		vsprintf( *strp, fmt, ap );
-	else
-		memcpy( *strp, tmp, len + 1 );
-	return len;
-}
-
-static int
 nfsnprintf( char *buf, int blen, const char *fmt, ... )
 {
 	int ret;
@@ -399,15 +383,6 @@ nfsnprintf( char *buf, int blen, const c
 	return ret;
 }
 
-static int
-nfvasprintf( char **str, const char *fmt, va_list va )
-{
-	int ret = git_vasprintf( str, fmt, va );
-	if (ret < 0)
-		die( "Fatal: Out of memory\n");
-	return ret;
-}
-
 static struct {
 	unsigned char i, j, s[256];
 } rs;
diff --git a/quote.c b/quote.c
index e220dcc..f90091e 100644
--- a/quote.c
+++ b/quote.c
@@ -74,6 +74,38 @@ char *sq_quote(const char *src)
 	return buf;
 }
 
+char *sq_quote_argv(const char** argv, int count)
+{
+	char *buf, *to;
+	int i;
+	size_t len = 0;
+
+	/* Count argv if needed. */
+	if (count < 0) {
+		for (count = 0; argv[count]; count++)
+			; /* just counting */
+	}
+
+	/* Special case: no argv. */
+	if (!count)
+		return xcalloc(1,1);
+	
+	/* Get destination buffer length. */
+	for (i = 0; i < count; i++)
+		len += sq_quote_buf(NULL, 0, argv[i]) + 1;
+
+	/* Alloc destination buffer. */
+	to = buf = xmalloc(len + 1);
+
+	/* Copy into destination buffer. */
+	for (i = 0; i < count; ++i) {
+		*to++ = ' ';
+		to += sq_quote_buf(to, len, argv[i]);
+	}
+
+	return buf;
+}
+
 char *sq_dequote(char *arg)
 {
 	char *dst = arg;
diff --git a/quote.h b/quote.h
index fc5481e..a6c4611 100644
--- a/quote.h
+++ b/quote.h
@@ -31,6 +31,7 @@ #include <stdio.h>
 extern char *sq_quote(const char *src);
 extern void sq_quote_print(FILE *stream, const char *src);
 extern size_t sq_quote_buf(char *dst, size_t n, const char *src);
+extern char *sq_quote_argv(const char** argv, int count);
 
 /* This unwraps what sq_quote() produces in place, but returns
  * NULL if the input does not look like what sq_quote would have
diff --git a/t/test-lib.sh b/t/test-lib.sh
index b6d119a..695243e 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -28,7 +28,6 @@ unset GIT_DIR
 unset GIT_EXTERNAL_DIFF
 unset GIT_INDEX_FILE
 unset GIT_OBJECT_DIRECTORY
-unset GIT_TRACE
 unset SHA1_FILE_DIRECTORIES
 unset SHA1_FILE_DIRECTORY
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
diff --git a/trace.c b/trace.c
new file mode 100644
index 0000000..939fa76
--- /dev/null
+++ b/trace.c
@@ -0,0 +1,125 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
+ * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
+ * Copyright (C) 2006 Mike McCormack
+ * Copyright (C) 2006 Christian Couder 
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "cache.h"
+#include "quote.h"
+
+/* Stolen from "imap-send.c". */
+static int git_vasprintf(char **strp, const char *fmt, va_list ap)
+{
+	int len;
+	char tmp[1024];
+
+	if ((len = vsnprintf(tmp, sizeof(tmp), fmt, ap)) < 0 ||
+	    !(*strp = xmalloc(len + 1)))
+		return -1;
+	if (len >= (int)sizeof(tmp))
+		vsprintf(*strp, fmt, ap);
+	else
+		memcpy(*strp, tmp, len + 1);
+	return len;
+}
+
+/* Stolen from "imap-send.c". */
+int nfvasprintf(char **str, const char *fmt, va_list va)
+{
+	int ret = git_vasprintf(str, fmt, va);
+	if (ret < 0)
+		die("Fatal: Out of memory\n");
+	return ret;
+}
+
+/* Get a trace file descriptor from GIT_TRACE env variable. */
+static int get_trace_fd()
+{
+	char *trace = getenv("GIT_TRACE");
+
+	if (!trace || !strcmp(trace, "0") || !strcasecmp(trace," false"))
+		return 0;
+	if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
+		return STDERR_FILENO;
+	if (strlen(trace) == 1 && isdigit(*trace))
+		return atoi(trace);
+
+	fprintf(stderr, "What does '%s' for GIT_TRACE means ?\n", trace);
+	fprintf(stderr, "Defaulting to tracing on stderr...\n");
+	return STDERR_FILENO;
+}
+
+static const char err_msg[] = "Could not trace into fd given by "
+	"GIT_TRACE environment variable";
+
+void trace_printf(const char *format, ...)
+{
+	char *trace_str;
+	va_list rest;
+	int fd = get_trace_fd();
+
+	if (!fd)
+		return;
+
+	va_start(rest, format);
+	nfvasprintf(&trace_str, format, rest);
+	va_end(rest);
+
+	write_or_whine(fd, trace_str, strlen(trace_str), err_msg);
+
+	free(trace_str);
+}
+
+void trace_argv_printf(const char **argv, int count, const char *format, ...)
+{
+	char *argv_str, *format_str, *trace_str;
+	size_t argv_len, format_len, trace_len;
+	va_list rest;
+	int fd = get_trace_fd();
+
+	if (!fd)
+		return;
+
+	/* Get the argv string. */
+	argv_str = sq_quote_argv(argv, count);
+	argv_len = strlen(argv_str);
+
+	/* Get the formated string. */
+	va_start(rest, format);
+	nfvasprintf(&format_str, format, rest);
+	va_end(rest);
+
+	/* Allocate buffer for trace string. */
+	format_len = strlen(format_str);
+	trace_len = argv_len + format_len + 1; /* + 1 for \n */
+	trace_str = xmalloc(trace_len + 1);
+
+	/* Copy everything into the trace string. */
+	strncpy(trace_str, format_str, format_len);
+	strncpy(trace_str + format_len, argv_str, argv_len);
+	strcpy(trace_str + trace_len - 1, "\n");
+
+	write_or_whine(fd, trace_str, trace_len, err_msg);
+	
+	free(argv_str);
+	free(format_str);
+	free(trace_str);
+}
diff --git a/write_or_die.c b/write_or_die.c
index ab4cb8a..bfe4eeb 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -18,3 +18,28 @@ void write_or_die(int fd, const void *bu
 		p += written;
 	}
 }
+
+int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
+{
+	const char *p = buf;
+	ssize_t written;
+
+	while (count > 0) {
+		written = xwrite(fd, p, count);
+		if (written == 0) {
+			fprintf(stderr, "%s: disk full?\n", msg);
+			return 0;
+		}
+		else if (written < 0) {
+			if (errno == EPIPE)
+				exit(0);
+			fprintf(stderr, "%s: write error (%s)\n",
+				msg, strerror(errno));
+			return 0;
+		}
+		count -= written;
+		p += written;
+	}
+
+	return 1;
+}
-- 
1.4.2.g584f7-dirty

^ permalink raw reply related

* Re: [PATCH/RFC 0/4] gitweb: ViewVC-like "tree_blame" view
From: Thomas Glanzmann @ 2006-08-31  8:20 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Michael Gernoth
In-Reply-To: <200608310043.12606.jnareb@gmail.com>

Hello,

> In response to Thomas Glanzmann (or rather a friend of his) request,
> here is series of patches which adds to tree view information about
> when last time and by whom was given file or directory touched. 

thanks, I bounced the messages to him.

        Thomas

^ permalink raw reply

* Re: Problematic git pack
From: Sergio Callegari @ 2006-08-31  8:45 UTC (permalink / raw)
  To: git

What can I say... I had never seen before such an action at such a rapid 
pace following the indication of a potential problem.
Thanks Linus and Junio and everybody who might have contributed.
>   Junio could then generate a new pack with the one corrupted object 
>   fixed, which obviously meant that all the deltas now worked too.
>   
Excellent news...
>   This is my (probably final) analysis of the resulting differences.. ]
>
> On Wed, 30 Aug 2006, Junio C Hamano wrote:
> > 
> > Ok, I was going to attach the resurrected pack that should
> > contain everything your corrupt pack had, but it is a bit too
> > large, so I'll place it here [*1*].  Drop me a note when you
> > retrieved it, so that I can remove it.
>   
Junio, can you please send me privately details about [*1*] so I can 
retrieve the pack also?

I also have another question... (maybe it was answered in some previous 
thread on this list, in this case a pointer would be enough).
Now I am going to have the fixed archive and also a new archive, which I 
restarted from the latest working copy I had of my project.
Is there any way to automatically do real "surgery" to attach one to the 
other and get a single archive with all the history?
Obviously, if I try to change a commit object to modify its parents, its 
signature changes, so I need to modify its childs and so on, is this 
correct?
Alternatively I belive that grafts should be a way to go... I had never 
used them before, do all git tools support them? Particularly do they 
get pushed and pulled correctly?
> So the _real_ difference is literally just the one byte at offset 0151000 
> (decimal 53760) which in the fixed pack is 0x96, and in the corrupt pack 
> it is 0x94. That's a single-bit difference (bit #1 has been cleared).
>
>   
So, possibly, the alpha particle theory could be the plausible one in 
the end...
> Now, that makes me feel happy on one level, because it's almost certainly 
> a hardware problem - subtle memory corruption, or disk corruption that 
> happened when either reading or writing the image. Sergio may not be that 
> happy about it, of course.
>   
The bad thing is that I don't know which of my two machines (the laptop 
or the desktop) caused the issue!

> Finally, this also points out that the corrupted packs _can_ be fixed, but 
> I think Sergio was a bit lucky (to offset all the bad luck). Sergio still 
> had access to the original file that had had its object corrupted. 
Actually, this could possibly be a not so rare case... In my tree I had 
the development of some LaTeX documents and packages (code like, the 
really "precious" files) and a few binary objects (images and openoffice 
files mainly, by far less precious).
Since the binary objects were so much overwhelming in size with regard 
to the text ones, assuming a single error the probability of having it 
in a non-code object was much larger than that of having it in a 
precious code object. Also commit and tree objects should be much 
smaller than data objects.
This assumption is the reason which initally pushed me to ask help to 
try to unpack at least all the correct objects (one of my first 
questions was: does git unpack-objects die on the first error or is 
there a way to convince it to simply skip the wrong object (or the delta 
against a wrong object)...
If git unpack-objects can gain an option like --continue-on-errors and 
if checkout/reset can also get an option to do the same (i.e. in a tree 
with missing objects, checkout all that can be found), I believe that 
one is at a good point already...
Finally, having a command to create an object out of a single file 
(contrary of git cat-file) could help re-creating the missing objects...
> And it 
> took a fair amount of work, and some git hacking by somebody who really 
> understood git (Junio).
>
> Maybe we'll end up having some of that effort being useful and checked in, 
> and we'll eventually have more infrastructure for fixing these things, but 
> I suspect that in most cases, even a _single_ bit of corruption will 
> generally result in so much havoc that nobody should depend on that. It's 
> a lot better to have backups.
>
> 			Linus

^ permalink raw reply

* Re: problem with git-cvsserver
From: Marco Roeland @ 2006-08-31  9:03 UTC (permalink / raw)
  To: Martin Langhoff; +Cc: Junio C Hamano, aonghus, git, Johannes Schindelin
In-Reply-To: <46a038f90608301329n14df4dd2tb1563cc48662cd14@mail.gmail.com>

On Thursday August 31st 2006 at 08:29 uur Martin Langhoff wrote:

> [git-cvsserver and trouble with Perl DBI and SQLite]
> 
> Actually, just looking at my etch dev box, libdbd-sqlite-perl is
> 0.29-1 and sqlite is 2.8.16-1. Not sure if the difference is
> significant. Perhaps SQLite v3 has a different invocation / driver
> name?

Yes, SQLite v2 and SQLite v3 are different and binary incompatible.

However, on Debian 'etch' you can install packages for both versions
concurrently; most packages for SQLite have either a '2' or a '3' in the
name. Packages without the number use the "best current choice" which is
"3" in 'etch' at the moment but was "2" not too long ago.

So at this moment in Debian 'etch' SQLite3 is the default version and
calling

        my $dsn = 'dbi:SQLite:dbname=foo';

will use the SQLite3 driver internally, for which you must have
installed the "libdbd-sqlite3-perl" package. Just for the record, if
you'd wanted the older SQLite2 version you would install the
"libdbd-sqlite2-perl" package and from Perl call "my $dsn =
'dbi:SQLite2:dbname=foo';".

I'd guess that you were unfortunate enough to just install some packages
during the transition and now some parts look for the "2" version
and other parts for the "3" version. Probably just installing the
"libdbd-sqlite3-perl" package and upgrading the other sqlite packages
(from synaptic say to easily find them!) will probably cure your situation.

Incidentally I'd guess that in itself SQLite2 (so version 2) would also
function perfectly well for git-cvsserver (as would PostgreSQL or
MySQL), it's probably in this case just a slight version skew between
packages!
-- 
Marco Roeland

^ 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