diff options
-rw-r--r-- | Documentation/git-repo-config.txt | 12 | ||||
-rw-r--r-- | Makefile | 40 | ||||
-rw-r--r-- | blame.c | 4 | ||||
-rw-r--r-- | builtin-diff-files.c | 2 | ||||
-rw-r--r-- | builtin-diff-index.c | 2 | ||||
-rw-r--r-- | builtin-diff-tree.c | 42 | ||||
-rw-r--r-- | builtin-diff.c | 26 | ||||
-rw-r--r-- | builtin-grep.c | 16 | ||||
-rw-r--r-- | builtin-log.c | 4 | ||||
-rw-r--r-- | builtin-mailinfo.c (renamed from mailinfo.c) | 85 | ||||
-rw-r--r-- | builtin-mailsplit.c (renamed from mailsplit.c) | 71 | ||||
-rw-r--r-- | builtin-read-tree.c | 3 | ||||
-rw-r--r-- | builtin-rev-list.c | 64 | ||||
-rw-r--r-- | builtin-stripspace.c (renamed from stripspace.c) | 16 | ||||
-rw-r--r-- | builtin-update-index.c (renamed from update-index.c) | 19 | ||||
-rw-r--r-- | builtin-update-ref.c (renamed from update-ref.c) | 5 | ||||
-rw-r--r-- | builtin-write-tree.c (renamed from write-tree.c) | 68 | ||||
-rw-r--r-- | builtin.h | 15 | ||||
-rw-r--r-- | config.c | 43 | ||||
-rw-r--r-- | diff-delta.c | 3 | ||||
-rw-r--r-- | diff-lib.c | 45 | ||||
-rw-r--r-- | diff.c | 79 | ||||
-rw-r--r-- | diff.h | 3 | ||||
-rw-r--r-- | git.c | 8 | ||||
-rwxr-xr-x | gitweb/gitweb.cgi | 149 | ||||
-rw-r--r-- | gitweb/gitweb.css | 230 | ||||
-rw-r--r-- | http-fetch.c | 13 | ||||
-rw-r--r-- | http-push.c | 41 | ||||
-rw-r--r-- | imap-send.c | 2 | ||||
-rw-r--r-- | name-rev.c | 19 | ||||
-rw-r--r-- | object.c | 17 | ||||
-rw-r--r-- | object.h | 13 | ||||
-rw-r--r-- | pager.c | 2 | ||||
-rw-r--r-- | quote.c | 9 | ||||
-rw-r--r-- | repo-config.c | 39 | ||||
-rw-r--r-- | revision.c | 33 | ||||
-rw-r--r-- | revision.h | 12 | ||||
-rw-r--r-- | t/Makefile | 2 | ||||
-rwxr-xr-x | t/t1300-repo-config.sh | 24 |
39 files changed, 899 insertions, 381 deletions
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index d5142e0dcd..803c0d5cae 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -73,6 +73,18 @@ OPTIONS List all variables set in .git/config. +ENVIRONMENT +----------- + +GIT_CONFIG:: + Take the configuration from the given file instead of .git/config. + +GIT_CONFIG_LOCAL:: + Currently the same as $GIT_CONFIG; when Git will support global + configuration files, this will cause it to take the configuration + from the global configuration file in addition to the given file. + + EXAMPLE ------- @@ -144,34 +144,33 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ # The ones that do not have to link with lcrypto, lz nor xdiff. SIMPLE_PROGRAMS = \ - git-mailsplit$X \ - git-stripspace$X git-daemon$X + git-daemon$X # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ git-checkout-index$X git-clone-pack$X \ git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ - git-mailinfo$X git-merge-base$X \ + git-merge-base$X \ git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-receive-pack$X \ git-send-pack$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-unpack-file$X \ - git-unpack-objects$X git-update-index$X git-update-server-info$X \ - git-upload-pack$X git-verify-pack$X git-write-tree$X \ - git-update-ref$X git-symbolic-ref$X \ + git-unpack-objects$X git-update-server-info$X \ + git-upload-pack$X git-verify-pack$X \ + git-symbolic-ref$X \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X -BUILT_INS = git-log$X git-whatchanged$X git-show$X \ - git-count-objects$X git-diff$X git-push$X \ - git-grep$X git-add$X git-rm$X git-rev-list$X \ - git-check-ref-format$X git-rev-parse$X \ +BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \ + git-count-objects$X git-diff$X git-push$X git-mailsplit$X \ + git-grep$X git-add$X git-rm$X git-rev-list$X git-stripspace$X \ + git-check-ref-format$X git-rev-parse$X git-mailinfo$X \ git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \ git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \ - git-read-tree$X git-commit-tree$X \ - git-apply$X git-show-branch$X git-diff-files$X \ + git-read-tree$X git-commit-tree$X git-write-tree$X \ + git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \ git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X # what 'all' will build and 'install' will install, in gitexecdir @@ -222,12 +221,13 @@ BUILTIN_OBJS = \ builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \ builtin-rm.o builtin-init-db.o builtin-rev-parse.o \ - builtin-tar-tree.o builtin-upload-tar.o \ - builtin-ls-files.o builtin-ls-tree.o \ - builtin-read-tree.o builtin-commit-tree.o \ + builtin-tar-tree.o builtin-upload-tar.o builtin-update-index.o \ + builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \ + builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \ builtin-apply.o builtin-show-branch.o builtin-diff-files.o \ builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \ - builtin-cat-file.o + builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \ + builtin-update-ref.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz @@ -380,9 +380,7 @@ ifdef NEEDS_LIBICONV else ICONV_LINK = endif - LIB_4_ICONV = $(ICONV_LINK) -liconv -else - LIB_4_ICONV = + LIBS += $(ICONV_LINK) -liconv endif ifdef NEEDS_SOCKET LIBS += -lsocket @@ -565,10 +563,6 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIB_FILE) $(SIMPLE_LIB) -git-mailinfo$X: mailinfo.o $(LIB_FILE) - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ - $(LIB_FILE) $(SIMPLE_LIB) $(LIB_4_ICONV) - git-local-fetch$X: fetch.o git-ssh-fetch$X: rsh.o fetch.o git-ssh-upload$X: rsh.o @@ -301,9 +301,9 @@ static void fill_line_map(struct commit *commit, struct commit *other, if (DEBUG) printf("map: i1: %d %d %p i2: %d %d %p\n", i1, map[i1], - i1 != -1 ? blame_lines[map[i1]] : NULL, + (void *) (i1 != -1 ? blame_lines[map[i1]] : NULL), i2, map2[i2], - i2 != -1 ? blame_lines[map2[i2]] : NULL); + (void *) (i2 != -1 ? blame_lines[map2[i2]] : NULL)); if (map2[i2] != -1 && blame_lines[map[i1]] && !blame_lines[map2[i2]]) diff --git a/builtin-diff-files.c b/builtin-diff-files.c index cebda828ee..5afc1d7208 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -41,7 +41,7 @@ int cmd_diff_files(int argc, const char **argv, char **envp) * rev.max_count is reasonable (0 <= n <= 3), * there is no other revision filtering parameters. */ - if (rev.pending_objects || + if (rev.pending.nr || rev.min_age != -1 || rev.max_age != -1) usage(diff_files_usage); /* diff --git a/builtin-diff-index.c b/builtin-diff-index.c index 1958580d82..c42ef9a7a7 100644 --- a/builtin-diff-index.c +++ b/builtin-diff-index.c @@ -32,7 +32,7 @@ int cmd_diff_index(int argc, const char **argv, char **envp) * Make sure there is one revision (i.e. pending object), * and there is no revision filtering parameters. */ - if (!rev.pending_objects || rev.pending_objects->next || + if (rev.pending.nr != 1 || rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1) usage(diff_cache_usage); return run_diff_index(&rev, cached); diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 58cf65856d..3409a39a9f 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -65,7 +65,6 @@ int cmd_diff_tree(int argc, const char **argv, char **envp) char line[1000]; struct object *tree1, *tree2; static struct rev_info *opt = &log_tree_opt; - struct object_list *list; int read_stdin = 0; git_config(git_diff_config); @@ -86,45 +85,28 @@ int cmd_diff_tree(int argc, const char **argv, char **envp) } /* - * NOTE! "setup_revisions()" will have inserted the revisions - * it parsed in reverse order. So if you do - * - * git-diff-tree a b - * - * the commit list will be "b" -> "a" -> NULL, so we reverse - * the order of the objects if the first one is not marked - * UNINTERESTING. + * NOTE! We expect "a ^b" to be equal to "a..b", so we + * reverse the order of the objects if the second one + * is marked UNINTERESTING. */ - nr_sha1 = 0; - list = opt->pending_objects; - if (list) { - nr_sha1++; - tree1 = list->item; - list = list->next; - if (list) { - nr_sha1++; - tree2 = tree1; - tree1 = list->item; - if (list->next) - usage(diff_tree_usage); - /* Switch them around if the second one was uninteresting.. */ - if (tree2->flags & UNINTERESTING) { - struct object *tmp = tree2; - tree2 = tree1; - tree1 = tmp; - } - } - } - + nr_sha1 = opt->pending.nr; switch (nr_sha1) { case 0: if (!read_stdin) usage(diff_tree_usage); break; case 1: + tree1 = opt->pending.objects[0].item; diff_tree_commit_sha1(tree1->sha1); break; case 2: + tree1 = opt->pending.objects[0].item; + tree2 = opt->pending.objects[1].item; + if (tree2->flags & UNINTERESTING) { + struct object *tmp = tree2; + tree2 = tree1; + tree1 = tmp; + } diff_tree_sha1(tree1->sha1, tree2->sha1, "", &opt->diffopt); diff --git a/builtin-diff.c b/builtin-diff.c index 6ac3d4b8ca..99a2f76605 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -50,7 +50,7 @@ static int builtin_diff_files(struct rev_info *revs, * specified rev.max_count is reasonable (0 <= n <= 3), and * there is no other revision filtering parameter. */ - if (revs->pending_objects || + if (revs->pending.nr || revs->min_age != -1 || revs->max_age != -1 || 3 < revs->max_count) @@ -172,7 +172,7 @@ static int builtin_diff_index(struct rev_info *revs, * Make sure there is one revision (i.e. pending object), * and there is no revision filtering parameters. */ - if (!revs->pending_objects || revs->pending_objects->next || + if (revs->pending.nr != 1 || revs->max_count != -1 || revs->min_age != -1 || revs->max_age != -1) usage(builtin_diff_usage); @@ -181,10 +181,10 @@ static int builtin_diff_index(struct rev_info *revs, static int builtin_diff_tree(struct rev_info *revs, int argc, const char **argv, - struct object_list *ent) + struct object_array_entry *ent) { const unsigned char *(sha1[2]); - int swap = 1; + int swap = 0; while (1 < argc) { const char *arg = argv[1]; if (!strcmp(arg, "--raw")) @@ -195,10 +195,10 @@ static int builtin_diff_tree(struct rev_info *revs, } /* We saw two trees, ent[0] and ent[1]. - * unless ent[0] is unintesting, they are swapped + * if ent[1] is unintesting, they are swapped */ - if (ent[0].item->flags & UNINTERESTING) - swap = 0; + if (ent[1].item->flags & UNINTERESTING) + swap = 1; sha1[swap] = ent[0].item->sha1; sha1[1-swap] = ent[1].item->sha1; diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt); @@ -208,7 +208,7 @@ static int builtin_diff_tree(struct rev_info *revs, static int builtin_diff_combined(struct rev_info *revs, int argc, const char **argv, - struct object_list *ent, + struct object_array_entry *ent, int ents) { const unsigned char (*parent)[20]; @@ -242,13 +242,14 @@ void add_head(struct rev_info *revs) obj = parse_object(sha1); if (!obj) return; - add_object(obj, &revs->pending_objects, NULL, "HEAD"); + add_pending_object(revs, obj, "HEAD"); } int cmd_diff(int argc, const char **argv, char **envp) { + int i; struct rev_info rev; - struct object_list *list, ent[100]; + struct object_array_entry ent[100]; int ents = 0, blobs = 0, paths = 0; const char *path = NULL; struct blobinfo blob[2]; @@ -281,7 +282,7 @@ int cmd_diff(int argc, const char **argv, char **envp) /* Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ - if (!rev.pending_objects) { + if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -294,7 +295,8 @@ int cmd_diff(int argc, const char **argv, char **envp) } } - for (list = rev.pending_objects; list; list = list->next) { + for (i = 0; i < rev.pending.nr; i++) { + struct object_array_entry *list = rev.pending.objects+i; struct object *obj = list->item; const char *name = list->name; int flags = (obj->flags & UNINTERESTING); diff --git a/builtin-grep.c b/builtin-grep.c index 9806499263..6a240fb6e9 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -657,7 +657,7 @@ int cmd_grep(int argc, const char **argv, char **envp) int cached = 0; int seen_dashdash = 0; struct grep_opt opt; - struct object_list *list, **tail, *object_list = NULL; + struct object_array list = { 0, 0, NULL }; const char *prefix = setup_git_directory(); const char **paths = NULL; int i; @@ -677,7 +677,6 @@ int cmd_grep(int argc, const char **argv, char **envp) * that continues up to the -- (if exists), and then paths. */ - tail = &object_list; while (1 < argc) { const char *arg = argv[1]; argc--; argv++; @@ -851,12 +850,9 @@ int cmd_grep(int argc, const char **argv, char **envp) /* Is it a rev? */ if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); - struct object_list *elem; if (!object) die("bad object %s", arg); - elem = object_list_insert(object, tail); - elem->name = arg; - tail = &elem->next; + add_object_array(object, arg, &list); continue; } if (!strcmp(arg, "--")) { @@ -881,16 +877,16 @@ int cmd_grep(int argc, const char **argv, char **envp) paths[1] = NULL; } - if (!object_list) + if (!list.nr) return !grep_cache(&opt, paths, cached); if (cached) die("both --cached and trees are given."); - for (list = object_list; list; list = list->next) { + for (i = 0; i < list.nr; i++) { struct object *real_obj; - real_obj = deref_tag(list->item, NULL, 0); - if (grep_object(&opt, paths, real_obj, list->name)) + real_obj = deref_tag(list.objects[i].item, NULL, 0); + if (grep_object(&opt, paths, real_obj, list.objects[i].name)) hit = 1; } return !hit; diff --git a/builtin-log.c b/builtin-log.c index 9187fd337b..5a8a50b81d 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -257,8 +257,8 @@ int cmd_format_patch(int argc, const char **argv, char **envp) output_directory); } - if (rev.pending_objects && rev.pending_objects->next == NULL) { - rev.pending_objects->item->flags |= UNINTERESTING; + if (rev.pending.nr == 1) { + rev.pending.objects[0].item->flags |= UNINTERESTING; add_head(&rev); } diff --git a/mailinfo.c b/builtin-mailinfo.c index d9b74f30de..821642a7af 100644 --- a/mailinfo.c +++ b/builtin-mailinfo.c @@ -12,11 +12,12 @@ #endif #include "git-compat-util.h" #include "cache.h" +#include "builtin.h" -static FILE *cmitmsg, *patchfile; +static FILE *cmitmsg, *patchfile, *fin, *fout; static int keep_subject = 0; -static char *metainfo_charset = NULL; +static const char *metainfo_charset = NULL; static char line[1000]; static char date[1000]; static char name[1000]; @@ -49,7 +50,7 @@ static int bogus_from(char *line) /* This is fallback, so do not bother if we already have an * e-mail address. - */ + */ if (*email) return 0; @@ -322,13 +323,13 @@ static char *cleanup_subject(char *subject) if (remove <= len *2) { subject = p+1; continue; - } + } break; } eatspace(subject); return subject; } -} +} static void cleanup_space(char *buf) { @@ -648,7 +649,7 @@ static void handle_info(void) cleanup_space(email); cleanup_space(sub); - printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", + fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", name, email, sub, date); } @@ -685,7 +686,7 @@ static int handle_commit_msg(int *seen) continue; fputs(line, cmitmsg); - } while (fgets(line, sizeof(line), stdin) != NULL); + } while (fgets(line, sizeof(line), fin) != NULL); fclose(cmitmsg); cmitmsg = NULL; return 0; @@ -706,7 +707,7 @@ static void handle_patch(void) decode_transfer_encoding(line); fputs(line, patchfile); patch_lines++; - } while (fgets(line, sizeof(line), stdin) != NULL); + } while (fgets(line, sizeof(line), fin) != NULL); } /* multipart boundary and transfer encoding are set up for us, and we @@ -719,7 +720,7 @@ static int handle_multipart_one_part(int *seen) { int n = 0; - while (fgets(line, sizeof(line), stdin) != NULL) { + while (fgets(line, sizeof(line), fin) != NULL) { again: n++; if (is_multipart_boundary(line)) @@ -740,7 +741,7 @@ static void handle_multipart_body(void) int part_num = 0; /* Skip up to the first boundary */ - while (fgets(line, sizeof(line), stdin) != NULL) + while (fgets(line, sizeof(line), fin) != NULL) if (is_multipart_boundary(line)) { part_num = 1; break; @@ -749,7 +750,7 @@ static void handle_multipart_body(void) return; /* We are on boundary line. Start slurping the subhead. */ while (1) { - int hdr = read_one_header_line(line, sizeof(line), stdin); + int hdr = read_one_header_line(line, sizeof(line), fin); if (!hdr) { if (handle_multipart_one_part(&seen) < 0) return; @@ -781,10 +782,45 @@ static void handle_body(void) } } +int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, + const char *msg, const char *patch) +{ + keep_subject = ks; + metainfo_charset = encoding; + fin = in; + fout = out; + + cmitmsg = fopen(msg, "w"); + if (!cmitmsg) { + perror(msg); + return -1; + } + patchfile = fopen(patch, "w"); + if (!patchfile) { + perror(patch); + fclose(cmitmsg); + return -1; + } + while (1) { + int hdr = read_one_header_line(line, sizeof(line), fin); + if (!hdr) { + if (multipart_boundary[0]) + handle_multipart_body(); + else + handle_body(); + handle_info(); + break; + } + check_header_line(line); + } + + return 0; +} + static const char mailinfo_usage[] = "git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info"; -int main(int argc, char **argv) +int cmd_mailinfo(int argc, const char **argv, char **envp) { /* NEEDSWORK: might want to do the optional .git/ directory * discovery @@ -805,27 +841,6 @@ int main(int argc, char **argv) if (argc != 3) usage(mailinfo_usage); - cmitmsg = fopen(argv[1], "w"); - if (!cmitmsg) { - perror(argv[1]); - exit(1); - } - patchfile = fopen(argv[2], "w"); - if (!patchfile) { - perror(argv[2]); - exit(1); - } - while (1) { - int hdr = read_one_header_line(line, sizeof(line), stdin); - if (!hdr) { - if (multipart_boundary[0]) - handle_multipart_body(); - else - handle_body(); - handle_info(); - break; - } - check_header_line(line); - } - return 0; + + return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]); } diff --git a/mailsplit.c b/builtin-mailsplit.c index 70a569c12a..e2a0058435 100644 --- a/mailsplit.c +++ b/builtin-mailsplit.c @@ -12,6 +12,7 @@ #include <string.h> #include <stdio.h> #include "cache.h" +#include "builtin.h" static const char git_mailsplit_usage[] = "git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>..."; @@ -102,14 +103,48 @@ static int split_one(FILE *mbox, const char *name, int allow_bare) exit(1); } -int main(int argc, const char **argv) +int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip) { - int nr = 0, nr_prec = 4; + char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip)); + int ret = -1; + + while (*mbox) { + const char *file = *mbox++; + FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); + int file_done = 0; + + if ( !f ) { + error("cannot open mbox %s", file); + goto out; + } + + if (fgets(buf, sizeof(buf), f) == NULL) { + if (f == stdin) + break; /* empty stdin is OK */ + error("cannot read mbox %s", file); + goto out; + } + + while (!file_done) { + sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); + file_done = split_one(f, name, allow_bare); + } + + if (f != stdin) + fclose(f); + } + ret = skip; +out: + free(name); + return ret; +} +int cmd_mailsplit(int argc, const char **argv, char **envp) +{ + int nr = 0, nr_prec = 4, ret; int allow_bare = 0; const char *dir = NULL; const char **argp; static const char *stdin_only[] = { "-", NULL }; - char *name; for (argp = argv+1; *argp; argp++) { const char *arg = *argp; @@ -158,31 +193,9 @@ int main(int argc, const char **argv) argp = stdin_only; } - name = xmalloc(strlen(dir) + 2 + 3 * sizeof(nr)); - - while (*argp) { - const char *file = *argp++; - FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); - int file_done = 0; - - if ( !f ) - die ("cannot open mbox %s", file); - - if (fgets(buf, sizeof(buf), f) == NULL) { - if (f == stdin) - break; /* empty stdin is OK */ - die("cannot read mbox %s", file); - } - - while (!file_done) { - sprintf(name, "%s/%0*d", dir, nr_prec, ++nr); - file_done = split_one(f, name, allow_bare); - } - - if (f != stdin) - fclose(f); - } + ret = split_mbox(argp, dir, allow_bare, nr_prec, nr); + if (ret != -1) + printf("%d\n", ret); - printf("%d\n", nr); - return 0; + return ret == -1; } diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 04506da892..9a2099d730 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -31,8 +31,7 @@ static int merge_size = 0; static struct object_list *trees = NULL; -static struct cache_entry df_conflict_entry = { -}; +static struct cache_entry df_conflict_entry; struct tree_entry_list { struct tree_entry_list *next; diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 71353eb19d..63bad0e96a 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -99,26 +99,26 @@ static void show_commit(struct commit *commit) } } -static struct object_list **process_blob(struct blob *blob, - struct object_list **p, - struct name_path *path, - const char *name) +static void process_blob(struct blob *blob, + struct object_array *p, + struct name_path *path, + const char *name) { struct object *obj = &blob->object; if (!revs.blob_objects) - return p; + return; if (obj->flags & (UNINTERESTING | SEEN)) - return p; + return; obj->flags |= SEEN; name = strdup(name); - return add_object(obj, p, path, name); + add_object(obj, p, path, name); } -static struct object_list **process_tree(struct tree *tree, - struct object_list **p, - struct name_path *path, - const char *name) +static void process_tree(struct tree *tree, + struct object_array *p, + struct name_path *path, + const char *name) { struct object *obj = &tree->object; struct tree_desc desc; @@ -126,14 +126,14 @@ static struct object_list **process_tree(struct tree *tree, struct name_path me; if (!revs.tree_objects) - return p; + return; if (obj->flags & (UNINTERESTING | SEEN)) - return p; + return; if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); obj->flags |= SEEN; name = strdup(name); - p = add_object(obj, p, path, name); + add_object(obj, p, path, name); me.up = path; me.elem = name; me.elem_len = strlen(name); @@ -143,57 +143,59 @@ static struct object_list **process_tree(struct tree *tree, while (tree_entry(&desc, &entry)) { if (S_ISDIR(entry.mode)) - p = process_tree(lookup_tree(entry.sha1), p, &me, entry.path); + process_tree(lookup_tree(entry.sha1), p, &me, entry.path); else - p = process_blob(lookup_blob(entry.sha1), p, &me, entry.path); + process_blob(lookup_blob(entry.sha1), p, &me, entry.path); } free(tree->buffer); tree->buffer = NULL; - return p; } static void show_commit_list(struct rev_info *revs) { + int i; struct commit *commit; - struct object_list *objects = NULL, **p = &objects, *pending; + struct object_array objects = { 0, 0, NULL }; while ((commit = get_revision(revs)) != NULL) { - p = process_tree(commit->tree, p, NULL, ""); + process_tree(commit->tree, &objects, NULL, ""); show_commit(commit); } - for (pending = revs->pending_objects; pending; pending = pending->next) { + for (i = 0; i < revs->pending.nr; i++) { + struct object_array_entry *pending = revs->pending.objects + i; struct object *obj = pending->item; const char *name = pending->name; if (obj->flags & (UNINTERESTING | SEEN)) continue; if (obj->type == TYPE_TAG) { obj->flags |= SEEN; - p = add_object(obj, p, NULL, name); + add_object_array(obj, name, &objects); continue; } if (obj->type == TYPE_TREE) { - p = process_tree((struct tree *)obj, p, NULL, name); + process_tree((struct tree *)obj, &objects, NULL, name); continue; } if (obj->type == TYPE_BLOB) { - p = process_blob((struct blob *)obj, p, NULL, name); + process_blob((struct blob *)obj, &objects, NULL, name); continue; } die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name); } - while (objects) { + for (i = 0; i < objects.nr; i++) { + struct object_array_entry *p = objects.objects + i; + /* An object with name "foo\n0000000..." can be used to * confuse downstream git-pack-objects very badly. */ - const char *ep = strchr(objects->name, '\n'); + const char *ep = strchr(p->name, '\n'); if (ep) { - printf("%s %.*s\n", sha1_to_hex(objects->item->sha1), - (int) (ep - objects->name), - objects->name); + printf("%s %.*s\n", sha1_to_hex(p->item->sha1), + (int) (ep - p->name), + p->name); } else - printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name); - objects = objects->next; + printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name); } } @@ -348,7 +350,7 @@ int cmd_rev_list(int argc, const char **argv, char **envp) if ((!list && (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && - !revs.pending_objects)) || + !revs.pending.nr)) || revs.diff) usage(rev_list_usage); diff --git a/stripspace.c b/builtin-stripspace.c index 65a6346452..2ce1264f7b 100644 --- a/stripspace.c +++ b/builtin-stripspace.c @@ -1,6 +1,7 @@ #include <stdio.h> #include <string.h> #include <ctype.h> +#include "builtin.h" /* * Remove empty lines from the beginning and end. @@ -28,21 +29,21 @@ static int cleanup(char *line) return 1; } -int main(int argc, char **argv) +void stripspace(FILE *in, FILE *out) { int empties = -1; int incomplete = 0; char line[1024]; - while (fgets(line, sizeof(line), stdin)) { + while (fgets(line, sizeof(line), in)) { incomplete = cleanup(line); /* Not just an empty line? */ if (line[0] != '\n') { if (empties > 0) - putchar('\n'); + fputc('\n', out); empties = 0; - fputs(line, stdout); + fputs(line, out); continue; } if (empties < 0) @@ -50,6 +51,11 @@ int main(int argc, char **argv) empties++; } if (incomplete) - putchar('\n'); + fputc('\n', out); +} + +int cmd_stripspace(int argc, const char **argv, char **envp) +{ + stripspace(stdin, stdout); return 0; } diff --git a/update-index.c b/builtin-update-index.c index fbccc4a67b..ef50243452 100644 --- a/update-index.c +++ b/builtin-update-index.c @@ -8,6 +8,7 @@ #include "quote.h" #include "cache-tree.h" #include "tree-walk.h" +#include "builtin.h" /* * Default to not allowing changes to the list of files. The @@ -186,8 +187,6 @@ static void chmod_path(int flip, const char *path) die("git-update-index: cannot chmod %cx '%s'", flip, path); } -static struct lock_file lock_file; - static void update_one(const char *path, const char *prefix, int prefix_length) { const char *p = prefix_path(prefix, prefix_length, path); @@ -238,7 +237,7 @@ static void read_index_info(int line_termination) * (2) mode SP type SP sha1 TAB path * The second format is to stuff git-ls-tree output * into the index file. - * + * * (3) mode SP sha1 SP stage TAB path * This format is to put higher order stages into the * index file and matches git-ls-files --stage output. @@ -477,7 +476,7 @@ static int do_reupdate(int ac, const char **av, return 0; } -int main(int argc, const char **argv) +int cmd_update_index(int argc, const char **argv, char **envp) { int i, newfd, entries, has_errors = 0, line_termination = '\n'; int allow_options = 1; @@ -486,12 +485,16 @@ int main(int argc, const char **argv) int prefix_length = prefix ? strlen(prefix) : 0; char set_executable_bit = 0; unsigned int refresh_flags = 0; + struct lock_file *lock_file; git_config(git_default_config); - newfd = hold_lock_file_for_update(&lock_file, get_index_file()); + /* We can't free this memory, it becomes part of a linked list parsed atexit() */ + lock_file = xcalloc(1, sizeof(struct lock_file)); + + newfd = hold_lock_file_for_update(lock_file, get_index_file()); if (newfd < 0) - die("unable to create new index file"); + die("unable to create new cachefile"); entries = read_cache(); if (entries < 0) @@ -645,9 +648,11 @@ int main(int argc, const char **argv) finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || - commit_lock_file(&lock_file)) + commit_lock_file(lock_file)) die("Unable to write new index file"); } + rollback_lock_file(lock_file); + return has_errors ? 1 : 0; } diff --git a/update-ref.c b/builtin-update-ref.c index a1e6bb90fe..00333c7e7c 100644 --- a/update-ref.c +++ b/builtin-update-ref.c @@ -1,10 +1,11 @@ #include "cache.h" #include "refs.h" +#include "builtin.h" static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>] [-m <reason>]"; -int main(int argc, char **argv) +int cmd_update_ref(int argc, const char **argv, char **envp) { const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; struct ref_lock *lock; @@ -52,5 +53,7 @@ int main(int argc, char **argv) return 1; if (write_ref_sha1(lock, sha1, msg) < 0) return 1; + + /* write_ref_sha1 always unlocks the ref, no need to do it explicitly */ return 0; } diff --git a/write-tree.c b/builtin-write-tree.c index bd07da6183..70e9b6fcc6 100644 --- a/write-tree.c +++ b/builtin-write-tree.c @@ -3,41 +3,24 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#include "builtin.h" #include "cache.h" #include "tree.h" #include "cache-tree.h" -static int missing_ok = 0; -static char *prefix = NULL; - static const char write_tree_usage[] = "git-write-tree [--missing-ok] [--prefix=<prefix>/]"; -static struct lock_file lock_file; - -int main(int argc, char **argv) +int write_tree(unsigned char *sha1, int missing_ok, const char *prefix) { int entries, was_valid, newfd; - setup_git_directory(); + /* We can't free this memory, it becomes part of a linked list parsed atexit() */ + struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); - newfd = hold_lock_file_for_update(&lock_file, get_index_file()); - entries = read_cache(); - - while (1 < argc) { - char *arg = argv[1]; - if (!strcmp(arg, "--missing-ok")) - missing_ok = 1; - else if (!strncmp(arg, "--prefix=", 9)) - prefix = arg + 9; - else - die(write_tree_usage); - argc--; argv++; - } - - if (argc > 2) - die("too many options"); + newfd = hold_lock_file_for_update(lock_file, get_index_file()); + entries = read_cache(); if (entries < 0) die("git-write-tree: error reading cache"); @@ -45,6 +28,7 @@ int main(int argc, char **argv) active_cache_tree = cache_tree(); was_valid = cache_tree_fully_valid(active_cache_tree); + if (!was_valid) { if (cache_tree_update(active_cache_tree, active_cache, active_nr, @@ -52,7 +36,7 @@ int main(int argc, char **argv) die("git-write-tree: error building trees"); if (0 <= newfd) { if (!write_cache(newfd, active_cache, active_nr)) - commit_lock_file(&lock_file); + commit_lock_file(lock_file); } /* Not being able to write is fine -- we are only interested * in updating the cache-tree part, and if the next caller @@ -61,12 +45,44 @@ int main(int argc, char **argv) * performance penalty and not a big deal. */ } + if (prefix) { struct cache_tree *subtree = cache_tree_find(active_cache_tree, prefix); - printf("%s\n", sha1_to_hex(subtree->sha1)); + memcpy(sha1, subtree->sha1, 20); } else - printf("%s\n", sha1_to_hex(active_cache_tree->sha1)); + memcpy(sha1, active_cache_tree->sha1, 20); + + rollback_lock_file(lock_file); + return 0; } + +int cmd_write_tree(int argc, const char **argv, char **envp) +{ + int missing_ok = 0, ret; + const char *prefix = NULL; + unsigned char sha1[20]; + + setup_git_directory(); + + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--missing-ok")) + missing_ok = 1; + else if (!strncmp(arg, "--prefix=", 9)) + prefix = arg + 9; + else + die(write_tree_usage); + argc--; argv++; + } + + if (argc > 2) + die("too many options"); + + ret = write_tree(sha1, missing_ok, prefix); + printf("%s\n", sha1_to_hex(sha1)); + + return ret; +} @@ -1,6 +1,8 @@ #ifndef BUILTIN_H #define BUILTIN_H +#include <stdio.h> + #ifndef PATH_MAX # define PATH_MAX 4096 #endif @@ -45,5 +47,18 @@ extern int cmd_diff_stages(int argc, const char **argv, char **envp); extern int cmd_diff_tree(int argc, const char **argv, char **envp); extern int cmd_cat_file(int argc, const char **argv, char **envp); extern int cmd_rev_parse(int argc, const char **argv, char **envp); +extern int cmd_update_index(int argc, const char **argv, char **envp); +extern int cmd_update_ref(int argc, const char **argv, char **envp); + +extern int cmd_write_tree(int argc, const char **argv, char **envp); +extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); + +extern int cmd_mailsplit(int argc, const char **argv, char **envp); +extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip); + +extern int cmd_mailinfo(int argc, const char **argv, char **envp); +extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch); +extern int cmd_stripspace(int argc, const char **argv, char **envp); +extern void stripspace(FILE *in, FILE *out); #endif @@ -317,7 +317,33 @@ int git_config_from_file(config_fn_t fn, const char *filename) int git_config(config_fn_t fn) { - return git_config_from_file(fn, git_path("config")); + int ret = 0; + char *repo_config = NULL; + const char *home = NULL, *filename; + + /* $GIT_CONFIG makes git read _only_ the given config file, + * $GIT_CONFIG_LOCAL will make it process it in addition to the + * global config file, the same way it would the per-repository + * config file otherwise. */ + filename = getenv("GIT_CONFIG"); + if (!filename) { + home = getenv("HOME"); + filename = getenv("GIT_CONFIG_LOCAL"); + if (!filename) + filename = repo_config = strdup(git_path("config")); + } + + if (home) { + char *user_config = strdup(mkpath("%s/.gitconfig", home)); + if (access(user_config, R_OK) > 0) + ret = git_config_from_file(fn, user_config); + free(user_config); + } + + ret += git_config_from_file(fn, filename); + if (repo_config) + free(repo_config); + return ret; } /* @@ -490,10 +516,19 @@ int git_config_set_multivar(const char* key, const char* value, int i, dot; int fd = -1, in_fd; int ret; - char* config_filename = strdup(git_path("config")); - char* lock_file = strdup(git_path("config.lock")); + char* config_filename; + char* lock_file; const char* last_dot = strrchr(key, '.'); + config_filename = getenv("GIT_CONFIG"); + if (!config_filename) { + config_filename = getenv("GIT_CONFIG_LOCAL"); + if (!config_filename) + config_filename = git_path("config"); + } + config_filename = strdup(config_filename); + lock_file = strdup(mkpath("%s.lock", config_filename)); + /* * Since "key" actually contains the section name and the real * key name separated by a dot, we have to know where the dot is. @@ -600,7 +635,7 @@ int git_config_set_multivar(const char* key, const char* value, * As a side effect, we make sure to transform only a valid * existing config file. */ - if (git_config(store_aux)) { + if (git_config_from_file(store_aux, config_filename)) { fprintf(stderr, "invalid config file\n"); free(store.key); if (store.value_regex != NULL) { diff --git a/diff-delta.c b/diff-delta.c index 25a798d050..74486b1b81 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -22,6 +22,7 @@ #include <string.h> #include "delta.h" +#include "git-compat-util.h" /* maximum hash entry list for the same hash bucket */ #define HASH_LIMIT 64 @@ -131,7 +132,7 @@ struct delta_index { const void *src_buf; unsigned long src_size; unsigned int hash_mask; - struct index_entry *hash[0]; + struct index_entry *hash[FLEX_ARRAY]; }; struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) diff --git a/diff-lib.c b/diff-lib.c index 2183b41b03..116b5a9d68 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -34,21 +34,23 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) continue; if (ce_stage(ce)) { - struct { - struct combine_diff_path p; - struct combine_diff_parent filler[5]; - } combine; + struct combine_diff_path *dpath; int num_compare_stages = 0; + size_t path_len; - combine.p.next = NULL; - combine.p.len = ce_namelen(ce); - combine.p.path = xmalloc(combine.p.len + 1); - memcpy(combine.p.path, ce->name, combine.p.len); - combine.p.path[combine.p.len] = 0; - combine.p.mode = 0; - memset(combine.p.sha1, 0, 20); - memset(&combine.p.parent[0], 0, - sizeof(combine.filler)); + path_len = ce_namelen(ce); + + dpath = xmalloc (combine_diff_path_size (5, path_len)); + dpath->path = (char *) &(dpath->parent[5]); + + dpath->next = NULL; + dpath->len = path_len; + memcpy(dpath->path, ce->name, path_len); + dpath->path[path_len] = '\0'; + dpath->mode = 0; + memset(dpath->sha1, 0, 20); + memset(&(dpath->parent[0]), 0, + sizeof(struct combine_diff_parent)*5); while (i < entries) { struct cache_entry *nce = active_cache[i]; @@ -64,11 +66,11 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) if (2 <= stage) { int mode = ntohl(nce->ce_mode); num_compare_stages++; - memcpy(combine.p.parent[stage-2].sha1, + memcpy(dpath->parent[stage-2].sha1, nce->sha1, 20); - combine.p.parent[stage-2].mode = + dpath->parent[stage-2].mode = canon_mode(mode); - combine.p.parent[stage-2].status = + dpath->parent[stage-2].status = DIFF_STATUS_MODIFIED; } @@ -83,13 +85,14 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) i--; if (revs->combine_merges && num_compare_stages == 2) { - show_combined_diff(&combine.p, 2, + show_combined_diff(dpath, 2, revs->dense_combined_merges, revs); - free(combine.p.path); + free(dpath); continue; } - free(combine.p.path); + free(dpath); + dpath = NULL; /* * Show the diff for the 'ce' if we found the one @@ -329,8 +332,8 @@ int run_diff_index(struct rev_info *revs, int cached) } mark_merge_entries(); - ent = revs->pending_objects->item; - tree_name = revs->pending_objects->name; + ent = revs->pending.objects[0].item; + tree_name = revs->pending.objects[0].name; tree = parse_tree_indirect(ent->sha1); if (!tree) return error("bad tree object %s", tree_name); @@ -25,6 +25,20 @@ int git_diff_config(const char *var, const char *value) return git_default_config(var, value); } +enum color_diff { + DIFF_PLAIN = 0, + DIFF_METAINFO = 1, + DIFF_FILE_OLD = 2, + DIFF_FILE_NEW = 3, +}; + +static const char *diff_colors[] = { + "\033[0;0m", + "\033[1;35m", + "\033[1;31m", + "\033[1;34m", +}; + static char *quote_one(const char *str) { int needlen; @@ -177,23 +191,54 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) } struct emit_callback { + struct xdiff_emit_state xm; + int nparents, color_diff; const char **label_path; }; -static int fn_out(void *priv, mmbuffer_t *mb, int nbuf) +static inline void color_diff(int diff_use_color, enum color_diff ix) +{ + if (diff_use_color) + fputs(diff_colors[ix], stdout); +} + +static void fn_out_consume(void *priv, char *line, unsigned long len) { int i; struct emit_callback *ecbdata = priv; if (ecbdata->label_path[0]) { + color_diff(ecbdata->color_diff, DIFF_METAINFO); printf("--- %s\n", ecbdata->label_path[0]); + color_diff(ecbdata->color_diff, DIFF_METAINFO); printf("+++ %s\n", ecbdata->label_path[1]); ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } - for (i = 0; i < nbuf; i++) - if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout)) - return -1; - return 0; + + /* This is not really necessary for now because + * this codepath only deals with two-way diffs. + */ + for (i = 0; i < len && line[i] == '@'; i++) + ; + if (2 <= i && i < len && line[i] == ' ') { + ecbdata->nparents = i - 1; + color_diff(ecbdata->color_diff, DIFF_METAINFO); + } + else if (len < ecbdata->nparents) + color_diff(ecbdata->color_diff, DIFF_PLAIN); + else { + int nparents = ecbdata->nparents; + int color = DIFF_PLAIN; + for (i = 0; i < nparents && len; i++) { + if (line[i] == '-') + color = DIFF_FILE_OLD; + else if (line[i] == '+') + color = DIFF_FILE_NEW; + } + color_diff(ecbdata->color_diff, color); + } + fwrite(line, len, 1, stdout); + color_diff(ecbdata->color_diff, DIFF_PLAIN); } static char *pprint_rename(const char *a, const char *b) @@ -549,25 +594,35 @@ static void builtin_diff(const char *name_a, b_two = quote_two("b/", name_b); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; + color_diff(o->color_diff, DIFF_METAINFO); printf("diff --git %s %s\n", a_one, b_two); if (lbl[0][0] == '/') { /* /dev/null */ + color_diff(o->color_diff, DIFF_METAINFO); printf("new file mode %06o\n", two->mode); - if (xfrm_msg && xfrm_msg[0]) + if (xfrm_msg && xfrm_msg[0]) { + color_diff(o->color_diff, DIFF_METAINFO); puts(xfrm_msg); + } } else if (lbl[1][0] == '/') { printf("deleted file mode %06o\n", one->mode); - if (xfrm_msg && xfrm_msg[0]) + if (xfrm_msg && xfrm_msg[0]) { + color_diff(o->color_diff, DIFF_METAINFO); puts(xfrm_msg); + } } else { if (one->mode != two->mode) { + color_diff(o->color_diff, DIFF_METAINFO); printf("old mode %06o\n", one->mode); + color_diff(o->color_diff, DIFF_METAINFO); printf("new mode %06o\n", two->mode); } - if (xfrm_msg && xfrm_msg[0]) + if (xfrm_msg && xfrm_msg[0]) { + color_diff(o->color_diff, DIFF_METAINFO); puts(xfrm_msg); + } /* * we do not run diff between different kind * of objects. @@ -575,6 +630,7 @@ static void builtin_diff(const char *name_a, if ((one->mode ^ two->mode) & S_IFMT) goto free_ab_and_return; if (complete_rewrite) { + color_diff(o->color_diff, DIFF_PLAIN); emit_rewrite_diff(name_a, name_b, one, two); goto free_ab_and_return; } @@ -602,7 +658,9 @@ static void builtin_diff(const char *name_a, xdemitcb_t ecb; struct emit_callback ecbdata; + memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.label_path = lbl; + ecbdata.color_diff = o->color_diff; xpp.flags = XDF_NEED_MINIMAL; xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; @@ -612,8 +670,9 @@ static void builtin_diff(const char *name_a, xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); else if (!strncmp(diffopts, "-u", 2)) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); - ecb.outf = fn_out; + ecb.outf = xdiff_outf; ecb.priv = &ecbdata; + ecbdata.xm.consume = fn_out_consume; xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } @@ -1456,6 +1515,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (40 < options->abbrev) options->abbrev = 40; } + else if (!strcmp(arg, "--color")) + options->color_diff = 1; else return 0; return 1; @@ -32,7 +32,8 @@ struct diff_options { full_index:1, silent_on_remove:1, find_copies_harder:1, - summary:1; + summary:1, + color_diff:1; int context; int break_opt; int detect_rename; @@ -178,7 +178,13 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "diff-stages", cmd_diff_stages }, { "diff-tree", cmd_diff_tree }, { "cat-file", cmd_cat_file }, - { "rev-parse", cmd_rev_parse } + { "rev-parse", cmd_rev_parse }, + { "write-tree", cmd_write_tree }, + { "mailsplit", cmd_mailsplit }, + { "mailinfo", cmd_mailinfo }, + { "stripspace", cmd_stripspace }, + { "update-index", cmd_update_index }, + { "update-ref", cmd_update_ref } }; int i; diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi index 5eabe06daf..fa90c51a4d 100755 --- a/gitweb/gitweb.cgi +++ b/gitweb/gitweb.cgi @@ -38,10 +38,22 @@ my $home_link = $my_uri; # html text to include at home page my $home_text = "indextext.html"; +# URI of default stylesheet +my $stylesheet = "gitweb.css"; + # source of projects list #my $projects_list = $projectroot; my $projects_list = "index/index.aux"; +# default blob_plain mimetype and default charset for text/plain blob +my $default_blob_plain_mimetype = 'text/plain'; +my $default_text_plain_charset = undef; + +# file to use for guessing MIME types before trying /etc/mime.types +# (relative to the current git repository) +my $mimetypes_file = undef; + + # input validation and dispatch my $action = $cgi->param('a'); if (defined $action) { @@ -260,68 +272,9 @@ sub git_header_html { <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <meta name="robots" content="index, nofollow"/> +<link rel="stylesheet" href="$stylesheet"/> <title>$title</title> $rss_link -<style type="text/css"> -body { - font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; - margin:10px; background-color:#ffffff; color:#000000; -} -a { color:#0000cc; } -a:hover, a:visited, a:active { color:#880000; } -div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; } -div.page_header a:visited, a.header { color:#0000cc; } -div.page_header a:hover { color:#880000; } -div.page_nav { padding:8px; } -div.page_nav a:visited { color:#0000cc; } -div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px} -div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; } -div.page_footer_text { float:left; color:#555555; font-style:italic; } -div.page_body { padding:8px; } -div.title, a.title { - display:block; padding:6px 8px; - font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000; -} -a.title:hover { background-color: #d9d8d1; } -div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; } -div.log_body { padding:8px 8px 8px 150px; } -span.age { position:relative; float:left; width:142px; font-style:italic; } -div.log_link { - padding:0px 8px; - font-size:10px; font-family:sans-serif; font-style:normal; - position:relative; float:left; width:136px; -} -div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; } -a.list { text-decoration:none; color:#000000; } -a.list:hover { text-decoration:underline; color:#880000; } -a.text { text-decoration:none; color:#0000cc; } -a.text:visited { text-decoration:none; color:#880000; } -a.text:hover { text-decoration:underline; color:#880000; } -table { padding:8px 4px; } -th { padding:2px 5px; font-size:12px; text-align:left; } -tr.light:hover { background-color:#edece6; } -tr.dark { background-color:#f6f6f0; } -tr.dark:hover { background-color:#edece6; } -td { padding:2px 5px; font-size:12px; vertical-align:top; } -td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; } -div.pre { font-family:monospace; font-size:12px; white-space:pre; } -div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; } -div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; } -div.search { margin:4px 8px; position:absolute; top:56px; right:12px } -a.linenr { color:#999999; text-decoration:none } -a.rss_logo { - float:right; padding:3px 0px; width:35px; line-height:10px; - border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e; - color:#ffffff; background-color:#ff6600; - font-weight:bold; font-family:sans-serif; font-size:10px; - text-align:center; text-decoration:none; -} -a.rss_logo:hover { background-color:#ee5500; } -span.tag { - padding:0px 4px; font-size:10px; font-weight:normal; - background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc; -} -</style> </head> <body> EOF @@ -1538,15 +1491,85 @@ sub git_blob { git_footer_html(); } +sub mimetype_guess_file { + my $filename = shift; + my $mimemap = shift; + -r $mimemap or return undef; + + my %mimemap; + open(MIME, $mimemap) or return undef; + while (<MIME>) { + my ($mime, $exts) = split(/\t+/); + my @exts = split(/\s+/, $exts); + foreach my $ext (@exts) { + $mimemap{$ext} = $mime; + } + } + close(MIME); + + $filename =~ /\.(.*?)$/; + return $mimemap{$1}; +} + +sub mimetype_guess { + my $filename = shift; + my $mime; + $filename =~ /\./ or return undef; + + if ($mimetypes_file) { + my $file = $mimetypes_file; + $file =~ m#^/# or $file = "$projectroot/$path/$file"; + $mime = mimetype_guess_file($filename, $file); + } + $mime ||= mimetype_guess_file($filename, '/etc/mime.types'); + return $mime; +} + +sub git_blob_plain_mimetype { + my $fd = shift; + my $filename = shift; + + # just in case + return $default_blob_plain_mimetype unless $fd; + + if ($filename) { + my $mime = mimetype_guess($filename); + $mime and return $mime; + } + + if (-T $fd) { + return 'text/plain' . + ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : ''); + } elsif (! $filename) { + return 'application/octet-stream'; + } elsif ($filename =~ m/\.png$/i) { + return 'image/png'; + } elsif ($filename =~ m/\.gif$/i) { + return 'image/gif'; + } elsif ($filename =~ m/\.jpe?g$/i) { + return 'image/jpeg'; + } else { + return 'application/octet-stream'; + } +} + sub git_blob_plain { - my $save_as = "$hash.txt"; + open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return; + my $type = git_blob_plain_mimetype($fd, $file_name); + + # save as filename, even when no $file_name is given + my $save_as = "$hash"; if (defined $file_name) { $save_as = $file_name; + } elsif ($type =~ m/^text\//) { + $save_as .= '.txt'; } - print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"$save_as\""); - open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return; + + print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\""); undef $/; + binmode STDOUT, ':raw'; print <$fd>; + binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi $/ = "\n"; close $fd; } diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css new file mode 100644 index 0000000000..4378067d3c --- /dev/null +++ b/gitweb/gitweb.css @@ -0,0 +1,230 @@ +body { + font-family: sans-serif; + font-size: 12px; + border:solid #d9d8d1; + border-width: 1px; + margin: 10px; + background-color: #ffffff; + color: #000000; +} + +a { + color: #0000cc; +} + +a:hover, a:visited, a:active { + color: #880000; +} + +div.page_header { + height: 25px; + padding: 8px; + font-size: 18px; + font-weight: bold; + background-color: #d9d8d1; +} + +div.page_header a:visited, a.header { + color: #0000cc; +} + +div.page_header a:hover { + color: #880000; +} + +div.page_nav { + padding:8px; +} + +div.page_nav a:visited { + color: #0000cc; +} + +div.page_path { + padding: 8px; + border: solid #d9d8d1; + border-width: 0px 0px 1px; +} + +div.page_footer { + height: 17px; + padding: 4px 8px; + background-color: #d9d8d1; +} + +div.page_footer_text { + float: left; + color: #555555; + font-style: italic; +} + +div.page_body { + padding: 8px; +} + +div.title, a.title { + display: block; + padding: 6px 8px; + font-weight: bold; + background-color: #edece6; + text-decoration: none; + color: #000000; +} + +a.title:hover { + background-color: #d9d8d1; +} + +div.title_text { + padding: 6px 0px; + border: solid #d9d8d1; + border-width: 0px 0px 1px; +} + +div.log_body { + padding: 8px 8px 8px 150px; +} + +span.age { + position: relative; + float: left; + width: 142px; + font-style:italic; +} + +div.log_link { + padding: 0px 8px; + font-size: 10px; + font-family: sans-serif; + font-style:normal; + position: relative; + float: left; + width: 136px; +} + +div.list_head { + padding: 6px 8px 4px; + border: solid #d9d8d1; + border-width: 1px 0px 0px; + font-style: italic; +} + +a.list { + text-decoration: none; + color: #000000; +} + +a.list:hover { + text-decoration: underline; + color: #880000; +} + +a.text { + text-decoration: none; + color: #0000cc; +} + +a.text:visited { + text-decoration: none; + color: #880000; +} + +a.text:hover { + text-decoration: underline; + color: #880000; +} + +table { + padding: 8px 4px; +} + +th { + padding: 2px 5px; + font-size: 12px; + text-align: left; +} + +tr.light:hover { + background-color: #edece6; +} + +tr.dark { + background-color: #f6f6f0; +} + +tr.dark:hover { + background-color: #edece6; +} + + +td { + padding: 2px 5px; + font-size: 12px; + vertical-align:top; +} + +td.link { + padding: 2px 5px; + font-family: sans-serif; + font-size: 10px; +} + +div.pre { + font-family: monospace; + font-size: 12px; + white-space: pre; +} + +div.diff_info { + font-family: monospace; + color: #000099; + background-color: #edece6; + font-style: italic; +} + +div.index_include { + border: solid #d9d8d1; + border-width: 0px 0px 1px; + padding: 12px 8px; +} + +div.search { + margin: 4px 8px; + position: absolute; + top: 56px; + right: 12px +} + +a.linenr { + color: #999999; + text-decoration: none +} + +a.rss_logo { + float: right; + padding: 3px 0px; + width: 35px; + line-height: 10px; + border: 1px solid; + border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e; + color: #ffffff; + background-color: #ff6600; + font-weight: bold; + font-family: sans-serif; + font-size: 10px; + text-align: center; + text-decoration: none; +} + +a.rss_logo:hover { + background-color: #ee5500; +} + +span.tag { + padding: 0px 4px; + font-size: 10px; + font-weight: normal; + background-color: #ffffaa; + border: 1px solid; + border-color: #ffffcc #ffee00 #ffee00 #ffffcc; +} diff --git a/http-fetch.c b/http-fetch.c index da1a7f5416..3a2cb5e1fc 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -1136,13 +1136,14 @@ int fetch(unsigned char *sha1) static inline int needs_quote(int ch) { - switch (ch) { - case '/': case '-': case '.': - case 'A'...'Z': case 'a'...'z': case '0'...'9': + if (((ch >= 'A') && (ch <= 'Z')) + || ((ch >= 'a') && (ch <= 'z')) + || ((ch >= '0') && (ch <= '9')) + || (ch == '/') + || (ch == '-') + || (ch == '.')) return 0; - default: - return 1; - } + return 1; } static inline int hex(int v) diff --git a/http-push.c b/http-push.c index ba64f8fff5..2e7d022819 100644 --- a/http-push.c +++ b/http-push.c @@ -1077,13 +1077,14 @@ static int fetch_indices(void) static inline int needs_quote(int ch) { - switch (ch) { - case '/': case '-': case '.': - case 'A'...'Z': case 'a'...'z': case '0'...'9': + if (((ch >= 'A') && (ch <= 'Z')) + || ((ch >= 'a') && (ch <= 'z')) + || ((ch >= '0') && (ch <= '9')) + || (ch == '/') + || (ch == '-') + || (ch == '.')) return 0; - default: - return 1; - } + return 1; } static inline int hex(int v) @@ -1171,7 +1172,7 @@ static void one_remote_object(const char *hex) obj->flags |= REMOTE; if (!object_list_contains(objects, obj)) - add_object(obj, &objects, NULL, ""); + object_list_insert(obj, &objects); } static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed) @@ -1699,6 +1700,15 @@ static int locking_available(void) return lock_flags; } +struct object_list **add_one_object(struct object *obj, struct object_list **p) +{ + struct object_list *entry = xmalloc(sizeof(struct object_list)); + entry->item = obj; + entry->next = *p; + *p = entry; + return &entry->next; +} + static struct object_list **process_blob(struct blob *blob, struct object_list **p, struct name_path *path, @@ -1712,8 +1722,7 @@ static struct object_list **process_blob(struct blob *blob, return p; obj->flags |= SEEN; - name = strdup(name); - return add_object(obj, p, path, name); + return add_one_object(obj, p); } static struct object_list **process_tree(struct tree *tree, @@ -1735,7 +1744,7 @@ static struct object_list **process_tree(struct tree *tree, obj->flags |= SEEN; name = strdup(name); - p = add_object(obj, p, NULL, name); + p = add_one_object(obj, p); me.up = path; me.elem = name; me.elem_len = strlen(name); @@ -1756,8 +1765,9 @@ static struct object_list **process_tree(struct tree *tree, static int get_delta(struct rev_info *revs, struct remote_lock *lock) { + int i; struct commit *commit; - struct object_list **p = &objects, *pending; + struct object_list **p = &objects; int count = 0; while ((commit = get_revision(revs)) != NULL) { @@ -1767,15 +1777,16 @@ static int get_delta(struct rev_info *revs, struct remote_lock *lock) count += add_send_request(&commit->object, lock); } - for (pending = revs->pending_objects; pending; pending = pending->next) { - struct object *obj = pending->item; - const char *name = pending->name; + for (i = 0; i < revs->pending.nr; i++) { + struct object_array_entry *entry = revs->pending.objects + i; + struct object *obj = entry->item; + const char *name = entry->name; if (obj->flags & (UNINTERESTING | SEEN)) continue; if (obj->type == TYPE_TAG) { obj->flags |= SEEN; - p = add_object(obj, p, NULL, name); + p = add_one_object(obj, p); continue; } if (obj->type == TYPE_TREE) { diff --git a/imap-send.c b/imap-send.c index 285ad29afb..94e39cd94c 100644 --- a/imap-send.c +++ b/imap-send.c @@ -93,7 +93,7 @@ typedef struct { char *data; int len; unsigned char flags; - unsigned char crlf:1; + unsigned int crlf:1; } msg_data_t; #define DRV_OK 0 diff --git a/name-rev.c b/name-rev.c index c29b93ea71..3a5ac35d16 100644 --- a/name-rev.c +++ b/name-rev.c @@ -125,11 +125,10 @@ static const char* get_rev_name(struct object *o) return buffer; } - + int main(int argc, char **argv) { - struct object_list *revs = NULL; - struct object_list **walker = &revs; + struct object_array revs = { 0, 0, NULL }; int as_is = 0, all = 0, transform_stdin = 0; setup_git_directory(); @@ -184,9 +183,7 @@ int main(int argc, char **argv) if (cutoff > commit->date) cutoff = commit->date; - object_list_append((struct object *)commit, walker); - (*walker)->name = *argv; - walker = &((*walker)->next); + add_object_array((struct object *)commit, *argv, &revs); } for_each_ref(name_ref); @@ -243,9 +240,13 @@ int main(int argc, char **argv) if (objs[i]) printf("%s %s\n", sha1_to_hex(objs[i]->sha1), get_rev_name(objs[i])); - } else - for ( ; revs; revs = revs->next) - printf("%s %s\n", revs->name, get_rev_name(revs->item)); + } else { + int i; + for (i = 0; i < revs.nr; i++) + printf("%s %s\n", + revs.objects[i].name, + get_rev_name(revs.objects[i].item)); + } return 0; } @@ -200,3 +200,20 @@ int object_list_contains(struct object_list *list, struct object *obj) } return 0; } + +void add_object_array(struct object *obj, const char *name, struct object_array *array) +{ + unsigned nr = array->nr; + unsigned alloc = array->alloc; + struct object_array_entry *objects = array->objects; + + if (nr >= alloc) { + alloc = (alloc + 32) * 2; + objects = xrealloc(objects, alloc * sizeof(*objects)); + array->alloc = alloc; + array->objects = objects; + } + objects[nr].item = obj; + objects[nr].name = name; + array->nr = ++nr; +} @@ -4,7 +4,6 @@ struct object_list { struct object *item; struct object_list *next; - const char *name; }; struct object_refs { @@ -13,6 +12,15 @@ struct object_refs { struct object *ref[FLEX_ARRAY]; /* more */ }; +struct object_array { + unsigned int nr; + unsigned int alloc; + struct object_array_entry { + struct object *item; + const char *name; + } *objects; +}; + #define TYPE_BITS 3 #define FLAG_BITS 27 @@ -72,4 +80,7 @@ unsigned object_list_length(struct object_list *list); int object_list_contains(struct object_list *list, struct object *obj); +/* Object array handling .. */ +void add_object_array(struct object *obj, const char *name, struct object_array *array); + #endif /* OBJECT_H */ @@ -46,7 +46,7 @@ void setup_pager(void) close(fd[0]); close(fd[1]); - setenv("LESS", "-S", 0); + setenv("LESS", "-RS", 0); run_pager(pager); die("unable to execute pager '%s'", pager); exit(255); @@ -206,7 +206,14 @@ char *unquote_c_style(const char *quoted, const char **endp) case '\\': case '"': break; /* verbatim */ - case '0'...'7': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': /* octal */ ac = ((ch - '0') << 6); if ((ch = *sp++) < '0' || '7' < ch) diff --git a/repo-config.c b/repo-config.c index 08fc4cc57d..03f108fe2f 100644 --- a/repo-config.c +++ b/repo-config.c @@ -64,7 +64,22 @@ static int show_config(const char* key_, const char* value_) static int get_value(const char* key_, const char* regex_) { + int ret = -1; char *tl; + char *global = NULL, *repo_config = NULL; + const char *local; + + local = getenv("GIT_CONFIG"); + if (!local) { + const char *home = getenv("HOME"); + local = getenv("GIT_CONFIG_LOCAL"); + if (!local) + local = repo_config; + else + local = repo_config = strdup(git_path("config")); + if (home) + global = strdup(mkpath("%s/.gitconfig", home)); + } key = strdup(key_); for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl) @@ -76,7 +91,7 @@ static int get_value(const char* key_, const char* regex_) key_regexp = (regex_t*)malloc(sizeof(regex_t)); if (regcomp(key_regexp, key, REG_EXTENDED)) { fprintf(stderr, "Invalid key pattern: %s\n", key_); - return -1; + goto free_strings; } } @@ -89,11 +104,16 @@ static int get_value(const char* key_, const char* regex_) regexp = (regex_t*)malloc(sizeof(regex_t)); if (regcomp(regexp, regex_, REG_EXTENDED)) { fprintf(stderr, "Invalid pattern: %s\n", regex_); - return -1; + goto free_strings; } } - git_config(show_config); + if (do_all && global) + git_config_from_file(show_config, global); + git_config_from_file(show_config, local); + if (!do_all && !seen && global) + git_config_from_file(show_config, global); + free(key); if (regexp) { regfree(regexp); @@ -101,9 +121,16 @@ static int get_value(const char* key_, const char* regex_) } if (do_all) - return !seen; - - return (seen == 1) ? 0 : 1; + ret = !seen; + else + ret = (seen == 1) ? 0 : 1; + +free_strings: + if (repo_config) + free(repo_config); + if (global) + free(global); + return ret; } int main(int argc, const char **argv) diff --git a/revision.c b/revision.c index 7bff2a10b1..b963f2adfd 100644 --- a/revision.c +++ b/revision.c @@ -31,17 +31,12 @@ static char *path_name(struct name_path *path, const char *name) return n; } -struct object_list **add_object(struct object *obj, - struct object_list **p, - struct name_path *path, - const char *name) +void add_object(struct object *obj, + struct object_array *p, + struct name_path *path, + const char *name) { - struct object_list *entry = xmalloc(sizeof(*entry)); - entry->item = obj; - entry->next = *p; - entry->name = path_name(path, name); - *p = entry; - return &entry->next; + add_object_array(obj, path_name(path, name), p); } static void mark_blob_uninteresting(struct blob *blob) @@ -117,9 +112,9 @@ void mark_parents_uninteresting(struct commit *commit) } } -static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) +void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) { - add_object(obj, &revs->pending_objects, NULL, name); + add_object_array(obj, name, &revs->pending); } static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) @@ -836,7 +831,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch object = get_reference(revs, arg, sha1, flags ^ local_flags); add_pending_object(revs, object, arg); } - if (def && !revs->pending_objects) { + if (def && !revs->pending.nr) { unsigned char sha1[20]; struct object *object; if (get_sha1(def, sha1)) @@ -868,11 +863,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch void prepare_revision_walk(struct rev_info *revs) { - struct object_list *list; + int nr = revs->pending.nr; + struct object_array_entry *list = revs->pending.objects; - list = revs->pending_objects; - revs->pending_objects = NULL; - while (list) { + revs->pending.nr = 0; + revs->pending.alloc = 0; + revs->pending.objects = NULL; + while (--nr >= 0) { struct commit *commit = handle_commit(revs, list->item, list->name); if (commit) { if (!(commit->object.flags & SEEN)) { @@ -880,7 +877,7 @@ void prepare_revision_walk(struct rev_info *revs) insert_by_date(commit, &revs->commits); } } - list = list->next; + list++; } if (revs->no_walk) diff --git a/revision.h b/revision.h index 4020e25c33..c010a08116 100644 --- a/revision.h +++ b/revision.h @@ -18,7 +18,7 @@ typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit); struct rev_info { /* Starting list */ struct commit_list *commits; - struct object_list *pending_objects; + struct object_array pending; /* Basic information */ const char *prefix; @@ -99,9 +99,11 @@ struct name_path { const char *elem; }; -extern struct object_list **add_object(struct object *obj, - struct object_list **p, - struct name_path *path, - const char *name); +extern void add_object(struct object *obj, + struct object_array *p, + struct name_path *path, + const char *name); + +extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name); #endif diff --git a/t/Makefile b/t/Makefile index 549598575b..632c55f6d5 100644 --- a/t/Makefile +++ b/t/Makefile @@ -19,7 +19,7 @@ endif all: $(T) clean $(T): - @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS) + @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS) clean: rm -fr trash diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 8260d57b63..0de2497746 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -309,5 +309,29 @@ EOF test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect' +cat > other-config << EOF +[ein] + bahn = strasse +EOF + +cat > expect << EOF +ein.bahn=strasse +EOF + +GIT_CONFIG=other-config git-repo-config -l > output + +test_expect_success 'alternative GIT_CONFIG' 'cmp output expect' + +GIT_CONFIG=other-config git-repo-config anwohner.park ausweis + +cat > expect << EOF +[ein] + bahn = strasse +[anwohner] + park = ausweis +EOF + +test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect' + test_done |