Git development
 help / color / mirror / Atom feed
* Re: [PATCH] Add "--branches" and "--tags" options to git-rev-parse.
From: Junio C Hamano @ 2006-05-13 17:38 UTC (permalink / raw)
  To: Sean Estabrooks; +Cc: git
In-Reply-To: <BAYC1-PASMTP0299DC98A51B55188BDF96AEAD0@CEZ.ICE>

Sean Estabrooks <seanlkml@sympatico.ca> writes:

> "git branch" uses rev-parse and can become slow when there are many
> tags.  Use the new "--branches" option of rev-parse to speed things up.

Makes sense perhaps.

I understand you added --tags for completeness.  Probably it
would make sense to add --remotes if you are shooting for that.

^ permalink raw reply

* Re: Tracking branch history
From: Shawn Pearce @ 2006-05-13 18:18 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, git
In-Reply-To: <7vbqu1vps3.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano <junkio@cox.net> wrote:
> Linus Torvalds <torvalds@osdl.org> writes:
> 
> > I actually disagree with Junio - I think the logging the name made sense. 
> > If you have a shared repo with multiple people pushing to the same branch, 
> > it's interesting to see who does the pushing.
> 
> Yes, I agree recording that information is interesting.  
> 
> My comment was about how it is recorded.  In the local case
> GIT_COMMITTER_IDENT is what you want as Shawn did in his
> original proposal, but remote "pushing into shared repository"
> case I do not think it is available in general.
> 
> Well, at least that was what I was thinking when I made that
> comment.  However,
> 
> If the pushers arrange to have appropriate environment variables
> while receive-pack does its work (.git/config in the target
> repository is not an appropriate place to get user.* settings
> from in a shared setting), what you say makes perfect sense.
> 
> Having the usual enviornment available would be handy to make
> the hooks on the receiving end to do useful things anyway, so I
> retract that suggestion.
> 
> > I also think it might be good to save the oldsha1 value. Yes, it _should_ 
> > always be the previous sha1 logged, but it's interesting to see in case it 
> > isn't (ie the ref was updated some other way), and it's also interesting 
> > for the first entry after logging has been enabled.
> >
> > 		Linue
> 
> Yes, and also what user-level command was used to cause update
> the ref; was it a merge from remote, own commit, rewind/rebase?
> 
> 	Junia
> 
> ;-)
> 

New version of the patch below; this patch superceeds the prior two.
The format now includes the old sha1 and committer ident, and does
not write a log record if the update-ref is pointless.

Hmm, why does update-ref do a pointless update?

Now the problem with this patch is it doesn't append to the log
in the case of upload-pack or fetch; nor does it record what user
level command caused the update.

  [spearce@pb15 git]$ grep update-ref *.sh *.perl *.py | wc -l
      20

Hmm.  That's easy enough to fix.  Anyone up for a '-m foo' comment
switch to update-ref from the higher-level tools such that foo
appears as part of the log line?

I'll be offline for a few hours but can work on this some more
later tonight.

--> -
Log ref updates to logs/refs/<ref>

If config parameter core.logRefUpdates is true then append a line
to .git/logs/refs/<ref> whenever git-update-ref <ref> is executed.
Each log line contains the following information:

  oldsha1 <SP> newsha1 <SP> committer <LF>

where committer is the current user, date, time and timezone in
the standard GIT ident format.  If the caller is unable to append
to the log file then git-update-ref will fail without updating <ref>.

---

 Documentation/config.txt         |    7 ++++++
 Documentation/git-update-ref.txt |   19 +++++++++++++++
 cache.h                          |    1 +
 config.c                         |    5 ++++
 environment.c                    |    1 +
 update-ref.c                     |   48 ++++++++++++++++++++++++++++++++++++--
 6 files changed, 78 insertions(+), 3 deletions(-)

898e33d9c2def73296697d65dff0676d96adb5d8
diff --git a/Documentation/config.txt b/Documentation/config.txt
index d1a4bec..f06695c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -70,6 +70,13 @@ core.preferSymlinkRefs::
 	This is sometimes needed to work with old scripts that
 	expect HEAD to be a symbolic link.
 
+core.logRefUpdates::
+	If true, `git-update-ref` will append a line to
+	"$GIT_DIR/logs/<ref>" listing the new SHA1 and the date/time
+	of the update.	This information can be used to determine
+	what commit was the tip of a branch "2 days ago".  This value
+	is false by default (no logging).
+
 core.repositoryFormatVersion::
 	Internal variable identifying the repository format and layout
 	version.
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 475237f..edd39d3 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -49,6 +49,25 @@ for reading but not for writing (so we'l
 ref symlink to some other tree, if you have copied a whole
 archive by creating a symlink tree).
 
+Logging Updates
+---------------
+If config parameter "core.logRefUpdates" is true then
+`git-update-ref` will append a line to the log file
+"$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before
+creating the log name) describing the change in ref value.  Log lines
+are formatted as:
+
+    . oldsha1 SP newsha1 SP committer LF
++
+Where "oldsha1" is the 40 character hexadecimal value previously
+stored in <ref>, "newsha1" is the 40 character hexadecimal value of
+<newvalue> and "committer" is the committer's name, email address
+and date in the standard GIT committer ident format.
+
+An update will fail (without changing <ref>) if the current user is
+unable to create a new log file, append to the existing log file
+or does not have committer information available.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>.
diff --git a/cache.h b/cache.h
index b1300cd..887ce20 100644
--- a/cache.h
+++ b/cache.h
@@ -171,6 +171,7 @@ extern void rollback_index_file(struct c
 extern int trust_executable_bit;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
+extern int log_ref_updates;
 extern int warn_ambiguous_refs;
 extern int diff_rename_limit_default;
 extern int shared_repository;
diff --git a/config.c b/config.c
index 0f518c9..f8a814e 100644
--- a/config.c
+++ b/config.c
@@ -232,6 +232,11 @@ int git_default_config(const char *var, 
 		return 0;
 	}
 
+	if (!strcmp(var, "core.logrefupdates")) {
+		log_ref_updates = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "core.warnambiguousrefs")) {
 		warn_ambiguous_refs = git_config_bool(var, value);
 		return 0;
diff --git a/environment.c b/environment.c
index 444c99e..437266e 100644
--- a/environment.c
+++ b/environment.c
@@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME];
 int trust_executable_bit = 1;
 int assume_unchanged = 0;
 int prefer_symlink_refs = 0;
+int log_ref_updates = 0;
 int warn_ambiguous_refs = 1;
 int repository_format_version = 0;
 char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
diff --git a/update-ref.c b/update-ref.c
index fd48742..64fc3db 100644
--- a/update-ref.c
+++ b/update-ref.c
@@ -22,7 +22,7 @@ int main(int argc, char **argv)
 	const char *refname, *value, *oldval, *path;
 	char *lockpath;
 	unsigned char sha1[20], oldsha1[20], currsha1[20];
-	int fd, written;
+	int fd, written, pfxlen;
 
 	setup_git_directory();
 	git_config(git_default_config);
@@ -38,7 +38,9 @@ int main(int argc, char **argv)
 	if (oldval && get_sha1(oldval, oldsha1))
 		die("%s: not a valid old SHA1", oldval);
 
-	path = resolve_ref(git_path("%s", refname), currsha1, !!oldval);
+	path = git_path("%s", refname);
+	pfxlen = strlen(path) - strlen(refname);
+	path = resolve_ref(path, currsha1, !!oldval);
 	if (!path)
 		die("No such ref: %s", refname);
 
@@ -50,7 +52,7 @@ int main(int argc, char **argv)
 			exit(0);
 	}
 	path = strdup(path);
-	lockpath = mkpath("%s.lock", path);
+	lockpath = strdup(mkpath("%s.lock", path));
 	if (safe_create_leading_directories(lockpath) < 0)
 		die("Unable to create all of %s", lockpath);
 
