Git development
 help / color / mirror / Atom feed
* Re: Proposal about --help options and man calls
From: David Kastrup @ 2007-07-18 23:28 UTC (permalink / raw)
  To: git
In-Reply-To: <7v644h715y.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano <gitster@pobox.com> writes:

> David Kastrup <dak@gnu.org> writes:
>
>> Frequently, git somecommand --help will call the man command to
>> display help pages.  I think that when it does so, it should pass the
>> value of the GIT_PAGER variable copied into the PAGER variable: the
>> paging happens on behalf of git here.
>
> Hmph.  Is that to help people who uses GIT_PAGER but no PAGER,
> or have both but set it differently (setting both and in the
> same way does not make much sense).  But what it means is that
> "git command --help" and "man git-command" would be paged
> differently.  I highly doubt it is really desirable.
>
> What's the reason to set GIT_PAGER and PAGER differently to begin
> with?  Can people give examples of the reason why?

If I call command --help, I don't want a pager barfing at me.  Never.
I have scrollback for that.  It is my choice when I page and when I
page not.  There are manual pages who go through 50 pages or so,
however.  There are commands that fundamentally are connected with a
pager.  man is, for example.  But most cases where git calls a pager
(and that includes his way of calling man without getting asked for it
explicitly) utterly surprise me.  So I set GIT_PAGER to cat and hoped
that it would get git to behave.  Sometimes it does, sometimes it
doesn't.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply

* [RFC PATCH] Re: Empty directories...
From: Linus Torvalds @ 2007-07-18 23:16 UTC (permalink / raw)
  To: Junio C Hamano, David Kastrup
  Cc: Matthieu Moy, Johannes Schindelin, Git Mailing List
In-Reply-To: <alpine.LFD.0.999.0707181444070.27353@woody.linux-foundation.org>


Gaah.

I'm a damn softie (and soft in the head too, for writing the code).

Ok, here's a trivial patch to start the ball rolling. I'm really not 
interested in taking this patch any further personally, but I'm hoping 
that maybe it can make somebody else who is actually _interested_ in 
trackign empty directories (hint hint) decide that it's a good enough 
start that they can fill in the details.

This really updates three different areas, which are nicely separated into 
three different files, so while it's one single patch, you can actually 
follow along the changes by just looking at the differences in each file, 
which directly translate to separate conceptual changes:

 - builtin-update-index.c

   This simply contains the changes to update the index file. As usual, 
   there are multiple different cases, and they boil down to:

	(a) No index entry existed at all previously. If so, a directory 
	    will first go through the "index_path()" logic, which tries to 
	    create a GITLINK entry for it, if the subdirectory is a git 
	    directory. However, the new thing is that if that fails, it 
	    will instead just create a fake empty tree entry for it, and 
	    set the index mode to S_IFDIR.

	(b) It was a gitlink entry before. It stays as a gitlink entry, 
	    even if it cannot be indexed, and a file/symlink entry in 
	    the working tree is a conflict error.

	(c) It was a empty directory entry before. A directory stays as an 
	    empty directory entry, and a file/symlink entry in the working 
	    tree is a conflict error.

   Somebody should check that we properly delete the directory entry if we 
   add a file under it, I honestly didn't bother to go through all the 
   logic. I *think* we do it correctly just thanks to all the previous 
   code for gitlinks. Whatever.

   What I'm trying to say is that the changes are fairly straightforward, 
   but if somebody decides to push this, they need to think about it a lot 
   more than I'm ready to right now.

 - read-cache.c: match the new index type with the filesystem.

   This is pretty damn obvious. A S_ISDIR() always matches, and nothing 
   else matches at all. 

 - unpack-trees.c: unpack empty directories not by unpacking them 
   recursively into the index, but by adding them directly to the index as 
   a S_IFDIR entry instead.

   This one almost certainly needs more work, in particular when merging 
   trees where one has an empty directory, and the other has files _in_ 
   that directory! But the trivial approach makes a simple "git read-tree"
   with an empty directory unpack it into the index as a S_IFDIR entry, so 
   now doing git-write-tree + git-read-tree should result in the original 
   index contents.

I think the patch itself is pretty simple, but the subtle interactions 
that flow out of this all are anything but. It may "just work" almost 
as-is, but quite frankly, I think people need to think about all the 
issues that can happen a lot!

So see this as a basis for further work. The "further work" may be pretty 
simple, or it may not be. I'm personally not that interested, but like my 
original "subprojects" series, hopefully somebody else ends up running 
with this (or alternatively just proving that trying to track empty 
directories is a total nightmare).

			Linus

---
 builtin-update-index.c |   33 +++++++++++++++++++++++----------
 read-cache.c           |    4 ++++
 unpack-trees.c         |   12 +++++++++---
 3 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/builtin-update-index.c b/builtin-update-index.c
index 509369e..2eb2a46 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -94,8 +94,16 @@ static int add_one_path(struct cache_entry *old, const char *path, int len, stru
 	fill_stat_cache_info(ce, st);
 	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
-	if (index_path(ce->sha1, path, st, !info_only))
-		return -1;
+	if (index_path(ce->sha1, path, st, !info_only)) {
+		/*
+		 * If we weren't able to index the directory as a GITLINK,
+		 * see if we can just add it as a plain directory instead.
+		 */
+		if (!S_ISDIR(st->st_mode))
+			return -1;
+		ce->ce_mode = htonl(S_IFDIR);
+		pretend_sha1_file(NULL, 0, OBJ_TREE, ce->sha1);
+	}
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
 	if (add_cache_entry(ce, option))
@@ -134,6 +142,11 @@ static int process_directory(const char *path, int len, struct stat *st)
 	/* Exact match: file or existing gitlink */
 	if (pos >= 0) {
 		struct cache_entry *ce = active_cache[pos];
+
+		/* Was it a directory before? */
+		if (S_ISDIR(ntohl(ce->ce_mode)))
+			return 0;
+
 		if (S_ISGITLINK(ntohl(ce->ce_mode))) {
 
 			/* Do nothing to the index if there is no HEAD! */
@@ -162,12 +175,8 @@ static int process_directory(const char *path, int len, struct stat *st)
 		return error("%s: is a directory - add individual files instead", path);
 	}
 
-	/* No match - should we add it as a gitlink? */
-	if (!resolve_gitlink_ref(path, "HEAD", sha1))
-		return add_one_path(NULL, path, len, st);
-
-	/* Error out. */
-	return error("%s: is a directory - add files inside instead", path);
+	/* No match - try to just add it as-is */
+	return add_one_path(NULL, path, len, st);
 }
 
 /*
@@ -178,8 +187,12 @@ static int process_file(const char *path, int len, struct stat *st)
 	int pos = cache_name_pos(path, len);
 	struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
 
-	if (ce && S_ISGITLINK(ntohl(ce->ce_mode)))
-		return error("%s is already a gitlink, not replacing", path);
+	if (ce) {
+		if (S_ISGITLINK(ntohl(ce->ce_mode)))
+			return error("%s is already a gitlink, not replacing", path);
+		if (S_ISDIR(ntohl(ce->ce_mode)))
+			return error("%s is already a directory entry, not replacing", path);
+	}
 
 	return add_one_path(ce, path, len, st);
 }
diff --git a/read-cache.c b/read-cache.c
index a363f31..d3d2cc0 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -142,6 +142,10 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 		    (has_symlinks || !S_ISREG(st->st_mode)))
 			changed |= TYPE_CHANGED;
 		break;
+	case S_IFDIR:
+		if (!S_ISDIR(st->st_mode))
+			changed |= TYPE_CHANGED;
+		return changed;
 	case S_IFGITLINK:
 		if (!S_ISDIR(st->st_mode))
 			changed |= TYPE_CHANGED;
diff --git a/unpack-trees.c b/unpack-trees.c
index 89dd279..22e452b 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -181,9 +181,13 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
 				any_dirs = 1;
 				parse_tree(tree);
 				subposns[i] = create_tree_entry_list(tree);
-				posns[i] = posns[i]->next;
-				src[i + o->merge] = o->df_conflict_entry;
-				continue;
+
+				/* If it wasn't empty, recurse into it */
+				if (subposns[i]) {
+					posns[i] = posns[i]->next;
+					src[i + o->merge] = o->df_conflict_entry;
+					continue;
+				}
 			}
 
 			if (!o->merge)
@@ -197,6 +201,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
 
 			ce = xcalloc(1, ce_size);
 			ce->ce_mode = create_ce_mode(posns[i]->mode);
+			if (posns[i]->directory)
+				ce->ce_mode = htonl(S_IFDIR);
 			ce->ce_flags = create_ce_flags(baselen + pathlen,
 						       ce_stage);
 			memcpy(ce->name, base, baselen);

^ permalink raw reply related

* Re: Proposal about --help options and man calls
From: Junio C Hamano @ 2007-07-18 23:16 UTC (permalink / raw)
  To: David Kastrup; +Cc: git
In-Reply-To: <85y7hdwfds.fsf@lola.goethe.zz>

David Kastrup <dak@gnu.org> writes:

> Frequently, git somecommand --help will call the man command to
> display help pages.  I think that when it does so, it should pass the
> value of the GIT_PAGER variable copied into the PAGER variable: the
> paging happens on behalf of git here.

Hmph.  Is that to help people who uses GIT_PAGER but no PAGER,
or have both but set it differently (setting both and in the
same way does not make much sense).  But what it means is that
"git command --help" and "man git-command" would be paged
differently.  I highly doubt it is really desirable.

What's the reason to set GIT_PAGER and PAGER differently to
begin with?  Can people give examples of the reason why?

By the way, I would understand if there is a request to honor
GIT_LESS environment and if it exists export its value instead
of FRSX as LESS when we spawn the pager.  As some people do not
want S nor R for other programs but do want them for colored git
output that tends to consist of long lines.  This is a slightly
related topic, but different from the issue you raised in your
message.

^ permalink raw reply

* Re: Empty directories...
From: David Kastrup @ 2007-07-18 23:13 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Matthieu Moy, Johannes Schindelin, git
In-Reply-To: <alpine.LFD.0.999.0707181444070.27353@woody.linux-foundation.org>

Linus Torvalds <torvalds@linux-foundation.org> writes:

> On Wed, 18 Jul 2007, David Kastrup wrote:
>>
>> You said that you are not inclined to do any work in that area
>> since it does not touch _your_ use cases (well, at least not to a
>> degree that you consider worth bothering about) but that is no reason
>> to get into ridiculous arguments about other usage.
>
> How hard is it for you to admit that I also said "please send in a
> patch".

Yup, that was one sentence in about 5 pages of bile.  In contrast,
Junio gave a good overview of the technical areas involved here, and
estimates about what to do there best.

That's a constructive way to encite somebody to delve into the task
and try to see whether he can come up with something.

But 5 pages of what amounts to "you are an idiot, come up with a
patch" is not leading anywhere.

> I don't need it. You do. You do the work. I'm just explaining why
> the work hasn't been done.

No, you are _defending_ why the work has not been done.  This
rationalizing around the bush is a waste of time.  You probably have
spent quite more time with your venting than Junio did with his
technical analysis, and the latter has been much more helpful.

So why waste all that time and adrenaline on something where you have
already said all you consider relevant?  The arguments don't get any
stronger by shouting, and it is not like you are inconvenienced in any
manner if somebody takes a look at the matter.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply

* [PATCH] Proposal for git-svn
From: Benoit SIGOURE @ 2007-07-18 21:07 UTC (permalink / raw)
  To: git

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

Hello,

I'm importing many SVN repositories in Git and I ran across a problem:
ufloat.h has mode 120000but is not a link

I've read the code and checked-out the revision where the problem  
occured and it turns out that some stupid user commited a broken  
symlink and I think that's where the problem came from.  I'm  
proposing the following trivial change to let git-svn clone continue  
its work:

