* [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
* Re: git-feed-mail-list.sh
From: Junio C Hamano @ 2006-05-09 2:41 UTC (permalink / raw)
To: Bertrand Jacquin; +Cc: Junio C Hamano, git
In-Reply-To: <4fb292fa0605081809r6aa76baai5eac9823183fc3fc@mail.gmail.com>
"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.
The comment on diffstat Linus already made applies to (3), but
if you want you could do "git diff --stat HEAD^..HEAD" to see
what happened to that branch by merging the other branch into
it.
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Linus Torvalds @ 2006-05-09 1:57 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v1wv4c7wk.fsf@assigned-by-dhcp.cox.net>
On Mon, 8 May 2006, Junio C Hamano wrote:
>
> Yes, but that statefulness is inviting user errors, and you need
> to update repo-config and config parser anyway, so I still do
> not see what the advantage is.
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.
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.
Linus
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Linus Torvalds @ 2006-05-09 1:30 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v1wv4c7wk.fsf@assigned-by-dhcp.cox.net>
On Mon, 8 May 2006, Junio C Hamano wrote:
>
> You dodged my comments on the SQL-like queries ;-). I was half
> (perhaps 3/4) joking, but some people actually might like to be
> able to say:
>
> git repo-config --get-all branch.name where url like 'git://%'
I think databases tend to be a huge mistake. Show me a SQL database where
users can edit the data by hand, and it's all readable, and maybe I'll
change my mind.
The monotone guys have almost everything in a database, and from what I
can tell it results in (a) you can do some really funky queries and (b)
it's confusing and slow as hell.
Now, the speed issue doesn't matter for a config file, but there the
editability very much does.
> Oops, by the way, why does a [branch] have url as its attribute?
> I think this was a bad example -- we are talking about [remote]
> here. But the main point is about syntax, so that is OK.
Yeah, "remote" is clearly better than "url".
> I am reluctant to buy that argument (I see it is an easy way
> from the implementation point of view) -- it appears to me that
> it would invite this easy user error.
>
> [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.
Linus
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: Linus Torvalds @ 2006-05-09 1:27 UTC (permalink / raw)
To: David Woodhouse; +Cc: Junio C Hamano, git
In-Reply-To: <1147136467.2694.53.camel@shinybook.infradead.org>
On Tue, 9 May 2006, David Woodhouse wrote:
>
> Ah, right. Those are _commit_ IDs in that strange first line. I'll
> reformat those to 'Commit:' and 'Parent:' for the mailing list.
Right. That first line (that starts with "commit") lists the commit ID,
and if you say "--parents", the commit ID's of the parents will be
appended.
So if you want to turn that into "Commit: <id>" and "Parent: <id>", you'll
want to do something like this:
git show --no-abbrev -C --patch-with-stat --pretty=fuller --parents $commit |
sed '1 s/commit \([0-9a-f]*\)/Commit: \1\nParent: /'
which should look pretty (count the spaces to make sure it lines up
right with the other fields).
(And if you ever want to report on merges, you'll want to change that a
bit, but it should be reasonably close to the above)
Linus
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Junio C Hamano @ 2006-05-09 1:18 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0605081801360.3718@g5.osdl.org>
Linus Torvalds <torvalds@osdl.org> writes:
> On Mon, 8 May 2006, Junio C Hamano wrote:
>>
>> Wait a minute... Statefulness is not the issue, I think.
>
> Well, it does end up being..
Not really, you ended up making it so, perhaps, but I do not
think it needs to be.
You dodged my comments on the SQL-like queries ;-). I was half
(perhaps 3/4) joking, but some people actually might like to be
able to say:
git repo-config --get-all branch.name where url like 'git://%'
to list all the repositories reachable via git-native protocol.
Oops, by the way, why does a [branch] have url as its attribute?
I think this was a bad example -- we are talking about [remote]
here. But the main point is about syntax, so that is OK.
> Exactly, git repo-config would have to know about this magic thing, and
> have a special argument like
>
> --state=branch.name
>
> that says that "state" is to be taken from the "branch.name" variable when
> seen.
>
> Then, in addition to the regexp, you would have a way to trigger on the
> "state" variable.
I am reluctant to buy that argument (I see it is an easy way
from the implementation point of view) -- it appears to me that
it would invite this easy user error.
[branch]
name = linus
url = git://git.kernel.org/../torvalds/linux-2.6
[branch]
url = git://git.kernel.org/../jgarzik/libata-dev
name = libata
> It would be _able_ to do all the same things, but thanks to statefulness
> you'd be able to keep the section (and key) names the way they are.
Yes, but that statefulness is inviting user errors, and you need
to update repo-config and config parser anyway, so I still do
not see what the advantage is.
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: Linus Torvalds @ 2006-05-09 1:18 UTC (permalink / raw)
To: Bertrand Jacquin; +Cc: David Woodhouse, Junio C Hamano, git
In-Reply-To: <4fb292fa0605081755m22e8239cjda0b1ac74b84c0d9@mail.gmail.com>
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.
(NOTE NOTE NOTE! This is _not_ the same as showing what conflicted: if you
edited the result to match one of the original branches, it will be quiet
in --cc, but if the result of a conflict was something that was in
_neither_ branch, it will be shown! So most clean merges will not show any
conflict diff at all, but the diffstat will be shown against the "first
parent").
And you probably don't want to abbreviate the parent commit SHA1's (which
are shown for merges, but not regular commits), so add "--no-abbrev".
If you want to show parents for _all_ commits, you could do something like
git show --no-abbrev --cc -C --patch-with-stat --pretty=fuller --parents |
sed '1 s/commit [0-9a-f]*/\0\nParents: / ; /^Merge: / d'
which removes a potential "Merge: " line in favour of listing the parents
on a "Parents:" line, and which also shows merges nicely.
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.
So the above is just a wild suggestion. Caveat emptor.
Linus
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: David Woodhouse @ 2006-05-09 1:12 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v64kgc8ik.fsf@assigned-by-dhcp.cox.net>
[-- Attachment #1: Type: text/plain, Size: 670 bytes --]
On Mon, 2006-05-08 at 18:05 -0700, Junio C Hamano wrote:
> That's the replacement of "git format-patch". If you have a
> chance, please try out what's in "next". Johannes did quite a
> nice enhancements.
I think I'd best wait for it to turn up in the release; preferably
already capable of MIME quoting. I don't like touching the scripts which
feed the mailing lists :)
This is what I currently have... since it changes the format I think
I'll won't deploy it yet -- I'll wait until the "replacement of
git-format-patch" is done, in case that would change the format _again_.
I'd rather not change the format I send to the list twice within a few
weeks.
--
dwmw2
[-- Attachment #2: git-feed-mail-list.sh --]
[-- Type: application/x-shellscript, Size: 2999 bytes --]
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: Bertrand Jacquin @ 2006-05-09 1:09 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vac9sc8m3.fsf@assigned-by-dhcp.cox.net>
On 5/9/06, Junio C Hamano <junkio@cox.net> wrote:
> "Bertrand Jacquin" <beber.mailing@gmail.com> writes:
>
> > Is there a way to track merge like that?
>
> The command line you quoted shows the relevant information for
> people who want to know what happened in that merge.
>
> Namely:
>
> * it always shows the header and the message
>
> * it shows the changes that are not trivial (i.e. merge parents
> have overlapping different versions and manual resolution
> resulted in something different from either parents).
>
> It is not a replacement for format-patch, but I think the commit
> mailing list is not for machines to receive and apply the
> received patches, but for humans to inspect, so it would be more
> suitable than a naive alternative of showing diff from all
> parents concatenated together.
That's right. And don't want to do that.
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.
--
Beber
#e.fr@freenode
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: Junio C Hamano @ 2006-05-09 1:05 UTC (permalink / raw)
To: David Woodhouse; +Cc: git
In-Reply-To: <1147136467.2694.53.camel@shinybook.infradead.org>
David Woodhouse <dwmw2@infradead.org> writes:
> Having 'git-show --pretty=email' would be nice. I think Junio is working
> on something which will achieve that, right?
That's the replacement of "git format-patch". If you have a
chance, please try out what's in "next". Johannes did quite a
nice enhancements.
^ permalink raw reply
* Re: Implementing branch attributes in git config
From: Linus Torvalds @ 2006-05-09 1:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7virogc90u.fsf@assigned-by-dhcp.cox.net>
On Mon, 8 May 2006, Junio C Hamano wrote:
> >
> > The problem with _that_ is that "git repo-config" can't add this kind of
> > setup sanely: it doesn't understand that kind of statefulness.
>
> Wait a minute... Statefulness is not the issue, I think.
Well, it does end up being..
> How would you tell your updated repo-config what to update and
> what to look up?
>
> - I want the url for branch whose name is "origin"
>
> - I want to fetch their "for-linus" branch when fetching
> from the branch whose name is "jgarzik" from now on.
Exactly, git repo-config would have to know about this magic thing, and
have a special argument like
--state=branch.name
that says that "state" is to be taken from the "branch.name" variable when
seen.
Then, in addition to the regexp, you would have a way to trigger on the
"state" variable.
> Now, how would that compare with:
>
> [branch.jgarzik]
> url = git://git.kernel.org/...
> fetch = for-linus
>
> or
> [branch."JGarzik"]
> url = git://git.kernel.org/...
> fetch = for-linus
It would be _able_ to do all the same things, but thanks to statefulness
you'd be able to keep the section (and key) names the way they are.
> On a related topic, I have always been torn about the "for"
> convention.
I agree. And I think it's actually very much the same thing. It adds
state, but it adds it to each _value_, instead of adding it once "before"
the values.
Linus
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: Junio C Hamano @ 2006-05-09 1:03 UTC (permalink / raw)
To: Bertrand Jacquin; +Cc: git
In-Reply-To: <4fb292fa0605081755m22e8239cjda0b1ac74b84c0d9@mail.gmail.com>
"Bertrand Jacquin" <beber.mailing@gmail.com> writes:
> Is there a way to track merge like that?
The command line you quoted shows the relevant information for
people who want to know what happened in that merge.
Namely:
* it always shows the header and the message
* it shows the changes that are not trivial (i.e. merge parents
have overlapping different versions and manual resolution
resulted in something different from either parents).
It is not a replacement for format-patch, but I think the commit
mailing list is not for machines to receive and apply the
received patches, but for humans to inspect, so it would be more
suitable than a naive alternative of showing diff from all
parents concatenated together.
^ permalink raw reply
* Re: git-feed-mail-list.sh
From: David Woodhouse @ 2006-05-09 1:01 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0605081742330.3718@g5.osdl.org>
On Mon, 2006-05-08 at 17:45 -0700, Linus Torvalds wrote:
> As long as the "commit <sha1>" id is there (and "--pretty=fuller" does
> have it), I'll be happy.
Ah, right. Those are _commit_ IDs in that strange first line. I'll
reformat those to 'Commit:' and 'Parent:' for the mailing list.
Having 'git-show --pretty=email' would be nice. I think Junio is working
on something which will achieve that, right?
> At some point, the commit mailing list didn't
> actually mention the commit ID itself, just the tree/parent IDs.
Yeah, I know -- and I got complaints :)
--
dwmw2
^ 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