@@ -75,6 +77,46 @@ int main(int argc, char **argv)
 	}
 
 	/*
+	 * Write to the log if logging of ref updates is enabled
+	 */
+	if (log_ref_updates && memcmp(currsha1, sha1, 20)) {
+		char *logrec, *logpath;
+		const char *comitter;
+		unsigned maxlen, len;
+		int logfd;
+
+		setup_ident();
+		comitter = git_committer_info(1);
+		maxlen = strlen(comitter) + 2*40 + 4;
+		logrec = xmalloc(maxlen);
+		len = snprintf(logrec, maxlen, "%s %s %s\n",
+			sha1_to_hex(currsha1),
+			sha1_to_hex(sha1),
+			comitter);
+		if (len >= maxlen) {
+			unlink(lockpath);
+			die("Internal error formatting log record.");
+		}
+
+		logpath = git_path("logs/%s", path + pfxlen);
+		if (safe_create_leading_directories(logpath) < 0) {
+			unlink(lockpath);
+			die("Unable to create all of %s", logpath);
+		}
+		logfd = open(logpath, O_CREAT | O_APPEND | O_WRONLY, 0666);
+		if (logfd < 0) {
+			unlink(lockpath);
+			die("Unable to append to %s", logpath);
+		}
+		written = write(logfd, logrec, len);
+		if (written != len) {
+			unlink(lockpath);
+			die("Unable to append to %s", logpath);
+		}
+		close(logfd);
+	}
+
+	/*
 	 * Finally, replace the old ref with the new one
 	 */
 	if (rename(lockpath, path) < 0) {
-- 
1.3.2.g7278

^ permalink raw reply related

* Re: [RFC] qgit with tabs
From: Pavel Roskin @ 2006-05-13 18:28 UTC (permalink / raw)
  To: Marco Costalba; +Cc: git
In-Reply-To: <e5bfff550605130431w417b8aacl2b17cf5655b46f31@mail.gmail.com>

Quoting Marco Costalba <mcostalba@gmail.com>:

> > Just one thing for now.  Double click on a file on the "rev list" tab
> should
> > show the patch (what Ctrl-P would do), not the whole file.  That would be
> more
> > compatible with gitk, and it's what I normally need if I just browse the
> latest
> > changes in the rev list.
> >
>
> Well, double click activates the current top entry in context menu.

That's a good thing.  But I think that the top entry should be different.

> This behaviour has not changed from past releases.

I know.  But as the more prominent issues are sorted out, minor things become
more visible.

> To show the patch
> perhaps you may, as always, double click on the selected revision, in
> revisions list.

Sure, but I often want to see what changed in a particular file.

And of course I only mean the subwindow dislaying the files affected by the
patch.  The file tree should still have file annotation bound to the double
click.

> FWIK gitk does not have a file content viewer.

That's true.  The reason may be because changes are more interesing than the
whole file in the context of git (as opposed to the context of an editor or a
compiler).

--
Regards,
Pavel Roskin

^ permalink raw reply

* git diff: support "-U" and "--unified" options properly
From: Linus Torvalds @ 2006-05-13 20:23 UTC (permalink / raw)
  To: Junio C Hamano, Git Mailing List


We used to parse "-U" and "--unified" as part of the GIT_DIFF_OPTS 
environment variable, but strangely enough we would _not_ parse them as 
part of the normal diff command line (where we only accepted "-u").

This adds parsing of -U and --unified, both with an optional numeric 
argument. So now you can just say

	git diff --unified=5

to get a unified diff with a five-line context, instead of having to do 
something silly like

	GIT_DIFF_OPTS="--unified=5" git diff -u

(that silly format does continue to still work, of course).

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---

[ Maybe this can still hit 1.3.3? ]

diff --git a/combine-diff.c b/combine-diff.c
index 8a8fe38..64b20cc 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -608,6 +608,7 @@ static int show_patch_diff(struct combin
 	int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
 	mmfile_t result_file;
 
+	context = opt->context;
 	/* Read the result of merge first */
 	if (!working_tree_file)
 		result = grab_blob(elem->sha1, &result_size);
diff --git a/diff.c b/diff.c
index 7a7b839..be925a3 100644
--- a/diff.c
+++ b/diff.c
@@ -558,7 +558,7 @@ static void builtin_diff(const char *nam
 
 		ecbdata.label_path = lbl;
 		xpp.flags = XDF_NEED_MINIMAL;
-		xecfg.ctxlen = 3;
+		xecfg.ctxlen = o->context;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
 		if (!diffopts)
 			;
@@ -1182,6 +1182,7 @@ void diff_setup(struct diff_options *opt
 	options->line_termination = '\n';
 	options->break_opt = -1;
 	options->rename_limit = -1;
+	options->context = 3;
 
 	options->change = diff_change;
 	options->add_remove = diff_addremove;
@@ -1222,11 +1223,60 @@ int diff_setup_done(struct diff_options 
 	return 0;
 }
 
+int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
+{
+	char c, *eq;
+	int len;
+
+	if (*arg != '-')
+		return 0;
+	c = *++arg;
+	if (!c)
+		return 0;
+	if (c == arg_short) {
+		c = *++arg;
+		if (!c)
+			return 1;
+		if (val && isdigit(c)) {
+			char *end;
+			int n = strtoul(arg, &end, 10);
+			if (*end)
+				return 0;
+			*val = n;
+			return 1;
+		}
+		return 0;
+	}
+	if (c != '-')
+		return 0;
+	arg++;
+	eq = strchr(arg, '=');
+	if (eq)
+		len = eq - arg;
+	else
+		len = strlen(arg);
+	if (!len || strncmp(arg, arg_long, len))
+		return 0;
+	if (eq) {
+		int n;
+		char *end;
+		if (!isdigit(*++eq))
+			return 0;
+		n = strtoul(eq, &end, 10);
+		if (*end)
+			return 0;
+		*val = n;
+	}
+	return 1;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
 	const char *arg = av[0];
 	if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
 		options->output_format = DIFF_FORMAT_PATCH;
+	else if (opt_arg(arg, 'U', "unified", &options->context))
+		options->output_format = DIFF_FORMAT_PATCH;
 	else if (!strcmp(arg, "--patch-with-raw")) {
 		options->output_format = DIFF_FORMAT_PATCH;
 		options->with_raw = 1;
diff --git a/diff.h b/diff.h
index d052608..bef586d 100644
--- a/diff.h
+++ b/diff.h
@@ -32,6 +32,7 @@ struct diff_options {
 		 full_index:1,
 		 silent_on_remove:1,
 		 find_copies_harder:1;
+	int context;
 	int break_opt;
 	int detect_rename;
 	int line_termination;

^ permalink raw reply related

* Re: git diff: support "-U" and "--unified" options properly
From: Junio C Hamano @ 2006-05-13 20:30 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.64.0605131317200.3866@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> We used to parse "-U" and "--unified" as part of the GIT_DIFF_OPTS 
> environment variable, but strangely enough we would _not_ parse them as 
> part of the normal diff command line (where we only accepted "-u").
>
> This adds parsing of -U and --unified, both with an optional numeric 
> argument. So now you can just say
>
> 	git diff --unified=5
>
> [ Maybe this can still hit 1.3.3? ]

I think so.

^ permalink raw reply

* Re: git diff: support "-U" and "--unified" options properly
From: Junio C Hamano @ 2006-05-13 20:59 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.64.0605131317200.3866@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> [ Maybe this can still hit 1.3.3? ]

Ah, we did not pass the diffopt to the function builtin_diff() in
1.3.X series, so not really.  We could forward port but I do not
know if it is worth the effort of backporting while stripping
the updates of the whole chain of diff generation from the post
1.3.0 "master" work.  I have to think a bit.

^ permalink raw reply

* Re: git diff: support "-U" and "--unified" options properly
From: Linus Torvalds @ 2006-05-13 21:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List
In-Reply-To: <7vzmhlsksm.fsf@assigned-by-dhcp.cox.net>



On Sat, 13 May 2006, Junio C Hamano wrote:
>
> Linus Torvalds <torvalds@osdl.org> writes:
> 
> > [ Maybe this can still hit 1.3.3? ]
> 
> Ah, we did not pass the diffopt to the function builtin_diff() in
> 1.3.X series, so not really.

Ahh, ok. Never mind. It's not like people have been clamoring for it, it 
just seemed to be such a _silly_ thing.

Might as well just go into the curren development tree, and then we'll 
have it fixed eventually (1.4.0?)

		Linus

^ permalink raw reply

* [RFC] Add "rcs format diff" support
From: Linus Torvalds @ 2006-05-13 21:14 UTC (permalink / raw)
  To: Junio C Hamano, Git Mailing List; +Cc: Al Viro, Davide Libenzi


Al was asking for the "diff -n" format, which is the old RCS format, and 
which is really easy to parse.

Now, we can't use the "-n" flag, because we use that for something else, 
and quite frankly, I don't know what to do about the diff _header_ (RCS 
format doesn't have a header, afaik), but this implements the actual core 
"xdiff" rcs-format patch emit logic, and exposes it with the 
XDL_EMIT_RCSFORMAT flag. 

(In order to get valid diffs, you also have to set the context to zero 
when you set the RCSFORMAT flag).

It also adds a "--rcs-format" flag to the git diff option parsing, so you 
can test it out, but as mentioned, we will still emit the full git header.

Davide - I think the "xdiff/" sub-part of the patch should apply fine to 
the standard xdiff sources, but I'm not sure you're really interested. The 
header issue doesn't matter there, of course, since xdiff doesn't output 
any headers (ie that is an issue for the higher-level user).

The biggest issue for the xdiff library was that I needed to pass down 
the xecfg parameter deeper into the call-chain (ie down to xdl_emit_record 
& co). The rest is pretty trivial.

Al - feel free to play with this. I didn't test it heavily, but it gave 
the right output for the one case I compared with "diff -n". This patch is 
on top of my previous patch to parse "-U" and "--unified".

Junio - this is not really meant for applying, although I don't think 
there is any real down-side to this either. 

		Linus

---
diff --git a/diff.c b/diff.c
index be925a3..fd8f454 100644
--- a/diff.c
+++ b/diff.c
@@ -568,6 +568,10 @@ static void builtin_diff(const char *nam
 			xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
 		ecb.outf = fn_out;
 		ecb.priv = &ecbdata;
+		if (o->rcs_format) {
+			xecfg.flags |= XDL_EMIT_RCSFORMAT;
+			xecfg.ctxlen = 0;
+		}
 		xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 	}
 
@@ -1277,6 +1281,8 @@ int diff_opt_parse(struct diff_options *
 		options->output_format = DIFF_FORMAT_PATCH;
 	else if (opt_arg(arg, 'U', "unified", &options->context))
 		options->output_format = DIFF_FORMAT_PATCH;
+	else if (!strcmp(arg, "--rcs-format"))
+		options->rcs_format = 1;
 	else if (!strcmp(arg, "--patch-with-raw")) {
 		options->output_format = DIFF_FORMAT_PATCH;
 		options->with_raw = 1;
diff --git a/diff.h b/diff.h
index bef586d..953beb9 100644
--- a/diff.h
+++ b/diff.h
@@ -29,6 +29,7 @@ struct diff_options {
 		 with_stat:1,
 		 tree_in_recursive:1,
 		 binary:1,
+		 rcs_format:1,
 		 full_index:1,
 		 silent_on_remove:1,
 		 find_copies_harder:1;
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 2540e8a..a52359e 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -36,6 +36,7 @@ #define XDL_PATCH_MODEMASK ((1 << 8) - 1
 #define XDL_PATCH_IGNOREBSPACE (1 << 8)
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
+#define XDL_EMIT_RCSFORMAT (1 << 1)
 
 #define XDL_MMB_READONLY (1 << 0)
 
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index ad5bfb1..e127469 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -26,7 +26,7 @@ #include "xinclude.h"
 
 
 static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb, xdemitconf_t const *xecfg);
 static xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
 
 
@@ -40,12 +40,13 @@ static long xdl_get_rec(xdfile_t *xdf, l
 }
 
 
-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre,
+	xdemitcb_t *ecb, xdemitconf_t const *xecfg) {
 	long size, psize = strlen(pre);
 	char const *rec;
 
 	size = xdl_get_rec(xdf, ri, &rec);
-	if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
+	if (xdl_emit_diffrec(rec, size, pre, psize, ecb, xecfg) < 0) {
 
 		return -1;
 	}
@@ -129,14 +130,14 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange
 				      sizeof(funcbuf), &funclen);
 		}
 		if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
-				      funcbuf, funclen, ecb) < 0)
+				      funcbuf, funclen, ecb, xecfg) < 0)
 			return -1;
 
 		/*
 		 * Emit pre-context.
 		 */
 		for (; s1 < xch->i1; s1++)
-			if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
+			if (xdl_emit_record(&xe->xdf1, s1, " ", ecb, xecfg) < 0)
 				return -1;
 
 		for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
@@ -144,21 +145,21 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange
 			 * Merge previous with current change atom.
 			 */
 			for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
-				if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
+				if (xdl_emit_record(&xe->xdf1, s1, " ", ecb, xecfg) < 0)
 					return -1;
 
 			/*
 			 * Removes lines from the first file.
 			 */
 			for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
-				if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
+				if (xdl_emit_record(&xe->xdf1, s1, "-", ecb, xecfg) < 0)
 					return -1;
 
 			/*
 			 * Adds lines from the second file.
 			 */
 			for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
-				if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
+				if (xdl_emit_record(&xe->xdf2, s2, "+", ecb, xecfg) < 0)
 					return -1;
 
 			if (xch == xche)
@@ -171,7 +172,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange
 		 * Emit post-context.
 		 */
 		for (s1 = xche->i1 + xche->chg1; s1 < e1; s1++)
-			if (xdl_emit_record(&xe->xdf1, s1, " ", ecb) < 0)
+			if (xdl_emit_record(&xe->xdf1, s1, " ", ecb, xecfg) < 0)
 				return -1;
 	}
 
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 21ab8e7..b0d075a 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -43,10 +43,20 @@ long xdl_bogosqrt(long n) {
 
 
 int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
-		     xdemitcb_t *ecb) {
+		     xdemitcb_t *ecb, xdemitconf_t const *xecfg) {
 	mmbuffer_t mb[3];
 	int i;
 
+	if (xecfg->flags & XDL_EMIT_RCSFORMAT) {
+		if (*pre != '+')
+			return 0;
+		mb[0].ptr = (char *) rec;
+		mb[0].size = size;
+		if (ecb->outf(ecb->priv, mb, 1) < 0)
+			return -1;
+		return 0;
+	}
+
 	mb[0].ptr = (char *) pre;
 	mb[0].size = psize;
 	mb[1].ptr = (char *) rec;
@@ -249,11 +259,34 @@ long xdl_atol(char const *str, char cons
 
 
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
-		      const char *func, long funclen, xdemitcb_t *ecb) {
+		      const char *func, long funclen,
+		      xdemitcb_t *ecb, xdemitconf_t const *xecfg) {
 	int nb = 0;
 	mmbuffer_t mb;
 	char buf[128];
 
+	if (xecfg->flags & XDL_EMIT_RCSFORMAT) {
+		if (c1) {
+			buf[nb++] = 'd';
+			nb += xdl_num_out(buf + nb, s1);
+			buf[nb++] = ' ';
+			nb += xdl_num_out(buf + nb, c1);
+			buf[nb++] = '\n';
+		}
+		if (c2) {
+			buf[nb++] = 'a';
+			nb += xdl_num_out(buf + nb, s2);
+			buf[nb++] = ' ';
+			nb += xdl_num_out(buf + nb, c2);
+			buf[nb++] = '\n';
+		}
+		mb.ptr = buf;
+		mb.size = nb;
+		if (ecb->outf(ecb->priv, &mb, 1) < 0)
+			return -1;
+		return 0;
+	}
+
 	memcpy(buf, "@@ -", 4);
 	nb += 4;
 
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index ea38ee9..e5c6ed0 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -26,7 +26,7 @@ #define XUTILS_H
 
 long xdl_bogosqrt(long n);
 int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
-		     xdemitcb_t *ecb);
+		     xdemitcb_t *ecb, xdemitconf_t const *xecfg);
 int xdl_cha_init(chastore_t *cha, long isize, long icount);
 void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
@@ -38,7 +38,8 @@ unsigned int xdl_hashbits(unsigned int s
 int xdl_num_out(char *out, long val);
 long xdl_atol(char const *str, char const **next);
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
-		      const char *func, long funclen, xdemitcb_t *ecb);
+		      const char *func, long funclen,
+		      xdemitcb_t *ecb, xdemitconf_t const *xecfg);
 
 
 

^ permalink raw reply related

* Re: git diff: support "-U" and "--unified" options properly
From: Junio C Hamano @ 2006-05-13 21:22 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0605131404391.3866@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> On Sat, 13 May 2006, Junio C Hamano wrote:
>>
>> Linus Torvalds <torvalds@osdl.org> writes:
>> 
>> > [ Maybe this can still hit 1.3.3? ]
>> 
>> Ah, we did not pass the diffopt to the function builtin_diff() in
>> 1.3.X series, so not really.
>
> Ahh, ok. Never mind. It's not like people have been clamoring for it, it 
> just seemed to be such a _silly_ thing.
>
> Might as well just go into the curren development tree, and then we'll 
> have it fixed eventually (1.4.0?)

No question about that part.  I've been meaning to start drawing
the line of what to have in 1.4 and what to leave out, but the
last week was shot so I haven't got around to.

A rough outline.

I'd like to have the following topics from "next":

 * cvsserver and cvsexportcommit updates (ml/cvs)

   Ready.

 * config syntax (lt/config)

   Ready.

 * built-in grep (jc/grep)

   Ready.

 * built-in format-patch (js/fmt-patch)

   Some features are still missing compared to the script
   version.

 * remotes/ information from .git/config (js/fetchconfig)

   This by itself is more or less ready, but I would like to
   further adjust it to the "per branch configuration"
   discussion before pushing it out.

   I'd like to eventually arrange things like this.

   [branch "master"]
	remote = "ko-private"
	; prevent "reset --hard" from rewinding past this.
        rewind-barrier = refs/heads/ko-master

   ; my private build areas on the kernel.org machines
   [remote "ko-private"]
   	url = "x86-64-build.kernel.org:git"
   	url = "i386-build.kernel.org:git"
        push = master:origin
        push = next:next
        push = +pu:pu
        push = maint:maint

   ; for publishing and keeping track of what I pushed there last time
   [remote "ko"]
   	url = "kernel.org:/pub/scm/git/git.git"
        push = master:master
        push = next:next
        push = +pu:pu
        push = maint:maint
	fetch = master:ko-master
	fetch = next:ko-next
        fetch = +pu:ko-pu
        fetch = maint:ko-maint

^ permalink raw reply

* Re: [RFC] qgit with tabs
From: Marco Costalba @ 2006-05-13 22:38 UTC (permalink / raw)
  To: Pavel Roskin; +Cc: git
In-Reply-To: <20060513142840.39c0kwkw84g8g88g@webmail.spamcop.net>

Hi Pavel,

>
> Sure, but I often want to see what changed in a particular file.
>
> And of course I only mean the subwindow dislaying the files affected by the
> patch.  The file tree should still have file annotation bound to the double
> click.
>

I understand your reasons, but I have some doubts about this change:

1) The context menu is currently shared between the tree and the file
list, splitting in two subcases adds some crap to the code (ok, this
is not the real doubt ;-)  )

2) The context menu is currently shared between the file list in main
view and the file list in patch view. The file list in patch view, of
course, does not need a double click, a single click is enough to
select corresponding file's diff. In main view you currently need a
single click _plus_  a 'p' key press to change the view. So we should
add another subcase here.

3) It is true that double clicking on a revision switch to the patch
view at top position (if no file is selected), but it's also true that
you can select the file's related diff directly in patch view with a
single click on the right column file list.

4) Once a file is selected, as example with a single click, you can
browse through rev list and the selection is preserved, it means that
anytime you switch to patch view page the content will be _already_
centered on the correct diff.

5) Double clicking on a file name is currently the only way (without
opening the menu) to show the file content tab, with your suggested
change we will have two ways to switch to patch view and no one to
switch to file view.

6) Selecting from the tree view is very slow if you have to search for
the correct file, it is fast only if the file is already selected, but
in this case is faster to press 'p' key ;-)


       Marco

^ permalink raw reply

* Re: git diff: support "-U" and "--unified" options properly
From: Linus Torvalds @ 2006-05-13 22:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vpsihsjq6.fsf@assigned-by-dhcp.cox.net>



On Sat, 13 May 2006, Junio C Hamano wrote:
> 
>  * built-in grep (jc/grep)
> 
>    Ready.

I'm not entirely convinced.

For the kernel, I currently can do a 

	git grep some-random-string

in about half a second.

The new built-in grep is about ten times slower.

Before:

   [torvalds@g5 linux]$ /usr/bin/time git grep some-random-string
   Command exited with non-zero status 123
   0.29user 0.30system 0:00.52elapsed 113%CPU (0avgtext+0avgdata 0maxresident)k
   0inputs+0outputs (0major+3206minor)pagefaults 0swaps

After:

   [torvalds@g5 linux]$ /usr/bin/time git grep some-random-string
   Command exited with non-zero status 1
   4.60user 0.33system 0:04.98elapsed 98%CPU (0avgtext+0avgdata 0maxresident)k
   0inputs+0outputs (0major+7369minor)pagefaults 0swaps

and that "half a second" vs "five seconds" difference is really 
noticeable.

Right now I do "git grep" as a random "ok, where was it", and it works 
very well, because it's basically instantaneous. And the difference 
between "instantaneous" and "5 seconds" is very big (the five seconds is 
also long enough that the CPU fan comes on on my G5, which is my sign of 
"too much work for the CPU".

I haven't looked at _why_ the builtin grep is ten times slower. I suspect 
it's just the regexp library being a lot slower than the external 
optimized grep, but it may also be just overhead (it looks, for example, 
like the builtin grep does all matches just one line at a time. And it 
actually reads the file in, when mmap might be more efficient, I dunno). 

Regardless, it's a huge downer.

		Linus

^ permalink raw reply

* Re: [RFC] Add "rcs format diff" support
From: Al Viro @ 2006-05-14  0:12 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, Git Mailing List, Al Viro, Davide Libenzi
In-Reply-To: <Pine.LNX.4.64.0605131405590.3866@g5.osdl.org>

On Sat, May 13, 2006 at 02:14:15PM -0700, Linus Torvalds wrote:
> 
> Al was asking for the "diff -n" format, which is the old RCS format, and 
> which is really easy to parse.

Heh... And I've just managed to get around that stuff on plain git.  Have fun:

Use:
	git-remap-data [git-diff arguments] > map
	git-remap map <old-log >remapped-old
	git-remap /dev/null <new-log >remapped-new
	diff -u remapped-old remapped-new
with old-log and new-log being build/sparse/whatever logs produced on
trees in question (for values of "whatever logs" including e.g. grep -n
results, etc.)

git-remap-data builds the description of how lines of old tree are mapped
to the new one; git-remap is a filter using that data to massage log
from the old tree to new one; lines of form
<file>:<line>:<text>
are turned into
N:<new-file>:<new-line>:<text>
if they survive in new tree and
O:<file>:<line>:<text>
otherwise.

Here they are; enjoy.  BTW, that puppy can be used on unified diffs with
zero context; won't catch renames, obviously...

git-remap-data.sh:
#!/bin/sh
GIT_DIFF_OPTS="-u 0" git-diff -M "$@" | git-remap

git-remap.c:

/*
 * Copyright (c) 2006, Al Viro.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

char *prefix1 = "a/", *prefix2 = "b/";
size_t len1, len2;

char *line;
size_t size;

void die(char *s)
{
	fprintf(stderr, "remap: %s\n", s);
	exit(1);
}

void Enomem(void)
{
	die("out of memory");
}

void Eio(void)
{
	die("IO error");
}

int getline(FILE *f)
{
	char *s;
	if (!fgets(line, size, f)) {
		if (!feof(f))
			Eio();
		return 0;
	}
	for (s = line + strlen(line); s[-1] != '\n'; s = s + strlen(s)) {
		if (s == line + size - 1) {
			line = realloc(line, 2 * size);
			if (!line)
				Enomem();
			s = line + size - 1;
			size *= 2;
		}
		if (!fgets(s, size - (s - line), f)) {
			if (!feof(f))
				Eio();
			return 1;
		}
	}
	s[-1] = '\0';
	return 1;
}

/* to == 0 -> deletion */
struct range_map {
	int from, to;
};

struct file_map {
	char *name;
	struct file_map *next;
	char *new_name;
	int count;
	int allocated;
	int last;
	struct range_map ranges[];
};

struct file_map *alloc_map(char *name)
{
	struct file_map *map;

	map = malloc(sizeof(struct file_map) + 16 * sizeof(struct range_map));
	if (!map)
		Enomem();
	map->name = map->new_name = strdup(name);
	if (!map->name)
		Enomem();
	map->count = 0;
	map->allocated = 16;
	map->next = NULL;
	map->last = 0;
	return map;
}

/* this is 32bit FNV1 */
uint32_t FNV_hash(char *name)
{
	uint32_t n = 0x811c9dc5;
	while (*name) {
		unsigned char c = *name++;
		n *= 0x01000193;
		n ^= c;
	}
	return n;
}

struct file_map *hash[1024];

int hash_map(struct file_map *map)
{
	int n = FNV_hash(map->name) % 1024;
	struct file_map **p = &hash[n];

	while (*p) {
		if (!strcmp((*p)->name, map->name))
			return 0;
		p = &(*p)->next;
	}
	*p = map;
	if (map->new_name && !map->count)
		return 0;
	if (map->new_name && map->ranges[0].from != 1)
		return 0;
	return 1;
}

struct file_map *find_map(char *name)
{
	static struct file_map *last = NULL;
	int n = FNV_hash(name) % 1024;
	struct file_map *p;

	if (last && !strcmp(last->name, name))
		return last;

	for (p = hash[n]; p && strcmp(p->name, name); p = p->next)
		;
	if (p)
		last = p;
	return p;
}

void parse_map(char *name)
{
	struct file_map *map = NULL;
	struct range_map *range;
	char *s;
	FILE *f;

	f = fopen(name, "r");
	if (!f)
		die("can't open map");
	while (getline(f)) {
		if (line[0] == 'D') {
			if (map && !hash_map(map))
				goto Ebadmap;
			if (line[1] != ' ')
				goto Ebadmap;
			if (strchr(line + 2, ' '))
				goto Ebadmap;
			map = alloc_map(line + 2);
			map->new_name = NULL;
			continue;
		}
		if (line[0] == 'M') {
			if (map && !hash_map(map))
				goto Ebadmap;
			if (line[1] != ' ')
				goto Ebadmap;
			s = strchr(line + 2, ' ');
			if (!s)
				goto Ebadmap;
			*s++ = '\0';
			if (strchr(s, ' '))
				goto Ebadmap;
			map = alloc_map(line + 2);
			if (strcmp(line + 2, s)) {
				map->new_name = strdup(s);
				if (!map->new_name)
					Enomem();
			}
			continue;
		}
		if (!map || !map->new_name)
			goto Ebadmap;
		if (map->count == map->allocated) {
			int n = 2 * map->allocated;
			map = realloc(map, sizeof(struct file_map) +
					   n * sizeof(struct range_map));
			if (!map)
				Enomem();
			map->allocated = n;
		}
		range = &map->ranges[map->count++];
		if (sscanf(line, "%d %d%*c", &range->from, &range->to) != 2)
			goto Ebadmap;
		if (range > map->ranges && range->from <= range[-1].from)
			goto Ebadmap;
	}
	if (map && !hash_map(map))
		goto Ebadmap;
	fclose(f);
	return;
Ebadmap:
	die("bad map");
}

struct range_map *find_range(struct file_map *map, int l)
{
	struct range_map *range = &map->ranges[map->last];
	struct range_map *p;

	if (range->from <= l) {
		p = &map->ranges[map->count - 1];
		if (p->from > l) {
			for (p = range; p->from <= l; p++)
				;
			p--;
		}
	} else {
		for (p = map->ranges; p->from <= l; p++)
			;
		p--;
	}
	map->last = p - map->ranges;
	return p;
}

void mapline(void)
{
	struct file_map *map;
	struct range_map *range;
	unsigned long l;
	char *s1, *s2;
	char *name;

	s1 = strchr(line, ':');
	if (!s1)
		goto noise;
	s2 = strchr(line, ' ');
	if (s2 && s2 < s1)
		goto noise;
	l = strtoul(s1 + 1, &s2, 10);
	if (s2 == s1 + 1 || *s2 != ':' || !l || l > INT_MAX)
		goto noise;
	*s1++ = *s2++ = '\0';
	name = line;
	map = find_map(line);
	if (!map)
		goto new;
	if (!map->new_name)
		goto old;
	name = map->new_name;
	range = find_range(map, l);
	if (!range->to)
		goto old;
	l += range->to - range->from;
new:
	printf("N:%s:%lu:%s\n", name, l, s2);
	return;
old:
	s1[-1] = s2[-1] = ':';
	printf("O:%s\n", line);
	return;
noise:
	printf("%s\n", line);
}

int parse_hunk(int *l1, int *l2, int *n1, int *n2)
{
	unsigned long n;
	char *s, *p;
	if (line[3] != '-')
		return 0;
	n = strtoul(line + 4, &s, 10);
	if (s == line + 4 || n > INT_MAX)
		return 0;
	*l1 = n;
	if (*s == ',') {
		n = strtoul(s + 1, &p, 10);
		if (p == s + 1 || n > INT_MAX)
			return 0;
		*n1 = n;
		if (!n)
			(*l1)++;
	} else {
		p = s;
		*n1 = 1;
	}
	if (*p != ' ' || p[1] != '+')
		return 0;
	n = strtoul(p + 2, &s, 10);
	if (s == p + 2 || n > INT_MAX)
		return 0;
	*l2 = n;
	if (*s == ',') {
		n = strtoul(s + 1, &p, 10);
		if (p == s + 1 || n > INT_MAX)
			return 0;
		*n2 = n;
		if (!n)
			(*l2)++;
	} else {
		p = s;
		*n2 = 1;
	}
	return 1;
}

void parse_diff(void)
{
	int skipping = -1, suppress = 1;
	char *name1 = NULL, *name2 = NULL;
	int from = 1, to = 1;
	int l1, l2, n1, n2;
	enum cmd {
		Diff, Hunk, New, Del, Copy, Rename, Junk
	} cmd;
	static struct { const char *s; size_t len; } pref[] = {
		[Hunk] = {"@@ ", 3},
		[Diff] = {"diff ", 5},
		[New] = {"new file ", 9},
		[Del] = {"deleted file ", 12},
		[Copy] = {"copy from ", 10},
		[Rename] = {"rename from ", 11},
		[Junk] = {"", 0},
	};
	size_t len1 = strlen(prefix1), len2 = strlen(prefix2);

	while (getline(stdin)) {
		if (skipping > 0) {
			switch (line[0]) {
			case '+':
			case '-':
			case '\\':
				continue;
			}
		}
		for (cmd = 0; strncmp(line, pref[cmd].s, pref[cmd].len); cmd++)
			;
		switch (cmd) {
		case Hunk:
			if (skipping < 0)
				goto Ediff;
			if (!suppress) {
				if (!skipping)
					printf("M %s %s\n", name1, name2);
				if (!parse_hunk(&l1, &l2, &n1, &n2))
					goto Ediff;
				if (l1 > from)
					printf("%d %d\n", from, to);
				if (n1)
					printf("%d 0\n", l1);
				from = l1 + n1;
				to = l2 + n2;
			}
			skipping = 1;
			break;
		case Diff:
			if (!suppress) {
				if (!skipping)
					printf("M %s %s\n", name1, name2);
				printf("%d %d\n", from, to);
			}
			free(name1);
			free(name2);
			name2 = strrchr(line, ' ');
			if (!name2)
				goto Ediff;
			*name2 = '\0';
			name1 = strrchr(line, ' ');
			if (!name1)
				goto Ediff;
			if (strncmp(name1 + 1, prefix1, len1))
				goto Ediff;
			if (strncmp(name2 + 1, prefix2, len2))
				goto Ediff;
			name1 = strdup(name1 + len1 + 1);
			name2 = strdup(name2 + len2 + 1);
			if (!name1 || !name2)
				goto Ediff;
			skipping = 0;
			suppress = 0;
			from = to = 1;
			break;
		case New:
			if (skipping)
				goto Ediff;
			suppress = 1;
			break;
		case Del:
		case Copy:
			if (skipping)
				goto Ediff;
			printf("D %s\n", name2);
			suppress = 1;
			break;
		case Rename:
			if (skipping)
				goto Ediff;
			printf("D %s\n", name2);
			break;
		default:
			break;
		}
	}
	return;
Ediff:
	die("odd diff");
}

int main(int argc, char **argv)
{
	int skipping = 0;
	size = 256;
	line = malloc(size);
	if (!line)
		Enomem();
	if (argc < 2) {
		parse_diff();
	} else {
		parse_map(argv[1]);
		while (getline(stdin))
			mapline();
	}
	return 0;
}

^ permalink raw reply

* [PATCH] Add "--branches", "--tags" and "--remotes" options to git-rev-parse.
From: Sean @ 2006-05-14  1:43 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vd5ehu8og.fsf@assigned-by-dhcp.cox.net>


"git branch" uses "rev-parse --all" and becomes much too slow when
there are many tags (it scans all refs).  Use the new "--branches"
option of rev-parse to speed things up.

Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>

---

On Sat, 13 May 2006 10:38:23 -0700
Junio C Hamano <junkio@cox.net> wrote:

> Makes sense perhaps.
> 
> I understand you added --tags for completeness.  Probably it
> would make sense to add --remotes if you are shooting for that.
> 

Hi Junio,

Here's an updated patch with --remotes as you asked.  I appened _ref to the
new functions to make it clear that for_each_remote didn't have anything to
do with remote files.  Also updated the "is_rev_argument" function which was
missed first time around.

On a related note, would it be okay to change "git tag -l" to produce a list
of tags without the "tags/" prefix in front of every tag as it does now?
Wanted to use the new "git rev-parse --tags" instead of "find" to produce 
the list but am not sure how important backward compatibility is in that case.

Sean

 Documentation/git-rev-parse.txt |    9 +++++++++
 git-branch.sh                   |    3 +--
 refs.c                          |   23 +++++++++++++++++++----
 refs.h                          |    3 +++
 rev-parse.c                     |   17 ++++++++++++++++-
 5 files changed, 48 insertions(+), 7 deletions(-)

260bda5eb4effca1a1dd33beb7f7e962d3eab602
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 8b95df0..ab896fc 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -67,6 +67,15 @@ OPTIONS
 --all::
 	Show all refs found in `$GIT_DIR/refs`.
 
+--branches::
+	Show branch refs found in `$GIT_DIR/refs/heads`.
+
+--tags::
+	Show tag refs found in `$GIT_DIR/refs/tags`.
+
+--remotes::
+	Show tag refs found in `$GIT_DIR/refs/remotes`.
+
 --show-prefix::
 	When the command is invoked from a subdirectory, show the
 	path of the current directory relative to the top-level
diff --git a/git-branch.sh b/git-branch.sh
index ebcc898..134e68c 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -82,8 +82,7 @@ done
 
 case "$#" in
 0)
-	git-rev-parse --symbolic --all |
-	sed -ne 's|^refs/heads/||p' |
+	git-rev-parse --symbolic --branches |
 	sort |
 	while read ref
 	do
diff --git a/refs.c b/refs.c
index 275b914..6c91ae6 100644
--- a/refs.c
+++ b/refs.c
@@ -114,7 +114,7 @@ int read_ref(const char *filename, unsig
 	return -1;
 }
 
-static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1))
+static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim)
 {
 	int retval = 0;
 	DIR *dir = opendir(git_path("%s", base));
@@ -146,7 +146,7 @@ static int do_for_each_ref(const char *b
 			if (stat(git_path("%s", path), &st) < 0)
 				continue;
 			if (S_ISDIR(st.st_mode)) {
-				retval = do_for_each_ref(path, fn);
+				retval = do_for_each_ref(path, fn, trim);
 				if (retval)
 					break;
 				continue;
@@ -160,7 +160,7 @@ static int do_for_each_ref(const char *b
 				      "commit object!", path);
 				continue;
 			}
-			retval = fn(path, sha1);
+			retval = fn(path + trim, sha1);
 			if (retval)
 				break;
 		}
@@ -180,7 +180,22 @@ int head_ref(int (*fn)(const char *path,
 
 int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
 {
-	return do_for_each_ref("refs", fn);
+	return do_for_each_ref("refs", fn, 0);
+}
+
+int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1))
+{
+	return do_for_each_ref("refs/tags", fn, 10);
+}
+
+int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1))
+{
+	return do_for_each_ref("refs/heads", fn, 11);
+}
+
+int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))
+{
+	return do_for_each_ref("refs/remotes", fn, 13);
 }
 
 static char *ref_file_name(const char *ref)
diff --git a/refs.h b/refs.h
index 2625596..fa816c1 100644
--- a/refs.h
+++ b/refs.h
@@ -7,6 +7,9 @@ #define REFS_H
  */
 extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1));
 extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1));
+extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1));
+extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1));
+extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1));
 
 /** Reads the refs file specified into sha1 **/
 extern int get_ref_sha1(const char *ref, unsigned char *sha1);
diff --git a/rev-parse.c b/rev-parse.c
index 62e16af..4e2d9fb 100644
--- a/rev-parse.c
+++ b/rev-parse.c
@@ -36,6 +36,7 @@ static int is_rev_argument(const char *a
 		"--all",
 		"--bisect",
 		"--dense",
+		"--branches",
 		"--header",
 		"--max-age=",
 		"--max-count=",
@@ -45,7 +46,9 @@ static int is_rev_argument(const char *a
 		"--objects-edge",
 		"--parents",
 		"--pretty",
+		"--remotes",
 		"--sparse",
+		"--tags",
 		"--topo-order",
 		"--date-order",
 		"--unpacked",
@@ -165,7 +168,7 @@ int main(int argc, char **argv)
 	int i, as_is = 0, verify = 0;
 	unsigned char sha1[20];
 	const char *prefix = setup_git_directory();
-	
+
 	git_config(git_default_config);
 
 	for (i = 1; i < argc; i++) {
@@ -255,6 +258,18 @@ int main(int argc, char **argv)
 				for_each_ref(show_reference);
 				continue;
 			}
+			if (!strcmp(arg, "--branches")) {
+				for_each_branch_ref(show_reference);
+				continue;
+			}
+			if (!strcmp(arg, "--tags")) {
+				for_each_tag_ref(show_reference);
+				continue;
+			}
+			if (!strcmp(arg, "--remotes")) {
+				for_each_remote_ref(show_reference);
+				continue;
+			}
 			if (!strcmp(arg, "--show-prefix")) {
 				if (prefix)
 					puts(prefix);
-- 
1.3.2.g575a1

^ permalink raw reply related

* [PATCH] Ensure author information is set before asking for commit message.
From: Sean @ 2006-05-14  1:51 UTC (permalink / raw)
  To: git

It's better to find out you need to set your author information
properly before you enter a long commit message.

Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>

---

 git-commit.sh |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

ae880db6fc12a0774c25925abbcc353fe6c9e46f
diff --git a/git-commit.sh b/git-commit.sh
index 26cd7ca..866ed6c 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -640,6 +640,7 @@ case "$no_edit" in
 		exit 1
 		;;
 	esac
+	git-var GIT_AUTHOR_IDENT > /dev/null  || die
 	${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
 	;;
 esac
-- 
1.3.2.g260bd

^ permalink raw reply related

* Re: [PATCH] Ensure author information is set before asking for commit message.
From: Junio C Hamano @ 2006-05-14  2:41 UTC (permalink / raw)
  To: Sean; +Cc: git
In-Reply-To: <BAYC1-PASMTP10DC791E7560155D9E71F4AEA20@CEZ.ICE>

Sean <seanlkml@sympatico.ca> writes:

> It's better to find out you need to set your author information
> properly before you enter a long commit message.
>
> Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>

Makes sense but I suspect you would want COMMITTER not AUTHOR.
Imagine you pulled from somewhere else and it conflicted,
requiring you to hand resolve and then run git-commit.

^ permalink raw reply

* [PATCH] Ensure author & committer before asking for commit message.
From: Sean @ 2006-05-14  3:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vlkt5s4yw.fsf@assigned-by-dhcp.cox.net>


It's better to find out you need to fix your author and
committer information before you enter a long commit message.

Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>

---

On Sat, 13 May 2006 19:41:27 -0700
Junio C Hamano <junkio@cox.net> wrote:

> Makes sense but I suspect you would want COMMITTER not AUTHOR.
> Imagine you pulled from somewhere else and it conflicted,
> requiring you to hand resolve and then run git-commit.

Hmm.. It seems that both sets of environment variables must be 
properly set before commit will succeed?  For some reason I thought 
that if AUTHOR was properly set it would be used as the committer too
when the GIT_COMMITTER_* environment variables weren't set.  All I
ever use are the config user.email/name variables which do work for
both author and committer.

But it looks like we have to test both variables before proceeding.

 git-commit.sh |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)


801ac3c2e4ec7fd0dd91975800897f30938b4e68
diff --git a/git-commit.sh b/git-commit.sh
index 26cd7ca..6ef1a9d 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -640,6 +640,8 @@ case "$no_edit" in
 		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
-- 
1.3.2.gd9a4

^ permalink raw reply related

* [PATCH] Make git rebase interactive help match documentation.
From: Sean @ 2006-05-14  3:34 UTC (permalink / raw)
  To: git


Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>

---

 Documentation/git-rebase.txt |   11 +++++------
 git-am.sh                    |   11 +++++++++--
 git-rebase.sh                |   21 ++++++++++++++++-----
 3 files changed, 30 insertions(+), 13 deletions(-)

0a4f0dac668dcba52e20d71c8503a50a92bc2b86
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1b482ab..08ee4aa 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -9,9 +9,7 @@ SYNOPSIS
 --------
 'git-rebase' [--onto <newbase>] <upstream> [<branch>]
 
-'git-rebase' --continue
-
-'git-rebase' --abort
+'git-rebase' --continue | --skip | --abort
 
 DESCRIPTION
 -----------
@@ -23,9 +21,10 @@ not exist in the <upstream> branch.
 
 It is possible that a merge failure will prevent this process from being
 completely automatic.  You will have to resolve any such merge failure
-and run `git rebase --continue`.  If you can not resolve the merge
-failure, running `git rebase --abort` will restore the original <branch>
-and remove the working files found in the .dotest directory.
+and run `git rebase --continue`.  Another option is to bypass the commit
+that caused the merge failure with `git rebase --skip`.  To restore the
+original <branch> and remove the .dotest working files, use the command
+`git rebase --abort` instead.
 
 Note that if <branch> is not specified on the command line, the currently
 checked out branch is used.
diff --git a/git-am.sh b/git-am.sh
index 507ae4d..33f208c 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -15,6 +15,10 @@ stop_here () {
 }
 
 stop_here_user_resolve () {
+    if [ -n "$resolvemsg" ]; then
+	    echo "$resolvemsg"
+	    stop_here $1
+    fi
     cmdline=$(basename $0)
     if test '' != "$interactive"
     then
@@ -121,7 +125,7 @@ fall_back_3way () {
 }
 
 prec=4
-dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws=
+dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
 
 while case "$#" in 0) break;; esac
 do
@@ -157,6 +161,9 @@ do
 	--whitespace=*)
 	ws=$1; shift ;;
 
+	--resolvemsg=*)
+	resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;;
+
 	--)
 	shift; break ;;
 	-*)
