Git development
 help / color / mirror / Atom feed
* Re: [PATCH 10/10] push: teach push to be quiet if local ref is strict subset of remote ref
From: Andreas Ericsson @ 2007-11-02 13:52 UTC (permalink / raw)
  To: Tom Prince; +Cc: Steffen Prohaska, Junio C Hamano, git
In-Reply-To: <20071102132446.GA31758@hermes.priv>

Tom Prince wrote:
> On Fri, Nov 02, 2007 at 11:03:36AM +0100, Andreas Ericsson wrote:
>> Steffen Prohaska wrote:
>>> On Nov 1, 2007, at 10:11 AM, Andreas Ericsson wrote:
>>>> It's easier to bisect. If git bisect lands you on a merge-commit,
>>>> you need to start a new bisect for each of the parents included
>>>> in the merge. Hopefully the nature of the merge gives a clue so
>>>> the user can make an educated guess as to which parent introduced
>>>> the bogus commit, but for an "evil octopus" (unusual) or if the
>>>> merge had conflicts which were resolved in a buggy way (not
>>>> exactly uncommon), it can be quite a hassle to get things right.
>>>> With a mostly linear history, this problem goes away.
>>> This is really an interesting point. I did not start to use
>>> git bisect regularly. But I certainly plan to do so in the future.
>>> Couldn't bisect learn to better cope with non-linear history?
>> Perhaps it could, but it's far from trivial. I started hacking on
>> a wrapper for git-bisect which would do just that, but gave up
>> rather quickly as the book-keeping required to remember each and
>> every parent-point tried just got out of hand, and it *still*
>> wouldn't run in full automatic. It broke down because I also
>> wanted merges on non-first-line parents to be delved into. If
>> that didn't happen, I wouldn't *know* the bisect would run fine
>> without me watching it, so then it was as useless as if I'd have
>> had to sit there the entire time anyway.
> 
> I haven't had occasion to use git-bisect much, but I was under the
> impression that bisect could already handle merges, or any other shaped
> history just fine.
> 

It appears the code supports your statement. I started writing on my
hack-around about a year ago, and the merge-handling code got in with
1c4fea3a40e836dcee2f16091bf7bfba96c924d0 at Wed Mar 21 22:16:24 2007.
Perhaps I shouldn't be so paranoid about useless merges anymore then.
Hmm. I shall have to look into it. Perhaps Junio can clarify how it
works? The man-page was terribly silent about how git-bisect handles
merges.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* Re: [PATCH] Make git-clone obey "--" (end argument parsing)
From: Heikki Orsila @ 2007-11-02 14:21 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano wrote:
> I do not think this breaks anything, but does it _help_
> anything in practice?
> 
> What kind of breakage does this patch fix?

It doesn't fix anything, but it is a good usability standard to obey --.
I was creating a script that would _automatically_ clone repositories of 
other users. As a little bit pedantic shell script writer I added "--" 
and noticed that git clone doesn't handle that, although it's expected 
behavior from many tools.

-- 
Heikki Orsila			Barbie's law:
heikki.orsila@iki.fi		"Math is hard, let's go shopping!"
http://www.iki.fi/shd

^ permalink raw reply

* Re: [PATCH 10/10] push: teach push to be quiet if local ref is strict subset of remote ref
From: Steffen Prohaska @ 2007-11-02 14:49 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: Tom Prince, Junio C Hamano, git
In-Reply-To: <472B2B8F.1060203@op5.se>


On Nov 2, 2007, at 2:52 PM, Andreas Ericsson wrote:

>> I haven't had occasion to use git-bisect much, but I was under the
>> impression that bisect could already handle merges, or any other  
>> shaped
>> history just fine.
>
> It appears the code supports your statement. I started writing on my
> hack-around about a year ago, and the merge-handling code got in with
> 1c4fea3a40e836dcee2f16091bf7bfba96c924d0 at Wed Mar 21 22:16:24 2007.
> Perhaps I shouldn't be so paranoid about useless merges anymore then.
> Hmm. I shall have to look into it. Perhaps Junio can clarify how it
> works? The man-page was terribly silent about how git-bisect handles
> merges.

So eventually there's coming something good out of this thread,
without actually writing any code ;)

	Steffen

^ permalink raw reply

* Re: New features in gitk
From: Linus Torvalds @ 2007-11-02 15:03 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: git
In-Reply-To: <18218.63946.772767.179841@cargo.ozlabs.ibm.com>



On Fri, 2 Nov 2007, Paul Mackerras wrote:
> 
> How would that help?  That doesn't list about 2/3 of the commits at
> all.

Yeah, you'd have to do all the parent processing on your own, I guess that 
would be too slow.

> In any case, no that's not the only reason.  The main reason is that
> it (i.e. --topo-order) spits out the commits in exactly the order that
> gitk wants to display them (of which the bit about parents coming
> after all their children is a part), and thus reduces the amount of
> processing I need to do in Tcl.

The thing is, you shouldn't *care* how long it takes to get 50,000+ 
commits.

You're only visualizing ~20 commits at a time. Ignore the rest.

THAT is the number that is timing-critical. You're optimizing for the 
wrong case - the "whole history" thing doesn't matter as much as the 
"recent history" does.

So I bet from a usability standpoint, you'd be *much* better off with 
something that might take ten times as long for the whole thing, if the 
first thirty lines show up in a third of the time.

In fact, if you want to really optimize parsing and that is a big issue, 
use

	git log --left-right --parents --pretty=format:"%m %H %P %an <%ae> %aD"

which will give you a single line per commit. I bet tk is good at reading 
single lines. Don't even read anythign else - until somebody actually 
*selects* the commit, at which point you do the diff *and* the full thing.

Yes, it will make things slower over-all. And no, the above won't work for 
the "search everywhere", which means that once people start searching for 
everything, you'll be screwed, but with somethign like the above, you'll 
get the first commits immediately and can start showing them.

And yes, if they come in the wrong order, you'll have to recalculate the 
display, but I thought you had something incremental already - ie you can 
always do it for just the current window of 100 commits or whatever.

			Linus

^ permalink raw reply

* Re: [PATCH 3/3] Act on WS_WARN for ws_mode_trailing.
From: David Symonds @ 2007-11-02 15:07 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Andreas Ericsson, David Symonds
In-Reply-To: <1194010463982-git-send-email-dsymonds@gmail.com>

On 11/3/07, David Symonds <dsymonds@gmail.com> wrote:
> Signed-off-by: David Symonds <dsymonds@gmail.com>
> ---
>  diff.c |   21 +++++++++++++--------
>  1 files changed, 13 insertions(+), 8 deletions(-)

Silly me; I somehow forgot I was only in diff.c. I'll fix and repost the series.


Dave.

^ permalink raw reply

* [PATCH 1/2] Implement parsing for new core.whitespace.* options.
From: David Symonds @ 2007-11-02 15:08 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Andreas Ericsson, David Symonds

Each of the new core.whitespace.* options (enumerated below) can be set to one
of:
	* okay (default): Whitespace of this type is okay
	* warn: Whitespace of this type should be warned about
	* error: Whitespace of this type should raise an error
	* autofix: Whitespace of this type should be automatically fixed

The initial options are:
	* trailing: Whitespace at the end of a line
	* space-before-tab: SP HT sequence in the initial whitespace of a line
	* space-indent: At least 8 spaces in a row at the start of a line

Example usage:
	[core "whitespace"]
		trailing = autofix
		space-before-tab = error
		space-indent = warn

Signed-off-by: David Symonds <dsymonds@gmail.com>
---
 cache.h       |   16 ++++++++++++++++
 config.c      |   28 ++++++++++++++++++++++++++++
 environment.c |    3 +++
 3 files changed, 47 insertions(+), 0 deletions(-)

diff --git a/cache.h b/cache.h
index bfffa05..51e3982 100644
--- a/cache.h
+++ b/cache.h
@@ -602,4 +602,20 @@ extern int diff_auto_refresh_index;
 /* match-trees.c */
 void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 
+/*
+ * whitespace rules.
+ * used by both diff and apply
+ */
+enum whitespace_mode {
+	WS_OKAY = 0,
+	WS_WARN,
+	WS_ERROR,
+	WS_AUTOFIX
+};
+extern enum whitespace_mode ws_mode_trailing;
+extern enum whitespace_mode ws_mode_space_before_tab;
+extern enum whitespace_mode ws_mode_space_indent;
+extern enum whitespace_mode git_config_whitespace_mode(const char *, const char *);
+
+
 #endif /* CACHE_H */
diff --git a/config.c b/config.c
index dc3148d..8e6f252 100644
--- a/config.c
+++ b/config.c
@@ -297,6 +297,19 @@ int git_config_bool(const char *name, const char *value)
 	return git_config_int(name, value) != 0;
 }
 