diff --git a/git-svn.perl b/git-svn.perl
index 01c3904..a82baf4 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2555,8 +2555,8 @@ sub close_file {
                 sysseek($fh, 0, 0) or croak $!;
                 if ($fb->{mode_b} == 120000) {
                         sysread($fh, my $buf, 5) == 5 or croak $!;
-                       $buf eq 'link ' or die "$path has mode 120000",
-                                              "but is not a link\n";
+                       $buf eq 'link ' or warn "$path has mode 120000",
+                                              " but is not a link\n";
                 }
                 defined(my $pid = open my $out,'-|') or die "Can't  
fork: $!\n";
                 if (!$pid) {

(I also added a whitespace because "120000but" does not look good :D)
I checked out the problematic revision in git and I see the broken  
symlink just like in SVN so I assume this change is correct.

-- 
Benoit Sigoure aka Tsuna
EPITA Research and Development Laboratory



[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 186 bytes --]

^ permalink raw reply related

* Proposal about --help options and man calls
From: David Kastrup @ 2007-07-18 21:50 UTC (permalink / raw)
  To: git


Frequently, git somecommand --help will call the man command to
display help pages.  I think that when it does so, it should pass the
value of the GIT_PAGER variable copied into the PAGER variable: the
paging happens on behalf of git here.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply

* Re: Empty directories...
From: Linus Torvalds @ 2007-07-18 21:45 UTC (permalink / raw)
  To: David Kastrup; +Cc: Matthieu Moy, Johannes Schindelin, git
In-Reply-To: <85644hxujp.fsf@lola.goethe.zz>



On Wed, 18 Jul 2007, David Kastrup wrote:
>
> You said that you are not inclined to do any work in that area
> since it does not touch _your_ use cases (well, at least not to a
> degree that you consider worth bothering about) but that is no reason
> to get into ridiculous arguments about other usage.

How hard is it for you to admit that I also said "please send in a patch".

I don't need it. You do. You do the work. I'm just explaining why the work 
hasn't been done.

		Linus

^ permalink raw reply

* [PATCH] Force listingblocks to be monospaced in manpages
From: Julian Phillips @ 2007-07-18 21:33 UTC (permalink / raw)
  To: Fredrik Tolf; +Cc: git
In-Reply-To: <m3k5sxiiib.fsf@pc7.dolda2000.com>

For the html output we can use a stylesheet to make sure that the
listingblocks are presented in a monospaced font.  For the manpages do
it manually by inserting a ".ft C" before and ".ft" after the block in
question.

In order for these roff commands to get through to the manpage they
have to be element encoded to prevent quoting.

Signed-off-by: Julian Phillips <julian@quantumfyre.co.uk>
---

On Wed, 18 Jul 2007, Fredrik Tolf wrote:

> Julian Phillips <julian@quantumfyre.co.uk> writes:
>
>> On Wed, 18 Jul 2007, Fredrik Tolf wrote:
>>
>>> I often read manpages using a `man -t whatever | ggv -' command, since
>>> I like how it is rendered in PostScript. However, it turns out that
>>> some things in the Git manpages don't really render very well using
>>> that method. For example, in the git-rebase manpage, there are two
>>> history graphs that look like this when reading the manpages normally
>>> in a terminal:
>>
>> It's a proportional font issue ...
>>
>> Running "groff -Tps -f C -man $(man -w git-rebase) | ggv -" should
>> display it correctly.  (The "-f C" being the part the man doesn't do)
>>
>> No idea how to make it use that font by default though ... not even
>> sure if you can put that kind of information into a man page?
>
> Well, if it were a "pure" manpage, I'd try to use pic(1) to do it for
> the PS version, but I don't suspect asciidoc has a similar feature. I
> don't actually know, though -- again, I know virtually nothing about
> asciidoc.

How about this?

Seems to work for me - but I'm not an asciidoc/docbook/roff expert ...

 Documentation/asciidoc.conf |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 6b6220d..d54fe29 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -27,7 +27,13 @@ ifdef::backend-docbook[]
 [listingblock]
 <example><title>{title}</title>
 <literallayout>
+ifdef::doctype-manpage[]
+&#10;&#x2302;ft C&#10;
+endif::doctype-manpage[]
 |
+ifdef::doctype-manpage[]
+&#10;&#x2302;ft&#10;
+endif::doctype-manpage[]
 </literallayout>
 {title#}</example>
 endif::backend-docbook[]
-- 
1.5.2.2

^ permalink raw reply related

* Re: Empty directories...
From: David Kastrup @ 2007-07-18 21:37 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Matthieu Moy, Johannes Schindelin, git
In-Reply-To: <alpine.LFD.0.999.0707181004330.27353@woody.linux-foundation.org>

Linus Torvalds <torvalds@linux-foundation.org> writes:

> On Wed, 18 Jul 2007, Matthieu Moy wrote:
>> 
>> If you checkout a branch, create an empty directory in this branch
>> (probably a placeholder, either for future versionned files, or for
>> generated files), you cannot tell git "this empty directory is in this
>> branch, but not in other ones" without adding a file in it.
>
> Right. Which is the suggested setup: add an empty ".gitignore" file
> to the directory, and you're done.

That implies that every directory in a versioned tree will exclusively
be created under manual and conscious control.  Not by running some
installer or script, unpacking some archive and so on.  But if every
content on a disk was created and put there under manual control of
the disk owner, we could still get along with floppy disks quite fine.
In practice, much more content gets sent around and juggled than what
is under immediate supervision of the user.

This is getting silly: you don't need to pull out rabbits out of your
head.  You said that you are not inclined to do any work in that area
since it does not touch _your_ use cases (well, at least not to a
degree that you consider worth bothering about) but that is no reason
to get into ridiculous arguments about other usage.  No code will come
of that.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply

* Re: [PATCH] Implement git commit as a builtin.
From: Carlos Rica @ 2007-07-18 21:27 UTC (permalink / raw)
  To: Kristian Høgsberg; +Cc: git
In-Reply-To: <11847863792344-git-send-email-krh@redhat.com>

2007/7/18, Kristian Høgsberg <krh@redhat.com>:
> +       if (buffer) {
> +               len = strip_lines(buffer, len);
> +
> +               if (fwrite(buffer, 1, len, fp) < len)
> +                       die("could not write commit template: %s\n",
> +                           strerror(errno));
> +       }
....
> +       len = strip_lines(buffer, len);
> +
> +       if (message_is_empty(buffer, len))
> +               die("* no commit message?  aborting commit.");
> +

Hi Kristian, you could call to the new stripspace() function
in builtin-stripspace.c, to reduce code in your file. The only
thing you should consider is that the new stripspace()
removes always the last '\n' in the file (if any), so you have to
add it when you need.

I sent a patch to change the name of read_pipe with read_fd
(not accepted yet), and to terminate the buffer with NUL
(although perhaps you don't need that), and to allow the
function to receive buffer NULL or size 0.
Your version was a lot different, therefore you would
need to set buffer and size before calling it
and free the buffer if reading failed after that:
http://article.gmane.org/gmane.comp.version-control.git/52835

Now I need to create the new file editor.c to group at
least three functions related with reading and editing text.
Those also will reduce the size of your code and also
can be reused from builtin-tag.c: launch_editor,
read_path (as read_file someone said), and stripspace.

The first problem now should be how to write a version for
launch_editor() which both builtins (yours and mine) could share,
because the references in your launch_editor() related to commits,
removed already in my version for git-tag.

I did this job trying to make easier your porting by moving
those parts shared with builtin-tag.c outside your code
and generating discussion on them, so you
can concentrate just in the specific parts and reuse
the rest. I hope those can help you.

---
Carlos Rica

^ permalink raw reply

* USE_CURL_MULTI and http.h in general
From: Daniel Barkalow @ 2007-07-18 21:19 UTC (permalink / raw)
  To: git

The changes I made to the commit-walker fetch code turn out not to work 
with USE_CURL_MULTI, and I'm not at all clear on how to deal with it. I 
think the current code for USE_CURL_MULTI also can't work for having 
multiple things in the same executable using http.h, which will be a 
problem for having both ls-remote and http-fetch as part of builtin-fetch, 
as well as eventually calling http-push without an exec from builtin-push. 
Is there anyone around who understands it?

	-Daniel
*This .sig left intentionally blank*

^ permalink raw reply

* Re: Wrong time in git-log when using right/ timezone
From: Junio C Hamano @ 2007-07-18 20:57 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Peter Hartlich, git
In-Reply-To: <alpine.LFD.0.999.0707180948080.27353@woody.linux-foundation.org>

Linus Torvalds <torvalds@linux-foundation.org> writes:

> Wow. I just tried:
>
> 	git show --raw --date=local
>
> with TZ=CET and TZ=right/Europe/Berlin respectively.
> ...
> ie apparently Berlin is in a timezone of its own that is roughly one 
> minute and 23 seconds away from CET.
>
> What the *heck*?
>
> I really don't think this is git that is confused: I get the exact same 
> thing with "date" too:
>
> 	[torvalds@woody git]$ TZ=right/Europe/Berlin date ; TZ=CET date
> 	Wed Jul 18 18:52:25 CEST 2007
> 	Wed Jul 18 18:52:48 CEST 2007
>
> so it really *is* the tzdata that says that Berlin is not +0200, and it's 
> not even +0159, it's something really strange with fractional minutes away 
> from UTC.
>
> What can I say? "Those wacky Germans - they have a wonderful sense of 
> humor"?

No, I do not think the wackiness is from Germans.

Using right/ perhaps without realizing the differences between
TZ=right/Europe/Berlin and TZ=Europe/Berlin is probably the
source of confusion.

I do not offhand know what role "leap second adjustment" should
play in the context of converting from Unix time we store in git
commit objects to human readable role.  As far as I understand,
the returned timestamp from time(2), which we record in commit
objects, is already "leap second adjusted".

^ permalink raw reply

* Re: Rsync fetch?
From: Alex Riesen @ 2007-07-18 20:53 UTC (permalink / raw)
  To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0707181226020.14596@iabervon.org>

Daniel Barkalow, Wed, Jul 18, 2007 18:37:21 +0200:
> The only thing that's totally missing at this point from my builtin-fetch 
> is rsync. Do we still care? Any takers for actually implementing it?

Maybe pull the rsync parts from the old fetch, put them into a script
and call it? Not very simple, as it looks. Maybe not worth the effort.

^ permalink raw reply

* Re: Wrong time in git-log when using right/ timezone
From: Alex Riesen @ 2007-07-18 20:29 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Peter Hartlich, git
In-Reply-To: <alpine.LFD.0.999.0707180948080.27353@woody.linux-foundation.org>

Linus Torvalds, Wed, Jul 18, 2007 19:01:31 +0200:
> I get:
> 
> 	commit 9dfdf14b3805e89aa2782458bda15b3dfae24c09
> 	Author: Junio C Hamano <gitster@pobox.com>
> 	Date:   Mon Jul 16 01:41:17 2007
> 
> for the CET case, but for right/Europe/Berlin I get:
> 
> 	commit 9dfdf14b3805e89aa2782458bda15b3dfae24c09
> 	Author: Junio C Hamano <gitster@pobox.com>
> 	Date:   Mon Jul 16 01:39:54 2007
> 
> ie apparently Berlin is in a timezone of its own that is roughly one 
> minute and 23 seconds away from CET.
> 
> What the *heck*?
> 

Maybe the "right/" timezones are just a little too right?

http://ecos.sourceware.org/ml/libc-alpha/2000-12/msg00068.html

^ permalink raw reply

* Re: Manpage rendering faults
From: Fredrik Tolf @ 2007-07-18 20:06 UTC (permalink / raw)
  To: git
In-Reply-To: <Pine.LNX.4.64.0707182024440.734@reaper.quantumfyre.co.uk>

Julian Phillips <julian@quantumfyre.co.uk> writes:

> On Wed, 18 Jul 2007, Fredrik Tolf wrote:
>
>> I often read manpages using a `man -t whatever | ggv -' command, since
>> I like how it is rendered in PostScript. However, it turns out that
>> some things in the Git manpages don't really render very well using
>> that method. For example, in the git-rebase manpage, there are two
>> history graphs that look like this when reading the manpages normally
>> in a terminal:
>
> It's a proportional font issue ...
>
> Running "groff -Tps -f C -man $(man -w git-rebase) | ggv -" should
> display it correctly.  (The "-f C" being the part the man doesn't do)
>
> No idea how to make it use that font by default though ... not even
> sure if you can put that kind of information into a man page?

Well, if it were a "pure" manpage, I'd try to use pic(1) to do it for
the PS version, but I don't suspect asciidoc has a similar feature. I
don't actually know, though -- again, I know virtually nothing about
asciidoc.

Fredrik Tolf

^ permalink raw reply

* Re: [PATCH] Implement git commit as a builtin.
From: Nicolas Pitre @ 2007-07-18 19:43 UTC (permalink / raw)
  To: Kristian Høgsberg; +Cc: git
In-Reply-To: <11847863792344-git-send-email-krh@redhat.com>

[-- Attachment #1: Type: TEXT/PLAIN, Size: 816 bytes --]

On Wed, 18 Jul 2007, Kristian Høgsberg wrote:

> ---
> Here's another update on the work in progress.  At this point, the C
> version is almost complete, there's only a few issues left (look for
> FIXME in builtin-commit.c).  I've added a commit test case which should
> be split out in a patch on its own, but the good news is that it
> successfully exercises most of the command line options and the C version
> passes.
> 
> My plan for the remainder of the work is still to wrap up the last few
> pieces of functionality and then start taking this big patch apart in a
> number of more manageable pieces.  However, the bulk of this work will
> still be a big patch that removes git-commit.sh and adds builtin-commit
> in one swoop.

Please move git-commit.sh to contrib/examples instead of deleting it.


Nicolas

^ permalink raw reply

* Re: Manpage rendering faults
From: Julian Phillips @ 2007-07-18 19:29 UTC (permalink / raw)
  To: Fredrik Tolf; +Cc: git
In-Reply-To: <m3odi9im5y.fsf@pc7.dolda2000.com>

On Wed, 18 Jul 2007, Fredrik Tolf wrote:

> I often read manpages using a `man -t whatever | ggv -' command, since
> I like how it is rendered in PostScript. However, it turns out that
> some things in the Git manpages don't really render very well using
> that method. For example, in the git-rebase manpage, there are two
> history graphs that look like this when reading the manpages normally
> in a terminal:

It's a proportional font issue ...

Running "groff -Tps -f C -man $(man -w git-rebase) | ggv -" should display 
it correctly.  (The "-f C" being the part the man doesn't do)

No idea how to make it use that font by default though ... not even sure 
if you can put that kind of information into a man page?

-- 
Julian

  ---
Dinner suggestion #302 (Hacker's De-lite):
 	1 tin imported Brisling sardines in tomato sauce
 	1 pouch Chocolate Malt Carnation Instant Breakfast
 	1 carton milk

^ permalink raw reply

* [PATCH] Implement git commit as a builtin.
From: Kristian Høgsberg @ 2007-07-18 19:19 UTC (permalink / raw)
  To: git; +Cc: Kristian Høgsberg

---
Here's another update on the work in progress.  At this point, the C
version is almost complete, there's only a few issues left (look for
FIXME in builtin-commit.c).  I've added a commit test case which should
be split out in a patch on its own, but the good news is that it
successfully exercises most of the command line options and the C version
passes.

My plan for the remainder of the work is still to wrap up the last few
pieces of functionality and then start taking this big patch apart in a
number of more manageable pieces.  However, the bulk of this work will
still be a big patch that removes git-commit.sh and adds builtin-commit
in one swoop.

Kristian


 Makefile              |    9 +-
 builtin-add.c         |   14 +-
 builtin-commit-tree.c |  120 +++++---
 builtin-commit.c      |  746 +++++++++++++++++++++++++++++++++++++++++++++++++
 builtin.h             |    3 +-
 cache.h               |    3 +-
 color.c               |   18 +-
 color.h               |    4 +-
 commit.h              |    9 +
 git-commit.sh         |  658 -------------------------------------------
 git.c                 |    3 +-
 mktag.c               |    8 +-
 sha1_file.c           |   44 ++-
 t/t7800-commit.sh     |  126 +++++++++
 wt-status.c           |   86 +++---
 wt-status.h           |    4 +
 16 files changed, 1066 insertions(+), 789 deletions(-)
 create mode 100644 builtin-commit.c
 delete mode 100755 git-commit.sh
 create mode 100644 t/t7800-commit.sh

diff --git a/Makefile b/Makefile
index 0f75955..967d5a5 100644
--- a/Makefile
+++ b/Makefile
@@ -198,7 +198,7 @@ BASIC_LDFLAGS =
 
 SCRIPT_SH = \
 	git-bisect.sh git-checkout.sh \
-	git-clean.sh git-clone.sh git-commit.sh \
+	git-clean.sh git-clone.sh \
 	git-fetch.sh \
 	git-ls-remote.sh \
 	git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
@@ -257,7 +257,7 @@ EXTRA_PROGRAMS =
 BUILT_INS = \
 	git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
 	git-get-tar-commit-id$X git-init$X git-repo-config$X \
-	git-fsck-objects$X git-cherry-pick$X \
+	git-fsck-objects$X git-cherry-pick$X git-status$X\
 	$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
 # what 'all' will build and 'install' will install, in gitexecdir
@@ -332,6 +332,7 @@ BUILTIN_OBJS = \
 	builtin-check-attr.o \
 	builtin-checkout-index.o \
 	builtin-check-ref-format.o \
+	builtin-commit.o \
 	builtin-commit-tree.o \
 	builtin-count-objects.o \
 	builtin-describe.o \
@@ -367,7 +368,6 @@ BUILTIN_OBJS = \
 	builtin-rev-parse.o \
 	builtin-revert.o \
 	builtin-rm.o \
-	builtin-runstatus.o \
 	builtin-shortlog.o \
 	builtin-show-branch.o \
 	builtin-stripspace.o \
@@ -791,9 +791,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
 	chmod +x $@+ && \
 	mv $@+ $@
 
-git-status: git-commit
-	$(QUIET_GEN)cp $< $@+ && mv $@+ $@
-
 gitweb/gitweb.cgi: gitweb/gitweb.perl
 	$(QUIET_GEN)rm -f $@ $@+ && \
 	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
diff --git a/builtin-add.c b/builtin-add.c
index 1591171..bcd796d 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -8,6 +8,7 @@
 #include "dir.h"
 #include "exec_cmd.h"
 #include "cache-tree.h"
+#include "run-command.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "commit.h"
@@ -148,6 +149,13 @@ static int git_add_config(const char *var, const char *value)
 	return git_default_config(var, value);
 }
 
+int interactive_add(void)
+{
+	const char *argv[2] = { "add--interactive", NULL };
+
+	return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
 static struct lock_file lock_file;
 
 static const char ignore_warning[] =
@@ -167,11 +175,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 			add_interactive++;
 	}
 	if (add_interactive) {
-		const char *args[] = { "add--interactive", NULL };
-
-		if (add_interactive != 1 || argc != 2)
+		if (argc != 2)
 			die("add --interactive does not take any parameters");
-		execv_git_cmd(args);
+		interactive_add();
 		exit(1);
 	}
 
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index ccbcbe3..bb20470 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -20,17 +20,11 @@ static void init_buffer(char **bufp, unsigned int *sizep)
 	*sizep = 0;
 }
 
-static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
+static void add_chunk(char **bufp, unsigned int *sizep, const char *data, int len)
 {
-	char one_line[2048];
-	va_list args;
-	int len;
 	unsigned long alloc, size, newsize;
 	char *buf;
 
-	va_start(args, fmt);
-	len = vsnprintf(one_line, sizeof(one_line), fmt, args);
-	va_end(args);
 	size = *sizep;
 	newsize = size + len + 1;
 	alloc = (size + 32767) & ~32767;
@@ -41,7 +35,19 @@ static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
 		*bufp = buf;
 	}
 	*sizep = newsize - 1;
-	memcpy(buf + size, one_line, len);
+	memcpy(buf + size, data, len);
+}
+
+static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
+{
+	char one_line[2048];
+	va_list args;
+	int len;
+
+	va_start(args, fmt);
+	len = vsnprintf(one_line, sizeof(one_line), fmt, args);
+	va_end(args);
+	add_chunk(bufp, sizep, one_line, len);
 }
 
 static void check_valid(unsigned char *sha1, enum object_type expect)
@@ -81,39 +87,16 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int cmd_commit_tree(int argc, const char **argv, const char *prefix)
+const unsigned char *
+create_commit(const unsigned char *tree_sha1,
+	      unsigned char parent_sha1[][20], int parents,
+	      const char *author_info, const char *committer_info,
+	      const char *message, int length)
 {
-	int i;
-	int parents = 0;
-	unsigned char tree_sha1[20];
-	unsigned char commit_sha1[20];
-	char comment[1000];
+	static unsigned char commit_sha1[20];
+	int encoding_is_utf8, i;
 	char *buffer;
 	unsigned int size;
-	int encoding_is_utf8;
-
-	git_config(git_default_config);
-
-	if (argc < 2)
-		usage(commit_tree_usage);
-	if (get_sha1(argv[1], tree_sha1))
-		die("Not a valid object name %s", argv[1]);
-
-	check_valid(tree_sha1, OBJ_TREE);
-	for (i = 2; i < argc; i += 2) {
-		const char *a, *b;
-		a = argv[i]; b = argv[i+1];
-		if (!b || strcmp(a, "-p"))
-			usage(commit_tree_usage);
-
-		if (parents >= MAXPARENT)
-			die("Too many parents (%d max)", MAXPARENT);
-		if (get_sha1(b, parent_sha1[parents]))
-			die("Not a valid object name %s", b);
-		check_valid(parent_sha1[parents], OBJ_COMMIT);
-		if (new_parent(parents))
-			parents++;
-	}
 
 	/* Not having i18n.commitencoding is the same as having utf-8 */
 	encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
@@ -130,26 +113,71 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 		add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i]));
 
 	/* Person/date information */
-	add_buffer(&buffer, &size, "author %s\n", git_author_info(1));
-	add_buffer(&buffer, &size, "committer %s\n", git_committer_info(1));
+	add_buffer(&buffer, &size, "author %s\n", author_info);
+	add_buffer(&buffer, &size, "committer %s\n", committer_info);
 	if (!encoding_is_utf8)
 		add_buffer(&buffer, &size,
 				"encoding %s\n", git_commit_encoding);
 	add_buffer(&buffer, &size, "\n");
 
 	/* And add the comment */
-	while (fgets(comment, sizeof(comment), stdin) != NULL)
-		add_buffer(&buffer, &size, "%s", comment);
+	add_chunk(&buffer, &size, message, length);
 
 	/* And check the encoding */
 	buffer[size] = '\0';
 	if (encoding_is_utf8 && !is_utf8(buffer))
 		fprintf(stderr, commit_utf8_warn);
 
-	if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) {
-		printf("%s\n", sha1_to_hex(commit_sha1));
-		return 0;
+	if (!write_sha1_file(buffer, size, commit_type, commit_sha1))
+		return commit_sha1;
+
+	return NULL;
+}
+
+int cmd_commit_tree(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	int parents = 0;
+	unsigned char tree_sha1[20];
+	char *buffer;
+	const unsigned char *commit_sha1;
+	unsigned long length;
+
+	git_config(git_default_config);
+
+	if (argc < 2)
+		usage(commit_tree_usage);
+	if (get_sha1(argv[1], tree_sha1))
+		die("Not a valid object name %s", argv[1]);
+
+	check_valid(tree_sha1, OBJ_TREE);
+	for (i = 2; i < argc; i += 2) {
+		const char *a, *b;
+		a = argv[i]; b = argv[i+1];
+		if (!b || strcmp(a, "-p"))
+			usage(commit_tree_usage);
+
+		if (parents >= MAXPARENT)
+			die("Too many parents (%d max)", MAXPARENT);
+		if (get_sha1(b, parent_sha1[parents]))
+			die("Not a valid object name %s", b);
+		check_valid(parent_sha1[parents], OBJ_COMMIT);
+		if (new_parent(parents))
+			parents++;
 	}
-	else
+
+	if (read_fd(0, &buffer, &length))
+		die("Could not read commit message from standard input");
+
+	commit_sha1 = create_commit(tree_sha1,
+				    parent_sha1, parents,
+				    git_author_info(1),	    
+				    git_committer_info(1),	    
+				    buffer, length);
+
+	if (!commit_sha1)
 		return 1;
+
+	printf("%s\n", sha1_to_hex(commit_sha1));
+	return 0;
 }
diff --git a/builtin-commit.c b/builtin-commit.c
new file mode 100644
index 0000000..198d5af
--- /dev/null
+++ b/builtin-commit.c
@@ -0,0 +1,746 @@
+/*
+ * Builtin "git commit"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "cache.h"
+#include "cache-tree.h"
+#include "builtin.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "commit.h"
+#include "revision.h"
+#include "wt-status.h"
+#include "run-command.h"
+#include "refs.h"
+#include "log-tree.h"
+
+static const char builtin_commit_usage[] =
+	"[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]";
+
+static unsigned char head_sha1[20], merge_head_sha1[20];
+static struct commit *use_message_commit;
+static const char commit_editmsg[] = "COMMIT_EDITMSG";
+static struct lock_file lock_file;
+
+enum option_type {
+    OPTION_NONE,
+    OPTION_STRING,
+    OPTION_INTEGER,
+    OPTION_LAST,
+};
+
+struct option {
+    enum option_type type;
+    const char *long_name;
+    char short_name;
+    void *value;
+};
+
+static int scan_options(const char ***argv, struct option *options)
+{
+	const char *value, *eq;
+	int i;
+
+	if (**argv == NULL)
+		return 0;
+	if ((**argv)[0] != '-')
+		return 0;
+	if (!strcmp(**argv, "--"))
+		return 0;
+
+	value = NULL;
+	for (i = 0; options[i].type != OPTION_LAST; i++) {
+		if ((**argv)[1] == '-') {
+			if (!prefixcmp(options[i].long_name, **argv + 2)) {
+				if (options[i].type != OPTION_NONE)
+					value = *++(*argv);
+				goto match;
+			}
+
+			eq = strchr(**argv + 2, '=');
+			if (eq && options[i].type != OPTION_NONE &&
+			    !strncmp(**argv + 2, 
+				     options[i].long_name, eq - **argv - 2)) {
+				value = eq + 1;
+				goto match;
+			}
+		}
+
+		if ((**argv)[1] == options[i].short_name) {
+			if ((**argv)[2] == '\0') {
+				if (options[i].type != OPTION_NONE)
+					value = *++(*argv);
+				goto match;
+			}
+
+			if (options[i].type != OPTION_NONE) {
+				value = **argv + 2;
+				goto match;
+			}
+		}
+	}
+
+	usage(builtin_commit_usage);
+
+ match:
+	switch (options[i].type) {
+	case OPTION_NONE:
+		*(int *)options[i].value = 1;
+		break;
+	case OPTION_STRING:
+		if (value == NULL)
+			die("option %s requires a value.", (*argv)[-1]);
+		*(const char **)options[i].value = value;
+		break;
+	case OPTION_INTEGER:
+		if (value == NULL)
+			die("option %s requires a value.", (*argv)[-1]);
+		*(int *)options[i].value = atoi(value);
+		break;
+	default:
+		assert(0);
+	}
+
+	(*argv)++;
+
+	return 1;
+}
+
+static char *logfile, *force_author, *message;
+static char *edit_message, *use_message;
+static int all, edit_flag, also, interactive, only, no_verify, amend, signoff;
+static int quiet, verbose, untracked_files;
+
+static int no_edit, initial_commit, in_merge;
+const char *only_include_assumed;
+
+static struct option commit_options[] = {
+	{ OPTION_STRING, "file", 'F', (void *) &logfile },
+	{ OPTION_NONE, "all", 'a', &all },
+	{ OPTION_STRING, "author", 0, (void *) &force_author },
+	{ OPTION_NONE, "edit", 0, &edit_flag },
+	{ OPTION_NONE, "include", 'i', &also },
+	{ OPTION_NONE, "interactive", 0, &interactive },
+	{ OPTION_NONE, "only", 'o', &only },
+	{ OPTION_STRING, "message", 'm', &message },
+	{ OPTION_NONE, "no-verify", 'n', &no_verify },
+	{ OPTION_NONE, "amend", 0, &amend },
+	{ OPTION_STRING, "reedit-message", 'c', &edit_message },
+	{ OPTION_STRING, "reuse-message", 'C', &use_message },
+	{ OPTION_NONE, "signoff", 's', &signoff },
+	{ OPTION_NONE, "quiet", 'q', &quiet },
+	{ OPTION_NONE, "verbose", 'v', &verbose },
+	{ OPTION_NONE, "untracked-files", 0, &untracked_files },
+	{ OPTION_LAST },
+};
+
+/* FIXME: Taken from builtin-add, should be shared. */
+
+static void update_callback(struct diff_queue_struct *q,
+			    struct diff_options *opt, void *cbdata)
+{
+	int i, verbose;
+
+	verbose = *((int *)cbdata);
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		const char *path = p->one->path;
+		switch (p->status) {
+		default:
+			die("unexpacted diff status %c", p->status);
+		case DIFF_STATUS_UNMERGED:
+		case DIFF_STATUS_MODIFIED:
+			add_file_to_cache(path, verbose);
+			break;
+		case DIFF_STATUS_DELETED:
+			remove_file_from_cache(path);
+			if (verbose)
+				printf("remove '%s'\n", path);
+			break;
+		}
+	}
+}
+
+static void
+add_files_to_cache(int fd, const char **files, const char *prefix)
+{
+	struct rev_info rev;
+
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.prune_data = get_pathspec(prefix, files);
+	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = update_callback;
+	rev.diffopt.format_callback_data = &verbose;
+
+	run_diff_files(&rev, 0);
+
+	if (write_cache(fd, active_cache, active_nr) || close(fd))
+		die("unable to write new index file");
+}
+
+static char *
+prepare_index(const char **files, const char *prefix)
+{
+	int fd;
+	struct tree *tree;
+	struct lock_file *next_index_lock;
+
+	fd = hold_locked_index(&lock_file, 1);
+	if (read_cache() < 0)
+		die("index file corrupt");
+
+	if (all) {
+		add_files_to_cache(fd, files, NULL);
+		return lock_file.filename;
+	} else if (also) {
+		add_files_to_cache(fd, files, prefix);
+		return lock_file.filename;
+	}
+
+	if (interactive)
+		interactive_add();
+
+	if (*files == NULL) {
+		/* Commit index as-is. */
+		rollback_lock_file(&lock_file);
+		return get_index_file();
+	}
+
+	/*
+	 * FIXME: Warn on unknown files.  Shell script does
+	 *
+	 *   commit_only=`git-ls-files --error-unmatch -- "$@"`
+	 */
+
+	/*
+	 * FIXME: shell script does
+	 *
+	 *   git-read-tree --index-output="$TMP_INDEX" -i -m HEAD
+	 *
+	 * which warns about unmerged files in the index.
+	 */
+
+	/* update the user index file */
+	add_files_to_cache(fd, files, prefix);
+
+	tree = parse_tree_indirect(head_sha1);
+	if (!tree)
+		die("failed to unpack HEAD tree object");
+	if (read_tree(tree, 0, NULL))
+		die("failed to read HEAD tree object");
+
+	/* Uh oh, abusing lock_file to create a garbage collected file */
+	next_index_lock = xmalloc(sizeof(*next_index_lock));
+	fd = hold_lock_file_for_update(next_index_lock,
+				       git_path("next-index-%d", getpid()), 1);
+	add_files_to_cache(fd, files, prefix);
+
+	return next_index_lock->filename;
+}
+
+static int strip_lines(char *buffer, int len)
+{
+	int blank_lines, i, j;
+	char *eol;
+
+	blank_lines = 1;
+	for (i = 0, j = 0; i < len; i++) {
+		if (blank_lines > 0 && buffer[i] == '#') {
+			eol = strchr(buffer + i, '\n');
+			if (!eol)
+				break;
+
+			i = eol - buffer;
+			continue;
+		}
+
+		if (buffer[i] == '\n') {
+			blank_lines++;
+			if (blank_lines > 1)
+				continue;
+		} else {
+			if (blank_lines > 2)
+				buffer[j++] = '\n';
+			blank_lines = 0;
+		}
+
+		buffer[j++] = buffer[i];
+	}
+
+	if (buffer[j - 1] != '\n')
+               buffer[j++] = '\n';
+
+	return j;
+}
+
+static int run_status(FILE *fp, const char *index_file)
+{
+	struct wt_status s;
+
+	wt_status_prepare(&s);
+
+	if (amend) {
+		s.amend = 1;
+		s.reference = "HEAD^1";
+	}
+	s.verbose = verbose;
+	s.untracked = untracked_files;
+	s.index_file = index_file;
+	s.fp = fp;
+
+	wt_status_print(&s);
+
+	return s.commitable;
+}
+
+static const char sign_off_header[] = "Signed-off-by: ";
+
+static int prepare_log_message(const char *index_file)
+{
+	char *buffer = NULL;
+	struct stat statbuf;
+	int commitable;
+	unsigned long len;
+	FILE *fp;
+
+	if (message) {
+		buffer = message;
+		len = strlen(message);
+	} else if (logfile && !strcmp(logfile, "-")) {
+		if (isatty(0))
+			fprintf(stderr, "(reading log message from standard input)\n");
+		if (read_fd(0, &buffer, &len))
+			die("could not read log from standard input");
+	} else if (logfile) {
+		if (read_path(logfile, &buffer, &len))
+			die("could not read log file '%s': %s",
+			    logfile, strerror(errno));
+	} else if (use_message) {
+		/* FIXME: encoding */
+		buffer = strstr(use_message_commit->buffer, "\n\n");
+		if (!buffer || buffer[2] == '\0')
+			die("commit has empty message");
+		buffer += 2;
+		len = strlen(buffer);
+	} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
+		if (read_path(git_path("MERGE_MSG"), &buffer, &len))
+			die("could not read MERGE_MSG: %s", strerror(errno));
+	} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
+		if (read_path(git_path("SQUASH_MSG"), &buffer, &len))
+			die("could not read SQUASH_MSG: %s", strerror(errno));
+	}
+
+	fp = fopen(git_path(commit_editmsg), "w");
+	if (fp == NULL)
+		die("could not open %s\n", git_path(commit_editmsg));
+		
+	if (buffer) {
+		len = strip_lines(buffer, len);
+
+		if (fwrite(buffer, 1, len, fp) < len)
+			die("could not write commit template: %s\n",
+			    strerror(errno));
+	}
+
+	if (signoff) {
+		const char *info, *bol;
+
+		info = git_committer_info(1);
+		if (buffer) {
+			bol = strrchr(buffer + len - 1, '\n');
+			if (!bol || prefixcmp(bol, sign_off_header))
+				fprintf(fp, "\n");
+		}
+		fprintf(fp, "Signed-off-by: %s\n", git_committer_info(1));
+	}
+
+	if (in_merge && !no_edit) {
+		fprintf(fp,
+			"#\n"
+			"# It looks like you may be committing a MERGE.\n"
+			"# If this is not correct, please remove the file\n"
+			"#	%s\n"
+			"# and try again.\n"
+			"#\n",
+			git_path("MERGE_HEAD"));
+	}
+
+	fprintf(fp,
+		"\n"
+		"# Please enter the commit message for your changes.\n"
+		"# (Comment lines starting with '#' will not be included)\n");
+	if (only_include_assumed)
+		fprintf(fp, "# %s\n", only_include_assumed);
+
+	commitable = run_status(fp, index_file);
+
+	fclose(fp);
+
+	return commitable;
+}
+
+struct commit_info_strings {
+	const char *header, *name_env, *email_env, *date_env;
+} author_info_strings = {
+	"\nauthor ",
+	"GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_AUTHOR_DATE"
+}, committer_info_strings = {
+	"\ncommitter ",
+	"GIT_COMMITTER_NAME", "GIT_COMMITER_EMAIL", "GIT_COMMITTER_DATE"
+};
+
+static char *determine_info(struct commit_info_strings *strings)
+{
+	char *p, *eol;
+	char *name = NULL, *email = NULL, *date = NULL;
+
+	if (use_message) {
+		p = strstr(use_message_commit->buffer, strings->header);
+		if (!p)
+			die("invalid commit: %s\n", use_message);
+		p += strlen(strings->header);
+		eol = strchr(p, '\n');
+		if (!eol)
+			die("invalid commit: %s\n", use_message);
+
+		return xstrndup(p, eol - p);
+	} else if (force_author && strings == &author_info_strings) {
+		const char *eoname = strstr(force_author, " <");
+		const char *eomail = strchr(force_author, '>');
+
+		if (!eoname || !eomail)
+			die("malformed --author parameter\n");
+		name = xstrndup(force_author, eoname - force_author);
+		email = xstrndup(eoname + 2, eomail - eoname - 2);
+	}
+
+	if (name == NULL)
+		name = getenv(strings->name_env);
+	if (email == NULL)
+		email = getenv(strings->email_env);
+	if (date == NULL)
+		date = getenv(strings->date_env);
+
+	return xstrdup(fmt_ident(name, email, date, 1));
+}
+
+static void parse_and_validate_options(const char ***argv)
+{
+	int f = 0;
+
+	git_config(git_status_config);
+
+	(*argv)++;
+	while (scan_options(argv, commit_options))
+		;
+
+	if (logfile || message || use_message)
+		no_edit = 1;
+
+	if (get_sha1("HEAD", head_sha1))
+		initial_commit = 1;
+
+	if (!get_sha1("MERGE_HEAD", merge_head_sha1))
+		in_merge = 1;
+
+	/* Sanity check options */
+	if (amend && initial_commit)
+		die("You have nothing to amend.");
+	if (amend && in_merge)
+		die("You are in the middle of a merger -- cannot amend.");
+
+	if (use_message)
+		f++;
+	if (edit_message)
+		f++;
+	if (logfile)
+		f++;
+	if (amend)
+		f++;
+	if (f > 1)
+		die("Only one of -c/-C/-F/--amend can be used.");
+	if (message && f > 0)
+		die("Option -m cannot be combined with -c/-C/-F/--amend.");
+	if (edit_message)
+		use_message = edit_message;
+	if (amend)
+		use_message = "HEAD";
+	if (use_message) {
+		unsigned char sha1[20];
+
+		if (get_sha1(use_message, sha1))
+			die("could not lookup commit %s", use_message);
+		use_message_commit = lookup_commit(sha1);
+		if (!use_message_commit || parse_commit(use_message_commit))
+			die("could not parse commit %s", use_message);
+	}
+
+	if (also && only)
+		die("Only one of --include/--only can be used.");
+	if (!*argv && (also || (only && !amend)))
+		die("No paths with --include/--only does not make sense.");
+	if (!*argv && only && amend)
+		only_include_assumed = "Clever... amending the last one with dirty index.";
+	if (*argv && !also && !only) {
+		only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
+		also = 0;
+	}
+
+	if (all && interactive)
+		die("Cannot use -a, --interactive or -i at the same time.");
+	else if (all && **argv)
+		die("Paths with -a does not make sense.");
+	else if (interactive && **argv)
+		die("Paths with --interactive does not make sense.");
+}
+
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+	const char *index_file;
+	int commitable;
+
+	parse_and_validate_options(&argv);
+
+	index_file = prepare_index(argv, prefix);
+
+	commitable = run_status(stdout, index_file);
+
+	rollback_lock_file(&lock_file);
+
+	return commitable ? 0 : 1;
+}
+
+static void launch_editor(const char *path)
+{
+	const char *editor, *terminal;
+	struct child_process child;
+	const char *args[3];
+
+	editor = getenv("VISUAL");
+	if (!editor)
+		editor = getenv("EDITOR");
+
+	terminal = getenv("TERM");
+	if (!editor && (!terminal || !strcmp(terminal, "dumb"))) {
+		fprintf(stderr, 
+			"Terminal is dumb but no VISUAL nor EDITOR defined.\n"
+			"Please supply the commit log message using either\n"
+			"-m or -F option.  A boilerplate log message has\n"
+			"been prepared in $GIT_DIR/COMMIT_EDITMSG\n");
+		exit(1);
+	}
+
+	if (!editor)
+		editor = "vi";
+	    
+	memset(&child, 0, sizeof(child));
+	child.argv = args;
+	args[0] = editor;
+	args[1] = path;
+	args[2] = NULL;
+
+	if (run_command(&child))
+		die("could not launch editor %s.", editor);
+}
+
+static int message_is_empty(const char *buffer, int len)
+{
+	static const char signed_off_by[] = "Signed-off-by: ";
+	const char *nl;
+	int eol, i;
+
+	for (i = 0; i < len; i++) {
+		nl = memchr(buffer + i, '\n', len - i);
+		if (nl)
+			eol = nl - buffer;
+		else
+			eol = len;
+
+		if (strlen(signed_off_by) <= eol - i &&
+		    !prefixcmp(buffer + i, signed_off_by)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(buffer[i++]))
+			    return 0;
+	}
+
+	return 1;
+}
+
+static int run_hook(const char *index_file, const char *name, const char *arg)
+{
+	struct child_process hook;
+	const char *argv[3], *env[2];
+	char index[PATH_MAX];
+
+	argv[0] = git_path("hooks/%s", name);
+	argv[1] = arg;
+	argv[2] = NULL;
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+	env[0] = index;
+	env[1] = NULL;
+
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static void print_summary(const char *prefix, const unsigned char *sha1)
+{
+	struct rev_info rev;
+	struct commit *commit;
+
+	commit = lookup_commit(sha1);
+	if (!commit)
+		die("couldn't look up newly created commit\n");
+	if (!commit || parse_commit(commit))
+		die("could not parse newly created commit");
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.abbrev = 0;
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	rev.commit_format = get_commit_format("format:%h: %s");
+	rev.always_show_header = 1;
+		
+	printf("Created %scommit ", initial_commit ? "initial " : "");
+
+	log_tree_commit(&rev, commit);
+}
+
+#define MAXPARENT (16)
+static unsigned char parent_sha1[MAXPARENT][20];
+
+int cmd_commit(int argc, const char **argv, const char *prefix)
+{
+	int parent_count = 0;
+	unsigned long len;
+	char *buffer;
+	const char *index_file, *reflog_msg;
+	const unsigned char *commit_sha1;
+	struct ref_lock *ref_lock;
+
+	parse_and_validate_options(&argv);
+
+	index_file = prepare_index(argv, prefix);
+
+	if (run_hook(index_file, "pre-commit", NULL))
+		exit(1);
+
+	if (!prepare_log_message(index_file)) {
+		run_status(stdout, index_file);
+		unlink(commit_editmsg);
+		return 1;
+	}
+
+	if (!no_edit)
+		launch_editor(git_path(commit_editmsg));
+
+	if (run_hook(index_file, "commit-msg", commit_editmsg))
+		exit(1);
+
+	if (read_path(git_path(commit_editmsg), &buffer, &len))
+		die("could not read commit message file '%s': %s",
+		    git_path(commit_editmsg), strerror(errno));
+
+	len = strip_lines(buffer, len);
+
+	if (message_is_empty(buffer, len))
+		die("* no commit message?  aborting commit.");
+
+	/* Determine parents */
+	if (initial_commit) {
+		reflog_msg = "commit (initial)"; 
+		parent_count = 0;
+	} else if (amend) {
+		struct commit_list *c;
+		struct commit *commit;
+		int i = 0;
+
+		reflog_msg = "commit (amend)";
+		commit = lookup_commit(head_sha1);
+		if (!commit || parse_commit(commit))
+			die("could not parse HEAD commit");
+
+		for (c = commit->parents; c; c = c->next) {
+			hashcpy(parent_sha1[i++], c->item->object.sha1);
+			if (i == MAXPARENT)
+				die("Too many parents (%d max)", MAXPARENT);
+		}
+		parent_count = i;
+	} else if (in_merge) {
+		/* FIXME: how are merges with more than two parents handled? */
+		reflog_msg = "commit (merge)";
+		hashcpy(parent_sha1[0], head_sha1);
+		hashcpy(parent_sha1[1], merge_head_sha1);
+		parent_count = 2;
+	} else {
+		reflog_msg = "commit";
+		hashcpy(parent_sha1[0], head_sha1);
+		parent_count = 1;
+	}
+
+	read_cache_from(index_file);
+	active_cache_tree = cache_tree();
+	if (cache_tree_update(active_cache_tree,
+			      active_cache, active_nr, 0, 0) < 0)
+		die("Error building trees");
+
+	commit_sha1 = create_commit(active_cache_tree->sha1,
+				    parent_sha1, parent_count,
+				    determine_info(&author_info_strings),
+				    determine_info(&committer_info_strings),
+				    buffer, len);
+		       
+	ref_lock = lock_any_ref_for_update("HEAD",
+					   initial_commit ? NULL : head_sha1,
+					   0);
+	if (!ref_lock)
+		die("cannot lock HEAD ref");
+	if (write_ref_sha1(ref_lock, commit_sha1, reflog_msg) < 0)
+		die("cannot update HEAD ref");
+
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+
+	if (lock_file.filename[0] && commit_locked_index(&lock_file))
+		die("failed to write new index");
+
+	/*
+	 * FIXME:
+	 *	if test -d "$GIT_DIR/rr-cache"
+	 *	then
+	 *		git-rerere
+	 *	fi
+	 */
+
+	run_hook(index_file, "post-commit", NULL);
+
+	if (!quiet)
+		print_summary(prefix, commit_sha1);
+
+	return 0;
+}
diff --git a/builtin.h b/builtin.h
index da4834c..7f395da 100644
--- a/builtin.h
+++ b/builtin.h
@@ -23,6 +23,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
+extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(int argc, const char **argv, const char *prefix);
@@ -63,10 +64,10 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_revert(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
-extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
 extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 5e7381e..0403ada 100644
--- a/cache.h
+++ b/cache.h
@@ -245,7 +245,8 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat
 extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
+extern int read_fd(int fd, char** return_buf, unsigned long* return_size);
+extern int read_path(const char *path, char** return_buf, unsigned long* return_size);
 extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
diff --git a/color.c b/color.c
index 09d82ee..124ba33 100644
--- a/color.c
+++ b/color.c
@@ -135,39 +135,39 @@ int git_config_colorbool(const char *var, const char *value)
 	return git_config_bool(var, value);
 }
 
-static int color_vprintf(const char *color, const char *fmt,
+static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
 		va_list args, const char *trail)
 {
 	int r = 0;
 
 	if (*color)
-		r += printf("%s", color);
-	r += vprintf(fmt, args);
+		r += fprintf(fp, "%s", color);
+	r += vfprintf(fp, fmt, args);
 	if (*color)
-		r += printf("%s", COLOR_RESET);
+		r += fprintf(fp, "%s", COLOR_RESET);
 	if (trail)
-		r += printf("%s", trail);
+		r += fprintf(fp, "%s", trail);
 	return r;
 }
 
 
 
-int color_printf(const char *color, const char *fmt, ...)
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
 {
 	va_list args;
 	int r;
 	va_start(args, fmt);
-	r = color_vprintf(color, fmt, args, NULL);
+	r = color_vfprintf(fp, color, fmt, args, NULL);
 	va_end(args);
 	return r;
 }
 
-int color_printf_ln(const char *color, const char *fmt, ...)
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
 {
 	va_list args;
 	int r;
 	va_start(args, fmt);
-	r = color_vprintf(color, fmt, args, "\n");
+	r = color_vfprintf(fp, color, fmt, args, "\n");
 	va_end(args);
 	return r;
 }
diff --git a/color.h b/color.h
index 88bb8ff..6809800 100644
--- a/color.h
+++ b/color.h
@@ -6,7 +6,7 @@
 
 int git_config_colorbool(const char *var, const char *value);
 void color_parse(const char *var, const char *value, char *dst);
-int color_printf(const char *color, const char *fmt, ...);
-int color_printf_ln(const char *color, const char *fmt, ...);
+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
 
 #endif /* COLOR_H */
diff --git a/commit.h b/commit.h
index a313b53..a07e379 100644
--- a/commit.h
+++ b/commit.h
@@ -122,4 +122,13 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
 		int depth, int shallow_flag, int not_shallow_flag);
 
 int in_merge_bases(struct commit *, struct commit **, int);
+
+const unsigned char *
+create_commit(const unsigned char *tree_sha1,
+	      unsigned char parent_sha1[][20], int parents,
+	      const char *author_info, const char *committer_info,
+	      const char *message, int length);
+
+int interactive_add(void);
+
 #endif /* COMMIT_H */
diff --git a/git-commit.sh b/git-commit.sh
deleted file mode 100755
index 5547a02..0000000
--- a/git-commit.sh
+++ /dev/null
@@ -1,658 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-# Copyright (c) 2006 Junio C Hamano
-
-USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
-SUBDIRECTORY_OK=Yes
-. git-sh-setup
-require_work_tree
-
-git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
-
-case "$0" in
-*status)
-	status_only=t
-	;;
-*commit)
-	status_only=
-	;;
-esac
-
-refuse_partial () {
-	echo >&2 "$1"
-	echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
-	exit 1
-}
-
-THIS_INDEX="$GIT_DIR/index"
-NEXT_INDEX="$GIT_DIR/next-index$$"
-rm -f "$NEXT_INDEX"
-save_index () {
-	cp -p "$THIS_INDEX" "$NEXT_INDEX"
-}
-
-run_status () {
-	# If TMP_INDEX is defined, that means we are doing
-	# "--only" partial commit, and that index file is used
-	# to build the tree for the commit.  Otherwise, if
-	# NEXT_INDEX exists, that is the index file used to
-	# make the commit.  Otherwise we are using as-is commit
-	# so the regular index file is what we use to compare.
-	if test '' != "$TMP_INDEX"
-	then
-		GIT_INDEX_FILE="$TMP_INDEX"
-		export GIT_INDEX_FILE
-	elif test -f "$NEXT_INDEX"
-	then
-		GIT_INDEX_FILE="$NEXT_INDEX"
-		export GIT_INDEX_FILE
-	fi
-
-	case "$status_only" in
-	t) color= ;;
-	*) color=--nocolor ;;
-	esac
-	git-runstatus ${color} \
-		${verbose:+--verbose} \
-		${amend:+--amend} \
-		${untracked_files:+--untracked}
-}
-
-trap '
-	test -z "$TMP_INDEX" || {
-		test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
-	}
-	rm -f "$NEXT_INDEX"
-' 0
-
-################################################################
-# Command line argument parsing and sanity checking
-
-all=
-also=
-interactive=
-only=
-logfile=
-use_commit=
-amend=
-edit_flag=
-no_edit=
-log_given=
-log_message=
-verify=t
-quiet=
-verbose=
-signoff=
-force_author=
-only_include_assumed=
-untracked_files=
-while case "$#" in 0) break;; esac
-do
-	case "$1" in
-	-F|--F|-f|--f|--fi|--fil|--file)
-		case "$#" in 1) usage ;; esac
-		shift
-		no_edit=t
-		log_given=t$log_given
-		logfile="$1"
-		shift
-		;;
-	-F*|-f*)
-		no_edit=t
-		log_given=t$log_given
-		logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
-		shift
-		;;
-	--F=*|--f=*|--fi=*|--fil=*|--file=*)
-		no_edit=t
-		log_given=t$log_given
-		logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		shift
-		;;
-	-a|--a|--al|--all)
-		all=t
-		shift
-		;;
-	--au=*|--aut=*|--auth=*|--autho=*|--author=*)
-		force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		shift
-		;;
-	--au|--aut|--auth|--autho|--author)
-		case "$#" in 1) usage ;; esac
-		shift
-		force_author="$1"
-		shift
-		;;
-	-e|--e|--ed|--edi|--edit)
-		edit_flag=t
-		shift
-		;;
-	-i|--i|--in|--inc|--incl|--inclu|--includ|--include)
-		also=t
-		shift
-		;;
-	--int|--inte|--inter|--intera|--interac|--interact|--interacti|\
-	--interactiv|--interactive)
-		interactive=t
-		shift
-		;;
-	-o|--o|--on|--onl|--only)
-		only=t
-		shift
-		;;
-	-m|--m|--me|--mes|--mess|--messa|--messag|--message)
-		case "$#" in 1) usage ;; esac
-		shift
-		log_given=m$log_given
-		if test "$log_message" = ''
-		then
-		    log_message="$1"
-		else
-		    log_message="$log_message
-
-$1"
-		fi
-		no_edit=t
-		shift
-		;;
-	-m*)
-		log_given=m$log_given
-		if test "$log_message" = ''
-		then
-		    log_message=`expr "z$1" : 'z-m\(.*\)'`
-		else
-		    log_message="$log_message
-
-`expr "z$1" : 'z-m\(.*\)'`"
-		fi
-		no_edit=t
-		shift
-		;;
-	--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
-		log_given=m$log_given
-		if test "$log_message" = ''
-		then
-		    log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		else
-		    log_message="$log_message
-
-`expr "z$1" : 'zq-[^=]*=\(.*\)'`"
-		fi
-		no_edit=t
-		shift
-		;;
-	-n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
-	--no-verify)
-		verify=
-		shift
-		;;
-	--a|--am|--ame|--amen|--amend)
-		amend=t
-		log_given=t$log_given
-		use_commit=HEAD
-		shift
-		;;
-	-c)
-		case "$#" in 1) usage ;; esac
-		shift
-		log_given=t$log_given
-		use_commit="$1"
-		no_edit=
-		shift
-		;;
-	--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
-	--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
-	--reedit-messag=*|--reedit-message=*)
-		log_given=t$log_given
-		use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		no_edit=
-		shift
-		;;
-	--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
-	--reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
-	--reedit-message)
-		case "$#" in 1) usage ;; esac
-		shift
-		log_given=t$log_given
-		use_commit="$1"
-		no_edit=
-		shift
-		;;
-	-C)
-		case "$#" in 1) usage ;; esac
-		shift
-		log_given=t$log_given
-		use_commit="$1"
-		no_edit=t
-		shift
-		;;
-	--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
-	--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
-	--reuse-message=*)
-		log_given=t$log_given
-		use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		no_edit=t
-		shift
-		;;
-	--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
-	--reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
-		case "$#" in 1) usage ;; esac
-		shift
-		log_given=t$log_given
-		use_commit="$1"
-		no_edit=t
-		shift
-		;;
-	-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
-		signoff=t
-		shift
-		;;
-	-q|--q|--qu|--qui|--quie|--quiet)
-		quiet=t
-		shift
-		;;
-	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
-		verbose=t
-		shift
-		;;
-	-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
-	--untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
-	--untracked-file|--untracked-files)
-		untracked_files=t
-		shift
-		;;
-	--)
-		shift
-		break
-		;;
-	-*)
-		usage
-		;;
-	*)
-		break
-		;;
-	esac
-done
-case "$edit_flag" in t) no_edit= ;; esac
-
-################################################################
-# Sanity check options
-
-case "$amend,$initial_commit" in
-t,t)
-	die "You do not have anything to amend." ;;
-t,)
-	if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
-		die "You are in the middle of a merge -- cannot amend."
-	fi ;;
-esac
-
-case "$log_given" in
-tt*)
-	die "Only one of -c/-C/-F/--amend can be used." ;;
-*tm*|*mt*)
-	die "Option -m cannot be combined with -c/-C/-F/--amend." ;;
-esac
-
-case "$#,$also,$only,$amend" in
-*,t,t,*)
-	die "Only one of --include/--only can be used." ;;
-0,t,,* | 0,,t,)
-	die "No paths with --include/--only does not make sense." ;;
-0,,t,t)
-	only_include_assumed="# Clever... amending the last one with dirty index." ;;
-0,,,*)
-	;;
-*,,,*)
-	only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
-	also=
-	;;
-esac
-unset only
-case "$all,$interactive,$also,$#" in
-*t,*t,*)
-	die "Cannot use -a, --interactive or -i at the same time." ;;
-t,,[1-9]*)
-	die "Paths with -a does not make sense." ;;
-,t,[1-9]*)
-	die "Paths with --interactive does not make sense." ;;
-,,t,0)
-	die "No paths with -i does not make sense." ;;
-esac
-
-################################################################
-# Prepare index to have a tree to be committed
-
-case "$all,$also" in
-t,)
-	if test ! -f "$THIS_INDEX"
-	then
-		die 'nothing to commit (use "git add file1 file2" to include for commit)'
-	fi
-	save_index &&
-	(
-		cd_to_toplevel &&
-		GIT_INDEX_FILE="$NEXT_INDEX" &&
-		export GIT_INDEX_FILE &&
-		git-diff-files --name-only -z |
-		git-update-index --remove -z --stdin
-	) || exit
-	;;
-,t)
-	save_index &&
-	git-ls-files --error-unmatch -- "$@" >/dev/null || exit
-
-	git-diff-files --name-only -z -- "$@"  |
-	(
-		cd_to_toplevel &&
-		GIT_INDEX_FILE="$NEXT_INDEX" &&
-		export GIT_INDEX_FILE &&
-		git-update-index --remove -z --stdin
-	) || exit
-	;;
-,)
-	if test "$interactive" = t; then
-		git add --interactive || exit
-	fi
-	case "$#" in
-	0)
-		;; # commit as-is
-	*)
-		if test -f "$GIT_DIR/MERGE_HEAD"
-		then
-			refuse_partial "Cannot do a partial commit during a merge."
-		fi
-		TMP_INDEX="$GIT_DIR/tmp-index$$"
-		commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
-
-		# Build a temporary index and update the real index
-		# the same way.
-		if test -z "$initial_commit"
-		then
-			GIT_INDEX_FILE="$THIS_INDEX" \
-			git-read-tree --index-output="$TMP_INDEX" -i -m HEAD
-		else
-			rm -f "$TMP_INDEX"
-		fi || exit
-
-		printf '%s\n' "$commit_only" |
-		GIT_INDEX_FILE="$TMP_INDEX" \
-		git-update-index --add --remove --stdin &&
-
-		save_index &&
-		printf '%s\n' "$commit_only" |
-		(
-			GIT_INDEX_FILE="$NEXT_INDEX"
-			export GIT_INDEX_FILE
-			git-update-index --remove --stdin
-		) || exit
-		;;
-	esac
-	;;
-esac
-
-################################################################
-# If we do as-is commit, the index file will be THIS_INDEX,
-# otherwise NEXT_INDEX after we make this commit.  We leave
-# the index as is if we abort.
-
-if test -f "$NEXT_INDEX"
-then
-	USE_INDEX="$NEXT_INDEX"
-else
-	USE_INDEX="$THIS_INDEX"
-fi
-
-case "$status_only" in
-t)
-	# This will silently fail in a read-only repository, which is
-	# what we want.
-	GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --unmerged --refresh
-	run_status
-	exit $?
-	;;
-'')
-	GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --refresh || exit
-	;;
-esac
-
-################################################################
-# Grab commit message, write out tree and make commit.
-
-if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
-then
-	if test "$TMP_INDEX"
-	then
-		GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit
-	else
-		GIT_INDEX_FILE="$USE_INDEX" "$GIT_DIR"/hooks/pre-commit
-	fi || exit
-fi
-
-if test "$log_message" != ''
-then
-	printf '%s\n' "$log_message"
-elif test "$logfile" != ""
-then
-	if test "$logfile" = -
-	then
-		test -t 0 &&
-		echo >&2 "(reading log message from standard input)"
-		cat
-	else
-		cat <"$logfile"
-	fi
-elif test "$use_commit" != ""
-then
-	encoding=$(git config i18n.commitencoding || echo UTF-8)
-	git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
-	sed -e '1,/^$/d' -e 's/^    //'
-elif test -f "$GIT_DIR/MERGE_MSG"
-then
-	cat "$GIT_DIR/MERGE_MSG"
-elif test -f "$GIT_DIR/SQUASH_MSG"
-then
-	cat "$GIT_DIR/SQUASH_MSG"
-fi | git-stripspace >"$GIT_DIR"/COMMIT_EDITMSG
-
-case "$signoff" in
-t)
-	need_blank_before_signoff=
-	tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
-	grep 'Signed-off-by:' >/dev/null || need_blank_before_signoff=yes
-	{
-		test -z "$need_blank_before_signoff" || echo
-		git-var GIT_COMMITTER_IDENT | sed -e '
-			s/>.*/>/
-			s/^/Signed-off-by: /
-		'
-	} >>"$GIT_DIR"/COMMIT_EDITMSG
-	;;
-esac
-
-if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
-	echo "#"
-	echo "# It looks like you may be committing a MERGE."
-	echo "# If this is not correct, please remove the file"
-	printf '%s\n' "#	$GIT_DIR/MERGE_HEAD"
-	echo "# and try again"
-	echo "#"
-fi >>"$GIT_DIR"/COMMIT_EDITMSG
-
-# Author
-if test '' != "$use_commit"
-then
-	pick_author_script='
-	/^author /{
-		s/'\''/'\''\\'\'\''/g
-		h
-		s/^author \([^<]*\) <[^>]*> .*$/\1/
-		s/'\''/'\''\'\'\''/g
-		s/.*/GIT_AUTHOR_NAME='\''&'\''/p
-
-		g
-		s/^author [^<]* <\([^>]*\)> .*$/\1/
-		s/'\''/'\''\'\'\''/g
-		s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
-
-		g
-		s/^author [^<]* <[^>]*> \(.*\)$/\1/
-		s/'\''/'\''\'\'\''/g
-		s/.*/GIT_AUTHOR_DATE='\''&'\''/p
-
-		q
-	}
-	'
-	encoding=$(git config i18n.commitencoding || echo UTF-8)
-	set_author_env=`git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
-	LANG=C LC_ALL=C sed -ne "$pick_author_script"`
-	eval "$set_author_env"
-	export GIT_AUTHOR_NAME
-	export GIT_AUTHOR_EMAIL
-	export GIT_AUTHOR_DATE
-fi
-if test '' != "$force_author"
-then
-	GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
-	GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
-	test '' != "$GIT_AUTHOR_NAME" &&
-	test '' != "$GIT_AUTHOR_EMAIL" ||
-	die "malformed --author parameter"
-	export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
-fi
-
-PARENTS="-p HEAD"
-if test -z "$initial_commit"
-then
-	rloga='commit'
-	if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
-		rloga='commit (merge)'
-		PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
-	elif test -n "$amend"; then
-		rloga='commit (amend)'
-		PARENTS=$(git-cat-file commit HEAD |
-			sed -n -e '/^$/q' -e 's/^parent /-p /p')
-	fi
-	current="$(git-rev-parse --verify HEAD)"
-else
-	if [ -z "$(git-ls-files)" ]; then
-		echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
-		exit 1
-	fi
-	PARENTS=""
-	rloga='commit (initial)'
-	current=''
-fi
-set_reflog_action "$rloga"
-
-if test -z "$no_edit"
-then
-	{
-		echo ""
-		echo "# Please enter the commit message for your changes."
-		echo "# (Comment lines starting with '#' will not be included)"
-		test -z "$only_include_assumed" || echo "$only_include_assumed"
-		run_status
-	} >>"$GIT_DIR"/COMMIT_EDITMSG
-else
-	# we need to check if there is anything to commit
-	run_status >/dev/null
-fi
-if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" -a -z "$amend" ]
-then
-	rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
-	run_status
-	exit 1
-fi
-
-case "$no_edit" in
-'')
-	case "${VISUAL:-$EDITOR},$TERM" in
-	,dumb)
-		echo >&2 "Terminal is dumb but no VISUAL nor EDITOR defined."
-		echo >&2 "Please supply the commit log message using either"
-		echo >&2 "-m or -F option.  A boilerplate log message has"
-		echo >&2 "been prepared in $GIT_DIR/COMMIT_EDITMSG"
-		exit 1
-		;;
-	esac
-	git-var GIT_AUTHOR_IDENT > /dev/null  || die
-	git-var GIT_COMMITTER_IDENT > /dev/null  || die
-	${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
-	;;
-esac
-
-case "$verify" in
-t)
-	if test -x "$GIT_DIR"/hooks/commit-msg
-	then
-		"$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
-	fi
-esac
-
-if test -z "$no_edit"
-then
-    sed -e '
-        /^diff --git a\/.*/{
-	    s///
-	    q
-	}
-	/^#/d
-    ' "$GIT_DIR"/COMMIT_EDITMSG
-else
-    cat "$GIT_DIR"/COMMIT_EDITMSG
-fi |
-git-stripspace >"$GIT_DIR"/COMMIT_MSG
-
-if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
-	git-stripspace |
-	wc -l` &&
-   test 0 -lt $cnt
-then
-	if test -z "$TMP_INDEX"
-	then
-		tree=$(GIT_INDEX_FILE="$USE_INDEX" git-write-tree)
-	else
-		tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) &&
-		rm -f "$TMP_INDEX"
-	fi &&
-	commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
-	rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
-	git-update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
-	rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
-	if test -f "$NEXT_INDEX"
-	then
-		mv "$NEXT_INDEX" "$THIS_INDEX"
-	else
-		: ;# happy
-	fi
-else
-	echo >&2 "* no commit message?  aborting commit."
-	false
-fi
-ret="$?"
-rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
-
-cd_to_toplevel
-
-if test -d "$GIT_DIR/rr-cache"
-then
-	git-rerere
-fi
-
-if test "$ret" = 0
-then
-	if test -x "$GIT_DIR"/hooks/post-commit
-	then
-		"$GIT_DIR"/hooks/post-commit
-	fi
-	if test -z "$quiet"
-	then
-		commit=`git-diff-tree --always --shortstat --pretty="format:%h: %s"\
-		       --summary --root HEAD --`
-		echo "Created${initial_commit:+ initial} commit $commit"
-	fi
-fi
-
-exit "$ret"
diff --git a/git.c b/git.c
index 29b55a1..4018e3c 100644
--- a/git.c
+++ b/git.c
@@ -237,6 +237,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 		{ "check-attr", cmd_check_attr, RUN_SETUP | NOT_BARE },
 		{ "cherry", cmd_cherry, RUN_SETUP },
 		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NOT_BARE },
+		{ "commit", cmd_commit, RUN_SETUP },
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "config", cmd_config },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
@@ -279,10 +280,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },
 		{ "revert", cmd_revert, RUN_SETUP | NOT_BARE },
 		{ "rm", cmd_rm, RUN_SETUP | NOT_BARE },