@@ -185,7 +192,7 @@ then
 else
 	# Make sure we are not given --skip nor --resolved
 	test ",$skip,$resolved," = ,,, ||
-		die "we are not resuming."
+		die "Resolve operation not in progress, we are not resuming."
 
 	# Start afresh.
 	mkdir -p "$dotest" || exit
diff --git a/git-rebase.sh b/git-rebase.sh
index 9e25902..6ff6088 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -12,9 +12,10 @@ It then attempts to create a new commit 
 
 It is possible that a merge failure will prevent this process from being
 completely automatic.  You will have to resolve any such merge failure
-and run git-rebase --continue.  If you can not resolve the merge failure,
-running git-rebase --abort will restore the original <branch> and remove
-the working files found in the .dotest directory.
+and run git rebase --continue.  Another option is to bypass the commit
+that caused the merge failure with git rebase --skip.  To restore the
+original <branch> and remove the .dotest working files, use the command
+git rebase --abort instead.
 
 Note that if <branch> is not specified on the command line, the
 currently checked out branch is used.  You must be in the top
@@ -28,6 +29,11 @@ Example:       git-rebase master~1 topic
 '
 . git-sh-setup
 
+RESOLVEMSG="
+When you have resolved this problem run \"git rebase --continue\".
+If you would prefer to skip this patch, instead run \"git rebase --skip\".
+To restore the original branch and stop rebasing run \"git rebase --abort\".
+"
 unset newbase
 while case "$#" in 0) break ;; esac
 do
@@ -40,7 +46,11 @@ do
 			exit 1
 			;;
 		esac
-		git am --resolved --3way
+		git am --resolved --3way --resolvemsg="$RESOLVEMSG"
+		exit
+		;;
+	--skip)
+		git am -3 --skip --resolvemsg="$RESOLVEMSG"
 		exit
 		;;
 	--abort)
@@ -143,4 +153,5 @@ then
 fi
 
 git-format-patch -k --stdout --full-index "$upstream" ORIG_HEAD |
-git am --binary -3 -k
+git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+
-- 
1.3.2.gd9a4

^ permalink raw reply related

* Re: git diff: support "-U" and "--unified" options properly
From: Martin Langhoff @ 2006-05-14  7:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, git
In-Reply-To: <7vpsihsjq6.fsf@assigned-by-dhcp.cox.net>

On 5/14/06, Junio C Hamano <junkio@cox.net> wrote:
> I'd like to have the following topics from "next":
>
>  * cvsserver and cvsexportcommit updates (ml/cvs)
>
>    Ready.

Yeah! What's the timeline for 1.4.0?

cheers,



martin

^ permalink raw reply

* [PATCH] Add "--summary" option to git diff.
From: Sean @ 2006-05-14 12:13 UTC (permalink / raw)
  To: git

Remove the need to pipe git diff through git apply to
get the extended headers summary.

Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>

---

e5d981eef0203c399d8b1890be94add525eee969
 Documentation/diff-options.txt |    4 ++
 diff.c                         |   88 ++++++++++++++++++++++++++++++++++++++++
 diff.h                         |    3 +
 3 files changed, 93 insertions(+), 2 deletions(-)

e5d981eef0203c399d8b1890be94add525eee969
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index c183dc9..f523ec2 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -10,6 +10,10 @@
 --stat::
 	Generate a diffstat instead of a patch.
 
+--summary::
+	Output a condensed summary of extended header information
+	such as creations, renames and mode changes.
+
 --patch-with-stat::
 	Generate patch and prepend its diffstat.
 
diff --git a/diff.c b/diff.c
index 7a7b839..00b1044 100644
--- a/diff.c
+++ b/diff.c
@@ -1233,6 +1233,8 @@ int diff_opt_parse(struct diff_options *
 	}
 	else if (!strcmp(arg, "--stat"))
 		options->output_format = DIFF_FORMAT_DIFFSTAT;
+	else if (!strcmp(arg, "--summary"))
+		options->summary = 1;
 	else if (!strcmp(arg, "--patch-with-stat")) {
 		options->output_format = DIFF_FORMAT_PATCH;
 		options->with_stat = 1;
@@ -1703,6 +1705,85 @@ static void flush_one_pair(struct diff_f
 	}
 }
 
+static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
+{
+	if (fs->mode)
+		printf(" %s mode %06o %s\n", newdelete, fs->mode, fs->path);
+	else
+		printf(" %s %s\n", newdelete, fs->path);
+}
+
+
+static void show_mode_change(struct diff_filepair *p, int show_name)
+{
+	if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
+		if (show_name)
+			printf(" mode change %06o => %06o %s\n",
+			       p->one->mode, p->two->mode, p->two->path);
+		else
+			printf(" mode change %06o => %06o\n",
+			       p->one->mode, p->two->mode);
+	}
+}
+
+static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
+{
+	const char *old, *new;
+
+	/* Find common prefix */
+	old = p->one->path;
+	new = p->two->path;
+	while (1) {
+		const char *slash_old, *slash_new;
+		slash_old = strchr(old, '/');
+		slash_new = strchr(new, '/');
+		if (!slash_old ||
+		    !slash_new ||
+		    slash_old - old != slash_new - new ||
+		    memcmp(old, new, slash_new - new))
+			break;
+		old = slash_old + 1;
+		new = slash_new + 1;
+	}
+	/* p->one->path thru old is the common prefix, and old and new
+	 * through the end of names are renames
+	 */
+	if (old != p->one->path)
+		printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
+		       (int)(old - p->one->path), p->one->path,
+		       old, new, (int)(0.5 + p->score * 100.0/MAX_SCORE));
+	else
+		printf(" %s %s => %s (%d%%)\n", renamecopy,
+		       p->one->path, p->two->path,
+		       (int)(0.5 + p->score * 100.0/MAX_SCORE));
+	show_mode_change(p, 0);
+}
+
+static void diff_summary(struct diff_filepair *p)
+{
+	switch(p->status) {
+	case DIFF_STATUS_DELETED:
+		show_file_mode_name("delete", p->one);
+		break;
+	case DIFF_STATUS_ADDED:
+		show_file_mode_name("create", p->two);
+		break;
+	case DIFF_STATUS_COPIED:
+		show_rename_copy("copy", p);
+		break;
+	case DIFF_STATUS_RENAMED:
+		show_rename_copy("rename", p);
+		break;
+	default:
+		if (p->score) {
+			printf(" rewrite %s (%d%%)\n", p->two->path,
+				(int)(0.5 + p->score * 100.0/MAX_SCORE));
+			show_mode_change(p, 0);
+		} else	show_mode_change(p, 1);
+		break;
+	}
+}
+
 void diff_flush(struct diff_options *options)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
@@ -1736,7 +1817,6 @@ void diff_flush(struct diff_options *opt
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
 		flush_one_pair(p, diff_output_format, options, diffstat);
-		diff_free_filepair(p);
 	}
 
 	if (diffstat) {
@@ -1744,6 +1824,12 @@ void diff_flush(struct diff_options *opt
 		free(diffstat);
 	}
 
+	for (i = 0; i < q->nr; i++) {
+		if (options->summary)
+			diff_summary(q->queue[i]);
+		diff_free_filepair(q->queue[i]);
+	}
+
 	free(q->queue);
 	q->queue = NULL;
 	q->nr = q->alloc = 0;
diff --git a/diff.h b/diff.h
index d052608..70077c6 100644
--- a/diff.h
+++ b/diff.h
@@ -31,7 +31,8 @@ struct diff_options {
 		 binary:1,
 		 full_index:1,
 		 silent_on_remove:1,
-		 find_copies_harder:1;
+		 find_copies_harder:1,
+		 summary:1;
 	int break_opt;
 	int detect_rename;
 	int line_termination;
-- 
1.3.2.gab2a

^ permalink raw reply related

* [PATCH] Convert some "apply --summary" users to "diff --summary".
From: Sean @ 2006-05-14 12:16 UTC (permalink / raw)
  To: git

Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>

---

10e6a3cba25ce956654e40d824da191e4e4d3062
 git-format-patch.sh |    2 +-
 git-merge.sh        |    3 +--
 git-request-pull.sh |    2 +-
 3 files changed, 3 insertions(+), 4 deletions(-)

10e6a3cba25ce956654e40d824da191e4e4d3062
diff --git a/git-format-patch.sh b/git-format-patch.sh
index c077f44..8a16ead 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -274,7 +274,7 @@ print "\n---\n\n";
 close FH or die "close $commsg pipe";
 ' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg
 
-	git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
+	git-diff-tree -p --stat --summary $diff_opts "$commit"
 	echo
 	case "$mimemagic" in
 	'');;
diff --git a/git-merge.sh b/git-merge.sh
index b834e79..af1f25b 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -55,8 +55,7 @@ finish () {
 
 	case "$no_summary" in
 	'')
-		git-diff-tree -p -M "$head" "$1" |
-		git-apply --stat --summary
+		git-diff-tree -p --stat --summary -M "$head" "$1"
 		;;
 	esac
 }
diff --git a/git-request-pull.sh b/git-request-pull.sh
index 2c48bfb..4319e35 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -30,4 +30,4 @@ echo "  $url"
 echo
 
 git log  $baserev..$headrev | git-shortlog ;
-git diff $baserev..$headrev | git-apply --stat --summary
+git diff --stat --summary $baserev..$headrev
-- 
1.3.2.gab2a

^ permalink raw reply related

* Re: git diff: support "-U" and "--unified" options properly
From: Josef Weidendorfer @ 2006-05-14 12:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vpsihsjq6.fsf@assigned-by-dhcp.cox.net>

On Saturday 13 May 2006 23:22, you wrote:
>  * remotes/ information from .git/config (js/fetchconfig)
> ...
>    [branch "master"]
> 	remote = "ko-private"

Why is this line needed? In this example, what is the relationship
of local "master" with the remote? I think it is enough to specify
the local upstream branch:

 [branch "master"]
    origin = "ko-master"

For the default pull action when on "master", we would have to look
up for remotes with a fetch line fetching into "ko-master", which
could be cumbersome. Besides, the fetch lines specify default actions
when fetching from a given remote, and there is no garantuee that we
want fetching into "ko-master" as any default action.

So we need

 [branch "ko-master"]
    tracksremote = "master of ko-private"

This also would specify that we are not allowed to commit on "ko-master".

If we do not want to have a local tracking branch at all, we would have

 [branch "master"]
    origin = "master of ko-private"

or

 [branch "master"]
    origin = "master of kernel.org:/pub/scm/git/git.git"


For a default push action when on master, I would add

 [branch "master"]
    push = "master of ko-private"

or alternatively

 [branch "master"]
    push = "master of kernel.org:/pub/scm/git/git.git"

> ...
>    [remote "ko"]
>    	url = "kernel.org:/pub/scm/git/git.git"
>       push = master:master
> ...
> 	fetch = master:ko-master

These specifications more or less are independent from the above,
as it specifies the defaults when fetching/pushing to the specified remote.

Josef

PS: Patch pending...

^ permalink raw reply

* [PATCH] gitk: Display commit messages with word wrap
From: Sergey Vlasov @ 2006-05-14 15:14 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: git

Some people put very long strings into commit messages, which then
become invisible in gitk (word wrapping in the commit details window is
turned off, and there is no horizontal scroll bar).  Enabling word wrap
for just the commit message looks much better.

Signed-off-by: Sergey Vlasov <vsu@altlinux.ru>


---

 gitk |   22 +++++++++++-----------
 1 files changed, 11 insertions(+), 11 deletions(-)