+enum whitespace_mode git_config_whitespace_mode(const char *name, const char *value)
+{
+	if (!strcasecmp(value, "okay"))
+		return WS_OKAY;
+	if (!strcasecmp(value, "warn"))
+		return WS_WARN;
+	if (!strcasecmp(value, "error"))
+		return WS_ERROR;
+	if (!strcasecmp(value, "autofix"))
+		return WS_AUTOFIX;
+	die("bad config value for '%s' in %s", name, config_file_name);
+}
+
 int git_default_config(const char *var, const char *value)
 {
 	/* This needs a better name */
@@ -431,6 +444,21 @@ int git_default_config(const char *var, const char *value)
 		return 0;
 	}
 
+	if (!strcmp(var, "core.whitespace.trailing")) {
+		ws_mode_trailing = git_config_whitespace_mode(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "core.whitespace.space-before-tab")) {
+		ws_mode_space_before_tab = git_config_whitespace_mode(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "core.whitespace.space-indent")) {
+		ws_mode_space_indent = git_config_whitespace_mode(var, value);
+		return 0;
+	}
+
 	/* Add other config variables here and to Documentation/config.txt. */
 	return 0;
 }
diff --git a/environment.c b/environment.c
index b5a6c69..71502fc 100644
--- a/environment.c
+++ b/environment.c
@@ -35,6 +35,9 @@ int pager_in_use;
 int pager_use_color = 1;
 char *editor_program;
 int auto_crlf = 0;	/* 1: both ways, -1: only when adding git objects */
+enum whitespace_mode ws_mode_trailing = WS_OKAY;
+enum whitespace_mode ws_mode_space_before_tab = WS_OKAY;
+enum whitespace_mode ws_mode_space_indent = WS_OKAY;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
-- 
1.5.3.1

^ permalink raw reply related

* [PATCH 2/2] git-diff: Respect core.whitespace.{space-indent,space-before-tab,trailing}.
From: David Symonds @ 2007-11-02 15:08 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Andreas Ericsson, David Symonds
In-Reply-To: <11940160932021-git-send-email-dsymonds@gmail.com>

Signed-off-by: David Symonds <dsymonds@gmail.com>
---
 diff.c |   24 ++++++++++++++++++------
 1 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/diff.c b/diff.c
index a6aaaf7..aa86fa1 100644
--- a/diff.c
+++ b/diff.c
@@ -503,12 +503,15 @@ static void emit_line_with_ws(int nparents,
 	int tail = len;
 	int need_highlight_leading_space = 0;
 	/* The line is a newly added line.  Does it have funny leading
-	 * whitespaces?  In indent, SP should never precede a TAB.
+	 * whitespaces?  In indent, SP should never precede a TAB. In
+	 * addition, under "indent with non tab" rule, there should not
+	 * be 8 or more consecutive spaces.
 	 */
 	for (i = col0; i < len; i++) {
 		if (line[i] == '\t') {
 			last_tab_in_indent = i;
-			if (0 <= last_space_in_indent)
+			if ((ws_mode_space_before_tab != WS_OKAY) &&
+			    (0 <= last_space_in_indent))
 				need_highlight_leading_space = 1;
 		}
 		else if (line[i] == ' ')
@@ -516,6 +519,13 @@ static void emit_line_with_ws(int nparents,
 		else
 			break;
 	}
+	if ((ws_mode_space_indent != WS_OKAY) &&
+	    (0 <= last_space_in_indent) &&
+	    (last_tab_in_indent < 0) &&
+	    (8 <= (i - col0))) {
+		last_tab_in_indent = i;
+		need_highlight_leading_space = 1;
+	}
 	fputs(set, stdout);
 	fwrite(line, col0, 1, stdout);
 	fputs(reset, stdout);
@@ -540,10 +550,12 @@ static void emit_line_with_ws(int nparents,
 	tail = len - 1;
 	if (line[tail] == '\n' && i < tail)
 		tail--;
-	while (i < tail) {
-		if (!isspace(line[tail]))
-			break;
-		tail--;
+	if (ws_mode_trailing != WS_OKAY) {
+		while (i < tail) {
+			if (!isspace(line[tail]))
+				break;
+			tail--;
+		}
 	}
 	if ((i < tail && line[tail + 1] != '\n')) {
 		/* This has whitespace between tail+1..len */
-- 
1.5.3.1

^ permalink raw reply related

* [PATCH] Update git-sh-setup(1) to allow transparent use of git-parseopt(1)
From: Pierre Habouzit @ 2007-11-02 15:09 UTC (permalink / raw)
  To: gitster, torvalds, Junio C Hamano, Linus Torvalds; +Cc: git, Pierre Habouzit
In-Reply-To: <1194016162-23599-2-git-send-email-madcoder@debian.org>

If you set OPTIONS_SPEC, git-sh-setups uses git-parseopt automatically.
It also diverts usage to re-exec $0 with the -h option as parse-options.c
will catch that.

PARSEOPT_OPTS can also be used to pass options git git-parseopt(1) (like
--keep-dashdash).
---
 git-sh-setup.sh |   44 ++++++++++++++++++++++++++------------------
 1 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 86d7d4c..d7b13e0 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -16,9 +16,32 @@ die() {
 	exit 1
 }
 
-usage() {
-	die "Usage: $0 $USAGE"
-}
+if test -n "$OPTIONS_SPEC"; then
+	usage() {
+		exec "$0" -h
+	}
+
+	eval `echo "$OPTIONS_SPEC" | git parseopt $PARSEOPT_OPTS -- "$@" || echo exit $?`
+else
+	usage() {
+		die "Usage: $0 $USAGE"
+	}
+
+	if [ -z "$LONG_USAGE" ]
+	then
+		LONG_USAGE="Usage: $0 $USAGE"
+	else
+		LONG_USAGE="Usage: $0 $USAGE
+
+$LONG_USAGE"
+	fi
+
+	case "$1" in
+		-h|--h|--he|--hel|--help)
+		echo "$LONG_USAGE"
+		exit
+	esac
+fi
 
 set_reflog_action() {
 	if [ -z "${GIT_REFLOG_ACTION:+set}" ]
@@ -91,21 +114,6 @@ get_author_ident_from_commit () {
 	LANG=C LC_ALL=C sed -ne "$pick_author_script"
 }
 
-if [ -z "$LONG_USAGE" ]
-then
-	LONG_USAGE="Usage: $0 $USAGE"
-else
-	LONG_USAGE="Usage: $0 $USAGE
-
-$LONG_USAGE"
-fi
-
-case "$1" in
-	-h|--h|--he|--hel|--help)
-	echo "$LONG_USAGE"
-	exit
-esac
-
 # Make sure we are in a valid repository of a vintage we understand.
 if [ -z "$SUBDIRECTORY_OK" ]
 then
-- 
1.5.3.5.1458.g2aa13-dirty


^ permalink raw reply related

* [PATCH] Migrate git-clean.sh to use git-parseoptions(1)
From: Pierre Habouzit @ 2007-11-02 15:09 UTC (permalink / raw)
  To: gitster, torvalds, Junio C Hamano, Linus Torvalds; +Cc: git, Pierre Habouzit
In-Reply-To: <1194016162-23599-3-git-send-email-madcoder@debian.org>

Also minor consistency tweaks in how errors are caught.
---

    This patch is a pretty good example of the fact that git-parseopt(1)
    is not very disruptive to the general coding style thanks to
    git-sh-setup.

 git-clean.sh |   38 ++++++++++++++++++++------------------
 1 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/git-clean.sh b/git-clean.sh
index 4491738..6959433 100755
--- a/git-clean.sh
+++ b/git-clean.sh
@@ -3,16 +3,21 @@
 # Copyright (c) 2005-2006 Pavel Roskin
 #
 
-USAGE="[-d] [-f] [-n] [-q] [-x | -X] [--] <paths>..."
-LONG_USAGE='Clean untracked files from the working directory
-	-d	remove directories as well
-	-f	override clean.requireForce and clean anyway
-	-n 	don'\''t remove anything, just show what would be done
-	-q	be quiet, only report errors
-	-x	remove ignored files as well
-	-X	remove only ignored files
+OPTIONS_SPEC="\
+git-clean [options] <paths>...
+
+Clean untracked files from the working directory
+
 When optional <paths>... arguments are given, the paths
-affected are further limited to those that match them.'
+affected are further limited to those that match them.
+--
+d remove directories as well
+f override clean.requireForce and clean anyway
+n don't remove anything, just show what would be done
+q be quiet, only report errors
+x remove ignored files as well
+X remove only ignored files"
+
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
 require_work_tree
@@ -55,23 +60,20 @@ do
 		shift
 		break
 		;;
-	-*)
-		usage
-		;;
 	*)
-		break
+		usage # should not happen
+		;;
 	esac
 	shift
 done
 
 if [ "$disabled" = true ]; then
-	echo "clean.requireForce set and -n or -f not given; refusing to clean"
-	exit 1
+	die "clean.requireForce set and -n or -f not given; refusing to clean"
 fi
 
-case "$ignored,$ignoredonly" in
-	1,1) usage;;
-esac
+if [ "$ignored,$ignoredonly" = "1,1" ]; then
+	die "-x and -X cannot be set together"
+fi
 
 if [ -z "$ignored" ]; then
 	excl="--exclude-per-directory=.gitignore"
-- 
1.5.3.5.1458.g2aa13-dirty


^ permalink raw reply related

* [PATCH] Add git-parseopt(1) to bring parse-options to shell scripts.
From: Pierre Habouzit @ 2007-11-02 15:09 UTC (permalink / raw)
  To: gitster, torvalds, Junio C Hamano, Linus Torvalds; +Cc: git, Pierre Habouzit
In-Reply-To: <1194016162-23599-1-git-send-email-madcoder@debian.org>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 8709 bytes --]

---
 .gitignore                     |    1 +
 Documentation/cmd-list.perl    |    1 +
 Documentation/git-parseopt.txt |   91 +++++++++++++++++++++++++++++
 Makefile                       |    1 +
 builtin-parseopt.c             |  123 ++++++++++++++++++++++++++++++++++++++++
 builtin.h                      |    1 +
 git.c                          |    1 +
 7 files changed, 219 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-parseopt.txt
 create mode 100644 builtin-parseopt.c

diff --git a/.gitignore b/.gitignore
index c8c13f5..521155c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,6 +86,7 @@ git-pack-redundant
 git-pack-objects
 git-pack-refs
 git-parse-remote
+git-parseopt
 git-patch-id
 git-peek-remote
 git-prune
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 8d21d42..c491964 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -147,6 +147,7 @@ git-pack-objects                        plumbingmanipulators
 git-pack-redundant                      plumbinginterrogators
 git-pack-refs                           ancillarymanipulators
 git-parse-remote                        synchelpers
+git-parseopt                            plumbingmanipulators
 git-patch-id                            purehelpers
 git-peek-remote                         purehelpers
 git-prune                               ancillarymanipulators
diff --git a/Documentation/git-parseopt.txt b/Documentation/git-parseopt.txt
new file mode 100644
index 0000000..77f5a10
--- /dev/null
+++ b/Documentation/git-parseopt.txt
@@ -0,0 +1,91 @@
+git-parseopt(1)
+============
+
+NAME
+----
+git-parseopt - Git shell script helper similar to getopt(1)
+
+
+SYNOPSIS
+--------
+'git-parseopt' [-u] [--keep-dashdash] -- [<args>...]
+
+DESCRIPTION
+-----------
+
+This is a helper for scripts to behave the same as git C commands. It merely
+breaks and normalize options so that the option parsing becomes shorter and
+easier to read. It takes the specifications of the options to undersand on its
+standard input.
+
+In case of error, it ouputs usage on the standard error stream, and exits with
+code 129. Else its ouput is suitable for evaluation with "eval" to replace
+the script arguments with the normalized form.
+
+OPTIONS
+-------
+
+--keep-dash-dash::
+	Keep the `--` in `<args>...`.
+
+\--::
+	Following arguments are the options to parse. This isn't optional.
+
+<args>...::
+	Options to parse and deal with
+
+INPUT FORMAT
+------------
+
+`git-parseopt` input format is fully text based. It has two parts, separated
+by a line that contains only `--`. The lines before (should be more than one)
+is used for the usage. The lines after describe the options.
+
+Each line of options has this format:
+
+------------
+<opt_spec><arg_spec>? SP+ help LF
+------------
+
+`<opt_spec>`::
+	its format is the short option character, then the long option name
+        separated by a comma. Both parts are not required, though at least one
+        is necessary. `h,help`, `dry-run` and `f` are all three correct
+        `<opt_spec>`.
+
+`<arg_spec>`::
+	an `<arg_spec>` tells the option parser if the option has an argument
+        (`=`), an optionnal one (`?` though its use is discouraged) or none
+        (no `<arg_spec>` in that case).
+
+The rest of the line after as many spaces up to the ending line feed is used
+as the help associated to the option.
+
+EXAMPLE
+-------
+
+------------
+OPTS_SPEC="\
+some-command [options] <args>...
+
+some-command does foo and bar !
+--
+h,help    show the help
+foo       some nifty option --foo
+bar=      some cool option --bar with an argument
+C?        option C with an optionnal argument"
+
+eval `echo "$OPTS_SPEC" | git-parseopt -- "$@" || echo exit $?`
+------------
+
+Author
+------
+Written by Pierre Habouzit <madcoder@debian.org>
+
+Documentation
+--------------
+Documentation by Pierre Habouzit.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index 042f79e..7c5df55 100644
--- a/Makefile
+++ b/Makefile
@@ -352,6 +352,7 @@ BUILTIN_OBJS = \
 	builtin-mv.o \
 	builtin-name-rev.o \
 	builtin-pack-objects.o \
+	builtin-parseopt.o \
 	builtin-prune.o \
 	builtin-prune-packed.o \
 	builtin-push.o \
diff --git a/builtin-parseopt.c b/builtin-parseopt.c
new file mode 100644
index 0000000..c032f5a
--- /dev/null
+++ b/builtin-parseopt.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2007 Pierre Habouzit <madcoder@debian.org>
+ */
+#include "git-compat-util.h"
+#include "cache.h"
+#include "quote.h"
+#include "parse-options.h"
+
+static int keep_dashdash = 0;
+static struct strbuf parsed = STRBUF_INIT;
+
+static struct option parseopt_opts[] = {
+	OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
+				"keep the `--` passed as an arg"),
+	OPT_END(),
+};
+
+static char const * const parseopt_usage[] = {
+	"git-parseopt [options] -- [<args>...]",
+	NULL
+};
+
+static int parseopt_dump(const struct option *o, const char *arg, int unset)
+{
+	if (unset)
+		strbuf_addf(&parsed, " --no-%s", o->long_name);
+	else if (o->short_name)
+		strbuf_addf(&parsed, " -%c", o->short_name);
+	else
+		strbuf_addf(&parsed, " --%s", o->long_name);
+	if (arg) {
+		strbuf_addch(&parsed, ' ');
+		sq_quote_buf(&parsed, arg);
+	}
+	return 0;
+}
+
+static const char *skipspaces(const char *s)
+{
+	while (isspace(*s))
+		s++;
+	return s;
+}
+
+int cmd_parseopt(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf sb;
+	const char **usage = NULL;
+	struct option *opts = NULL;
+	int onb = 0, osz = 0, unb = 0, usz = 0;
+
+	strbuf_addstr(&parsed, "set --");
+	argc = parse_options(argc, argv, parseopt_opts, parseopt_usage,
+	                     PARSE_OPT_KEEP_DASHDASH);
+	if (argc < 1 || strcmp(argv[0], "--"))
+		usage_with_options(parseopt_usage, parseopt_opts);
+
+	strbuf_init(&sb, 0);
+	/* get the usage up to the first line with a -- on it */
+	for (;;) {
+		if (strbuf_getline(&sb, stdin, '\n') == EOF)
+			die("premature end of input");
+		ALLOC_GROW(usage, unb + 1, usz);
+		if (!strcmp("--", sb.buf)) {
+			if (unb < 1)
+				die("no usage string given before the `--' separator");
+			usage[unb] = NULL;
+			break;
+		}
+		usage[unb++] = strbuf_detach(&sb, NULL);
+	}
+
+	/* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
+	while (strbuf_getline(&sb, stdin, '\n') != EOF) {
+		const char *s;
+		struct option *o;
+
+		ALLOC_GROW(opts, onb + 1, osz);
+		memset(opts + onb, 0, sizeof(opts[onb]));
+
+		o = &opts[onb++];
+		s = strchr(sb.buf, ' ');
+		if (!s || *sb.buf == ' ')
+			die("invalid option spec");
+
+		o->type = OPTION_CALLBACK;
+		o->help = xstrdup(skipspaces(s));
+		o->callback = &parseopt_dump;
+		switch (s[-1]) {
+		case '=':
+			s--;
+			break;
+		case '?':
+			o->flags = PARSE_OPT_OPTARG;
+			s--;
+			break;
+		default:
+			o->flags = PARSE_OPT_NOARG;
+			break;
+		}
+
+		if (s - sb.buf == 1) /* short option only */
+			o->short_name = *sb.buf;
+		else if (sb.buf[1] != ',') /* long option only */
+			o->long_name = xmemdupz(sb.buf, s - sb.buf);
+		else {
+			o->short_name = *sb.buf;
+			o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
+		}
+	}
+	strbuf_release(&sb);
+
+	/* put an OPT_END() */
+	ALLOC_GROW(opts, onb + 1, osz);
+	memset(opts + onb, 0, sizeof(opts[onb]));
+	argc = parse_options(argc, argv, opts, usage,
+	                     keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0);
+
+	strbuf_addf(&parsed, " --");
+	sq_quote_argv(&parsed, argv, argc, 0);
+	puts(parsed.buf);
+	return 0;
+}
diff --git a/builtin.h b/builtin.h
index 9a6213a..b2ef7b6 100644
--- a/builtin.h
+++ b/builtin.h
@@ -55,6 +55,7 @@ extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_parseopt(int argc, const char **argv, const char *prefix);
 extern int cmd_pickaxe(int argc, const char **argv, const char *prefix);
 extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