-		{ "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
 		{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
 		{ "show-branch", cmd_show_branch, RUN_SETUP },
 		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
+		{ "status", cmd_status, RUN_SETUP | NOT_BARE },
 		{ "stripspace", cmd_stripspace },
 		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 		{ "tar-tree", cmd_tar_tree },
diff --git a/mktag.c b/mktag.c
index b82e377..26b9ebf 100644
--- a/mktag.c
+++ b/mktag.c
@@ -111,8 +111,8 @@ static int verify_tag(char *buffer, unsigned long size)
 
 int main(int argc, char **argv)
 {
-	unsigned long size = 4096;
-	char *buffer = xmalloc(size);
+	unsigned long size;
+	char *buffer;
 	unsigned char result_sha1[20];
 
 	if (argc != 1)
@@ -120,10 +120,8 @@ int main(int argc, char **argv)
 
 	setup_git_directory();
 
-	if (read_pipe(0, &buffer, &size)) {
-		free(buffer);
+	if (read_fd(0, &buffer, &size))
 		die("could not read from stdin");
-	}
 
 	/* Verify it for some basic sanity: it needs to start with
 	   "object <sha1>\ntype\ntagger " */
diff --git a/sha1_file.c b/sha1_file.c
index 2b86086..91e8854 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2286,18 +2286,17 @@ int has_sha1_file(const unsigned char *sha1)
 }
 
 /*
- * reads from fd as long as possible into a supplied buffer of size bytes.
- * If necessary the buffer's size is increased using realloc()
+ * reads from fd as long as possible and allocates a buffer to hold
+ * the contents.  The buffer and size of the contents is returned in
+ * *return_buf and *return_size.  In case of failure, the allocated
+ * buffers are freed, otherwise, the buffer must be freed using xfree.
  *
  * returns 0 if anything went fine and -1 otherwise
- *
- * NOTE: both buf and size may change, but even when -1 is returned
- * you still have to free() it yourself.
  */
-int read_pipe(int fd, char** return_buf, unsigned long* return_size)
+int read_fd(int fd, char** return_buf, unsigned long* return_size)
 {
-	char* buf = *return_buf;
-	unsigned long size = *return_size;
+	unsigned long size = 4096;
+	char* buf = xmalloc(size);
 	ssize_t iret;
 	unsigned long off = 0;
 
@@ -2315,21 +2314,38 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size)
 	*return_buf = buf;
 	*return_size = off;
 
-	if (iret < 0)
+	if (iret < 0) {
+		free(buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+int read_path(const char *path, char** return_buf, unsigned long* return_size)
+{
+	int fd; 
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -1;
+	if (read_fd(fd, return_buf, return_size)) {
+		close(fd);
 		return -1;
+	}
+	close(fd);
+
 	return 0;
 }
 
 int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
 {
-	unsigned long size = 4096;
-	char *buf = xmalloc(size);
+	unsigned long size;
+	char *buf;
 	int ret;
 
-	if (read_pipe(fd, &buf, &size)) {
-		free(buf);
+	if (read_fd(fd, &buf, &size))
 		return -1;
-	}
 
 	if (!type)
 		type = blob_type;
diff --git a/t/t7800-commit.sh b/t/t7800-commit.sh
new file mode 100644
index 0000000..1f97caa
--- /dev/null
+++ b/t/t7800-commit.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+#
+
+# FIXME: Test the various index usages, -i and -o, test reflog,
+# signoff, hooks
+
+test_description='git-commit'
+. ./test-lib.sh
+
+# Pick a date so we get consistent commits. 7/7/07 means good luck!
+export GIT_AUTHOR_DATE="July 7, 2007"
+export GIT_COMMITTER_DATE="July 7, 2007"
+
+echo "bongo bongo" >file
+test_expect_success \
+	"initial status" \
+	"git-add file && \
+	 git-status | grep 'Initial commit'"
+
+test_expect_failure \
+	"fail initial amend" \
+	"git-commit -m initial --amend"
+
+test_expect_success \
+	"initial commit" \
+	"git-commit -m initial"
+
+test_expect_failure \
+	"testing nothing to commit" \
+	"git-commit -m initial"
+
+echo "bongo bongo bongo" >file
+
+test_expect_success \
+	"next commit" \
+	"git-commit -m next -a"
+
+echo "more bongo: bongo bongo bongo bongo" >file
+
+test_expect_failure \
+	"commit message from non-existing file" \
+	"git-commit -F gah -a"
+
+cat >msg <<EOF
+		
+
+  
+Signed-off-by: hula
+EOF
+test_expect_failure \
+	"empty commit message" \
+	"git-commit -F msg -a"
+
+echo "this is the commit message, coming from a file" >msg
+test_expect_success \
+	"commit message from file" \
+	"git-commit -F msg -a"
+
+cat >editor <<\EOF
+#!/bin/sh
+sed -i -e "s/a file/an amend commit/g" $1
+EOF
+chmod 755 editor
+
+test_expect_success \
+	"amend commit" \
+	"VISUAL=./editor git-commit --amend"
+
+echo "enough with the bongos" >file
+test_expect_failure \
+	"passing --amend and -F" \
+	"git-commit -F msg --amend ."
+
+test_expect_success \
+	"using message from other commit" \
+	"git-commit -C HEAD^ ."
+
+cat >editor <<\EOF
+#!/bin/sh
+sed -i -e "s/amend/older/g" $1
+EOF
+chmod 755 editor
+
+echo "hula hula" >file
+test_expect_success \
+	"editing message from other commit" \
+	"VISUAL=./editor git-commit -c HEAD^ -a"
+
+echo "silly new contents" >file
+test_expect_success \
+	"message from stdin" \
+	"echo commit message from stdin | git-commit -F - -a"
+
+echo "gak" >file
+test_expect_success \
+	"overriding author from command line" \
+	"git-commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a"
+
+test_expect_success \
+	"interactive add" \
+	"echo 7 | git-commit --interactive | grep 'What now'"
+
+test_expect_success \
+	"showing committed revisions" \
+	"git-rev-list HEAD >current"
+
+# We could just check the head sha1, but checking each commit makes it
+# easier to isolate bugs.
+
+cat >expected <<\EOF
+9a7ad90c32f43aecc081722a72f26ead981bd6fa
+d5c6c34f0361d64d3d1b1f26736b1ae98e2baa90
+ca0ddb06fc90f3f857dd623f8418804aad75d9fa
+9cc5d9b9e5a29f1c46d0d0cf2dd716fb8241316a
+ca750e97c137587aa181b6b9526b3b04fb9db667
+4515202a60be41cb1b8f6b89edb3f6948130ac1c
+7cc9a125522fe28b84cd3f6c7aeef6e5c62b3f8b
+EOF
+
+test_expect_success \
+    'validate git-rev-list output.' \
+    'diff current expected'
+
+test_done
diff --git a/wt-status.c b/wt-status.c
index 5205420..5bf800a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -54,29 +54,30 @@ void wt_status_prepare(struct wt_status *s)
 	s->reference = "HEAD";
 }
 
-static void wt_status_print_cached_header(const char *reference)
+static void wt_status_print_cached_header(struct wt_status *s)
 {
 	const char *c = color(WT_STATUS_HEADER);
-	color_printf_ln(c, "# Changes to be committed:");
-	if (reference) {
-		color_printf_ln(c, "#   (use \"git reset %s <file>...\" to unstage)", reference);
+	color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+	if (s->reference) {
+		color_fprintf_ln(s->fp, c, "#   (use \"git reset %s <file>...\" to unstage)", s->reference);
 	} else {
-		color_printf_ln(c, "#   (use \"git rm --cached <file>...\" to unstage)");
+		color_fprintf_ln(s->fp, c, "#   (use \"git rm --cached <file>...\" to unstage)");
 	}
-	color_printf_ln(c, "#");
+	color_fprintf_ln(s->fp, c, "#");
 }
 
-static void wt_status_print_header(const char *main, const char *sub)
+static void wt_status_print_header(struct wt_status *s,
+				   const char *main, const char *sub)
 {
 	const char *c = color(WT_STATUS_HEADER);
-	color_printf_ln(c, "# %s:", main);
-	color_printf_ln(c, "#   (%s)", sub);
-	color_printf_ln(c, "#");
+	color_fprintf_ln(s->fp, c, "# %s:", main);
+	color_fprintf_ln(s->fp, c, "#   (%s)", sub);
+	color_fprintf_ln(s->fp, c, "#");
 }
 
-static void wt_status_print_trailer(void)
+static void wt_status_print_trailer(struct wt_status *s)
 {
-	color_printf_ln(color(WT_STATUS_HEADER), "#");
+	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
 }
 
 static const char *quote_crlf(const char *in, char *buf, size_t sz)
@@ -108,7 +109,8 @@ static const char *quote_crlf(const char *in, char *buf, size_t sz)
 	return ret;
 }
 
-static void wt_status_print_filepair(int t, struct diff_filepair *p)
+static void wt_status_print_filepair(struct wt_status *s,
+				     int t, struct diff_filepair *p)
 {
 	const char *c = color(t);
 	const char *one, *two;
@@ -117,36 +119,36 @@ static void wt_status_print_filepair(int t, struct diff_filepair *p)
 	one = quote_crlf(p->one->path, onebuf, sizeof(onebuf));
 	two = quote_crlf(p->two->path, twobuf, sizeof(twobuf));
 
-	color_printf(color(WT_STATUS_HEADER), "#\t");
+	color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
 	switch (p->status) {
 	case DIFF_STATUS_ADDED:
-		color_printf(c, "new file:   %s", one);
+		color_fprintf(s->fp, c, "new file:   %s", one);
 		break;
 	case DIFF_STATUS_COPIED:
-		color_printf(c, "copied:     %s -> %s", one, two);
+		color_fprintf(s->fp, c, "copied:     %s -> %s", one, two);
 		break;
 	case DIFF_STATUS_DELETED:
-		color_printf(c, "deleted:    %s", one);
+		color_fprintf(s->fp, c, "deleted:    %s", one);
 		break;
 	case DIFF_STATUS_MODIFIED:
-		color_printf(c, "modified:   %s", one);
+		color_fprintf(s->fp, c, "modified:   %s", one);
 		break;
 	case DIFF_STATUS_RENAMED:
-		color_printf(c, "renamed:    %s -> %s", one, two);
+		color_fprintf(s->fp, c, "renamed:    %s -> %s", one, two);
 		break;
 	case DIFF_STATUS_TYPE_CHANGED:
-		color_printf(c, "typechange: %s", one);
+		color_fprintf(s->fp, c, "typechange: %s", one);
 		break;
 	case DIFF_STATUS_UNKNOWN:
-		color_printf(c, "unknown:    %s", one);
+		color_fprintf(s->fp, c, "unknown:    %s", one);
 		break;
 	case DIFF_STATUS_UNMERGED:
-		color_printf(c, "unmerged:   %s", one);
+		color_fprintf(s->fp, c, "unmerged:   %s", one);
 		break;
 	default:
 		die("bug: unhandled diff status %c", p->status);
 	}
-	printf("\n");
+	fprintf(s->fp, "\n");
 }
 
 static void wt_status_print_updated_cb(struct diff_queue_struct *q,
@@ -160,14 +162,14 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,
 		if (q->queue[i]->status == 'U')
 			continue;
 		if (!shown_header) {
-			wt_status_print_cached_header(s->reference);
+			wt_status_print_cached_header(s);
 			s->commitable = 1;
 			shown_header = 1;
 		}
-		wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]);
+		wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
 	}
 	if (shown_header)
-		wt_status_print_trailer();
+		wt_status_print_trailer(s);
 }
 
 static void wt_status_print_changed_cb(struct diff_queue_struct *q,
@@ -184,18 +186,18 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
 				msg = use_add_rm_msg;
 				break;
 			}
-		wt_status_print_header("Changed but not updated", msg);
+		wt_status_print_header(s, "Changed but not updated", msg);
 	}
 	for (i = 0; i < q->nr; i++)