be428b9cd6287b214e61c614bd4c4b4fa3d20075
diff --git a/gitk b/gitk
index 4aa57c0..41f25df 100755
--- a/gitk
+++ b/gitk
@@ -527,6 +527,7 @@ proc makewindow {} {
     pack $ctext -side left -fill both -expand 1
     .ctop.cdet add .ctop.cdet.left
 
+    $ctext tag conf comment -wrap word
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
     $ctext tag conf hunksep -fore blue
     $ctext tag conf d0 -fore red
@@ -3222,12 +3223,12 @@ proc commit_descriptor {p} {
 
 # append some text to the ctext widget, and make any SHA1 ID
 # that we know about be a clickable link.
-proc appendwithlinks {text} {
+proc appendwithlinks {text tags} {
     global ctext commitrow linknum curview
 
     set start [$ctext index "end - 1c"]
-    $ctext insert end $text
-    $ctext insert end "\n"
+    $ctext insert end $text $tags
+    $ctext insert end "\n" {}
     set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
     foreach l $links {
 	set s [lindex $l 0]
@@ -3354,7 +3355,7 @@ proc selectline {l isnew} {
 	$ctext insert end "\n"
     }
  
-    set comment {}
+    set headers {}
     set olds [lindex $parentlist $l]
     if {[llength $olds] > 1} {
 	set np 0
@@ -3365,23 +3366,22 @@ proc selectline {l isnew} {
 		set tag m$np
 	    }
 	    $ctext insert end "Parent: " $tag
-	    appendwithlinks [commit_descriptor $p]
+	    appendwithlinks [commit_descriptor $p] {}
 	    incr np
 	}
     } else {
 	foreach p $olds {
-	    append comment "Parent: [commit_descriptor $p]\n"
+	    append headers "Parent: [commit_descriptor $p]\n"
 	}
     }
 
     foreach c [lindex $childlist $l] {
-	append comment "Child:  [commit_descriptor $c]\n"
+	append headers "Child:  [commit_descriptor $c]\n"
     }
-    append comment "\n"
-    append comment [lindex $info 5]
 
     # make anything that looks like a SHA1 ID be a clickable link
-    appendwithlinks $comment
+    appendwithlinks $headers {}
+    appendwithlinks [lindex $info 5] {comment}
 
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
@@ -4504,7 +4504,7 @@ proc showtag {tag isnew} {
     } else {
 	set text "Tag: $tag\nId:  $tagids($tag)"
     }
-    appendwithlinks $text
+    appendwithlinks $text {}
     $ctext conf -state disabled
     init_flist {}
 }
-- 
1.3.2.g8252

^ permalink raw reply related

* [PATCH/RFC] gitopt - command-line parsing enhancements (take #2)
From: Eric Wong @ 2006-05-14 15:19 UTC (permalink / raw)
  To: git

This should be a less scary series of patches for gitopt.

[PATCH 1/5] gitopt: a new command-line option parser for git

[PATCH 2/5] gitopt: convert ls-files, ls-tree, update-index
	Simple conversions.

[PATCH 3/5] gitopt: convert setup_revisions() and friends
	This one is pretty big, some extra testing + review would be
	nice.

[PATCH 4/5] commit: allow --pretty= args to be abbreviated
	Not strictly related to gitopt, but finger-friendly nevertheless.

[PATCH 5/5] diff: parse U/u/unified options with an optional integer arg
	Originally, this was bundled into:
		<11471512123005-git-send-email-normalperson@yhbt.net>,
	Then Linus did a more correct one that didn't forget combine-diff:
		<Pine.LNX.4.64.0605131317200.3866@g5.osdl.org>
	This one combines both of them.

^ permalink raw reply

* [PATCH 1/5] gitopt: a new command-line option parser for git
From: Eric Wong @ 2006-05-14 15:19 UTC (permalink / raw)
  To: git; +Cc: Eric Wong
In-Reply-To: <11476199622462-git-send-email-normalperson@yhbt.net>

It was initially conceived as an addition to the git.c wrapper,
and not affect other programs.  But it turns out existing C
programs can use it pretty easily, too.

Features include:

 * getopt-style permuting (can easily be disabled for
   things like update-index)

 * command-line compatibile with existing usage:
   -S=pickaxe-arg-with-leading-equals is unchanged

 * printf-style rewriting (for front-ending shell scripts)

 * unbundling of short options: -un20z => -u -n20 -z

 * automatically understands unambiguous abbreviations

 * optional argument handling (-C<num>, -M<num>)
   -C <num> (with a space between them) has not changed,
   however, <num> can be a sha1, or a path

Changes from the initial patch:

 * automatically handle rewrites when not in pass-through mode (pass-through
   mode is used for git wrapper only, usually for shell scripts).

 * Fixed an off-by-one error in parse_bundled that could cause a segfault

 * Supports being called as an iterator mode, as suggested by
   Junio, meaning:

 * no additional global variables required for converting
   existing C programs

 * no more scary macros :)

Signed-off-by: Eric Wong <normalperson@yhbt.net>

---

 .gitignore           |    1 
 Makefile             |   10 +
 git.c                |   11 +
 gitopt.c             |  662 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gitopt.h             |  120 +++++++++
 gitopt/git_wrapper.h |   45 +++
 gitopt/sh.h          |   45 +++
 t/t0200-gitopt.sh    |  295 ++++++++++++++++++++++
 test-gitopt.c        |  112 ++++++++
 9 files changed, 1296 insertions(+), 5 deletions(-)
 create mode 100644 gitopt.c
 create mode 100644 gitopt.h
 create mode 100644 gitopt/git_wrapper.h
 create mode 100644 gitopt/sh.h
 create mode 100755 t/t0200-gitopt.sh
 create mode 100644 test-gitopt.c

eea531f2f17355161d58b61c3371f496f12c5364
diff --git a/.gitignore b/.gitignore
index b5959d6..b2d8b06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,7 @@ git-write-tree
 git-core-*/?*
 test-date
 test-delta
+test-gitopt
 common-cmds.h
 *.tar.gz
 *.dsc
diff --git a/Makefile b/Makefile
index 37fbe78..8fd3e13 100644
--- a/Makefile
+++ b/Makefile
@@ -197,7 +197,7 @@ LIB_H = \
 	blob.h cache.h commit.h csum-file.h delta.h \
 	diff.h object.h pack.h pkt-line.h quote.h refs.h \
 	run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
-	tree-walk.h log-tree.h
+	tree-walk.h log-tree.h gitopt.h
 
 DIFF_OBJS = \
 	diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -212,6 +212,7 @@ LIB_OBJS = \
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
 	fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
+	gitopt.o \
 	$(DIFF_OBJS)
 
 BUILTIN_OBJS = \
@@ -470,6 +471,8 @@ all:
 strip: $(PROGRAMS) git$X
 	$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
 
+gitopt.o: gitopt.c gitopt.h gitopt/*.h
+
 git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS)
 	$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
 		$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
@@ -600,7 +603,7 @@ # with that.
 
 export NO_PYTHON
 
-test: all
+test: all test-gitopt$X
 	$(MAKE) -C t/ all
 
 test-date$X: test-date.c date.o ctype.o
@@ -609,6 +612,9 @@ test-date$X: test-date.c date.o ctype.o
 test-delta$X: test-delta.c diff-delta.o patch-delta.o
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
 
+test-gitopt$X: test-gitopt.c gitopt.o ctype.o usage.o
+	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
+
 check:
 	for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
 
diff --git a/git.c b/git.c
index 49ba518..785d97f 100644
--- a/git.c
+++ b/git.c
@@ -11,6 +11,7 @@ #include <stdarg.h>
 #include "git-compat-util.h"
 #include "exec_cmd.h"
 
+#include "gitopt/git_wrapper.h"
 #include "builtin.h"
 
 static void prepend_to_path(const char *dir, int len)
@@ -72,6 +73,7 @@ int main(int argc, const char **argv, ch
 	char *slash = strrchr(cmd, '/');
 	char git_command[PATH_MAX + 1];
 	const char *exec_path = NULL;
+	struct exec_args *ea;
 
 	/*
 	 * Take the basename of argv[0] as the command
@@ -99,7 +101,8 @@ int main(int argc, const char **argv, ch
 	if (!strncmp(cmd, "git-", 4)) {
 		cmd += 4;
 		argv[0] = cmd;
-		handle_internal_command(argc, argv, envp);
+		ea = gitopt_parse_git(argc, argv);
+		handle_internal_command(ea->argc, ea->argv, envp);
 		die("cannot handle %s internally", cmd);
 	}
 
@@ -144,6 +147,8 @@ int main(int argc, const char **argv, ch
 	}
 	argv[0] = cmd;
 
+	ea = gitopt_parse_git(argc, argv);
+
 	/*
 	 * We search for git commands in the following order:
 	 *  - git_exec_path()
@@ -157,10 +162,10 @@ int main(int argc, const char **argv, ch
 	prepend_to_path(exec_path, strlen(exec_path));
 
 	/* See if it's an internal command */
-	handle_internal_command(argc, argv, envp);
+	handle_internal_command(ea->argc, ea->argv, envp);
 
 	/* .. then try the external ones */
-	execv_git_cmd(argv);
+	execv_git_cmd(ea->argv);
 
 	if (errno == ENOENT)
 		cmd_usage(0, exec_path, "'%s' is not a git-command", cmd);
diff --git a/gitopt.c b/gitopt.c
new file mode 100644
index 0000000..9e85247
--- /dev/null
+++ b/gitopt.c
@@ -0,0 +1,662 @@
+#include "git-compat-util.h"
+#include "gitopt.h"
+#include "cache.h"
+
+/* whether or not to pass-through unrecognized switches or to report an error.
+ * This is only intended to be set to true for use in the git.c wrapper */
+int gitopt_pass_through = 0;
+static int gitopt_errno = 0;
+int gitopt_dd_seen = 0;	/* double-dash seen flag */
+
+struct exec_args *new_exec_args(const int argc)
+{
+	struct exec_args *ea = xmalloc(sizeof(*ea));
+	ea->argc = argc;
+	ea->argv = xcalloc((argc+1), sizeof(char*));
+
+	return ea;
+}
+
+struct exec_args *nil_exec_args(struct exec_args *ea)
+{
+	int i;
+	for (i = 0; i < ea->argc; i++)
+		ea->argv[i] = NULL;
+	ea->argc = 0;
+	return ea;
+}
+
+static int combine_exec_args(struct exec_args *dest, struct exec_args *from)
+{
+	int i;
+	size_t nr = 4 + dest->argc + from->argc;
+
+	dest->argv = xrealloc(dest->argv, nr * sizeof(char*));
+
+	for (i = 0; i < from->argc; i++)
+		dest->argv[dest->argc++] = from->argv[i];
+
+	dest->argv[dest->argc] = NULL;
+
+	return i;
+}
+
+static struct exec_args *rewrite_args(const char *rewrite_fmt, const char *arg)
+{
+	struct exec_args *ea;
+	size_t len = strlen(rewrite_fmt) + (arg ? strlen(arg) : 0);
+	char *dest = xmalloc(len); /* don't free this */
+	int nr_ws = 0;
+	char *a, *b;
+
+	if (!arg) {
+		strcpy(dest, rewrite_fmt);
+		if ((a = strstr(dest,"=%s")) || (a = strstr(dest," %s")))
+			a[0] = a[1] = a[2] = '\0';
+		else if ((a = strstr(dest,"%s")))
+			a[0] = a[1] = '\0';
+	} else {
+		const char *c = rewrite_fmt;
+
+		do { if (isspace(*c++)) nr_ws++; } while (*c);
+		snprintf(dest, len, rewrite_fmt, arg);
+	}
+
+	ea = new_exec_args(1 + nr_ws);
+	a = b = dest;
+
+	for (ea->argc = 0; nr_ws && *b; b++) {
+		if (isspace(*b)) {
+			*b = '\0';
+			if (strlen(a))
+				ea->argv[ea->argc++] = a;
+			a = b + 1;
+		}
+	}
+	if (strlen(a))
+		ea->argv[ea->argc++] = a;
+	ea->argv[ea->argc] = NULL;
+
+	return ea;
+}
+
+static struct exec_args *one_arg(const struct opt_spec *s,
+			const int argc, const char **argv, int *argc_pos)
+{
+	const char *c = argv[*argc_pos];
+	struct exec_args *ea = NULL;
+	const char *a = NULL;
+	size_t l_len = s->l ? strlen(s->l) : 0;
+	int so = 0; /* short option passed flag */
+
+	if (*c == '-')  {
+		if (s->s && c[1] != '-') {
+			if (isspace(s->s)) {
+				a = c + 1;
+				so = 1;
+			} else if (c[1] == s->s) {
+				a = c + 2;
+				so = 1;
+			}
+		} else if (s->l && c[1] == '-' && !strncmp(c+2, s->l, l_len))
+			a = c + 2 + l_len;
+	}
+
+	if (!a)
+		return NULL;
+
+	switch(a[0]) {
+	case '\0':
+		if (((*argc_pos + 1) < argc) && (a = argv[*argc_pos + 1])) {
+			/* optional arguments must be attached to the switch
+			 * so that there are no abiguities */
+			if (s->arg_fl & ARG_IS_OPT)
+				break;
+			*argc_pos += 1;
+			if (s->rewrite_fmt)
+				ea = rewrite_args(s->rewrite_fmt, a);
+			else if (gitopt_pass_through) {
+				ea = new_exec_args(2);
+				ea->argv[0] = c;
+				ea->argv[1] = a;
+			} else if (s->arg_fl & ARG_ONE)
+				ea = rewrite_args("%s", a);
+		}
+		break;
+	default:
+		if (!so) {
+			/* only long options get to use "=" to denote an arg */
+			/* Make sure -S'= assigned_val;' still works */
+			if (*a == '=')
+				a++;
+			else
+				return NULL;
+		}
+		if (s->rewrite_fmt)
+			ea = rewrite_args(s->rewrite_fmt, a);
+		else if (gitopt_pass_through) {
+			ea = new_exec_args(1);
+			ea->argv[0] = c;
+		} else {
+			if (s->arg_fl & ARG_IS_OPT) {
+				ea = new_exec_args(2);
+				ea->argv[0] = "ignore";
+				ea->argv[1] = a;
+			} else if (s->arg_fl & ARG_ONE) {
+				ea = new_exec_args(1);
+				ea->argv[0] = a;
+			}
+		}
+	}
+
+	return ea;
+}
+
+static struct exec_args *optional_arg_common(struct exec_args *ea,
+			const struct opt_spec *s,
+			const int argc, const char **argv, int *argc_pos)
+{
+	if (!ea) {
+		if (s->rewrite_fmt)
+			ea = rewrite_args(s->rewrite_fmt, NULL);
+		else {
+			ea = new_exec_args(1);
+			ea->argv[0] = argv[*argc_pos];
+		}
+	}
+	return ea;
+}
+
+struct exec_args *optional_arg(const struct opt_spec *s,
+			const int argc, const char **argv, int *argc_pos)
+{
+	return optional_arg_common( one_arg(s, argc, argv, argc_pos),
+						s, argc, argv, argc_pos);
+}
+
+static struct exec_args *int_arg(const struct opt_spec *s,
+			const int argc, const char **argv, int *argc_pos)
+{
+	struct exec_args *ea = one_arg(s, argc, argv, argc_pos);
+
+	if (ea) {
+		const char *c = ea->argv[ea->argc - 1];
+		char *endptr;
+		long int tmp;
+
+		/* -C<num>: */
+		if (ea->argc == 1) {
+			while (*c && !isdigit(*c)) c++;
+			if (!c) goto err;
+		}
+
+		endptr = (char *)c;
+		tmp = strtol(c, &endptr, 10);
+		if (endptr == c) {
+err:
+			if (s->arg_fl & ARG_IS_INT)
+				(*argc_pos)--;
+			free_exec_args(ea);
+			return NULL;
+		}
+	}
+	return ea;
+}
+
+static struct exec_args *optional_int_arg(const struct opt_spec *s,
+			const int argc, const char **argv, int *argc_pos)
+{
+	return optional_arg_common( int_arg(s, argc, argv, argc_pos),
+						s, argc, argv, argc_pos);
+}
+
+static struct exec_args *nr_args(int nr, const struct opt_spec *s,
+			const int argc, const char **argv, int *argc_pos)
+{
+	struct exec_args *ea;
+
+	nr++;
+	ea = new_exec_args(nr);
+	ea->argc = 1;
+	ea->argv[0] = argv[*argc_pos];
+	while (*argc_pos < (argc-1) && ea->argc < nr)
+		ea->argv[ea->argc++] = argv[++(*argc_pos)];
+	if (ea->argc != nr)
+		gitopt_errno = error("%s needed %d arguments, to %d",
+				ea->argv[0], nr-1, ea->argc - 1);
+	return ea;
+}
+
+static struct exec_args *run_proc(const struct opt_spec *s,
+			const int argc, const char **argv, int *argc_pos)
+{
+	switch (s->arg_fl) {
+	case ARG_ONE:
+		return one_arg(s, argc, argv, argc_pos);
+	case ARG_INT:
+		return int_arg(s, argc, argv, argc_pos);
+	case ARG_OPT:
+		return optional_arg(s, argc, argv, argc_pos);
+	case ARG_OPTINT:
+		return optional_int_arg(s, argc, argv, argc_pos);
+	case ARG_THREE:
+		return nr_args(3, s, argc, argv, argc_pos);
+	case ARG_TWO:
+		return nr_args(2, s, argc, argv, argc_pos);
+	default:
+		return NULL;
+	}
+}
+
+static const char * parse_bundled(struct gitopt_iterator *gi,
+			const struct opt_spec *s, const char *cur)
+{
+	struct exec_args *ea = NULL;
+	const char *orig = cur;
+	char *c = xmalloc(strlen(cur) + 2); /* don't free this */
+	const char *tmp_argv[] = { c };
+	int i = 0;
+
+	*c++ = '-';
+	*c++ = *cur++;
+	if (!s || !s->arg_fl) {
+		ea = new_exec_args(1);
+		if (s && s->rewrite_fmt)
+			ea->argv[0] = s->rewrite_fmt;
+		else {
+			ea->argv[0] = tmp_argv[0];
+			*c = '\0';
+		}
+	} else if (s->arg_fl) {
+		if (*cur) {
+			/* no space between the arg and opt switch: */
+			if (s->arg_fl & ARG_IS_INT) {
+				/* we know to handle stuff like:
+				 * -h24w80 => -h=24 -w=80 */
+				char *endptr = (char *)cur;
+				strtol(cur, &endptr, 10);
+
+				while (cur < endptr)
+					*c++ = *cur++;
+			} else if (s->arg_fl & ARG_ONE) {
+				/* unfortunately, other args are less
+				 * clear-cut */
+				while (*cur)
+					*c++ = *cur++;
+			}
+			*c = '\0';
+			ea = run_proc(s, 1, tmp_argv, &i);
+		} else if ((gi->pos + 1) < gi->argc) {
+			int j = gi->pos;
+			int x = gi->argc - j + 1;
+			const char **argv2 = xmalloc(x * sizeof(char *));
+
+			*c = '\0';
+			argv2[i++] = tmp_argv[0];
+			while (i < x) argv2[i++] = gi->argv[++j];
+
+			i = 0;
+			ea = run_proc(s, x, argv2, &i);
+			gi->pos += i;
+			free(argv2);
+		}
+	}
+	if (ea) {
+		combine_exec_args(gi->ea, ea);
+		free_exec_args(ea);
+	} else
+		gitopt_errno = error("Failed to parse bundled arguments in: %s",
+									orig);
+	return cur;
+}
+
+static int unbundle_iter(struct gitopt_iterator *gi, const struct opt_spec *ost)
+{
+	const struct opt_spec *s;
+	const char *c, *cur = gi->argv[gi->pos];
+
+	if (!gi->upos || gi->upos < cur || gi->upos > strrchr(cur,0))
+		gi->upos = cur + 1; /* only short options */
+	c = gi->upos;
+
+	while (*c) {
+		int i;
+		for (i = 0; ost[i].s || ost[i].l; i++) {
+			s = ost + i;
+			if (!s->s || isspace(s->s) || s->s != *c)
+				continue;
+			c = parse_bundled(gi, s, c);
+			if (c != gi->upos) {
+				gi->upos = c;
+				return s->id;
+			}
+			break;
+		}
+		if (ost[i].l || ost[i].s) continue;
+		/* pass-through while unbundling and creating switches:
+		 * this means that if we see -abc here, but we only
+		 * had -a defined in ost (-a defined to not accept args),
+		 * then we'd create switches
+		 * for -b and -c here (since we already knew -a)
+		 * and we're assuming -b and -c were just forgotten
+		 * in the ost.  If we had gotten -bac, that would
+		 * be passed through as -bac in gitopt_parse_ost()
+		 * as an unknown option if -b is undefined in the ost
+		 */
+		if (gitopt_pass_through) {
+			c = parse_bundled(gi, NULL, c);
+			if (c != gi->upos) {
+				gi->upos = c;
+				return GITOPT_ERROR;
+			}
+		} else {
+			/* non-fatal error, should it be non-fatal? */
+			gitopt_errno = error("Unknown option '%s' in '%s'",
+							c, gi->argv[gi->pos]);
+			c++;
+		}
+	}
+
+	gi->upos = c;
+	return GITOPT_ERROR;
+}
+
+
+static int push_one_opt(struct gitopt_iterator *gi, const struct opt_spec *s)
+{
+	struct exec_args *ea;
+
+	if (!s->arg_fl) {
+		gi->ea->argv[gi->ea->argc++] = s->rewrite_fmt ? s->rewrite_fmt
+							: gi->argv[gi->pos];
+		return s->id;
+	}
+
+	if ((ea = run_proc(s, gi->argc, gi->argv, &(gi->pos)))) {
+		combine_exec_args(gi->ea, ea);
+		free_exec_args(ea);
+		return s->id;
+	}
+	gitopt_errno = error("Failed to parse arguments for: %s %s",
+				gi->argv[gi->pos],
+				gi->argv[gi->pos+1] ? gi->argv[gi->pos+1] : "");
+	return GITOPT_ERROR;
+}
+
+/* look for a prefix abbreviation */
+static int opt_abbrev_match(const struct opt_spec *s, const char *p)
+{
+	const char *l = s->l;
+
+	while (*p) {
+		if (*l++ != *p++) return 0;
+		if (!*p || (s->arg_fl && *p == '=')) return 1;
+	}
+
+	return 0;
+}
+
+/* match a short option switch */
+static int opt_char_match(const struct opt_spec *s, const char *p)
+{
+	return ((s->s == p[0]) && ((!s->arg_fl && p[1] == '\0')
+				||
+			(s->arg_fl && (p[1] == '\0' || p[1] == '='))));
+}
+
+/* tokenize on '-' and look for a prefix abbreviation match */
+static int opt_token_match(const struct opt_spec *s, const char *p0)
+{
+	const char *l = s->l;
+	const char *p;
+
+	while ((l = strchr(l,'-'))) {
+		l++;
+		p = p0;
+		while (*p) {
+			if (*l++ != *p++) break;
+			if (!*p || (s->arg_fl && *p == '=')) return 1;
+		}
+	}
+
+	return 0;
+}
+
+/* look for unambigious abbreviated switches, if it can't be found,
+ * assume it's a non-option and pass it to b */
+static int fallback_long(struct gitopt_iterator *gi,
+			const struct opt_spec *ost, const char *cur)
+{
+	const struct opt_spec *s, *found = NULL;
+	int i;
+
+	/* look for abbreviations: */
+	for (i = 0; ost[i].l || ost[i].s; i++) {
+		s = &(ost[i]);
+		/* maybe they wanted to use a short option
+		 * (normally a single-dash) but typed two dashes instead.
+		 * note: even if we find a short option here, we do not
+		 * attempt to unbundle in this case */
+		if ((s->s && opt_char_match(s, cur)) ||
+					(s->l && opt_abbrev_match(s, cur))) {
+			if (found && found->id != s->id)
+				goto pass_through;
+			found = s;
+		}
+	}
+
+	/* ok, try harder, based on tokenization on '-' */
+	if (!found && getenv("GIT_ABBREV_HARDER")) {
+		for (i = 0; ost[i].l || ost[i].s; i++) {
+			s = &(ost[i]);
+			if (s->l && opt_token_match(s,cur)) {
+				if (found && found->id != s->id)
+					goto pass_through;
+				found = s;
+			}
+		}
+	}
+	/* should I add a strstr() matcher here for the desperate? */
+
+	/* rewrite the abbreviated switch in it's unabbreviated form: */
+	if (found) {
+		char *tmp;
+		size_t len = 3 + strlen(cur); /* --cur=potential_args\0 */
+		size_t l_len;
+
+		/* favor long options: */
+		l_len = found->l ? strlen(found->l) : 0;
+		tmp = xcalloc(len + l_len, 1); /* don't free this */
+		tmp[0] = '-';
+
+		if (found->l) {
+			tmp[1] = '-';
+			memcpy(tmp + 2, found->l, l_len);
+		} else
+			tmp[1] = found->s;
+		if (found->arg_fl) {
+			const char *c;
+			if ((c = strchr(cur,'='))) {
+				/* skip '=' for short opts masquerading as
+				 * long opts: --S=foo */
+				if (!l_len) c++;
+				strcpy(tmp + 2 + l_len, c);
+			}
+		}
+
+		gi->argv[gi->pos] = tmp;
+		return push_one_opt(gi, found);
+	}
+
+pass_through:
+	if (gitopt_pass_through)
+		return GITOPT_NON_OPTION;
+	gitopt_errno = error("Unknown option: '%s'",gi->argv[gi->pos]);
+	return GITOPT_ERROR;
+}
+
+static int opt_complete_match(const struct opt_spec *s, const char *p)
+{
+	if (s->arg_fl) {
+		size_t len = strlen(s->l);
+
+		return (!strncmp(s->l,p,len) &&
+				(p[len] == '\0' || (p[len] == '=')));
+	}
+	return !strcmp(s->l,p);
+}
+
+int gitopt_verify_b_args(const struct exec_args *b)
+{
+	const char **arg;
+
+	for (arg = b->argv; *arg; arg++) {
+		/* anything goes after a double dash */
+		if (!memcmp("--",*arg,3))
+			return 1;
+		if (*arg[0] == '-')
+			return 0;
+	}
+	return 1;
+}
+
+void gitopt_iter_setup(struct gitopt_iterator *gi,
+			const int argc, const char **argv)
+{
+	gi->upos = NULL;
+	gi->pos = 1;
+	gi->ea = new_exec_args(4); /* most we currently have is ARG_THREE */
+	gi->b = new_exec_args(argc);
+	gi->b->argc = 0;
+	gi->argc = argc;
+	gi->argv = argv;
+}
+
+int gitopt_iter_parse(struct gitopt_iterator *gi,
+			const struct opt_spec *ost)
+{
+	const char *c = gi->argv[gi->pos];
+	const struct opt_spec *s;
+	int i;
+
+	gitopt_errno = 0;
+	if (!c) return 0;
+	nil_exec_args(gi->ea);
+
+	if (!gitopt_dd_seen && !memcmp("--",c,3)) {
+		gitopt_dd_seen = 1;
+		return GITOPT_DD;
+	}
+	if (gitopt_dd_seen)
+		return GITOPT_NON_OPTION;
+	if (!memcmp("--",c,2)) {	/* long options */
+		c += 2;
+		for (i = 0; ost[i].l || ost[i].s; i++) {
+			s = &(ost[i]);
+			if (!s->l || !opt_complete_match(s, c)) continue;
+			return push_one_opt(gi, s);
+		}
+		/* undefined --option: */
+		return fallback_long(gi, ost, c);
+	}
+	if ((c[0] == '-') && (c[1] != '-')) { /* short option */
+		c++;
+		for (i = 0; ost[i].l || ost[i].s; i++) {
+			s = &(ost[i]);
+			if (!s->s) continue;
+			if (isspace(s->s) && (*c == '\0' || isdigit(*c))) {
+				/* special case for -<num> */
+				return push_one_opt(gi, s);
+			}
+			if (s->s != *c) continue;
+			if ((c[1] != '\0') && (c[1] != '='))
+				return unbundle_iter(gi, ost);
+			return push_one_opt(gi, s);
+		}
+		/* undefined: */
+		if (gitopt_pass_through)
+			return GITOPT_NON_OPTION;
+		return GITOPT_ERROR;
+	}
+	return GITOPT_NON_OPTION;
+}
+
+void gitopt_iter_next(struct gitopt_iterator *gi)
+{
+	if (!gi->upos || !gi->upos[0])
+		gi->pos++;
+}
+
+static int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,
+			const struct opt_spec *ost,
+			const int argc, const char **argv)
+{
+	struct gitopt_iterator gi;
+
+	gitopt_dd_seen = 0;
+	b->argc = 0;
+	a->argv[0] = argv[0];
+	a->argc = 1;
+
+	gitopt_iter_setup(&gi, argc, argv);
+	for (; gi.pos < argc; gitopt_iter_next(&gi)) {
+		switch (gitopt_iter_parse(&gi, ost)) {
+		case GITOPT_ERROR:
+		case GITOPT_DD:
+			if (!gitopt_pass_through)
+				break;
+		case GITOPT_NON_OPTION:
+			b->argv[b->argc++] = gi.argv[gi.pos];
+			break;
+		default:
+			combine_exec_args(a, gi.ea);
+		}
+	}
+
+	free_exec_args(gi.ea);
+	free_exec_args(gi.b);
+	return gitopt_errno;
+}
+
+/* You should really only use this in git (wrapper) and test-gitopt: */
+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,
+			const int argc, const char **argv)
+{
+	struct exec_args *a = new_exec_args(argc); /* argv[0] and options: */
+	struct exec_args *b = new_exec_args(argc); /* non-option args */
+
+	gitopt_pass_through = 1;
+
+	if (gitopt_parse_ost_split(a, b, ost, argc, argv) < 0)
+		die("gitopt argument parsing failed");
+	combine_exec_args(a, b);
+	free_exec_args(b);
+
+	return a;
+}
+
+void free_exec_args(struct exec_args *ea)
+{
+	free(ea->argv);
+	free(ea);
+}
+
+struct opt_spec *combine_opt_spec(const struct opt_spec *a,
+					const struct opt_spec *b)
+{
+	struct opt_spec *rv, *tmp;
+	size_t a_size = 0, b_size = 0;
+
+	while (a[a_size].l || a[a_size].s) a_size++;
+	while (b[b_size].l || b[b_size].s) b_size++;
+
+	tmp = rv = xmalloc( (a_size + b_size + 1) * sizeof(*a) );
+
+	while (a->s || a->l) memcpy(tmp++, a++, sizeof(*a));
+	while (b->s || b->l) memcpy(tmp++, b++, sizeof(*b));
+
+	memcpy(tmp, b, sizeof(*b));
+
+	return rv;
+}
+
diff --git a/gitopt.h b/gitopt.h
new file mode 100644
index 0000000..4108cf5
--- /dev/null
+++ b/gitopt.h
@@ -0,0 +1,120 @@
+#ifndef GITOPT_H
+#define GITOPT_H
+
+/* gitopt_* functions will return this structure
+ * the elements in this struct can then be treated just
+ * like their counterparts from main(). */
+struct exec_args {
+	const char **argv;
+	int argc;
+};
+
+#define GITOPT_DIFF_BASE		64
+#define GITOPT_SR_BASE			128
+
+enum gitopt_status {
+	GITOPT_DD = 253,
+	GITOPT_NON_OPTION,
+	GITOPT_ERROR
+};
+
+/* @l: long option string (without the leading "--")
+ *
+ * @s: single option char, ' ' has a special meaning for accepting a
+ * single '-' (dash), which can also accept an integer argument This is
+ * for things like "-5" => "--max-count=5" or "-" => "--stdin"
+ *
+ * @rewrite_fmt: rewrite the passed argument(s) (if any) into this
+ * (*printf) style string.  Only a single %s can be accepted and handled
+ * by the default fn() handlers included in gitopt.
+ *
+ * Do not use this if you need to use more than one "%s", you'll need to
+ * define and use a custom fn().  rewrite_fmt is only intended for
+ * the common argument rewriting cases.
+ *
+ * If rewrite_fmt has a "%s", " %s" or "=%s" in it, it will be stripped
+ * out if no arguments are passed to it (this can be the case where
+ * fn() (see below) is defined to optional_arg).
+ *
+ * Any single space between non-space characters will be interpreted as
+ * break in the option and the options will be split out into seperate
+ * elements in argv.
+ *
+ * @arg_fl: argument flags, see ARG_* #defines
+ *
+ * @id: unique identifier to return, must be non-zero and < 64
+ */
+struct opt_spec {
+	const char *l;
+	const char s;
+	const char *rewrite_fmt;
+	const unsigned int arg_fl;
+	const int id;
+};
+
+/* internal use: */
+#define ARG_IS_INT	0x08
+#define ARG_IS_OPT	0x10
+
+/* use these for opt_spec flags: */
+#define ARG_NIL		0x00
+#define ARG_ONE		0x01
+#define ARG_TWO		0x02	/* not really supported yet */
+#define ARG_THREE	0x04	/* not really supported yet */
+#define ARG_TRE		ARG_THREE
+#define ARG_INT		(ARG_ONE | ARG_IS_INT)
+#define ARG_OPT		(ARG_ONE | ARG_IS_OPT)
+#define ARG_OPTINT	(ARG_ONE | ARG_IS_OPT | ARG_IS_INT)
+
+extern int gitopt_pass_through;
+extern int gitopt_dd_seen;	/* double-dash seen flag */
+
+struct exec_args *new_exec_args(const int argc);
+void free_exec_args(struct exec_args *ea);
+struct exec_args *nil_exec_args(struct exec_args *ea);
+
+/* You should really only use this in the git wrapper or tests: */
+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,
+			const int argc, const char **argv);
+
+/* used for debugging */
+static inline void dump_ea(const char *pfx, struct exec_args *ea)
+{
+	const char **arg;
+	int i = 0;
+	for (arg = ea->argv; *arg; arg++)
+		fprintf(stderr,"[%d] %s: %s\n",i++,pfx,*arg);
+}
+
+struct gitopt_iterator {
+	struct exec_args *ea;	/* tmp, for passing --opt args */
+	struct exec_args *b;	/* unrecognized arguments */
+	const char *upos;	/* unbundle position */
+	const char **argv;
+	int argc;
+	int pos;		/* argc position */
+};
+
+void gitopt_iter_setup(struct gitopt_iterator *gi,
+			const int argc, const char **argv);
+int gitopt_iter_parse(struct gitopt_iterator *gi,
+			const struct opt_spec *ost);
+void gitopt_iter_next(struct gitopt_iterator *gi);
+
+static inline void gitopt_iter_done(struct gitopt_iterator *gi)
+{
+	free_exec_args(gi->ea);
+}
+
+/* returns a newly allocated opt_spec struct, can be free()-ed: */
+struct opt_spec *combine_opt_spec(const struct opt_spec *a,
+					const struct opt_spec *b);
+
+struct gitopt_extra {
+	const struct opt_spec *ost;
+	int (*opt_handler)(struct gitopt_iterator *gi,
+			const int id, void *args);
+	void *args;
+};
+
+#endif /* GITOPT_H */
diff --git a/gitopt/git_wrapper.h b/gitopt/git_wrapper.h
new file mode 100644
index 0000000..5f27bf4
--- /dev/null
+++ b/gitopt/git_wrapper.h
@@ -0,0 +1,45 @@
+/* opt_spec table mappings for the git.c wrapper */
+
+#include "../gitopt.h"
+#include "../gitopt/sh.h"
+
+static const struct opt_spec ost_null[] = { { 0, 0 } };
+
+static const struct opt_spec_map {
+	const char *cmd;
+	const struct opt_spec *ost;
+} opt_specs[] = {
+	{ "checkout",		ost_checkout },
+	{ "commit",		ost_commit },
+	{ "am",			ost_am},
+};
+
+static const struct opt_spec *find_cmd_ost(const int argc, const char **argv)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(opt_specs); i++) {
+		const char *cmd = opt_specs[i].cmd;
+		if (strcmp(cmd, *argv))
+			continue;
+		return opt_specs[i].ost;
+	}
+	return NULL;
+}
+
+static struct exec_args *gitopt_parse_git(const int argc, const char **argv)
+{
+	const struct opt_spec *ost;
+
+	if (!strcmp(argv[0],"help") || !strcmp(argv[0],"version") ||
+				!(ost = find_cmd_ost(argc, argv))) {
+		struct exec_args *ea = new_exec_args(argc);
+		int i;
+
+		for (i = 0; i <= argc; i++) /* argv[argc] == NULL */
+			ea->argv[i] = argv[i];
+
+		return ea;
+	}
+	return gitopt_parse_ost(ost, argc, argv);
+}
diff --git a/gitopt/sh.h b/gitopt/sh.h
new file mode 100644
index 0000000..0ce6620
--- /dev/null
+++ b/gitopt/sh.h
@@ -0,0 +1,45 @@
+#ifndef GITOPT_SH_H
+#define GITOPT_SH_H
+
+/* opt_spec tables for some git programs written in shell that don't
+ * have too many options */
+
+static const struct opt_spec ost_am[] = {
+	{ "dotest",		'd',	0,		ARG_ONE,	0 },
+	{ "interactive",	'i',	0,		0,		0 },
+	{ "binary",		'b',	0,		0,		0 },
+	{ "3way",		'3',	0,		0,		0 },
+	{ "signoff",		's',	0,		0,		0 },
+	{ "utf8",		'u',	0,		0,		0 },
+	{ "keep",		'k',	0,		0,		0 },
+	{ "resolved",		'r',	0,		0,		0 },
+	{ "skip",		0,	0,		0,		0 },
+	{ "whitespace",		0,	0,		ARG_ONE,	0 },
+	{ 0, 0 }
+};
+
+static const struct opt_spec ost_checkout[] = {
+	{ 0,			'f',	0,		0,		0 },
+	{ 0,			'm',	0,		0,		0 },
+	{ 0,			'b',	"-b %s",	ARG_ONE,	0 },
+	{ 0, 0 }
+};
+
+static const struct opt_spec ost_commit[] = {
+	{ "file",		'F',	0,		ARG_ONE,	0 },
+	{ "all",		'a',	0,		0,		0 },
+	{ "author",		0,	0,		ARG_ONE,	0 },
+	{ "edit",		'e',	0,		0,		0 },
+	{ "include",		'i',	0,		0,		0 },
+	{ "only",		'o',	0,		0,		0 },
+	{ "message",		'm',	0,		ARG_ONE,	0 },
+	{ "no-verify",		'n',	0,		0,		0 },
+	{ "amend",		0,	0,		0,		0 },
+	{ "reedit",		'c',	0,		ARG_ONE,	0 },
+	{ "reuse-message",	'C',	0,		ARG_ONE,	0 },
+	{ "signoff",		's',	0,		0,		0 },
+	{ "verbose",		'v',	0,		0,		0 },
+	{ 0, 0 }
+};
+
+#endif /* GITOPT_SH_H */
diff --git a/t/t0200-gitopt.sh b/t/t0200-gitopt.sh
new file mode 100755
index 0000000..7e140a0
--- /dev/null
+++ b/t/t0200-gitopt.sh
@@ -0,0 +1,295 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='gitopt command-line pa{ss,rs}er'
+
+. ./test-lib.sh
+
+cat > expect <<EOF
+00 'commit'
+01 '(null)'
+EOF
+test_expect_success 'single command' \
+	'test-gitopt commit > output && cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '(null)'
+EOF
+test_expect_success 'simple command with switches' \
+	'test-gitopt commit -a -s > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '(null)'
+EOF
+test_expect_success 'command with bundled switches' \
+	'test-gitopt commit -as > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '-mhello world'
+04 '(null)'
+EOF
+test_expect_success 'bundle with args for last (no space)' \
+	'test-gitopt commit -asm"hello world" > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '-m'
+04 'hello world'
+05 '(null)'
+EOF
+test_expect_success 'unbundle with args (space)' \
+	'test-gitopt commit -asm "hello world" > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '-m'
+04 'hello world'
+05 'file1'
+06 'file2'
+07 '(null)'
+EOF
+test_expect_success 'unbundle and reorder switches and command w/args' \
+	'test-gitopt commit file1 file2 -asm "hello world" > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '--edit'
+04 'file2'
+05 '--'
+06 'file1'
+07 '-as'
+08 '--all'
+09 '(null)'
+EOF
+test_expect_success 'reorder up to and pass-through "--"' \
+	'test-gitopt commit -as file2 --edit -- file1 -as --all > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-s'
+02 '-m'
+03 'message'
+04 '--'
+05 'file1'
+06 'file2'
+07 '(null)'
+EOF
+test_expect_success 'reorder up to and pass-through "--"' \
+	'test-gitopt commit -sm message -- file1 file2 > output &&
+	cmp expect output'
+
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '--find-copies-harder'
+02 '(null)'
+EOF
+test_expect_success 'abbreviation finder (prefix)' \
+	'test-gitopt whatchanged --find-c > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '--patch-with-raw'
+02 '(null)'
+EOF
+test_expect_success 'abbreviation finder (substring on "-" token)' \
+	'GIT_ABBREV_HARDER=1 test-gitopt whatchanged --raw > output &&
+	 cmp expect output'
+
+# we assume unknown switches that cannot be resolved to a single known
+# switch to just be an new argument we do not know about, so we pass
+# it to the underlying command
+cat > expect <<EOF
+00 'whatchanged'
+01 '--zzzz'
+02 '(null)'
+EOF
+test_expect_success 'ambiguous abbreviation (pass-through)' \
+	'test-gitopt whatchanged --zzzz > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '--diff-filter=MCT'
+02 'file1'
+03 '(null)'
+EOF
+test_expect_success 'rewrite on long argument' \
+	'test-gitopt whatchanged file1 --diff-filter MCT > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-Shello'
+02 '(null)'
+EOF
+test_expect_success 'rewrite on short argument (#1)' \
+	'test-gitopt whatchanged -Shello > output &&
+	 cmp expect output'
+test_expect_success 'rewrite on short argument (#2)' \
+	'test-gitopt whatchanged -S hello > output &&
+	 cmp expect output'
+test_expect_success 'rewrite on --short argument (#3)' \
+	'test-gitopt whatchanged --S hello > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-Shello'
+02 '(null)'
+EOF
+test_expect_success 'rewrite on short argument (leading "=" arg) (#1)' \
+	'test-gitopt whatchanged --S=hello > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C20'
+02 '-M'
+03 '(null)'
+EOF
+test_expect_success 'pass optional arg (#1)' \
+	'test-gitopt whatchanged -C20 -M > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C'
+02 '--find-copies-harder'
+03 '(null)'
+EOF
+test_expect_success 'detect optional arg bogus (#1)' \
+	'test-gitopt whatchanged -C --find-copies-harder > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C20'
+02 '(null)'
+EOF
+test_expect_success 'pass optional arg (#2)' \
+	'test-gitopt whatchanged -C20 > output &&
+	 cmp expect output'
+# test_expect_success 'pass optional arg (#3)' \
+	# 'test-gitopt whatchanged -C=20 > output &&
+	 # cmp expect output'
+
+cat > expect <<EOF
+00 'checkout'
+01 '-b'
+02 'newbranch'
+03 '(null)'
+EOF
+test_expect_success 'rewrite short split arg (#1)' \
+	'test-gitopt checkout -bnewbranch > output &&
+	 cmp expect output'
+# test_expect_success 'rewrite short split arg (#2)' \
+	# 'test-gitopt checkout --b=newbranch > output &&
+	 # cmp expect output'
+test_expect_success 'rewrite short sanity check' \
+	'test-gitopt checkout -b newbranch > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'log'
+01 '--default'
+02 'dunno'
+03 '--all'
+04 '(null)'
+EOF
+test_expect_success 'rewrite long split arg' \
+	'test-gitopt log --default=dunno --all > output &&
+	 cmp expect output'
+test_expect_success 'rewrite long sanity check' \
+	'test-gitopt log --default dunno --all > output &&
+	 cmp expect output'
+
+cat > expect <<EOF
+00 'log'
+01 '--max-count=56'
+02 '(null)'
+EOF
+test_expect_success 'rewrite -<num> => --max-count=<num>' \
+	'test-gitopt log -56 > output && cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-u'
+02 '-l20'
+03 '-p'
+04 '-Spicktoken'
+05 '(null)'
+EOF
+test_expect_success 'bundle options with integer args mixed in' \
+	'test-gitopt whatchanged -ul20pSpicktoken > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-u'
+02 '-C20'
+03 '-p'
+04 '(null)'
+EOF
+test_expect_success 'bundle options with optional integer args used' \
+	'test-gitopt whatchanged -uC20p > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-u'
+02 '-C'
+03 '-p'
+04 '(null)'
+EOF
+test_expect_success 'bundle options with optional integer args not used' \
+	'test-gitopt whatchanged -uCp > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C'
+02 '20'
+03 '(null)'
+EOF
+test_expect_success 'optional integer arg switch must be attached' \
+	'test-gitopt whatchanged -C 20 > output &&
+	cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '--message'
+02 'hello world'
+03 '-s'
+04 'file'
+05 '(null)'
+EOF
+test_expect_success 'long option argument parsing when short option can work' \
+	'test-gitopt commit --message "hello world" -s file > output &&
+	cmp expect output'
+
+test_done
diff --git a/test-gitopt.c b/test-gitopt.c
new file mode 100644
index 0000000..2692e2b
--- /dev/null
+++ b/test-gitopt.c
@@ -0,0 +1,112 @@
+
+#include "git-compat-util.h"
+#include "gitopt.h"
+#include "gitopt/sh.h"
+
+#define _rev \
+{ "max-count",		'n',	"--max-count=%s",	ARG_INT,	0 }, \
+{ 0,			' ',	"--max-count=%s",	ARG_INT,	0 }, \
+{ "max-age",		0,	"--max-age=%s",		ARG_INT,	0 }, \
+{ "min-age",		0,	"--min-age=%s",		ARG_INT,	0 }, \
+{ "since",		0,	"--since=%s",		ARG_ONE,	0 }, \
+{ "after",		0,	"--after=%s",		ARG_ONE,	0 }, \
+{ "before",		0,	"--before=%s",		ARG_ONE,	0 }, \
+{ "until",		0,	"--until=%s",		ARG_ONE,	0 }, \
+{ "all",		0,	0,			0,		0 }, \
+{ "not",		0,	0,			0,		0 }, \
+{ "default",		0,	"--default %s",		ARG_ONE,	0 }, \
+{ "topo-order",		0,	0,			0,		0 }, \
+{ "date-order",		0,	0,			0,		0 }, \
+{ "parents",		0,	0,			0,		0 }, \
+{ "dense",		0,	0,			0,		0 }, \
+{ "sparse",		0,	0,			0,		0 }, \
+{ "remove-empty",	0,	0,			0,		0 }, \
+{ "no-merges",		0,	0,			0,		0 }, \
+{ "boundary",		0,	0,			0,		0 }, \
+{ "objects",		0,	0,			0,		0 }, \
+{ "objects-edge",	0,	0,			0,		0 }, \
+{ "unpacked",		0,	0,			0,		0 }, \
+{ 0,			'r',	0,			0,		0 }, \
+{ 0,			't',	0,			0,		0 }, \
+{ 0,			'm',	0,			0,		0 }, \
+{ 0,			'c',	0,			0,		0 }, \
+{ "cc",			0,	0,			0,		0 }, \
+{ 0,			'v',	0,			0,		0 }, \
+{ "pretty",		0,	"--pretty=%s",		ARG_ONE,	0 }, \
+{ "root",		0,	0,			0,		0 }, \
+{ "no-commit-id",	0,	0,			0,		0 }, \
+{ "always",		0,	0,			0,		0 }, \
+{ "no-abbrev",		0,	0,			0,		0 }, \
+{ "abbrev",		0,	0,			0,		0 }, \
+{ "abbrev-commit",	0,	0,			0,		0 }, \
+{ "full-diff",		0,	0,			0,		0 }, \
+
+#define _diff \
+{ 0,			'u',	0,			0,		0 }, \
+{ 0,			'p',	0,			0,		0 }, \
+{ "patch-with-raw",	0,	0,			0,		0 }, \
+{ "stat",		0,	0,			0,		0 }, \
+{ "patch-with-stat",	0,	0,			0,		0 }, \
+{ 0,			'z',	0,			0,		0 }, \
+{ 0,			'l',	"-l%s",			ARG_INT,	0 }, \
+{ "full-index",		0,	0,			0,		0 }, \
+{ "name-only",		0,	0,			0,		0 }, \
+{ "name-status",	0,	0,			0,		0 }, \
+{ 0,			'R',	0,			0,		0 }, \
+{ 0,			'S',	"-S%s",			ARG_ONE,	0 }, \
+{ 0,			's',	0,			0,		0 }, \
+{ 0,			'O',	"-O%s",			ARG_ONE,	0 }, \
+{ "diff-filter",	0,	"--diff-filter=%s",	ARG_ONE,	0 }, \
+{ "pickaxe-all",	0,	0,			0,		0 }, \
+{ "pickaxe-regex",	0,	0,			0,		0 }, \
+{ 0,			'B',	"-B%s",			ARG_OPTINT,	0 }, \
+{ 0,			'M',	"-M%s",			ARG_OPTINT,	0 }, \
+{ 0,			'C',	"-C%s",			ARG_OPTINT,	0 }, \
+{ "find-copies-harder",	0,	0,			0,		0 }, \
+{ "abbrev",		0,	"--abbrev=%s",		ARG_OPT,	0 }, \
+
+#define end {0,0}
+
+static const struct opt_spec ost_log[] = { _rev end };
+static const struct opt_spec ost_rev_list[] = { _rev end };
+static const struct opt_spec ost_whatchanged[] = { _diff _rev end };
+static const struct opt_spec ost_show[] = { _diff _rev end };
+
+static const struct opt_spec_map {
+	const char *cmd;
+	const struct opt_spec *ost;
+} opt_specs[] = {
+	{ "checkout",		ost_checkout },
+	{ "commit",		ost_commit },
+	{ "log",		ost_log },
+	{ "rev-list",		ost_rev_list },
+	{ "show",		ost_show },
+	{ "whatchanged",	ost_whatchanged },
+};
+
+
+int main(int argc, const char **argv, char **envp)
+{
+	int i;
+	struct exec_args *ea;
+	const struct opt_spec *ost = NULL;
+
+	if (!argv[1])
+		usage("test-gitopt [<options>] <command> [<options>]");
+
+	for (i = 0; i < ARRAY_SIZE(opt_specs); i++) {
+		if (!strcmp(argv[1], opt_specs[i].cmd)) {
+			ost = opt_specs[i].ost;
+			break;
+		}
+	}
+	if (!ost)
+		usage("test-gitopt [<options>] <command> [<options>]");
+
+	ea = gitopt_parse_ost(ost, argc - 1, argv + 1);
+
+	for (i = 0; i <= ea->argc; i++)
+		printf("%02d '%s'\n", i, ea->argv[i] ? ea->argv[i] : "(null)");
+
+	return 0;
+}
-- 
1.3.2.g102e322