diff --git a/git.c b/git.c
index 4e10581..89524dd 100644
--- a/git.c
+++ b/git.c
@@ -333,6 +333,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
+		{ "parseopt", cmd_parseopt },
 		{ "pickaxe", cmd_blame, RUN_SETUP },
 		{ "prune", cmd_prune, RUN_SETUP },
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
-- 
1.5.3.5.1458.g2aa13-dirty


^ permalink raw reply related

* Bring parse_options to the shell
From: Pierre Habouzit @ 2007-11-02 15:09 UTC (permalink / raw)
  To: gitster, torvalds; +Cc: git

This is also something that itches me, so here is a proposal for a
git-parseopt helper that can be used in shell scripts as an option
normalizer like getopt(1) does.

I migrated the discussed git-clean.sh to use it as a proof of concept.

Cheers,
-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

^ permalink raw reply

* Re: What's cooking in git.git (topics)
From: Miles Bader @ 2007-11-02 15:13 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, git
In-Reply-To: <buofxzp18qp.fsf@dhapc248.dev.necel.com>

I previously wrote:
> Indeed... but for my personal shell scripts I like to use a construct
> like the following for parsing args:

In a little more detail, the arg-splitting case:

>        -[!-]?*)
>          # split concatenated single-letter options apart
>          FIRST="$1"; shift
>          set -- `echo $FIRST | $SED 's/-\(.\)\(.*\)/-\1 -\2/'` "$@"
>          ;;

Just strips off the first short option and stuffs it back into the list
of args to parse, so "-xyz" becomes "-x -yz".  That way short args get
split by default, but short-args with an appended value still work
correctly.

So, for instance, if in the above example, "-y" takes an argument, then
there'd be a switch case case for "-y*") which would consume the "-yz"
before it reached the arg-splitting case; if "-y" _doesn't_ take an
argument, then "-yz" would get split in turn, becoming "-y -z".

-Miles

-- 
/\ /\
(^.^)
(")")
*This is the cute kitty virus, please copy this into your sig so it can spread.

^ permalink raw reply

* Re: Bring parse_options to the shell
From: Pierre Habouzit @ 2007-11-02 15:14 UTC (permalink / raw)
  To: gitster, torvalds; +Cc: git
In-Reply-To: <1194016162-23599-1-git-send-email-madcoder@debian.org>

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

On Fri, Nov 02, 2007 at 03:09:18PM +0000, Pierre Habouzit wrote:
> This is also something that itches me, so here is a proposal for a
> git-parseopt helper that can be used in shell scripts as an option
> normalizer like getopt(1) does.
> 
> I migrated the discussed git-clean.sh to use it as a proof of concept.

  Needless to say, this is fetchable from
git://git.madism.org/git.git#ph/parseopt

-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: [PATCH 1/3] Implement parsing for new core.whitespace.* options.
From: Baz @ 2007-11-02 15:25 UTC (permalink / raw)
  To: David Symonds; +Cc: git, Junio C Hamano, Andreas Ericsson
In-Reply-To: <11940104611948-git-send-email-dsymonds@gmail.com>

On 02/11/2007, David Symonds <dsymonds@gmail.com> wrote:
> Each of the new core.whitespace.* options (enumerated below) can be set to one
> of:
>         * okay (default): Whitespace of this type is okay
>         * warn: Whitespace of this type should be warned about
>         * error: Whitespace of this type should raise an error
>         * autofix: Whitespace of this type should be automatically fixed

Sorry, this is a bit bikesheddy, but shouldn't that be 'fix' rather
than autofix? Otherwise you might want to rename the others
'autowarn', 'autookay' etc... since the computer does them
automatically too.

Cheers,
Baz

^ permalink raw reply

* Re: [PATCH] Implement git commit as a builtin command.
From: Kristian Høgsberg @ 2007-11-02 15:31 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vr6j97ce6.fsf@gitster.siamese.dyndns.org>

On Thu, 2007-11-01 at 16:51 -0700, Junio C Hamano wrote:

Hi Junio,

Thanks for the review (again).  I've split the patch up in a couple of
test suite patches (a patch to accept ':' as a no-op editor, fixing
hard-coding of editor, and a test case for the commit --amend --author
case), exporting launch_editor() and a reworked version of the commit
patch that addresses your comments.

> Kristian Høgsberg <krh@redhat.com> writes:
> 
> > @@ -364,7 +365,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 \
> 
> I did not read in the commit log that runstatus is gone...

True, that should be documented.  Added in the following patch.