-		wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
+		wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
 	if (q->nr)
-		wt_status_print_trailer();
+		wt_status_print_trailer(s);
 }
 
 static void wt_read_cache(struct wt_status *s)
 {
 	discard_cache();
-	read_cache();
+	read_cache_from(s->index_file);
 }
 
 static void wt_status_print_initial(struct wt_status *s)
@@ -206,16 +208,16 @@ static void wt_status_print_initial(struct wt_status *s)
 	wt_read_cache(s);
 	if (active_nr) {
 		s->commitable = 1;
-		wt_status_print_cached_header(NULL);
+		wt_status_print_cached_header(s);
 	}
 	for (i = 0; i < active_nr; i++) {
-		color_printf(color(WT_STATUS_HEADER), "#\t");
-		color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s",
+		color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
+		color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s",
 				quote_crlf(active_cache[i]->name,
 					   buf, sizeof(buf)));
 	}
 	if (active_nr)
-		wt_status_print_trailer();
+		wt_status_print_trailer(s);
 }
 
 static void wt_status_print_updated(struct wt_status *s)
@@ -281,12 +283,12 @@ static void wt_status_print_untracked(struct wt_status *s)
 		}
 		if (!shown_header) {
 			s->workdir_untracked = 1;
-			wt_status_print_header("Untracked files",
+			wt_status_print_header(s, "Untracked files",
 					       use_add_to_include_msg);
 			shown_header = 1;
 		}
-		color_printf(color(WT_STATUS_HEADER), "#\t");
-		color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s",
+		color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
+		color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%.*s",
 				ent->len, ent->name);
 	}
 }