^ permalink raw reply related

* [PATCH 2/5] gitopt: convert ls-files, ls-tree, update-index
From: Eric Wong @ 2006-05-14 15:19 UTC (permalink / raw)
  To: git; +Cc: Eric Wong
In-Reply-To: <11476199622462-git-send-email-normalperson@yhbt.net>

All of these are pretty straighforward conversions

Signed-off-by: Eric Wong <normalperson@yhbt.net>

---

 ls-files.c     |  184 +++++++++++++++++++++-------------------
 ls-tree.c      |   82 ++++++++++++------
 update-index.c |  257 ++++++++++++++++++++++++++++++--------------------------
 3 files changed, 287 insertions(+), 236 deletions(-)

962175b2be70eab14997a10ebb9c7cd719d72c1b
diff --git a/ls-files.c b/ls-files.c
index 4a4af1c..9f4d328 100644
--- a/ls-files.c
+++ b/ls-files.c
@@ -10,6 +10,7 @@ #include <fnmatch.h>
 
 #include "cache.h"
 #include "quote.h"
+#include "gitopt.h"
 
 static int abbrev = 0;
 static int show_deleted = 0;
@@ -648,133 +649,140 @@ static const char ls_files_usage[] =
 	"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
 	"[--] [<file>]*";
 
+enum ls_files_ids {
+	opt_z = 1, opt_v, opt_t, opt_c, opt_d, opt_m, opt_o, opt_i, opt_s,
+	opt_k, opt_directory, opt_no_empty_directory,
+	opt_u, opt_x, opt_X, opt_exclude_per_dir,
+	opt_abbrev, opt_full_name, opt_error_unmatch
+};
+
+static const struct opt_spec ls_files_ost[] = {
+	{ 0,			'z',	0,	0,	opt_z },
+	{ 0,			'v',	0,	0,	opt_v },
+	{ 0,			't',	0,	0,	opt_t },
+	{ "cached",		'c',	0,	0,	opt_c },
+	{ "deleted",		'd',	0,	0,	opt_d },
+	{ "modified",		'm',	0,	0,	opt_m },
+	{ "others",		'o',	0,	0,	opt_o },
+	{ "ignored",		'i',	0,	0,	opt_i },
+	{ "stage",		's',	0,	0,	opt_s },
+	{ "killed",		'k',	0,	0,	opt_k },
+	{ "directory",		0,	0,	0,	opt_directory },
+	{ "no-empty-directory",	0,	0,	0,	opt_no_empty_directory},
+	{ "unmerged",		'u',	0,	0,	opt_u },
+	{ "exclude",		'x',	0,	ARG_ONE,	opt_x },
+	{ "exclude-from",	'X',	0,	ARG_ONE,	opt_X },
+	{ "exclude-per-directory",0,	0,	ARG_ONE, opt_exclude_per_dir },
+	{ "full-name",		0,	0,	0,	opt_full_name },
+	{ "error-unmatch",	0,	0,	0,	opt_error_unmatch },
+	{ "abbrev",		0,	0,	ARG_OPTINT, opt_abbrev },
+	{ 0, 0 }
+};
+
 int main(int argc, const char **argv)
 {
 	int i;
 	int exc_given = 0;
+	struct gitopt_iterator gi;
+	struct exec_args *ea, *b;
 
 	prefix = setup_git_directory();
 	if (prefix)
 		prefix_offset = strlen(prefix);
 	git_config(git_default_config);
 
-	for (i = 1; i < argc; i++) {
-		const char *arg = argv[i];
-
-		if (!strcmp(arg, "--")) {
-			i++;
-			break;
-		}
-		if (!strcmp(arg, "-z")) {
+	gitopt_iter_setup(&gi, argc, argv);
+	ea = gi.ea;
+	b = gi.b;
+	for (; (i = gitopt_iter_parse(&gi, ls_files_ost));
+				gitopt_iter_next(&gi)) {
+		switch (i) {
+		case opt_z:
 			line_terminator = 0;
-			continue;
-		}
-		if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
+			break;
+		case opt_v:
+			show_valid_bit = 1; /* fall through */
+		case opt_t:
 			tag_cached = "H ";
 			tag_unmerged = "M ";
 			tag_removed = "R ";
 			tag_modified = "C ";
 			tag_other = "? ";
 			tag_killed = "K ";
-			if (arg[1] == 'v')
-				show_valid_bit = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
+			break;
+		case opt_c:
 			show_cached = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
+			break;
+		case opt_d:
 			show_deleted = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
+			break;
+		case opt_m:
 			show_modified = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
+			break;
+		case opt_o:
 			show_others = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
+			break;
+		case opt_i:
 			show_ignored = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
+			break;
+		case opt_s:
 			show_stage = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
+			break;
+		case opt_k:
 			show_killed = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--directory")) {
+			break;
+		case opt_directory:
 			show_other_directories = 1;
-			continue;
-		}
-		if (!strcmp(arg, "--no-empty-directory")) {
+			break;
+		case opt_no_empty_directory:
 			hide_empty_directories = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
+			break;
+		case opt_u:
 			/* There's no point in showing unmerged unless
 			 * you also show the stage information.
 			 */
 			show_stage = 1;
 			show_unmerged = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-x") && i+1 < argc) {
-			exc_given = 1;
-			add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]);
-			continue;
-		}
-		if (!strncmp(arg, "--exclude=", 10)) {
-			exc_given = 1;
-			add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]);
-			continue;
-		}
-		if (!strcmp(arg, "-X") && i+1 < argc) {
+			break;
+		case opt_x:
 			exc_given = 1;
-			add_excludes_from_file(argv[++i]);
-			continue;
-		}
-		if (!strncmp(arg, "--exclude-from=", 15)) {
+			add_exclude(ea->argv[0], "", 0, &exclude_list[EXC_CMDL]);
+			break;
+		case opt_X:
 			exc_given = 1;
-			add_excludes_from_file(arg+15);
-			continue;
-		}
-		if (!strncmp(arg, "--exclude-per-directory=", 24)) {
+			add_excludes_from_file(ea->argv[0]);
+			break;
+		case opt_exclude_per_dir:
 			exc_given = 1;
-			exclude_per_dir = arg + 24;
-			continue;
-		}
-		if (!strcmp(arg, "--full-name")) {
+			exclude_per_dir = ea->argv[0];
+			break;
+		case opt_full_name:
 			prefix_offset = 0;
-			continue;
-		}
-		if (!strcmp(arg, "--error-unmatch")) {
+			break;
+		case opt_error_unmatch:
 			error_unmatch = 1;
-			continue;
-		}
-		if (!strncmp(arg, "--abbrev=", 9)) {
-			abbrev = strtoul(arg+9, NULL, 10);
-			if (abbrev && abbrev < MINIMUM_ABBREV)
-				abbrev = MINIMUM_ABBREV;
-			else if (abbrev > 40)
-				abbrev = 40;
-			continue;
-		}
-		if (!strcmp(arg, "--abbrev")) {
-			abbrev = DEFAULT_ABBREV;
-			continue;
-		}
-		if (*arg == '-')
+			break;
+		case opt_abbrev:
+			if (ea->argc == 1)
+				abbrev = DEFAULT_ABBREV;
+			else {
+				if (abbrev && abbrev < MINIMUM_ABBREV)
+					abbrev = MINIMUM_ABBREV;
+				else if (abbrev > 40)
+					abbrev = 40;
+			}
+			break;
+		case GITOPT_DD:
+			break;
+		case GITOPT_NON_OPTION:
+			b->argv[b->argc++] = argv[gi.pos];
+			break;
+		case GITOPT_ERROR:
 			usage(ls_files_usage);