> > diff --git a/builtin-commit.c b/builtin-commit.c
> > new file mode 100644
> > index 0000000..56c7427
> > --- /dev/null
> > +++ b/builtin-commit.c
> > @@ -0,0 +1,608 @@
> > +/*
> > + * 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"
> 
> 
> The system header files on some systems have a nasty habit of
> changing the behaviour depending on the order they are included.
> Since 85023577a8f4b540aa64aa37f6f44578c0c305a3 (simplify
> inclusion of system header files) in late 2006, we have a few
> rules for system-header inclusion to avoid the portability
> issues:
> 
>  (1) sources under compat/, platform sha-1 implementations, and
>      xdelta code are exempt from the following rules;
> 
>  (2) the first #include must be "git-compat-util.h" or one of
>      our own header file that includes it first (e.g. config.h,
>      builtin.h, pkt-line.h);
> 
>  (3) system headers that are included in "git-compat-util.h"
>      need not be included in individual C source files.
> 
>  (4) "git-compat-util.h" does not have to include subsystem
>      specific header files (e.g. expat.h).

Ah, yes, this has been pointed out to me before, sorry.  Patch updated.

> > +static void determine_author_info(struct strbuf *sb)
> > +{
> > +	char *p, *eol;
> > +	char *name = NULL, *email = NULL;
> > +
> > +	if (use_message) {
> > +		p = strstr(use_message_buffer, "\nauthor");
> > +		if (!p)
> > +			die("invalid commit: %s\n", use_message);
> > +		p++;
> > +		eol = strchr(p, '\n');
> > +		if (!eol)
> > +			die("invalid commit: %s\n", use_message);
> > +
> > +		strbuf_add(sb, p, eol + 1 - p);
> > +	} else if (force_author) {
> > +		const char *eoname = strstr(force_author, " <");
> > +		const char *eomail = strchr(force_author, '>');
> 
> Doesn't this break:
> 
>     $ git commit --amend --author='A U Thor <author@example.com>'
> 
> to fix a misattribution?

Dang, good catch.  I've fixed it by swapping the (use_message) and
(force_author) clauses and the new patch adds a test case for this.

> > +static int parse_and_validate_options(int argc, const char *argv[])
> > +{
> > ...
> > +	if (use_message) {
> > +		unsigned char sha1[20];
> > +		static char utf8[] = "UTF-8";
> > +		const char *out_enc;
> > +		char *enc, *end;
> > +		struct commit *commit;
> > +
> > +		if (get_sha1(use_message, sha1))
> > +			die("could not lookup commit %s", use_message);
> > +		commit = lookup_commit(sha1);
> > +		if (!commit || parse_commit(commit))
> > +			die("could not parse commit %s", use_message);
> > +
> > +		enc = strstr(commit->buffer, "\nencoding");
> > +		if (enc) {
> > +			end = strchr(enc + 10, '\n');
> > +			enc = xstrndup(enc + 10, end - (enc + 10));
> > +		} else {
> > +			enc = utf8;
> > +		}
> > +		out_enc = git_commit_encoding ? git_commit_encoding : utf8;
> > +
> > +		use_message_buffer =
> > +			reencode_string(commit->buffer, out_enc, enc);
> > +		if (enc != utf8)
> > +			free(enc);
> 
> A few issues.
> 
>  * When use_message is set because of --amend that wanted to
>    amend a commit message that was recorded in a corrupt
>    encoding, and iconv() in reencode_string() fails, saying "I
>    cannot convert that completely", most of the message can
>    still be salvageable.  This part should allow looser
>    reencoding if the message will be eyeballed and edited by the
>    user.

So in this case we just want to copy the old message byte for byte,
right?  That's what I have in the updated patch.

>  * We would want to avoid reencoding when !strcmp(out_enc, enc).
>    Both builtin-mailinfo.c and commit.c skip the call to the
>    function at the calling site, but it might make sense to
>    teach reencode_string() about such an optimization.

Right.  I just added the call-site optimization for builtin-commit for
now, but I would expect iconv() to be smart in case input and output
encodings are the same.

cheers,
Kristian

^ permalink raw reply

* [PATCH 1/4] Add testcase for ammending and fixing author in git commit.
From: Kristian Høgsberg @ 2007-11-02 15:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
 t/t7501-commit.sh |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index b151b51..3f2112a 100644
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -163,4 +163,14 @@ test_expect_success 'partial commit that involves removal (3)' '
 
 '
 
+author="The Real Author <someguy@his.email.org>"
+test_expect_success 'amend commit to fix author' '
+
+	git reset --hard
+	git cat-file -p HEAD | sed -e "s/author.*>/author $author/" > expected &&
+	git commit --amend --author="$author" &&
+	git cat-file -p HEAD > current &&
+	diff expected current
+	
+'
 test_done
-- 
1.5.3.4.206.g58ba4

^ permalink raw reply related

* [PATCH 2/4] Remove unecessary hard-coding of EDITOR=':' VISUAL=':' in some test suites.
From: Kristian Høgsberg @ 2007-11-02 15:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <1194017589-4669-1-git-send-email-krh@redhat.com>

Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
 t/t3501-revert-cherry-pick.sh |    4 ++--
 t/t3901-i18n-patch.sh         |    8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 552af1c..2dbe04f 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -44,7 +44,7 @@ test_expect_success setup '
 test_expect_success 'cherry-pick after renaming branch' '
 
 	git checkout rename2 &&
-	EDITOR=: VISUAL=: git cherry-pick added &&
+	git cherry-pick added &&
 	test -f opos &&
 	grep "Add extra line at the end" opos
 
@@ -53,7 +53,7 @@ test_expect_success 'cherry-pick after renaming branch' '
 test_expect_success 'revert after renaming branch' '
 
 	git checkout rename1 &&
-	EDITOR=: VISUAL=: git revert added &&
+	git revert added &&
 	test -f spoo &&
 	! grep "Add extra line at the end" spoo
 
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 28e9e37..235f372 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -154,7 +154,7 @@ test_expect_success 'cherry-pick(U/U)' '
 	git reset --hard master &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
-	EDITOR=: VISUAL=: git revert HEAD &&
+	git revert HEAD &&
 
 	check_encoding 3
 '
@@ -169,7 +169,7 @@ test_expect_success 'cherry-pick(L/L)' '
 	git reset --hard master &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
-	EDITOR=: VISUAL=: git revert HEAD &&
+	git revert HEAD &&
 
 	check_encoding 3 8859
 '
@@ -184,7 +184,7 @@ test_expect_success 'cherry-pick(U/L)' '
 	git reset --hard master &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
-	EDITOR=: VISUAL=: git revert HEAD &&
+	git revert HEAD &&
 
 	check_encoding 3
 '
@@ -200,7 +200,7 @@ test_expect_success 'cherry-pick(L/U)' '
 	git reset --hard master &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
-	EDITOR=: VISUAL=: git revert HEAD &&
+	git revert HEAD &&
 
 	check_encoding 3 8859
 '
-- 
1.5.3.4.206.g58ba4

^ permalink raw reply related

* [PATCH 3/4] Export launch_editor() and make it accept ':' as a no-op editor.
From: Kristian Høgsberg @ 2007-11-02 15:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <1194017589-4669-2-git-send-email-krh@redhat.com>

Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
 builtin-tag.c |    5 ++++-
 strbuf.h      |    1 +
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/builtin-tag.c b/builtin-tag.c
index 66e5a58..bcb3a7a 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -17,7 +17,7 @@ static const char builtin_tag_usage[] =
 
 static char signingkey[1000];
 
-static void launch_editor(const char *path, struct strbuf *buffer)
+void launch_editor(const char *path, struct strbuf *buffer)
 {
 	const char *editor, *terminal;
 	struct child_process child;
@@ -42,6 +42,9 @@ static void launch_editor(const char *path, struct strbuf *buffer)
 	if (!editor)
 		editor = "vi";
 
+	if (!strcmp(editor, ":"))
+		return;
+
 	memset(&child, 0, sizeof(child));
 	child.argv = args;
 	args[0] = editor;
diff --git a/strbuf.h b/strbuf.h
index 9b9e861..9720826 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -113,5 +113,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 extern int strbuf_getline(struct strbuf *, FILE *, int);
 
 extern void stripspace(struct strbuf *buf, int skip_comments);
+extern void launch_editor(const char *path, struct strbuf *buffer);
 
 #endif /* STRBUF_H */
-- 
1.5.3.4.206.g58ba4

^ permalink raw reply related

* [PATCH 4/4] Implement git commit and status as a builtin commands.
From: Kristian Høgsberg @ 2007-11-02 15:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <1194017589-4669-3-git-send-email-krh@redhat.com>

Move git-commit.sh to contrib/examples.  This also removes the git-runstatus
helper, which was mostly just a git-status.sh implementation detail.

Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
 Makefile                                        |    9 +-
 builtin-commit.c                                |  614 +++++++++++++++++++++++
 builtin.h                                       |    3 +-
 git-commit.sh => contrib/examples/git-commit.sh |    0 
 git.c                                           |    3 +-
 5 files changed, 621 insertions(+), 8 deletions(-)
 create mode 100644 builtin-commit.c
 rename git-commit.sh => contrib/examples/git-commit.sh (100%)

diff --git a/Makefile b/Makefile
index 042f79e..689eb33 100644
--- a/Makefile
+++ b/Makefile
@@ -209,7 +209,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-ls-remote.sh \
 	git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
 	git-pull.sh git-rebase.sh git-rebase--interactive.sh \
@@ -256,7 +256,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
@@ -326,6 +326,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 \
@@ -364,7 +365,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 \
@@ -830,9 +830,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) $@ $@+ && \
 	sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
