* Re: Unresolved issues #2
From: David Woodhouse @ 2006-05-09 11:40 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v4q065hq0.fsf@assigned-by-dhcp.cox.net>
On Thu, 2006-05-04 at 01:15 -0700, Junio C Hamano wrote:
>
> * Message-ID:
> <4fb292fa0604290630r19edd7ejf88642e33b350d1d@mail.gmail.com>
> Content-type charset for send-email (Bertrand Jacquin)
>
> The output from format-patch by default is unmarked, which
> means the commit message part is UTF-8 (by strong convention),
> and the contents of the diff is whatever the contents of the
> file is encoded in.
Email without a Content-Type: header is supposed to be ASCII. If it
contains 8-bit characters, it's invalid. It'll be interpreted by
different systems in different ways -- not necessarily as UTF-8. Some
may even just reject it, on grounds of RFC non-compliance.
> David Woodhouse did a patch to allow specifying charset on the
> command line (and default to UTF-8) which is a move in the
> right direction, but Bertrand's system seems to have trouble
> with it.
I thought Bertrand then confirmed that he was having trouble _before_
applying my patch, too? His response when I asked it it appears without
my patch was "[it] appear without in 1.3.1 and I can't seed mail with
too. Also, 1.2.4 work fine here (without patch)."
> I think if we were to do this we probably need to teach
> format-patch to optionally do multi-part. We may not
> necessarily want to mark the payload to be in the same
> encoding as the commit message (not that git-apply cares -- to
> it, the payload is just 8-bit unencoded text, but we would
> want to protect it from getting mangled by e-mail transport).
I'm not sure about that. The payload is patches, isn't it? That's just
text, too -- we aren't going to deal with diffs of binary content very
well _anyway_, are we?
Obviously, there's nothing to stop people from storing binary blobs in
GIT, but unless you want to start sending actual _blobs_ as attachments
instead of sending patches, I think there's no need to play with MIME
multipart stuff.
I've no particular objection to it, but it's a separate issue to
Bertanrd's. That's a bug-fix, while multipart is an RFE without much
point, IMO.
--
dwmw2
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Johannes Schindelin @ 2006-05-09 11:34 UTC (permalink / raw)
To: Martin Waitz; +Cc: Linus Torvalds, sean, junkio, git
In-Reply-To: <20060509112641.GB3228@admingilde.org>
Hi,
On Tue, 9 May 2006, Martin Waitz wrote:
> So why is everybody trying to munch all branch related data into
> one .ini style config file?
>
> why not simply use the mechanisms we use elsewhere and build something
> like our remotes or the new HEAD file?
Because it is good to have one consistent tool to query/update what makes
up the configuration. Of course, we really could go about it like M$ who
invent a hundred an twenty three ways to do the same thing, all with their
own set of bugs.
I admit that repo-config has not been as stable as it could have been.
That was my fault, certainly. But with the help of the list, it has become
more stable.
Now, if we decide upon a totally different config format, okay, that's
what it takes. But please let's not have several different formats
*again*.
Ciao,
Dscho
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Martin Waitz @ 2006-05-09 11:26 UTC (permalink / raw)
To: Linus Torvalds; +Cc: sean, junkio, git
In-Reply-To: <Pine.LNX.4.64.0605082007100.3718@g5.osdl.org>
[-- Attachment #1: Type: text/plain, Size: 816 bytes --]
hoi :)
On Mon, May 08, 2006 at 08:08:41PM -0700, Linus Torvalds wrote:
> > What's the advantage of section quotation marks over just allowing these
> > characters in regular section names? To be specific, what is wrong with:
> >
> > [jc/show-branch-dense]
>
> This would _suck_
>
> What if you have a branch called "core"? Not all all unlikely.
>
> Think about what a section like
>
> [core]
>
> really means.
>
> Plus I really want to not be case sensitive by default. Case sensitivity
> really is _not_ normal for this kind of config file syntax.
So why is everybody trying to munch all branch related data into
one .ini style config file?
why not simply use the mechanisms we use elsewhere and build something
like our remotes or the new HEAD file?
--
Martin Waitz
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Johannes Schindelin @ 2006-05-09 11:21 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <e3p5om$djs$1@sea.gmane.org>
Hi,
On Tue, 9 May 2006, Jakub Narebski wrote:
> Linus Torvalds wrote:
>
> > I would suggest a much more readable format:
> >
> > [core]
> > ...
> >
> > [branch "core"]
> > ...
> >
> > [remote "core"]
> > ...
> >
> > and yes, enforce the <space>+<quoted name> format. We'd turn it internally
> > into some random internal string format (probably replacing the space with
> > a dot, and removing the quotes, so it would become "remote.core.<key>").
>
> I'm all for it. Nice compromise of [branch."miXedCaps"] and ["miXedCaps"],
> human readable end editable, and easy parsable.
Okay, to summarize what people proposed (and that I remember):
1) [branch."AnY+String"]
2) multiple [branch]
3) magic <space>+<quoted>
4) [branch.just/allow-slashes/and-dashes]
5) the " for " notation
Of all these, only (5) is backwards compatible, but it is also the only
one where you have to type the branch name over and over again.
However, the old gits do not really know what to do with the [branch]
section anyway, so you could consider (2) (and (4), if you do not have
branch names with slashes and/or dashes) backwards-compatible, because git
will continue to work -- ignoring the funny entries.
(1) and (3) definitely would make an old git choke.
Now, for the ease of use:
(1), (3) and (4) are in the same league of easiness (except maybe that you
have to keep in mind to extra-quote in shell scripts with (1) and (3)),
(2) is especially good for people with a database mindset, and (5) is
annoying as hell.
Now, for the ease of implementation:
(1) and (3) are in the same league, they have to change the way the config
is parsed as well as make downcasing conditional in repo-config. (2) is
obviously hardest of all. (4) is very easy (one line in config.c), and (5)
easiest (nothing to do).
Now, for the versatility, i.e. what you can express with the syntax:
The same for all (except for (4) which has very weak restrictions on the
branch name).
Oh, I completely forgot about another proposal: (6) subkeys (something
like "url[branchname] = blablabla"). It has about the same effects as (1)
or (3).
Another thing: I completely ignored the case sensitivity. Because it is
irrelevant. Why? Because you do not have two branches which are only
different by case-ness. It is confusing, and that's why. And you don't
need to handle the case specially, because the comparison is done by
downcasing anyway.
Obviously, I deem (4) the best solution ATM, because it has all the
expressability needed, while being the simplest.
Ciao,
Dscho
^ permalink raw reply
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
From: Timo Hirvonen @ 2006-05-09 9:08 UTC (permalink / raw)
To: normalperson; +Cc: git
In-Reply-To: <11471512103526-git-send-email-normalperson@yhbt.net>
Eric Wong <normalperson@yhbt.net> wrote:
> * unbundling of short options: -uC20n20z => -u -C20 -n20 -z
Does anyone ever use this? I think this makes sense only for flags that
don't have parameters but that would create an ugly special case. Is it
too hard to type "-u -C=20 -n=20 -z"?
> * 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
IMO optional arguments are usually bad idea.
-C 2 (is "2" argument?)
-C2 (-C=2 or -C -2?)
Better to make it obvious there's an argument
-C=2
or not support optional arguments at all and "-C 2" becomes unambiguous.
--
http://onion.dynserv.net/~timo/
^ permalink raw reply
* Re: [PATCH/RFC] gitopt - command-line parsing enhancements
From: Junio C Hamano @ 2006-05-09 8:35 UTC (permalink / raw)
To: Eric Wong; +Cc: git
In-Reply-To: <1147151209168-git-send-email-normalperson@yhbt.net>
Eric Wong <normalperson@yhbt.net> writes:
> Here's my take at a new command-line option parser to reduce wear on my
> fingers. It handles both long and short options, permuting, automatic
> abbreviations, required arguments, optional arguments, and bundling.
Taken a superficial look at it.
Sounds nice, might be a tad too ambitious though. Looks
intrusive at places.
And scary, especially the "eat" macros are very scary.
I have to think about it a bit.
^ permalink raw reply
* [PATCH] apply: fix infinite loop with multiple patches with --index
From: Eric Wong @ 2006-05-09 8:08 UTC (permalink / raw)
To: junkio, git; +Cc: Eric Wong
When multiple patches are passed to git-apply, it will attempt
to open multiple file descriptors to an index, which means
multiple entries will be in the circular cache_file_list.
This change makes git-apply only open the index once and
write the index at exit.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
apply.c | 18 +++++++++---------
1 files changed, 9 insertions(+), 9 deletions(-)
1d0b15a178abaf7ba61085f0acc70521bd71a961
diff --git a/apply.c b/apply.c
index 269210a..ca36391 100644
--- a/apply.c
+++ b/apply.c
@@ -19,6 +19,7 @@ #include "blob.h"
//
static const char *prefix;
static int prefix_length = -1;
+static int newfd = -1;
static int p_value = 1;
static int allow_binary_replacement = 0;
@@ -1873,7 +1874,6 @@ static int use_patch(struct patch *p)
static int apply_patch(int fd, const char *filename)
{
- int newfd;
unsigned long offset, size;
char *buffer = read_patch_file(fd, &size);
struct patch *list = NULL, **listp = &list;
@@ -1904,12 +1904,11 @@ static int apply_patch(int fd, const cha
size -= nr;
}
- newfd = -1;
if (whitespace_error && (new_whitespace == error_on_whitespace))
apply = 0;
write_index = check_index && apply;
- if (write_index)
+ if (write_index && newfd < 0)
newfd = hold_index_file_for_update(&cache_file, get_index_file());
if (check_index) {
if (read_cache() < 0)
@@ -1922,12 +1921,6 @@ static int apply_patch(int fd, const cha
if (apply)
write_out_results(list, skipped_patch);
- if (write_index) {
- if (write_cache(newfd, active_cache, active_nr) ||
- commit_index_file(&cache_file))
- die("Unable to write new cachefile");
- }
-
if (show_index_info)
show_index_list(list);
@@ -2085,5 +2078,12 @@ int main(int argc, char **argv)
whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : "");
}
+
+ if (write_index) {
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_index_file(&cache_file))
+ die("Unable to write new cachefile");
+ }
+
return 0;
}
--
1.3.2.g45f7-dirty
^ permalink raw reply related
* Re: git-feed-mail-list.sh
From: Junio C Hamano @ 2006-05-09 7:32 UTC (permalink / raw)
To: Bertrand Jacquin; +Cc: git, Linus Torvalds
In-Reply-To: <Pine.LNX.4.64.0605081951390.3718@g5.osdl.org>
Linus Torvalds <torvalds@osdl.org> writes:
> But if you want to get it for any random merges, you can always just do
>
> git log -11 --pretty=oneline ^$commit^ $commit^@ |
> sed 's/[0-9a-f]* // ; 11 s/.*/\.\.\./'
>
> which will show up to the ten first commits that were merged (and turn the
> eleventh one, if it exists, into "..." - that's a pretty disgusting trick
> to make it show when you left things out).
>
> That "^$commit^ $commit^@" part is important. It may look like some
> deranged git smiley, but it does exactly what you want it to do: take all
> the parents of the commit, but ignore any commit reachable from the first
> one (the "mainline" of the person who did the commit).
>
> The ^@ syntax is obviously pretty new, so it requires a modern git.
It is indeed very quite new. Merged into "master" branch at the
beginning of this month.
I often wish we had a straightforward way to tell when a given
feature went into the mainline, not just appeared on a topic
branch. In this case, I said:
$ git whatchanged -p -S'"^@"' master -- revision.c
to find ea4a19 commit (Apr 30 00:54:29 2006 -0700). But that
was when the feature was first made on one of my topic branches,
which is not what I was looking for.
By looking at gitk, I can then tell 83262e (May 1 01:54:27)
merged it to "next", and 746437 (May 1 22:55:40) merged it to
"master".
In general this is an unsolvable question, because I can have a
topic branch forked off of the tip of "master", cook it for a
few days without advancing "master" at all, and merge it to
"master" after that. But such a merge will be a fast-forward.
^ permalink raw reply
* Re: [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse()
From: Eric Wong @ 2006-05-09 7:16 UTC (permalink / raw)
To: git
In-Reply-To: <11471512123005-git-send-email-normalperson@yhbt.net>
Eric Wong <normalperson@yhbt.net> wrote:
> I've added --raw to diff_opt_parse() for consistency's sake:
> --patch-with-raw would otherwise be inconsistently abbreviated
> to --raw if it wasn't added.
I was looking for this patch, but then I realized it slipped in here.
I've added --unified=<num>/-u<num> context here so I don't have to go
through the awkwardness of setting GIT_DIFF_OPTS in the environment.
--
Eric Wong
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: Junio C Hamano @ 2006-05-09 7:15 UTC (permalink / raw)
To: Bertrand Jacquin; +Cc: git, David Woodhouse, Linus Torvalds
In-Reply-To: <Pine.LNX.4.64.0605081805290.3718@g5.osdl.org>
Linus Torvalds <torvalds@osdl.org> writes:
> On Tue, 9 May 2006, Bertrand Jacquin wrote:
>
>> On 5/9/06, Linus Torvalds <torvalds@osdl.org> wrote:
>> >
>> > Ie you could probably more easily parse the data from something like
>> >
>> > git show -B --patch-with-stat --pretty=fuller $commit
>>
>> Is there a way to track merge like that ? Documentation is not very
>> clear and near from empty.
>
> Sure.
>
> If you want to track merges and get their patches, add the "--cc" flag,
> which tells git to use the "conflict combination patch" that shows any
> visible conflicts.
Actually, show defaults to --cc so what's shown is good as is.
> That said, the diffstat for merges is usually just a lot of noise. It's
> sometimes nice (you've merged from a topic branch), but if you have merged
> from the mainline _into_ a topic branch, it's just annoying.
True. We made --cc --patch-with-stat to do the combined patch
text with diffstat for first-parent-diff to make it most natural
for merging into upstream, not merging from upstream.
^ permalink raw reply
* [PATCH 6/6] commit: allow --pretty= args to be abbreviated
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
In-Reply-To: <11471512123005-git-send-email-normalperson@yhbt.net>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
commit.c | 42 +++++++++++++++++++++++++++++-------------
1 files changed, 29 insertions(+), 13 deletions(-)
0a3aed7c25eca808b29f2318d5e4c087e03b9bfb
diff --git a/commit.c b/commit.c
index a056b25..055064a 100644
--- a/commit.c
+++ b/commit.c
@@ -22,23 +22,39 @@ struct sort_node
const char *commit_type = "commit";
+struct cmt_fmt_map {
+ const char *n;
+ enum cmit_fmt v;
+} cmt_fmts[] = {
+ { "raw", CMIT_FMT_RAW },
+ { "medium", CMIT_FMT_MEDIUM },
+ { "short", CMIT_FMT_SHORT },
+ { "full", CMIT_FMT_FULL },
+ { "fuller", CMIT_FMT_FULLER },
+ { "oneline", CMIT_FMT_ONELINE },
+};
+
enum cmit_fmt get_commit_format(const char *arg)
{
+ int i, found = -1;
if (!*arg)
return CMIT_FMT_DEFAULT;
- if (!strcmp(arg, "raw"))
- return CMIT_FMT_RAW;
- if (!strcmp(arg, "medium"))
- return CMIT_FMT_MEDIUM;
- if (!strcmp(arg, "short"))
- return CMIT_FMT_SHORT;
- if (!strcmp(arg, "full"))
- return CMIT_FMT_FULL;
- if (!strcmp(arg, "fuller"))
- return CMIT_FMT_FULLER;
- if (!strcmp(arg, "oneline"))
- return CMIT_FMT_ONELINE;
- die("invalid --pretty format");
+ for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
+ if (!strcmp(arg, cmt_fmts[i].n))
+ return cmt_fmts[i].v;
+ }
+
+ /* look for abbreviations */
+ for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
+ if (strstr(cmt_fmts[i].n, arg)) {
+ if (found >= 0)
+ die("invalid --pretty format: %s", arg);
+ found = i;
+ }
+ }
+ if (found >= 0)
+ return cmt_fmts[found].v;
+ die("invalid --pretty format: %s", arg);
}
static struct commit *check_commit(struct object *obj,
--
1.3.2.g0a3ae
^ permalink raw reply related
* [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse()
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
In-Reply-To: <11471512121152-git-send-email-normalperson@yhbt.net>
I've added --raw to diff_opt_parse() for consistency's sake:
--patch-with-raw would otherwise be inconsistently abbreviated
to --raw if it wasn't added.
--with-r => --patch-with-raw works great
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
builtin-diff.c | 109 ++++---------
builtin-log.c | 7 -
commit.c | 12 +
diff-files.c | 22 +--
diff-index.c | 19 +-
diff-tree.c | 21 +-
diff.c | 182 ++++++++++++---------
diff.h | 1
gitopt/diff.h | 26 +++
http-push.c | 2
rev-list.c | 34 ++--
revision.c | 484 +++++++++++++++++++++++++-------------------------------
revision.h | 6 +
13 files changed, 437 insertions(+), 488 deletions(-)
create mode 100644 gitopt/diff.h
28cbff1d1b543e8234acc6cada0fc889d4767a59
diff --git a/builtin-diff.c b/builtin-diff.c
index d3ac581..ff4a0ec 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -12,6 +12,7 @@ #include "diffcore.h"
#include "revision.h"
#include "log-tree.h"
#include "builtin.h"
+#include "gitopt/diff.h"
/* NEEDSWORK: struct object has place for name but we _do_
* know mode when we extracted the blob out of a tree, which
@@ -29,22 +30,12 @@ static int builtin_diff_files(struct rev
int argc, const char **argv)
{
int silent = 0;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--base"))
- revs->max_count = 1;
- else if (!strcmp(arg, "--ours"))
- revs->max_count = 2;
- else if (!strcmp(arg, "--theirs"))
- revs->max_count = 3;
- else if (!strcmp(arg, "-q"))
- silent = 1;
- else if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ struct exec_args *a;
+
+ g_rev = revs;
+ a = new_exec_args(argc);
+ if (gitopt_parse_ost_split(a, a, diff_files_ost, argc, argv))
+ usage(builtin_diff_usage);
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* specified rev.max_count is reasonable (0 <= n <= 3), and
@@ -107,14 +98,8 @@ static int builtin_diff_b_f(struct rev_i
/* Blob vs file in the working tree*/
struct stat st;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ if (argc)
+ usage(builtin_diff_usage);
if (lstat(path, &st))
die("'%s': %s", path, strerror(errno));
if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
@@ -135,14 +120,9 @@ static int builtin_diff_blobs(struct rev
/* Blobs */
unsigned mode = canon_mode(S_IFREG | 0644);
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ if (argc)
+ usage(builtin_diff_usage);
+
stuff_change(&revs->diffopt,
mode, mode,
blob[0].sha1, blob[1].sha1,
@@ -155,17 +135,13 @@ static int builtin_diff_blobs(struct rev
static int builtin_diff_index(struct rev_info *revs,
int argc, const char **argv)
{
- int cached = 0;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--cached"))
- cached = 1;
- else if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ struct exec_args *a;
+
+ g_rev = revs;
+ a = new_exec_args(argc);
+ if (gitopt_parse_ost_split(a, a, diff_index_ost, argc, argv))
+ usage(builtin_diff_usage);
+
/*
* Make sure there is one revision (i.e. pending object),
* and there is no revision filtering parameters.
@@ -183,14 +159,6 @@ static int builtin_diff_tree(struct rev_
{
const unsigned char *(sha1[2]);
int swap = 1;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
/* We saw two trees, ent[0] and ent[1].
* unless ent[0] is unintesting, they are swapped
@@ -212,14 +180,6 @@ static int builtin_diff_combined(struct
const unsigned char (*parent)[20];
int i;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
if (!revs->dense_combined_merges && !revs->combine_merges)
revs->dense_combined_merges = revs->combine_merges = 1;
parent = xmalloc(ents * sizeof(*parent));
@@ -250,6 +210,7 @@ int cmd_diff(int argc, const char **argv
int ents = 0, blobs = 0, paths = 0;
const char *path = NULL;
struct blobinfo blob[2];
+ struct exec_args *b;
/*
* We could get N tree-ish in the rev.pending_objects list.
@@ -274,23 +235,14 @@ int cmd_diff(int argc, const char **argv
git_config(git_diff_config);
init_revisions(&rev);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+ cached = 0;
- argc = setup_revisions(argc, argv, &rev, NULL);
+ b = setup_revisions(argc, argv, &rev, NULL, diff_index_ost);
/* Do we have --cached and not have a pending object, then
* default to HEAD by hand. Eek.
*/
- if (!rev.pending_objects) {
- int i;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (!strcmp(arg, "--"))
- break;
- else if (!strcmp(arg, "--cached")) {
- add_head(&rev);
- break;
- }
- }
- }
+ if (!rev.pending_objects && cached)
+ add_head(&rev);
for (list = rev.pending_objects; list; list = list->next) {
struct object *obj = list->item;
@@ -340,17 +292,18 @@ int cmd_diff(int argc, const char **argv
if (!ents) {
switch (blobs) {
case 0:
- return builtin_diff_files(&rev, argc, argv);
+ return builtin_diff_files(&rev, b->argc, b->argv);
break;
case 1:
if (paths != 1)
usage(builtin_diff_usage);
- return builtin_diff_b_f(&rev, argc, argv, blob, path);
+ return builtin_diff_b_f(&rev, b->argc, b->argv,
+ blob, path);
break;
case 2:
if (paths)
usage(builtin_diff_usage);
- return builtin_diff_blobs(&rev, argc, argv, blob);
+ return builtin_diff_blobs(&rev, b->argc, b->argv, blob);
break;
default:
usage(builtin_diff_usage);
@@ -359,10 +312,10 @@ int cmd_diff(int argc, const char **argv
else if (blobs)
usage(builtin_diff_usage);
else if (ents == 1)
- return builtin_diff_index(&rev, argc, argv);
+ return builtin_diff_index(&rev, b->argc, b->argv);
else if (ents == 2)
- return builtin_diff_tree(&rev, argc, argv, ent);
+ return builtin_diff_tree(&rev, b->argc, b->argv, ent);
else
- return builtin_diff_combined(&rev, argc, argv, ent, ents);
+ return builtin_diff_combined(&rev, b->argc, b->argv, ent, ents);
usage(builtin_diff_usage);
}
diff --git a/builtin-log.c b/builtin-log.c
index 69f2911..b004bc5 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -14,14 +14,15 @@ static int cmd_log_wc(int argc, const ch
struct rev_info *rev)
{
struct commit *commit;
+ struct exec_args *b;
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
rev->verbose_header = 1;
- argc = setup_revisions(argc, argv, rev, "HEAD");
+ b = setup_revisions(argc, argv, rev, "HEAD", NULL);
- if (argc > 1)
- die("unrecognized argument: %s", argv[1]);
+ if (b->argc)
+ die("unrecognized argument: %s", b->argv[0]);
prepare_revision_walk(rev);
setup_pager();
diff --git a/commit.c b/commit.c
index 2717dd8..a056b25 100644
--- a/commit.c
+++ b/commit.c
@@ -26,17 +26,17 @@ enum cmit_fmt get_commit_format(const ch
{
if (!*arg)
return CMIT_FMT_DEFAULT;
- if (!strcmp(arg, "=raw"))
+ if (!strcmp(arg, "raw"))
return CMIT_FMT_RAW;
- if (!strcmp(arg, "=medium"))
+ if (!strcmp(arg, "medium"))
return CMIT_FMT_MEDIUM;
- if (!strcmp(arg, "=short"))
+ if (!strcmp(arg, "short"))
return CMIT_FMT_SHORT;
- if (!strcmp(arg, "=full"))
+ if (!strcmp(arg, "full"))
return CMIT_FMT_FULL;
- if (!strcmp(arg, "=fuller"))
+ if (!strcmp(arg, "fuller"))
return CMIT_FMT_FULLER;
- if (!strcmp(arg, "=oneline"))
+ if (!strcmp(arg, "oneline"))
return CMIT_FMT_ONELINE;
die("invalid --pretty format");
}
diff --git a/diff-files.c b/diff-files.c
index b9d193d..e5b69f4 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -7,6 +7,7 @@ #include "cache.h"
#include "diff.h"
#include "commit.h"
#include "revision.h"
+#include "gitopt/diff.h"
static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
@@ -15,26 +16,17 @@ COMMON_DIFF_OPTIONS_HELP;
int main(int argc, const char **argv)
{
struct rev_info rev;
- int silent = 0;
+ struct exec_args *b;
+ silent = 0;
git_config(git_diff_config);
init_revisions(&rev);
rev.abbrev = 0;
+ g_rev = &rev;
- argc = setup_revisions(argc, argv, &rev, NULL);
- while (1 < argc && argv[1][0] == '-') {
- if (!strcmp(argv[1], "--base"))
- rev.max_count = 1;
- else if (!strcmp(argv[1], "--ours"))
- rev.max_count = 2;
- else if (!strcmp(argv[1], "--theirs"))
- rev.max_count = 3;
- else if (!strcmp(argv[1], "-q"))
- silent = 1;
- else
- usage(diff_files_usage);
- argv++; argc--;
- }
+ b = setup_revisions(argc, argv, &rev, NULL, diff_files_ost);
+ if (b->argc)
+ usage(diff_files_usage);
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* rev.max_count is reasonable (0 <= n <= 3),
diff --git a/diff-index.c b/diff-index.c
index 8c9f601..92052dc 100644
--- a/diff-index.c
+++ b/diff-index.c
@@ -2,6 +2,8 @@ #include "cache.h"
#include "diff.h"
#include "commit.h"
#include "revision.h"
+#include "gitopt.h"
+#include "gitopt/diff.h"
static const char diff_cache_usage[] =
"git-diff-index [-m] [--cached] "
@@ -11,22 +13,17 @@ COMMON_DIFF_OPTIONS_HELP;
int main(int argc, const char **argv)
{
struct rev_info rev;
- int cached = 0;
- int i;
+ struct exec_args *b;
+ cached = 0;
git_config(git_diff_config);
init_revisions(&rev);
rev.abbrev = 0;
- argc = setup_revisions(argc, argv, &rev, NULL);
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (!strcmp(arg, "--cached"))
- cached = 1;
- else
- usage(diff_cache_usage);
- }
+ b = setup_revisions(argc, argv, &rev, NULL, diff_index_ost);
+ if (b->argc)
+ usage(diff_cache_usage);
+
/*
* Make sure there is one revision (i.e. pending object),
* and there is no revision filtering parameters.
diff --git a/diff-tree.c b/diff-tree.c
index 7207867..240242c 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -2,6 +2,7 @@ #include "cache.h"
#include "diff.h"
#include "commit.h"
#include "log-tree.h"
+#include "gitopt.h"
static struct rev_info log_tree_opt;
@@ -58,6 +59,13 @@ static const char diff_tree_usage[] =
" --root include the initial commit as diff against /dev/null\n"
COMMON_DIFF_OPTIONS_HELP;
+static int read_stdin = 0;
+gitopt_eat(opt_stdin, read_stdin = 1;)
+static const struct opt_spec diff_tree_ost[] = {
+ { "stdin", 0, 0, 0, opt_stdin },
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
int nr_sha1;
@@ -65,24 +73,17 @@ int main(int argc, const char **argv)
struct object *tree1, *tree2;
static struct rev_info *opt = &log_tree_opt;
struct object_list *list;
- int read_stdin = 0;
+ struct exec_args *b;
git_config(git_diff_config);
nr_sha1 = 0;
init_revisions(opt);
opt->abbrev = 0;
opt->diff = 1;
- argc = setup_revisions(argc, argv, opt, NULL);
-
- while (--argc > 0) {
- const char *arg = *++argv;
- if (!strcmp(arg, "--stdin")) {
- read_stdin = 1;
- continue;
- }
+ b = setup_revisions(argc, argv, opt, NULL, diff_tree_ost);
+ if (b->argc)
usage(diff_tree_usage);
- }
/*
* NOTE! "setup_revisions()" will have inserted the revisions
diff --git a/diff.c b/diff.c
index 5315270..3b56efc 100644
--- a/diff.c
+++ b/diff.c
@@ -9,6 +9,8 @@ #include "quote.h"
#include "diff.h"
#include "diffcore.h"
#include "xdiff-interface.h"
+#include "gitopt.h"
+#include "gitopt/abbrev.h"
static int use_size_cache;
@@ -407,7 +409,8 @@ static void builtin_diff(const char *nam
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
- int complete_rewrite)
+ int complete_rewrite,
+ struct diff_options *o)
{
mmfile_t mf1, mf2;
const char *lbl[2];
@@ -463,10 +466,9 @@ static void builtin_diff(const char *nam
ecbdata.label_path = lbl;
xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_FUNCNAMES;
if (!diffopts)
- ;
+ xecfg.ctxlen = o->ctxlen;
else if (!strncmp(diffopts, "--unified=", 10))
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!strncmp(diffopts, "-u", 2))
@@ -928,7 +930,8 @@ static void run_diff_cmd(const char *pgm
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
- int complete_rewrite)
+ int complete_rewrite,
+ struct diff_options *o)
{
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
@@ -937,7 +940,7 @@ static void run_diff_cmd(const char *pgm
}
if (one && two)
builtin_diff(name, other ? other : name,
- one, two, xfrm_msg, complete_rewrite);
+ one, two, xfrm_msg, complete_rewrite, o);
else
printf("* Unmerged path %s\n", name);
}
@@ -971,7 +974,7 @@ static void run_diff(struct diff_filepai
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
- run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+ run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0, o);
return;
}
@@ -1041,15 +1044,15 @@ static void run_diff(struct diff_filepai
* needs to be split into deletion and creation.
*/
struct diff_filespec *null = alloc_filespec(two->path);
- run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+ run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0, o);
free(null);
null = alloc_filespec(one->path);
- run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+ run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0, o);
free(null);
}
else
run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
- complete_rewrite);
+ complete_rewrite, o);
free(name_munged);
free(other_munged);
@@ -1086,6 +1089,7 @@ void diff_setup(struct diff_options *opt
options->line_termination = '\n';
options->break_opt = -1;
options->rename_limit = -1;
+ options->ctxlen = 3;
options->change = diff_change;
options->add_remove = diff_addremove;
@@ -1126,76 +1130,100 @@ int diff_setup_done(struct diff_options
return 0;
}
+static struct diff_options *g_opt = NULL; /* gitopt needs globals :x */
+static int rv_diff_opt_parse = 0;
+
+gitopt_eat_opt_int(opt_unified,
+ g_opt->output_format = DIFF_FORMAT_PATCH;
+ if (ea->argc) g_opt->ctxlen = strtol(ea->argv[0], NULL, 10);)
+gitopt_eat(opt_p, g_opt->output_format = DIFF_FORMAT_PATCH;)
+gitopt_eat(opt_patch_with_raw,
+ g_opt->output_format = DIFF_FORMAT_PATCH;
+ g_opt->with_raw = 1;)
+gitopt_eat(opt_raw, g_opt->output_format = DIFF_FORMAT_RAW;)
+gitopt_eat(opt_stat,
+ g_opt->output_format = DIFF_FORMAT_DIFFSTAT;)
+gitopt_eat(opt_patch_with_stat,
+ g_opt->output_format = DIFF_FORMAT_PATCH;
+ g_opt->with_stat = 1;)
+gitopt_eat(opt_z, g_opt->line_termination = 0;)
+gitopt_eat_one_arg(opt_l, g_opt->rename_limit = strtoul(ea->argv[0],NULL,10);)
+gitopt_eat(opt_full_index, g_opt->full_index = 1;)
+gitopt_eat(opt_name_only, g_opt->output_format = DIFF_FORMAT_NAME;)
+gitopt_eat(opt_name_status,
+ g_opt->output_format = DIFF_FORMAT_NAME_STATUS;)
+gitopt_eat(opt_R, g_opt->reverse_diff = 1;)
+gitopt_eat_one_arg(opt_S, g_opt->pickaxe = ea->argv[0];)
+gitopt_eat(opt_s, g_opt->output_format = DIFF_FORMAT_NO_OUTPUT;)
+gitopt_eat_one_arg(opt_O, g_opt->orderfile = ea->argv[0];)
+gitopt_eat_one_arg(opt_diff_filter, g_opt->filter = ea->argv[0];)
+gitopt_eat(opt_pickaxe_all, g_opt->pickaxe_opts = DIFF_PICKAXE_ALL;)
+gitopt_eat(opt_pickaxe_regex, g_opt->pickaxe_opts = DIFF_PICKAXE_REGEX;)
+gitopt_eat_opt_int(opt_B,
+ if ((g_opt->break_opt = diff_scoreopt_parse(*argv)) == -1)
+ rv_diff_opt_parse = -1;)
+gitopt_eat_opt_int(opt_M,
+ if ((g_opt->rename_score = diff_scoreopt_parse(*argv)) == -1)
+ rv_diff_opt_parse = -1;
+ g_opt->detect_rename = DIFF_DETECT_RENAME;)
+gitopt_eat_opt_int(opt_C,
+ if ((g_opt->rename_score = diff_scoreopt_parse(*argv)) == -1)
+ rv_diff_opt_parse = -1;
+ g_opt->detect_rename = DIFF_DETECT_COPY;)
+gitopt_eat(opt_find_copies_harder, g_opt->find_copies_harder = 1;)
+gitopt_opt_abbrev(g_opt->abbrev)
+
+static const struct opt_spec diff_ost[] = {
+ { 0, 'p', 0, 0, opt_p },
+ { "unified", 'u', "%s", ARG_OPTINT, opt_unified },
+ { "raw", 0, 0, 0, opt_raw },
+ { "patch-with-raw", 0, 0, 0, opt_patch_with_raw },
+ { "stat", 0, 0, 0, opt_stat },
+ { "patch-with-stat", 0, 0, 0, opt_patch_with_stat },
+ { 0, 'z', 0, 0, opt_z },
+ { 0, 'l', "%s", ARG_INT,opt_l },
+ { "full-index", 0, 0, 0, opt_full_index },
+ { "name-only", 0, 0, 0, opt_name_only },
+ { "name-status", 0, 0, 0, opt_name_status },
+ { 0, 'R', 0, 0, opt_R },
+ { 0, 'S', "%s", ARG_ONE,opt_S },
+ { 0, 's', 0, 0, opt_s },
+ { 0, 'O', "%s", ARG_ONE, opt_O },
+ { "diff-filter", 0, "%s", ARG_ONE, opt_diff_filter },
+ { "pickaxe-all", 0, 0, 0, opt_pickaxe_all },
+ { "pickaxe-regex", 0, 0, 0, opt_pickaxe_regex },
+ { 0, 'B', "%s", ARG_OPTINT,opt_B },
+ { 0, 'M', "%s", ARG_OPTINT,opt_M },
+ { 0, 'C', "%s", ARG_OPTINT,opt_C },
+ { "find-copies-harder", 0, 0, 0, opt_find_copies_harder},
+ abbrev_ost_row,
+ { 0, 0 }
+};
+
+static void diff_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos)
+{
+ rv_diff_opt_parse = 0;
+}
+
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 (!strcmp(arg, "--patch-with-raw")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_raw = 1;
- }
- else if (!strcmp(arg, "--stat"))
- options->output_format = DIFF_FORMAT_DIFFSTAT;
- else if (!strcmp(arg, "--patch-with-stat")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_stat = 1;
- }
- else if (!strcmp(arg, "-z"))
- options->line_termination = 0;
- else if (!strncmp(arg, "-l", 2))
- options->rename_limit = strtoul(arg+2, NULL, 10);
- else if (!strcmp(arg, "--full-index"))
- options->full_index = 1;
- else if (!strcmp(arg, "--name-only"))
- options->output_format = DIFF_FORMAT_NAME;
- else if (!strcmp(arg, "--name-status"))
- options->output_format = DIFF_FORMAT_NAME_STATUS;
- else if (!strcmp(arg, "-R"))
- options->reverse_diff = 1;
- else if (!strncmp(arg, "-S", 2))
- options->pickaxe = arg + 2;
- else if (!strcmp(arg, "-s"))
- options->output_format = DIFF_FORMAT_NO_OUTPUT;
- else if (!strncmp(arg, "-O", 2))
- options->orderfile = arg + 2;
- else if (!strncmp(arg, "--diff-filter=", 14))
- options->filter = arg + 14;
- else if (!strcmp(arg, "--pickaxe-all"))
- options->pickaxe_opts = DIFF_PICKAXE_ALL;
- else if (!strcmp(arg, "--pickaxe-regex"))
- options->pickaxe_opts = DIFF_PICKAXE_REGEX;
- else if (!strncmp(arg, "-B", 2)) {
- if ((options->break_opt =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- }
- else if (!strncmp(arg, "-M", 2)) {
- if ((options->rename_score =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- options->detect_rename = DIFF_DETECT_RENAME;
- }
- else if (!strncmp(arg, "-C", 2)) {
- if ((options->rename_score =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- options->detect_rename = DIFF_DETECT_COPY;
- }
- else if (!strcmp(arg, "--find-copies-harder"))
- options->find_copies_harder = 1;
- else if (!strcmp(arg, "--abbrev"))
- options->abbrev = DEFAULT_ABBREV;
- else if (!strncmp(arg, "--abbrev=", 9)) {
- options->abbrev = strtoul(arg + 9, NULL, 10);
- if (options->abbrev < MINIMUM_ABBREV)
- options->abbrev = MINIMUM_ABBREV;
- else if (40 < options->abbrev)
- options->abbrev = 40;
- }
- else
- return 0;
- return 1;
+ struct exec_args *a = new_exec_args(1);
+ int i = 0;
+ void (*old_non_option_cb)(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+ g_opt = options;
+ rv_diff_opt_parse = 1;
+ gitopt_pass_through = 1;
+ old_non_option_cb = gitopt_non_option_cb;
+ gitopt_non_option_cb = diff_non_option_cb;
+
+ gitopt_parse_one_opt(a, a, diff_ost, ac, av, &i);
+
+ free_exec_args(a);
+ gitopt_non_option_cb = old_non_option_cb;
+
+ return rv_diff_opt_parse;
}
static int parse_num(const char **cp_p)
diff --git a/diff.h b/diff.h
index b3b2c4d..b5f016e 100644
--- a/diff.h
+++ b/diff.h
@@ -41,6 +41,7 @@ struct diff_options {
int rename_limit;
int setup;
int abbrev;
+ int ctxlen;
int nr_paths;
const char **paths;
diff --git a/gitopt/diff.h b/gitopt/diff.h
new file mode 100644
index 0000000..6530474
--- /dev/null
+++ b/gitopt/diff.h
@@ -0,0 +1,26 @@
+#ifndef GITOPT_DIFF_H
+#define GITOPT_DIFF_H
+
+static struct rev_info *g_rev;
+static int silent;
+static int cached;
+
+gitopt_eat(opt_base, g_rev->max_count = 1;)
+gitopt_eat(opt_ours, g_rev->max_count = 2;)
+gitopt_eat(opt_theres, g_rev->max_count = 3;)
+gitopt_eat(opt_q, silent = 1;)
+static const struct opt_spec diff_files_ost[] = {
+ { "base", 0, 0, 0, opt_base },
+ { "ours", 0, 0, 0, opt_ours },
+ { "theres", 0, 0, 0, opt_theres },
+ { 0, 'q', 0, 0, opt_q },
+ { 0, 0 }
+};
+
+gitopt_eat(opt_cached, cached = 1;)
+static const struct opt_spec diff_index_ost[] = {
+ { "cached", 0, 0, 0, opt_cached },
+ { 0, 0 }
+};
+
+#endif /* GITOPT_DIFF_H */
diff --git a/http-push.c b/http-push.c
index b4327d9..9c16f3b 100644
--- a/http-push.c
+++ b/http-push.c
@@ -2499,7 +2499,7 @@ int main(int argc, char **argv)
commit_argc++;
}
init_revisions(&revs);
- setup_revisions(commit_argc, commit_argv, &revs, NULL);
+ setup_revisions(commit_argc, commit_argv, &revs, NULL, NULL);
free(new_sha1_hex);
if (old_sha1_hex) {
free(old_sha1_hex);
diff --git a/rev-list.c b/rev-list.c
index 8b0ec38..3c72c59 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -7,6 +7,7 @@ #include "blob.h"
#include "tree-walk.h"
#include "diff.h"
#include "revision.h"
+#include "gitopt.h"
/* bits #0-15 in revision.h */
@@ -291,34 +292,31 @@ static void mark_edges_uninteresting(str
}
}
+static struct rev_info *g_rev;
+gitopt_eat(opt_header, g_rev->verbose_header = 1;)
+gitopt_eat(opt_timestamp, show_timestamp = 1;)
+gitopt_eat(opt_bisect, bisect_list = 1;)
+static const struct opt_spec rev_list_ost[] = {
+ { "header", 0, 0, 0, opt_header },
+ { "timestamp", 0, 0, 0, opt_timestamp },
+ { "bisect", 0, 0, 0, opt_bisect },
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
struct commit_list *list;
- int i;
+ struct exec_args *b;
init_revisions(&revs);
revs.abbrev = 0;
revs.commit_format = CMIT_FMT_UNSPECIFIED;
- argc = setup_revisions(argc, argv, &revs, NULL);
-
- for (i = 1 ; i < argc; i++) {
- const char *arg = argv[i];
+ g_rev = &revs;
- if (!strcmp(arg, "--header")) {
- revs.verbose_header = 1;
- continue;
- }
- if (!strcmp(arg, "--timestamp")) {
- show_timestamp = 1;
- continue;
- }
- if (!strcmp(arg, "--bisect")) {
- bisect_list = 1;
- continue;
- }
+ b = setup_revisions(argc, argv, &revs, NULL, rev_list_ost);
+ if (b->argc)
usage(rev_list_usage);
- }
if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
/* The command line has a --pretty */
hdr_termination = '\n';
diff --git a/revision.c b/revision.c
index 2294b16..6769a48 100644
--- a/revision.c
+++ b/revision.c
@@ -6,6 +6,7 @@ #include "commit.h"
#include "diff.h"
#include "refs.h"
#include "revision.h"
+#include "gitopt/abbrev.h"
static char *path_name(struct name_path *path, const char *name)
{
@@ -534,6 +535,198 @@ void init_revisions(struct rev_info *rev
diff_setup(&revs->diffopt);
}
+static const char *g_def = NULL;
+
+/* I think I should just be able to use the all_* versions of these.. -ew */
+static struct rev_info *g_revs = NULL;
+static int g_flags = 0;
+static const struct opt_spec *g_extra_ost;
+
+gitopt_eat_int(opt_max_count, g_revs->max_count = atoi(ea->argv[0]);)
+gitopt_eat_int(opt_max_age, g_revs->max_age = atoi(ea->argv[0]);)
+gitopt_eat_int(opt_since, g_revs->max_age = approxidate(ea->argv[0]);)
+gitopt_eat_int(opt_min_age, g_revs->min_age = atoi(ea->argv[0]);)
+gitopt_eat_int(opt_until, g_revs->min_age = approxidate(ea->argv[0]);)
+gitopt_eat(opt_all, handle_all(g_revs, g_flags);)
+gitopt_eat(opt_not, g_flags ^= UNINTERESTING;)
+gitopt_eat_arg(opt_default, g_def = ea->argv[0];)
+gitopt_eat(opt_topo_order, g_revs->topo_order = 1;)
+gitopt_eat(opt_date_order, g_revs->topo_order = 1; g_revs->lifo = 0;)
+gitopt_eat(opt_parents, g_revs->parents = 1;)
+gitopt_eat(opt_dense, g_revs->dense = 1;)
+gitopt_eat(opt_sparse, g_revs->dense = 0;)
+gitopt_eat(opt_remove_empty, g_revs->remove_empty_trees = 1;)
+gitopt_eat(opt_no_merges, g_revs->no_merges = 1;)
+gitopt_eat(opt_boundary, g_revs->boundary = 1;)
+gitopt_eat(opt_objects, g_revs->tag_objects = g_revs->tree_objects =
+ g_revs->blob_objects = 1;)
+gitopt_eat(opt_objects_edge, g_revs->tag_objects = g_revs->tree_objects =
+ g_revs->blob_objects = g_revs->edge_hint = 1;)
+gitopt_eat(opt_unpacked, g_revs->unpacked = 1;)
+gitopt_eat(opt_r, g_revs->diff = g_revs->diffopt.recursive = 1;)
+gitopt_eat(opt_t, g_revs->diff = g_revs->diffopt.recursive =
+ g_revs->diffopt.tree_in_recursive = 1;)
+gitopt_eat(opt_m, g_revs->ignore_merges = 0;)
+gitopt_eat(opt_c, g_revs->diff = g_revs->combine_merges = 1;
+ g_revs->dense_combined_merges = 0;)
+gitopt_eat(opt_cc, g_revs->diff = g_revs->combine_merges =
+ g_revs->dense_combined_merges = 1;)
+gitopt_eat(opt_v, g_revs->verbose_header = 1;)
+gitopt_eat_arg(opt_pretty, g_revs->verbose_header = 1;
+ g_revs->commit_format = get_commit_format(
+ ea->argv[0]);)
+gitopt_eat(opt_root, g_revs->show_root_diff = 1;)
+gitopt_eat(opt_no_commit_id, g_revs->no_commit_id = 1;)
+gitopt_eat(opt_always, g_revs->always_show_header = 1;)
+gitopt_opt_abbrev(g_revs->abbrev)
+gitopt_eat(opt_no_abbrev, g_revs->abbrev = 0;)
+gitopt_eat(opt_abbrev_commit, g_revs->abbrev_commit = 1;)
+gitopt_eat(opt_full_diff, g_revs->full_diff = g_revs->diff = 1;)
+
+static const struct opt_spec setup_revisions_ost[] = {
+ { "max-count", 'n', "%s", ARG_INT, opt_max_count },
+ { 0, ' ', "%s", ARG_INT, opt_max_count },
+ { "max-age", 0, "%s", ARG_INT, opt_max_age },
+ { "min-age", 0, "%s", ARG_INT, opt_min_age },
+ { "since", 0, "%s", ARG_ONE, opt_since },
+ { "after", 0, "%s", ARG_ONE, opt_since },
+ { "before", 0, "%s", ARG_ONE, opt_until },
+ { "until", 0, "%s", ARG_ONE, opt_until },
+ { "all", 0, 0, 0, opt_all },
+ { "not", 0, 0, 0, opt_not },
+ { "default", 0, "%s", ARG_ONE, opt_default },
+ { "topo-order", 0, 0, 0, opt_topo_order },
+ { "date-order", 0, 0, 0, opt_date_order },
+ { "parents", 0, 0, 0, opt_parents },
+ { "dense", 0, 0, 0, opt_dense },
+ { "sparse", 0, 0, 0, opt_sparse },
+ { "remove-empty", 0, 0, 0, opt_remove_empty },
+ { "no-merges", 0, 0, 0, opt_no_merges },
+ { "boundary", 0, 0, 0, opt_boundary },
+ { "objects", 0, 0, 0, opt_objects },
+ { "objects-edge", 0, 0, 0, opt_objects_edge },
+ { "unpacked", 0, 0, 0, opt_unpacked },
+ { 0, 'r', 0, 0, opt_r },
+ { 0, 't', 0, 0, opt_t },
+ { 0, 'm', 0, 0, opt_m },
+ { 0, 'c', 0, 0, opt_c },
+ { "cc", 0, 0, 0, opt_cc },
+ { 0, 'v', 0, 0, opt_v },
+ { "pretty", 0, "%s", ARG_ONE, opt_pretty },
+ { "root", 0, 0, 0, opt_root },
+ { "no-commit-id", 0, 0, 0, opt_no_commit_id },
+ { "always", 0, 0, 0, opt_always },
+ { "no-abbrev", 0, 0, 0, opt_no_abbrev },
+ abbrev_ost_row,
+ { "abbrev-commit", 0, 0, 0, opt_abbrev_commit },
+ { "full-diff", 0, 0, 0, opt_full_diff },
+ { 0, 0 }
+};
+
+/* call this for every non-option (and everything after "--") we have */
+static void setup_revisions_non_option_cb(struct exec_args *b,
+ const int argc, const char **argv, int *argc_pos)
+{
+ int i = *argc_pos;
+ const char *arg = argv[i];
+ unsigned char sha1[20];
+ struct object *object;
+ char *dotdot;
+ int local_flags;
+
+ if (arg[0] == '-') { /* handle diff options: */
+ int opts = diff_opt_parse(&(g_revs->diffopt),
+ argv + i, argc - 1);
+ if (opts > 0) {
+ g_revs->diff = 1;
+ *argc_pos += opts - 1;
+ return;
+ }
+ if (g_extra_ost) {
+ void (*old_non_option_cb)(struct exec_args *b,
+ const int argc, const char **argv,
+ int *argc_pos);
+ int j = 0;
+ old_non_option_cb = gitopt_non_option_cb;
+ gitopt_non_option_cb = gitopt_default_non_option_cb;
+ gitopt_parse_one_opt(b, b, g_extra_ost,
+ argc - i, argv + i, &j);
+ gitopt_non_option_cb = old_non_option_cb;
+ *argc_pos += j;
+ }
+ return;
+ }
+
+ /* otherwise it's a revision */
+ dotdot = strstr(arg, "..");
+ if (dotdot) {
+ unsigned char from_sha1[20];
+ const char *next = dotdot + 2;
+ const char *this = arg;
+ *dotdot = 0;
+ if (!*next)
+ next = "HEAD";
+ if (dotdot == arg)
+ this = "HEAD";
+ if (!get_sha1(this, from_sha1) &&
+ !get_sha1(next, sha1)) {
+ struct object *exclude;
+ struct object *include;
+
+ exclude = get_reference(g_revs, this, from_sha1,
+ g_flags ^ UNINTERESTING);
+ include = get_reference(g_revs, next, sha1, g_flags);
+ if (!exclude || !include)
+ die("Invalid revision range %s..%s", arg, next);
+
+ if (!gitopt_dd_seen) {
+ *dotdot = '.';
+ verify_non_filename(g_revs->prefix, arg);
+ }
+ add_pending_object(g_revs, exclude, this);
+ add_pending_object(g_revs, include, next);
+ return;
+ }
+ *dotdot = '.';
+ }
+
+ dotdot = strstr(arg, "^@");
+ if (dotdot && !dotdot[2]) {
+ *dotdot = 0;
+ if (add_parents_only(g_revs, arg, g_flags))
+ return;
+ *dotdot = '^';
+ }
+ local_flags = 0;
+ if (*arg == '^') {
+ local_flags = UNINTERESTING;
+ arg++;
+ }
+ if (get_sha1(arg, sha1) < 0) {
+ int j;
+
+ if (gitopt_dd_seen || local_flags)
+ die("bad revision '%s'", arg);
+
+ /* If we didn't have a "--":
+ * (1) all filenames must exist;
+ * (2) all rev-args must not be interpretable
+ * as a valid filename.
+ * but the latter we have checked in the main loop.
+ */
+ for (j = *argc_pos; j < argc; j++)
+ verify_filename(g_revs->prefix, argv[j]);
+
+ g_revs->prune_data = get_pathspec(g_revs->prefix,
+ argv + *argc_pos);
+ return;
+ }
+ if (!gitopt_dd_seen)
+ verify_non_filename(g_revs->prefix, arg);
+ object = get_reference(g_revs, arg, sha1, g_flags ^ local_flags);
+ add_pending_object(g_revs, object, arg);
+}
+
/*
* Parse revision information, filling in the "rev_info" structure,
* and removing the used arguments from the argument list.
@@ -541,14 +734,19 @@ void init_revisions(struct rev_info *rev
* Returns the number of arguments left that weren't recognized
* (which are also moved to the head of the argument list)
*/
-int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
+struct exec_args *setup_revisions(int argc, const char **argv,
+ struct rev_info *revs, const char *def,
+ const struct opt_spec *extra_ost)
{
- int i, flags, seen_dashdash;
- const char **unrecognized = argv + 1;
- int left = 1;
+ int i;
+ struct exec_args *a, *b;
+
+ g_extra_ost = extra_ost;
+ g_def = def;
+ g_revs = revs;
+ gitopt_dd_seen = 0;
/* First, search for "--" */
- seen_dashdash = 0;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (strcmp(arg, "--"))
@@ -556,274 +754,24 @@ int setup_revisions(int argc, const char
argv[i] = NULL;
argc = i;
revs->prune_data = get_pathspec(revs->prefix, argv + i + 1);
- seen_dashdash = 1;
+ gitopt_dd_seen = 1;
break;
}
- flags = 0;
- for (i = 1; i < argc; i++) {
- struct object *object;
- const char *arg = argv[i];
- unsigned char sha1[20];
- char *dotdot;
- int local_flags;
-
- if (*arg == '-') {
- int opts;
- if (!strncmp(arg, "--max-count=", 12)) {
- revs->max_count = atoi(arg + 12);
- continue;
- }
- /* accept -<digit>, like traditional "head" */
- if ((*arg == '-') && isdigit(arg[1])) {
- revs->max_count = atoi(arg + 1);
- continue;
- }
- if (!strcmp(arg, "-n")) {
- if (argc <= i + 1)
- die("-n requires an argument");
- revs->max_count = atoi(argv[++i]);
- continue;
- }
- if (!strncmp(arg,"-n",2)) {
- revs->max_count = atoi(arg + 2);
- continue;
- }
- if (!strncmp(arg, "--max-age=", 10)) {
- revs->max_age = atoi(arg + 10);
- continue;
- }
- if (!strncmp(arg, "--since=", 8)) {
- revs->max_age = approxidate(arg + 8);
- continue;
- }
- if (!strncmp(arg, "--after=", 8)) {
- revs->max_age = approxidate(arg + 8);
- continue;
- }
- if (!strncmp(arg, "--min-age=", 10)) {
- revs->min_age = atoi(arg + 10);
- continue;
- }
- if (!strncmp(arg, "--before=", 9)) {
- revs->min_age = approxidate(arg + 9);
- continue;
- }
- if (!strncmp(arg, "--until=", 8)) {
- revs->min_age = approxidate(arg + 8);
- continue;
- }
- if (!strcmp(arg, "--all")) {
- handle_all(revs, flags);
- continue;
- }
- if (!strcmp(arg, "--not")) {
- flags ^= UNINTERESTING;
- continue;
- }
- if (!strcmp(arg, "--default")) {
- if (++i >= argc)
- die("bad --default argument");
- def = argv[i];
- continue;
- }
- if (!strcmp(arg, "--topo-order")) {
- revs->topo_order = 1;
- continue;
- }
- if (!strcmp(arg, "--date-order")) {
- revs->lifo = 0;
- revs->topo_order = 1;
- continue;
- }
- if (!strcmp(arg, "--parents")) {
- revs->parents = 1;
- continue;
- }
- if (!strcmp(arg, "--dense")) {
- revs->dense = 1;
- continue;
- }
- if (!strcmp(arg, "--sparse")) {
- revs->dense = 0;
- continue;
- }
- if (!strcmp(arg, "--remove-empty")) {
- revs->remove_empty_trees = 1;
- continue;
- }
- if (!strcmp(arg, "--no-merges")) {
- revs->no_merges = 1;
- continue;
- }
- if (!strcmp(arg, "--boundary")) {
- revs->boundary = 1;
- continue;
- }
- if (!strcmp(arg, "--objects")) {
- revs->tag_objects = 1;
- revs->tree_objects = 1;
- revs->blob_objects = 1;
- continue;
- }
- if (!strcmp(arg, "--objects-edge")) {
- revs->tag_objects = 1;
- revs->tree_objects = 1;
- revs->blob_objects = 1;
- revs->edge_hint = 1;
- continue;
- }
- if (!strcmp(arg, "--unpacked")) {
- revs->unpacked = 1;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- revs->diff = 1;
- revs->diffopt.recursive = 1;
- continue;
- }
- if (!strcmp(arg, "-t")) {
- revs->diff = 1;
- revs->diffopt.recursive = 1;
- revs->diffopt.tree_in_recursive = 1;
- continue;
- }
- if (!strcmp(arg, "-m")) {
- revs->ignore_merges = 0;
- continue;
- }
- if (!strcmp(arg, "-c")) {
- revs->diff = 1;
- revs->dense_combined_merges = 0;
- revs->combine_merges = 1;
- continue;
- }
- if (!strcmp(arg, "--cc")) {
- revs->diff = 1;
- revs->dense_combined_merges = 1;
- revs->combine_merges = 1;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- revs->verbose_header = 1;
- continue;
- }
- if (!strncmp(arg, "--pretty", 8)) {
- revs->verbose_header = 1;
- revs->commit_format = get_commit_format(arg+8);
- continue;
- }
- if (!strcmp(arg, "--root")) {
- revs->show_root_diff = 1;
- continue;
- }
- if (!strcmp(arg, "--no-commit-id")) {
- revs->no_commit_id = 1;
- continue;
- }
- if (!strcmp(arg, "--always")) {
- revs->always_show_header = 1;
- continue;
- }
- if (!strcmp(arg, "--no-abbrev")) {
- revs->abbrev = 0;
- continue;
- }
- if (!strcmp(arg, "--abbrev")) {
- revs->abbrev = DEFAULT_ABBREV;
- continue;
- }
- if (!strcmp(arg, "--abbrev-commit")) {
- revs->abbrev_commit = 1;
- continue;
- }
- if (!strcmp(arg, "--full-diff")) {
- revs->diff = 1;
- revs->full_diff = 1;
- continue;
- }
- opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
- if (opts > 0) {
- revs->diff = 1;
- i += opts - 1;
- continue;
- }
- *unrecognized++ = arg;
- left++;
- continue;
- }
- dotdot = strstr(arg, "..");
- if (dotdot) {
- unsigned char from_sha1[20];
- const char *next = dotdot + 2;
- const char *this = arg;
- *dotdot = 0;
- if (!*next)
- next = "HEAD";
- if (dotdot == arg)
- this = "HEAD";
- if (!get_sha1(this, from_sha1) &&
- !get_sha1(next, sha1)) {
- struct object *exclude;
- struct object *include;
-
- exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
- include = get_reference(revs, next, sha1, flags);
- if (!exclude || !include)
- die("Invalid revision range %s..%s", arg, next);
-
- if (!seen_dashdash) {
- *dotdot = '.';
- verify_non_filename(revs->prefix, arg);
- }
- add_pending_object(revs, exclude, this);
- add_pending_object(revs, include, next);
- continue;
- }
- *dotdot = '.';
- }
- dotdot = strstr(arg, "^@");
- if (dotdot && !dotdot[2]) {
- *dotdot = 0;
- if (add_parents_only(revs, arg, flags))
- continue;
- *dotdot = '^';
- }
- local_flags = 0;
- if (*arg == '^') {
- local_flags = UNINTERESTING;
- arg++;
- }
- if (get_sha1(arg, sha1)) {
- int j;
-
- if (seen_dashdash || local_flags)
- die("bad revision '%s'", arg);
-
- /* If we didn't have a "--":
- * (1) all filenames must exist;
- * (2) all rev-args must not be interpretable
- * as a valid filename.
- * but the latter we have checked in the main loop.
- */
- for (j = i; j < argc; j++)
- verify_filename(revs->prefix, argv[j]);
+ a = new_exec_args(argc);
+ b = new_exec_args(argc);
+ gitopt_pass_through = 1;
+ gitopt_non_option_cb = setup_revisions_non_option_cb;
+ if (gitopt_parse_ost_split(a, b, setup_revisions_ost, argc, argv))
+ return b;
- revs->prune_data = get_pathspec(revs->prefix, argv + i);
- break;
- }
- if (!seen_dashdash)
- verify_non_filename(revs->prefix, arg);
- object = get_reference(revs, arg, sha1, flags ^ local_flags);
- add_pending_object(revs, object, arg);
- }
- if (def && !revs->pending_objects) {
+ if (g_def && !revs->pending_objects) {
unsigned char sha1[20];
struct object *object;
- if (get_sha1(def, sha1))
- die("bad default revision '%s'", def);
- object = get_reference(revs, def, sha1, 0);
- add_pending_object(revs, object, def);
+ if (get_sha1(g_def, sha1))
+ die("bad default revision '%s'", g_def);
+ object = get_reference(revs, g_def, sha1, 0);
+ add_pending_object(revs, object, g_def);
}
if (revs->topo_order || revs->unpacked)
@@ -844,7 +792,7 @@ int setup_revisions(int argc, const char
revs->diffopt.abbrev = revs->abbrev;
diff_setup_done(&revs->diffopt);
- return left;
+ return b;
}
void prepare_revision_walk(struct rev_info *revs)
diff --git a/revision.h b/revision.h
index 48d7b4c..11a5820 100644
--- a/revision.h
+++ b/revision.h
@@ -1,6 +1,8 @@
#ifndef REVISION_H
#define REVISION_H
+#include "gitopt.h"
+
#define SEEN (1u<<0)
#define UNINTERESTING (1u<<1)
#define TREECHANGE (1u<<2)
@@ -81,7 +83,9 @@ extern int rev_same_tree_as_empty(struct
extern int rev_compare_tree(struct rev_info *, struct tree *t1, struct tree *t2);
extern void init_revisions(struct rev_info *revs);
-extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
+extern struct exec_args *setup_revisions(int argc, const char **argv,
+ struct rev_info *revs, const char *def,
+ const struct opt_spec *extra_ost);
extern void prepare_revision_walk(struct rev_info *revs);
extern struct commit *get_revision(struct rev_info *revs);
--
1.3.2.g0a3ae
^ permalink raw reply related
* [PATCH 4/6] ls-files: convert to using gitopt
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
In-Reply-To: <1147151211399-git-send-email-normalperson@yhbt.net>
Another simple and straight forward conversion, imho.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
ls-files.c | 187 ++++++++++++++++++++++--------------------------------------
1 files changed, 69 insertions(+), 118 deletions(-)
6a65d6f1e59638185c846920d8c6f0dbd82f1bf7
diff --git a/ls-files.c b/ls-files.c
index 4a4af1c..62f9e10 100644
--- a/ls-files.c
+++ b/ls-files.c
@@ -10,6 +10,8 @@ #include <fnmatch.h>
#include "cache.h"
#include "quote.h"
+#include "gitopt.h"
+#include "gitopt/abbrev.h"
static int abbrev = 0;
static int show_deleted = 0;
@@ -648,133 +650,82 @@ static const char ls_files_usage[] =
"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
"[--] [<file>]*";
+static void tag_pfx()
+{
+ tag_cached = "H ";
+ tag_unmerged = "M ";
+ tag_removed = "R ";
+ tag_modified = "C ";
+ tag_other = "? ";
+ tag_killed = "K ";
+}
+
+static int exc_given = 0;
+
+gitopt_eat(opt_z, line_terminator = 0;)
+gitopt_eat(opt_t, tag_pfx();)
+gitopt_eat(opt_v, tag_pfx(); show_valid_bit = 1;)
+gitopt_eat(opt_c, show_cached = 1;)
+gitopt_eat(opt_d, show_deleted = 1;)
+gitopt_eat(opt_m, show_modified = 1;)
+gitopt_eat(opt_o, show_others = 1;)
+gitopt_eat(opt_i, show_ignored = 1;)
+gitopt_eat(opt_s, show_stage = 1;)
+gitopt_eat(opt_k, show_killed = 1;)
+gitopt_eat(opt_directory, show_other_directories = 1;)
+gitopt_eat(opt_no_empty_directory, hide_empty_directories = 1;)
+gitopt_eat(opt_u, show_stage = 1; show_unmerged = 1;)
+gitopt_eat_one_arg(opt_x,
+ exc_given = 1;
+ add_exclude(ea->argv[0], "", 0, &exclude_list[EXC_CMDL]);)
+gitopt_eat_one_arg(opt_X,
+ exc_given = 1;
+ add_excludes_from_file(ea->argv[0]);)
+gitopt_eat_one_arg(opt_exclude_per_dir,
+ exc_given = 1;
+ exclude_per_dir = ea->argv[0];)
+gitopt_eat(opt_full_name, prefix_offset = 0;)
+gitopt_eat(opt_error_unmatch, error_unmatch = 1;)
+gitopt_opt_abbrev(abbrev)
+
+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', "%s", ARG_ONE, opt_x },
+ { "exclude-from", 'X', "%s", ARG_ONE, opt_X },
+ { "exclude-per-directory",0, "%s", ARG_ONE, opt_exclude_per_dir },
+ { "full-name", 0, 0, 0, opt_full_name },
+ { "error-unmatch", 0, 0, 0, opt_error_unmatch },
+ abbrev_ost_row,
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
- int i;
- int exc_given = 0;
+ struct exec_args *a = new_exec_args(argc); /* argv[0] and options: */
+ struct exec_args *b = new_exec_args(argc); /* non-option args */
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")) {
- line_terminator = 0;
- continue;
- }
- if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
- 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")) {
- show_cached = 1;
- continue;
- }
- if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
- show_deleted = 1;
- continue;
- }
- if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
- show_modified = 1;
- continue;
- }
- if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
- show_others = 1;
- continue;
- }
- if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
- show_ignored = 1;
- continue;
- }
- if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
- show_stage = 1;
- continue;
- }
- if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
- show_killed = 1;
- continue;
- }
- if (!strcmp(arg, "--directory")) {
- show_other_directories = 1;
- continue;
- }
- if (!strcmp(arg, "--no-empty-directory")) {
- hide_empty_directories = 1;
- continue;
- }
- if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
- /* 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) {
- exc_given = 1;
- add_excludes_from_file(argv[++i]);
- continue;
- }
- if (!strncmp(arg, "--exclude-from=", 15)) {
- exc_given = 1;
- add_excludes_from_file(arg+15);
- continue;
- }
- if (!strncmp(arg, "--exclude-per-directory=", 24)) {
- exc_given = 1;
- exclude_per_dir = arg + 24;
- continue;
- }
- if (!strcmp(arg, "--full-name")) {
- prefix_offset = 0;
- continue;
- }
- if (!strcmp(arg, "--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 == '-')
- usage(ls_files_usage);
- break;
- }
+ if (gitopt_parse_ost_split(a, b, ls_files_ost, argc, argv) < 0)
+ usage(ls_files_usage);
+ free_exec_args(a);
- pathspec = get_pathspec(prefix, argv + i);
+ pathspec = get_pathspec(prefix,b->argv);
/* Verify that the pathspec matches the prefix */
if (pathspec)
--
1.3.2.g0a3ae
^ permalink raw reply related
* [PATCH 3/6] ls-tree: convert to gitopt
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
In-Reply-To: <11471512101532-git-send-email-normalperson@yhbt.net>
A pretty simple and straight forward conversion, imho.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
gitopt/abbrev.h | 24 +++++++++++++++++
ls-tree.c | 79 +++++++++++++++++++++++--------------------------------
2 files changed, 57 insertions(+), 46 deletions(-)
create mode 100644 gitopt/abbrev.h
06d5dc3649e7407f7ff8df0d42a55a906b39cb39
diff --git a/gitopt/abbrev.h b/gitopt/abbrev.h
new file mode 100644
index 0000000..c3b2353
--- /dev/null
+++ b/gitopt/abbrev.h
@@ -0,0 +1,24 @@
+#include "../cache.h"
+
+/* we specify rewrite_fmt here to make opt_abbrev() simpler: */
+#define abbrev_ost_row \
+ { "abbrev", 0, "a %s", ARG_OPTINT, opt_abbrev }
+
+#define gitopt_opt_abbrev(dest) \
+static struct exec_args *opt_abbrev(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ struct exec_args *ea = optional_int_arg(s, argc, argv, argc_pos); \
+ if (!ea) return NULL; \
+ if (ea->argc == 1) \
+ dest = DEFAULT_ABBREV; \
+ else { \
+ dest = strtoul(ea->argv[1], NULL, 10); \
+ if (dest && dest < MINIMUM_ABBREV) \
+ dest = MINIMUM_ABBREV; \
+ else if (dest > 40) \
+ dest = 40; \
+ } \
+ return nil_exec_args(ea); \
+}
+
diff --git a/ls-tree.c b/ls-tree.c
index f2b3bc1..4fb6343 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -7,6 +7,8 @@ #include "cache.h"
#include "blob.h"
#include "tree.h"
#include "quote.h"
+#include "gitopt.h"
+#include "gitopt/abbrev.h"
static int line_termination = '\n';
#define LS_RECURSIVE 1
@@ -84,68 +86,53 @@ static int show_tree(unsigned char *sha1
return retval;
}
+gitopt_eat(opt_d, ls_options |= LS_TREE_ONLY;)
+gitopt_eat(opt_t, ls_options |= LS_SHOW_TREES;)
+gitopt_eat(opt_z, line_termination = 0;)
+gitopt_eat(opt_r, ls_options |= LS_RECURSIVE;)
+gitopt_eat(opt_name_only_status, ls_options |= LS_NAME_ONLY;)
+gitopt_eat(opt_full_name, chomp_prefix = 0;)
+gitopt_opt_abbrev(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_ost_row,
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
unsigned char sha1[20];
struct tree *tree;
+ struct exec_args *a = new_exec_args(argc); /* argv[0] and options: */
+ struct exec_args *b = new_exec_args(argc); /* non-option args */
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':
- line_termination = 0;
- break;
- case 'r':
- ls_options |= LS_RECURSIVE;
- break;
- case 'd':
- ls_options |= LS_TREE_ONLY;
- break;
- case '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);
- 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:
- usage(ls_tree_usage);
- }
- argc--; argv++;
- }
+
+ if (gitopt_parse_ost_split(a, b, ls_tree_ost, argc, argv) < 0)
+ usage(ls_tree_usage);
+ free_exec_args(a);
+
/* -d -r should imply -t, but -d by itself should not have to. */
if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
ls_options |= LS_SHOW_TREES;
- if (argc < 2)
+ if (b->argc < 1)
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");
--
1.3.2.g0a3ae
^ permalink raw reply related
* [PATCH 1/6] gitopt: a new command-line option parser for git
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
In-Reply-To: <1147151209168-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: -uC20n20z => -u -C20 -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
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
.gitignore | 1
Makefile | 10 +
git.c | 11 +
gitopt.c | 603 ++++++++++++++++++++++++++++++++++++++++++++++++++
gitopt.h | 142 ++++++++++++
gitopt/git_wrapper.h | 45 ++++
gitopt/sh.h | 45 ++++
t/t0200-gitopt.sh | 280 +++++++++++++++++++++++
test-gitopt.c | 112 +++++++++
9 files changed, 1244 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
083777c55feab0b68b859f8cb0d5bcf105db4ef5
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 45484fc..46f2401 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) $^ -lz
+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..056e163
--- /dev/null
+++ b/gitopt.c
@@ -0,0 +1,603 @@
+#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 */
+
+void (*gitopt_non_option_cb)(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+
+void gitopt_default_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos)
+{
+ b->argv[b->argc++] = argv[*argc_pos];
+}
+
+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;
+
+ dest->argv = xrealloc(dest->argv,
+ (2 + dest->argc + from->argc)*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;
+}
+
+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 {
+ ea = new_exec_args(2);
+ ea->argv[0] = c;
+ ea->argv[1] = a;
+ }
+ }
+ break;
+ case '=': /* only long options get to use "=" to denote an arg */
+ if (!so) {
+ if (s->rewrite_fmt)
+ ea = rewrite_args(s->rewrite_fmt, a + 1);
+ else {
+ ea = new_exec_args(1);
+ ea->argv[0] = c;
+ }
+ break;
+ }
+ /* fall-through, so that -S'= assigned_val;' still works */
+ default:
+ /* -s<a> (short opts only), long opts are ambiguous
+ * w/o '=' or a space separating the opt */
+ if (!so)
+ break;
+ if (s->rewrite_fmt)
+ ea = rewrite_args(s->rewrite_fmt, a);
+ else {
+ ea = new_exec_args(1);
+ ea->argv[0] = c;
+ }
+ }
+
+ 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);
+}
+
+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;
+}
+
+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 *run_proc(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ if (s->fn)
+ return s->fn(s, argc, argv, 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);
+ default:
+ return NULL;
+ }
+}
+
+static const char * parse_bundled(struct exec_args *dest,
+ const struct opt_spec *s, const char *cur,
+ const int argc, const char **argv, int *arg_pos)
+{
+ 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 && !s->fn)) {
+ 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 || s->fn) {
+ 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 ((*arg_pos + 1) < argc) {
+ int j = *arg_pos;
+ int x = argc - j;
+ const char **argv2 = xmalloc(x * sizeof(char *));
+
+ *c = '\0';
+ argv2[i++] = tmp_argv[0];
+ while (i <= x) argv2[i++] = argv[++j];
+
+ i = 0;
+ ea = run_proc(s, x, argv2, &i);
+ *arg_pos += i;
+ free(argv2);
+ }
+ }
+ if (ea) {
+ combine_exec_args(dest, ea);
+ free_exec_args(ea);
+ } else
+ gitopt_errno = error("Failed to parse bundled arguments in: %s",
+ orig);
+ return cur;
+}
+
+static void unbundle(struct exec_args *dest, const char *cur,
+ const struct opt_spec *ost,
+ const struct opt_spec *cur_spec,
+ const int argc, const char **argv, int *arg_pos)
+{
+ struct exec_args *ea = new_exec_args(strlen(cur));
+ const struct opt_spec *s;
+ const char *c = cur;
+
+ ea->argc = 0;
+
+ 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(ea, s, c, argc, argv, arg_pos);
+ 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(ea, NULL, c, argc, argv, arg_pos);
+ else {
+ /* continue for now, a higher-up function will work
+ * with the error */
+ gitopt_errno = error("Unknown option '%s' in '%s'",
+ c,argv[*arg_pos]);
+ c++;
+ }
+ }
+ combine_exec_args(dest, ea);
+ free_exec_args(ea);
+}
+
+static void push_one_opt(struct exec_args *dest, const struct opt_spec *s,
+ const int argc, const char **argv, int *i)
+{
+ struct exec_args *ea;
+
+ if (!s->arg_fl && !s->fn) {
+ dest->argv[dest->argc++] = s->rewrite_fmt ? s->rewrite_fmt
+ : argv[*i];
+ return;
+ }
+
+ if ((ea = run_proc(s,argc,argv,i))) {
+ combine_exec_args(dest, ea);
+ free_exec_args(ea);
+ } else
+ gitopt_errno = error("Failed to parse arguments for: %s %s",
+ argv[*i], argv[*i+1]?argv[*i+1]:"");
+}
+
+/* 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 void fallback_long(const struct opt_spec *ost,
+ struct exec_args *a, struct exec_args *b,
+ const int argc, const char **argv,
+ const char *cur, int *argc_pos)
+{
+ const struct opt_spec *s;
+ int i, found = -1;
+
+ /* 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 >= 0)
+ goto pass_through;
+ found = i;
+ }
+ }
+
+ /* ok, try harder, based on tokenization on '-' */
+ if (found < 0) {
+ for (i = 0; ost[i].l || ost[i].s; i++) {
+ s = &(ost[i]);
+ if (s->l && opt_token_match(s,cur)) {
+ if (found >= 0)
+ goto pass_through;
+ found = i;
+ }
+ }
+ }
+ /* should I add a strstr() matcher here for the desperate? */
+
+ /* rewrite the abbreviated switch in it's unabbreviated form: */
+ if (found >= 0) {
+ char *tmp;
+ size_t len = 3 + strlen(cur); /* --cur=potential_args\0 */
+ size_t l_len;
+
+ s = &(ost[found]);
+ l_len = s->l ? strlen(s->l) : 0; /* favor long options */
+ tmp = xcalloc(len + l_len, 1); /* don't free this */
+ tmp[0] = '-';
+
+ if (s->l) {
+ tmp[1] = '-';
+ memcpy(tmp + 2, s->l, l_len);
+ } else
+ tmp[1] = s->s;
+ if (s->arg_fl) {
+ const char *c;
+ if ((c = strchr(cur,'='))) {
+ if (!l_len) c++; /* skip '=' for short opts */
+ strcpy(tmp + 2 + l_len, c);
+ }
+ }
+
+ argv[*argc_pos] = tmp;
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+
+pass_through:
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+ if (!gitopt_pass_through)
+ gitopt_errno = error("Unknown option: '%s'",argv[*argc_pos]);
+}
+
+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);
+}
+
+/* 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;
+}
+
+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_parse_one_opt(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv, int *argc_pos)
+{
+ const char *c = argv[*argc_pos];
+ const struct opt_spec *s;
+ int i;
+
+ if (!gitopt_dd_seen && !memcmp("--",c,3)) {
+ gitopt_dd_seen = 1;
+ if (!gitopt_pass_through)
+ return;
+ }
+ if (gitopt_dd_seen) {
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+ return;
+ }
+ 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;
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+ if (ost[i].l || ost[i].s) return;
+ /* undefined --option: */
+ fallback_long(ost, a, b, argc, argv, c, argc_pos);
+ return;
+ }
+ 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> */
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+ if (s->s != *c) continue;
+ if ((c[1] != '\0') && (c[1] != '='))
+ unbundle(a, c, ost, s, argc, argv, argc_pos);
+ else
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+ if (ost[i].l || ost[i].s) return;
+ /* undefined: */
+ if (gitopt_pass_through)
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+ else
+ gitopt_errno = error("Unknown option: %s",
+ argv[*argc_pos]);
+ return;
+ }
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+}
+
+int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv)
+{
+ int i;
+
+ if (!gitopt_non_option_cb)
+ gitopt_non_option_cb = gitopt_default_non_option_cb;
+
+ gitopt_dd_seen = 0;
+ b->argc = 0;
+ a->argv[0] = argv[0];
+ a->argc = 1;
+
+ for (i = 1; i < argc; i++)
+ gitopt_parse_one_opt(a, b, ost, argc, argv, &i);
+ return gitopt_errno;
+}
+
+void free_exec_args(struct exec_args *ea)
+{
+ free(ea->argv);
+ free(ea);
+}
+
diff --git a/gitopt.h b/gitopt.h
new file mode 100644
index 0000000..78c9e5d
--- /dev/null
+++ b/gitopt.h
@@ -0,0 +1,142 @@
+#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;
+};
+
+/* @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.
+ *
+ * @fn: define this to the function you wish to use for handling any
+ * arguments, or cases where rewrite_fmt is too limited. You can define
+ * your own functions and use it here, of course. The gitopt_eat*
+ * macros provide convenient ways to write functions for this member.
+ * Set this to 0/NULL if you have no args to parse.
+ */
+struct opt_spec {
+ const char *l;
+ const char s;
+ const char *rewrite_fmt;
+ unsigned int arg_fl;
+ struct exec_args *(*fn)(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+};
+
+/* 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 */
+void (*gitopt_non_option_cb)(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+void gitopt_default_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+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);
+
+/* Most C programs should use this internally. Returns -1 on error */
+int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv);
+
+# define gitopt_eat(func,code_block) \
+static struct exec_args *func(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ code_block \
+ return new_exec_args(0); /* return non-NULL if no errors */ \
+}
+
+# define gitopt_eat_one_arg(func,code_block) \
+static struct exec_args *func(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) return NULL; \
+ code_block \
+ return nil_exec_args(ea); /* return non-NULL if no errors */ \
+}
+#define gitopt_eat_arg(func,code_block) gitopt_eat_one_arg(func,code_block)
+
+# define gitopt_eat_int(func,code_block) \
+static struct exec_args *func(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) return NULL; \
+ code_block \
+ return nil_exec_args(ea); /* return non-NULL if no errors */ \
+}
+
+# define gitopt_eat_opt_int(func,code_block) \
+static struct exec_args *func(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ struct exec_args *ea = optional_int_arg(s, argc, argv, argc_pos); \
+ code_block \
+ return nil_exec_args(ea); /* return non-NULL if no errors */ \
+}
+
+/* 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);
+}
+
+void gitopt_parse_one_opt(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv, int *argc_pos);
+void gitopt_set_pass_through(); /* should only be used by git.c wrapper */
+
+struct exec_args *one_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+struct exec_args *int_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+struct exec_args *optional_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+struct exec_args *optional_int_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+#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..6da686c
--- /dev/null
+++ b/t/t0200-gitopt.sh
@@ -0,0 +1,280 @@
+#!/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 '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)' \
+ '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 '--name'
+02 '(null)'
+EOF
+test_expect_success 'ambiguous abbreviation (pass-through)' \
+ 'test-gitopt whatchanged --name > 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.g0a3ae
^ permalink raw reply related
* [PATCH 2/6] update-index: convert to using gitopt
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
In-Reply-To: <11471512103526-git-send-email-normalperson@yhbt.net>
This is an example how how to override gitopt_non_option_cb, as
well as disabling permuting of options to the front of the
command list. This way, commands like:
git-update-index --add new_file --remove old_file
will continue to work.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
update-index.c | 261 ++++++++++++++++++++++++++++----------------------------
1 files changed, 130 insertions(+), 131 deletions(-)
50f33d232367e8d7a46bd014f5a5da4d0ad370b3
diff --git a/update-index.c b/update-index.c
index 00cde70..6205f85 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
@@ -25,6 +26,14 @@ static int info_only;
static int force_remove;
static int verbose;
static int mark_valid_only = 0;
+static int prefix_length = 0;
+static int line_termination = '\n';
+static const char *prefix = NULL;
+static char set_executable_bit = 0;
+static int read_from_stdin = 0;
+static int newfd = 0;
+static int has_errors = 0;
+
#define MARK_VALID 1
#define UNMARK_VALID 2
@@ -189,10 +198,9 @@ static struct cache_entry *refresh_entry
return updated;
}
-static int refresh_cache(int really)
+static void refresh_cache(int really)
{
int i;
- int has_errors = 0;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce, *new;
@@ -205,7 +213,7 @@ static int refresh_cache(int really)
if (allow_unmerged)
continue;
printf("%s: needs merge\n", ce->name);
- has_errors = 1;
+ has_errors |= 1;
continue;
}
@@ -225,7 +233,7 @@ static int refresh_cache(int really)
if (quiet)
continue;
printf("%s: needs update\n", ce->name);
- has_errors = 1;
+ has_errors |= 1;
continue;
}
active_cache_changed = 1;
@@ -234,7 +242,7 @@ static int refresh_cache(int really)
* from mmap(). */
active_cache[i] = new;
}
- return has_errors;
+ return;
}
/*
@@ -600,15 +608,123 @@ static int do_unresolve(int ac, const ch
return err;
}
+static void write_cache_if_changed ()
+{
+ if (active_cache_changed) {
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_index_file(&cache_file))
+ die("Unable to write new cachefile");
+ }
+}
+
+gitopt_eat(opt_q, quiet = 1;)
+gitopt_eat(opt_add, allow_add = 1;)
+gitopt_eat(opt_replace, allow_replace = 1;)
+gitopt_eat(opt_remove, allow_remove = 1;)
+gitopt_eat(opt_unmerged, allow_unmerged = 1;)
+gitopt_eat(opt_refresh, refresh_cache(0);)
+gitopt_eat(opt_really_refresh, refresh_cache(1);)
+
+static struct exec_args *opt_cacheinfo(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ struct exec_args *ea = new_exec_args(4);
+ unsigned char sha1[20];
+ unsigned int mode;
+
+ ea->argc = 1;
+ ea->argv[0] = argv[*argc_pos];
+ while (*argc_pos < (argc-1) && ea->argc < 4)
+ ea->argv[ea->argc++] = argv[++(*argc_pos)];
+ 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]);
+
+ return nil_exec_args(ea);
+}
+
+gitopt_eat_one_arg(opt_chmod,
+ if (*argc_pos + 1 >= argc)
+ die("git-update-index: --chmod=%s <path>",ea->argv[0]);
+ set_executable_bit = ea->argv[0][0];)
+
+gitopt_eat(opt_assume_unchanged, mark_valid_only = MARK_VALID;)
+gitopt_eat(opt_no_assume_unchanged, mark_valid_only = UNMARK_VALID;)
+gitopt_eat(opt_info_only, info_only = 1;)
+gitopt_eat(opt_force_remove, force_remove = 1;)
+gitopt_eat(opt_z, line_termination = 0;)
+gitopt_eat(opt_stdin,
+ if (*argc_pos != argc - 1)
+ die("--stdin must be at the end");
+ read_from_stdin = 1;)
+
+gitopt_eat(opt_index_info,
+ if (*argc_pos != argc - 1)
+ die("--index-info must be at the end");
+ allow_add = allow_replace = allow_remove = 1;
+ read_index_info(line_termination);)
+
+gitopt_eat(opt_unresolve,
+ has_errors = do_unresolve(argc - *argc_pos, argv + *argc_pos,
+ prefix, prefix_length);
+ if (has_errors)
+ active_cache_changed = 0;
+ write_cache_if_changed();
+ exit(has_errors ? 1 : 0);)
+
+gitopt_eat(opt_ignore_missing, not_new = 1;)
+gitopt_eat(opt_verbose, verbose = 1;)
+gitopt_eat(opt_h, usage(update_index_usage);)
+
+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 },
+ { "ignore-missing", 0, 0, 0, opt_ignore_missing },
+ { "verbose", 0, 0, 0, opt_verbose },
+ { "help", 'h', 0, 0, opt_h },
+ { 0, 0 }
+};
+
+/* call this for every non-option (and everything after "--") we have */
+static void update_index_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos)
+{
+ const char *arg = argv[*argc_pos];
+ update_one(arg, prefix, prefix_length);
+ if (set_executable_bit)
+ chmod_path(set_executable_bit, arg);
+}
+
int main(int argc, const char **argv)
{
- int i, newfd, entries, has_errors = 0, line_termination = '\n';
- int allow_options = 1;
- int read_from_stdin = 0;
- const char *prefix = setup_git_directory();
- int prefix_length = prefix ? strlen(prefix) : 0;
- char set_executable_bit = 0;
+ int entries;
+ struct exec_args *a;
+
+ prefix = setup_git_directory();
+ if (prefix)
+ prefix_length = strlen(prefix);
+ a = new_exec_args(argc); /* argv[0] and options: */
+ gitopt_non_option_cb = update_index_non_option_cb;
git_config(git_default_config);
newfd = hold_index_file_for_update(&cache_file, get_index_file());
@@ -619,120 +735,9 @@ 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 (gitopt_parse_ost_split(a, a, update_index_ost, argc, argv) < 0)
+ usage(update_index_usage);
- 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, "--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);
- }
- update_one(path, prefix, prefix_length);
- if (set_executable_bit)
- chmod_path(set_executable_bit, path);
- }
if (read_from_stdin) {
struct strbuf buf;
strbuf_init(&buf);
@@ -757,12 +762,6 @@ int main(int argc, const char **argv)
}
}
- finish:
- if (active_cache_changed) {
- if (write_cache(newfd, active_cache, active_nr) ||
- commit_index_file(&cache_file))
- die("Unable to write new cachefile");
- }
-
+ write_cache_if_changed();
return has_errors ? 1 : 0;
}
--
1.3.2.g0a3ae
^ permalink raw reply related
* [PATCH/RFC] gitopt - command-line parsing enhancements
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git
Here's my take at a new command-line option parser to reduce wear on my
fingers. It handles both long and short options, permuting, automatic
abbreviations, required arguments, optional arguments, and bundling.
It doesn't use getopt or argp, so it should be easily portable to
non-GNU systems (and only uses git-isms trivially, so other programs can
use it easily, too).
I've tested everything lightly but it seems to be working well. The
unit test was very helpful throughout.
More intensive testing, bugfixes and comments would be greatly
appreciated.
It should be pretty easy to convert other git-* programs to use gitopt,
and write shell wrappers for git.c like I did with am/checkout/commit.
I'll probably do so in a few days once my fingers recover a bit, of
course feedback and patches for bug reports/enhancements are encouraged,
too.
1 - gitopt: a new command-line option parser for git
This adds the parser, tests, and some simple changes to
the git.c wrapper.
2 - update-index: convert to using gitopt
3 - ls-tree: convert to gitopt
4 - ls-files: convert to using gitopt
These three are fairly straightforward conversions.
I was somewhat disappointed that update-index didn't take
kindly to permuting arguments, but it's also a good example
how to disable permuting via gitopt: a == b
5 - gitopt: convert setup_revisions(), and diff_opt_parse()
This is a fairly intrusive change that affects several
important programs. All the tests still pass, so it must be
working :)
6 - commit: allow --pretty= args to be abbreviated
This one isn't strictly dependent on gitopt, and can be
trivially changed to work without the parent patches.
--
Eric Wong
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Junio C Hamano @ 2006-05-09 5:31 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0605081828250.3718@g5.osdl.org>
Linus Torvalds <torvalds@osdl.org> writes:
>> [branch]
>> name = linus
>> url = git://git.kernel.org/../torvalds/linux-2.6
>>
>> [branch]
>> url = git://git.kernel.org/../jgarzik/libata-dev
>> name = libata
>
> Yes, that would be a silent and confusing error.
Although I haved raised objections, I actually started to like
the idea of using multiple [branch] (or wasn't it [remote] given
the example variables we have been using?) sections.
We should first depart from the Windoze .INI mindset. While I
do not think users expect case insensisitivity, only because the
section headers are marked with [brackets], if that syntax
somehow makes people expect such, maybe we should stop using
[bracket] as section markers.
Whatever marker we end up using, I'd suggest somewhat different
approach.
- Treat each part that are grouped under [bracketted-string]
marker as a bag of variable=value pairs. Loosely speaking,
the bracketted-string defines a schema -- what kind of
variables are expected to be there in that seciton. For
example, a section for things we traditionally had in remotes
file would contain fields (variables) such as url, fetch, and
push (we might add proxy to this list). And we call this
"bag of variable=value" a section.
- There can be multiple sections in a config file that uses the
same schema. The example at the beginning of this message
is perfectly valid. It defines two sections of type
[branch], each of which has two variables (name and url) in
it.
Unlike your earlier suggestion, the second [branch] is not just
for readability; it is mandatory, because we are talking about
two different [branch]es (eh, that's [remote]s, really), it
needs to be there to separate two instances.
The above would break the existing repo-config command, but
let's forget about it for now. I think we are breaking
forward/backward compatibility in any proposals brought up so
far anyway.
We would need user interface level commands to
add a new section
delete a section
We would need a way to identify a secion, perhaps using a value
of arbitrary key (e.g. "where name=blah"). Creating a section
could be implicit, just like the current repo-config.
add a variable=value to a section
delete a variable=value from a section
retrieve variables' values from a section
list value of a variable from all sections of a kind.
Probably need to support the same variable name appearing more
than once, just like the current multi-value support.
The current multi-value stuff assumes that multi-values are
exceptions, and rare. While I do not necessarily agree with
that, for now let's assume that is true.
Creating a new section with given variables:
$ cfg --set section var value var value ...
(eg) $ cfg --set branch name master merge origin pull linus
Here, 'var' and 'value' are case sensitive; if they have
syntactical metacharacters (WS, =, quotes, etc), they need
to be quoted when cfg command writes to the file (i.e. the
user do not have to quote more than necessary to protect
them from the shell).
Updating an existing section's variables, or create a new one:
$ cfg --replace section.var value where var0 = val0
(eg) $ cfg --replace remote.url git://... where name = linus
Look for a "remote" section that has a variable "name" with
value "linus" in it, and replace its "url" variable with
"git://...". If there is no "remote" section with such a
name, create it. For the key matching syntax, I do not
insist on using "where" (I merely used it for continuity
from the previous discussion). For the comparison operator,
in addition to the '=' shown above, we would probably want
to have regexp match (perhaps ':' to emulate "expr") as well.
Retrieving a variable:
$ cfg --get section.var [where var0 = val0]
(eg) $ cfg --get remote.url where name = linus
List sections:
$ cfg --list section.var
(eg) $ cfg --list remote.name
So, an equivalent of "grep -H URL: .git/remotes/*" becomes something like:
for name in `cfg --list remote.name`
do
url=`cfg --get remote.url where name = "$name"`
echo "$name: URL: $url"
done
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Jakub Narebski @ 2006-05-09 4:28 UTC (permalink / raw)
To: git
In-Reply-To: <Pine.LNX.4.64.0605082100460.3718@g5.osdl.org>
Linus Torvalds wrote:
> I would suggest a much more readable format:
>
> [core]
> ...
>
> [branch "core"]
> ...
>
> [remote "core"]
> ...
>
> and yes, enforce the <space>+<quoted name> format. We'd turn it internally
> into some random internal string format (probably replacing the space with
> a dot, and removing the quotes, so it would become "remote.core.<key>").
I'm all for it. Nice compromise of [branch."miXedCaps"] and ["miXedCaps"],
human readable end editable, and easy parsable.
--
Jakub Narebski
Warsaw, Poland
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Pavel Roskin @ 2006-05-09 4:20 UTC (permalink / raw)
To: git
In-Reply-To: <20060508230752.43118643.seanlkml@sympatico.ca>
Hello!
I feel so bad that I sparked this discussion about config files and
couldn't participate in it in real time. I'd like to summarize my
thoughts on the subject - maybe they will help us come to an agreement.
User convenience trumps backward compatibility.
Case in-sensitivity is almost a foreign concept for POSIX. There is no
expectation (except among newbies) that bash would run grep if it's
asked to run Grep. Why would git-repo-config need to foster such
expectations, and do so inconsistently, e.g. for key names but not for
values?
The config files use escaping by backslash, which is easier to work with
than quoting. Quoting should be introduced if backslash escaping
doesn't work, and I think backslash escaping in fine.
Users who edit the config file manually and mindlessly get what they
deserve. Users who misspell "master" as "Master" get what they deserve.
Occasional typos could be caught and reported if practical, but hand
holding shouldn't be a design goal.
Either we need the third layer in key hierarchy, and that layer should
support user defined strings, or we need to relax one of the layers to
user define strings. User defined means that it can include spaces,
slashes, dots and many other characters. Whenever a character is not
allowed, we should have a good reason.
An example of two-layer approach:
[branchdescriptions]
master = My master branch
netdev-master = Patches for netdev
[branchremotes]
master = origin
netdev-master = netdev
All other examples quoted here are examples of three-layer approach.
Either the extra key is inserted into the section name (Linus) or into
the value (Dscho). It can also be inserted into the existing key.
If we want to group all branch properties for each branch, we have to go
three-layer. If we don't want that, the above example should accepted
as the simplest approach.
Adding an additional key layer to the existing keys is syntactically
nice, but buys us very little in terms of ability to group branch data:
[branchdata]
remote[master] = origin
remote[netdev-master] = master
description[master] = My master branch
description[netdev-master] = Patches for netdev
Adding an additional key layer to the value is inherently fragile. The
value has free format, and so is the new key. It also has the same
problem that the data for different branches is mixed together.
Adding an additional key layer to the section name looks strange
syntactically, but it's the approach that gives us immediate grouping of
all branch data for every branch.
My personal preference is the Linus' proposal, but with backslash
escaping instead of quoting, with explicit "branch." in the section
names, and with case sensitive sections and keys.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Linus Torvalds @ 2006-05-09 4:11 UTC (permalink / raw)
To: sean; +Cc: junkio, git
In-Reply-To: <BAYC1-PASMTP05953E2B948CB07A171FD8AEA90@CEZ.ICE>
On Mon, 8 May 2006, sean wrote:
>
> [core]
> ...
> [branch.core]
> ...
> [remote.core]
> ...
Ok. In that case, I would suggest a much more readable format:
[core]
...
[branch "core"]
...
[remote "core"]
...
and yes, enforce the <space>+<quoted name> format. We'd turn it internally
into some random internal string format (probably replacing the space with
a dot, and removing the quotes, so it would become "remote.core.<key>").
> But it's not just the config file, it's also how it ends up being used
> on the command line..
Actually, the command line migth as well allow any strange thing, and
_add_ the quotes internally. So you could do something that does
git repo-config set Remote.Core.Pull master
and we could just let that generate
[remote "Core"]
pull = master
or whatever.
I care about the _file_ being human-editable, but that means that I think
it's perfectly fine to have some smarts in the tools that help us do so,
and let them recognize the magic "remote" and "branch" prefixes.
Linus
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: sean @ 2006-05-09 3:07 UTC (permalink / raw)
To: Linus Torvalds; +Cc: junkio, git
In-Reply-To: <Pine.LNX.4.64.0605082007100.3718@g5.osdl.org>
On Mon, 8 May 2006 20:08:41 -0700 (PDT)
Linus Torvalds <torvalds@osdl.org> wrote:
> On Mon, 8 May 2006, sean wrote:
> >
> > What's the advantage of section quotation marks over just allowing these
> > characters in regular section names? To be specific, what is wrong with:
> >
> > [jc/show-branch-dense]
>
> This would _suck_
>
> What if you have a branch called "core"? Not all all unlikely.
>
> Think about what a section like
>
> [core]
>
> really means.
Yeah, but the part of my message you didn't quote made it quite clear i know
about this problem, what i would really propose is:
[core]
...
[branch.core]
...
[remote.core]
...
etc...
> Plus I really want to not be case sensitive by default. Case sensitivity
> really is _not_ normal for this kind of config file syntax.
But it's not just the config file, it's also how it ends up being used
on the command line.. you have to admit silent differences between
these two command lines is _not_ desirable:
$ git repo-config "Branch".url
$ git repo-config Branch.url
That can't be something you want to see either.
Sean
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Linus Torvalds @ 2006-05-09 3:08 UTC (permalink / raw)
To: sean; +Cc: junkio, git
In-Reply-To: <BAYC1-PASMTP04C9C4BF5B89E55B9D877AAEA90@CEZ.ICE>
On Mon, 8 May 2006, sean wrote:
>
> What's the advantage of section quotation marks over just allowing these
> characters in regular section names? To be specific, what is wrong with:
>
> [jc/show-branch-dense]
This would _suck_
What if you have a branch called "core"? Not all all unlikely.
Think about what a section like
[core]
really means.
Plus I really want to not be case sensitive by default. Case sensitivity
really is _not_ normal for this kind of config file syntax.
Linus
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: Linus Torvalds @ 2006-05-09 3:06 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Bertrand Jacquin, git
In-Reply-To: <7vwtcvc42s.fsf@assigned-by-dhcp.cox.net>
On Mon, 8 May 2006, Junio C Hamano wrote:
> "Bertrand Jacquin" <beber.mailing@gmail.com> writes:
>
> > But I would like to send an email after merge to inform people that:
> >
> > o tree ``a'' and ``b'' have been merged.
> > o made by John Doe at a time
> > o show a diffstat.
> > o show a --short-log=oneline from merge base.
>
> Forgetting about a fast-forward merge, (1) and (2) are
> available in the commit header and the commit log, so is (4) if
> you enable merge.summary configuration like Linus does in his
> kernel repository.
NOTE! Please don't enable "merge.summary" if you ever merge from the
upstream tree. That just looks ugly. Your merge messages will be just
filled with crap that has nothing to do with your tree - and everything to
do with all the _unrelated_ normal development that happened in the tree.
So in general, "merge.summary" makes sense only for trees that pull from
downstreams, and never merge with anything upstream. My tree obviously
does that for the kernel. Think of it as a "top-level maintainer" flag,
although it works find also for sub-maintainers as long as they
synchronize upwards _purely_ by being pulled from, not by pulling.
But if you want to get it for any random merges, you can always just do
git log -11 --pretty=oneline ^$commit^ $commit^@ |
sed 's/[0-9a-f]* // ; 11 s/.*/\.\.\./'
which will show up to the ten first commits that were merged (and turn the
eleventh one, if it exists, into "..." - that's a pretty disgusting trick
to make it show when you left things out).
That "^$commit^ $commit^@" part is important. It may look like some
deranged git smiley, but it does exactly what you want it to do: take all
the parents of the commit, but ignore any commit reachable from the first
one (the "mainline" of the person who did the commit).
The ^@ syntax is obviously pretty new, so it requires a modern git.
Linus
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: sean @ 2006-05-09 2:47 UTC (permalink / raw)
To: Linus Torvalds; +Cc: junkio, git
In-Reply-To: <Pine.LNX.4.64.0605081854190.3718@g5.osdl.org>
On Mon, 8 May 2006 18:57:08 -0700 (PDT)
Linus Torvalds <torvalds@osdl.org> wrote:
> Btw, I keep coming back to the same
>
> ["jc/show-branch-dense"]
> remote = git://...
>
> branch specifier syntax. It just seems very intuitive and is easy to
> parse.
We already need such section headers for remotes, and for branches..
both which may well need to be quoted as above.. how to distinguish
between them? How to handle the next case that comes along where we
want these special header semantics?
We really need:
[branch.Whatever]
and:
[remote.Whatever]
As in the case of "origin" where we have a remote and a branch
named that.
> The only real downside ends up being the non-forwards-compatibility thing.
> But trying to be forwards-compatible for old git versions with this thing
> would seem to be a major pain for rather slim gain.
What's the advantage of section quotation marks over just allowing these
characters in regular section names? To be specific, what is wrong with:
[jc/show-branch-dense]
remote = git://...
If we just relax the legal characters in identifiers to include slash and dash?
It doesn't seem to be any different in amount of code needed to achieve, and it
is just as intuitive (perhaps more so) and no worse on the forward-compatibility
thing.
If we continue with case insensitive section names, it's quite possible for
the user to mess up while hand editing or forgetting proper "git" quoting
rules on the command line and end up with silent breakage:
$ git repo-config "Branch".url = git://...
(updates section ["Branch"])
And then the next time forget the quotes and use:
$ git repo-config Branch.url = git://...
(updates section [branch])
And all of a sudden the user is updating _different_ sections with
unpredictable results. This is just awkward; why not just admit that
section names should always be case sensitive if we're going to put
filenames inside them?
Sean
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox