From: Junio C Hamano Date: Fri, 26 Feb 2016 21:37:16 +0000 (-0800) Subject: Merge branch 'jk/tighten-alloc' X-Git-Tag: v2.8.0-rc0~19 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/11529ecec914d2f0d7575e6d443c2d5a6ff75424?hp=-c Merge branch 'jk/tighten-alloc' Update various codepaths to avoid manually-counted malloc(). * jk/tighten-alloc: (22 commits) ewah: convert to REALLOC_ARRAY, etc convert ewah/bitmap code to use xmalloc diff_populate_gitlink: use a strbuf transport_anonymize_url: use xstrfmt git-compat-util: drop mempcpy compat code sequencer: simplify memory allocation of get_message test-path-utils: fix normalize_path_copy output buffer size fetch-pack: simplify add_sought_entry fast-import: simplify allocation in start_packfile write_untracked_extension: use FLEX_ALLOC helper prepare_{git,shell}_cmd: use argv_array use st_add and st_mult for allocation size computation convert trivial cases to FLEX_ARRAY macros use xmallocz to avoid size arithmetic convert trivial cases to ALLOC_ARRAY convert manual allocations to argv_array argv-array: add detach function add helpers for allocating flex-array structs harden REALLOC_ARRAY and xcalloc against size_t overflow tree-diff: catch integer overflow in combine_diff_path allocation ... --- 11529ecec914d2f0d7575e6d443c2d5a6ff75424 diff --combined bisect.c index 06ec54e599,6d0793b9fc..7996c2907b --- a/bisect.c +++ b/bisect.c @@@ -440,7 -440,7 +440,7 @@@ static void read_bisect_paths(struct ar if (!fp) die_errno("Could not open file '%s'", filename); - while (strbuf_getline(&str, fp, '\n') != EOF) { + while (strbuf_getline_lf(&str, fp) != EOF) { strbuf_trim(&str); if (sq_dequote_to_argv_array(str.buf, array)) die("Badly quoted content in file '%s': %s", @@@ -668,7 -668,7 +668,7 @@@ static int is_expected_rev(const struc if (!fp) return 0; - if (strbuf_getline(&str, fp, '\n') != EOF) + if (strbuf_getline_lf(&str, fp) != EOF) res = !strcmp(str.buf, oid_to_hex(oid)); strbuf_release(&str); @@@ -708,10 -708,10 +708,10 @@@ static struct commit *get_commit_refere static struct commit **get_bad_and_good_commits(int *rev_nr) { - int len = 1 + good_revs.nr; - struct commit **rev = xmalloc(len * sizeof(*rev)); + struct commit **rev; int i, n = 0; + ALLOC_ARRAY(rev, 1 + good_revs.nr); rev[n++] = get_commit_reference(current_bad_oid->hash); for (i = 0; i < good_revs.nr; i++) rev[n++] = get_commit_reference(good_revs.sha1[i]); @@@ -914,9 -914,9 +914,9 @@@ void read_bisect_terms(const char **rea strerror(errno)); } } else { - strbuf_getline(&str, fp, '\n'); + strbuf_getline_lf(&str, fp); *read_bad = strbuf_detach(&str, NULL); - strbuf_getline(&str, fp, '\n'); + strbuf_getline_lf(&str, fp); *read_good = strbuf_detach(&str, NULL); } strbuf_release(&str); diff --combined builtin/apply.c index d61ac65dab,0db6d14cc2..42c610e2ec --- a/builtin/apply.c +++ b/builtin/apply.c @@@ -2632,7 -2632,7 +2632,7 @@@ static void update_image(struct image * insert_count = postimage->len; /* Adjust the contents */ - result = xmalloc(img->len + insert_count - remove_count + 1); + result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1)); memcpy(result, img->buf, applied_at); memcpy(result + applied_at, postimage->buf, postimage->len); memcpy(result + applied_at + postimage->len, @@@ -4464,6 -4464,16 +4464,6 @@@ static int option_parse_p(const struct return 0; } -static int option_parse_z(const struct option *opt, - const char *arg, int unset) -{ - if (unset) - line_termination = '\n'; - else - line_termination = 0; - return 0; -} - static int option_parse_space_change(const struct option *opt, const char *arg, int unset) { @@@ -4536,9 -4546,9 +4536,9 @@@ int cmd_apply(int argc, const char **ar N_( "attempt three-way merge if a patch does not apply")), OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, N_("build a temporary index based on embedded index information")), - { OPTION_CALLBACK, 'z', NULL, NULL, NULL, - N_("paths are separated with NUL character"), - PARSE_OPT_NOARG, option_parse_z }, + /* Think twice before adding "--nul" synonym to this */ + OPT_SET_INT('z', NULL, &line_termination, + N_("paths are separated with NUL character"), '\0'), OPT_INTEGER('C', NULL, &p_context, N_("ensure at least lines of context match")), { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"), diff --combined builtin/blame.c index 55bf5fae9d,e175d86e56..e982fb8137 --- a/builtin/blame.c +++ b/builtin/blame.c @@@ -28,7 -28,6 +28,7 @@@ #include "line-range.h" #include "line-log.h" #include "dir.h" +#include "progress.h" static char blame_usage[] = N_("git blame [] [] [] [--] "); @@@ -51,7 -50,6 +51,7 @@@ static int incremental static int xdl_opts; static int abbrev = -1; static int no_whole_file_rename; +static int show_progress; static struct date_mode blame_date_mode = { DATE_ISO8601 }; static size_t blame_date_width; @@@ -129,11 -127,6 +129,11 @@@ struct origin char path[FLEX_ARRAY]; }; +struct progress_info { + struct progress *progress; + int blamed_lines; +}; + static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen, xdl_emit_hunk_consume_func_t hunk_func, void *cb_data) { @@@ -466,13 -459,11 +466,11 @@@ static void queue_blames(struct scorebo static struct origin *make_origin(struct commit *commit, const char *path) { struct origin *o; - size_t pathlen = strlen(path) + 1; - o = xcalloc(1, sizeof(*o) + pathlen); + FLEX_ALLOC_STR(o, path, path); o->commit = commit; o->refcnt = 1; o->next = commit->util; commit->util = o; - memcpy(o->path, path, pathlen); /* includes NUL */ return o; } @@@ -1753,8 -1744,7 +1751,8 @@@ static int emit_one_suspect_detail(stru * The blame_entry is found to be guilty for the range. * Show it in incremental output. */ -static void found_guilty_entry(struct blame_entry *ent) +static void found_guilty_entry(struct blame_entry *ent, + struct progress_info *pi) { if (incremental) { struct origin *suspect = ent->suspect; @@@ -1766,8 -1756,6 +1764,8 @@@ write_filename_info(suspect->path); maybe_flush_or_die(stdout, "stdout"); } + pi->blamed_lines += ent->num_lines; + display_progress(pi->progress, pi->blamed_lines); } /* @@@ -1778,11 -1766,6 +1776,11 @@@ static void assign_blame(struct scorebo { struct rev_info *revs = sb->revs; struct commit *commit = prio_queue_get(&sb->commits); + struct progress_info pi = { NULL, 0 }; + + if (show_progress) + pi.progress = start_progress_delay(_("Blaming lines"), + sb->num_lines, 50, 1); while (commit) { struct blame_entry *ent; @@@ -1824,7 -1807,7 +1822,7 @@@ suspect->guilty = 1; for (;;) { struct blame_entry *next = ent->next; - found_guilty_entry(ent); + found_guilty_entry(ent, &pi); if (next) { ent = next; continue; @@@ -1840,8 -1823,6 +1838,8 @@@ if (DEBUG) /* sanity */ sanity_check_refcnt(sb); } + + stop_progress(&pi.progress); } static const char *format_time(unsigned long time, const char *tz_str, @@@ -2059,7 -2040,8 +2057,8 @@@ static int prepare_lines(struct scorebo for (p = buf; p < end; p = get_next_line(p, end)) num++; - sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1)); + ALLOC_ARRAY(sb->lineno, num + 1); + lineno = sb->lineno; for (p = buf; p < end; p = get_next_line(p, end)) *lineno++ = p - buf; @@@ -2409,6 -2391,11 +2408,6 @@@ static struct commit *fake_working_tree ce->ce_mode = create_ce_mode(mode); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); - /* - * We are not going to write this out, so this does not matter - * right now, but someday we might optimize diff-index --cached - * with cache-tree information. - */ cache_tree_invalidate_path(&the_index, path); return commit; @@@ -2532,7 -2519,6 +2531,7 @@@ int cmd_blame(int argc, const char **ar OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")), OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")), + OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")), OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE), OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME), OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER), @@@ -2568,7 -2554,6 +2567,7 @@@ save_commit_buffer = 0; dashdash_pos = 0; + show_progress = -1; parse_options_start(&ctx, argc, argv, prefix, options, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); @@@ -2593,13 -2578,6 +2592,13 @@@ parse_done DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES); argc = parse_options_end(&ctx); + if (incremental || (output_option & OUTPUT_PORCELAIN)) { + if (show_progress > 0) + die("--progress can't be used with --incremental or porcelain formats"); + show_progress = 0; + } else if (show_progress < 0) + show_progress = isatty(2); + if (0 < abbrev) /* one more abbrev length is needed for the boundary commit */ abbrev++; @@@ -2849,11 -2827,11 +2848,11 @@@ read_mailmap(&mailmap, NULL); + assign_blame(&sb, opt); + if (!incremental) setup_pager(); - assign_blame(&sb, opt); - free(final_commit_name); if (incremental) diff --combined builtin/clean.c index 7b08237480,fb1824ce95..0371010afb --- a/builtin/clean.c +++ b/builtin/clean.c @@@ -543,7 -543,7 +543,7 @@@ static int *list_and_choose(struct menu int eof = 0; int i; - chosen = xmalloc(sizeof(int) * stuff->nr); + ALLOC_ARRAY(chosen, stuff->nr); /* set chosen as uninitialized */ for (i = 0; i < stuff->nr; i++) chosen[i] = -1; @@@ -570,7 -570,7 +570,7 @@@ clean_get_color(CLEAN_COLOR_RESET)); } - if (strbuf_getline(&choice, stdin, '\n') != EOF) { + if (strbuf_getline_lf(&choice, stdin) != EOF) { strbuf_trim(&choice); } else { eof = 1; @@@ -615,7 -615,7 +615,7 @@@ nr += chosen[i]; } - result = xcalloc(nr + 1, sizeof(int)); + result = xcalloc(st_add(nr, 1), sizeof(int)); for (i = 0; i < stuff->nr && j < nr; i++) { if (chosen[i]) result[j++] = i; @@@ -652,7 -652,7 +652,7 @@@ static int filter_by_patterns_cmd(void clean_print_color(CLEAN_COLOR_PROMPT); printf(_("Input ignore patterns>> ")); clean_print_color(CLEAN_COLOR_RESET); - if (strbuf_getline(&confirm, stdin, '\n') != EOF) + if (strbuf_getline_lf(&confirm, stdin) != EOF) strbuf_trim(&confirm); else putchar('\n'); @@@ -750,7 -750,7 +750,7 @@@ static int ask_each_cmd(void qname = quote_path_relative(item->string, NULL, &buf); /* TRANSLATORS: Make sure to keep [y/N] as is */ printf(_("Remove %s [y/N]? "), qname); - if (strbuf_getline(&confirm, stdin, '\n') != EOF) { + if (strbuf_getline_lf(&confirm, stdin) != EOF) { strbuf_trim(&confirm); } else { putchar('\n'); diff --combined builtin/fetch-pack.c index 9b2a514e1d,7d5914f921..79a611fda1 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@@ -10,33 -10,24 +10,24 @@@ static const char fetch_pack_usage[] "[--include-tag] [--upload-pack=] [--depth=] " "[--no-progress] [--diag-url] [-v] [:] [...]"; - static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc, - const char *name, int namelen) + static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, + const char *name) { - struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1); + struct ref *ref; struct object_id oid; - const int chunksz = GIT_SHA1_HEXSZ + 1; - if (namelen > chunksz && name[chunksz - 1] == ' ' && - !get_oid_hex(name, &oid)) { - oidcpy(&ref->old_oid, &oid); - name += chunksz; - namelen -= chunksz; - } + if (!get_oid_hex(name, &oid) && name[GIT_SHA1_HEXSZ] == ' ') + name += GIT_SHA1_HEXSZ + 1; + else + oidclr(&oid); - memcpy(ref->name, name, namelen); - ref->name[namelen] = '\0'; + ref = alloc_ref(name); + oidcpy(&ref->old_oid, &oid); (*nr)++; ALLOC_GROW(*sought, *nr, *alloc); (*sought)[*nr - 1] = ref; } - static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, - const char *string) - { - add_sought_entry_mem(sought, nr, alloc, string, strlen(string)); - } - int cmd_fetch_pack(int argc, const char **argv, const char *prefix) { int i, ret; @@@ -158,7 -149,7 +149,7 @@@ else { /* read from stdin one ref per line, until EOF */ struct strbuf line = STRBUF_INIT; - while (strbuf_getline(&line, stdin, '\n') != EOF) + while (strbuf_getline_lf(&line, stdin) != EOF) add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf); strbuf_release(&line); } diff --combined builtin/fetch.c index 48c45ea5e3,683f08ec91..e4639d8eb1 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@@ -37,8 -37,6 +37,8 @@@ static int prune = -1; /* unspecified * static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int tags = TAGS_DEFAULT, unshallow, update_shallow; +static int max_children = 1; +static enum transport_family family; static const char *depth; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; @@@ -101,8 -99,6 +101,8 @@@ static struct option builtin_fetch_opti N_("fetch all tags and associated objects"), TAGS_SET), OPT_SET_INT('n', NULL, &tags, N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), + OPT_INTEGER('j', "jobs", &max_children, + N_("number of submodules fetched in parallel")), OPT_BOOL('p', "prune", &prune, N_("prune remote-tracking branches no longer on remote")), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"), @@@ -128,10 -124,6 +128,10 @@@ N_("accept refs that update .git/shallow")), { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"), N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg }, + OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), + TRANSPORT_FAMILY_IPV4), + OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), + TRANSPORT_FAMILY_IPV6), OPT_END() }; @@@ -869,7 -861,6 +869,7 @@@ static struct transport *prepare_transp struct transport *transport; transport = transport_get(remote, NULL); transport_set_verbosity(transport, verbosity, progress); + transport->family = family; if (upload_pack) set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack); if (keep) @@@ -1022,9 -1013,10 +1022,9 @@@ static int add_remote_or_group(const ch git_config(get_remote_group, &g); if (list->nr == prev_nr) { - struct remote *remote; - if (!remote_is_configured(name)) + struct remote *remote = remote_get(name); + if (!remote_is_configured(remote)) return 0; - remote = remote_get(name); string_list_append(list, remote->name); } return 1; @@@ -1115,7 -1107,7 +1115,7 @@@ static int fetch_one(struct remote *rem if (argc > 0) { int j = 0; int i; - refs = xcalloc(argc + 1, sizeof(const char *)); + refs = xcalloc(st_add(argc, 1), sizeof(const char *)); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "tag")) { i++; @@@ -1221,8 -1213,7 +1221,8 @@@ int cmd_fetch(int argc, const char **ar result = fetch_populated_submodules(&options, submodule_prefix, recurse_submodules, - verbosity < 0); + verbosity < 0, + max_children); argv_array_clear(&options); } diff --combined builtin/grep.c index 8c516a9543,95ddf96d1e..aa7435f380 --- a/builtin/grep.c +++ b/builtin/grep.c @@@ -24,11 -24,11 +24,11 @@@ static char const * const grep_usage[] NULL }; -static int use_threads = 1; +#define GREP_NUM_THREADS_DEFAULT 8 +static int num_threads; #ifndef NO_PTHREADS -#define THREADS 8 -static pthread_t threads[THREADS]; +static pthread_t *threads; /* We use one producer thread and THREADS consumer * threads. The producer adds struct work_items to 'todo' and the @@@ -63,13 -63,13 +63,13 @@@ static pthread_mutex_t grep_mutex static inline void grep_lock(void) { - if (use_threads) + if (num_threads) pthread_mutex_lock(&grep_mutex); } static inline void grep_unlock(void) { - if (use_threads) + if (num_threads) pthread_mutex_unlock(&grep_mutex); } @@@ -206,8 -206,7 +206,8 @@@ static void start_threads(struct grep_o strbuf_init(&todo[i].out, 0); } - for (i = 0; i < ARRAY_SIZE(threads); i++) { + threads = xcalloc(num_threads, sizeof(*threads)); + for (i = 0; i < num_threads; i++) { int err; struct grep_opt *o = grep_opt_dup(opt); o->output = strbuf_out; @@@ -239,14 -238,12 +239,14 @@@ static int wait_all(void pthread_cond_broadcast(&cond_add); grep_unlock(); - for (i = 0; i < ARRAY_SIZE(threads); i++) { + for (i = 0; i < num_threads; i++) { void *h; pthread_join(threads[i], &h); hit |= (int) (intptr_t) h; } + free(threads); + pthread_mutex_destroy(&grep_mutex); pthread_mutex_destroy(&grep_read_mutex); pthread_mutex_destroy(&grep_attr_mutex); @@@ -270,14 -267,6 +270,14 @@@ static int grep_cmd_config(const char * int st = grep_config(var, value, cb); if (git_color_default_config(var, value, cb) < 0) st = -1; + + if (!strcmp(var, "grep.threads")) { + num_threads = git_config_int(var, value); + if (num_threads < 0) + die(_("invalid number of threads specified (%d) for %s"), + num_threads, var); + } + return st; } @@@ -305,7 -294,7 +305,7 @@@ static int grep_sha1(struct grep_opt *o } #ifndef NO_PTHREADS - if (use_threads) { + if (num_threads) { add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); strbuf_release(&pathbuf); return 0; @@@ -334,7 -323,7 +334,7 @@@ static int grep_file(struct grep_opt *o strbuf_addstr(&buf, filename); #ifndef NO_PTHREADS - if (use_threads) { + if (num_threads) { add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename); strbuf_release(&buf); return 0; @@@ -365,17 -354,17 +365,17 @@@ static void append_path(struct grep_op static void run_pager(struct grep_opt *opt, const char *prefix) { struct string_list *path_list = opt->output_priv; - const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1)); + struct child_process child = CHILD_PROCESS_INIT; int i, status; for (i = 0; i < path_list->nr; i++) - argv[i] = path_list->items[i].string; - argv[path_list->nr] = NULL; + argv_array_push(&child.args, path_list->items[i].string); + child.dir = prefix; + child.use_shell = 1; - status = run_command_v_opt_cd_env(argv, RUN_USING_SHELL, prefix, NULL); + status = run_command(&child); if (status) exit(status); - free(argv); } static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached) @@@ -386,7 -375,7 +386,7 @@@ for (nr = 0; nr < active_nr; nr++) { const struct cache_entry *ce = active_cache[nr]; - if (!S_ISREG(ce->ce_mode)) + if (!S_ISREG(ce->ce_mode) || ce_intent_to_add(ce)) continue; if (!ce_path_match(ce, pathspec, NULL)) continue; @@@ -573,7 -562,7 +573,7 @@@ static int file_callback(const struct o patterns = from_stdin ? stdin : fopen(arg, "r"); if (!patterns) die_errno(_("cannot open '%s'"), arg); - while (strbuf_getline(&sb, patterns, '\n') == 0) { + while (strbuf_getline(&sb, patterns) == 0) { /* ignore empty line like grep does */ if (sb.len == 0) continue; @@@ -708,8 -697,6 +708,8 @@@ int cmd_grep(int argc, const char **arg N_("show context lines before matches")), OPT_INTEGER('A', "after-context", &opt.post_context, N_("show context lines after matches")), + OPT_INTEGER(0, "threads", &num_threads, + N_("use worker threads")), OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), context_callback), OPT_BOOL('p', "show-function", &opt.funcname, @@@ -768,15 -755,9 +768,15 @@@ PARSE_OPT_STOP_AT_NON_OPTION); grep_commit_pattern_type(pattern_type_arg, &opt); - if (use_index && !startup_info->have_repository) - /* die the same way as if we did it at the beginning */ - setup_git_directory(); + if (use_index && !startup_info->have_repository) { + int fallback = 0; + git_config_get_bool("grep.fallbacktonoindex", &fallback); + if (fallback) + use_index = 0; + else + /* die the same way as if we did it at the beginning */ + setup_git_directory(); + } /* * skip a -- separator; we know it cannot be @@@ -805,6 -786,7 +805,6 @@@ opt.output_priv = &path_list; opt.output = append_path; string_list_append(&path_list, show_in_pager); - use_threads = 0; } if (!opt.pattern_list) @@@ -835,18 -817,14 +835,18 @@@ } #ifndef NO_PTHREADS - if (list.nr || cached || online_cpus() == 1) - use_threads = 0; + if (list.nr || cached || show_in_pager) + num_threads = 0; + else if (num_threads == 0) + num_threads = GREP_NUM_THREADS_DEFAULT; + else if (num_threads < 0) + die(_("invalid number of threads specified (%d)"), num_threads); #else - use_threads = 0; + num_threads = 0; #endif #ifndef NO_PTHREADS - if (use_threads) { + if (num_threads) { if (!(opt.name_only || opt.unmatch_name_only || opt.count) && (opt.pre_context || opt.post_context || opt.file_break || opt.funcbody)) @@@ -916,7 -894,7 +916,7 @@@ hit = grep_objects(&opt, &pathspec, &list); } - if (use_threads) + if (num_threads) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); diff --combined builtin/mktree.c index a237caacfd,b0aab65353..4282b62c59 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@@ -19,16 -19,17 +19,17 @@@ static int alloc, used static void append_to_tree(unsigned mode, unsigned char *sha1, char *path) { struct treeent *ent; - int len = strlen(path); + size_t len = strlen(path); if (strchr(path, '/')) die("path %s contains slash", path); - ALLOC_GROW(entries, used + 1, alloc); - ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1); + FLEX_ALLOC_MEM(ent, name, path, len); ent->mode = mode; ent->len = len; hashcpy(ent->sha1, sha1); - memcpy(ent->name, path, len+1); + + ALLOC_GROW(entries, used + 1, alloc); + entries[used++] = ent; } static int ent_compare(const void *a_, const void *b_) @@@ -65,7 -66,7 +66,7 @@@ static const char *mktree_usage[] = NULL }; -static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing) +static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_missing) { char *ptr, *ntr; unsigned mode; @@@ -97,7 -98,7 +98,7 @@@ *ntr++ = 0; /* now at the beginning of SHA1 */ path = ntr + 41; /* at the beginning of name */ - if (line_termination && path[0] == '"') { + if (!nul_term_line && path[0] == '"') { struct strbuf p_uq = STRBUF_INIT; if (unquote_c_style(&p_uq, path, NULL)) die("invalid quoting"); @@@ -141,25 -142,23 +142,25 @@@ int cmd_mktree(int ac, const char **av { struct strbuf sb = STRBUF_INIT; unsigned char sha1[20]; - int line_termination = '\n'; + int nul_term_line = 0; int allow_missing = 0; int is_batch_mode = 0; int got_eof = 0; + strbuf_getline_fn getline_fn; const struct option option[] = { - OPT_SET_INT('z', NULL, &line_termination, N_("input is NUL terminated"), '\0'), + OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")), OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1), OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1), OPT_END() }; ac = parse_options(ac, av, prefix, option, mktree_usage, 0); + getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; while (!got_eof) { while (1) { - if (strbuf_getline(&sb, stdin, line_termination) == EOF) { + if (getline_fn(&sb, stdin) == EOF) { got_eof = 1; break; } @@@ -169,7 -168,7 +170,7 @@@ break; die("input format error: (blank line only valid in batch mode)"); } - mktree_line(sb.buf, sb.len, line_termination, allow_missing); + mktree_line(sb.buf, sb.len, nul_term_line, allow_missing); } if (is_batch_mode && got_eof && used < 1) { /* diff --combined builtin/pack-objects.c index a6609f19ff,b4f1fa6d33..a27de5b323 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@@ -624,7 -624,7 +624,7 @@@ static struct object_entry **compute_wr { unsigned int i, wo_end, last_untagged; - struct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo)); + struct object_entry **wo; struct object_entry *objects = to_pack.objects; for (i = 0; i < to_pack.nr_objects; i++) { @@@ -657,6 -657,7 +657,7 @@@ * Give the objects in the original recency order until * we see a tagged tip. */ + ALLOC_ARRAY(wo, to_pack.nr_objects); for (i = wo_end = 0; i < to_pack.nr_objects; i++) { if (objects[i].tagged) break; @@@ -769,7 -770,7 +770,7 @@@ static void write_pack_file(void if (progress > pack_to_stdout) progress_state = start_progress(_("Writing objects"), nr_result); - written_list = xmalloc(to_pack.nr_objects * sizeof(*written_list)); + ALLOC_ARRAY(written_list, to_pack.nr_objects); write_order = compute_write_order(); do { @@@ -2129,7 -2130,7 +2130,7 @@@ static void prepare_pack(int window, in if (!to_pack.nr_objects || !window || !depth) return; - delta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list)); + ALLOC_ARRAY(delta_list, to_pack.nr_objects); nr_deltas = n = 0; for (i = 0; i < to_pack.nr_objects; i++) { @@@ -2284,11 -2285,21 +2285,11 @@@ static void show_commit(struct commit * index_commit_for_bitmap(commit); } -static void show_object(struct object *obj, - const struct name_path *path, const char *last, - void *data) +static void show_object(struct object *obj, const char *name, void *data) { - char *name = path_name(path, last); - add_preferred_base_object(name); add_object_entry(obj->oid.hash, obj->type, name, 0); obj->flags |= OBJECT_ADDED; - - /* - * We will have generated the hash from the name, - * but not saved a pointer to it - we can free it - */ - free((char *)name); } static void show_edge(struct commit *commit) @@@ -2470,7 -2481,8 +2471,7 @@@ static int get_object_list_from_bitmap( } static void record_recent_object(struct object *obj, - const struct name_path *path, - const char *last, + const char *name, void *data) { sha1_array_append(&recent_objects, obj->oid.hash); diff --combined builtin/worktree.c index 20cf67a549,0a45710be8..38b56096bd --- a/builtin/worktree.c +++ b/builtin/worktree.c @@@ -52,7 -52,7 +52,7 @@@ static int prune_worktree(const char *i return 1; } len = st.st_size; - path = xmalloc(len + 1); + path = xmallocz(len); read_in_full(fd, path, len); close(fd); while (len && (path[len - 1] == '\n' || path[len - 1] == '\r')) @@@ -201,7 -201,9 +201,7 @@@ static int add_worktree(const char *pat die(_("'%s' already exists"), path); /* is 'refname' a branch or commit? */ - if (opts->force_new_branch) /* definitely a branch */ - ; - else if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && + if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && ref_exists(symref.buf)) { /* it's a branch */ if (!opts->force) die_if_checked_out(symref.buf); @@@ -334,18 -336,9 +334,18 @@@ static int add(int ac, const char **av branch = ac < 2 ? "HEAD" : av[1]; opts.force_new_branch = !!new_branch_force; - if (opts.force_new_branch) + if (opts.force_new_branch) { + struct strbuf symref = STRBUF_INIT; + opts.new_branch = new_branch_force; + if (!opts.force && + !strbuf_check_branch_ref(&symref, opts.new_branch) && + ref_exists(symref.buf)) + die_if_checked_out(symref.buf); + strbuf_release(&symref); + } + if (ac < 2 && !opts.new_branch && !opts.detach) { int n; const char *s = worktree_basename(path, &n); diff --combined cache-tree.c index 20ee7b52df,1fbe79a003..3ebf9c3aa4 --- a/cache-tree.c +++ b/cache-tree.c @@@ -79,11 -79,9 +79,9 @@@ static struct cache_tree_sub *find_subt ALLOC_GROW(it->down, it->subtree_nr + 1, it->subtree_alloc); it->subtree_nr++; - down = xmalloc(sizeof(*down) + pathlen + 1); + FLEX_ALLOC_MEM(down, name, path, pathlen); down->cache_tree = NULL; down->namelen = pathlen; - memcpy(down->name, path, pathlen); - down->name[pathlen] = 0; if (pos < it->subtree_nr) memmove(it->down + pos + 1, @@@ -377,7 -375,7 +375,7 @@@ static int update_one(struct cache_tre * they are not part of generated trees. Invalidate up * to root to force cache-tree users to read elsewhere. */ - if (ce->ce_flags & CE_INTENT_TO_ADD) { + if (ce_intent_to_add(ce)) { to_invalidate = 1; continue; } diff --combined compat/mingw.c index fbe69b874b,ae16d089ad..cfedcf9656 --- a/compat/mingw.c +++ b/compat/mingw.c @@@ -6,8 -6,6 +6,8 @@@ #include "../run-command.h" #include "../cache.h" +#define HCAST(type, handle) ((type)(intptr_t)handle) + static const int delay[] = { 0, 1, 10, 20, 40 }; int err_win_to_posix(DWORD winerr) @@@ -454,39 -452,6 +454,39 @@@ static inline time_t filetime_to_time_t return (time_t)(filetime_to_hnsec(ft) / 10000000); } +/** + * Verifies that safe_create_leading_directories() would succeed. + */ +static int has_valid_directory_prefix(wchar_t *wfilename) +{ + int n = wcslen(wfilename); + + while (n > 0) { + wchar_t c = wfilename[--n]; + DWORD attributes; + + if (!is_dir_sep(c)) + continue; + + wfilename[n] = L'\0'; + attributes = GetFileAttributesW(wfilename); + wfilename[n] = c; + if (attributes == FILE_ATTRIBUTE_DIRECTORY || + attributes == FILE_ATTRIBUTE_DEVICE) + return 1; + if (attributes == INVALID_FILE_ATTRIBUTES) + switch (GetLastError()) { + case ERROR_PATH_NOT_FOUND: + continue; + case ERROR_FILE_NOT_FOUND: + /* This implies parent directory exists. */ + return 1; + } + return 0; + } + return 1; +} + /* We keep the do_lstat code in a separate function to avoid recursion. * When a path ends with a slash, the stat will fail with ENOENT. In * this case, we strip the trailing slashes and stat again. @@@ -547,12 -512,6 +547,12 @@@ static int do_lstat(int follow, const c case ERROR_NOT_ENOUGH_MEMORY: errno = ENOMEM; break; + case ERROR_PATH_NOT_FOUND: + if (!has_valid_directory_prefix(wfilename)) { + errno = ENOTDIR; + break; + } + /* fallthru */ default: errno = ENOENT; break; @@@ -732,13 -691,13 +732,13 @@@ int pipe(int filedes[2] errno = err_win_to_posix(GetLastError()); return -1; } - filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT); + filedes[0] = _open_osfhandle(HCAST(int, h[0]), O_NOINHERIT); if (filedes[0] < 0) { CloseHandle(h[0]); CloseHandle(h[1]); return -1; } - filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT); + filedes[1] = _open_osfhandle(HCAST(int, h[1]), O_NOINHERIT); if (filedes[1] < 0) { close(filedes[0]); CloseHandle(h[1]); @@@ -810,7 -769,7 +810,7 @@@ static const char *quote_arg(const cha return arg; /* insert \ where necessary */ - d = q = xmalloc(len+n+3); + d = q = xmalloc(st_add3(len, n, 3)); *d++ = '"'; while (*arg) { if (*arg == '"') @@@ -893,7 -852,7 +893,7 @@@ static char **get_path_split(void if (!n) return NULL; - path = xmalloc((n+1)*sizeof(char *)); + ALLOC_ARRAY(path, n + 1); p = envpath; i = 0; do { @@@ -978,7 -937,7 +978,7 @@@ static wchar_t *make_environment_block( i++; /* copy the environment, leaving space for changes */ - tmpenv = xmalloc((size + i) * sizeof(char*)); + ALLOC_ARRAY(tmpenv, size + i); memcpy(tmpenv, environ, size * sizeof(char*)); /* merge supplied environment changes into the temporary environment */ @@@ -1069,7 -1028,7 +1069,7 @@@ static pid_t mingw_spawnve_fd(const cha free(quoted); } - wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t)); + wargs = xmalloc_array(st_add(st_mult(2, args.len), 1), sizeof(wchar_t)); xutftowcs(wargs, args.buf, 2 * args.len + 1); strbuf_release(&args); @@@ -1168,7 -1127,7 +1168,7 @@@ static int try_shell_exec(const char *c int argc = 0; const char **argv2; while (argv[argc]) argc++; - argv2 = xmalloc(sizeof(*argv) * (argc+1)); + ALLOC_ARRAY(argv2, argc + 1); argv2[0] = (char *)cmd; /* full path to the script file */ memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc); pid = mingw_spawnv(prog, argv2, 1); @@@ -1642,12 -1601,7 +1642,12 @@@ repeat if (gle == ERROR_ACCESS_DENIED && (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) { if (attrs & FILE_ATTRIBUTE_DIRECTORY) { - errno = EISDIR; + DWORD attrsold = GetFileAttributesW(wpold); + if (attrsold == INVALID_FILE_ATTRIBUTES || + !(attrsold & FILE_ATTRIBUTE_DIRECTORY)) + errno = EISDIR; + else if (!_wrmdir(wpnew)) + goto repeat; return -1; } if ((attrs & FILE_ATTRIBUTE_READONLY) && @@@ -1892,8 -1846,7 +1892,8 @@@ void mingw_open_html(const char *unixpa die("cannot run browser"); printf("Launching default browser to display HTML ...\n"); - r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL); + r = HCAST(int, ShellExecute(NULL, "open", htmlpath, + NULL, "\\", SW_SHOWNORMAL)); FreeLibrary(shell32); /* see the MSDN documentation referring to the result codes here */ if (r <= 32) { @@@ -2091,37 -2044,6 +2091,37 @@@ int xwcstoutf(char *utf, const wchar_t return -1; } +static void setup_windows_environment() +{ + char *tmp = getenv("TMPDIR"); + + /* on Windows it is TMP and TEMP */ + if (!tmp) { + if (!(tmp = getenv("TMP"))) + tmp = getenv("TEMP"); + if (tmp) { + setenv("TMPDIR", tmp, 1); + tmp = getenv("TMPDIR"); + } + } + + if (tmp) { + /* + * Convert all dir separators to forward slashes, + * to help shell commands called from the Git + * executable (by not mistaking the dir separators + * for escape characters). + */ + for (; *tmp; tmp++) + if (*tmp == '\\') + *tmp = '/'; + } + + /* simulate TERM to enable auto-color (see color.c) */ + if (!getenv("TERM")) + setenv("TERM", "cygwin", 1); +} + /* * Disable MSVCRT command line wildcard expansion (__getmainargs called from * mingw startup code, see init.c in mingw runtime). @@@ -2200,7 -2122,19 +2200,7 @@@ void mingw_startup( qsort(environ, i, sizeof(char*), compareenv); /* fix Windows specific environment settings */ - - /* on Windows it is TMP and TEMP */ - if (!mingw_getenv("TMPDIR")) { - const char *tmp = mingw_getenv("TMP"); - if (!tmp) - tmp = mingw_getenv("TEMP"); - if (tmp) - setenv("TMPDIR", tmp, 1); - } - - /* simulate TERM to enable auto-color (see color.c) */ - if (!getenv("TERM")) - setenv("TERM", "cygwin", 1); + setup_windows_environment(); /* initialize critical section for waitpid pinfo_t list */ InitializeCriticalSection(&pinfo_cs); diff --combined config.c index b95ac3a9cd,ba8fd13765..e7b589a3a8 --- a/config.c +++ b/config.c @@@ -1594,30 -1594,6 +1594,30 @@@ int git_config_get_pathname(const char return ret; } +int git_config_get_untracked_cache(void) +{ + int val = -1; + const char *v; + + /* Hack for test programs like test-dump-untracked-cache */ + if (ignore_untracked_cache_config) + return -1; + + if (!git_config_get_maybe_bool("core.untrackedcache", &val)) + return val; + + if (!git_config_get_value("core.untrackedcache", &v)) { + if (!strcasecmp(v, "keep")) + return -1; + + error("unknown core.untrackedCache value '%s'; " + "using 'keep' default value", v); + return -1; + } + + return -1; /* default value */ +} + NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr) { @@@ -1902,7 -1878,7 +1902,7 @@@ static int git_config_parse_key_1(cons * Validate the key and while at it, lower case it for matching. */ if (store_key) - *store_key = xmalloc(strlen(key) + 1); + *store_key = xmallocz(strlen(key)); dot = 0; for (i = 0; key[i]; i++) { @@@ -1926,8 -1902,6 +1926,6 @@@ if (store_key) (*store_key)[i] = c; } - if (store_key) - (*store_key)[i] = 0; return 0; diff --combined daemon.c index 46b411c7d9,1e258ac8f9..8d45c336f5 --- a/daemon.c +++ b/daemon.c @@@ -424,7 -424,7 +424,7 @@@ static void copy_to_log(int fd return; } - while (strbuf_getline(&line, fp, '\n') != EOF) { + while (strbuf_getline_lf(&line, fp) != EOF) { logerror("%s", line.buf); strbuf_setlen(&line, 0); } @@@ -808,7 -808,7 +808,7 @@@ static void check_dead_children(void cradle = &blanket->next; } - static char **cld_argv; + static struct argv_array cld_argv = ARGV_ARRAY_INIT; static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen) { struct child_process cld = CHILD_PROCESS_INIT; @@@ -842,7 -842,7 +842,7 @@@ #endif } - cld.argv = (const char **)cld_argv; + cld.argv = cld_argv.argv; cld.in = incoming; cld.out = dup(incoming); @@@ -1374,12 -1374,10 +1374,10 @@@ int main(int argc, char **argv write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid()); /* prepare argv for serving-processes */ - cld_argv = xmalloc(sizeof (char *) * (argc + 2)); - cld_argv[0] = argv[0]; /* git-daemon */ - cld_argv[1] = "--serve"; + argv_array_push(&cld_argv, argv[0]); /* git-daemon */ + argv_array_push(&cld_argv, "--serve"); for (i = 1; i < argc; ++i) - cld_argv[i+1] = argv[i]; - cld_argv[argc+1] = NULL; + argv_array_push(&cld_argv, argv[i]); return serve(&listen_addr, listen_port, cred); } diff --combined diff.c index a088e269b4,a70ec6ef1a..059123c5dc --- a/diff.c +++ b/diff.c @@@ -2607,12 -2607,9 +2607,9 @@@ static void builtin_checkdiff(const cha struct diff_filespec *alloc_filespec(const char *path) { - int namelen = strlen(path); - struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1); + struct diff_filespec *spec; - memset(spec, 0, sizeof(*spec)); - spec->path = (char *)(spec + 1); - memcpy(spec->path, path, namelen+1); + FLEXPTR_ALLOC_STR(spec, path, path); spec->count = 1; spec->is_binary = -1; return spec; @@@ -2707,21 -2704,21 +2704,21 @@@ static int reuse_worktree_file(const ch static int diff_populate_gitlink(struct diff_filespec *s, int size_only) { - int len; - char *data = xmalloc(100), *dirty = ""; + struct strbuf buf = STRBUF_INIT; + char *dirty = ""; /* Are we looking at the work tree? */ if (s->dirty_submodule) dirty = "-dirty"; - len = snprintf(data, 100, - "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty); - s->data = data; - s->size = len; - s->should_free = 1; + strbuf_addf(&buf, "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty); + s->size = buf.len; if (size_only) { s->data = NULL; - free(data); + strbuf_release(&buf); + } else { + s->data = strbuf_detach(&buf, NULL); + s->should_free = 1; } return 0; } @@@ -5085,7 -5082,7 +5082,7 @@@ size_t fill_textconv(struct userdiff_dr { size_t size; - if (!driver || !driver->textconv) { + if (!driver) { if (!DIFF_FILE_VALID(df)) { *outbuf = ""; return 0; @@@ -5096,9 -5093,6 +5093,9 @@@ return df->size; } + if (!driver->textconv) + die("BUG: fill_textconv called with non-textconv driver"); + if (driver->textconv_cache && df->sha1_valid) { *outbuf = notes_cache_get(driver->textconv_cache, df->sha1, &size); diff --combined diff.h index 4505b4d91d,beafbbdec7..e7d68edaf9 --- a/diff.h +++ b/diff.h @@@ -222,8 -222,8 +222,8 @@@ struct combine_diff_path } parent[FLEX_ARRAY]; }; #define combine_diff_path_size(n, l) \ - (sizeof(struct combine_diff_path) + \ - sizeof(struct combine_diff_parent) * (n) + (l) + 1) + st_add4(sizeof(struct combine_diff_path), (l), 1, \ + st_mult(sizeof(struct combine_diff_parent), (n))) extern void show_combined_diff(struct combine_diff_path *elem, int num_parent, int dense, struct rev_info *); @@@ -349,26 -349,10 +349,26 @@@ extern void diff_no_index(struct rev_in extern int index_differs_from(const char *def, int diff_flags); +/* + * Fill the contents of the filespec "df", respecting any textconv defined by + * its userdiff driver. The "driver" parameter must come from a + * previous call to get_textconv(), and therefore should either be NULL or have + * textconv enabled. + * + * Note that the memory ownership of the resulting buffer depends on whether + * the driver field is NULL. If it is, then the memory belongs to the filespec + * struct. If it is non-NULL, then "outbuf" points to a newly allocated buffer + * that should be freed by the caller. + */ extern size_t fill_textconv(struct userdiff_driver *driver, struct diff_filespec *df, char **outbuf); +/* + * Look up the userdiff driver for the given filespec, and return it if + * and only if it has textconv enabled (otherwise return NULL). The result + * can be passed to fill_textconv(). + */ extern struct userdiff_driver *get_textconv(struct diff_filespec *one); extern int parse_rename_score(const char **cp_p); diff --combined dir.c index 552af23704,3087b7eb2e..69e0be6aa2 --- a/dir.c +++ b/dir.c @@@ -53,8 -53,6 +53,8 @@@ static enum path_treatment read_directo int check_only, const struct path_simplify *simplify); static int get_dtype(struct dirent *de, const char *path, int len); +static struct trace_key trace_exclude = TRACE_KEY_INIT(EXCLUDE); + /* helper string functions with support for the ignore_case flag */ int strcmp_icase(const char *a, const char *b) { @@@ -505,12 -503,7 +505,7 @@@ void add_exclude(const char *string, co parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen); if (flags & EXC_FLAG_MUSTBEDIR) { - char *s; - x = xmalloc(sizeof(*x) + patternlen + 1); - s = (char *)(x+1); - memcpy(s, string, patternlen); - s[patternlen] = '\0'; - x->pattern = s; + FLEXPTR_ALLOC_MEM(x, pattern, string, patternlen); } else { x = xmalloc(sizeof(*x)); x->pattern = string; @@@ -521,7 -514,6 +516,7 @@@ x->baselen = baselen; x->flags = flags; x->srcpos = srcpos; + string_list_init(&x->sticky_paths, 1); ALLOC_GROW(el->excludes, el->nr + 1, el->alloc); el->excludes[el->nr++] = x; x->el = el; @@@ -562,10 -554,8 +557,10 @@@ void clear_exclude_list(struct exclude_ { int i; - for (i = 0; i < el->nr; i++) + for (i = 0; i < el->nr; i++) { + string_list_clear(&el->excludes[i]->sticky_paths, 0); free(el->excludes[i]); + } free(el->excludes); free(el->filebuf); @@@ -630,10 -620,7 +625,7 @@@ static struct untracked_cache_dir *look } uc->dir_created++; - d = xmalloc(sizeof(*d) + len + 1); - memset(d, 0, sizeof(*d)); - memcpy(d->name, name, len); - d->name[len] = '\0'; + FLEX_ALLOC_MEM(d, name, name, len); ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc); memmove(dir->dirs + first + 1, dir->dirs + first, @@@ -702,7 -689,7 +694,7 @@@ static int add_excludes(const char *fna return 0; } if (buf[size-1] != '\n') { - buf = xrealloc(buf, size+1); + buf = xrealloc(buf, st_add(size, 1)); buf[size++] = '\n'; } } else { @@@ -716,7 -703,7 +708,7 @@@ close(fd); return 0; } - buf = xmalloc(size+1); + buf = xmallocz(size); if (read_in_full(fd, buf, size) != size) { free(buf); close(fd); @@@ -883,7 -870,7 +875,7 @@@ int match_pathname(const char *pathname * then our prefix match is all we need; we * do not need to call fnmatch at all. */ - if (!patternlen && !namelen) + if (!patternlen && (!namelen || *name == '/')) return 1; } @@@ -892,113 -879,6 +884,113 @@@ WM_PATHNAME) == 0; } +static void add_sticky(struct exclude *exc, const char *pathname, int pathlen) +{ + struct strbuf sb = STRBUF_INIT; + int i; + + for (i = exc->sticky_paths.nr - 1; i >= 0; i--) { + const char *sticky = exc->sticky_paths.items[i].string; + int len = strlen(sticky); + + if (pathlen < len && sticky[pathlen] == '/' && + !strncmp(pathname, sticky, pathlen)) + return; + } + + strbuf_add(&sb, pathname, pathlen); + string_list_append_nodup(&exc->sticky_paths, strbuf_detach(&sb, NULL)); +} + +static int match_sticky(struct exclude *exc, const char *pathname, int pathlen, int dtype) +{ + int i; + + for (i = exc->sticky_paths.nr - 1; i >= 0; i--) { + const char *sticky = exc->sticky_paths.items[i].string; + int len = strlen(sticky); + + if (pathlen == len && dtype == DT_DIR && + !strncmp(pathname, sticky, len)) + return 1; + + if (pathlen > len && pathname[len] == '/' && + !strncmp(pathname, sticky, len)) + return 1; + } + + return 0; +} + +static inline int different_decisions(const struct exclude *a, + const struct exclude *b) +{ + return (a->flags & EXC_FLAG_NEGATIVE) != (b->flags & EXC_FLAG_NEGATIVE); +} + +/* + * Return non-zero if pathname is a directory and an ancestor of the + * literal path in a pattern. + */ +static int match_directory_part(const char *pathname, int pathlen, + int *dtype, struct exclude *x) +{ + const char *base = x->base; + int baselen = x->baselen ? x->baselen - 1 : 0; + const char *pattern = x->pattern; + int prefix = x->nowildcardlen; + int patternlen = x->patternlen; + + if (*dtype == DT_UNKNOWN) + *dtype = get_dtype(NULL, pathname, pathlen); + if (*dtype != DT_DIR) + return 0; + + if (*pattern == '/') { + pattern++; + patternlen--; + prefix--; + } + + if (baselen) { + if (((pathlen < baselen && base[pathlen] == '/') || + pathlen == baselen) && + !strncmp_icase(pathname, base, pathlen)) + return 1; + pathname += baselen + 1; + pathlen -= baselen + 1; + } + + + if (prefix && + (((pathlen < prefix && pattern[pathlen] == '/') || + pathlen == prefix) && + !strncmp_icase(pathname, pattern, pathlen))) + return 1; + + return 0; +} + +static struct exclude *should_descend(const char *pathname, int pathlen, + int *dtype, struct exclude_list *el, + struct exclude *exc) +{ + int i; + + for (i = el->nr - 1; 0 <= i; i--) { + struct exclude *x = el->excludes[i]; + + if (x == exc) + break; + + if (!(x->flags & EXC_FLAG_NODIR) && + different_decisions(x, exc) && + match_directory_part(pathname, pathlen, dtype, x)) + return x; + } + return NULL; +} + /* * Scan the given exclude list in reverse to see whether pathname * should be ignored. The first match (i.e. the last on the list), if @@@ -1012,32 -892,16 +1004,32 @@@ static struct exclude *last_exclude_mat struct exclude_list *el) { struct exclude *exc = NULL; /* undecided */ - int i; + int i, maybe_descend = 0; if (!el->nr) return NULL; /* undefined */ + trace_printf_key(&trace_exclude, "exclude: from %s\n", el->src); + for (i = el->nr - 1; 0 <= i; i--) { struct exclude *x = el->excludes[i]; const char *exclude = x->pattern; int prefix = x->nowildcardlen; + if (!maybe_descend && i < el->nr - 1 && + different_decisions(x, el->excludes[i+1])) + maybe_descend = 1; + + if (x->sticky_paths.nr) { + if (*dtype == DT_UNKNOWN) + *dtype = get_dtype(NULL, pathname, pathlen); + if (match_sticky(x, pathname, pathlen, *dtype)) { + exc = x; + break; + } + continue; + } + if (x->flags & EXC_FLAG_MUSTBEDIR) { if (*dtype == DT_UNKNOWN) *dtype = get_dtype(NULL, pathname, pathlen); @@@ -1064,45 -928,6 +1056,45 @@@ break; } } + + if (!exc) { + trace_printf_key(&trace_exclude, "exclude: %.*s => n/a\n", + pathlen, pathname); + return NULL; + } + + /* + * We have found a matching pattern "exc" that may exclude whole + * directory. We also found that there may be a pattern that matches + * something inside the directory and reincludes stuff. + * + * Go through the patterns again, find that pattern and double check. + * If it's true, return "undecided" and keep descending in. "exc" is + * marked sticky so that it continues to match inside the directory. + */ + if (!(exc->flags & EXC_FLAG_NEGATIVE) && maybe_descend) { + struct exclude *x; + + if (*dtype == DT_UNKNOWN) + *dtype = get_dtype(NULL, pathname, pathlen); + + if (*dtype == DT_DIR && + (x = should_descend(pathname, pathlen, dtype, el, exc))) { + add_sticky(exc, pathname, pathlen); + trace_printf_key(&trace_exclude, + "exclude: %.*s vs %s at line %d => %s," + " forced open by %s at line %d => n/a\n", + pathlen, pathname, exc->pattern, exc->srcpos, + exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes", + x->pattern, x->srcpos); + return NULL; + } + } + + trace_printf_key(&trace_exclude, "exclude: %.*s vs %s at line %d => %s%s\n", + pathlen, pathname, exc->pattern, exc->srcpos, + exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes", + exc->sticky_paths.nr ? " (stuck)" : ""); return exc; } @@@ -1334,10 -1159,8 +1326,8 @@@ static struct dir_entry *dir_entry_new( { struct dir_entry *ent; - ent = xmalloc(sizeof(*ent) + len + 1); + FLEX_ALLOC_MEM(ent, name, pathname, len); ent->len = len; - memcpy(ent->name, pathname, len); - ent->name[len] = 0; return ent; } @@@ -1850,13 -1673,9 +1840,13 @@@ static enum path_treatment read_directo struct cached_dir cdir; enum path_treatment state, subdir_state, dir_state = path_none; struct strbuf path = STRBUF_INIT; + static int level = 0; strbuf_add(&path, base, baselen); + trace_printf_key(&trace_exclude, "exclude: [%d] enter '%.*s'\n", + level++, baselen, base); + if (open_cached_dir(&cdir, dir, untracked, &path, check_only)) goto out; @@@ -1920,8 -1739,6 +1910,8 @@@ } close_cached_dir(&cdir); out: + trace_printf_key(&trace_exclude, "exclude: [%d] leave '%.*s'\n", + --level, baselen, base); strbuf_release(&path); return dir_state; @@@ -2012,67 -1829,31 +2002,67 @@@ static const char *get_ident_string(voi return sb.buf; if (uname(&uts) < 0) die_errno(_("failed to get kernel name and information")); - strbuf_addf(&sb, "Location %s, system %s %s %s", get_git_work_tree(), - uts.sysname, uts.release, uts.version); + strbuf_addf(&sb, "Location %s, system %s", get_git_work_tree(), + uts.sysname); return sb.buf; } static int ident_in_untracked(const struct untracked_cache *uc) { - const char *end = uc->ident.buf + uc->ident.len; - const char *p = uc->ident.buf; + /* + * Previous git versions may have saved many NUL separated + * strings in the "ident" field, but it is insane to manage + * many locations, so just take care of the first one. + */ - for (p = uc->ident.buf; p < end; p += strlen(p) + 1) - if (!strcmp(p, get_ident_string())) - return 1; - return 0; + return !strcmp(uc->ident.buf, get_ident_string()); } -void add_untracked_ident(struct untracked_cache *uc) +static void set_untracked_ident(struct untracked_cache *uc) { - if (ident_in_untracked(uc)) - return; + strbuf_reset(&uc->ident); strbuf_addstr(&uc->ident, get_ident_string()); - /* this strbuf contains a list of strings, save NUL too */ + + /* + * This strbuf used to contain a list of NUL separated + * strings, so save NUL too for backward compatibility. + */ strbuf_addch(&uc->ident, 0); } +static void new_untracked_cache(struct index_state *istate) +{ + struct untracked_cache *uc = xcalloc(1, sizeof(*uc)); + strbuf_init(&uc->ident, 100); + uc->exclude_per_dir = ".gitignore"; + /* should be the same flags used by git-status */ + uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; + set_untracked_ident(uc); + istate->untracked = uc; + istate->cache_changed |= UNTRACKED_CHANGED; +} + +void add_untracked_cache(struct index_state *istate) +{ + if (!istate->untracked) { + new_untracked_cache(istate); + } else { + if (!ident_in_untracked(istate->untracked)) { + free_untracked_cache(istate->untracked); + new_untracked_cache(istate); + } + } +} + +void remove_untracked_cache(struct index_state *istate) +{ + if (istate->untracked) { + free_untracked_cache(istate->untracked); + istate->untracked = NULL; + istate->cache_changed |= UNTRACKED_CHANGED; + } +} + static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir, int base_len, const struct pathspec *pathspec) @@@ -2130,7 -1911,7 +2120,7 @@@ return NULL; if (!ident_in_untracked(dir->untracked)) { - warning(_("Untracked cache is disabled on this system.")); + warning(_("Untracked cache is disabled on this system or location.")); return NULL; } @@@ -2158,25 -1939,6 +2148,25 @@@ return root; } +static void clear_sticky(struct dir_struct *dir) +{ + struct exclude_list_group *g; + struct exclude_list *el; + struct exclude *x; + int i, j, k; + + for (i = EXC_CMDL; i <= EXC_FILE; i++) { + g = &dir->exclude_list_group[i]; + for (j = g->nr - 1; j >= 0; j--) { + el = &g->el[j]; + for (k = el->nr - 1; 0 <= k; k--) { + x = el->excludes[k]; + string_list_clear(&x->sticky_paths, 0); + } + } + } +} + int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec) { struct path_simplify *simplify; @@@ -2197,12 -1959,6 +2187,12 @@@ if (has_symlink_leading_path(path, len)) return dir->nr; + /* + * Stay on the safe side. if read_directory() has run once on + * "dir", some sticky flag may have been left. Clear them all. + */ + clear_sticky(dir); + /* * exclude patterns are treated like positive ones in * create_simplify. Usually exclude patterns should be a @@@ -2568,16 -2324,15 +2558,15 @@@ void write_untracked_extension(struct s struct ondisk_untracked_cache *ouc; struct write_data wd; unsigned char varbuf[16]; - int len = 0, varint_len; - if (untracked->exclude_per_dir) - len = strlen(untracked->exclude_per_dir); - ouc = xmalloc(sizeof(*ouc) + len + 1); + int varint_len; + size_t len = strlen(untracked->exclude_per_dir); + + FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len); stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat); stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat); hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1); hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1); ouc->dir_flags = htonl(untracked->dir_flags); - memcpy(ouc->exclude_per_dir, untracked->exclude_per_dir, len + 1); varint_len = encode_varint(untracked->ident.len, varbuf); strbuf_add(out, varbuf, varint_len); @@@ -2682,21 -2437,21 +2671,21 @@@ static int read_one_dir(struct untracke ud.untracked_alloc = value; ud.untracked_nr = value; if (ud.untracked_nr) - ud.untracked = xmalloc(sizeof(*ud.untracked) * ud.untracked_nr); + ALLOC_ARRAY(ud.untracked, ud.untracked_nr); data = next; next = data; ud.dirs_alloc = ud.dirs_nr = decode_varint(&next); if (next > end) return -1; - ud.dirs = xmalloc(sizeof(*ud.dirs) * ud.dirs_nr); + ALLOC_ARRAY(ud.dirs, ud.dirs_nr); data = next; len = strlen((const char *)data); next = data + len + 1; if (next > rd->end) return -1; - *untracked_ = untracked = xmalloc(sizeof(*untracked) + len); + *untracked_ = untracked = xmalloc(st_add(sizeof(*untracked), len)); memcpy(untracked, &ud, sizeof(ud)); memcpy(untracked->name, data, len + 1); data = next; @@@ -2809,7 -2564,7 +2798,7 @@@ struct untracked_cache *read_untracked_ rd.data = next; rd.end = end; rd.index = 0; - rd.ucd = xmalloc(sizeof(*rd.ucd) * len); + ALLOC_ARRAY(rd.ucd, len); if (read_one_dir(&uc->root, &rd) || rd.index != len) goto done; diff --combined exec_cmd.c index 680b257cd5,cf442a97f8..9d5703a157 --- a/exec_cmd.c +++ b/exec_cmd.c @@@ -1,6 -1,7 +1,7 @@@ #include "cache.h" #include "exec_cmd.h" #include "quote.h" + #include "argv-array.h" #define MAX_ARGS 32 static const char *argv_exec_path; @@@ -43,10 -44,12 +44,10 @@@ const char *git_extract_argv0_path(cons if (!argv0 || !*argv0) return NULL; - slash = argv0 + strlen(argv0); - while (argv0 <= slash && !is_dir_sep(*slash)) - slash--; + slash = find_last_dir_sep(argv0); - if (slash >= argv0) { + if (slash) { argv0_path = xstrndup(argv0, slash - argv0); return slash + 1; } @@@ -105,32 -108,25 +106,25 @@@ void setup_path(void strbuf_release(&new_path); } - const char **prepare_git_cmd(const char **argv) + const char **prepare_git_cmd(struct argv_array *out, const char **argv) { - int argc; - const char **nargv; - - for (argc = 0; argv[argc]; argc++) - ; /* just counting */ - nargv = xmalloc(sizeof(*nargv) * (argc + 2)); - - nargv[0] = "git"; - for (argc = 0; argv[argc]; argc++) - nargv[argc + 1] = argv[argc]; - nargv[argc + 1] = NULL; - return nargv; + argv_array_push(out, "git"); + argv_array_pushv(out, argv); + return out->argv; } int execv_git_cmd(const char **argv) { - const char **nargv = prepare_git_cmd(argv); - trace_argv_printf(nargv, "trace: exec:"); + struct argv_array nargv = ARGV_ARRAY_INIT; + + prepare_git_cmd(&nargv, argv); + trace_argv_printf(nargv.argv, "trace: exec:"); /* execvp() can only ever return if it fails */ - sane_execvp("git", (char **)nargv); + sane_execvp("git", (char **)nargv.argv); trace_printf("trace: exec failed: %s\n", strerror(errno)); - free(nargv); + argv_array_clear(&nargv); return -1; } diff --combined fast-import.c index bf01b34221,75f77480a2..9fc7093406 --- a/fast-import.c +++ b/fast-import.c @@@ -622,7 -622,7 +622,7 @@@ static void *pool_alloc(size_t len return xmalloc(len); } total_allocd += sizeof(struct mem_pool) + mem_pool_alloc; - p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc); + p = xmalloc(st_add(sizeof(struct mem_pool), mem_pool_alloc)); p->next_pool = mem_pool; p->next_free = (char *) p->space; p->end = p->next_free + mem_pool_alloc; @@@ -814,7 -814,8 +814,8 @@@ static struct tree_entry *new_tree_entr if (!avail_tree_entry) { unsigned int n = tree_entry_alloc; total_allocd += n * sizeof(struct tree_entry); - avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry)); + ALLOC_ARRAY(e, n); + avail_tree_entry = e; while (n-- > 1) { *((void**)e) = e + 1; e++; @@@ -864,15 -865,12 +865,12 @@@ static void start_packfile(void { static char tmp_file[PATH_MAX]; struct packed_git *p; - int namelen; struct pack_header hdr; int pack_fd; pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file), "pack/tmp_pack_XXXXXX"); - namelen = strlen(tmp_file) + 2; - p = xcalloc(1, sizeof(*p) + namelen); - xsnprintf(p->pack_name, namelen, "%s", tmp_file); + FLEX_ALLOC_STR(p, pack_name, tmp_file); p->pack_fd = pack_fd; p->do_not_close = 1; pack_file = sha1fd(pack_fd, p->pack_name); @@@ -898,7 -896,7 +896,7 @@@ static const char *create_index(void struct object_entry_pool *o; /* Build the table of object IDs. */ - idx = xmalloc(object_count * sizeof(*idx)); + ALLOC_ARRAY(idx, object_count); c = idx; for (o = blocks; o; o = o->next_pool) for (e = o->next_free; e-- != o->entries;) @@@ -1888,7 -1886,7 +1886,7 @@@ static int read_next_command(void struct recent_command *rc; strbuf_detach(&command_buf, NULL); - stdin_eof = strbuf_getline(&command_buf, stdin, '\n'); + stdin_eof = strbuf_getline_lf(&command_buf, stdin); if (stdin_eof) return EOF; @@@ -1960,7 -1958,7 +1958,7 @@@ static int parse_data(struct strbuf *sb strbuf_detach(&command_buf, NULL); for (;;) { - if (strbuf_getline(&command_buf, stdin, '\n') == EOF) + if (strbuf_getline_lf(&command_buf, stdin) == EOF) die("EOF in data (terminator '%s' not found)", term); if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) diff --combined git-compat-util.h index 2acef3f113,1459f9b87e..c07e0c1778 --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -96,6 -96,14 +96,14 @@@ #define unsigned_add_overflows(a, b) \ ((b) > maximum_unsigned_value_of_type(a) - (a)) + /* + * Returns true if the multiplication of "a" and "b" will + * overflow. The types of "a" and "b" must match and must be unsigned. + * Note that this macro evaluates "a" twice! + */ + #define unsigned_mult_overflows(a, b) \ + ((a) && (b) > maximum_unsigned_value_of_type(a) / (a)) + #ifdef __GNUC__ #define TYPEOF(x) (__typeof__(x)) #else @@@ -325,6 -333,10 +333,6 @@@ extern char *gitdirname(char *) #define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin" #endif -#ifndef STRIP_EXTENSION -#define STRIP_EXTENSION "" -#endif - #ifndef has_dos_drive_prefix static inline int git_has_dos_drive_prefix(const char *path) { @@@ -669,7 -681,6 +677,6 @@@ extern int git_vsnprintf(char *str, siz #ifdef __GLIBC_PREREQ #if __GLIBC_PREREQ(2, 1) #define HAVE_STRCHRNUL - #define HAVE_MEMPCPY #endif #endif @@@ -683,14 -694,6 +690,6 @@@ static inline char *gitstrchrnul(const } #endif - #ifndef HAVE_MEMPCPY - #define mempcpy gitmempcpy - static inline void *gitmempcpy(void *dest, const void *src, size_t n) - { - return (char *)memcpy(dest, src, n) + n; - } - #endif - #ifdef NO_INET_PTON int inet_pton(int af, const char *src, void *dst); #endif @@@ -709,6 -712,32 +708,32 @@@ extern void release_pack_memory(size_t) typedef void (*try_to_free_t)(size_t); extern try_to_free_t set_try_to_free_routine(try_to_free_t); + static inline size_t st_add(size_t a, size_t b) + { + if (unsigned_add_overflows(a, b)) + die("size_t overflow: %"PRIuMAX" + %"PRIuMAX, + (uintmax_t)a, (uintmax_t)b); + return a + b; + } + #define st_add3(a,b,c) st_add((a),st_add((b),(c))) + #define st_add4(a,b,c,d) st_add((a),st_add3((b),(c),(d))) + + static inline size_t st_mult(size_t a, size_t b) + { + if (unsigned_mult_overflows(a, b)) + die("size_t overflow: %"PRIuMAX" * %"PRIuMAX, + (uintmax_t)a, (uintmax_t)b); + return a * b; + } + + static inline size_t st_sub(size_t a, size_t b) + { + if (a < b) + die("size_t underflow: %"PRIuMAX" - %"PRIuMAX, + (uintmax_t)a, (uintmax_t)b); + return a - b; + } + #ifdef HAVE_ALLOCA_H # include # define xalloca(size) (alloca(size)) @@@ -741,7 -770,70 +766,70 @@@ extern int odb_pack_keep(char *name, si extern char *xgetcwd(void); extern FILE *fopen_for_writing(const char *path); - #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), (alloc) * sizeof(*(x))) + #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc))) + #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc))) + + /* + * These functions help you allocate structs with flex arrays, and copy + * the data directly into the array. For example, if you had: + * + * struct foo { + * int bar; + * char name[FLEX_ARRAY]; + * }; + * + * you can do: + * + * struct foo *f; + * FLEX_ALLOC_MEM(f, name, src, len); + * + * to allocate a "foo" with the contents of "src" in the "name" field. + * The resulting struct is automatically zero'd, and the flex-array field + * is NUL-terminated (whether the incoming src buffer was or not). + * + * The FLEXPTR_* variants operate on structs that don't use flex-arrays, + * but do want to store a pointer to some extra data in the same allocated + * block. For example, if you have: + * + * struct foo { + * char *name; + * int bar; + * }; + * + * you can do: + * + * struct foo *f; + * FLEX_ALLOC_STR(f, name, src); + * + * and "name" will point to a block of memory after the struct, which will be + * freed along with the struct (but the pointer can be repointed anywhere). + * + * The *_STR variants accept a string parameter rather than a ptr/len + * combination. + * + * Note that these macros will evaluate the first parameter multiple + * times, and it must be assignable as an lvalue. + */ + #define FLEX_ALLOC_MEM(x, flexname, buf, len) do { \ + (x) = NULL; /* silence -Wuninitialized for offset calculation */ \ + (x) = xalloc_flex(sizeof(*(x)), (char *)(&((x)->flexname)) - (char *)(x), (buf), (len)); \ + } while (0) + #define FLEXPTR_ALLOC_MEM(x, ptrname, buf, len) do { \ + (x) = xalloc_flex(sizeof(*(x)), sizeof(*(x)), (buf), (len)); \ + (x)->ptrname = (void *)((x)+1); \ + } while(0) + #define FLEX_ALLOC_STR(x, flexname, str) \ + FLEX_ALLOC_MEM((x), flexname, (str), strlen(str)) + #define FLEXPTR_ALLOC_STR(x, ptrname, str) \ + FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str)) + + static inline void *xalloc_flex(size_t base_len, size_t offset, + const void *src, size_t src_len) + { + unsigned char *ret = xcalloc(1, st_add3(base_len, src_len, 1)); + memcpy(ret + offset, src, src_len); + return ret; + } static inline char *xstrdup_or_null(const char *str) { diff --combined git.c index 9bc04fd528,e61a59c014..6cc0c077f9 --- a/git.c +++ b/git.c @@@ -25,14 -25,14 +25,14 @@@ static const char *env_names[] = GIT_PREFIX_ENVIRONMENT }; static char *orig_env[4]; -static int saved_environment; +static int save_restore_env_balance; -static void save_env(void) +static void save_env_before_alias(void) { int i; - if (saved_environment) - return; - saved_environment = 1; + + assert(save_restore_env_balance == 0); + save_restore_env_balance = 1; orig_cwd = xgetcwd(); for (i = 0; i < ARRAY_SIZE(env_names); i++) { orig_env[i] = getenv(env_names[i]); @@@ -41,25 -41,17 +41,25 @@@ } } -static void restore_env(void) +static void restore_env(int external_alias) { int i; - if (orig_cwd && chdir(orig_cwd)) + + assert(save_restore_env_balance == 1); + save_restore_env_balance = 0; + if (!external_alias && orig_cwd && chdir(orig_cwd)) die_errno("could not move to %s", orig_cwd); free(orig_cwd); for (i = 0; i < ARRAY_SIZE(env_names); i++) { - if (orig_env[i]) + if (external_alias && + !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT)) + continue; + if (orig_env[i]) { setenv(env_names[i], orig_env[i], 1); - else + free(orig_env[i]); + } else { unsetenv(env_names[i]); + } } } @@@ -234,33 -226,28 +234,29 @@@ static int handle_options(const char ** static int handle_alias(int *argcp, const char ***argv) { int envchanged = 0, ret = 0, saved_errno = errno; - const char *subdir; int count, option_count; const char **new_argv; const char *alias_command; char *alias_string; int unused_nongit; - subdir = setup_git_directory_gently(&unused_nongit); + save_env_before_alias(); + setup_git_directory_gently(&unused_nongit); alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { if (alias_string[0] == '!') { - const char **alias_argv; - int argc = *argcp, i; + struct child_process child = CHILD_PROCESS_INIT; commit_pager_choice(); + restore_env(1); - /* build alias_argv */ - alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1)); - alias_argv[0] = alias_string + 1; - for (i = 1; i < argc; ++i) - alias_argv[i] = (*argv)[i]; - alias_argv[argc] = NULL; + child.use_shell = 1; + argv_array_push(&child.args, alias_string + 1); + argv_array_pushv(&child.args, (*argv) + 1); - ret = run_command_v_opt(alias_argv, RUN_USING_SHELL); + ret = run_command(&child); if (ret >= 0) /* normal exit */ exit(ret); @@@ -300,7 -287,8 +296,7 @@@ ret = 1; } - if (subdir && chdir(subdir)) - die_errno("Cannot change to '%s'", subdir); + restore_env(0); errno = saved_errno; @@@ -315,6 -303,7 +311,6 @@@ * RUN_SETUP for reading from the configuration file. */ #define NEED_WORK_TREE (1<<3) -#define NO_SETUP (1<<4) struct cmd_struct { const char *cmd; @@@ -396,7 -385,7 +392,7 @@@ static struct cmd_struct commands[] = { "cherry", cmd_cherry, RUN_SETUP }, { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, - { "clone", cmd_clone, NO_SETUP }, + { "clone", cmd_clone }, { "column", cmd_column, RUN_SETUP_GENTLY }, { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, @@@ -422,8 -411,8 +418,8 @@@ { "hash-object", cmd_hash_object }, { "help", cmd_help }, { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY }, - { "init", cmd_init_db, NO_SETUP }, - { "init-db", cmd_init_db, NO_SETUP }, + { "init", cmd_init_db }, + { "init-db", cmd_init_db }, { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY }, { "log", cmd_log, RUN_SETUP }, { "ls-files", cmd_ls_files, RUN_SETUP }, @@@ -513,25 -502,21 +509,25 @@@ int is_builtin(const char *s return !!get_builtin(s); } +#ifdef STRIP_EXTENSION +static void strip_extension(const char **argv) +{ + size_t len; + + if (strip_suffix(argv[0], STRIP_EXTENSION, &len)) + argv[0] = xmemdupz(argv[0], len); +} +#else +#define strip_extension(cmd) +#endif + static void handle_builtin(int argc, const char **argv) { - const char *cmd = argv[0]; - int i; - static const char ext[] = STRIP_EXTENSION; + const char *cmd; struct cmd_struct *builtin; - if (sizeof(ext) > 1) { - i = strlen(argv[0]) - strlen(ext); - if (i > 0 && !strcmp(argv[0] + i, ext)) { - char *argv0 = xstrdup(argv[0]); - argv[0] = cmd = argv0; - argv0[i] = '\0'; - } - } + strip_extension(argv); + cmd = argv[0]; /* Turn "git cmd --help" into "git help cmd" */ if (argc > 1 && !strcmp(argv[1], "--help")) { @@@ -540,8 -525,12 +536,8 @@@ } builtin = get_builtin(cmd); - if (builtin) { - if (saved_environment && (builtin->option & NO_SETUP)) - restore_env(); - else - exit(run_builtin(builtin, argc, argv)); - } + if (builtin) + exit(run_builtin(builtin, argc, argv)); } static void execv_dashed_external(const char **argv) @@@ -585,17 -574,8 +581,17 @@@ static int run_argv(int *argcp, const c int done_alias = 0; while (1) { - /* See if it's a builtin */ - handle_builtin(*argcp, *argv); + /* + * If we tried alias and futzed with our environment, + * it no longer is safe to invoke builtins directly in + * general. We have to spawn them as dashed externals. + * + * NEEDSWORK: if we can figure out cases + * where it is safe to do, we can avoid spawning a new + * process. + */ + if (!done_alias) + handle_builtin(*argcp, *argv); /* .. then try the external ones */ execv_dashed_external(*argv); @@@ -606,6 -586,7 +602,6 @@@ */ if (done_alias) break; - save_env(); if (!handle_alias(argcp, argv)) break; done_alias = 1; diff --combined notes.c index c1e5035590,8b59ccdb11..88cf4747c1 --- a/notes.c +++ b/notes.c @@@ -1011,16 -1011,13 +1011,16 @@@ void init_notes(struct notes_tree *t, c t->first_non_note = NULL; t->prev_non_note = NULL; t->ref = xstrdup_or_null(notes_ref); + t->update_ref = (flags & NOTES_INIT_WRITABLE) ? t->ref : NULL; t->combine_notes = combine_notes; t->initialized = 1; t->dirty = 0; if (flags & NOTES_INIT_EMPTY || !notes_ref || - read_ref(notes_ref, object_sha1)) + get_sha1_treeish(notes_ref, object_sha1)) return; + if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, object_sha1)) + die("Cannot use notes ref %s", notes_ref); if (get_tree_entry(object_sha1, "", sha1, &mode)) die("Failed to read notes tree referenced by %s (%s)", notes_ref, sha1_to_hex(object_sha1)); @@@ -1030,15 -1027,15 +1030,15 @@@ load_subtree(t, &root_tree, t->root, 0); } -struct notes_tree **load_notes_trees(struct string_list *refs) +struct notes_tree **load_notes_trees(struct string_list *refs, int flags) { struct string_list_item *item; int counter = 0; struct notes_tree **trees; - trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *)); + ALLOC_ARRAY(trees, refs->nr + 1); for_each_string_list_item(item, refs) { struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree)); - init_notes(t, item->string, combine_notes_ignore, 0); + init_notes(t, item->string, combine_notes_ignore, flags); trees[counter++] = t; } trees[counter] = NULL; @@@ -1074,7 -1071,7 +1074,7 @@@ void init_display_notes(struct display_ item->string); } - display_notes_trees = load_notes_trees(&display_notes_refs); + display_notes_trees = load_notes_trees(&display_notes_refs, 0); string_list_clear(&display_notes_refs, 0); } @@@ -1306,13 -1303,3 +1306,13 @@@ void expand_notes_ref(struct strbuf *sb else strbuf_insert(sb, 0, "refs/notes/", 11); } + +void expand_loose_notes_ref(struct strbuf *sb) +{ + unsigned char object[20]; + + if (get_sha1(sb->buf, object)) { + /* fallback to expand_notes_ref */ + expand_notes_ref(sb); + } +} diff --combined pack-revindex.c index 155a8a3d69,f6a3613beb..96d51c3467 --- a/pack-revindex.c +++ b/pack-revindex.c @@@ -8,13 -8,52 +8,13 @@@ * size is easily available by examining the pack entry header). It is * also rather expensive to find the sha1 for an object given its offset. * - * We build a hashtable of existing packs (pack_revindex), and keep reverse - * index here -- pack index file is sorted by object name mapping to offset; - * this pack_revindex[].revindex array is a list of offset/index_nr pairs + * The pack index file is sorted by object name mapping to offset; + * this revindex array is a list of offset/index_nr pairs * ordered by offset, so if you know the offset of an object, next offset * is where its packed representation ends and the index_nr can be used to * get the object sha1 from the main index. */ -static struct pack_revindex *pack_revindex; -static int pack_revindex_hashsz; - -static int pack_revindex_ix(struct packed_git *p) -{ - unsigned long ui = (unsigned long)(intptr_t)p; - int i; - - ui = ui ^ (ui >> 16); /* defeat structure alignment */ - i = (int)(ui % pack_revindex_hashsz); - while (pack_revindex[i].p) { - if (pack_revindex[i].p == p) - return i; - if (++i == pack_revindex_hashsz) - i = 0; - } - return -1 - i; -} - -static void init_pack_revindex(void) -{ - int num; - struct packed_git *p; - - for (num = 0, p = packed_git; p; p = p->next) - num++; - if (!num) - return; - pack_revindex_hashsz = num * 11; - pack_revindex = xcalloc(pack_revindex_hashsz, sizeof(*pack_revindex)); - for (p = packed_git; p; p = p->next) { - num = pack_revindex_ix(p); - num = - 1 - num; - pack_revindex[num].p = p; - } - /* revindex elements are lazily initialized */ -} - /* * This is a least-significant-digit radix sort. * @@@ -44,10 -83,14 +44,14 @@@ static void sort_revindex(struct revind * keep track of them with alias pointers, always sorting from "from" * to "to". */ - struct revindex_entry *tmp = xmalloc(n * sizeof(*tmp)); - struct revindex_entry *from = entries, *to = tmp; + struct revindex_entry *tmp, *from, *to; int bits; - unsigned *pos = xmalloc(BUCKETS * sizeof(*pos)); + unsigned *pos; + + ALLOC_ARRAY(pos, BUCKETS); + ALLOC_ARRAY(tmp, n); + from = entries; + to = tmp; /* * If (max >> bits) is zero, then we know that the radix digit we are @@@ -115,13 -158,14 +119,13 @@@ /* * Ordered list of offsets of objects in the pack. */ -static void create_pack_revindex(struct pack_revindex *rix) +static void create_pack_revindex(struct packed_git *p) { - struct packed_git *p = rix->p; unsigned num_ent = p->num_objects; unsigned i; const char *index = p->index_data; - p->revindex = xmalloc(sizeof(*p->revindex) * (num_ent + 1)); - ALLOC_ARRAY(rix->revindex, num_ent + 1); ++ ALLOC_ARRAY(p->revindex, num_ent + 1); index += 4 * 256; if (p->index_version > 1) { @@@ -131,42 -175,55 +135,42 @@@ for (i = 0; i < num_ent; i++) { uint32_t off = ntohl(*off_32++); if (!(off & 0x80000000)) { - rix->revindex[i].offset = off; + p->revindex[i].offset = off; } else { - rix->revindex[i].offset = + p->revindex[i].offset = ((uint64_t)ntohl(*off_64++)) << 32; - rix->revindex[i].offset |= + p->revindex[i].offset |= ntohl(*off_64++); } - rix->revindex[i].nr = i; + p->revindex[i].nr = i; } } else { for (i = 0; i < num_ent; i++) { uint32_t hl = *((uint32_t *)(index + 24 * i)); - rix->revindex[i].offset = ntohl(hl); - rix->revindex[i].nr = i; + p->revindex[i].offset = ntohl(hl); + p->revindex[i].nr = i; } } /* This knows the pack format -- the 20-byte trailer * follows immediately after the last object data. */ - rix->revindex[num_ent].offset = p->pack_size - 20; - rix->revindex[num_ent].nr = -1; - sort_revindex(rix->revindex, num_ent, p->pack_size); + p->revindex[num_ent].offset = p->pack_size - 20; + p->revindex[num_ent].nr = -1; + sort_revindex(p->revindex, num_ent, p->pack_size); } -struct pack_revindex *revindex_for_pack(struct packed_git *p) +void load_pack_revindex(struct packed_git *p) { - int num; - struct pack_revindex *rix; - - if (!pack_revindex_hashsz) - init_pack_revindex(); - - num = pack_revindex_ix(p); - if (num < 0) - die("internal error: pack revindex fubar"); - - rix = &pack_revindex[num]; - if (!rix->revindex) - create_pack_revindex(rix); - - return rix; + if (!p->revindex) + create_pack_revindex(p); } -int find_revindex_position(struct pack_revindex *pridx, off_t ofs) +int find_revindex_position(struct packed_git *p, off_t ofs) { int lo = 0; - int hi = pridx->p->num_objects + 1; - struct revindex_entry *revindex = pridx->revindex; + int hi = p->num_objects + 1; + struct revindex_entry *revindex = p->revindex; do { unsigned mi = lo + (hi - lo) / 2; @@@ -184,13 -241,11 +188,13 @@@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs) { - struct pack_revindex *pridx = revindex_for_pack(p); - int pos = find_revindex_position(pridx, ofs); + int pos; + + load_pack_revindex(p); + pos = find_revindex_position(p, ofs); if (pos < 0) return NULL; - return pridx->revindex + pos; + return p->revindex + pos; } diff --combined ref-filter.c index d13d002270,9ccfc51cea..bb79d6b9cc --- a/ref-filter.c +++ b/ref-filter.c @@@ -16,162 -16,14 +16,162 @@@ typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; +struct align { + align_type position; + unsigned int width; +}; + +/* + * An atom is a valid field atom listed below, possibly prefixed with + * a "*" to denote deref_tag(). + * + * We parse given format string and sort specifiers, and make a list + * of properties that we need to extract out of objects. ref_array_item + * structure will hold an array of values extracted that can be + * indexed with the "atom number", which is an index into this + * array. + */ +static struct used_atom { + const char *name; + cmp_type type; + union { + char color[COLOR_MAXLEN]; + struct align align; + enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } + remote_ref; + struct { + enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option; + unsigned int nlines; + } contents; + enum { O_FULL, O_SHORT } objectname; + } u; +} *used_atom; +static int used_atom_cnt, need_tagged, need_symref; +static int need_color_reset_at_eol; + +static void color_atom_parser(struct used_atom *atom, const char *color_value) +{ + if (!color_value) + die(_("expected format: %%(color:)")); + if (color_parse(color_value, atom->u.color) < 0) + die(_("unrecognized color: %%(color:%s)"), color_value); +} + +static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) +{ + if (!arg) + atom->u.remote_ref = RR_NORMAL; + else if (!strcmp(arg, "short")) + atom->u.remote_ref = RR_SHORTEN; + else if (!strcmp(arg, "track")) + atom->u.remote_ref = RR_TRACK; + else if (!strcmp(arg, "trackshort")) + atom->u.remote_ref = RR_TRACKSHORT; + else + die(_("unrecognized format: %%(%s)"), atom->name); +} + +static void body_atom_parser(struct used_atom *atom, const char *arg) +{ + if (arg) + die("%%(body) does not take arguments"); + atom->u.contents.option = C_BODY_DEP; +} + +static void subject_atom_parser(struct used_atom *atom, const char *arg) +{ + if (arg) + die("%%(subject) does not take arguments"); + atom->u.contents.option = C_SUB; +} + +static void contents_atom_parser(struct used_atom *atom, const char *arg) +{ + if (!arg) + atom->u.contents.option = C_BARE; + else if (!strcmp(arg, "body")) + atom->u.contents.option = C_BODY; + else if (!strcmp(arg, "signature")) + atom->u.contents.option = C_SIG; + else if (!strcmp(arg, "subject")) + atom->u.contents.option = C_SUB; + else if (skip_prefix(arg, "lines=", &arg)) { + atom->u.contents.option = C_LINES; + if (strtoul_ui(arg, 10, &atom->u.contents.nlines)) + die(_("positive value expected contents:lines=%s"), arg); + } else + die(_("unrecognized %%(contents) argument: %s"), arg); +} + +static void objectname_atom_parser(struct used_atom *atom, const char *arg) +{ + if (!arg) + atom->u.objectname = O_FULL; + else if (!strcmp(arg, "short")) + atom->u.objectname = O_SHORT; + else + die(_("unrecognized %%(objectname) argument: %s"), arg); +} + +static align_type parse_align_position(const char *s) +{ + if (!strcmp(s, "right")) + return ALIGN_RIGHT; + else if (!strcmp(s, "middle")) + return ALIGN_MIDDLE; + else if (!strcmp(s, "left")) + return ALIGN_LEFT; + return -1; +} + +static void align_atom_parser(struct used_atom *atom, const char *arg) +{ + struct align *align = &atom->u.align; + struct string_list params = STRING_LIST_INIT_DUP; + int i; + unsigned int width = ~0U; + + if (!arg) + die(_("expected format: %%(align:,)")); + + align->position = ALIGN_LEFT; + + string_list_split(¶ms, arg, ',', -1); + for (i = 0; i < params.nr; i++) { + const char *s = params.items[i].string; + int position; + + if (skip_prefix(s, "position=", &s)) { + position = parse_align_position(s); + if (position < 0) + die(_("unrecognized position:%s"), s); + align->position = position; + } else if (skip_prefix(s, "width=", &s)) { + if (strtoul_ui(s, 10, &width)) + die(_("unrecognized width:%s"), s); + } else if (!strtoul_ui(s, 10, &width)) + ; + else if ((position = parse_align_position(s)) >= 0) + align->position = position; + else + die(_("unrecognized %%(align) argument: %s"), s); + } + + if (width == ~0U) + die(_("positive width expected with the %%(align) atom")); + align->width = width; + string_list_clear(¶ms, 0); +} + static struct { const char *name; cmp_type cmp_type; + void (*parser)(struct used_atom *atom, const char *arg); } valid_atom[] = { { "refname" }, { "objecttype" }, { "objectsize", FIELD_ULONG }, - { "objectname" }, + { "objectname", FIELD_STR, objectname_atom_parser }, { "tree" }, { "parent" }, { "numparent", FIELD_ULONG }, @@@ -192,21 -44,31 +192,21 @@@ { "taggerdate", FIELD_TIME }, { "creator" }, { "creatordate", FIELD_TIME }, - { "subject" }, - { "body" }, - { "contents" }, - { "upstream" }, - { "push" }, + { "subject", FIELD_STR, subject_atom_parser }, + { "body", FIELD_STR, body_atom_parser }, + { "contents", FIELD_STR, contents_atom_parser }, + { "upstream", FIELD_STR, remote_ref_atom_parser }, + { "push", FIELD_STR, remote_ref_atom_parser }, { "symref" }, { "flag" }, { "HEAD" }, - { "color" }, - { "align" }, + { "color", FIELD_STR, color_atom_parser }, + { "align", FIELD_STR, align_atom_parser }, { "end" }, }; #define REF_FORMATTING_STATE_INIT { 0, NULL } -struct align { - align_type position; - unsigned int width; -}; - -struct contents { - unsigned int lines; - struct object_id oid; -}; - struct ref_formatting_stack { struct ref_formatting_stack *prev; struct strbuf output; @@@ -223,18 -85,33 +223,18 @@@ struct atom_value const char *s; union { struct align align; - struct contents contents; } u; void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state); unsigned long ul; /* used for sorting when not FIELD_STR */ }; -/* - * An atom is a valid field atom listed above, possibly prefixed with - * a "*" to denote deref_tag(). - * - * We parse given format string and sort specifiers, and make a list - * of properties that we need to extract out of objects. ref_array_item - * structure will hold an array of values extracted that can be - * indexed with the "atom number", which is an index into this - * array. - */ -static const char **used_atom; -static cmp_type *used_atom_type; -static int used_atom_cnt, need_tagged, need_symref; -static int need_color_reset_at_eol; - /* * Used to parse format string and sort specifiers */ int parse_ref_filter_atom(const char *atom, const char *ep) { const char *sp; + const char *arg; int i, at; sp = atom; @@@ -245,24 -122,24 +245,24 @@@ /* Do we have the atom already used elsewhere? */ for (i = 0; i < used_atom_cnt; i++) { - int len = strlen(used_atom[i]); - if (len == ep - atom && !memcmp(used_atom[i], atom, len)) + int len = strlen(used_atom[i].name); + if (len == ep - atom && !memcmp(used_atom[i].name, atom, len)) return i; } /* Is the atom a valid one? */ for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { int len = strlen(valid_atom[i].name); + /* * If the atom name has a colon, strip it and everything after * it off - it specifies the format for this entry, and * shouldn't be used for checking against the valid_atom * table. */ - const char *formatp = strchr(sp, ':'); - if (!formatp || ep < formatp) - formatp = ep; - if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len)) + arg = memchr(sp, ':', ep - sp); + if (len == (arg ? arg : ep) - sp && + !memcmp(valid_atom[i].name, sp, len)) break; } @@@ -273,16 -150,12 +273,16 @@@ at = used_atom_cnt; used_atom_cnt++; REALLOC_ARRAY(used_atom, used_atom_cnt); - REALLOC_ARRAY(used_atom_type, used_atom_cnt); - used_atom[at] = xmemdupz(atom, ep - atom); - used_atom_type[at] = valid_atom[i].cmp_type; + used_atom[at].name = xmemdupz(atom, ep - atom); + used_atom[at].type = valid_atom[i].cmp_type; + if (arg) + arg = used_atom[at].name + (arg - atom) + 1; + memset(&used_atom[at].u, 0, sizeof(used_atom[at].u)); + if (valid_atom[i].parser) + valid_atom[i].parser(&used_atom[at], arg); if (*atom == '*') need_tagged = 1; - if (!strcmp(used_atom[at], "symref")) + if (!strcmp(used_atom[at].name, "symref")) need_symref = 1; return at; } @@@ -385,6 -258,22 +385,6 @@@ static void end_atom_handler(struct ato pop_stack_element(&state->stack); } -static int match_atom_name(const char *name, const char *atom_name, const char **val) -{ - const char *body; - - if (!skip_prefix(name, atom_name, &body)) - return 0; /* doesn't even begin with "atom_name" */ - if (!body[0]) { - *val = NULL; /* %(atom_name) and no customization */ - return 1; - } - if (body[0] != ':') - return 0; /* "atom_namefoo" is not "atom_name" or "atom_name:..." */ - *val = body + 1; /* "atom_name:val" */ - return 1; -} - /* * In a format string, find the next occurrence of %(atom). */ @@@ -426,7 -315,7 +426,7 @@@ int verify_ref_format(const char *forma at = parse_ref_filter_atom(sp + 2, ep); cp = ep + 1; - if (skip_prefix(used_atom[at], "color:", &color)) + if (skip_prefix(used_atom[at].name, "color:", &color)) need_color_reset_at_eol = !!strcmp(color, "reset"); } return 0; @@@ -451,17 -340,15 +451,17 @@@ static void *get_obj(const unsigned cha } static int grab_objectname(const char *name, const unsigned char *sha1, - struct atom_value *v) + struct atom_value *v, struct used_atom *atom) { - if (!strcmp(name, "objectname")) { - v->s = xstrdup(sha1_to_hex(sha1)); - return 1; - } - if (!strcmp(name, "objectname:short")) { - v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); - return 1; + if (starts_with(name, "objectname")) { + if (atom->u.objectname == O_SHORT) { + v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); + return 1; + } else if (atom->u.objectname == O_FULL) { + v->s = xstrdup(sha1_to_hex(sha1)); + return 1; + } else + die("BUG: unknown %%(objectname) option"); } return 0; } @@@ -472,7 -359,7 +472,7 @@@ static void grab_common_values(struct a int i; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@@ -485,7 -372,7 +485,7 @@@ v->s = xstrfmt("%lu", sz); } else if (deref) - grab_objectname(name, obj->oid.hash, v); + grab_objectname(name, obj->oid.hash, v, &used_atom[i]); } } @@@ -496,7 -383,7 +496,7 @@@ static void grab_tag_values(struct atom struct tag *tag = (struct tag *) obj; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@@ -518,7 -405,7 +518,7 @@@ static void grab_commit_values(struct a struct commit *commit = (struct commit *) obj; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@@ -648,7 -535,7 +648,7 @@@ static void grab_person(const char *who const char *wholine = NULL; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@@ -686,7 -573,7 +686,7 @@@ if (!wholine) return; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@@ -776,16 -663,20 +776,16 @@@ static void grab_sub_body_contents(stru unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + struct used_atom *atom = &used_atom[i]; + const char *name = atom->name; struct atom_value *v = &val[i]; - const char *valp = NULL; if (!!deref != (*name == '*')) continue; if (deref) name++; if (strcmp(name, "subject") && strcmp(name, "body") && - strcmp(name, "contents") && - strcmp(name, "contents:subject") && - strcmp(name, "contents:body") && - strcmp(name, "contents:signature") && - !starts_with(name, "contents:lines=")) + !starts_with(name, "contents")) continue; if (!subpos) find_subpos(buf, sz, @@@ -793,23 -684,28 +793,23 @@@ &bodypos, &bodylen, &nonsiglen, &sigpos, &siglen); - if (!strcmp(name, "subject")) + if (atom->u.contents.option == C_SUB) v->s = copy_subject(subpos, sublen); - else if (!strcmp(name, "contents:subject")) - v->s = copy_subject(subpos, sublen); - else if (!strcmp(name, "body")) + else if (atom->u.contents.option == C_BODY_DEP) v->s = xmemdupz(bodypos, bodylen); - else if (!strcmp(name, "contents:body")) + else if (atom->u.contents.option == C_BODY) v->s = xmemdupz(bodypos, nonsiglen); - else if (!strcmp(name, "contents:signature")) + else if (atom->u.contents.option == C_SIG) v->s = xmemdupz(sigpos, siglen); - else if (!strcmp(name, "contents")) - v->s = xstrdup(subpos); - else if (skip_prefix(name, "contents:lines=", &valp)) { + else if (atom->u.contents.option == C_LINES) { struct strbuf s = STRBUF_INIT; const char *contents_end = bodylen + bodypos - siglen; - if (strtoul_ui(valp, 10, &v->u.contents.lines)) - die(_("positive value expected contents:lines=%s"), valp); /* Size is the length of the message after removing the signature */ - append_lines(&s, subpos, contents_end - subpos, v->u.contents.lines); + append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines); v->s = strbuf_detach(&s, NULL); - } + } else if (atom->u.contents.option == C_BARE) + v->s = xstrdup(subpos); } } @@@ -890,43 -786,6 +890,43 @@@ static const char *strip_ref_components return start; } +static void fill_remote_ref_details(struct used_atom *atom, const char *refname, + struct branch *branch, const char **s) +{ + int num_ours, num_theirs; + if (atom->u.remote_ref == RR_SHORTEN) + *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs); + else if (atom->u.remote_ref == RR_TRACK) { + if (stat_tracking_info(branch, &num_ours, + &num_theirs, NULL)) + return; + + if (!num_ours && !num_theirs) + *s = ""; + else if (!num_ours) + *s = xstrfmt("[behind %d]", num_theirs); + else if (!num_theirs) + *s = xstrfmt("[ahead %d]", num_ours); + else + *s = xstrfmt("[ahead %d, behind %d]", + num_ours, num_theirs); + } else if (atom->u.remote_ref == RR_TRACKSHORT) { + if (stat_tracking_info(branch, &num_ours, + &num_theirs, NULL)) + return; + + if (!num_ours && !num_theirs) + *s = "="; + else if (!num_ours) + *s = "<"; + else if (!num_theirs) + *s = ">"; + else + *s = "<>"; + } else /* RR_NORMAL */ + *s = refname; +} + /* * Parse the object referred by ref, and grab needed value. */ @@@ -950,12 -809,12 +950,12 @@@ static void populate_value(struct ref_a /* Fill in specials first */ for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + struct used_atom *atom = &used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &ref->value[i]; int deref = 0; const char *refname; const char *formatp; - const char *valp; struct branch *branch = NULL; v->handler = append_atom; @@@ -978,9 -837,8 +978,9 @@@ branch = branch_get(branch_name); refname = branch_get_upstream(branch, NULL); - if (!refname) - continue; + if (refname) + fill_remote_ref_details(atom, refname, branch, &v->s); + continue; } else if (starts_with(name, "push")) { const char *branch_name; if (!skip_prefix(ref->refname, "refs/heads/", @@@ -991,10 -849,14 +991,10 @@@ refname = branch_get_push(branch, NULL); if (!refname) continue; - } else if (match_atom_name(name, "color", &valp)) { - char color[COLOR_MAXLEN] = ""; - - if (!valp) - die(_("expected format: %%(color:)")); - if (color_parse(valp, color) < 0) - die(_("unable to parse format")); - v->s = xstrdup(color); + fill_remote_ref_details(atom, refname, branch, &v->s); + continue; + } else if (starts_with(name, "color:")) { + v->s = atom->u.color; continue; } else if (!strcmp(name, "flag")) { char buf[256], *cp = buf; @@@ -1009,7 -871,7 +1009,7 @@@ v->s = xstrdup(buf + 1); } continue; - } else if (!deref && grab_objectname(name, ref->objectname, v)) { + } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) { continue; } else if (!strcmp(name, "HEAD")) { const char *head; @@@ -1022,8 -884,43 +1022,8 @@@ else v->s = " "; continue; - } else if (match_atom_name(name, "align", &valp)) { - struct align *align = &v->u.align; - struct strbuf **s, **to_free; - int width = -1; - - if (!valp) - die(_("expected format: %%(align:,)")); - - /* - * TODO: Implement a function similar to strbuf_split_str() - * which would omit the separator from the end of each value. - */ - s = to_free = strbuf_split_str(valp, ',', 0); - - align->position = ALIGN_LEFT; - - while (*s) { - /* Strip trailing comma */ - if (s[1]) - strbuf_setlen(s[0], s[0]->len - 1); - if (!strtoul_ui(s[0]->buf, 10, (unsigned int *)&width)) - ; - else if (!strcmp(s[0]->buf, "left")) - align->position = ALIGN_LEFT; - else if (!strcmp(s[0]->buf, "right")) - align->position = ALIGN_RIGHT; - else if (!strcmp(s[0]->buf, "middle")) - align->position = ALIGN_MIDDLE; - else - die(_("improper format entered align:%s"), s[0]->buf); - s++; - } - - if (width < 0) - die(_("positive width expected with the %%(align) atom")); - align->width = width; - strbuf_list_free(to_free); + } else if (starts_with(name, "align")) { + v->u.align = atom->u.align; v->handler = align_atom_handler; continue; } else if (!strcmp(name, "end")) { @@@ -1034,6 -931,7 +1034,6 @@@ formatp = strchr(name, ':'); if (formatp) { - int num_ours, num_theirs; const char *arg; formatp++; @@@ -1042,7 -940,43 +1042,7 @@@ warn_ambiguous_refs); else if (skip_prefix(formatp, "strip=", &arg)) refname = strip_ref_components(refname, arg); - else if (!strcmp(formatp, "track") && - (starts_with(name, "upstream") || - starts_with(name, "push"))) { - - if (stat_tracking_info(branch, &num_ours, - &num_theirs, NULL)) - continue; - - if (!num_ours && !num_theirs) - v->s = ""; - else if (!num_ours) - v->s = xstrfmt("[behind %d]", num_theirs); - else if (!num_theirs) - v->s = xstrfmt("[ahead %d]", num_ours); - else - v->s = xstrfmt("[ahead %d, behind %d]", - num_ours, num_theirs); - continue; - } else if (!strcmp(formatp, "trackshort") && - (starts_with(name, "upstream") || - starts_with(name, "push"))) { - assert(branch); - - if (stat_tracking_info(branch, &num_ours, - &num_theirs, NULL)) - continue; - - if (!num_ours && !num_theirs) - v->s = "="; - else if (!num_ours) - v->s = "<"; - else if (!num_theirs) - v->s = ">"; - else - v->s = "<>"; - continue; - } else + else die("unknown %.*s format %s", (int)(formatp - name), name, formatp); } @@@ -1321,10 -1255,8 +1321,8 @@@ static struct ref_array_item *new_ref_a const unsigned char *objectname, int flag) { - size_t len = strlen(refname); - struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1); - memcpy(ref->refname, refname, len); - ref->refname[len] = '\0'; + struct ref_array_item *ref; + FLEX_ALLOC_STR(ref, refname, refname); hashcpy(ref->objectname, objectname); ref->flag = flag; @@@ -1537,7 -1469,7 +1535,7 @@@ static int cmp_ref_sorting(struct ref_s { struct atom_value *va, *vb; int cmp; - cmp_type cmp_type = used_atom_type[s->atom]; + cmp_type cmp_type = used_atom[s->atom].type; get_ref_atom_value(a, s->atom, &va); get_ref_atom_value(b, s->atom, &vb); diff --combined refs/files-backend.c index b569762888,de9af1615c..81f68f846b --- a/refs/files-backend.c +++ b/refs/files-backend.c @@@ -199,17 -199,14 +199,14 @@@ static struct ref_entry *create_ref_ent const unsigned char *sha1, int flag, int check_name) { - int len; struct ref_entry *ref; if (check_name && check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) die("Reference has invalid format: '%s'", refname); - len = strlen(refname) + 1; - ref = xmalloc(sizeof(struct ref_entry) + len); + FLEX_ALLOC_STR(ref, name, refname); hashcpy(ref->u.value.oid.hash, sha1); oidclr(&ref->u.value.peeled); - memcpy(ref->name, refname, len); ref->flag = flag; return ref; } @@@ -268,9 -265,7 +265,7 @@@ static struct ref_entry *create_dir_ent int incomplete) { struct ref_entry *direntry; - direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1); - memcpy(direntry->name, dirname, len); - direntry->name[len] = '\0'; + FLEX_ALLOC_MEM(direntry, name, dirname, len); direntry->u.subdir.ref_cache = ref_cache; direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0); return direntry; @@@ -939,13 -934,10 +934,10 @@@ static void clear_loose_ref_cache(struc */ static struct ref_cache *create_ref_cache(const char *submodule) { - int len; struct ref_cache *refs; if (!submodule) submodule = ""; - len = strlen(submodule) + 1; - refs = xcalloc(1, sizeof(struct ref_cache) + len); - memcpy(refs->name, submodule, len); + FLEX_ALLOC_STR(refs, name, submodule); refs->next = submodule_ref_caches; submodule_ref_caches = refs; return refs; @@@ -1858,17 -1850,12 +1850,17 @@@ static int verify_lock(struct ref_lock if (read_ref_full(lock->ref_name, mustexist ? RESOLVE_REF_READING : 0, lock->old_oid.hash, NULL)) { - int save_errno = errno; - strbuf_addf(err, "can't verify ref %s", lock->ref_name); - errno = save_errno; - return -1; + if (old_sha1) { + int save_errno = errno; + strbuf_addf(err, "can't verify ref %s", lock->ref_name); + errno = save_errno; + return -1; + } else { + hashclr(lock->old_oid.hash); + return 0; + } } - if (hashcmp(lock->old_oid.hash, old_sha1)) { + if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) { strbuf_addf(err, "ref %s is at %s but expected %s", lock->ref_name, sha1_to_hex(lock->old_oid.hash), @@@ -1905,8 -1892,7 +1897,8 @@@ static struct ref_lock *lock_ref_sha1_b const char *orig_refname = refname; struct ref_lock *lock; int last_errno = 0; - int type, lflags; + int type; + int lflags = 0; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); int resolve_flags = 0; int attempts_remaining = 3; @@@ -1917,11 -1903,10 +1909,11 @@@ if (mustexist) resolve_flags |= RESOLVE_REF_READING; - if (flags & REF_DELETING) { + if (flags & REF_DELETING) resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME; - if (flags & REF_NODEREF) - resolve_flags |= RESOLVE_REF_NO_RECURSE; + if (flags & REF_NODEREF) { + resolve_flags |= RESOLVE_REF_NO_RECURSE; + lflags |= LOCK_NO_DEREF; } refname = resolve_ref_unsafe(refname, resolve_flags, @@@ -1957,10 -1942,6 +1949,10 @@@ goto error_return; } + + if (flags & REF_NODEREF) + refname = orig_refname; + /* * If the ref did not exist and we are creating it, make sure * there is no existing packed ref whose name begins with our @@@ -1976,6 -1957,11 +1968,6 @@@ lock->lk = xcalloc(1, sizeof(struct lock_file)); - lflags = 0; - if (flags & REF_NODEREF) { - refname = orig_refname; - lflags |= LOCK_NO_DEREF; - } lock->ref_name = xstrdup(refname); lock->orig_ref_name = xstrdup(orig_refname); strbuf_git_path(&ref_file, "%s", refname); @@@ -2009,7 -1995,7 +2001,7 @@@ goto error_return; } } - if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) { + if (verify_lock(lock, old_sha1, mustexist, err)) { last_errno = errno; goto error_return; } @@@ -2197,10 -2183,9 +2189,9 @@@ static int pack_if_possible_fn(struct r /* Schedule the loose reference for pruning if requested. */ if ((cb->flags & PACK_REFS_PRUNE)) { - int namelen = strlen(entry->name) + 1; - struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); + struct ref_to_prune *n; + FLEX_ALLOC_STR(n, name, entry->name); hashcpy(n->sha1, entry->u.value.oid.hash); - memcpy(n->name, entry->name, namelen); /* includes NUL */ n->next = cb->ref_to_prune; cb->ref_to_prune = n; } @@@ -2835,72 -2820,73 +2826,72 @@@ static int commit_ref_update(struct ref return 0; } -int create_symref(const char *ref_target, const char *refs_heads_master, - const char *logmsg) +static int create_ref_symlink(struct ref_lock *lock, const char *target) { - char *lockpath = NULL; - char ref[1000]; - int fd, len, written; - char *git_HEAD = git_pathdup("%s", ref_target); - unsigned char old_sha1[20], new_sha1[20]; - struct strbuf err = STRBUF_INIT; - - if (logmsg && read_ref(ref_target, old_sha1)) - hashclr(old_sha1); - - if (safe_create_leading_directories(git_HEAD) < 0) - return error("unable to create directory for %s", git_HEAD); - + int ret = -1; #ifndef NO_SYMLINK_HEAD - if (prefer_symlink_refs) { - unlink(git_HEAD); - if (!symlink(refs_heads_master, git_HEAD)) - goto done; + char *ref_path = get_locked_file_path(lock->lk); + unlink(ref_path); + ret = symlink(target, ref_path); + free(ref_path); + + if (ret) fprintf(stderr, "no symlink - falling back to symbolic ref\n"); - } #endif + return ret; +} - len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master); - if (sizeof(ref) <= len) { - error("refname too long: %s", refs_heads_master); - goto error_free_return; - } - lockpath = mkpathdup("%s.lock", git_HEAD); - fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (fd < 0) { - error("Unable to open %s for writing", lockpath); - goto error_free_return; - } - written = write_in_full(fd, ref, len); - if (close(fd) != 0 || written != len) { - error("Unable to write to %s", lockpath); - goto error_unlink_return; - } - if (rename(lockpath, git_HEAD) < 0) { - error("Unable to create %s", git_HEAD); - goto error_unlink_return; - } - if (adjust_shared_perm(git_HEAD)) { - error("Unable to fix permissions on %s", lockpath); - error_unlink_return: - unlink_or_warn(lockpath); - error_free_return: - free(lockpath); - free(git_HEAD); - return -1; +static void update_symref_reflog(struct ref_lock *lock, const char *refname, + const char *target, const char *logmsg) +{ + struct strbuf err = STRBUF_INIT; + unsigned char new_sha1[20]; + if (logmsg && !read_ref(target, new_sha1) && + log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) { + error("%s", err.buf); + strbuf_release(&err); } - free(lockpath); +} -#ifndef NO_SYMLINK_HEAD - done: -#endif - if (logmsg && !read_ref(refs_heads_master, new_sha1) && - log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) { +static int create_symref_locked(struct ref_lock *lock, const char *refname, + const char *target, const char *logmsg) +{ + if (prefer_symlink_refs && !create_ref_symlink(lock, target)) { + update_symref_reflog(lock, refname, target, logmsg); + return 0; + } + + if (!fdopen_lock_file(lock->lk, "w")) + return error("unable to fdopen %s: %s", + lock->lk->tempfile.filename.buf, strerror(errno)); + + update_symref_reflog(lock, refname, target, logmsg); + + /* no error check; commit_ref will check ferror */ + fprintf(lock->lk->tempfile.fp, "ref: %s\n", target); + if (commit_ref(lock) < 0) + return error("unable to write symref for %s: %s", refname, + strerror(errno)); + return 0; +} + +int create_symref(const char *refname, const char *target, const char *logmsg) +{ + struct strbuf err = STRBUF_INIT; + struct ref_lock *lock; + int ret; + + lock = lock_ref_sha1_basic(refname, NULL, NULL, NULL, REF_NODEREF, NULL, + &err); + if (!lock) { error("%s", err.buf); strbuf_release(&err); + return -1; } - free(git_HEAD); - return 0; + ret = create_symref_locked(lock, refname, target, logmsg); + unlock_ref(lock); + return ret; } int reflog_exists(const char *refname) diff --combined remote-curl.c index 2e2266b856,e85333a51b..15e48e25fb --- a/remote-curl.c +++ b/remote-curl.c @@@ -119,19 -119,6 +119,19 @@@ static int set_option(const char *name else return -1; return 0; + +#if LIBCURL_VERSION_NUM >= 0x070a08 + } else if (!strcmp(name, "family")) { + if (!strcmp(value, "ipv4")) + git_curl_ipresolve = CURL_IPRESOLVE_V4; + else if (!strcmp(value, "ipv6")) + git_curl_ipresolve = CURL_IPRESOLVE_V6; + else if (!strcmp(value, "all")) + git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER; + else + return -1; + return 0; +#endif /* LIBCURL_VERSION_NUM >= 0x070a08 */ } else { return 1 /* unsupported */; } @@@ -452,20 -439,8 +452,20 @@@ static int run_slot(struct active_reque err = run_one_slot(slot, results); if (err != HTTP_OK && err != HTTP_REAUTH) { - error("RPC failed; result=%d, HTTP code = %ld", - results->curl_result, results->http_code); + struct strbuf msg = STRBUF_INIT; + if (results->http_code && results->http_code != 200) + strbuf_addf(&msg, "HTTP %ld", results->http_code); + if (results->curl_result != CURLE_OK) { + if (msg.len) + strbuf_addch(&msg, ' '); + strbuf_addf(&msg, "curl %d", results->curl_result); + if (curl_errorstr[0]) { + strbuf_addch(&msg, ' '); + strbuf_addstr(&msg, curl_errorstr); + } + } + error("RPC failed; %s", msg.buf); + strbuf_release(&msg); } return err; @@@ -721,9 -696,10 +721,10 @@@ static int rpc_service(struct rpc_stat static int fetch_dumb(int nr_heads, struct ref **to_fetch) { struct walker *walker; - char **targets = xmalloc(nr_heads * sizeof(char*)); + char **targets; int ret, i; + ALLOC_ARRAY(targets, nr_heads); if (options.depth) die("dumb http transport does not support --depth"); for (i = 0; i < nr_heads; i++) @@@ -852,7 -828,7 +853,7 @@@ static void parse_fetch(struct strbuf * die("http transport does not support %s", buf->buf); strbuf_reset(buf); - if (strbuf_getline(buf, stdin, '\n') == EOF) + if (strbuf_getline_lf(buf, stdin) == EOF) return; if (!*buf->buf) break; @@@ -870,23 -846,22 +871,22 @@@ static int push_dav(int nr_spec, char **specs) { - const char **argv = xmalloc((10 + nr_spec) * sizeof(char*)); - int argc = 0, i; + struct child_process child = CHILD_PROCESS_INIT; + size_t i; - argv[argc++] = "http-push"; - argv[argc++] = "--helper-status"; + child.git_cmd = 1; + argv_array_push(&child.args, "http-push"); + argv_array_push(&child.args, "--helper-status"); if (options.dry_run) - argv[argc++] = "--dry-run"; + argv_array_push(&child.args, "--dry-run"); if (options.verbosity > 1) - argv[argc++] = "--verbose"; - argv[argc++] = url.buf; + argv_array_push(&child.args, "--verbose"); + argv_array_push(&child.args, url.buf); for (i = 0; i < nr_spec; i++) - argv[argc++] = specs[i]; - argv[argc++] = NULL; + argv_array_push(&child.args, specs[i]); - if (run_command_v_opt(argv, RUN_GIT_CMD)) - die("git-%s failed", argv[0]); - free(argv); + if (run_command(&child)) + die("git-http-push failed"); return 0; } @@@ -965,7 -940,7 +965,7 @@@ static void parse_push(struct strbuf *b die("http transport does not support %s", buf->buf); strbuf_reset(buf); - if (strbuf_getline(buf, stdin, '\n') == EOF) + if (strbuf_getline_lf(buf, stdin) == EOF) goto free_specs; if (!*buf->buf) break; @@@ -1015,7 -990,7 +1015,7 @@@ int main(int argc, const char **argv do { const char *arg; - if (strbuf_getline(&buf, stdin, '\n') == EOF) { + if (strbuf_getline_lf(&buf, stdin) == EOF) { if (ferror(stdin)) error("remote-curl: error reading command stream from git"); return 1; diff --combined remote.c index f001681105,f182382c83..fc02698587 --- a/remote.c +++ b/remote.c @@@ -256,7 -256,7 +256,7 @@@ static void read_remotes_file(struct re if (!f) return; remote->origin = REMOTE_REMOTES; - while (strbuf_getline(&buf, f, '\n') != EOF) { + while (strbuf_getline(&buf, f) != EOF) { const char *v; strbuf_rtrim(&buf); @@@ -281,7 -281,7 +281,7 @@@ static void read_branches_file(struct r if (!f) return; - strbuf_getline(&buf, f, '\n'); + strbuf_getline_lf(&buf, f); fclose(f); strbuf_trim(&buf); if (!buf.len) { @@@ -318,88 -318,93 +318,88 @@@ static int handle_config(const char *key, const char *value, void *cb) { const char *name; + int namelen; const char *subkey; struct remote *remote; struct branch *branch; - if (starts_with(key, "branch.")) { - name = key + 7; - subkey = strrchr(name, '.'); - if (!subkey) + if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) { + if (!name) return 0; - branch = make_branch(name, subkey - name); - if (!strcmp(subkey, ".remote")) { + branch = make_branch(name, namelen); + if (!strcmp(subkey, "remote")) { return git_config_string(&branch->remote_name, key, value); - } else if (!strcmp(subkey, ".pushremote")) { + } else if (!strcmp(subkey, "pushremote")) { return git_config_string(&branch->pushremote_name, key, value); - } else if (!strcmp(subkey, ".merge")) { + } else if (!strcmp(subkey, "merge")) { if (!value) return config_error_nonbool(key); add_merge(branch, xstrdup(value)); } return 0; } - if (starts_with(key, "url.")) { + if (parse_config_key(key, "url", &name, &namelen, &subkey) >= 0) { struct rewrite *rewrite; - name = key + 4; - subkey = strrchr(name, '.'); - if (!subkey) + if (!name) return 0; - if (!strcmp(subkey, ".insteadof")) { - rewrite = make_rewrite(&rewrites, name, subkey - name); + if (!strcmp(subkey, "insteadof")) { + rewrite = make_rewrite(&rewrites, name, namelen); if (!value) return config_error_nonbool(key); add_instead_of(rewrite, xstrdup(value)); - } else if (!strcmp(subkey, ".pushinsteadof")) { - rewrite = make_rewrite(&rewrites_push, name, subkey - name); + } else if (!strcmp(subkey, "pushinsteadof")) { + rewrite = make_rewrite(&rewrites_push, name, namelen); if (!value) return config_error_nonbool(key); add_instead_of(rewrite, xstrdup(value)); } } - if (!starts_with(key, "remote.")) + if (parse_config_key(key, "remote", &name, &namelen, &subkey) < 0) return 0; - name = key + 7; /* Handle remote.* variables */ - if (!strcmp(name, "pushdefault")) + if (!name && !strcmp(subkey, "pushdefault")) return git_config_string(&pushremote_name, key, value); + if (!name) + return 0; /* Handle remote..* variables */ if (*name == '/') { warning("Config remote shorthand cannot begin with '/': %s", name); return 0; } - subkey = strrchr(name, '.'); - if (!subkey) - return 0; - remote = make_remote(name, subkey - name); + remote = make_remote(name, namelen); remote->origin = REMOTE_CONFIG; - if (!strcmp(subkey, ".mirror")) + if (!strcmp(subkey, "mirror")) remote->mirror = git_config_bool(key, value); - else if (!strcmp(subkey, ".skipdefaultupdate")) + else if (!strcmp(subkey, "skipdefaultupdate")) remote->skip_default_update = git_config_bool(key, value); - else if (!strcmp(subkey, ".skipfetchall")) + else if (!strcmp(subkey, "skipfetchall")) remote->skip_default_update = git_config_bool(key, value); - else if (!strcmp(subkey, ".prune")) + else if (!strcmp(subkey, "prune")) remote->prune = git_config_bool(key, value); - else if (!strcmp(subkey, ".url")) { + else if (!strcmp(subkey, "url")) { const char *v; if (git_config_string(&v, key, value)) return -1; add_url(remote, v); - } else if (!strcmp(subkey, ".pushurl")) { + } else if (!strcmp(subkey, "pushurl")) { const char *v; if (git_config_string(&v, key, value)) return -1; add_pushurl(remote, v); - } else if (!strcmp(subkey, ".push")) { + } else if (!strcmp(subkey, "push")) { const char *v; if (git_config_string(&v, key, value)) return -1; add_push_refspec(remote, v); - } else if (!strcmp(subkey, ".fetch")) { + } else if (!strcmp(subkey, "fetch")) { const char *v; if (git_config_string(&v, key, value)) return -1; add_fetch_refspec(remote, v); - } else if (!strcmp(subkey, ".receivepack")) { + } else if (!strcmp(subkey, "receivepack")) { const char *v; if (git_config_string(&v, key, value)) return -1; @@@ -407,7 -412,7 +407,7 @@@ remote->receivepack = v; else error("more than one receivepack given, using the first"); - } else if (!strcmp(subkey, ".uploadpack")) { + } else if (!strcmp(subkey, "uploadpack")) { const char *v; if (git_config_string(&v, key, value)) return -1; @@@ -415,18 -420,15 +415,18 @@@ remote->uploadpack = v; else error("more than one uploadpack given, using the first"); - } else if (!strcmp(subkey, ".tagopt")) { + } else if (!strcmp(subkey, "tagopt")) { if (!strcmp(value, "--no-tags")) remote->fetch_tags = -1; else if (!strcmp(value, "--tags")) remote->fetch_tags = 2; - } else if (!strcmp(subkey, ".proxy")) { + } else if (!strcmp(subkey, "proxy")) { return git_config_string((const char **)&remote->http_proxy, key, value); - } else if (!strcmp(subkey, ".vcs")) { + } else if (!strcmp(subkey, "proxyauthmethod")) { + return git_config_string((const char **)&remote->http_proxy_authmethod, + key, value); + } else if (!strcmp(subkey, "vcs")) { return git_config_string(&remote->foreign_vcs, key, value); } return 0; @@@ -713,9 -715,18 +713,9 @@@ struct remote *pushremote_get(const cha return remote_get_1(name, pushremote_for_branch); } -int remote_is_configured(const char *name) +int remote_is_configured(struct remote *remote) { - struct remotes_hash_key lookup; - struct hashmap_entry lookup_entry; - read_config(); - - init_remotes_hash(); - lookup.str = name; - lookup.len = strlen(name); - hashmap_entry_init(&lookup_entry, memhash(name, lookup.len)); - - return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL; + return remote && remote->origin; } int for_each_remote(each_remote_fn fn, void *priv) @@@ -917,7 -928,7 +917,7 @@@ static struct ref *alloc_ref_with_prefi const char *name) { size_t len = strlen(name); - struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1); + struct ref *ref = xcalloc(1, st_add4(sizeof(*ref), prefixlen, len, 1)); memcpy(ref->name, prefix, prefixlen); memcpy(ref->name + prefixlen, name, len); return ref; @@@ -934,9 -945,9 +934,9 @@@ struct ref *copy_ref(const struct ref * size_t len; if (!ref) return NULL; - len = strlen(ref->name); - cpy = xmalloc(sizeof(struct ref) + len + 1); - memcpy(cpy, ref, sizeof(struct ref) + len + 1); + len = st_add3(sizeof(struct ref), strlen(ref->name), 1); + cpy = xmalloc(len); + memcpy(cpy, ref, len); cpy->next = NULL; cpy->symref = xstrdup_or_null(ref->symref); cpy->remote_status = xstrdup_or_null(ref->remote_status); @@@ -1534,8 -1545,11 +1534,8 @@@ void set_ref_status_for_push(struct re } /* - * Bypass the usual "must fast-forward" check but - * replace it with a weaker "the old value must be - * this value we observed". If the remote ref has - * moved and is now different from what we expect, - * reject any push. + * If the remote ref has moved and is now different + * from what we expect, reject any push. * * It also is an error if the user told us to check * with the remote-tracking branch to find the value @@@ -1546,14 -1560,10 +1546,14 @@@ if (ref->expect_old_no_trackback || oidcmp(&ref->old_oid, &ref->old_oid_expect)) reject_reason = REF_STATUS_REJECT_STALE; + else + /* If the ref isn't stale then force the update. */ + force_ref_update = 1; } /* - * The usual "must fast-forward" rules. + * If the update isn't already rejected then check + * the usual "must fast-forward" rules. * * Decide whether an individual refspec A:B can be * pushed. The push will succeed if any of the @@@ -1572,7 -1582,7 +1572,7 @@@ * passing the --force argument */ - else if (!ref->deletion && !is_null_oid(&ref->old_oid)) { + if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) { if (starts_with(ref->name, "refs/tags/")) reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; else if (!has_object_file(&ref->old_oid)) @@@ -2122,16 -2132,13 +2122,13 @@@ static int one_local_ref(const char *re { struct ref ***local_tail = cb_data; struct ref *ref; - int len; /* we already know it starts with refs/ to get here */ if (check_refname_format(refname + 5, 0)) return 0; - len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); + ref = alloc_ref(refname); oidcpy(&ref->new_oid, oid); - memcpy(ref->name, refname, len); **local_tail = ref; *local_tail = &ref->next; return 0; diff --combined revision.c index 82f3ca44b3,df56fcea0e..8b2dfe3160 --- a/revision.c +++ b/revision.c @@@ -25,13 -25,69 +25,13 @@@ volatile show_early_output_fn_t show_ea static const char *term_bad; static const char *term_good; -char *path_name(const struct name_path *path, const char *name) +void show_object_with_name(FILE *out, struct object *obj, const char *name) { - const struct name_path *p; - char *n, *m; - int nlen = strlen(name); - int len = nlen + 1; - - for (p = path; p; p = p->up) { - if (p->elem_len) - len += p->elem_len + 1; - } - n = xmalloc(len); - m = n + len - (nlen + 1); - memcpy(m, name, nlen + 1); - for (p = path; p; p = p->up) { - if (p->elem_len) { - m -= p->elem_len + 1; - memcpy(m, p->elem, p->elem_len); - m[p->elem_len] = '/'; - } - } - return n; -} - -static int show_path_component_truncated(FILE *out, const char *name, int len) -{ - int cnt; - for (cnt = 0; cnt < len; cnt++) { - int ch = name[cnt]; - if (!ch || ch == '\n') - return -1; - fputc(ch, out); - } - return len; -} - -static int show_path_truncated(FILE *out, const struct name_path *path) -{ - int emitted, ours; - - if (!path) - return 0; - emitted = show_path_truncated(out, path->up); - if (emitted < 0) - return emitted; - if (emitted) - fputc('/', out); - ours = show_path_component_truncated(out, path->elem, path->elem_len); - if (ours < 0) - return ours; - return ours || emitted; -} - -void show_object_with_name(FILE *out, struct object *obj, - const struct name_path *path, const char *component) -{ - struct name_path leaf; - leaf.up = (struct name_path *)path; - leaf.elem = component; - leaf.elem_len = strlen(component); + const char *p; fprintf(out, "%s ", oid_to_hex(&obj->oid)); - show_path_truncated(out, &leaf); + for (p = name; *p && *p != '\n'; p++) + fputc(*p, out); fputc('\n', out); } @@@ -484,7 -540,7 +484,7 @@@ struct treesame_state static struct treesame_state *initialise_treesame(struct rev_info *revs, struct commit *commit) { unsigned n = commit_list_count(commit->parents); - struct treesame_state *st = xcalloc(1, sizeof(*st) + n); + struct treesame_state *st = xcalloc(1, st_add(sizeof(*st), n)); st->nparents = n; add_decoration(&revs->treesame, &commit->object, st); return st; @@@ -1579,7 -1635,10 +1579,7 @@@ static void append_prune_data(struct cm static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb, struct cmdline_pathspec *prune) { - while (strbuf_getwholeline(sb, stdin, '\n') != EOF) { - int len = sb->len; - if (len && sb->buf[len - 1] == '\n') - sb->buf[--len] = '\0'; + while (strbuf_getline(sb, stdin) != EOF) { ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc); prune->path[prune->nr++] = xstrdup(sb->buf); } @@@ -1596,8 -1655,10 +1596,8 @@@ static void read_revisions_from_stdin(s warn_on_object_refname_ambiguity = 0; strbuf_init(&sb, 1000); - while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) { + while (strbuf_getline(&sb, stdin) != EOF) { int len = sb.len; - if (len && sb.buf[len - 1] == '\n') - sb.buf[--len] = '\0'; if (!len) break; if (sb.buf[0] == '-') { diff --combined run-command.c index cdf0184579,171cbaa944..019f6d19a5 --- a/run-command.c +++ b/run-command.c @@@ -3,8 -3,6 +3,8 @@@ #include "exec_cmd.h" #include "sigchain.h" #include "argv-array.h" +#include "thread-utils.h" +#include "strbuf.h" void child_process_init(struct child_process *child) { @@@ -160,50 -158,41 +160,41 @@@ int sane_execvp(const char *file, char return -1; } - static const char **prepare_shell_cmd(const char **argv) + static const char **prepare_shell_cmd(struct argv_array *out, const char **argv) { - int argc, nargc = 0; - const char **nargv; - - for (argc = 0; argv[argc]; argc++) - ; /* just counting */ - /* +1 for NULL, +3 for "sh -c" plus extra $0 */ - nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3)); - - if (argc < 1) + if (!argv[0]) die("BUG: shell command is empty"); if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) { #ifndef GIT_WINDOWS_NATIVE - nargv[nargc++] = SHELL_PATH; + argv_array_push(out, SHELL_PATH); #else - nargv[nargc++] = "sh"; + argv_array_push(out, "sh"); #endif - nargv[nargc++] = "-c"; - - if (argc < 2) - nargv[nargc++] = argv[0]; - else { - struct strbuf arg0 = STRBUF_INIT; - strbuf_addf(&arg0, "%s \"$@\"", argv[0]); - nargv[nargc++] = strbuf_detach(&arg0, NULL); - } - } + argv_array_push(out, "-c"); - for (argc = 0; argv[argc]; argc++) - nargv[nargc++] = argv[argc]; - nargv[nargc] = NULL; + /* + * If we have no extra arguments, we do not even need to + * bother with the "$@" magic. + */ + if (!argv[1]) + argv_array_push(out, argv[0]); + else + argv_array_pushf(out, "%s \"$@\"", argv[0]); + } - return nargv; + argv_array_pushv(out, argv); + return out->argv; } #ifndef GIT_WINDOWS_NATIVE static int execv_shell_cmd(const char **argv) { - const char **nargv = prepare_shell_cmd(argv); - trace_argv_printf(nargv, "trace: exec:"); - sane_execvp(nargv[0], (char **)nargv); - free(nargv); + struct argv_array nargv = ARGV_ARRAY_INIT; + prepare_shell_cmd(&nargv, argv); + trace_argv_printf(nargv.argv, "trace: exec:"); + sane_execvp(nargv.argv[0], (char **)nargv.argv); + argv_array_clear(&nargv); return -1; } #endif @@@ -247,7 -236,7 +238,7 @@@ static int wait_or_whine(pid_t pid, con error("waitpid is confused (%s)", argv0); } else if (WIFSIGNALED(status)) { code = WTERMSIG(status); - if (code != SIGINT && code != SIGQUIT) + if (code != SIGINT && code != SIGQUIT && code != SIGPIPE) error("%s died of signal %d", argv0, code); /* * This return value is chosen so that code & 0xff @@@ -457,6 -446,7 +448,7 @@@ fail_pipe { int fhin = 0, fhout = 1, fherr = 2; const char **sargv = cmd->argv; + struct argv_array nargv = ARGV_ARRAY_INIT; if (cmd->no_stdin) fhin = open("/dev/null", O_RDWR); @@@ -482,9 -472,9 +474,9 @@@ fhout = dup(cmd->out); if (cmd->git_cmd) - cmd->argv = prepare_git_cmd(cmd->argv); + cmd->argv = prepare_git_cmd(&nargv, cmd->argv); else if (cmd->use_shell) - cmd->argv = prepare_shell_cmd(cmd->argv); + cmd->argv = prepare_shell_cmd(&nargv, cmd->argv); cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env, cmd->dir, fhin, fhout, fherr); @@@ -494,9 -484,7 +486,7 @@@ if (cmd->clean_on_exit && cmd->pid >= 0) mark_child_for_cleanup(cmd->pid); - if (cmd->git_cmd) - free(cmd->argv); - + argv_array_clear(&nargv); cmd->argv = sargv; if (fhin != 0) close(fhin); @@@ -867,336 -855,3 +857,336 @@@ int capture_command(struct child_proces close(cmd->out); return finish_command(cmd); } + +enum child_state { + GIT_CP_FREE, + GIT_CP_WORKING, + GIT_CP_WAIT_CLEANUP, +}; + +struct parallel_processes { + void *data; + + int max_processes; + int nr_processes; + + get_next_task_fn get_next_task; + start_failure_fn start_failure; + task_finished_fn task_finished; + + struct { + enum child_state state; + struct child_process process; + struct strbuf err; + void *data; + } *children; + /* + * The struct pollfd is logically part of *children, + * but the system call expects it as its own array. + */ + struct pollfd *pfd; + + unsigned shutdown : 1; + + int output_owner; + struct strbuf buffered_output; /* of finished children */ +}; + +static int default_start_failure(struct child_process *cp, + struct strbuf *err, + void *pp_cb, + void *pp_task_cb) +{ + int i; + + strbuf_addstr(err, "Starting a child failed:"); + for (i = 0; cp->argv[i]; i++) + strbuf_addf(err, " %s", cp->argv[i]); + + return 0; +} + +static int default_task_finished(int result, + struct child_process *cp, + struct strbuf *err, + void *pp_cb, + void *pp_task_cb) +{ + int i; + + if (!result) + return 0; + + strbuf_addf(err, "A child failed with return code %d:", result); + for (i = 0; cp->argv[i]; i++) + strbuf_addf(err, " %s", cp->argv[i]); + + return 0; +} + +static void kill_children(struct parallel_processes *pp, int signo) +{ + int i, n = pp->max_processes; + + for (i = 0; i < n; i++) + if (pp->children[i].state == GIT_CP_WORKING) + kill(pp->children[i].process.pid, signo); +} + +static struct parallel_processes *pp_for_signal; + +static void handle_children_on_signal(int signo) +{ + kill_children(pp_for_signal, signo); + sigchain_pop(signo); + raise(signo); +} + +static void pp_init(struct parallel_processes *pp, + int n, + get_next_task_fn get_next_task, + start_failure_fn start_failure, + task_finished_fn task_finished, + void *data) +{ + int i; + + if (n < 1) + n = online_cpus(); + + pp->max_processes = n; + + trace_printf("run_processes_parallel: preparing to run up to %d tasks", n); + + pp->data = data; + if (!get_next_task) + die("BUG: you need to specify a get_next_task function"); + pp->get_next_task = get_next_task; + + pp->start_failure = start_failure ? start_failure : default_start_failure; + pp->task_finished = task_finished ? task_finished : default_task_finished; + + pp->nr_processes = 0; + pp->output_owner = 0; + pp->shutdown = 0; + pp->children = xcalloc(n, sizeof(*pp->children)); + pp->pfd = xcalloc(n, sizeof(*pp->pfd)); + strbuf_init(&pp->buffered_output, 0); + + for (i = 0; i < n; i++) { + strbuf_init(&pp->children[i].err, 0); + child_process_init(&pp->children[i].process); + pp->pfd[i].events = POLLIN | POLLHUP; + pp->pfd[i].fd = -1; + } + + pp_for_signal = pp; + sigchain_push_common(handle_children_on_signal); +} + +static void pp_cleanup(struct parallel_processes *pp) +{ + int i; + + trace_printf("run_processes_parallel: done"); + for (i = 0; i < pp->max_processes; i++) { + strbuf_release(&pp->children[i].err); + child_process_clear(&pp->children[i].process); + } + + free(pp->children); + free(pp->pfd); + + /* + * When get_next_task added messages to the buffer in its last + * iteration, the buffered output is non empty. + */ + fputs(pp->buffered_output.buf, stderr); + strbuf_release(&pp->buffered_output); + + sigchain_pop_common(); +} + +/* returns + * 0 if a new task was started. + * 1 if no new jobs was started (get_next_task ran out of work, non critical + * problem with starting a new command) + * <0 no new job was started, user wishes to shutdown early. Use negative code + * to signal the children. + */ +static int pp_start_one(struct parallel_processes *pp) +{ + int i, code; + + for (i = 0; i < pp->max_processes; i++) + if (pp->children[i].state == GIT_CP_FREE) + break; + if (i == pp->max_processes) + die("BUG: bookkeeping is hard"); + + code = pp->get_next_task(&pp->children[i].process, + &pp->children[i].err, + pp->data, + &pp->children[i].data); + if (!code) { + strbuf_addbuf(&pp->buffered_output, &pp->children[i].err); + strbuf_reset(&pp->children[i].err); + return 1; + } + pp->children[i].process.err = -1; + pp->children[i].process.stdout_to_stderr = 1; + pp->children[i].process.no_stdin = 1; + + if (start_command(&pp->children[i].process)) { + code = pp->start_failure(&pp->children[i].process, + &pp->children[i].err, + pp->data, + &pp->children[i].data); + strbuf_addbuf(&pp->buffered_output, &pp->children[i].err); + strbuf_reset(&pp->children[i].err); + if (code) + pp->shutdown = 1; + return code; + } + + pp->nr_processes++; + pp->children[i].state = GIT_CP_WORKING; + pp->pfd[i].fd = pp->children[i].process.err; + return 0; +} + +static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout) +{ + int i; + + while ((i = poll(pp->pfd, pp->max_processes, output_timeout)) < 0) { + if (errno == EINTR) + continue; + pp_cleanup(pp); + die_errno("poll"); + } + + /* Buffer output from all pipes. */ + for (i = 0; i < pp->max_processes; i++) { + if (pp->children[i].state == GIT_CP_WORKING && + pp->pfd[i].revents & (POLLIN | POLLHUP)) { + int n = strbuf_read_once(&pp->children[i].err, + pp->children[i].process.err, 0); + if (n == 0) { + close(pp->children[i].process.err); + pp->children[i].state = GIT_CP_WAIT_CLEANUP; + } else if (n < 0) + if (errno != EAGAIN) + die_errno("read"); + } + } +} + +static void pp_output(struct parallel_processes *pp) +{ + int i = pp->output_owner; + if (pp->children[i].state == GIT_CP_WORKING && + pp->children[i].err.len) { + fputs(pp->children[i].err.buf, stderr); + strbuf_reset(&pp->children[i].err); + } +} + +static int pp_collect_finished(struct parallel_processes *pp) +{ + int i, code; + int n = pp->max_processes; + int result = 0; + + while (pp->nr_processes > 0) { + for (i = 0; i < pp->max_processes; i++) + if (pp->children[i].state == GIT_CP_WAIT_CLEANUP) + break; + if (i == pp->max_processes) + break; + + code = finish_command(&pp->children[i].process); + + code = pp->task_finished(code, &pp->children[i].process, + &pp->children[i].err, pp->data, + &pp->children[i].data); + + if (code) + result = code; + if (code < 0) + break; + + pp->nr_processes--; + pp->children[i].state = GIT_CP_FREE; + pp->pfd[i].fd = -1; + child_process_init(&pp->children[i].process); + + if (i != pp->output_owner) { + strbuf_addbuf(&pp->buffered_output, &pp->children[i].err); + strbuf_reset(&pp->children[i].err); + } else { + fputs(pp->children[i].err.buf, stderr); + strbuf_reset(&pp->children[i].err); + + /* Output all other finished child processes */ + fputs(pp->buffered_output.buf, stderr); + strbuf_reset(&pp->buffered_output); + + /* + * Pick next process to output live. + * NEEDSWORK: + * For now we pick it randomly by doing a round + * robin. Later we may want to pick the one with + * the most output or the longest or shortest + * running process time. + */ + for (i = 0; i < n; i++) + if (pp->children[(pp->output_owner + i) % n].state == GIT_CP_WORKING) + break; + pp->output_owner = (pp->output_owner + i) % n; + } + } + return result; +} + +int run_processes_parallel(int n, + get_next_task_fn get_next_task, + start_failure_fn start_failure, + task_finished_fn task_finished, + void *pp_cb) +{ + int i, code; + int output_timeout = 100; + int spawn_cap = 4; + struct parallel_processes pp; + + pp_init(&pp, n, get_next_task, start_failure, task_finished, pp_cb); + while (1) { + for (i = 0; + i < spawn_cap && !pp.shutdown && + pp.nr_processes < pp.max_processes; + i++) { + code = pp_start_one(&pp); + if (!code) + continue; + if (code < 0) { + pp.shutdown = 1; + kill_children(&pp, -code); + } + break; + } + if (!pp.nr_processes) + break; + pp_buffer_stderr(&pp, output_timeout); + pp_output(&pp); + code = pp_collect_finished(&pp); + if (code) { + pp.shutdown = 1; + if (code < 0) + kill_children(&pp, -code); + } + } + + pp_cleanup(&pp); + return 0; +} diff --combined sequencer.c index 80487860c8,e60e75ad74..e66f2fe0f0 --- a/sequencer.c +++ b/sequencer.c @@@ -124,42 -124,33 +124,33 @@@ static const char *action_name(const st struct commit_message { char *parent_label; - const char *label; - const char *subject; + char *label; + char *subject; const char *message; }; static int get_message(struct commit *commit, struct commit_message *out) { const char *abbrev, *subject; - int abbrev_len, subject_len; - char *q; + int subject_len; - if (!git_commit_encoding) - git_commit_encoding = "UTF-8"; - - out->message = logmsg_reencode(commit, NULL, git_commit_encoding); + out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding()); abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV); - abbrev_len = strlen(abbrev); subject_len = find_commit_subject(out->message, &subject); - out->parent_label = xmalloc(strlen("parent of ") + abbrev_len + - strlen("... ") + subject_len + 1); - q = out->parent_label; - q = mempcpy(q, "parent of ", strlen("parent of ")); - out->label = q; - q = mempcpy(q, abbrev, abbrev_len); - q = mempcpy(q, "... ", strlen("... ")); - out->subject = q; - q = mempcpy(q, subject, subject_len); - *q = '\0'; + out->subject = xmemdupz(subject, subject_len); + out->label = xstrfmt("%s... %s", abbrev, out->subject); + out->parent_label = xstrfmt("parent of %s", out->label); + return 0; } static void free_message(struct commit *commit, struct commit_message *msg) { free(msg->parent_label); + free(msg->label); + free(msg->subject); unuse_commit_buffer(commit, msg->message); } @@@ -886,7 -877,7 +877,7 @@@ static int sequencer_rollback(struct re if (!f) return error(_("cannot open %s: %s"), git_path_head_file(), strerror(errno)); - if (strbuf_getline(&buf, f, '\n')) { + if (strbuf_getline_lf(&buf, f)) { error(_("cannot read %s: %s"), git_path_head_file(), ferror(f) ? strerror(errno) : _("unexpected end of file")); fclose(f); diff --combined setup.c index 59ec6587aa,669062a090..de1a2a7ea5 --- a/setup.c +++ b/setup.c @@@ -88,7 -88,7 +88,7 @@@ char *prefix_path_gently(const char *pr const char *orig = path; char *sanitized; if (is_absolute_path(orig)) { - sanitized = xmalloc(strlen(path) + 1); + sanitized = xmallocz(strlen(path)); if (remaining_prefix) *remaining_prefix = 0; if (normalize_path_copy_len(sanitized, path, remaining_prefix)) { @@@ -139,7 -139,9 +139,7 @@@ int check_filename(const char *prefix, if (arg[2] == '\0') /* ":/" is root dir, always exists */ return 1; name = arg + 2; - } else if (!no_wildcard(arg)) - return 1; - else if (prefix) + } else if (prefix) name = prefix_filename(prefix, strlen(prefix), arg); else name = arg; @@@ -200,7 -202,7 +200,7 @@@ void verify_filename(const char *prefix { if (*arg == '-') die("bad flag '%s' used after filename", arg); - if (check_filename(prefix, arg)) + if (check_filename(prefix, arg) || !no_wildcard(arg)) return; die_verify_filename(prefix, arg, diagnose_misspelt_rev); } @@@ -449,6 -451,17 +449,6 @@@ static int check_repository_format_gent return ret; } -static void update_linked_gitdir(const char *gitfile, const char *gitdir) -{ - struct strbuf path = STRBUF_INIT; - struct stat st; - - strbuf_addf(&path, "%s/gitdir", gitdir); - if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL)) - write_file(path.buf, "%s", gitfile); - strbuf_release(&path); -} - /* * Try to read the location of the git directory from the .git file, * return path to git directory if found. @@@ -486,14 -499,13 +486,13 @@@ const char *read_gitfile_gently(const c error_code = READ_GITFILE_ERR_OPEN_FAILED; goto cleanup_return; } - buf = xmalloc(st.st_size + 1); + buf = xmallocz(st.st_size); len = read_in_full(fd, buf, st.st_size); close(fd); if (len != st.st_size) { error_code = READ_GITFILE_ERR_READ_FAILED; goto cleanup_return; } - buf[len] = '\0'; if (!starts_with(buf, "gitdir: ")) { error_code = READ_GITFILE_ERR_INVALID_FORMAT; goto cleanup_return; @@@ -518,6 -530,7 +517,6 @@@ error_code = READ_GITFILE_ERR_NOT_A_REPO; goto cleanup_return; } - update_linked_gitdir(path, dir); path = real_path(dir); cleanup_return: diff --combined sha1_file.c index aab1872b4c,ab16c7b98c..02517009c1 --- a/sha1_file.c +++ b/sha1_file.c @@@ -253,7 -253,7 +253,7 @@@ static int link_alt_odb_entry(const cha { struct alternate_object_database *ent; struct alternate_object_database *alt; - int pfxlen, entlen; + size_t pfxlen, entlen; struct strbuf pathbuf = STRBUF_INIT; if (!is_absolute_path(entry) && relative_base) { @@@ -273,8 -273,8 +273,8 @@@ while (pfxlen && pathbuf.buf[pfxlen-1] == '/') pfxlen -= 1; - entlen = pfxlen + 43; /* '/' + 2 hex + '/' + 38 hex + NUL */ - ent = xmalloc(sizeof(*ent) + entlen); + entlen = st_add(pfxlen, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */ + ent = xmalloc(st_add(sizeof(*ent), entlen)); memcpy(ent->base, pathbuf.buf, pfxlen); strbuf_release(&pathbuf); @@@ -396,7 -396,7 +396,7 @@@ void add_to_alternates_file(const char struct strbuf line = STRBUF_INIT; int found = 0; - while (strbuf_getline(&line, in, '\n') != EOF) { + while (strbuf_getline(&line, in) != EOF) { if (!strcmp(reference, line.buf)) { found = 1; break; @@@ -1134,7 -1134,7 +1134,7 @@@ unsigned char *use_pack(struct packed_g static struct packed_git *alloc_packed_git(int extra) { - struct packed_git *p = xmalloc(sizeof(*p) + extra); + struct packed_git *p = xmalloc(st_add(sizeof(*p), extra)); memset(p, 0, sizeof(*p)); p->pack_fd = -1; return p; @@@ -1168,7 -1168,7 +1168,7 @@@ struct packed_git *add_packed_git(cons * ".pack" is long enough to hold any suffix we're adding (and * the use xsnprintf double-checks that) */ - alloc = path_len + strlen(".pack") + 1; + alloc = st_add3(path_len, strlen(".pack"), 1); p = alloc_packed_git(alloc); memcpy(p->pack_name, path, path_len); @@@ -1196,7 -1196,7 +1196,7 @@@ struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path) { const char *path = sha1_pack_name(sha1); - int alloc = strlen(path) + 1; + size_t alloc = st_add(strlen(path), 1); struct packed_git *p = alloc_packed_git(alloc); memcpy(p->pack_name, path, alloc); /* includes NUL */ @@@ -1413,10 -1413,12 +1413,12 @@@ static void mark_bad_packed_object(stru { unsigned i; for (i = 0; i < p->num_bad_objects; i++) - if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i)) + if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i)) return; - p->bad_object_sha1 = xrealloc(p->bad_object_sha1, 20 * (p->num_bad_objects + 1)); - hashcpy(p->bad_object_sha1 + 20 * p->num_bad_objects, sha1); + p->bad_object_sha1 = xrealloc(p->bad_object_sha1, + st_mult(GIT_SHA1_RAWSZ, + st_add(p->num_bad_objects, 1))); + hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1); p->num_bad_objects++; } @@@ -1942,7 -1944,7 +1944,7 @@@ static enum object_type packed_to_objec /* Push the object we're going to leave behind */ if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) { poi_stack_alloc = alloc_nr(poi_stack_nr); - poi_stack = xmalloc(sizeof(off_t)*poi_stack_alloc); + ALLOC_ARRAY(poi_stack, poi_stack_alloc); memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr); } else { ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc); @@@ -2308,7 -2310,7 +2310,7 @@@ void *unpack_entry(struct packed_git *p if (delta_stack_nr >= delta_stack_alloc && delta_stack == small_delta_stack) { delta_stack_alloc = alloc_nr(delta_stack_nr); - delta_stack = xmalloc(sizeof(*delta_stack)*delta_stack_alloc); + ALLOC_ARRAY(delta_stack, delta_stack_alloc); memcpy(delta_stack, small_delta_stack, sizeof(*delta_stack)*delta_stack_nr); } else { diff --combined sha1_name.c index d0f844db89,532db4fdc6..3acf221f92 --- a/sha1_name.c +++ b/sha1_name.c @@@ -87,9 -87,8 +87,8 @@@ static void find_short_object_filename( * object databases including our own. */ const char *objdir = get_object_directory(); - int objdir_len = strlen(objdir); - int entlen = objdir_len + 43; - fakeent = xmalloc(sizeof(*fakeent) + entlen); + size_t objdir_len = strlen(objdir); + fakeent = xmalloc(st_add3(sizeof(*fakeent), objdir_len, 43)); memcpy(fakeent->base, objdir, objdir_len); fakeent->name = fakeent->base + objdir_len + 1; fakeent->name[-1] = '/'; @@@ -848,12 -847,8 +847,12 @@@ static int get_sha1_1(const char *name * through history and returning the first commit whose message starts * the given regular expression. * - * For future extension, ':/!' is reserved. If you want to match a message - * beginning with a '!', you have to repeat the exclamation mark. + * For negative-matching, prefix the pattern-part with '!-', like: ':/!-WIP'. + * + * For a literal '!' character at the beginning of a pattern, you have to repeat + * that, like: ':/!!foo' + * + * For future extension, all other sequences beginning with ':/!' are reserved. */ /* Remember to update object flag allocation in object.h */ @@@ -882,22 -877,16 +881,22 @@@ static int get_sha1_oneline(const char { struct commit_list *backup = NULL, *l; int found = 0; + int negative = 0; regex_t regex; if (prefix[0] == '!') { - if (prefix[1] != '!') - die ("Invalid search pattern: %s", prefix); prefix++; + + if (prefix[0] == '-') { + prefix++; + negative = 1; + } else if (prefix[0] != '!') { + return -1; + } } if (regcomp(®ex, prefix, REG_EXTENDED)) - die("Invalid search pattern: %s", prefix); + return -1; for (l = list; l; l = l->next) { l->item->object.flags |= ONELINE_SEEN; @@@ -913,7 -902,7 +912,7 @@@ continue; buf = get_commit_buffer(commit, NULL); p = strstr(buf, "\n\n"); - matches = p && !regexec(®ex, p + 2, 0, NULL, 0); + matches = negative ^ (p && !regexec(®ex, p + 2, 0, NULL, 0)); unuse_commit_buffer(commit, buf); if (matches) { diff --combined strbuf.c index bab316dda8,de7a7c2730..f60e2ee72b --- a/strbuf.c +++ b/strbuf.c @@@ -384,17 -384,6 +384,17 @@@ ssize_t strbuf_read(struct strbuf *sb, return sb->len - oldlen; } +ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint) +{ + ssize_t cnt; + + strbuf_grow(sb, hint ? hint : 8192); + cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); + if (cnt > 0) + strbuf_setlen(sb, sb->len + cnt); + return cnt; +} + #define STRBUF_MAXLINK (2*PATH_MAX) int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) @@@ -512,37 -501,15 +512,37 @@@ int strbuf_getwholeline(struct strbuf * } #endif -int strbuf_getline(struct strbuf *sb, FILE *fp, int term) +static int strbuf_getdelim(struct strbuf *sb, FILE *fp, int term) { if (strbuf_getwholeline(sb, fp, term)) return EOF; - if (sb->buf[sb->len-1] == term) - strbuf_setlen(sb, sb->len-1); + if (sb->buf[sb->len - 1] == term) + strbuf_setlen(sb, sb->len - 1); return 0; } +int strbuf_getline(struct strbuf *sb, FILE *fp) +{ + if (strbuf_getwholeline(sb, fp, '\n')) + return EOF; + if (sb->buf[sb->len - 1] == '\n') { + strbuf_setlen(sb, sb->len - 1); + if (sb->len && sb->buf[sb->len - 1] == '\r') + strbuf_setlen(sb, sb->len - 1); + } + return 0; +} + +int strbuf_getline_lf(struct strbuf *sb, FILE *fp) +{ + return strbuf_getdelim(sb, fp, '\n'); +} + +int strbuf_getline_nul(struct strbuf *sb, FILE *fp) +{ + return strbuf_getdelim(sb, fp, '\0'); +} + int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term) { strbuf_reset(sb); @@@ -718,7 -685,7 +718,7 @@@ char *xstrdup_tolower(const char *strin size_t len, i; len = strlen(string); - result = xmalloc(len + 1); + result = xmallocz(len); for (i = 0; i < len; i++) result[i] = tolower(string[i]); result[i] = '\0'; diff --combined submodule.c index b83939c294,bd97b15a92..ac61c65611 --- a/submodule.c +++ b/submodule.c @@@ -12,7 -12,6 +12,7 @@@ #include "sha1-array.h" #include "argv-array.h" #include "blob.h" +#include "thread-utils.h" static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; static struct string_list changed_submodule_paths; @@@ -123,7 -122,7 +123,7 @@@ static int add_submodule_odb(const cha struct strbuf objects_directory = STRBUF_INIT; struct alternate_object_database *alt_odb; int ret = 0; - int alloc; + size_t alloc; strbuf_git_path_submodule(&objects_directory, path, "objects/"); if (!is_directory(objects_directory.buf)) { @@@ -138,8 -137,8 +138,8 @@@ objects_directory.len)) goto done; - alloc = objects_directory.len + 42; /* for "12/345..." sha1 */ - alt_odb = xmalloc(sizeof(*alt_odb) + alloc); + alloc = st_add(objects_directory.len, 42); /* for "12/345..." sha1 */ + alt_odb = xmalloc(st_add(sizeof(*alt_odb), alloc)); alt_odb->next = alt_odb_list; xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf); alt_odb->name = alt_odb->base + objects_directory.len; @@@ -611,28 -610,37 +611,28 @@@ static void calculate_changed_submodule initialized_fetch_ref_tips = 0; } -int fetch_populated_submodules(const struct argv_array *options, - const char *prefix, int command_line_option, - int quiet) +struct submodule_parallel_fetch { + int count; + struct argv_array args; + const char *work_tree; + const char *prefix; + int command_line_option; + int quiet; + int result; +}; +#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0} + +static int get_next_submodule(struct child_process *cp, + struct strbuf *err, void *data, void **task_cb) { - int i, result = 0; - struct child_process cp = CHILD_PROCESS_INIT; - struct argv_array argv = ARGV_ARRAY_INIT; - const char *work_tree = get_git_work_tree(); - if (!work_tree) - goto out; - - if (read_cache() < 0) - die("index file corrupt"); - - argv_array_push(&argv, "fetch"); - for (i = 0; i < options->argc; i++) - argv_array_push(&argv, options->argv[i]); - argv_array_push(&argv, "--recurse-submodules-default"); - /* default value, "--submodule-prefix" and its value are added later */ - - cp.env = local_repo_env; - cp.git_cmd = 1; - cp.no_stdin = 1; - - calculate_changed_submodule_paths(); + int ret = 0; + struct submodule_parallel_fetch *spf = data; - for (i = 0; i < active_nr; i++) { + for (; spf->count < active_nr; spf->count++) { struct strbuf submodule_path = STRBUF_INIT; struct strbuf submodule_git_dir = STRBUF_INIT; struct strbuf submodule_prefix = STRBUF_INIT; - const struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[spf->count]; const char *git_dir, *default_argv; const struct submodule *submodule; @@@ -644,7 -652,7 +644,7 @@@ submodule = submodule_from_name(null_sha1, ce->name); default_argv = "yes"; - if (command_line_option == RECURSE_SUBMODULES_DEFAULT) { + if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) { if (submodule && submodule->fetch_recurse != RECURSE_SUBMODULES_NONE) { @@@ -667,101 -675,40 +667,101 @@@ default_argv = "on-demand"; } } - } else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) { + } else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) { if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name)) continue; default_argv = "on-demand"; } - strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name); + strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name); strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf); - strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name); + strbuf_addf(&submodule_prefix, "%s%s/", spf->prefix, ce->name); git_dir = read_gitfile(submodule_git_dir.buf); if (!git_dir) git_dir = submodule_git_dir.buf; if (is_directory(git_dir)) { - if (!quiet) - printf("Fetching submodule %s%s\n", prefix, ce->name); - cp.dir = submodule_path.buf; - argv_array_push(&argv, default_argv); - argv_array_push(&argv, "--submodule-prefix"); - argv_array_push(&argv, submodule_prefix.buf); - cp.argv = argv.argv; - if (run_command(&cp)) - result = 1; - argv_array_pop(&argv); - argv_array_pop(&argv); - argv_array_pop(&argv); + child_process_init(cp); + cp->dir = strbuf_detach(&submodule_path, NULL); + cp->env = local_repo_env; + cp->git_cmd = 1; + if (!spf->quiet) + strbuf_addf(err, "Fetching submodule %s%s\n", + spf->prefix, ce->name); + argv_array_init(&cp->args); + argv_array_pushv(&cp->args, spf->args.argv); + argv_array_push(&cp->args, default_argv); + argv_array_push(&cp->args, "--submodule-prefix"); + argv_array_push(&cp->args, submodule_prefix.buf); + ret = 1; } strbuf_release(&submodule_path); strbuf_release(&submodule_git_dir); strbuf_release(&submodule_prefix); + if (ret) { + spf->count++; + return 1; + } } - argv_array_clear(&argv); + return 0; +} + +static int fetch_start_failure(struct child_process *cp, + struct strbuf *err, + void *cb, void *task_cb) +{ + struct submodule_parallel_fetch *spf = cb; + + spf->result = 1; + + return 0; +} + +static int fetch_finish(int retvalue, struct child_process *cp, + struct strbuf *err, void *cb, void *task_cb) +{ + struct submodule_parallel_fetch *spf = cb; + + if (retvalue) + spf->result = 1; + + return 0; +} + +int fetch_populated_submodules(const struct argv_array *options, + const char *prefix, int command_line_option, + int quiet, int max_parallel_jobs) +{ + int i; + struct submodule_parallel_fetch spf = SPF_INIT; + + spf.work_tree = get_git_work_tree(); + spf.command_line_option = command_line_option; + spf.quiet = quiet; + spf.prefix = prefix; + + if (!spf.work_tree) + goto out; + + if (read_cache() < 0) + die("index file corrupt"); + + argv_array_push(&spf.args, "fetch"); + for (i = 0; i < options->argc; i++) + argv_array_push(&spf.args, options->argv[i]); + argv_array_push(&spf.args, "--recurse-submodules-default"); + /* default value, "--submodule-prefix" and its value are added later */ + + calculate_changed_submodule_paths(); + run_processes_parallel(max_parallel_jobs, + get_next_submodule, + fetch_start_failure, + fetch_finish, + &spf); + + argv_array_clear(&spf.args); out: string_list_clear(&changed_submodule_paths, 1); - return result; + return spf.result; } unsigned is_submodule_modified(const char *path, int ignore_untracked) diff --combined test-path-utils.c index 6232dfe661,0c15f1821f..ba805b374c --- a/test-path-utils.c +++ b/test-path-utils.c @@@ -8,21 -8,14 +8,14 @@@ */ static int normalize_ceiling_entry(struct string_list_item *item, void *unused) { - const char *ceil = item->string; - int len = strlen(ceil); - char buf[PATH_MAX+1]; + char *ceil = item->string; - if (len == 0) + if (!*ceil) die("Empty path is not supported"); - if (len > PATH_MAX) - die("Path \"%s\" is too long", ceil); if (!is_absolute_path(ceil)) die("Path \"%s\" is not absolute", ceil); - if (normalize_path_copy(buf, ceil) < 0) + if (normalize_path_copy(ceil, ceil) < 0) die("Path \"%s\" could not be normalized", ceil); - len = strlen(buf); - free(item->string); - item->string = xstrdup(buf); return 1; } @@@ -56,7 -49,7 +49,7 @@@ static int test_function(struct test_da if (!data[i].from) to = func(NULL); else { - strcpy(buffer, data[i].from); + xsnprintf(buffer, sizeof(buffer), "%s", data[i].from); to = func(buffer); } if (!strcmp(to, data[i].to)) @@@ -166,7 -159,7 +159,7 @@@ static struct test_data dirname_data[] int main(int argc, char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { - char *buf = xmalloc(PATH_MAX + 1); + char *buf = xmallocz(strlen(argv[2])); int rv = normalize_path_copy(buf, argv[2]); if (rv) buf = "++failed++"; diff --combined transport.c index e20bb771fa,988047b12e..ca3cfa4b00 --- a/transport.c +++ b/transport.c @@@ -17,6 -17,133 +17,6 @@@ #include "sha1-array.h" #include "sigchain.h" -/* rsync support */ - -/* - * We copy packed-refs and refs/ into a temporary file, then read the - * loose refs recursively (sorting whenever possible), and then inserting - * those packed refs that are not yet in the list (not validating, but - * assuming that the file is sorted). - * - * Appears refactoring this from refs.c is too cumbersome. - */ - -static int str_cmp(const void *a, const void *b) -{ - const char *s1 = a; - const char *s2 = b; - - return strcmp(s1, s2); -} - -/* path->buf + name_offset is expected to point to "refs/" */ - -static int read_loose_refs(struct strbuf *path, int name_offset, - struct ref **tail) -{ - DIR *dir = opendir(path->buf); - struct dirent *de; - struct { - char **entries; - int nr, alloc; - } list; - int i, pathlen; - - if (!dir) - return -1; - - memset (&list, 0, sizeof(list)); - - while ((de = readdir(dir))) { - if (is_dot_or_dotdot(de->d_name)) - continue; - ALLOC_GROW(list.entries, list.nr + 1, list.alloc); - list.entries[list.nr++] = xstrdup(de->d_name); - } - closedir(dir); - - /* sort the list */ - - qsort(list.entries, list.nr, sizeof(char *), str_cmp); - - pathlen = path->len; - strbuf_addch(path, '/'); - - for (i = 0; i < list.nr; i++, strbuf_setlen(path, pathlen + 1)) { - strbuf_addstr(path, list.entries[i]); - if (read_loose_refs(path, name_offset, tail)) { - int fd = open(path->buf, O_RDONLY); - char buffer[40]; - struct ref *next; - - if (fd < 0) - continue; - next = alloc_ref(path->buf + name_offset); - if (read_in_full(fd, buffer, 40) != 40 || - get_oid_hex(buffer, &next->old_oid)) { - close(fd); - free(next); - continue; - } - close(fd); - (*tail)->next = next; - *tail = next; - } - } - strbuf_setlen(path, pathlen); - - for (i = 0; i < list.nr; i++) - free(list.entries[i]); - free(list.entries); - - return 0; -} - -/* insert the packed refs for which no loose refs were found */ - -static void insert_packed_refs(const char *packed_refs, struct ref **list) -{ - FILE *f = fopen(packed_refs, "r"); - static char buffer[PATH_MAX]; - - if (!f) - return; - - for (;;) { - int cmp = 0; /* assigned before used */ - int len; - - if (!fgets(buffer, sizeof(buffer), f)) { - fclose(f); - return; - } - - if (!isxdigit(buffer[0])) - continue; - len = strlen(buffer); - if (len && buffer[len - 1] == '\n') - buffer[--len] = '\0'; - if (len < 41) - continue; - while ((*list)->next && - (cmp = strcmp(buffer + 41, - (*list)->next->name)) > 0) - list = &(*list)->next; - if (!(*list)->next || cmp < 0) { - struct ref *next = alloc_ref(buffer + 41); - buffer[40] = '\0'; - if (get_oid_hex(buffer, &next->old_oid)) { - warning ("invalid SHA-1: %s", buffer); - free(next); - continue; - } - next->next = (*list)->next; - (*list)->next = next; - list = &(*list)->next; - } - } -} - static void set_upstreams(struct transport *transport, struct ref *refs, int pretend) { @@@ -65,6 -192,205 +65,6 @@@ } } -static const char *rsync_url(const char *url) -{ - if (!starts_with(url, "rsync://")) - skip_prefix(url, "rsync:", &url); - return url; -} - -static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) -{ - struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; - struct ref dummy = {NULL}, *tail = &dummy; - struct child_process rsync = CHILD_PROCESS_INIT; - const char *args[5]; - int temp_dir_len; - - if (for_push) - return NULL; - - /* copy the refs to the temporary directory */ - - strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX")); - if (!mkdtemp(temp_dir.buf)) - die_errno ("Could not make temporary directory"); - temp_dir_len = temp_dir.len; - - strbuf_addstr(&buf, rsync_url(transport->url)); - strbuf_addstr(&buf, "/refs"); - - rsync.argv = args; - rsync.stdout_to_stderr = 1; - args[0] = "rsync"; - args[1] = (transport->verbose > 1) ? "-rv" : "-r"; - args[2] = buf.buf; - args[3] = temp_dir.buf; - args[4] = NULL; - - if (run_command(&rsync)) - die ("Could not run rsync to get refs"); - - strbuf_reset(&buf); - strbuf_addstr(&buf, rsync_url(transport->url)); - strbuf_addstr(&buf, "/packed-refs"); - - args[2] = buf.buf; - - if (run_command(&rsync)) - die ("Could not run rsync to get refs"); - - /* read the copied refs */ - - strbuf_addstr(&temp_dir, "/refs"); - read_loose_refs(&temp_dir, temp_dir_len + 1, &tail); - strbuf_setlen(&temp_dir, temp_dir_len); - - tail = &dummy; - strbuf_addstr(&temp_dir, "/packed-refs"); - insert_packed_refs(temp_dir.buf, &tail); - strbuf_setlen(&temp_dir, temp_dir_len); - - if (remove_dir_recursively(&temp_dir, 0)) - warning ("Error removing temporary directory %s.", - temp_dir.buf); - - strbuf_release(&buf); - strbuf_release(&temp_dir); - - return dummy.next; -} - -static int fetch_objs_via_rsync(struct transport *transport, - int nr_objs, struct ref **to_fetch) -{ - struct child_process rsync = CHILD_PROCESS_INIT; - - rsync.stdout_to_stderr = 1; - argv_array_push(&rsync.args, "rsync"); - argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r"); - argv_array_push(&rsync.args, "--ignore-existing"); - argv_array_push(&rsync.args, "--exclude"); - argv_array_push(&rsync.args, "info"); - argv_array_pushf(&rsync.args, "%s/objects/", rsync_url(transport->url)); - argv_array_push(&rsync.args, get_object_directory()); - - /* NEEDSWORK: handle one level of alternates */ - return run_command(&rsync); -} - -static int write_one_ref(const char *name, const struct object_id *oid, - int flags, void *data) -{ - struct strbuf *buf = data; - int len = buf->len; - - /* when called via for_each_ref(), flags is non-zero */ - if (flags && !starts_with(name, "refs/heads/") && - !starts_with(name, "refs/tags/")) - return 0; - - strbuf_addstr(buf, name); - if (safe_create_leading_directories(buf->buf) || - write_file_gently(buf->buf, "%s", oid_to_hex(oid))) - return error("problems writing temporary file %s: %s", - buf->buf, strerror(errno)); - strbuf_setlen(buf, len); - return 0; -} - -static int write_refs_to_temp_dir(struct strbuf *temp_dir, - int refspec_nr, const char **refspec) -{ - int i; - - for (i = 0; i < refspec_nr; i++) { - struct object_id oid; - char *ref; - - if (dwim_ref(refspec[i], strlen(refspec[i]), oid.hash, &ref) != 1) - return error("Could not get ref %s", refspec[i]); - - if (write_one_ref(ref, &oid, 0, temp_dir)) { - free(ref); - return -1; - } - free(ref); - } - return 0; -} - -static int rsync_transport_push(struct transport *transport, - int refspec_nr, const char **refspec, int flags) -{ - struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; - int result = 0, i; - struct child_process rsync = CHILD_PROCESS_INIT; - const char *args[10]; - - if (flags & TRANSPORT_PUSH_MIRROR) - return error("rsync transport does not support mirror mode"); - - /* first push the objects */ - - strbuf_addstr(&buf, rsync_url(transport->url)); - strbuf_addch(&buf, '/'); - - rsync.argv = args; - rsync.stdout_to_stderr = 1; - i = 0; - args[i++] = "rsync"; - args[i++] = "-a"; - if (flags & TRANSPORT_PUSH_DRY_RUN) - args[i++] = "--dry-run"; - if (transport->verbose > 1) - args[i++] = "-v"; - args[i++] = "--ignore-existing"; - args[i++] = "--exclude"; - args[i++] = "info"; - args[i++] = get_object_directory(); - args[i++] = buf.buf; - args[i++] = NULL; - - if (run_command(&rsync)) - return error("Could not push objects to %s", - rsync_url(transport->url)); - - /* copy the refs to the temporary directory; they could be packed. */ - - strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX")); - if (!mkdtemp(temp_dir.buf)) - die_errno ("Could not make temporary directory"); - strbuf_addch(&temp_dir, '/'); - - if (flags & TRANSPORT_PUSH_ALL) { - if (for_each_ref(write_one_ref, &temp_dir)) - return -1; - } else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec)) - return -1; - - i = 2; - if (flags & TRANSPORT_PUSH_DRY_RUN) - args[i++] = "--dry-run"; - if (!(flags & TRANSPORT_PUSH_FORCE)) - args[i++] = "--ignore-existing"; - args[i++] = temp_dir.buf; - args[i++] = rsync_url(transport->url); - args[i++] = NULL; - if (run_command(&rsync)) - result = error("Could not push to %s", - rsync_url(transport->url)); - - if (remove_dir_recursively(&temp_dir, 0)) - warning ("Could not remove temporary directory %s.", - temp_dir.buf); - - strbuf_release(&buf); - strbuf_release(&temp_dir); - - return result; -} - struct bundle_transport_data { int fd; struct bundle_header header; @@@ -155,24 -481,17 +155,24 @@@ static int set_git_option(struct git_tr return 1; } -static int connect_setup(struct transport *transport, int for_push, int verbose) +static int connect_setup(struct transport *transport, int for_push) { struct git_transport_data *data = transport->data; + int flags = transport->verbose > 0 ? CONNECT_VERBOSE : 0; if (data->conn) return 0; + switch (transport->family) { + case TRANSPORT_FAMILY_ALL: break; + case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break; + case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break; + } + data->conn = git_connect(data->fd, transport->url, for_push ? data->options.receivepack : data->options.uploadpack, - verbose ? CONNECT_VERBOSE : 0); + flags); return 0; } @@@ -182,7 -501,7 +182,7 @@@ static struct ref *get_refs_via_connect struct git_transport_data *data = transport->data; struct ref *refs; - connect_setup(transport, for_push, 0); + connect_setup(transport, for_push); get_remote_heads(data->fd[0], NULL, 0, &refs, for_push ? REF_NORMAL : 0, &data->extra_have, @@@ -217,7 -536,7 +217,7 @@@ static int fetch_refs_via_pack(struct t args.update_shallow = data->options.update_shallow; if (!data->got_remote_heads) { - connect_setup(transport, 0, 0); + connect_setup(transport, 0); get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0, NULL, &data->shallow); data->got_remote_heads = 1; @@@ -493,7 -812,7 +493,7 @@@ static int git_transport_push(struct tr if (!data->got_remote_heads) { struct ref *tmp_refs; - connect_setup(transport, 1, 0); + connect_setup(transport, 1); get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL, &data->shallow); @@@ -665,7 -984,11 +665,7 @@@ struct transport *transport_get(struct if (helper) { transport_helper_init(ret, helper); } else if (starts_with(url, "rsync:")) { - transport_check_allowed("rsync"); - ret->get_refs_list = get_refs_via_rsync; - ret->fetch = fetch_objs_via_rsync; - ret->push = rsync_transport_push; - ret->smart_options = NULL; + die("git-over-rsync is no longer supported"); } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); transport_check_allowed("file"); @@@ -983,7 -1306,7 +983,7 @@@ int transport_fetch_refs(struct transpo * This condition shouldn't be met in a non-deepening fetch * (see builtin/fetch.c:quickfetch()). */ - heads = xmalloc(nr_refs * sizeof(*heads)); + ALLOC_ARRAY(heads, nr_refs); for (rm = refs; rm; rm = rm->next) heads[nr_heads++] = rm; } @@@ -1027,7 -1350,7 +1027,7 @@@ int transport_disconnect(struct transpo */ char *transport_anonymize_url(const char *url) { - char *anon_url, *scheme_prefix, *anon_part; + char *scheme_prefix, *anon_part; size_t anon_len, prefix_len = 0; anon_part = strchr(url, '@'); @@@ -1061,10 -1384,8 +1061,8 @@@ goto literal_copy; prefix_len = scheme_prefix - url + 3; } - anon_url = xcalloc(1, 1 + prefix_len + anon_len); - memcpy(anon_url, url, prefix_len); - memcpy(anon_url + prefix_len, anon_part, anon_len); - return anon_url; + return xstrfmt("%.*s%.*s", (int)prefix_len, url, + (int)anon_len, anon_part); literal_copy: return xstrdup(url); } diff --combined wrapper.c index 29a45d2654,32c6c609da..9afc1a021c --- a/wrapper.c +++ b/wrapper.c @@@ -152,6 -152,9 +152,9 @@@ void *xcalloc(size_t nmemb, size_t size { void *ret; + if (unsigned_mult_overflows(nmemb, size)) + die("data too large to fit into virtual memory space"); + memory_limit_check(size * nmemb, 0); ret = calloc(nmemb, size); if (!ret && (!nmemb || !size)) @@@ -236,24 -239,8 +239,24 @@@ ssize_t xread(int fd, void *buf, size_ len = MAX_IO_SIZE; while (1) { nr = read(fd, buf, len); - if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) - continue; + if (nr < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) { + struct pollfd pfd; + pfd.events = POLLIN; + pfd.fd = fd; + /* + * it is OK if this poll() failed; we + * want to leave this infinite loop + * only when read() returns with + * success, or an expected failure, + * which would be checked by the next + * call to read(2). + */ + poll(&pfd, 1, -1); + } + } return nr; } }