diff --git a/builtin-commit.c b/builtin-commit.c
new file mode 100644
index 0000000..a5a9e3e
--- /dev/null
+++ b/builtin-commit.c
@@ -0,0 +1,614 @@
+/*
+ * 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 "git-compat-util.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"
+#include "strbuf.h"
+#include "utf8.h"
+#include "parse-options.h"
+
+static const char * const builtin_commit_usage[] = {
+	"git-commit [options] [--] <filepattern>...",
+	NULL
+};
+
+static unsigned char head_sha1[20], merge_head_sha1[20];
+static char *use_message_buffer;
+static const char commit_editmsg[] = "COMMIT_EDITMSG";
+static struct lock_file lock_file;
+
+static char *logfile, *force_author, *message, *template_file;
+static char *edit_message, *use_message;
+static int all, edit_flag, also, interactive, only, amend, signoff;
+static int quiet, verbose, untracked_files, no_verify;
+
+static int no_edit, initial_commit, in_merge;
+const char *only_include_assumed;
+
+static struct option builtin_commit_options[] = {
+	OPT__QUIET(&quiet),
+	OPT__VERBOSE(&verbose),
+	OPT_GROUP("Commit message options"),
+
+	OPT_STRING('F', "file", &logfile, "FILE", "read log from file"),
+	OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
+	OPT_STRING('m', "message", &message, "MESSAGE", "specify commit message"),
+	OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
+	OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
+	OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by: header"),
+	OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
+	OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+
+	OPT_GROUP("Commit contents options"),
+	OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
+	OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
+	OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+	OPT_BOOLEAN('o', "only", &only, ""),
+	OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+	OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
+	OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
+
+	OPT_END()
+};
+
+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(verbose, NULL, files);
+		if (write_cache(fd, active_cache, active_nr) || close(fd))
+			die("unable to write new_index file");
+		return lock_file.filename;
+	} else if (also) {
+		add_files_to_cache(verbose, prefix, files);
+		if (write_cache(fd, active_cache, active_nr) || close(fd))
+			die("unable to write new_index file");
+		return lock_file.filename;
+	}
+
+	if (interactive)
+		interactive_add();
+
+	if (*files == NULL) {
+		/* Commit index as-is. */
+		rollback_lock_file(&lock_file);
+		return get_index_file();
+	}
+
+	/* update the user index file */
+	add_files_to_cache(verbose, prefix, files);
+	if (write_cache(fd, active_cache, active_nr) || close(fd))
+		die("unable to write new_index file");
+
+	if (!initial_commit) {
+		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(verbose, prefix, files);
+	if (write_cache(fd, active_cache, active_nr) || close(fd))
+		die("unable to write new_index file");
+
+	return next_index_lock->filename;
+}
+
+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)
+{
+	struct stat statbuf;
+	int commitable;
+	struct strbuf sb;
+	char *buffer;
+	FILE *fp;
+
+	strbuf_init(&sb, 0);
+	if (message) {
+		strbuf_add(&sb, message, strlen(message));
+	} else if (logfile && !strcmp(logfile, "-")) {
+		if (isatty(0))
+			fprintf(stderr, "(reading log message from standard input)\n");
+		if (strbuf_read(&sb, 0, 0) < 0)
+			die("could not read log from standard input");
+	} else if (logfile) {
+		if (strbuf_read_file(&sb, logfile, 0) < 0)
+			die("could not read log file '%s': %s",
+			    logfile, strerror(errno));
+	} else if (use_message) {
+		buffer = strstr(use_message_buffer, "\n\n");
+		if (!buffer || buffer[2] == '\0')
+			die("commit has empty message");
+		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+	} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
+		if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
+			die("could not read MERGE_MSG: %s", strerror(errno));
+	} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
+		if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
+			die("could not read SQUASH_MSG: %s", strerror(errno));
+	} else if (!stat(template_file, &statbuf)) {
+		if (strbuf_read_file(&sb, template_file, 0) < 0)
+			die("could not read %s: %s",
+			    template_file, strerror(errno));
+	}
+
+	fp = fopen(git_path(commit_editmsg), "w");
+	if (fp == NULL)
+		die("could not open %s\n", git_path(commit_editmsg));
+		
+	stripspace(&sb, 0);
+	if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
+		die("could not write commit template: %s\n",
+		    strerror(errno));
+
+	if (signoff) {
+		const char *info, *bol;
+
+		info = git_committer_info(1);
+		strbuf_addch(&sb, '\0');
+		bol = strrchr(sb.buf + sb.len - 1, '\n');
+		if (!bol || prefixcmp(bol, sign_off_header))
+			fprintf(fp, "\n");
+		fprintf(fp, "%s%s\n", sign_off_header, git_committer_info(1));
+	}
+
+	strbuf_release(&sb);
+
+	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;
+}
+
+/* Find out if the message starting at position 'start' in the strbuf
+ * contains only whitespace and Signed-off-by lines. */
+static int message_is_empty(struct strbuf *sb, int start)
+{
+	struct strbuf tmpl;
+	const char *nl;
+	int eol, i;
+
+	/* See if the template is just a prefix of the message. */
+	strbuf_init(&tmpl, 0);
+	if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
+		stripspace(&tmpl, 1);
+		if (start + tmpl.len <= sb->len &&
+		    memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
+			start += tmpl.len;
+	}
+	strbuf_release(&tmpl);
+
+	/* Check if the rest is just whitespace and Signed-of-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    !prefixcmp(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+static void determine_author_info(struct strbuf *sb)
+{
+	char *p, *eol;
+	char *name = NULL, *email = NULL;
+
+	if (force_author) {
+		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);
+		strbuf_addf(sb, "author %s\n",
+			    fmt_ident(name, email, 
+				      getenv("GIT_AUTHOR_DATE"), 1));
+		free(name);
+		free(email);
+	} else if (use_message) {
+		p = strstr(use_message_buffer, "\nauthor");
+		if (!p)
+			die("invalid commit: %s\n", use_message);
+		p++;
+		eol = strchr(p, '\n');
+		if (!eol)
+			die("invalid commit: %s\n", use_message);
+
+		strbuf_add(sb, p, eol + 1 - p);
+	} else {
+		strbuf_addf(sb, "author %s\n", git_author_info(1));
+	}
+}
+
+static int parse_and_validate_options(int argc, const char *argv[])
+{
+	int f = 0;
+
+	argc = parse_options(argc, argv, builtin_commit_options,
+			     builtin_commit_usage, 0);
+
+	if (logfile || message || use_message)
+		no_edit = 1;
+	if (edit_flag)
+		no_edit = 0;
+
+	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 (f > 1)
+		die("Only one of -c/-C/-F can be used.");
+	if (message && f > 0)
+		die("Option -m cannot be combined with -c/-C/-F.");
+	if (edit_message)
+		use_message = edit_message;
+	if (amend)
+		use_message = "HEAD";
+	if (use_message) {
+		unsigned char sha1[20];
+		static char utf8[] = "UTF-8";
+		const char *out_enc;
+		char *enc, *end;
+		struct commit *commit;
+
+		if (get_sha1(use_message, sha1))
+			die("could not lookup commit %s", use_message);
+		commit = lookup_commit(sha1);
+		if (!commit || parse_commit(commit))
+			die("could not parse commit %s", use_message);
+
+		enc = strstr(commit->buffer, "\nencoding");
+		if (enc) {
+			end = strchr(enc + 10, '\n');
+			enc = xstrndup(enc + 10, end - (enc + 10));
+		} else {
+			enc = utf8;
+		}
+		out_enc = git_commit_encoding ? git_commit_encoding : utf8;
+
+		if (strcmp(out_enc, enc))
+			use_message_buffer =
+				reencode_string(commit->buffer, out_enc, enc);
+
+		/* If we failed to reencode the buffer, just copy it
+		 * byte for byte so the user can try to fix it up.
+		 * This also handles the case where input and output
+		 * encodings are identical. */
+		if (use_message_buffer == NULL)
+			use_message_buffer = xstrdup(commit->buffer);
+		if (enc != utf8)
+			free(enc);
+	}
+
+	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 && argc > 0)
+		die("Paths with -a does not make sense.");
+	else if (interactive && argc > 0)
+		die("Paths with --interactive does not make sense.");
+
+	return argc;
+}
+
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+	const char *index_file;
+	int commitable;
+
+	git_config(git_status_config);
+
+	argc = parse_and_validate_options(argc, argv);
+
+	index_file = prepare_index(argv, prefix);
+
+	commitable = run_status(stdout, index_file);
+
+	rollback_lock_file(&lock_file);
+
+	return commitable ? 0 : 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);
+}
+
+int git_commit_config(const char *k, const char *v)
+{
+	if (!strcmp(k, "commit.template")) {
+		template_file = xstrdup(v);
+		return 0;
+	}
+
+	return git_status_config(k, v);
+}
+
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"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(int argc, const char **argv, const char *prefix)
+{
+	int header_len, parent_count = 0;
+	struct strbuf sb;
+	const char *index_file, *reflog_msg;
+	char *nl, *header_line;
+	unsigned char commit_sha1[20];
+	struct ref_lock *ref_lock;
+
+	git_config(git_commit_config);
+
+	argc = parse_and_validate_options(argc, argv);
+
+	index_file = prepare_index(argv, prefix);
+
+	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+		exit(1);
+
+	if (!prepare_log_message(index_file) && !in_merge) {
+		run_status(stdout, index_file);
+		unlink(commit_editmsg);
+		return 1;
+	}
+
+	strbuf_init(&sb, 0);
+
+	/* Start building up the commit header */
+	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");
+	strbuf_addf(&sb, "tree %s\n",
+		    sha1_to_hex(active_cache_tree->sha1));
+
+	/* Determine parents */
+	if (initial_commit) {
+		reflog_msg = "commit (initial)"; 
+		parent_count = 0;
+	} else if (amend) {
+		struct commit_list *c;
+		struct commit *commit;
+
+		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)
+			strbuf_addf(&sb, "parent %s\n",
+				      sha1_to_hex(c->item->object.sha1));
+	} else if (in_merge) {
+		struct strbuf m;
+		FILE *fp;
+
+		reflog_msg = "commit (merge)";
+		strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+		strbuf_init(&m, 0);
+		fp = fopen(git_path("MERGE_HEAD"), "r");
+		if (fp == NULL)
+			die("could not open %s for reading: %s",
+			    git_path("MERGE_HEAD"), strerror(errno));
+		while (strbuf_getline(&m, fp, '\n') != EOF)
+			strbuf_addf(&sb, "parent %s\n", m.buf);
+		fclose(fp);
+		strbuf_release(&m);
+	} else {
+		reflog_msg = "commit";
+		strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+	}
+
+	determine_author_info(&sb);
+	strbuf_addf(&sb, "committer %s\n", git_committer_info(1));
+	if (!is_encoding_utf8(git_commit_encoding))
+		strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
+	strbuf_addch(&sb, '\n');
+
+	/* Get the commit message and validate it */
+	header_len = sb.len;
+	if (!no_edit) {
+		fprintf(stderr, "launching editor, log %s\n", logfile);
+		launch_editor(git_path(commit_editmsg), &sb);
+	}
+	else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0)
+		die("could not read commit message\n");
+	if (run_hook(index_file, "commit-msg", commit_editmsg))
+		exit(1);
+	stripspace(&sb, 1);
+	if (sb.len < header_len ||
+	    message_is_empty(&sb, header_len))
+		die("* no commit message?  aborting commit.");
+	strbuf_addch(&sb, '\0');
+	if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
+		fprintf(stderr, commit_utf8_warn);
+
+	if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1))
+		die("failed to write commit object");
+		       
+	ref_lock = lock_any_ref_for_update("HEAD",
+					   initial_commit ? NULL : head_sha1,
+					   0);
+
+	nl = strchr(sb.buf + header_len, '\n');
+	header_line = xstrndup(sb.buf + header_len,
+			       nl - (sb.buf + header_len));
+	strbuf_release(&sb);
+	strbuf_addf(&sb, "%s: %s\n", reflog_msg, header_line);
+	strbuf_addch(&sb, '\0');
+	free(header_line);
+
+	if (!ref_lock)
+		die("cannot lock HEAD ref");
+	if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 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");
+
+	rerere();
+
+	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 9a6213a..0f8456c 100644
--- a/builtin.h
+++ b/builtin.h
@@ -24,6 +24,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);
@@ -68,10 +69,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_tag(int argc, const char **argv, const char *prefix);
diff --git a/git-commit.sh b/contrib/examples/git-commit.sh
similarity index 100%
rename from git-commit.sh
rename to contrib/examples/git-commit.sh
diff --git a/git.c b/git.c
index 4e10581..8522095 100644
--- a/git.c
+++ b/git.c
@@ -298,6 +298,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
 		{ "cherry", cmd_cherry, RUN_SETUP },
 		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "config", cmd_config },
 		{ "count-objects", cmd_count_objects, RUN_SETUP },
