From: Junio C Hamano Date: Tue, 20 Jun 2006 09:39:53 +0000 (-0700) Subject: Merge branch 'jn/web' into next X-Git-Tag: v1.4.1-rc1~14 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/a796b895111d5f4a76be2365dbacef2e17d7d9c9?hp=7b6511a13f9913a7a54e76b18e21c2b2bfae18cf Merge branch 'jn/web' into next --- 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 ------- diff --git a/Makefile b/Makefile index ea8cd283e2..22bb47319e 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -216,18 +215,19 @@ 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 \ - $(DIFF_OBJS) + alloc.o $(DIFF_OBJS) 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 diff --git a/alloc.c b/alloc.c new file mode 100644 index 0000000000..e3b22f4322 --- /dev/null +++ b/alloc.c @@ -0,0 +1,51 @@ +/* + * alloc.c - specialized allocator for internal objects + * + * Copyright (C) 2006 Linus Torvalds + * + * The standard malloc/free wastes too much space for objects, partly because + * it maintains all the allocation infrastructure (which isn't needed, since + * we never free an object descriptor anyway), but even more because it ends + * up with maximal alignment because it doesn't know what the object alignment + * for the new allocation is. + */ +#include "cache.h" +#include "object.h" +#include "blob.h" +#include "tree.h" +#include "commit.h" +#include "tag.h" + +#define BLOCKING 1024 + +#define DEFINE_ALLOCATOR(name) \ +static unsigned int name##_allocs; \ +struct name *alloc_##name##_node(void) \ +{ \ + static int nr; \ + static struct name *block; \ + \ + if (!nr) { \ + nr = BLOCKING; \ + block = xcalloc(BLOCKING, sizeof(struct name)); \ + } \ + nr--; \ + name##_allocs++; \ + return block++; \ +} + +DEFINE_ALLOCATOR(blob) +DEFINE_ALLOCATOR(tree) +DEFINE_ALLOCATOR(commit) +DEFINE_ALLOCATOR(tag) + +#define REPORT(name) \ + fprintf(stderr, "%10s: %8u (%zu kB)\n", #name, name##_allocs, name##_allocs*sizeof(struct name) >> 10) + +void alloc_report(void) +{ + REPORT(blob); + REPORT(tree); + REPORT(commit); + REPORT(tag); +} diff --git a/blame.c b/blame.c index 7c0b62cf3f..c86e2fd4b3 100644 --- a/blame.c +++ b/blame.c @@ -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/blob.c b/blob.c index 7377008744..496f270043 100644 --- a/blob.c +++ b/blob.c @@ -8,7 +8,7 @@ struct blob *lookup_blob(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) { - struct blob *ret = xcalloc(1, sizeof(struct blob)); + struct blob *ret = alloc_blob_node(); created_object(sha1, &ret->object); ret->object.type = TYPE_BLOB; return ret; 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/builtin-mailinfo.c b/builtin-mailinfo.c new file mode 100644 index 0000000000..821642a7af --- /dev/null +++ b/builtin-mailinfo.c @@ -0,0 +1,846 @@ +/* + * Another stupid program, this one parsing the headers of an + * email to figure out authorship and subject + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#ifndef NO_ICONV +#include +#endif +#include "git-compat-util.h" +#include "cache.h" +#include "builtin.h" + +static FILE *cmitmsg, *patchfile, *fin, *fout; + +static int keep_subject = 0; +static const char *metainfo_charset = NULL; +static char line[1000]; +static char date[1000]; +static char name[1000]; +static char email[1000]; +static char subject[1000]; + +static enum { + TE_DONTCARE, TE_QP, TE_BASE64, +} transfer_encoding; +static char charset[256]; + +static char multipart_boundary[1000]; +static int multipart_boundary_len; +static int patch_lines = 0; + +static char *sanity_check(char *name, char *email) +{ + int len = strlen(name); + if (len < 3 || len > 60) + return email; + if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>')) + return email; + return name; +} + +static int bogus_from(char *line) +{ + /* John Doe */ + char *bra, *ket, *dst, *cp; + + /* This is fallback, so do not bother if we already have an + * e-mail address. + */ + if (*email) + return 0; + + bra = strchr(line, '<'); + if (!bra) + return 0; + ket = strchr(bra, '>'); + if (!ket) + return 0; + + for (dst = email, cp = bra+1; cp < ket; ) + *dst++ = *cp++; + *dst = 0; + for (cp = line; isspace(*cp); cp++) + ; + for (bra--; isspace(*bra); bra--) + *bra = 0; + cp = sanity_check(cp, email); + strcpy(name, cp); + return 1; +} + +static int handle_from(char *in_line) +{ + char line[1000]; + char *at; + char *dst; + + strcpy(line, in_line); + at = strchr(line, '@'); + if (!at) + return bogus_from(line); + + /* + * If we already have one email, don't take any confusing lines + */ + if (*email && strchr(at+1, '@')) + return 0; + + /* Pick up the string around '@', possibly delimited with <> + * pair; that is the email part. White them out while copying. + */ + while (at > line) { + char c = at[-1]; + if (isspace(c)) + break; + if (c == '<') { + at[-1] = ' '; + break; + } + at--; + } + dst = email; + for (;;) { + unsigned char c = *at; + if (!c || c == '>' || isspace(c)) { + if (c == '>') + *at = ' '; + break; + } + *at++ = ' '; + *dst++ = c; + } + *dst++ = 0; + + /* The remainder is name. It could be "John Doe " + * or "john.doe@xz (John Doe)", but we have whited out the + * email part, so trim from both ends, possibly removing + * the () pair at the end. + */ + at = line + strlen(line); + while (at > line) { + unsigned char c = *--at; + if (!isspace(c)) { + at[(c == ')') ? 0 : 1] = 0; + break; + } + } + + at = line; + for (;;) { + unsigned char c = *at; + if (!c || !isspace(c)) { + if (c == '(') + at++; + break; + } + at++; + } + at = sanity_check(at, email); + strcpy(name, at); + return 1; +} + +static int handle_date(char *line) +{ + strcpy(date, line); + return 0; +} + +static int handle_subject(char *line) +{ + strcpy(subject, line); + return 0; +} + +/* NOTE NOTE NOTE. We do not claim we do full MIME. We just attempt + * to have enough heuristics to grok MIME encoded patches often found + * on our mailing lists. For example, we do not even treat header lines + * case insensitively. + */ + +static int slurp_attr(const char *line, const char *name, char *attr) +{ + char *ends, *ap = strcasestr(line, name); + size_t sz; + + if (!ap) { + *attr = 0; + return 0; + } + ap += strlen(name); + if (*ap == '"') { + ap++; + ends = "\""; + } + else + ends = "; \t"; + sz = strcspn(ap, ends); + memcpy(attr, ap, sz); + attr[sz] = 0; + return 1; +} + +static int handle_subcontent_type(char *line) +{ + /* We do not want to mess with boundary. Note that we do not + * handle nested multipart. + */ + if (strcasestr(line, "boundary=")) { + fprintf(stderr, "Not handling nested multipart message.\n"); + exit(1); + } + slurp_attr(line, "charset=", charset); + if (*charset) { + int i, c; + for (i = 0; (c = charset[i]) != 0; i++) + charset[i] = tolower(c); + } + return 0; +} + +static int handle_content_type(char *line) +{ + *multipart_boundary = 0; + if (slurp_attr(line, "boundary=", multipart_boundary + 2)) { + memcpy(multipart_boundary, "--", 2); + multipart_boundary_len = strlen(multipart_boundary); + } + slurp_attr(line, "charset=", charset); + return 0; +} + +static int handle_content_transfer_encoding(char *line) +{ + if (strcasestr(line, "base64")) + transfer_encoding = TE_BASE64; + else if (strcasestr(line, "quoted-printable")) + transfer_encoding = TE_QP; + else + transfer_encoding = TE_DONTCARE; + return 0; +} + +static int is_multipart_boundary(const char *line) +{ + return (!memcmp(line, multipart_boundary, multipart_boundary_len)); +} + +static int eatspace(char *line) +{ + int len = strlen(line); + while (len > 0 && isspace(line[len-1])) + line[--len] = 0; + return len; +} + +#define SEEN_FROM 01 +#define SEEN_DATE 02 +#define SEEN_SUBJECT 04 +#define SEEN_BOGUS_UNIX_FROM 010 +#define SEEN_PREFIX 020 + +/* First lines of body can have From:, Date:, and Subject: or empty */ +static void handle_inbody_header(int *seen, char *line) +{ + if (*seen & SEEN_PREFIX) + return; + if (isspace(*line)) { + char *cp; + for (cp = line + 1; *cp; cp++) { + if (!isspace(*cp)) + break; + } + if (!*cp) + return; + } + if (!memcmp(">From", line, 5) && isspace(line[5])) { + if (!(*seen & SEEN_BOGUS_UNIX_FROM)) { + *seen |= SEEN_BOGUS_UNIX_FROM; + return; + } + } + if (!memcmp("From:", line, 5) && isspace(line[5])) { + if (!(*seen & SEEN_FROM) && handle_from(line+6)) { + *seen |= SEEN_FROM; + return; + } + } + if (!memcmp("Date:", line, 5) && isspace(line[5])) { + if (!(*seen & SEEN_DATE)) { + handle_date(line+6); + *seen |= SEEN_DATE; + return; + } + } + if (!memcmp("Subject:", line, 8) && isspace(line[8])) { + if (!(*seen & SEEN_SUBJECT)) { + handle_subject(line+9); + *seen |= SEEN_SUBJECT; + return; + } + } + if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) { + if (!(*seen & SEEN_SUBJECT)) { + handle_subject(line); + *seen |= SEEN_SUBJECT; + return; + } + } + *seen |= SEEN_PREFIX; +} + +static char *cleanup_subject(char *subject) +{ + if (keep_subject) + return subject; + for (;;) { + char *p; + int len, remove; + switch (*subject) { + case 'r': case 'R': + if (!memcmp("e:", subject+1, 2)) { + subject +=3; + continue; + } + break; + case ' ': case '\t': case ':': + subject++; + continue; + + case '[': + p = strchr(subject, ']'); + if (!p) { + subject++; + continue; + } + len = strlen(p); + remove = p - subject; + if (remove <= len *2) { + subject = p+1; + continue; + } + break; + } + eatspace(subject); + return subject; + } +} + +static void cleanup_space(char *buf) +{ + unsigned char c; + while ((c = *buf) != 0) { + buf++; + if (isspace(c)) { + buf[-1] = ' '; + c = *buf; + while (isspace(c)) { + int len = strlen(buf); + memmove(buf, buf+1, len); + c = *buf; + } + } + } +} + +static void decode_header_bq(char *it); +typedef int (*header_fn_t)(char *); +struct header_def { + const char *name; + header_fn_t func; + int namelen; +}; + +static void check_header(char *line, struct header_def *header) +{ + int i; + + if (header[0].namelen <= 0) { + for (i = 0; header[i].name; i++) + header[i].namelen = strlen(header[i].name); + } + for (i = 0; header[i].name; i++) { + int len = header[i].namelen; + if (!strncasecmp(line, header[i].name, len) && + line[len] == ':' && isspace(line[len + 1])) { + /* Unwrap inline B and Q encoding, and optionally + * normalize the meta information to utf8. + */ + decode_header_bq(line + len + 2); + header[i].func(line + len + 2); + break; + } + } +} + +static void check_subheader_line(char *line) +{ + static struct header_def header[] = { + { "Content-Type", handle_subcontent_type }, + { "Content-Transfer-Encoding", + handle_content_transfer_encoding }, + { NULL }, + }; + check_header(line, header); +} +static void check_header_line(char *line) +{ + static struct header_def header[] = { + { "From", handle_from }, + { "Date", handle_date }, + { "Subject", handle_subject }, + { "Content-Type", handle_content_type }, + { "Content-Transfer-Encoding", + handle_content_transfer_encoding }, + { NULL }, + }; + check_header(line, header); +} + +static int is_rfc2822_header(char *line) +{ + /* + * The section that defines the loosest possible + * field name is "3.6.8 Optional fields". + * + * optional-field = field-name ":" unstructured CRLF + * field-name = 1*ftext + * ftext = %d33-57 / %59-126 + */ + int ch; + char *cp = line; + while ((ch = *cp++)) { + if (ch == ':') + return cp != line; + if ((33 <= ch && ch <= 57) || + (59 <= ch && ch <= 126)) + continue; + break; + } + return 0; +} + +static int read_one_header_line(char *line, int sz, FILE *in) +{ + int ofs = 0; + while (ofs < sz) { + int peek, len; + if (fgets(line + ofs, sz - ofs, in) == NULL) + break; + len = eatspace(line + ofs); + if ((len == 0) || !is_rfc2822_header(line)) { + /* Re-add the newline */ + line[ofs + len] = '\n'; + line[ofs + len + 1] = '\0'; + break; + } + ofs += len; + /* Yuck, 2822 header "folding" */ + peek = fgetc(in); ungetc(peek, in); + if (peek != ' ' && peek != '\t') + break; + } + /* Count mbox From headers as headers */ + if (!ofs && !memcmp(line, "From ", 5)) + ofs = 1; + return ofs; +} + +static unsigned hexval(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return ~0; +} + +static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047) +{ + int c; + while ((c = *in++) != 0 && (in <= ep)) { + if (c == '=') { + int d = *in++; + if (d == '\n' || !d) + break; /* drop trailing newline */ + *ot++ = ((hexval(d) << 4) | hexval(*in++)); + continue; + } + if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */ + c = 0x20; + *ot++ = c; + } + *ot = 0; + return 0; +} + +static int decode_b_segment(char *in, char *ot, char *ep) +{ + /* Decode in..ep, possibly in-place to ot */ + int c, pos = 0, acc = 0; + + while ((c = *in++) != 0 && (in <= ep)) { + if (c == '+') + c = 62; + else if (c == '/') + c = 63; + else if ('A' <= c && c <= 'Z') + c -= 'A'; + else if ('a' <= c && c <= 'z') + c -= 'a' - 26; + else if ('0' <= c && c <= '9') + c -= '0' - 52; + else if (c == '=') { + /* padding is almost like (c == 0), except we do + * not output NUL resulting only from it; + * for now we just trust the data. + */ + c = 0; + } + else + continue; /* garbage */ + switch (pos++) { + case 0: + acc = (c << 2); + break; + case 1: + *ot++ = (acc | (c >> 4)); + acc = (c & 15) << 4; + break; + case 2: + *ot++ = (acc | (c >> 2)); + acc = (c & 3) << 6; + break; + case 3: + *ot++ = (acc | c); + acc = pos = 0; + break; + } + } + *ot = 0; + return 0; +} + +static void convert_to_utf8(char *line, char *charset) +{ +#ifndef NO_ICONV + char *in, *out; + size_t insize, outsize, nrc; + char outbuf[4096]; /* cheat */ + static char latin_one[] = "latin1"; + char *input_charset = *charset ? charset : latin_one; + iconv_t conv = iconv_open(metainfo_charset, input_charset); + + if (conv == (iconv_t) -1) { + static int warned_latin1_once = 0; + if (input_charset != latin_one) { + fprintf(stderr, "cannot convert from %s to %s\n", + input_charset, metainfo_charset); + *charset = 0; + } + else if (!warned_latin1_once) { + warned_latin1_once = 1; + fprintf(stderr, "tried to convert from %s to %s, " + "but your iconv does not work with it.\n", + input_charset, metainfo_charset); + } + return; + } + in = line; + insize = strlen(in); + out = outbuf; + outsize = sizeof(outbuf); + nrc = iconv(conv, &in, &insize, &out, &outsize); + iconv_close(conv); + if (nrc == (size_t) -1) + return; + *out = 0; + strcpy(line, outbuf); +#endif +} + +static void decode_header_bq(char *it) +{ + char *in, *out, *ep, *cp, *sp; + char outbuf[1000]; + + in = it; + out = outbuf; + while ((ep = strstr(in, "=?")) != NULL) { + int sz, encoding; + char charset_q[256], piecebuf[256]; + if (in != ep) { + sz = ep - in; + memcpy(out, in, sz); + out += sz; + in += sz; + } + /* E.g. + * ep : "=?iso-2022-jp?B?GyR...?= foo" + * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz" + */ + ep += 2; + cp = strchr(ep, '?'); + if (!cp) + return; /* no munging */ + for (sp = ep; sp < cp; sp++) + charset_q[sp - ep] = tolower(*sp); + charset_q[cp - ep] = 0; + encoding = cp[1]; + if (!encoding || cp[2] != '?') + return; /* no munging */ + ep = strstr(cp + 3, "?="); + if (!ep) + return; /* no munging */ + switch (tolower(encoding)) { + default: + return; /* no munging */ + case 'b': + sz = decode_b_segment(cp + 3, piecebuf, ep); + break; + case 'q': + sz = decode_q_segment(cp + 3, piecebuf, ep, 1); + break; + } + if (sz < 0) + return; + if (metainfo_charset) + convert_to_utf8(piecebuf, charset_q); + strcpy(out, piecebuf); + out += strlen(out); + in = ep + 2; + } + strcpy(out, in); + strcpy(it, outbuf); +} + +static void decode_transfer_encoding(char *line) +{ + char *ep; + + switch (transfer_encoding) { + case TE_QP: + ep = line + strlen(line); + decode_q_segment(line, line, ep, 0); + break; + case TE_BASE64: + ep = line + strlen(line); + decode_b_segment(line, line, ep); + break; + case TE_DONTCARE: + break; + } +} + +static void handle_info(void) +{ + char *sub; + + sub = cleanup_subject(subject); + cleanup_space(name); + cleanup_space(date); + cleanup_space(email); + cleanup_space(sub); + + fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", + name, email, sub, date); +} + +/* We are inside message body and have read line[] already. + * Spit out the commit log. + */ +static int handle_commit_msg(int *seen) +{ + if (!cmitmsg) + return 0; + do { + if (!memcmp("diff -", line, 6) || + !memcmp("---", line, 3) || + !memcmp("Index: ", line, 7)) + break; + if ((multipart_boundary[0] && is_multipart_boundary(line))) { + /* We come here when the first part had only + * the commit message without any patch. We + * pretend we have not seen this line yet, and + * go back to the loop. + */ + return 1; + } + + /* Unwrap transfer encoding and optionally + * normalize the log message to UTF-8. + */ + decode_transfer_encoding(line); + if (metainfo_charset) + convert_to_utf8(line, charset); + + handle_inbody_header(seen, line); + if (!(*seen & SEEN_PREFIX)) + continue; + + fputs(line, cmitmsg); + } while (fgets(line, sizeof(line), fin) != NULL); + fclose(cmitmsg); + cmitmsg = NULL; + return 0; +} + +/* We have done the commit message and have the first + * line of the patch in line[]. + */ +static void handle_patch(void) +{ + do { + if (multipart_boundary[0] && is_multipart_boundary(line)) + break; + /* Only unwrap transfer encoding but otherwise do not + * do anything. We do *NOT* want UTF-8 conversion + * here; we are dealing with the user payload. + */ + decode_transfer_encoding(line); + fputs(line, patchfile); + patch_lines++; + } while (fgets(line, sizeof(line), fin) != NULL); +} + +/* multipart boundary and transfer encoding are set up for us, and we + * are at the end of the sub header. do equivalent of handle_body up + * to the next boundary without closing patchfile --- we will expect + * that the first part to contain commit message and a patch, and + * handle other parts as pure patches. + */ +static int handle_multipart_one_part(int *seen) +{ + int n = 0; + + while (fgets(line, sizeof(line), fin) != NULL) { + again: + n++; + if (is_multipart_boundary(line)) + break; + if (handle_commit_msg(seen)) + goto again; + handle_patch(); + break; + } + if (n == 0) + return -1; + return 0; +} + +static void handle_multipart_body(void) +{ + int seen = 0; + int part_num = 0; + + /* Skip up to the first boundary */ + while (fgets(line, sizeof(line), fin) != NULL) + if (is_multipart_boundary(line)) { + part_num = 1; + break; + } + if (!part_num) + return; + /* We are on boundary line. Start slurping the subhead. */ + while (1) { + int hdr = read_one_header_line(line, sizeof(line), fin); + if (!hdr) { + if (handle_multipart_one_part(&seen) < 0) + return; + /* Reset per part headers */ + transfer_encoding = TE_DONTCARE; + charset[0] = 0; + } + else + check_subheader_line(line); + } + fclose(patchfile); + if (!patch_lines) { + fprintf(stderr, "No patch found\n"); + exit(1); + } +} + +/* Non multipart message */ +static void handle_body(void) +{ + int seen = 0; + + handle_commit_msg(&seen); + handle_patch(); + fclose(patchfile); + if (!patch_lines) { + fprintf(stderr, "No patch found\n"); + exit(1); + } +} + +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=] msg patch info"; + +int cmd_mailinfo(int argc, const char **argv, char **envp) +{ + /* NEEDSWORK: might want to do the optional .git/ directory + * discovery + */ + git_config(git_default_config); + + while (1 < argc && argv[1][0] == '-') { + if (!strcmp(argv[1], "-k")) + keep_subject = 1; + else if (!strcmp(argv[1], "-u")) + metainfo_charset = git_commit_encoding; + else if (!strncmp(argv[1], "--encoding=", 11)) + metainfo_charset = argv[1] + 11; + else + usage(mailinfo_usage); + argc--; argv++; + } + + if (argc != 3) + usage(mailinfo_usage); + + return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]); +} diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c new file mode 100644 index 0000000000..e2a0058435 --- /dev/null +++ b/builtin-mailsplit.c @@ -0,0 +1,201 @@ +/* + * Totally braindamaged mbox splitter program. + * + * It just splits a mbox into a list of files: "0001" "0002" .. + * so you can process them further from there. + */ +#include +#include +#include +#include +#include +#include +#include +#include "cache.h" +#include "builtin.h" + +static const char git_mailsplit_usage[] = +"git-mailsplit [-d] [-f] [-b] -o ..."; + +static int is_from_line(const char *line, int len) +{ + const char *colon; + + if (len < 20 || memcmp("From ", line, 5)) + return 0; + + colon = line + len - 2; + line += 5; + for (;;) { + if (colon < line) + return 0; + if (*--colon == ':') + break; + } + + if (!isdigit(colon[-4]) || + !isdigit(colon[-2]) || + !isdigit(colon[-1]) || + !isdigit(colon[ 1]) || + !isdigit(colon[ 2])) + return 0; + + /* year */ + if (strtol(colon+3, NULL, 10) <= 90) + return 0; + + /* Ok, close enough */ + return 1; +} + +/* Could be as small as 64, enough to hold a Unix "From " line. */ +static char buf[4096]; + +/* Called with the first line (potentially partial) + * already in buf[] -- normally that should begin with + * the Unix "From " line. Write it into the specified + * file. + */ +static int split_one(FILE *mbox, const char *name, int allow_bare) +{ + FILE *output = NULL; + int len = strlen(buf); + int fd; + int status = 0; + int is_bare = !is_from_line(buf, len); + + if (is_bare && !allow_bare) + goto corrupt; + + fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (fd < 0) + die("cannot open output file %s", name); + output = fdopen(fd, "w"); + + /* Copy it out, while searching for a line that begins with + * "From " and having something that looks like a date format. + */ + for (;;) { + int is_partial = (buf[len-1] != '\n'); + + if (fputs(buf, output) == EOF) + die("cannot write output"); + + if (fgets(buf, sizeof(buf), mbox) == NULL) { + if (feof(mbox)) { + status = 1; + break; + } + die("cannot read mbox"); + } + len = strlen(buf); + if (!is_partial && !is_bare && is_from_line(buf, len)) + break; /* done with one message */ + } + fclose(output); + return status; + + corrupt: + if (output) + fclose(output); + unlink(name); + fprintf(stderr, "corrupt mailbox\n"); + exit(1); +} + +int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip) +{ + 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 }; + + for (argp = argv+1; *argp; argp++) { + const char *arg = *argp; + + if (arg[0] != '-') + break; + /* do flags here */ + if ( arg[1] == 'd' ) { + nr_prec = strtol(arg+2, NULL, 10); + if (nr_prec < 3 || 10 <= nr_prec) + usage(git_mailsplit_usage); + continue; + } else if ( arg[1] == 'f' ) { + nr = strtol(arg+2, NULL, 10); + } else if ( arg[1] == 'b' && !arg[2] ) { + allow_bare = 1; + } else if ( arg[1] == 'o' && arg[2] ) { + dir = arg+2; + } else if ( arg[1] == '-' && !arg[2] ) { + argp++; /* -- marks end of options */ + break; + } else { + die("unknown option: %s", arg); + } + } + + if ( !dir ) { + /* Backwards compatibility: if no -o specified, accept + or just */ + switch (argc - (argp-argv)) { + case 1: + dir = argp[0]; + argp = stdin_only; + break; + case 2: + stdin_only[0] = argp[0]; + dir = argp[1]; + argp = stdin_only; + break; + default: + usage(git_mailsplit_usage); + } + } else { + /* New usage: if no more argument, parse stdin */ + if ( !*argp ) + argp = stdin_only; + } + + ret = split_mbox(argp, dir, allow_bare, nr_prec, nr); + if (ret != -1) + printf("%d\n", ret); + + 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/builtin-stripspace.c b/builtin-stripspace.c new file mode 100644 index 0000000000..2ce1264f7b --- /dev/null +++ b/builtin-stripspace.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include "builtin.h" + +/* + * Remove empty lines from the beginning and end. + * + * Turn multiple consecutive empty lines into just one + * empty line. Return true if it is an incomplete line. + */ +static int cleanup(char *line) +{ + int len = strlen(line); + + if (len && line[len-1] == '\n') { + if (len == 1) + return 0; + do { + unsigned char c = line[len-2]; + if (!isspace(c)) + break; + line[len-2] = '\n'; + len--; + line[len] = 0; + } while (len > 1); + return 0; + } + return 1; +} + +void stripspace(FILE *in, FILE *out) +{ + int empties = -1; + int incomplete = 0; + char line[1024]; + + while (fgets(line, sizeof(line), in)) { + incomplete = cleanup(line); + + /* Not just an empty line? */ + if (line[0] != '\n') { + if (empties > 0) + fputc('\n', out); + empties = 0; + fputs(line, out); + continue; + } + if (empties < 0) + continue; + empties++; + } + if (incomplete) + fputc('\n', out); +} + +int cmd_stripspace(int argc, const char **argv, char **envp) +{ + stripspace(stdin, stdout); + return 0; +} diff --git a/builtin-update-index.c b/builtin-update-index.c new file mode 100644 index 0000000000..ef50243452 --- /dev/null +++ b/builtin-update-index.c @@ -0,0 +1,658 @@ +/* + * GIT - The information manager from hell + * + * Copyright (C) Linus Torvalds, 2005 + */ +#include "cache.h" +#include "strbuf.h" +#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 + * tool doesn't actually care, but this makes it harder to add + * files to the revision control by mistake by doing something + * like "git-update-index *" and suddenly having all the object + * files be revision controlled. + */ +static int allow_add; +static int allow_remove; +static int allow_replace; +static int info_only; +static int force_remove; +static int verbose; +static int mark_valid_only = 0; +#define MARK_VALID 1 +#define UNMARK_VALID 2 + +static void report(const char *fmt, ...) +{ + va_list vp; + + if (!verbose) + return; + + va_start(vp, fmt); + vprintf(fmt, vp); + putchar('\n'); + va_end(vp); +} + +static int mark_valid(const char *path) +{ + int namelen = strlen(path); + int pos = cache_name_pos(path, namelen); + if (0 <= pos) { + switch (mark_valid_only) { + case MARK_VALID: + active_cache[pos]->ce_flags |= htons(CE_VALID); + break; + case UNMARK_VALID: + active_cache[pos]->ce_flags &= ~htons(CE_VALID); + break; + } + cache_tree_invalidate_path(active_cache_tree, path); + active_cache_changed = 1; + return 0; + } + return -1; +} + +static int add_file_to_cache(const char *path) +{ + int size, namelen, option, status; + struct cache_entry *ce; + struct stat st; + + status = lstat(path, &st); + + /* We probably want to do this in remove_file_from_cache() and + * add_cache_entry() instead... + */ + cache_tree_invalidate_path(active_cache_tree, path); + + if (status < 0 || S_ISDIR(st.st_mode)) { + /* When we used to have "path" and now we want to add + * "path/file", we need a way to remove "path" before + * being able to add "path/file". However, + * "git-update-index --remove path" would not work. + * --force-remove can be used but this is more user + * friendly, especially since we can do the opposite + * case just fine without --force-remove. + */ + if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) { + if (allow_remove) { + if (remove_file_from_cache(path)) + return error("%s: cannot remove from the index", + path); + else + return 0; + } else if (status < 0) { + return error("%s: does not exist and --remove not passed", + path); + } + } + if (0 == status) + return error("%s: is a directory - add files inside instead", + path); + else + return error("lstat(\"%s\"): %s", path, + strerror(errno)); + } + + namelen = strlen(path); + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + memcpy(ce->name, path, namelen); + ce->ce_flags = htons(namelen); + fill_stat_cache_info(ce, &st); + + ce->ce_mode = create_ce_mode(st.st_mode); + if (!trust_executable_bit) { + /* If there is an existing entry, pick the mode bits + * from it. + */ + int pos = cache_name_pos(path, namelen); + if (0 <= pos) + ce->ce_mode = active_cache[pos]->ce_mode; + } + + if (index_path(ce->sha1, path, &st, !info_only)) + return -1; + option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; + option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; + if (add_cache_entry(ce, option)) + return error("%s: cannot add to the index - missing --add option?", + path); + return 0; +} + +static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, + const char *path, int stage) +{ + int size, len, option; + struct cache_entry *ce; + + if (!verify_path(path)) + return -1; + + len = strlen(path); + size = cache_entry_size(len); + ce = xcalloc(1, size); + + memcpy(ce->sha1, sha1, 20); + memcpy(ce->name, path, len); + ce->ce_flags = create_ce_flags(len, stage); + ce->ce_mode = create_ce_mode(mode); + if (assume_unchanged) + ce->ce_flags |= htons(CE_VALID); + option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; + option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; + if (add_cache_entry(ce, option)) + return error("%s: cannot add to the index - missing --add option?", + path); + report("add '%s'", path); + cache_tree_invalidate_path(active_cache_tree, path); + return 0; +} + +static void chmod_path(int flip, const char *path) +{ + int pos; + struct cache_entry *ce; + unsigned int mode; + + pos = cache_name_pos(path, strlen(path)); + if (pos < 0) + goto fail; + ce = active_cache[pos]; + mode = ntohl(ce->ce_mode); + if (!S_ISREG(mode)) + goto fail; + switch (flip) { + case '+': + ce->ce_mode |= htonl(0111); break; + case '-': + ce->ce_mode &= htonl(~0111); break; + default: + goto fail; + } + cache_tree_invalidate_path(active_cache_tree, path); + active_cache_changed = 1; + report("chmod %cx '%s'", flip, path); + return; + fail: + die("git-update-index: cannot chmod %cx '%s'", flip, path); +} + +static void update_one(const char *path, const char *prefix, int prefix_length) +{ + const char *p = prefix_path(prefix, prefix_length, path); + if (!verify_path(p)) { + fprintf(stderr, "Ignoring path %s\n", path); + goto free_return; + } + if (mark_valid_only) { + if (mark_valid(p)) + die("Unable to mark file %s", path); + goto free_return; + } + cache_tree_invalidate_path(active_cache_tree, path); + + if (force_remove) { + if (remove_file_from_cache(p)) + die("git-update-index: unable to remove %s", path); + report("remove '%s'", path); + goto free_return; + } + if (add_file_to_cache(p)) + die("Unable to process file %s", path); + report("add '%s'", path); + free_return: + if (p < path || p > path + strlen(path)) + free((char*)p); +} + +static void read_index_info(int line_termination) +{ + struct strbuf buf; + strbuf_init(&buf); + while (1) { + char *ptr, *tab; + char *path_name; + unsigned char sha1[20]; + unsigned int mode; + int stage; + + /* This reads lines formatted in one of three formats: + * + * (1) mode SP sha1 TAB path + * The first format is what "git-apply --index-info" + * reports, and used to reconstruct a partial tree + * that is used for phony merge base tree when falling + * back on 3-way merge. + * + * (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. + */ + read_line(&buf, stdin, line_termination); + if (buf.eof) + break; + + mode = strtoul(buf.buf, &ptr, 8); + if (ptr == buf.buf || *ptr != ' ') + goto bad_line; + + tab = strchr(ptr, '\t'); + if (!tab || tab - ptr < 41) + goto bad_line; + + if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') { + stage = tab[-1] - '0'; + ptr = tab + 1; /* point at the head of path */ + tab = tab - 2; /* point at tail of sha1 */ + } + else { + stage = 0; + ptr = tab + 1; /* point at the head of path */ + } + + if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ') + goto bad_line; + + if (line_termination && ptr[0] == '"') + path_name = unquote_c_style(ptr, NULL); + else + path_name = ptr; + + if (!verify_path(path_name)) { + fprintf(stderr, "Ignoring path %s\n", path_name); + if (path_name != ptr) + free(path_name); + continue; + } + cache_tree_invalidate_path(active_cache_tree, path_name); + + if (!mode) { + /* mode == 0 means there is no such path -- remove */ + if (remove_file_from_cache(path_name)) + die("git-update-index: unable to remove %s", + ptr); + } + else { + /* mode ' ' sha1 '\t' name + * ptr[-1] points at tab, + * ptr[-41] is at the beginning of sha1 + */ + ptr[-42] = ptr[-1] = 0; + if (add_cacheinfo(mode, sha1, path_name, stage)) + die("git-update-index: unable to update %s", + path_name); + } + if (path_name != ptr) + free(path_name); + continue; + + bad_line: + die("malformed index info %s", buf.buf); + } +} + +static const char update_index_usage[] = +"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again] [--ignore-missing] [-z] [--verbose] [--] ..."; + +static unsigned char head_sha1[20]; +static unsigned char merge_head_sha1[20]; + +static struct cache_entry *read_one_ent(const char *which, + unsigned char *ent, const char *path, + int namelen, int stage) +{ + unsigned mode; + unsigned char sha1[20]; + int size; + struct cache_entry *ce; + + if (get_tree_entry(ent, path, sha1, &mode)) { + if (which) + error("%s: not in %s branch.", path, which); + return NULL; + } + if (mode == S_IFDIR) { + if (which) + error("%s: not a blob in %s branch.", path, which); + return NULL; + } + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + + memcpy(ce->sha1, sha1, 20); + memcpy(ce->name, path, namelen); + ce->ce_flags = create_ce_flags(namelen, stage); + ce->ce_mode = create_ce_mode(mode); + return ce; +} + +static int unresolve_one(const char *path) +{ + int namelen = strlen(path); + int pos; + int ret = 0; + struct cache_entry *ce_2 = NULL, *ce_3 = NULL; + + /* See if there is such entry in the index. */ + pos = cache_name_pos(path, namelen); + if (pos < 0) { + /* If there isn't, either it is unmerged, or + * resolved as "removed" by mistake. We do not + * want to do anything in the former case. + */ + pos = -pos-1; + if (pos < active_nr) { + struct cache_entry *ce = active_cache[pos]; + if (ce_namelen(ce) == namelen && + !memcmp(ce->name, path, namelen)) { + fprintf(stderr, + "%s: skipping still unmerged path.\n", + path); + goto free_return; + } + } + } + + /* Grab blobs from given path from HEAD and MERGE_HEAD, + * stuff HEAD version in stage #2, + * stuff MERGE_HEAD version in stage #3. + */ + ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); + ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); + + if (!ce_2 || !ce_3) { + ret = -1; + goto free_return; + } + if (!memcmp(ce_2->sha1, ce_3->sha1, 20) && + ce_2->ce_mode == ce_3->ce_mode) { + fprintf(stderr, "%s: identical in both, skipping.\n", + path); + goto free_return; + } + + cache_tree_invalidate_path(active_cache_tree, path); + remove_file_from_cache(path); + if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { + error("%s: cannot add our version to the index.", path); + ret = -1; + goto free_return; + } + if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) + return 0; + error("%s: cannot add their version to the index.", path); + ret = -1; + free_return: + free(ce_2); + free(ce_3); + return ret; +} + +static void read_head_pointers(void) +{ + if (read_ref(git_path("HEAD"), head_sha1)) + die("No HEAD -- no initial commit yet?\n"); + if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { + fprintf(stderr, "Not in the middle of a merge.\n"); + exit(0); + } +} + +static int do_unresolve(int ac, const char **av, + const char *prefix, int prefix_length) +{ + int i; + int err = 0; + + /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we + * are not doing a merge, so exit with success status. + */ + read_head_pointers(); + + for (i = 1; i < ac; i++) { + const char *arg = av[i]; + const char *p = prefix_path(prefix, prefix_length, arg); + err |= unresolve_one(p); + if (p < arg || p > arg + strlen(arg)) + free((char*)p); + } + return err; +} + +static int do_reupdate(int ac, const char **av, + const char *prefix, int prefix_length) +{ + /* Read HEAD and run update-index on paths that are + * merged and already different between index and HEAD. + */ + int pos; + int has_head = 1; + const char **pathspec = get_pathspec(prefix, av + 1); + + if (read_ref(git_path("HEAD"), head_sha1)) + /* If there is no HEAD, that means it is an initial + * commit. Update everything in the index. + */ + has_head = 0; + redo: + for (pos = 0; pos < active_nr; pos++) { + struct cache_entry *ce = active_cache[pos]; + struct cache_entry *old = NULL; + int save_nr; + + if (ce_stage(ce) || !ce_path_match(ce, pathspec)) + continue; + if (has_head) + old = read_one_ent(NULL, head_sha1, + ce->name, ce_namelen(ce), 0); + if (old && ce->ce_mode == old->ce_mode && + !memcmp(ce->sha1, old->sha1, 20)) { + free(old); + continue; /* unchanged */ + } + /* Be careful. The working tree may not have the + * path anymore, in which case, under 'allow_remove', + * or worse yet 'allow_replace', active_nr may decrease. + */ + save_nr = active_nr; + update_one(ce->name + prefix_length, prefix, prefix_length); + if (save_nr != active_nr) + goto redo; + } + return 0; +} + +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; + int read_from_stdin = 0; + const char *prefix = setup_git_directory(); + 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); + + /* 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 cachefile"); + + entries = read_cache(); + if (entries < 0) + die("cache corrupted"); + + for (i = 1 ; i < argc; i++) { + const char *path = argv[i]; + + if (allow_options && *path == '-') { + if (!strcmp(path, "--")) { + allow_options = 0; + continue; + } + if (!strcmp(path, "-q")) { + refresh_flags |= REFRESH_QUIET; + 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")) { + refresh_flags |= REFRESH_UNMERGED; + continue; + } + if (!strcmp(path, "--refresh")) { + has_errors |= refresh_cache(refresh_flags); + continue; + } + if (!strcmp(path, "--really-refresh")) { + has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags); + continue; + } + if (!strcmp(path, "--cacheinfo")) { + unsigned char sha1[20]; + unsigned int mode; + + if (i+3 >= argc) + die("git-update-index: --cacheinfo "); + + 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); + set_executable_bit = path[8]; + continue; + } + if (!strcmp(path, "--assume-unchanged")) { + mark_valid_only = MARK_VALID; + continue; + } + if (!strcmp(path, "--no-assume-unchanged")) { + mark_valid_only = UNMARK_VALID; + continue; + } + if (!strcmp(path, "--info-only")) { + info_only = 1; + continue; + } + if (!strcmp(path, "--force-remove")) { + force_remove = 1; + continue; + } + if (!strcmp(path, "-z")) { + line_termination = 0; + continue; + } + if (!strcmp(path, "--stdin")) { + if (i != argc - 1) + die("--stdin must be at the end"); + read_from_stdin = 1; + break; + } + if (!strcmp(path, "--index-info")) { + if (i != argc - 1) + die("--index-info must be at the end"); + allow_add = allow_replace = allow_remove = 1; + read_index_info(line_termination); + break; + } + if (!strcmp(path, "--unresolve")) { + has_errors = do_unresolve(argc - i, argv + i, + prefix, prefix_length); + if (has_errors) + active_cache_changed = 0; + goto finish; + } + if (!strcmp(path, "--again")) { + has_errors = do_reupdate(argc - i, argv + i, + prefix, prefix_length); + if (has_errors) + active_cache_changed = 0; + goto finish; + } + if (!strcmp(path, "--ignore-missing")) { + refresh_flags |= REFRESH_IGNORE_MISSING; + 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); + while (1) { + char *path_name; + const char *p; + read_line(&buf, stdin, line_termination); + if (buf.eof) + break; + if (line_termination && buf.buf[0] == '"') + path_name = unquote_c_style(buf.buf, NULL); + else + path_name = buf.buf; + p = prefix_path(prefix, prefix_length, path_name); + update_one(p, NULL, 0); + if (set_executable_bit) + chmod_path(set_executable_bit, p); + if (p < path_name || p > path_name + strlen(path_name)) + free((char*) p); + if (path_name != buf.buf) + free(path_name); + } + } + + finish: + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + 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/builtin-update-ref.c b/builtin-update-ref.c new file mode 100644 index 0000000000..00333c7e7c --- /dev/null +++ b/builtin-update-ref.c @@ -0,0 +1,59 @@ +#include "cache.h" +#include "refs.h" +#include "builtin.h" + +static const char git_update_ref_usage[] = +"git-update-ref [] [-m ]"; + +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; + unsigned char sha1[20], oldsha1[20]; + int i; + + setup_git_directory(); + git_config(git_default_config); + + for (i = 1; i < argc; i++) { + if (!strcmp("-m", argv[i])) { + if (i+1 >= argc) + usage(git_update_ref_usage); + msg = argv[++i]; + if (!*msg) + die("Refusing to perform update with empty message."); + if (strchr(msg, '\n')) + die("Refusing to perform update with \\n in message."); + continue; + } + if (!refname) { + refname = argv[i]; + continue; + } + if (!value) { + value = argv[i]; + continue; + } + if (!oldval) { + oldval = argv[i]; + continue; + } + } + if (!refname || !value) + usage(git_update_ref_usage); + + if (get_sha1(value, sha1)) + die("%s: not a valid SHA1", value); + memset(oldsha1, 0, 20); + if (oldval && get_sha1(oldval, oldsha1)) + die("%s: not a valid old SHA1", oldval); + + lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0); + if (!lock) + 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/builtin-write-tree.c b/builtin-write-tree.c new file mode 100644 index 0000000000..70e9b6fcc6 --- /dev/null +++ b/builtin-write-tree.c @@ -0,0 +1,88 @@ +/* + * GIT - The information manager from hell + * + * Copyright (C) Linus Torvalds, 2005 + */ +#include "builtin.h" +#include "cache.h" +#include "tree.h" +#include "cache-tree.h" + +static const char write_tree_usage[] = +"git-write-tree [--missing-ok] [--prefix=/]"; + +int write_tree(unsigned char *sha1, int missing_ok, const char *prefix) +{ + int entries, was_valid, newfd; + + /* 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(); + if (entries < 0) + die("git-write-tree: error reading cache"); + + if (!active_cache_tree) + 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, + missing_ok, 0) < 0) + die("git-write-tree: error building trees"); + if (0 <= newfd) { + if (!write_cache(newfd, active_cache, active_nr)) + 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 + * ends up using the old index with unupdated cache-tree part + * it misses the work we did here, but that is just a + * performance penalty and not a big deal. + */ + } + + if (prefix) { + struct cache_tree *subtree = + cache_tree_find(active_cache_tree, prefix); + memcpy(sha1, subtree->sha1, 20); + } + else + 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; +} diff --git a/builtin.h b/builtin.h index b9f36beb66..f12d5e68f6 100644 --- a/builtin.h +++ b/builtin.h @@ -1,6 +1,8 @@ #ifndef BUILTIN_H #define BUILTIN_H +#include + #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 diff --git a/cache.h b/cache.h index 7fcb6d406a..eaa5c0c356 100644 --- a/cache.h +++ b/cache.h @@ -384,4 +384,15 @@ extern void setup_pager(void); int decode_85(char *dst, char *line, int linelen); void encode_85(char *buf, unsigned char *data, int bytes); +/* alloc.c */ +struct blob; +struct tree; +struct commit; +struct tag; +extern struct blob *alloc_blob_node(void); +extern struct tree *alloc_tree_node(void); +extern struct commit *alloc_commit_node(void); +extern struct tag *alloc_tag_node(void); +extern void alloc_report(void); + #endif /* CACHE_H */ diff --git a/commit.c b/commit.c index 5914200a2f..946615d2ad 100644 --- a/commit.c +++ b/commit.c @@ -84,7 +84,7 @@ struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) { - struct commit *ret = xcalloc(1, sizeof(struct commit)); + struct commit *ret = alloc_commit_node(); created_object(sha1, &ret->object); ret->object.type = TYPE_COMMIT; return ret; @@ -447,7 +447,7 @@ static int add_rfc2047(char *buf, const char *line, int len) memcpy(bp, q_utf8, sizeof(q_utf8)-1); bp += sizeof(q_utf8)-1; for (i = 0; i < len; i++) { - unsigned ch = line[i]; + unsigned ch = line[i] & 0xFF; if (is_rfc2047_special(ch)) { sprintf(bp, "=%02X", ch); bp += 3; @@ -571,10 +571,23 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit * to say this is not a 7-bit ASCII. */ if (fmt == CMIT_FMT_EMAIL && !after_subject) { - int i; - for (i = 0; !plain_non_ascii && msg[i] && i < len; i++) - if (msg[i] & 0x80) + int i, ch, in_body; + + for (in_body = i = 0; (ch = msg[i]) && i < len; i++) { + if (!in_body) { + /* author could be non 7-bit ASCII but + * the log may so; skip over the + * header part first. + */ + if (ch == '\n' && + i + 1 < len && msg[i+1] == '\n') + in_body = 1; + } + else if (ch & 0x80) { plain_non_ascii = 1; + break; + } + } } for (;;) { diff --git a/config.c b/config.c index 984c75f5dd..3e077d4c6c 100644 --- a/config.c +++ b/config.c @@ -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)) + 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/connect.c b/connect.c index 52d709e58d..db7342e4d2 100644 --- a/connect.c +++ b/connect.c @@ -581,6 +581,11 @@ int git_connect(int fd[2], char *url, const char *prog) enum protocol protocol = PROTO_LOCAL; int free_path = 0; + /* Without this we cannot rely on waitpid() to tell + * what happened to our children. + */ + signal(SIGCHLD, SIG_DFL); + host = strstr(url, "://"); if(host) { *host = '\0'; diff --git a/daemon.c b/daemon.c index 2f03f99d2d..10670047f2 100644 --- a/daemon.c +++ b/daemon.c @@ -671,6 +671,11 @@ int main(int argc, char **argv) int inetd_mode = 0; int i; + /* Without this we cannot rely on waitpid() to tell + * what happened to our children. + */ + signal(SIGCHLD, SIG_DFL); + for (i = 1; i < argc; i++) { char *arg = argv[i]; 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 #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); diff --git a/diff.c b/diff.c index 9e9cfc8b75..bc32a4aa29 100644 --- a/diff.c +++ b/diff.c @@ -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; diff --git a/diff.h b/diff.h index 4fc597c594..de9de574e7 100644 --- a/diff.h +++ b/diff.h @@ -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; diff --git a/git.c b/git.c index 329ebec78c..94e9a4a4b9 100644 --- a/git.c +++ b/git.c @@ -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/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/mailinfo.c b/mailinfo.c deleted file mode 100644 index d9b74f30de..0000000000 --- a/mailinfo.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * Another stupid program, this one parsing the headers of an - * email to figure out authorship and subject - */ -#define _GNU_SOURCE -#include -#include -#include -#include -#ifndef NO_ICONV -#include -#endif -#include "git-compat-util.h" -#include "cache.h" - -static FILE *cmitmsg, *patchfile; - -static int keep_subject = 0; -static char *metainfo_charset = NULL; -static char line[1000]; -static char date[1000]; -static char name[1000]; -static char email[1000]; -static char subject[1000]; - -static enum { - TE_DONTCARE, TE_QP, TE_BASE64, -} transfer_encoding; -static char charset[256]; - -static char multipart_boundary[1000]; -static int multipart_boundary_len; -static int patch_lines = 0; - -static char *sanity_check(char *name, char *email) -{ - int len = strlen(name); - if (len < 3 || len > 60) - return email; - if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>')) - return email; - return name; -} - -static int bogus_from(char *line) -{ - /* John Doe */ - char *bra, *ket, *dst, *cp; - - /* This is fallback, so do not bother if we already have an - * e-mail address. - */ - if (*email) - return 0; - - bra = strchr(line, '<'); - if (!bra) - return 0; - ket = strchr(bra, '>'); - if (!ket) - return 0; - - for (dst = email, cp = bra+1; cp < ket; ) - *dst++ = *cp++; - *dst = 0; - for (cp = line; isspace(*cp); cp++) - ; - for (bra--; isspace(*bra); bra--) - *bra = 0; - cp = sanity_check(cp, email); - strcpy(name, cp); - return 1; -} - -static int handle_from(char *in_line) -{ - char line[1000]; - char *at; - char *dst; - - strcpy(line, in_line); - at = strchr(line, '@'); - if (!at) - return bogus_from(line); - - /* - * If we already have one email, don't take any confusing lines - */ - if (*email && strchr(at+1, '@')) - return 0; - - /* Pick up the string around '@', possibly delimited with <> - * pair; that is the email part. White them out while copying. - */ - while (at > line) { - char c = at[-1]; - if (isspace(c)) - break; - if (c == '<') { - at[-1] = ' '; - break; - } - at--; - } - dst = email; - for (;;) { - unsigned char c = *at; - if (!c || c == '>' || isspace(c)) { - if (c == '>') - *at = ' '; - break; - } - *at++ = ' '; - *dst++ = c; - } - *dst++ = 0; - - /* The remainder is name. It could be "John Doe " - * or "john.doe@xz (John Doe)", but we have whited out the - * email part, so trim from both ends, possibly removing - * the () pair at the end. - */ - at = line + strlen(line); - while (at > line) { - unsigned char c = *--at; - if (!isspace(c)) { - at[(c == ')') ? 0 : 1] = 0; - break; - } - } - - at = line; - for (;;) { - unsigned char c = *at; - if (!c || !isspace(c)) { - if (c == '(') - at++; - break; - } - at++; - } - at = sanity_check(at, email); - strcpy(name, at); - return 1; -} - -static int handle_date(char *line) -{ - strcpy(date, line); - return 0; -} - -static int handle_subject(char *line) -{ - strcpy(subject, line); - return 0; -} - -/* NOTE NOTE NOTE. We do not claim we do full MIME. We just attempt - * to have enough heuristics to grok MIME encoded patches often found - * on our mailing lists. For example, we do not even treat header lines - * case insensitively. - */ - -static int slurp_attr(const char *line, const char *name, char *attr) -{ - char *ends, *ap = strcasestr(line, name); - size_t sz; - - if (!ap) { - *attr = 0; - return 0; - } - ap += strlen(name); - if (*ap == '"') { - ap++; - ends = "\""; - } - else - ends = "; \t"; - sz = strcspn(ap, ends); - memcpy(attr, ap, sz); - attr[sz] = 0; - return 1; -} - -static int handle_subcontent_type(char *line) -{ - /* We do not want to mess with boundary. Note that we do not - * handle nested multipart. - */ - if (strcasestr(line, "boundary=")) { - fprintf(stderr, "Not handling nested multipart message.\n"); - exit(1); - } - slurp_attr(line, "charset=", charset); - if (*charset) { - int i, c; - for (i = 0; (c = charset[i]) != 0; i++) - charset[i] = tolower(c); - } - return 0; -} - -static int handle_content_type(char *line) -{ - *multipart_boundary = 0; - if (slurp_attr(line, "boundary=", multipart_boundary + 2)) { - memcpy(multipart_boundary, "--", 2); - multipart_boundary_len = strlen(multipart_boundary); - } - slurp_attr(line, "charset=", charset); - return 0; -} - -static int handle_content_transfer_encoding(char *line) -{ - if (strcasestr(line, "base64")) - transfer_encoding = TE_BASE64; - else if (strcasestr(line, "quoted-printable")) - transfer_encoding = TE_QP; - else - transfer_encoding = TE_DONTCARE; - return 0; -} - -static int is_multipart_boundary(const char *line) -{ - return (!memcmp(line, multipart_boundary, multipart_boundary_len)); -} - -static int eatspace(char *line) -{ - int len = strlen(line); - while (len > 0 && isspace(line[len-1])) - line[--len] = 0; - return len; -} - -#define SEEN_FROM 01 -#define SEEN_DATE 02 -#define SEEN_SUBJECT 04 -#define SEEN_BOGUS_UNIX_FROM 010 -#define SEEN_PREFIX 020 - -/* First lines of body can have From:, Date:, and Subject: or empty */ -static void handle_inbody_header(int *seen, char *line) -{ - if (*seen & SEEN_PREFIX) - return; - if (isspace(*line)) { - char *cp; - for (cp = line + 1; *cp; cp++) { - if (!isspace(*cp)) - break; - } - if (!*cp) - return; - } - if (!memcmp(">From", line, 5) && isspace(line[5])) { - if (!(*seen & SEEN_BOGUS_UNIX_FROM)) { - *seen |= SEEN_BOGUS_UNIX_FROM; - return; - } - } - if (!memcmp("From:", line, 5) && isspace(line[5])) { - if (!(*seen & SEEN_FROM) && handle_from(line+6)) { - *seen |= SEEN_FROM; - return; - } - } - if (!memcmp("Date:", line, 5) && isspace(line[5])) { - if (!(*seen & SEEN_DATE)) { - handle_date(line+6); - *seen |= SEEN_DATE; - return; - } - } - if (!memcmp("Subject:", line, 8) && isspace(line[8])) { - if (!(*seen & SEEN_SUBJECT)) { - handle_subject(line+9); - *seen |= SEEN_SUBJECT; - return; - } - } - if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) { - if (!(*seen & SEEN_SUBJECT)) { - handle_subject(line); - *seen |= SEEN_SUBJECT; - return; - } - } - *seen |= SEEN_PREFIX; -} - -static char *cleanup_subject(char *subject) -{ - if (keep_subject) - return subject; - for (;;) { - char *p; - int len, remove; - switch (*subject) { - case 'r': case 'R': - if (!memcmp("e:", subject+1, 2)) { - subject +=3; - continue; - } - break; - case ' ': case '\t': case ':': - subject++; - continue; - - case '[': - p = strchr(subject, ']'); - if (!p) { - subject++; - continue; - } - len = strlen(p); - remove = p - subject; - if (remove <= len *2) { - subject = p+1; - continue; - } - break; - } - eatspace(subject); - return subject; - } -} - -static void cleanup_space(char *buf) -{ - unsigned char c; - while ((c = *buf) != 0) { - buf++; - if (isspace(c)) { - buf[-1] = ' '; - c = *buf; - while (isspace(c)) { - int len = strlen(buf); - memmove(buf, buf+1, len); - c = *buf; - } - } - } -} - -static void decode_header_bq(char *it); -typedef int (*header_fn_t)(char *); -struct header_def { - const char *name; - header_fn_t func; - int namelen; -}; - -static void check_header(char *line, struct header_def *header) -{ - int i; - - if (header[0].namelen <= 0) { - for (i = 0; header[i].name; i++) - header[i].namelen = strlen(header[i].name); - } - for (i = 0; header[i].name; i++) { - int len = header[i].namelen; - if (!strncasecmp(line, header[i].name, len) && - line[len] == ':' && isspace(line[len + 1])) { - /* Unwrap inline B and Q encoding, and optionally - * normalize the meta information to utf8. - */ - decode_header_bq(line + len + 2); - header[i].func(line + len + 2); - break; - } - } -} - -static void check_subheader_line(char *line) -{ - static struct header_def header[] = { - { "Content-Type", handle_subcontent_type }, - { "Content-Transfer-Encoding", - handle_content_transfer_encoding }, - { NULL }, - }; - check_header(line, header); -} -static void check_header_line(char *line) -{ - static struct header_def header[] = { - { "From", handle_from }, - { "Date", handle_date }, - { "Subject", handle_subject }, - { "Content-Type", handle_content_type }, - { "Content-Transfer-Encoding", - handle_content_transfer_encoding }, - { NULL }, - }; - check_header(line, header); -} - -static int is_rfc2822_header(char *line) -{ - /* - * The section that defines the loosest possible - * field name is "3.6.8 Optional fields". - * - * optional-field = field-name ":" unstructured CRLF - * field-name = 1*ftext - * ftext = %d33-57 / %59-126 - */ - int ch; - char *cp = line; - while ((ch = *cp++)) { - if (ch == ':') - return cp != line; - if ((33 <= ch && ch <= 57) || - (59 <= ch && ch <= 126)) - continue; - break; - } - return 0; -} - -static int read_one_header_line(char *line, int sz, FILE *in) -{ - int ofs = 0; - while (ofs < sz) { - int peek, len; - if (fgets(line + ofs, sz - ofs, in) == NULL) - break; - len = eatspace(line + ofs); - if ((len == 0) || !is_rfc2822_header(line)) { - /* Re-add the newline */ - line[ofs + len] = '\n'; - line[ofs + len + 1] = '\0'; - break; - } - ofs += len; - /* Yuck, 2822 header "folding" */ - peek = fgetc(in); ungetc(peek, in); - if (peek != ' ' && peek != '\t') - break; - } - /* Count mbox From headers as headers */ - if (!ofs && !memcmp(line, "From ", 5)) - ofs = 1; - return ofs; -} - -static unsigned hexval(int c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - return ~0; -} - -static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047) -{ - int c; - while ((c = *in++) != 0 && (in <= ep)) { - if (c == '=') { - int d = *in++; - if (d == '\n' || !d) - break; /* drop trailing newline */ - *ot++ = ((hexval(d) << 4) | hexval(*in++)); - continue; - } - if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */ - c = 0x20; - *ot++ = c; - } - *ot = 0; - return 0; -} - -static int decode_b_segment(char *in, char *ot, char *ep) -{ - /* Decode in..ep, possibly in-place to ot */ - int c, pos = 0, acc = 0; - - while ((c = *in++) != 0 && (in <= ep)) { - if (c == '+') - c = 62; - else if (c == '/') - c = 63; - else if ('A' <= c && c <= 'Z') - c -= 'A'; - else if ('a' <= c && c <= 'z') - c -= 'a' - 26; - else if ('0' <= c && c <= '9') - c -= '0' - 52; - else if (c == '=') { - /* padding is almost like (c == 0), except we do - * not output NUL resulting only from it; - * for now we just trust the data. - */ - c = 0; - } - else - continue; /* garbage */ - switch (pos++) { - case 0: - acc = (c << 2); - break; - case 1: - *ot++ = (acc | (c >> 4)); - acc = (c & 15) << 4; - break; - case 2: - *ot++ = (acc | (c >> 2)); - acc = (c & 3) << 6; - break; - case 3: - *ot++ = (acc | c); - acc = pos = 0; - break; - } - } - *ot = 0; - return 0; -} - -static void convert_to_utf8(char *line, char *charset) -{ -#ifndef NO_ICONV - char *in, *out; - size_t insize, outsize, nrc; - char outbuf[4096]; /* cheat */ - static char latin_one[] = "latin1"; - char *input_charset = *charset ? charset : latin_one; - iconv_t conv = iconv_open(metainfo_charset, input_charset); - - if (conv == (iconv_t) -1) { - static int warned_latin1_once = 0; - if (input_charset != latin_one) { - fprintf(stderr, "cannot convert from %s to %s\n", - input_charset, metainfo_charset); - *charset = 0; - } - else if (!warned_latin1_once) { - warned_latin1_once = 1; - fprintf(stderr, "tried to convert from %s to %s, " - "but your iconv does not work with it.\n", - input_charset, metainfo_charset); - } - return; - } - in = line; - insize = strlen(in); - out = outbuf; - outsize = sizeof(outbuf); - nrc = iconv(conv, &in, &insize, &out, &outsize); - iconv_close(conv); - if (nrc == (size_t) -1) - return; - *out = 0; - strcpy(line, outbuf); -#endif -} - -static void decode_header_bq(char *it) -{ - char *in, *out, *ep, *cp, *sp; - char outbuf[1000]; - - in = it; - out = outbuf; - while ((ep = strstr(in, "=?")) != NULL) { - int sz, encoding; - char charset_q[256], piecebuf[256]; - if (in != ep) { - sz = ep - in; - memcpy(out, in, sz); - out += sz; - in += sz; - } - /* E.g. - * ep : "=?iso-2022-jp?B?GyR...?= foo" - * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz" - */ - ep += 2; - cp = strchr(ep, '?'); - if (!cp) - return; /* no munging */ - for (sp = ep; sp < cp; sp++) - charset_q[sp - ep] = tolower(*sp); - charset_q[cp - ep] = 0; - encoding = cp[1]; - if (!encoding || cp[2] != '?') - return; /* no munging */ - ep = strstr(cp + 3, "?="); - if (!ep) - return; /* no munging */ - switch (tolower(encoding)) { - default: - return; /* no munging */ - case 'b': - sz = decode_b_segment(cp + 3, piecebuf, ep); - break; - case 'q': - sz = decode_q_segment(cp + 3, piecebuf, ep, 1); - break; - } - if (sz < 0) - return; - if (metainfo_charset) - convert_to_utf8(piecebuf, charset_q); - strcpy(out, piecebuf); - out += strlen(out); - in = ep + 2; - } - strcpy(out, in); - strcpy(it, outbuf); -} - -static void decode_transfer_encoding(char *line) -{ - char *ep; - - switch (transfer_encoding) { - case TE_QP: - ep = line + strlen(line); - decode_q_segment(line, line, ep, 0); - break; - case TE_BASE64: - ep = line + strlen(line); - decode_b_segment(line, line, ep); - break; - case TE_DONTCARE: - break; - } -} - -static void handle_info(void) -{ - char *sub; - - sub = cleanup_subject(subject); - cleanup_space(name); - cleanup_space(date); - cleanup_space(email); - cleanup_space(sub); - - printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", - name, email, sub, date); -} - -/* We are inside message body and have read line[] already. - * Spit out the commit log. - */ -static int handle_commit_msg(int *seen) -{ - if (!cmitmsg) - return 0; - do { - if (!memcmp("diff -", line, 6) || - !memcmp("---", line, 3) || - !memcmp("Index: ", line, 7)) - break; - if ((multipart_boundary[0] && is_multipart_boundary(line))) { - /* We come here when the first part had only - * the commit message without any patch. We - * pretend we have not seen this line yet, and - * go back to the loop. - */ - return 1; - } - - /* Unwrap transfer encoding and optionally - * normalize the log message to UTF-8. - */ - decode_transfer_encoding(line); - if (metainfo_charset) - convert_to_utf8(line, charset); - - handle_inbody_header(seen, line); - if (!(*seen & SEEN_PREFIX)) - continue; - - fputs(line, cmitmsg); - } while (fgets(line, sizeof(line), stdin) != NULL); - fclose(cmitmsg); - cmitmsg = NULL; - return 0; -} - -/* We have done the commit message and have the first - * line of the patch in line[]. - */ -static void handle_patch(void) -{ - do { - if (multipart_boundary[0] && is_multipart_boundary(line)) - break; - /* Only unwrap transfer encoding but otherwise do not - * do anything. We do *NOT* want UTF-8 conversion - * here; we are dealing with the user payload. - */ - decode_transfer_encoding(line); - fputs(line, patchfile); - patch_lines++; - } while (fgets(line, sizeof(line), stdin) != NULL); -} - -/* multipart boundary and transfer encoding are set up for us, and we - * are at the end of the sub header. do equivalent of handle_body up - * to the next boundary without closing patchfile --- we will expect - * that the first part to contain commit message and a patch, and - * handle other parts as pure patches. - */ -static int handle_multipart_one_part(int *seen) -{ - int n = 0; - - while (fgets(line, sizeof(line), stdin) != NULL) { - again: - n++; - if (is_multipart_boundary(line)) - break; - if (handle_commit_msg(seen)) - goto again; - handle_patch(); - break; - } - if (n == 0) - return -1; - return 0; -} - -static void handle_multipart_body(void) -{ - int seen = 0; - int part_num = 0; - - /* Skip up to the first boundary */ - while (fgets(line, sizeof(line), stdin) != NULL) - if (is_multipart_boundary(line)) { - part_num = 1; - break; - } - if (!part_num) - return; - /* We are on boundary line. Start slurping the subhead. */ - while (1) { - int hdr = read_one_header_line(line, sizeof(line), stdin); - if (!hdr) { - if (handle_multipart_one_part(&seen) < 0) - return; - /* Reset per part headers */ - transfer_encoding = TE_DONTCARE; - charset[0] = 0; - } - else - check_subheader_line(line); - } - fclose(patchfile); - if (!patch_lines) { - fprintf(stderr, "No patch found\n"); - exit(1); - } -} - -/* Non multipart message */ -static void handle_body(void) -{ - int seen = 0; - - handle_commit_msg(&seen); - handle_patch(); - fclose(patchfile); - if (!patch_lines) { - fprintf(stderr, "No patch found\n"); - exit(1); - } -} - -static const char mailinfo_usage[] = - "git-mailinfo [-k] [-u | --encoding=] msg patch info"; - -int main(int argc, char **argv) -{ - /* NEEDSWORK: might want to do the optional .git/ directory - * discovery - */ - git_config(git_default_config); - - while (1 < argc && argv[1][0] == '-') { - if (!strcmp(argv[1], "-k")) - keep_subject = 1; - else if (!strcmp(argv[1], "-u")) - metainfo_charset = git_commit_encoding; - else if (!strncmp(argv[1], "--encoding=", 11)) - metainfo_charset = argv[1] + 11; - else - usage(mailinfo_usage); - argc--; 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; -} diff --git a/mailsplit.c b/mailsplit.c deleted file mode 100644 index 70a569c12a..0000000000 --- a/mailsplit.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Totally braindamaged mbox splitter program. - * - * It just splits a mbox into a list of files: "0001" "0002" .. - * so you can process them further from there. - */ -#include -#include -#include -#include -#include -#include -#include -#include "cache.h" - -static const char git_mailsplit_usage[] = -"git-mailsplit [-d] [-f] [-b] -o ..."; - -static int is_from_line(const char *line, int len) -{ - const char *colon; - - if (len < 20 || memcmp("From ", line, 5)) - return 0; - - colon = line + len - 2; - line += 5; - for (;;) { - if (colon < line) - return 0; - if (*--colon == ':') - break; - } - - if (!isdigit(colon[-4]) || - !isdigit(colon[-2]) || - !isdigit(colon[-1]) || - !isdigit(colon[ 1]) || - !isdigit(colon[ 2])) - return 0; - - /* year */ - if (strtol(colon+3, NULL, 10) <= 90) - return 0; - - /* Ok, close enough */ - return 1; -} - -/* Could be as small as 64, enough to hold a Unix "From " line. */ -static char buf[4096]; - -/* Called with the first line (potentially partial) - * already in buf[] -- normally that should begin with - * the Unix "From " line. Write it into the specified - * file. - */ -static int split_one(FILE *mbox, const char *name, int allow_bare) -{ - FILE *output = NULL; - int len = strlen(buf); - int fd; - int status = 0; - int is_bare = !is_from_line(buf, len); - - if (is_bare && !allow_bare) - goto corrupt; - - fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (fd < 0) - die("cannot open output file %s", name); - output = fdopen(fd, "w"); - - /* Copy it out, while searching for a line that begins with - * "From " and having something that looks like a date format. - */ - for (;;) { - int is_partial = (buf[len-1] != '\n'); - - if (fputs(buf, output) == EOF) - die("cannot write output"); - - if (fgets(buf, sizeof(buf), mbox) == NULL) { - if (feof(mbox)) { - status = 1; - break; - } - die("cannot read mbox"); - } - len = strlen(buf); - if (!is_partial && !is_bare && is_from_line(buf, len)) - break; /* done with one message */ - } - fclose(output); - return status; - - corrupt: - if (output) - fclose(output); - unlink(name); - fprintf(stderr, "corrupt mailbox\n"); - exit(1); -} - -int main(int argc, const char **argv) -{ - int nr = 0, nr_prec = 4; - 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; - - if (arg[0] != '-') - break; - /* do flags here */ - if ( arg[1] == 'd' ) { - nr_prec = strtol(arg+2, NULL, 10); - if (nr_prec < 3 || 10 <= nr_prec) - usage(git_mailsplit_usage); - continue; - } else if ( arg[1] == 'f' ) { - nr = strtol(arg+2, NULL, 10); - } else if ( arg[1] == 'b' && !arg[2] ) { - allow_bare = 1; - } else if ( arg[1] == 'o' && arg[2] ) { - dir = arg+2; - } else if ( arg[1] == '-' && !arg[2] ) { - argp++; /* -- marks end of options */ - break; - } else { - die("unknown option: %s", arg); - } - } - - if ( !dir ) { - /* Backwards compatibility: if no -o specified, accept - or just */ - switch (argc - (argp-argv)) { - case 1: - dir = argp[0]; - argp = stdin_only; - break; - case 2: - stdin_only[0] = argp[0]; - dir = argp[1]; - argp = stdin_only; - break; - default: - usage(git_mailsplit_usage); - } - } else { - /* New usage: if no more argument, parse stdin */ - if ( !*argp ) - 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); - } - - printf("%d\n", nr); - return 0; -} diff --git a/merge-index.c b/merge-index.c index 024196e7ac..190e12fb7c 100644 --- a/merge-index.c +++ b/merge-index.c @@ -99,6 +99,11 @@ int main(int argc, char **argv) { int i, force_file = 0; + /* Without this we cannot rely on waitpid() to tell + * what happened to our children. + */ + signal(SIGCHLD, SIG_DFL); + if (argc < 3) usage("git-merge-index [-o] [-q] (-a | *)"); 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; } diff --git a/object.c b/object.c index e26e319ccd..37784cee9a 100644 --- a/object.c +++ b/object.c @@ -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; +} diff --git a/object.h b/object.h index c537b4b72a..6f23a9a180 100644 --- a/object.h +++ b/object.h @@ -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 */ diff --git a/pager.c b/pager.c index 9a30939016..2d186e8bde 100644 --- a/pager.c +++ b/pager.c @@ -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); diff --git a/quote.c b/quote.c index 06792d47c3..dcc2326610 100644 --- a/quote.c +++ b/quote.c @@ -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..ab8f1afeea 100644 --- a/repo-config.c +++ b/repo-config.c @@ -64,7 +64,20 @@ 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 = 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 +89,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 +102,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 +119,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/stripspace.c b/stripspace.c deleted file mode 100644 index 65a6346452..0000000000 --- a/stripspace.c +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include - -/* - * Remove empty lines from the beginning and end. - * - * Turn multiple consecutive empty lines into just one - * empty line. Return true if it is an incomplete line. - */ -static int cleanup(char *line) -{ - int len = strlen(line); - - if (len && line[len-1] == '\n') { - if (len == 1) - return 0; - do { - unsigned char c = line[len-2]; - if (!isspace(c)) - break; - line[len-2] = '\n'; - len--; - line[len] = 0; - } while (len > 1); - return 0; - } - return 1; -} - -int main(int argc, char **argv) -{ - int empties = -1; - int incomplete = 0; - char line[1024]; - - while (fgets(line, sizeof(line), stdin)) { - incomplete = cleanup(line); - - /* Not just an empty line? */ - if (line[0] != '\n') { - if (empties > 0) - putchar('\n'); - empties = 0; - fputs(line, stdout); - continue; - } - if (empties < 0) - continue; - empties++; - } - if (incomplete) - putchar('\n'); - return 0; -} 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/annotate-tests.sh b/t/annotate-tests.sh index c04f0e1540..1148b0257d 100644 --- a/t/annotate-tests.sh +++ b/t/annotate-tests.sh @@ -111,7 +111,7 @@ test_expect_success \ test_expect_success \ 'some edit' \ - 'perl -pi -e "s/^1A.*\n$//; s/^3A/99/" file && + 'perl -p -i.orig -e "s/^1A.*\n$//; s/^3A/99/" file && GIT_AUTHOR_NAME="D" git commit -a -m "edit"' test_expect_success \ 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 diff --git a/tag.c b/tag.c index 9191333270..5f70a5b810 100644 --- a/tag.c +++ b/tag.c @@ -19,7 +19,7 @@ struct tag *lookup_tag(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) { - struct tag *ret = xcalloc(1, sizeof(struct tag)); + struct tag *ret = alloc_tag_node(); created_object(sha1, &ret->object); ret->object.type = TYPE_TAG; return ret; diff --git a/tree.c b/tree.c index 64422fd27e..10236555cc 100644 --- a/tree.c +++ b/tree.c @@ -129,7 +129,7 @@ struct tree *lookup_tree(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) { - struct tree *ret = xcalloc(1, sizeof(struct tree)); + struct tree *ret = alloc_tree_node(); created_object(sha1, &ret->object); ret->object.type = TYPE_TREE; return ret; diff --git a/update-index.c b/update-index.c deleted file mode 100644 index fbccc4a67b..0000000000 --- a/update-index.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#include "cache.h" -#include "strbuf.h" -#include "quote.h" -#include "cache-tree.h" -#include "tree-walk.h" - -/* - * Default to not allowing changes to the list of files. The - * tool doesn't actually care, but this makes it harder to add - * files to the revision control by mistake by doing something - * like "git-update-index *" and suddenly having all the object - * files be revision controlled. - */ -static int allow_add; -static int allow_remove; -static int allow_replace; -static int info_only; -static int force_remove; -static int verbose; -static int mark_valid_only = 0; -#define MARK_VALID 1 -#define UNMARK_VALID 2 - -static void report(const char *fmt, ...) -{ - va_list vp; - - if (!verbose) - return; - - va_start(vp, fmt); - vprintf(fmt, vp); - putchar('\n'); - va_end(vp); -} - -static int mark_valid(const char *path) -{ - int namelen = strlen(path); - int pos = cache_name_pos(path, namelen); - if (0 <= pos) { - switch (mark_valid_only) { - case MARK_VALID: - active_cache[pos]->ce_flags |= htons(CE_VALID); - break; - case UNMARK_VALID: - active_cache[pos]->ce_flags &= ~htons(CE_VALID); - break; - } - cache_tree_invalidate_path(active_cache_tree, path); - active_cache_changed = 1; - return 0; - } - return -1; -} - -static int add_file_to_cache(const char *path) -{ - int size, namelen, option, status; - struct cache_entry *ce; - struct stat st; - - status = lstat(path, &st); - - /* We probably want to do this in remove_file_from_cache() and - * add_cache_entry() instead... - */ - cache_tree_invalidate_path(active_cache_tree, path); - - if (status < 0 || S_ISDIR(st.st_mode)) { - /* When we used to have "path" and now we want to add - * "path/file", we need a way to remove "path" before - * being able to add "path/file". However, - * "git-update-index --remove path" would not work. - * --force-remove can be used but this is more user - * friendly, especially since we can do the opposite - * case just fine without --force-remove. - */ - if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) { - if (allow_remove) { - if (remove_file_from_cache(path)) - return error("%s: cannot remove from the index", - path); - else - return 0; - } else if (status < 0) { - return error("%s: does not exist and --remove not passed", - path); - } - } - if (0 == status) - return error("%s: is a directory - add files inside instead", - path); - else - return error("lstat(\"%s\"): %s", path, - strerror(errno)); - } - - namelen = strlen(path); - size = cache_entry_size(namelen); - ce = xcalloc(1, size); - memcpy(ce->name, path, namelen); - ce->ce_flags = htons(namelen); - fill_stat_cache_info(ce, &st); - - ce->ce_mode = create_ce_mode(st.st_mode); - if (!trust_executable_bit) { - /* If there is an existing entry, pick the mode bits - * from it. - */ - int pos = cache_name_pos(path, namelen); - if (0 <= pos) - ce->ce_mode = active_cache[pos]->ce_mode; - } - - if (index_path(ce->sha1, path, &st, !info_only)) - return -1; - option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; - option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; - if (add_cache_entry(ce, option)) - return error("%s: cannot add to the index - missing --add option?", - path); - return 0; -} - -static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, - const char *path, int stage) -{ - int size, len, option; - struct cache_entry *ce; - - if (!verify_path(path)) - return -1; - - len = strlen(path); - size = cache_entry_size(len); - ce = xcalloc(1, size); - - memcpy(ce->sha1, sha1, 20); - memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(len, stage); - ce->ce_mode = create_ce_mode(mode); - if (assume_unchanged) - ce->ce_flags |= htons(CE_VALID); - option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; - option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; - if (add_cache_entry(ce, option)) - return error("%s: cannot add to the index - missing --add option?", - path); - report("add '%s'", path); - cache_tree_invalidate_path(active_cache_tree, path); - return 0; -} - -static void chmod_path(int flip, const char *path) -{ - int pos; - struct cache_entry *ce; - unsigned int mode; - - pos = cache_name_pos(path, strlen(path)); - if (pos < 0) - goto fail; - ce = active_cache[pos]; - mode = ntohl(ce->ce_mode); - if (!S_ISREG(mode)) - goto fail; - switch (flip) { - case '+': - ce->ce_mode |= htonl(0111); break; - case '-': - ce->ce_mode &= htonl(~0111); break; - default: - goto fail; - } - cache_tree_invalidate_path(active_cache_tree, path); - active_cache_changed = 1; - report("chmod %cx '%s'", flip, path); - return; - fail: - 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); - if (!verify_path(p)) { - fprintf(stderr, "Ignoring path %s\n", path); - goto free_return; - } - if (mark_valid_only) { - if (mark_valid(p)) - die("Unable to mark file %s", path); - goto free_return; - } - cache_tree_invalidate_path(active_cache_tree, path); - - if (force_remove) { - if (remove_file_from_cache(p)) - die("git-update-index: unable to remove %s", path); - report("remove '%s'", path); - goto free_return; - } - if (add_file_to_cache(p)) - die("Unable to process file %s", path); - report("add '%s'", path); - free_return: - if (p < path || p > path + strlen(path)) - free((char*)p); -} - -static void read_index_info(int line_termination) -{ - struct strbuf buf; - strbuf_init(&buf); - while (1) { - char *ptr, *tab; - char *path_name; - unsigned char sha1[20]; - unsigned int mode; - int stage; - - /* This reads lines formatted in one of three formats: - * - * (1) mode SP sha1 TAB path - * The first format is what "git-apply --index-info" - * reports, and used to reconstruct a partial tree - * that is used for phony merge base tree when falling - * back on 3-way merge. - * - * (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. - */ - read_line(&buf, stdin, line_termination); - if (buf.eof) - break; - - mode = strtoul(buf.buf, &ptr, 8); - if (ptr == buf.buf || *ptr != ' ') - goto bad_line; - - tab = strchr(ptr, '\t'); - if (!tab || tab - ptr < 41) - goto bad_line; - - if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') { - stage = tab[-1] - '0'; - ptr = tab + 1; /* point at the head of path */ - tab = tab - 2; /* point at tail of sha1 */ - } - else { - stage = 0; - ptr = tab + 1; /* point at the head of path */ - } - - if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ') - goto bad_line; - - if (line_termination && ptr[0] == '"') - path_name = unquote_c_style(ptr, NULL); - else - path_name = ptr; - - if (!verify_path(path_name)) { - fprintf(stderr, "Ignoring path %s\n", path_name); - if (path_name != ptr) - free(path_name); - continue; - } - cache_tree_invalidate_path(active_cache_tree, path_name); - - if (!mode) { - /* mode == 0 means there is no such path -- remove */ - if (remove_file_from_cache(path_name)) - die("git-update-index: unable to remove %s", - ptr); - } - else { - /* mode ' ' sha1 '\t' name - * ptr[-1] points at tab, - * ptr[-41] is at the beginning of sha1 - */ - ptr[-42] = ptr[-1] = 0; - if (add_cacheinfo(mode, sha1, path_name, stage)) - die("git-update-index: unable to update %s", - path_name); - } - if (path_name != ptr) - free(path_name); - continue; - - bad_line: - die("malformed index info %s", buf.buf); - } -} - -static const char update_index_usage[] = -"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again] [--ignore-missing] [-z] [--verbose] [--] ..."; - -static unsigned char head_sha1[20]; -static unsigned char merge_head_sha1[20]; - -static struct cache_entry *read_one_ent(const char *which, - unsigned char *ent, const char *path, - int namelen, int stage) -{ - unsigned mode; - unsigned char sha1[20]; - int size; - struct cache_entry *ce; - - if (get_tree_entry(ent, path, sha1, &mode)) { - if (which) - error("%s: not in %s branch.", path, which); - return NULL; - } - if (mode == S_IFDIR) { - if (which) - error("%s: not a blob in %s branch.", path, which); - return NULL; - } - size = cache_entry_size(namelen); - ce = xcalloc(1, size); - - memcpy(ce->sha1, sha1, 20); - memcpy(ce->name, path, namelen); - ce->ce_flags = create_ce_flags(namelen, stage); - ce->ce_mode = create_ce_mode(mode); - return ce; -} - -static int unresolve_one(const char *path) -{ - int namelen = strlen(path); - int pos; - int ret = 0; - struct cache_entry *ce_2 = NULL, *ce_3 = NULL; - - /* See if there is such entry in the index. */ - pos = cache_name_pos(path, namelen); - if (pos < 0) { - /* If there isn't, either it is unmerged, or - * resolved as "removed" by mistake. We do not - * want to do anything in the former case. - */ - pos = -pos-1; - if (pos < active_nr) { - struct cache_entry *ce = active_cache[pos]; - if (ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) { - fprintf(stderr, - "%s: skipping still unmerged path.\n", - path); - goto free_return; - } - } - } - - /* Grab blobs from given path from HEAD and MERGE_HEAD, - * stuff HEAD version in stage #2, - * stuff MERGE_HEAD version in stage #3. - */ - ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); - ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); - - if (!ce_2 || !ce_3) { - ret = -1; - goto free_return; - } - if (!memcmp(ce_2->sha1, ce_3->sha1, 20) && - ce_2->ce_mode == ce_3->ce_mode) { - fprintf(stderr, "%s: identical in both, skipping.\n", - path); - goto free_return; - } - - cache_tree_invalidate_path(active_cache_tree, path); - remove_file_from_cache(path); - if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { - error("%s: cannot add our version to the index.", path); - ret = -1; - goto free_return; - } - if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) - return 0; - error("%s: cannot add their version to the index.", path); - ret = -1; - free_return: - free(ce_2); - free(ce_3); - return ret; -} - -static void read_head_pointers(void) -{ - if (read_ref(git_path("HEAD"), head_sha1)) - die("No HEAD -- no initial commit yet?\n"); - if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { - fprintf(stderr, "Not in the middle of a merge.\n"); - exit(0); - } -} - -static int do_unresolve(int ac, const char **av, - const char *prefix, int prefix_length) -{ - int i; - int err = 0; - - /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we - * are not doing a merge, so exit with success status. - */ - read_head_pointers(); - - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - const char *p = prefix_path(prefix, prefix_length, arg); - err |= unresolve_one(p); - if (p < arg || p > arg + strlen(arg)) - free((char*)p); - } - return err; -} - -static int do_reupdate(int ac, const char **av, - const char *prefix, int prefix_length) -{ - /* Read HEAD and run update-index on paths that are - * merged and already different between index and HEAD. - */ - int pos; - int has_head = 1; - const char **pathspec = get_pathspec(prefix, av + 1); - - if (read_ref(git_path("HEAD"), head_sha1)) - /* If there is no HEAD, that means it is an initial - * commit. Update everything in the index. - */ - has_head = 0; - redo: - for (pos = 0; pos < active_nr; pos++) { - struct cache_entry *ce = active_cache[pos]; - struct cache_entry *old = NULL; - int save_nr; - - if (ce_stage(ce) || !ce_path_match(ce, pathspec)) - continue; - if (has_head) - old = read_one_ent(NULL, head_sha1, - ce->name, ce_namelen(ce), 0); - if (old && ce->ce_mode == old->ce_mode && - !memcmp(ce->sha1, old->sha1, 20)) { - free(old); - continue; /* unchanged */ - } - /* Be careful. The working tree may not have the - * path anymore, in which case, under 'allow_remove', - * or worse yet 'allow_replace', active_nr may decrease. - */ - save_nr = active_nr; - update_one(ce->name + prefix_length, prefix, prefix_length); - if (save_nr != active_nr) - goto redo; - } - return 0; -} - -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; - unsigned int refresh_flags = 0; - - git_config(git_default_config); - - newfd = hold_lock_file_for_update(&lock_file, get_index_file()); - if (newfd < 0) - die("unable to create new index file"); - - entries = read_cache(); - if (entries < 0) - die("cache corrupted"); - - for (i = 1 ; i < argc; i++) { - const char *path = argv[i]; - - if (allow_options && *path == '-') { - if (!strcmp(path, "--")) { - allow_options = 0; - continue; - } - if (!strcmp(path, "-q")) { - refresh_flags |= REFRESH_QUIET; - 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")) { - refresh_flags |= REFRESH_UNMERGED; - continue; - } - if (!strcmp(path, "--refresh")) { - has_errors |= refresh_cache(refresh_flags); - continue; - } - if (!strcmp(path, "--really-refresh")) { - has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags); - continue; - } - if (!strcmp(path, "--cacheinfo")) { - unsigned char sha1[20]; - unsigned int mode; - - if (i+3 >= argc) - die("git-update-index: --cacheinfo "); - - 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); - set_executable_bit = path[8]; - continue; - } - if (!strcmp(path, "--assume-unchanged")) { - mark_valid_only = MARK_VALID; - continue; - } - if (!strcmp(path, "--no-assume-unchanged")) { - mark_valid_only = UNMARK_VALID; - continue; - } - if (!strcmp(path, "--info-only")) { - info_only = 1; - continue; - } - if (!strcmp(path, "--force-remove")) { - force_remove = 1; - continue; - } - if (!strcmp(path, "-z")) { - line_termination = 0; - continue; - } - if (!strcmp(path, "--stdin")) { - if (i != argc - 1) - die("--stdin must be at the end"); - read_from_stdin = 1; - break; - } - if (!strcmp(path, "--index-info")) { - if (i != argc - 1) - die("--index-info must be at the end"); - allow_add = allow_replace = allow_remove = 1; - read_index_info(line_termination); - break; - } - if (!strcmp(path, "--unresolve")) { - has_errors = do_unresolve(argc - i, argv + i, - prefix, prefix_length); - if (has_errors) - active_cache_changed = 0; - goto finish; - } - if (!strcmp(path, "--again")) { - has_errors = do_reupdate(argc - i, argv + i, - prefix, prefix_length); - if (has_errors) - active_cache_changed = 0; - goto finish; - } - if (!strcmp(path, "--ignore-missing")) { - refresh_flags |= REFRESH_IGNORE_MISSING; - 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); - while (1) { - char *path_name; - const char *p; - read_line(&buf, stdin, line_termination); - if (buf.eof) - break; - if (line_termination && buf.buf[0] == '"') - path_name = unquote_c_style(buf.buf, NULL); - else - path_name = buf.buf; - p = prefix_path(prefix, prefix_length, path_name); - update_one(p, NULL, 0); - if (set_executable_bit) - chmod_path(set_executable_bit, p); - if (p < path_name || p > path_name + strlen(path_name)) - free((char*) p); - if (path_name != buf.buf) - free(path_name); - } - } - - finish: - if (active_cache_changed) { - if (write_cache(newfd, active_cache, active_nr) || - commit_lock_file(&lock_file)) - die("Unable to write new index file"); - } - - return has_errors ? 1 : 0; -} diff --git a/update-ref.c b/update-ref.c deleted file mode 100644 index a1e6bb90fe..0000000000 --- a/update-ref.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "cache.h" -#include "refs.h" - -static const char git_update_ref_usage[] = -"git-update-ref [] [-m ]"; - -int main(int argc, char **argv) -{ - const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; - struct ref_lock *lock; - unsigned char sha1[20], oldsha1[20]; - int i; - - setup_git_directory(); - git_config(git_default_config); - - for (i = 1; i < argc; i++) { - if (!strcmp("-m", argv[i])) { - if (i+1 >= argc) - usage(git_update_ref_usage); - msg = argv[++i]; - if (!*msg) - die("Refusing to perform update with empty message."); - if (strchr(msg, '\n')) - die("Refusing to perform update with \\n in message."); - continue; - } - if (!refname) { - refname = argv[i]; - continue; - } - if (!value) { - value = argv[i]; - continue; - } - if (!oldval) { - oldval = argv[i]; - continue; - } - } - if (!refname || !value) - usage(git_update_ref_usage); - - if (get_sha1(value, sha1)) - die("%s: not a valid SHA1", value); - memset(oldsha1, 0, 20); - if (oldval && get_sha1(oldval, oldsha1)) - die("%s: not a valid old SHA1", oldval); - - lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0); - if (!lock) - return 1; - if (write_ref_sha1(lock, sha1, msg) < 0) - return 1; - return 0; -} diff --git a/write-tree.c b/write-tree.c deleted file mode 100644 index bd07da6183..0000000000 --- a/write-tree.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#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=/]"; - -static struct lock_file lock_file; - -int main(int argc, char **argv) -{ - int entries, was_valid, newfd; - - setup_git_directory(); - - 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"); - - if (entries < 0) - die("git-write-tree: error reading cache"); - - if (!active_cache_tree) - 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, - missing_ok, 0) < 0) - die("git-write-tree: error building trees"); - if (0 <= newfd) { - if (!write_cache(newfd, active_cache, active_nr)) - 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 - * ends up using the old index with unupdated cache-tree part - * it misses the work we did here, but that is just a - * 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)); - } - else - printf("%s\n", sha1_to_hex(active_cache_tree->sha1)); - return 0; -} diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 21ab8e7e26..f91b403475 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -44,21 +44,18 @@ long xdl_bogosqrt(long n) { int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, xdemitcb_t *ecb) { + int i = 2; mmbuffer_t mb[3]; - int i; mb[0].ptr = (char *) pre; mb[0].size = psize; mb[1].ptr = (char *) rec; mb[1].size = size; - i = 2; - - if (!size || rec[size-1] != '\n') { - mb[2].ptr = "\n\\ No newline at end of file\n"; + if (size > 0 && rec[size - 1] != '\n') { + mb[2].ptr = (char *) "\n\\ No newline at end of file\n"; mb[2].size = strlen(mb[2].ptr); - i = 3; + i++; } - if (ecb->outf(ecb->priv, mb, i) < 0) { return -1; diff --git a/xdiff/xutils.h b/xdiff/xutils.h index ea38ee903f..08691a2447 100644 --- a/xdiff/xutils.h +++ b/xdiff/xutils.h @@ -24,6 +24,7 @@ #define XUTILS_H + long xdl_bogosqrt(long n); int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, xdemitcb_t *ecb);