@@ -316,14 +318,14 @@ void wt_status_print(struct wt_status *s)
 			branch_name = "";
 			on_what = "Not currently on any branch.";
 		}
-		color_printf_ln(color(WT_STATUS_HEADER),
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
 			"# %s%s", on_what, branch_name);
 	}
 
 	if (s->is_initial) {
-		color_printf_ln(color(WT_STATUS_HEADER), "#");
-		color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit");
-		color_printf_ln(color(WT_STATUS_HEADER), "#");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
 		wt_status_print_initial(s);
 	}
 	else {
@@ -337,7 +339,7 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_verbose(s);
 	if (!s->commitable) {
 		if (s->amend)
-			printf("# No changes\n");
+			fprintf(s->fp, "# No changes\n");
 		else if (s->workdir_dirty)
 			printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
 		else if (s->workdir_untracked)
diff --git a/wt-status.h b/wt-status.h
index cfea4ae..7744932 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -1,6 +1,8 @@
 #ifndef STATUS_H
 #define STATUS_H
 
+#include <stdio.h>
+
 enum color_wt_status {
 	WT_STATUS_HEADER,
 	WT_STATUS_UPDATED,
@@ -19,6 +21,8 @@ struct wt_status {
 	int commitable;
 	int workdir_dirty;
 	int workdir_untracked;
+	const char *index_file;
+	FILE *fp;
 };
 
 int git_status_config(const char *var, const char *value);
-- 
1.5.2.GIT

^ permalink raw reply related

* Manpage rendering faults
From: Fredrik Tolf @ 2007-07-18 18:47 UTC (permalink / raw)
  To: git

I often read manpages using a `man -t whatever | ggv -' command, since
I like how it is rendered in PostScript. However, it turns out that
some things in the Git manpages don't really render very well using
that method. For example, in the git-rebase manpage, there are two
history graphs that look like this when reading the manpages normally
in a terminal:

      A---B---C topic
     /
D---E---F---G master

and then

              A'--B'--C' topic
             /
D---E---F---G master

However, in the PostScript rendering, they look rather like this:

  A---B---C topic
 /
D---E---F---G master

and then

      A'--B'--C' topic
     /
D---E---F---G master

It took me quite a while to figure out that the rendering was
wrong. Before that, I just couldn't figure out why git-rebase would do
that. :)

Unfortunately, I can't say that I have a fix available -- particularly
as I don't really know anything at all about asciidoc. I thought I'd
at least let you know, though.

Fredrik Tolf

^ permalink raw reply

* Re: git svn dcommit seg fault
From: Perrin Meyer @ 2007-07-18 18:33 UTC (permalink / raw)
  To: Eric Wong; +Cc: git


[perrin@whisper TESTGIT]$ git svn --version
git-svn version 1.5.3.rc2.4.g726f9 (svn 1.2.3)

I get the "post commit" email from our SVN central server, so it appears that on the SVN side the commit worked? 

I tried running git svn with --verbose, but that did not print out any more error messages (besides the seg fault).

Thanks,

Perrin



----- Original Message ----
From: Eric Wong <eric@petta-tech.com>
To: Perrin Meyer <perrinmeyer@yahoo.com>
Cc: git@vger.kernel.org
Sent: Wednesday, July 18, 2007 12:34:50 AM
Subject: Re: git svn dcommit seg fault

Perrin Meyer <perrinmeyer@yahoo.com> wrote:
> 
> I'm able to clone svn repo's fine with
> 
> $ git svn clone https://svn.eng.msli.com/perrin/trunk/TESTGIT/ .
> 
> and I'm then able to use git commit to commit local changes, but 
> when I try 
> 
> $ git svn dcommit
> 
> I get
> 
> [perrin@whisper TESTGIT]$ git svn dcommit
>         M       test.c
> Committed r717
> Segmentation fault


> As far as I can tell, the commit worked fine (verified by trying 'svn
> update' on another box).
> 
> I've tried git version 1.5.2.3, 1.5.3-rc2, and the latest build, and
> all give the seg fault.
> 
> I'm guessing it has something to do with using the https connection to
> svn?

I primarily work with https repositories using git-svn and I haven't
seen any segfaults in a while.  Which version of the SVN libraries do
you have?  (git-svn --version will tell you).

-- 
Eric Wong

^ permalink raw reply

* [PATCH] Rename read_pipe() with read_fd() and make its buffer nul-terminated.
From: Carlos Rica @ 2007-07-18 18:31 UTC (permalink / raw)
  To: git, Junio C Hamano, Johannes Schindelin, Kristian Høgsberg

The new name is closer to the purpose of the function.

A NUL-terminated buffer makes things easier when callers need that.
Since the function returns only the memory written with data,
almost always allocating more space than needed because final
size is unknown, an extra NUL terminating the buffer is harmless.
It is not included in the returned size, so the function
remains working as before.

Also, now the function allows the buffer passed to be NULL at first,
and alloc_nr is now used for growing the buffer, instead size=*2.

Signed-off-by: Carlos Rica <jasampler@gmail.com>
---
 builtin-stripspace.c |    4 +++-
 cache.h              |    2 +-
 mktag.c              |    2 +-
 sha1_file.c          |   21 +++++++++++++++------
 4 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index 0c970aa..5571687 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -79,8 +79,10 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)

 	size = 1024;
 	buffer = xmalloc(size);
-	if (read_pipe(0, &buffer, &size))
+	if (read_fd(0, &buffer, &size)) {
+		free(buffer);
 		die("could not read the input");
+	}

 	size = stripspace(buffer, size, 0);
 	write_or_die(1, buffer, size);
diff --git a/cache.h b/cache.h
index 328c1ad..53801b8 100644
--- a/cache.h
+++ b/cache.h
@@ -265,7 +265,7 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat
 extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
+extern int read_fd(int fd, char **return_buf, unsigned long *return_size);
 extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
diff --git a/mktag.c b/mktag.c
index b82e377..38acd5a 100644
--- a/mktag.c
+++ b/mktag.c
@@ -120,7 +120,7 @@ int main(int argc, char **argv)

 	setup_git_directory();

-	if (read_pipe(0, &buffer, &size)) {
+	if (read_fd(0, &buffer, &size)) {
 		free(buffer);
 		die("could not read from stdin");
 	}
diff --git a/sha1_file.c b/sha1_file.c
index 1efd9ae..aca741b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2304,27 +2304,36 @@ int has_sha1_file(const unsigned char *sha1)
  *
  * returns 0 if anything went fine and -1 otherwise
  *
+ * The buffer is always NUL-terminated, not including it in returned size.
+ *
  * NOTE: both buf and size may change, but even when -1 is returned
  * you still have to free() it yourself.
  */
-int read_pipe(int fd, char** return_buf, unsigned long* return_size)
+int read_fd(int fd, char **return_buf, unsigned long *return_size)
 {
-	char* buf = *return_buf;
+	char *buf = *return_buf;
 	unsigned long size = *return_size;
 	ssize_t iret;
 	unsigned long off = 0;

+	if (!buf || size <= 1) {
+		size = 1024;
+		buf = xrealloc(buf, size);
+	}
+
 	do {
-		iret = xread(fd, buf + off, size - off);
+		iret = xread(fd, buf + off, (size - 1) - off);
 		if (iret > 0) {
 			off += iret;
-			if (off == size) {
-				size *= 2;
+			if (off == size - 1) {
+				size = alloc_nr(size);
 				buf = xrealloc(buf, size);
 			}
 		}
 	} while (iret > 0);

+	buf[off] = '\0';
+
 	*return_buf = buf;
 	*return_size = off;

@@ -2339,7 +2348,7 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
 	char *buf = xmalloc(size);
 	int ret;

-	if (read_pipe(fd, &buf, &size)) {
+	if (read_fd(fd, &buf, &size)) {
 		free(buf);
 		return -1;
 	}
-- 
1.5.0

^ permalink raw reply related

* Re: Wrong time in git-log when using right/ timezone
From: Linus Torvalds @ 2007-07-18 18:19 UTC (permalink / raw)
  To: Julian Phillips; +Cc: Peter Hartlich, git
In-Reply-To: <Pine.LNX.4.64.0707181849060.1544@reaper.quantumfyre.co.uk>



On Wed, 18 Jul 2007, Julian Phillips wrote:
> 
> Is git working internally in TZ=UTC?

Yes.

The TZ in git terms has no meaning what-so-ever, it's purely for 
"decoration", so that people can see what the local time was. But a git 
time really *is* the UTC seconds-since-1970, and the timezone is saved so 
that it can be shown, but not for anything else.

So git always stores times in UTC, but then when showing them, it shows 
them as relative to the timezone they were done in.

You can see this by doing

	git cat-file commit HEAD

which shows something like

	author Junio C Hamano <gitster@pobox.com> 1184542877 -0700

and that "1184542877" is literally the time in UTC, and is the only thing 
that git really tracks. The "-0700" is informational, so that when you 
show it as a log entry, it shows as

	Author: Junio C Hamano <gitster@pobox.com>
	Date:   Sun Jul 15 16:41:17 2007 -0700

even though that "1184542877" really means "Sun Jul 15 23:41:17 2007".

So git never actually saves anything at all in local time, it just tries 
to show things in whatever the local time was for the person who did 
something (unless you use the "--date=local" option, in which case it 
ignores the saved TZ, and uses your _current_ TZ to show the date)

Unless we have a bug, of course.

			Linus

^ permalink raw reply

* Re: "git clone" executed as root on solaris 10 shreds UFS (it is possible to create hardlinks for directories as root under solaris)
From: Thomas Glanzmann @ 2007-07-18 18:15 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: GIT
In-Reply-To: <alpine.LFD.0.999.0707180844150.27353@woody.linux-foundation.org>

Hello,

> I don't think the recipient "got it", since it already has a comment about 
> "just use rmdir".  Can you please tell them that that isn't the point?

I already did. They got the point. I have confidence that they fix it.
But there is no ETA. And the fix will not be backported.

	Thomas

^ permalink raw reply

* Re: Empty directories...
From: Linus Torvalds @ 2007-07-18 18:05 UTC (permalink / raw)
  To: David Kastrup; +Cc: git
In-Reply-To: <85r6n5y5m3.fsf@lola.goethe.zz>



On Wed, 18 Jul 2007, David Kastrup wrote:
> 
> Since I did not try to argue this, could you beat another strawman?

How about a bit of honesty?

Here's the quote:

 "The FAQ answer is weazeling on several accounts:

  a) No, git only cares about files, or rather git tracks content and
     empty directories have no content.

  In the same manner as empty regular files have no contents, and git
  tracks those.  Existence and permissions are important."

You called it "weaselly" to say that git tracks only content, and then 
very much tried to equate "existence and permissions" with content.

That's the part I answered.

So it wasn't a strawman, it was a direct answer to your assertion. Now go 
away and either come back with the patch to implement it (that I have 
encouraged you to do), or add a ".gitignore" file to the directory (that 
others have told you will solve your problems).

Don't bother talking crap.

			Linus

^ permalink raw reply

* Re: Wrong time in git-log when using right/ timezone
From: Julian Phillips @ 2007-07-18 17:55 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Peter Hartlich, git
In-Reply-To: <alpine.LFD.0.999.0707180948080.27353@woody.linux-foundation.org>

On Wed, 18 Jul 2007, Linus Torvalds wrote:

> ie apparently Berlin is in a timezone of its own that is roughly one
> minute and 23 seconds away from CET.
>
> What the *heck*?
>
> I really don't think this is git that is confused: I get the exact same
> thing with "date" too:
>
> 	[torvalds@woody git]$ TZ=right/Europe/Berlin date ; TZ=CET date
> 	Wed Jul 18 18:52:25 CEST 2007
> 	Wed Jul 18 18:52:48 CEST 2007
>
> so it really *is* the tzdata that says that Berlin is not +0200, and it's
> not even +0159, it's something really strange with fractional minutes away
> from UTC.

Well ... not if you compare right/Europe/Berlin vs right/CET

jp3@neutron: ~>TZ=right/CET date '+%H:%M:%S %z'; TZ=right/Europe/Berlin 
date '+%H:%M:%S %z'
19:49:45 +0200
19:49:45 +0200

but, I suspect that the following is the underlying cause of the problem?

jp3@neutron: ~>TZ=right/UTC date '+%H:%M:%S %z'; TZ=UTC date '+%H:%M:%S 
%z'
17:51:32 +0000
17:51:55 +0000

Is git working internally in TZ=UTC?

-- 
Julian

  ---

Homer:	We always have one good kid and one lousy kid.  Why can't both
 	our kids be good?

Marge:	We have three kids, Homer.

 		   Separate Vacations

^ 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