@@ -346,10 +347,10 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },
 		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
 		{ "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE },
-		{ "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
 		{ "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 | NEED_WORK_TREE },
 		{ "stripspace", cmd_stripspace },
 		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 		{ "tag", cmd_tag, RUN_SETUP },
-- 
1.5.3.4.206.g58ba4

^ permalink raw reply related

* Re: New features in gitk
From: Linus Torvalds @ 2007-11-02 15:42 UTC (permalink / raw)
  To: Marco Costalba; +Cc: Paul Mackerras, git
In-Reply-To: <e5bfff550711020544h1e9a648apfd268eb549645ccc@mail.gmail.com>



On Fri, 2 Nov 2007, Marco Costalba wrote:
> 
> I have tried to overcome --topo-order in qgit but I found it very
> difficult, too much for me.
> 
> Lazily drawing the layout it doesn't mean that you lazy load the data
> from git, indeed you load all the git-log output as soon as it
> arrives.

Would it be more palatable if I tried to write some visualization-specific 
front-end that acted kind of like "git rev-list", but would have some way 
of "resetting" its output?

The thing is, I'm pretty sure I can feed you commits really quickly if I 
don't sort them, and if I don't do the full and careful "oops, this commit 
was reachable from a commit that was marked uninteresting", but while the 
fast-and-stupid approach will work well enough for most things, it will 
occasionally get the wrong answer.

But it will *notice* when it gets the wrong answer, though, and can reset 
and start over!

IOW, I might be able to do something that

 - prints out the commit info per line

 - prepends each line with a line number

 - goes back to an earlier line 'n' when it notices that it needs to 
   output a commit before a previous commit (or when it notices that a 
   commit that it had already output was actually not supposed to show up)

and with something like that, I could make git give you incremental 
output.

The thing is, any revision information that requires "global knowledge" 
simply cannot scale. And "git rev-list --topo-order" may be fast as hell, 
and I can do it in one second on the kernel archive on my machine, but 
that's really only true when it's all cached. 

If it's not cached, it will inevitably have to read in every single commit 
if you want a "final and unchanging ordering". Which inevitably gets you a 
really irritating startup latency. That's just fundamental.

On the other hand, if there is some way to say "oops, restart", I can 
optimistically give you a list that is always properly sorted on a *local* 
scale, but then based on later data I might notice that it wasn't right 
globally and that I need to re-do all or part of it.

But as mentioned, that requires that side-band data of "uhhuh, I screwed 
up, let me go back and fix it".

			Linus

^ permalink raw reply

* Re: [PATCH 3/4] Export launch_editor() and make it accept ':' as a no-op editor.
From: Andreas Ericsson @ 2007-11-02 15:46 UTC (permalink / raw)
  To: Kristian Høgsberg; +Cc: Junio C Hamano, git
In-Reply-To: <1194017589-4669-3-git-send-email-krh@redhat.com>

Kristian Høgsberg wrote:
>  
> +	if (!strcmp(editor, ":"))
> +		return;
> +

Doesn't this make the change in 2/4 superfluous?

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* [PATCH] New script: git-changelog.perl
From: Ronald Landheer-Cieslak @ 2007-11-02 15:55 UTC (permalink / raw)
  To: git

Hello all,

I've written a little script that will format the changes as reported
by git-log in a ChangeLog-like format. Entries look like this:
<date>\t<author>\n\t<sha1>: <subject>

An example taken from Gits own recent history:

2007-10-30      Junio C Hamano
        9c51414: Merge branch 'maint' into HEAD
        7eedc1c: Merge branch 'nd/worktree' into HEAD
        9725bb8: Merge branch 'cc/skip' into HEAD
        7ae4dd0: Merge branch 'jk/send-pack' into HEAD
        7e9a464: Merge branch 'lt/rename' into HEAD
        6beb669: Merge branch 'jn/web' into HEAD
        791e421: Merge branch 'ds/gitweb' into HEAD
        5153399: Merge branch 'js/rebase' into HEAD
        0bdb5af: Update GIT 1.5.3.5 Release Notes

2007-10-30      Gerrit Pape
        fee9832: No longer install git-svnimport, move to contrib/examples

I intend to use it for releases of my software, and I though others
might like it as well.

I haven't implemented any help yet, but if any-one would like some
documentation, I'll be glad to provide it.

rlc

diff --git a/Makefile b/Makefile
index 71479a2..cf2cb32 100644
--- a/Makefile
+++ b/Makefile
@@ -226,7 +226,7 @@ SCRIPT_PERL = \
        git-add--interactive.perl \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
        git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
-       git-send-email.perl git-svn.perl
+       git-send-email.perl git-svn.perl git-changelog.perl

 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
diff --git a/git-changelog.perl b/git-changelog.perl
new file mode 100755
index 0000000..bffa1ab
--- /dev/null
+++ b/git-changelog.perl
@@ -0,0 +1,68 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+sub fetch_log {
+       my $command='git log --pretty=format:"%ai|%an|%h|%s" ';
+       foreach (@_) {
+               $command .= ' ' . $_;
+       }
+       $command .= " |";
+       my $fh;
+       open $fh, $command
+               or die "Failed to fetch logs";
+       # logs are presented as lines in which fields are separated by pipes.
+       # the fields are the date in ISO format (so the date as we
want it extends to the
+       # first space), the name of the author, the abbreviated SHA1
hash and the subject line
+       my $date_time;
+       my $date;
+       my $cruft;
+       my $author;
+       my $hash;
+       my $subject;
+       my $prev_date="";
+       my @cache; # a cache of the changes for the current date (i.e.
while $prev_date eq $date)
+       my %entry; # a cache entry; $entry{'author'} is the entry and
@$entry{'changes'} are the changes for the author in question
+       while (<$fh>) {
+               ($date_time, $author, $hash, $subject) = split(/\|/);
+               ($date, $cruft) = split(/\s/, $date_time, 2);
+               chomp $author;
+
+               if ($date ne $prev_date)
+               {
+                       foreach (@cache)
+                       {
+                               # print out the line with the date and
the author
+                               my $changes = $_->{'changes'};
+                               print "\n" . $prev_date . "\t" .
$_->{'author'} . "\n" if ($#{$changes} != -1);
+                               foreach (@{$changes})
+                               {
+                                       print "\t" . $_->{'hash'} . ':
' . $_->{'subject'};
+                               }
+                       }
+                       $prev_date = $date;
+                       @cache = ();
+               }
+               # try to find an entry with the same author in the cache
+               my $found = -1;
+               my $i;
+               for ($i = 0; $i <= $#cache && $found == -1; $i++)
+               {
+                       if ($cache[$i]->{'author'} eq $author)
+                       {
+                               $found = $i;
+                       }
+               }
+               if ($found == -1)
+               {
+                       my $changes = ();
+                       push @cache, { 'author', $author, 'changes', $changes };
+                       $found = $#cache;
+               }
+               push @{$cache[$found]->{'changes'}}, { 'hash', $hash,
'subject', $subject };
+       }
+}
+
+fetch_log @ARGV;
+
+



-- 
Ronald Landheer-Cieslak
Software Architect
http://www.landheer-cieslak.com/
New White Paper: "Three Good Reasons to Plan Ahead"

^ permalink raw reply related

* Re: Bring parse_options to the shell
From: Linus Torvalds @ 2007-11-02 15:51 UTC (permalink / raw)
  To: Pierre Habouzit; +Cc: gitster, git
In-Reply-To: <20071102151453.GB27505@artemis.corp>



On Fri, 2 Nov 2007, Pierre Habouzit wrote:

> On Fri, Nov 02, 2007 at 03:09:18PM +0000, Pierre Habouzit wrote:
> > This is also something that itches me, so here is a proposal for a
> > git-parseopt helper that can be used in shell scripts as an option
> > normalizer like getopt(1) does.
> > 
> > I migrated the discussed git-clean.sh to use it as a proof of concept.
> 
>   Needless to say, this is fetchable from
> git://git.madism.org/git.git#ph/parseopt

Hmm. Any reason why you didn't just extend on "git rev-parse"?

That command was written exactly to parse a command line. This is really 
cheesy, and doesn't really work right (it splits up numbers too), but you 
get the idea..

		Linus

---
 builtin-rev-parse.c |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 8d78b69..10b62f7 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -116,6 +116,17 @@ static int show_flag(const char *arg)
 	if (!(filter & DO_FLAGS))
 		return 0;
 	if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
+		if (arg[0] == '-' && arg[1] != '-') {
+			char split[3];
+			split[0] = '-';
+			split[2] = 0;
+			while (arg[1]) {
+				split[1] = arg[1];
+				arg++;
+				show(split);
+			}
+			return 1;
+		}
 		show(arg);
 		return 1;
 	}

^ permalink raw reply related

* [PATCH] Migrate git-clone to use git-parseopt(1)
From: Pierre Habouzit @ 2007-11-02 15:58 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <1194016162-23599-4-git-send-email-madcoder@debian.org>

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 git-clone.sh |  101 ++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 57 insertions(+), 44 deletions(-)

diff --git a/git-clone.sh b/git-clone.sh
index 0ea3c24..b30005d 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -8,15 +8,35 @@
 # See git-sh-setup why.
 unset CDPATH
 
+OPTIONS_SPEC="\
+git-clone [options] <repo> [<dir>]
+--
+n,no-checkout        don't create a checkout
+bare                 create a bare repository
+naked                create a bare repository
+l,local              to clone from a local repository
+no-hardlinks         don't use local hardlinks, always copy
+s,shared             setup as a shared repository
+template=            path to the template directory
+q,quiet              be quiet
+reference=           reference repository
+o,origin=            use <name> instead of 'origin' to track upstream
+u,upload-pack=       path to git-upload-pack on the remote
+depth=               create a shallow clone of that depth
+use-separate-remote  compatibility, do not use
+no-separate-remote   compatibility, do not use"
+
 die() {
 	echo >&2 "$@"
 	exit 1
 }
 
 usage() {
-	die "Usage: $0 [--template=<template_directory>] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [--depth <n>] [-n] <repo> [<dir>]"
+	exec "$0" -h
 }
 
+eval `echo "$OPTIONS_SPEC" | git parseopt $PARSEOPT_OPTS -- "$@" || echo exit $?`
+
 get_repo_base() {
 	(
 		cd "`/bin/pwd`" &&
@@ -106,64 +126,57 @@ depth=
 no_progress=
 local_explicitly_asked_for=
 test -t 1 || no_progress=--no-progress
-while
-	case "$#,$1" in
-	0,*) break ;;
-	*,-n|*,--no|*,--no-|*,--no-c|*,--no-ch|*,--no-che|*,--no-chec|\
-	*,--no-check|*,--no-checko|*,--no-checkou|*,--no-checkout)
-	  no_checkout=yes ;;
-	*,--na|*,--nak|*,--nake|*,--naked|\
-	*,-b|*,--b|*,--ba|*,--bar|*,--bare) bare=yes ;;
-	*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local)
-	  local_explicitly_asked_for=yes
-	  use_local_hardlink=yes ;;
-	*,--no-h|*,--no-ha|*,--no-har|*,--no-hard|*,--no-hardl|\
-	*,--no-hardli|*,--no-hardlin|*,--no-hardlink|*,--no-hardlinks)
-	  use_local_hardlink=no ;;
-        *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
-          local_shared=yes; ;;
-	1,--template) usage ;;
-	*,--template)
+
+while test $# != 0
+do
+	case "$1" in
+	-n|--no-checkout)
+		no_checkout=yes ;;
+	--naked|--bare)
+		bare=yes ;;
+	-l|--local)
+		local_explicitly_asked_for=yes
+		use_local_hardlink=yes
+		;;
+	--no-hardlinks)
+		use_local_hardlink=no ;;
+	-s|--shared)
+		local_shared=yes ;;
+	--template)
 		shift; template="--template=$1" ;;