-		break;
+		}
 	}
 
-	pathspec = get_pathspec(prefix, argv + i);
+	pathspec = get_pathspec(prefix, b->argv);
 
 	/* Verify that the pathspec matches the prefix */
 	if (pathspec)
diff --git a/ls-tree.c b/ls-tree.c
index f2b3bc1..3de85bc 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -7,6 +7,7 @@ #include "cache.h"
 #include "blob.h"
 #include "tree.h"
 #include "quote.h"
+#include "gitopt.h"
 
 static int line_termination = '\n';
 #define LS_RECURSIVE 1
@@ -84,56 +85,79 @@ static int show_tree(unsigned char *sha1
 	return retval;
 }
 
+enum ls_tree_ids {
+	opt_z = 1, opt_r, opt_d, opt_t,
+	opt_name_only_status, opt_full_name, opt_abbrev
+};
+
+static const struct opt_spec ls_tree_ost[] = {
+	{ 0,			'z',	0,	0,	opt_z },
+	{ 0,			'r',	0,	0,	opt_r },
+	{ 0,			'd',	0,	0,	opt_d },
+	{ 0,			't',	0,	0,	opt_t },
+	{ "name-only",		0,	0,	0,	opt_name_only_status },
+	{ "name-status",	0,	0,	0,	opt_name_only_status },
+	{ "full-name",		0,	0,	0,	opt_full_name },
+	{ "abbrev",		0,	0,	ARG_OPTINT,	opt_abbrev },
+	{ 0, 0 }
+};
+
 int main(int argc, const char **argv)
 {
 	unsigned char sha1[20];
 	struct tree *tree;
+	struct exec_args *b, *ea;
+	struct gitopt_iterator gi;
+	int i;
 
 	prefix = setup_git_directory();
 	git_config(git_default_config);
 	if (prefix && *prefix)
 		chomp_prefix = strlen(prefix);
-	while (1 < argc && argv[1][0] == '-') {
-		switch (argv[1][1]) {
-		case 'z':
+
+	gitopt_iter_setup(&gi, argc, argv);
+	ea = gi.ea;
+	b = gi.b;
+	for (; (i = gitopt_iter_parse(&gi, ls_tree_ost));
+				gitopt_iter_next(&gi)) {
+		switch (i) {
+		case opt_z:
 			line_termination = 0;
 			break;
-		case 'r':
+		case opt_r:
 			ls_options |= LS_RECURSIVE;
 			break;
-		case 'd':
+		case opt_d:
 			ls_options |= LS_TREE_ONLY;
 			break;
-		case 't':
+		case opt_t:
 			ls_options |= LS_SHOW_TREES;
 			break;
-		case '-':
-			if (!strcmp(argv[1]+2, "name-only") ||
-			    !strcmp(argv[1]+2, "name-status")) {
-				ls_options |= LS_NAME_ONLY;
-				break;
-			}
-			if (!strcmp(argv[1]+2, "full-name")) {
-				chomp_prefix = 0;
-				break;
-			}
-			if (!strncmp(argv[1]+2, "abbrev=",7)) {
-				abbrev = strtoul(argv[1]+9, NULL, 10);
+		case opt_name_only_status:
+			ls_options |= LS_NAME_ONLY;
+			break;
+		case opt_full_name:
+			chomp_prefix = 0;
+			break;
+		case opt_abbrev:
+			if (ea->argc == 1)
+				abbrev = DEFAULT_ABBREV;
+			else {
+				abbrev = strtoul(ea->argv[1], NULL, 10);
 				if (abbrev && abbrev < MINIMUM_ABBREV)
 					abbrev = MINIMUM_ABBREV;
 				else if (abbrev > 40)
 					abbrev = 40;
-				break;
-			}
-			if (!strcmp(argv[1]+2, "abbrev")) {
-				abbrev = DEFAULT_ABBREV;
-				break;
 			}
-			/* otherwise fallthru */
-		default:
+			break;
+		case GITOPT_DD:
+			break;
+		case GITOPT_NON_OPTION:
+			b->argv[b->argc++] = argv[gi.pos];
+			break;
+		case GITOPT_ERROR:
 			usage(ls_tree_usage);
 		}
-		argc--; argv++;
 	}
 	/* -d -r should imply -t, but -d by itself should not have to. */
 	if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
@@ -142,10 +166,10 @@ int main(int argc, const char **argv)
 
 	if (argc < 2)
 		usage(ls_tree_usage);
-	if (get_sha1(argv[1], sha1))
-		die("Not a valid object name %s", argv[1]);
+	if (get_sha1(b->argv[0], sha1))
+		die("Not a valid object name %s", b->argv[0]);
 
-	pathspec = get_pathspec(prefix, argv + 2);
+	pathspec = get_pathspec(prefix, b->argv + 1);
 	tree = parse_tree_indirect(sha1);
 	if (!tree)
 		die("not a tree object");
diff --git a/update-index.c b/update-index.c
index 3d7e02d..f4e15d7 100644
--- a/update-index.c
+++ b/update-index.c
@@ -7,6 +7,7 @@ #include "cache.h"
 #include "strbuf.h"
 #include "quote.h"
 #include "tree-walk.h"
+#include "gitopt.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -645,6 +646,39 @@ static int do_reupdate(int ac, const cha
 	return 0;
 }
 
+enum update_index_ids {
+	opt_q = 1, opt_add, opt_replace, opt_remove, opt_unmerged,
+	opt_refresh, opt_really_refresh, opt_cacheinfo, opt_chmod,
+	opt_assume_unchanged, opt_no_assume_unchanged, opt_info_only,
+	opt_force_remove, opt_z, opt_stdin, opt_index_info, opt_unresolve,
+	opt_again, opt_ignore_missing, opt_verbose, opt_h
+};
+
+static const struct opt_spec update_index_ost[] = {
+	{ 0,			'q',	0,	0,	opt_q },
+	{ "add",		0,	0,	0,	opt_add },
+	{ "replace",		0,	0,	0,	opt_replace },
+	{ "remove",		0,	0,	0,	opt_remove },
+	{ "unmerged",		0,	0,	0,	opt_unmerged },
+	{ "refresh",		0,	0,	0,	opt_refresh },
+	{ "really-refresh",	0,	0,	0,	opt_really_refresh },
+	{ "cacheinfo",		0,	0,	ARG_TRE, opt_cacheinfo},
+	{ "chmod",		0,	"%s",	ARG_ONE, opt_chmod },
+	{ "assume-unchanged",	0,	0,	0,	opt_assume_unchanged },
+	{ "no-assume-unchanged",0,	0,	0,	opt_no_assume_unchanged},
+	{ "info-only",		0,	0,	0,	opt_info_only },
+	{ "force-remove",	0,	0,	0,	opt_force_remove },
+	{ 0,			'z',	0,	0,	opt_z },
+	{ "stdin",		0,	0,	0,	opt_stdin },
+	{ "index-info",		0,	0,	0,	opt_index_info },
+	{ "unresolve",		0,	0,	0,	opt_unresolve },
+	{ "again",		0,	0,	0,	opt_again },
+	{ "ignore-missing",	0,	0,	0,	opt_ignore_missing },
+	{ "verbose",		0,	0,	0,	opt_verbose },
+	{ "help",		'h',	0,	0,	opt_h },
+	{ 0, 0 }
+};
+
 int main(int argc, const char **argv)
 {
 	int i, newfd, entries, has_errors = 0, line_termination = '\n';
@@ -653,6 +687,10 @@ int main(int argc, const char **argv)
 	const char *prefix = setup_git_directory();
 	int prefix_length = prefix ? strlen(prefix) : 0;
 	char set_executable_bit = 0;
+	struct exec_args *ea;
+	struct gitopt_iterator gi;
+	unsigned char sha1[20];
+	unsigned int mode;
 
 	git_config(git_default_config);
 
@@ -664,126 +702,107 @@ int main(int argc, const char **argv)
 	if (entries < 0)
 		die("cache corrupted");
 
-	for (i = 1 ; i < argc; i++) {
-		const char *path = argv[i];
-
-		if (allow_options && *path == '-') {
-			if (!strcmp(path, "--")) {
-				allow_options = 0;
-				continue;
-			}
-			if (!strcmp(path, "-q")) {
-				quiet = 1;
-				continue;
-			}
-			if (!strcmp(path, "--add")) {
-				allow_add = 1;
-				continue;
-			}
-			if (!strcmp(path, "--replace")) {
-				allow_replace = 1;
-				continue;
-			}
-			if (!strcmp(path, "--remove")) {
-				allow_remove = 1;
-				continue;
-			}
-			if (!strcmp(path, "--unmerged")) {
-				allow_unmerged = 1;
-				continue;
-			}
-			if (!strcmp(path, "--refresh")) {
-				has_errors |= refresh_cache(0);
-				continue;
-			}
-			if (!strcmp(path, "--really-refresh")) {
-				has_errors |= refresh_cache(1);
-				continue;
-			}
-			if (!strcmp(path, "--cacheinfo")) {
-				unsigned char sha1[20];
-				unsigned int mode;
-
-				if (i+3 >= argc)
-					die("git-update-index: --cacheinfo <mode> <sha1> <path>");
-
-				if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
-				    get_sha1_hex(argv[i+2], sha1) ||
-				    add_cacheinfo(mode, sha1, argv[i+3], 0))
-					die("git-update-index: --cacheinfo"
-					    " cannot add %s", argv[i+3]);
-				i += 3;
-				continue;
-			}
-			if (!strcmp(path, "--chmod=-x") ||
-			    !strcmp(path, "--chmod=+x")) {
-				if (argc <= i+1)
-					die("git-update-index: %s <path>", path);
-				set_executable_bit = path[8];
-				continue;
-			}
-			if (!strcmp(path, "--assume-unchanged")) {
-				mark_valid_only = MARK_VALID;
-				continue;
-			}
-			if (!strcmp(path, "--no-assume-unchanged")) {
-				mark_valid_only = UNMARK_VALID;
-				continue;
-			}
-			if (!strcmp(path, "--info-only")) {
-				info_only = 1;
-				continue;
-			}
-			if (!strcmp(path, "--force-remove")) {
-				force_remove = 1;
-				continue;
-			}
-			if (!strcmp(path, "-z")) {
-				line_termination = 0;
-				continue;
-			}
-			if (!strcmp(path, "--stdin")) {
-				if (i != argc - 1)
-					die("--stdin must be at the end");
-				read_from_stdin = 1;
-				break;
-			}
-			if (!strcmp(path, "--index-info")) {
-				if (i != argc - 1)
-					die("--index-info must be at the end");
-				allow_add = allow_replace = allow_remove = 1;
-				read_index_info(line_termination);
-				break;
-			}
-			if (!strcmp(path, "--unresolve")) {
-				has_errors = do_unresolve(argc - i, argv + i,
-							  prefix, prefix_length);
-				if (has_errors)
-					active_cache_changed = 0;
-				goto finish;
-			}
-			if (!strcmp(path, "--again")) {
-				has_errors = do_reupdate(argc - i, argv + i,
-							 prefix, prefix_length);
-				if (has_errors)
-					active_cache_changed = 0;
-				goto finish;
-			}
-			if (!strcmp(path, "--ignore-missing")) {
-				not_new = 1;
-				continue;
-			}
-			if (!strcmp(path, "--verbose")) {
-				verbose = 1;
-				continue;
-			}
-			if (!strcmp(path, "-h") || !strcmp(path, "--help"))
-				usage(update_index_usage);
-			die("unknown option %s", path);
+	gitopt_iter_setup(&gi, argc, argv);
+	ea = gi.ea;
+	for (; (i = gitopt_iter_parse(&gi, update_index_ost));
+				gitopt_iter_next(&gi)) {
+		switch (i) {
+		case opt_q:
+			quiet = 1;
+			break;
+		case opt_add:
+			allow_add = 1;
+			break;
+		case opt_replace:
+			allow_replace = 1;
+			break;
+		case opt_remove:
+			allow_remove = 1;
+			break;
+		case opt_unmerged:
+			allow_unmerged = 1;
+			break;
+		case opt_refresh:
+			has_errors |= refresh_cache(0);
+			break;
+		case opt_really_refresh:
+			has_errors |= refresh_cache(1);
+			break;
+		case opt_cacheinfo:
+			if (ea->argc != 4)
+				die("git-update-index: --cacheinfo "
+				    "<mode> <sha1> <path>");
+			if ((sscanf(ea->argv[1], "%o", &mode) != 1) ||
+			    get_sha1_hex(ea->argv[2], sha1) ||
+			    add_cacheinfo(mode, sha1, ea->argv[3], 0))
+				die("git-update-index: --cacheinfo"
+				    " cannot add %s", ea->argv[3]);
+			break;
+		case opt_chmod:
+			if (i+1 >= argc)
+				die("git-update-index: --chmod=%s <path>",
+								ea->argv[0]);
+			set_executable_bit = ea->argv[0][0];
+			break;
+		case opt_assume_unchanged:
+			mark_valid_only = MARK_VALID;
+			break;
+		case opt_no_assume_unchanged:
+			mark_valid_only = UNMARK_VALID;
+			break;
+		case opt_info_only:
+			info_only = 1;
+			break;
+		case opt_force_remove:
+			force_remove = 1;
+			break;
+		case opt_z:
+			line_termination = 0;
+			break;
+		case opt_stdin:
+			if (gi.pos != argc - 1)
+				die("--stdin must be at the end");
+			read_from_stdin = 1;
+			break;
+		case opt_index_info:
+			if (gi.pos != argc - 1)
+				die("--index-info must be at the end");
+			allow_add = allow_replace = allow_remove = 1;
+			read_index_info(line_termination);
+			break;
+		case opt_unresolve:
+			has_errors = do_unresolve(argc - gi.pos, argv + gi.pos,
+						  prefix, prefix_length);
+			if (has_errors)
+				active_cache_changed = 0;
+			goto finish;
+		case opt_again:
+			has_errors = do_reupdate(argc - gi.pos, argv + gi.pos,
+						 prefix, prefix_length);
+			if (has_errors)
+				active_cache_changed = 0;
+			goto finish;
+		case opt_ignore_missing:
+			not_new = 1;
+			break;
+		case opt_verbose:
+			verbose = 1;
+			break;
+		case opt_h:
+			usage(update_index_usage);
+			break;
+		case GITOPT_DD:
+			allow_options = 0;
+			break;
+		case GITOPT_NON_OPTION:
+			update_one(argv[gi.pos], prefix, prefix_length);
+			if (set_executable_bit)
+				chmod_path(set_executable_bit, argv[gi.pos]);
+			break;
+		case GITOPT_ERROR:
+			die("unknown option %s", argv[gi.pos]);
+			break;
 		}
-		update_one(path, prefix, prefix_length);
-		if (set_executable_bit)
-			chmod_path(set_executable_bit, path);
 	}
 	if (read_from_stdin) {
 		struct strbuf buf;
-- 
1.3.2.g102e322

^ permalink raw reply related


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