-	*,--template=*)
-	  template="$1" ;;
-	*,-q|*,--quiet) quiet=-q ;;
-	*,--use-separate-remote) ;;
-	*,--no-separate-remote)
+	-q|--quiet)
+		quiet=-q ;;
+	--use-separate-remote|--no-separate-remote)
 		die "clones are always made with separate-remote layout" ;;
-	1,--reference) usage ;;
-	*,--reference)
+	--reference)
 		shift; reference="$1" ;;
-	*,--reference=*)
-		reference=`expr "z$1" : 'z--reference=\(.*\)'` ;;
-	*,-o|*,--or|*,--ori|*,--orig|*,--origi|*,--origin)
-		case "$2" in
+	-o,--origin)
+		shift;
+		case "$1" in
 		'')
 		    usage ;;
 		*/*)
-		    die "'$2' is not suitable for an origin name"
+		    die "'$1' is not suitable for an origin name"
 		esac
-		git check-ref-format "heads/$2" ||
-		    die "'$2' is not suitable for a branch name"
+		git check-ref-format "heads/$1" ||
+		    die "'$1' is not suitable for a branch name"
 		test -z "$origin_override" ||
 		    die "Do not give more than one --origin options."
 		origin_override=yes
-		origin="$2"; shift
+		origin="$1"
 		;;
-	1,-u|1,--upload-pack) usage ;;
-	*,-u|*,--upload-pack)
+	-u|--upload-pack)
 		shift
 		upload_pack="--upload-pack=$1" ;;
-	*,--upload-pack=*)
-		upload_pack=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
-	1,--depth) usage;;
-	*,--depth)
+	--depth)
+		shift
+		depth="--depth=$1" ;;
+	--)
 		shift
-		depth="--depth=$1";;
-	*,-*) usage ;;
-	*) break ;;
+		break ;;
+	*)
+		usage ;;
 	esac
-do
 	shift
 done
 
-- 
1.5.3.5.1458.g2aa13-dirty

^ permalink raw reply related

* Re: Bring parse_options to the shell
From: Pierre Habouzit @ 2007-11-02 16:09 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: gitster, git
In-Reply-To: <alpine.LFD.0.999.0711020844310.3342@woody.linux-foundation.org>

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

On Fri, Nov 02, 2007 at 03:51:13PM +0000, Linus Torvalds wrote:
> 
> 
> On Fri, 2 Nov 2007, Pierre Habouzit wrote:
> 
> > On Fri, Nov 02, 2007 at 03:09:18PM +0000, Pierre Habouzit wrote:
> > > This is also something that itches me, so here is a proposal for a
> > > git-parseopt helper that can be used in shell scripts as an option
> > > normalizer like getopt(1) does.
> > > 
> > > I migrated the discussed git-clean.sh to use it as a proof of concept.
> > 
> >   Needless to say, this is fetchable from
> > git://git.madism.org/git.git#ph/parseopt
> 
> Hmm. Any reason why you didn't just extend on "git rev-parse"?

Because I wasn't aware of the fact it was used to massage arguments of a
function :) Though looking at the code git-rev-parse looks quite
complicated and does not works the proper way:

It show()s the arguments along the way, whereas you definitely need to
parse them first if you end up spitting out the usage. I could of course
plumb it as a new "mode" of git-rev-parse, but it sounds awkward.

> That command was written exactly to parse a command line. This is really 
> cheesy, and doesn't really work right (it splits up numbers too), but you 
> get the idea..

  I get the idea, though parse-options is not incremental at all, this
could probably be done, but would complicate the API (we would need to
allocate a state object e.g.). And parseoptions checks that options
getting an argument have one, checks that options exists and so on. It
looks like to me that it's not easy to plumb into rev-parse without
being a brand new independant mode.

  We can do that, if we don't want yet-another-git-builtin/command, but
in the spirit it'll remain a brand new "thing".

  Though I'd be glad to hear about what others think about it.

-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ 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