diff options
Diffstat (limited to 'builtin')
58 files changed, 1370 insertions, 988 deletions
diff --git a/builtin/am.c b/builtin/am.c index de235cf11a..d003939bc5 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -46,21 +46,6 @@ static int is_empty_file(const char *filename) } /** - * Like strbuf_getline(), but treats both '\n' and "\r\n" as line terminators. - */ -static int strbuf_getline_crlf(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 > 0 && sb->buf[sb->len - 1] == '\r') - strbuf_setlen(sb, sb->len - 1); - } - return 0; -} - -/** * Returns the length of the first line of msg. */ static int linelen(const char *msg) @@ -284,7 +269,7 @@ static char *read_shell_var(FILE *fp, const char *key) struct strbuf sb = STRBUF_INIT; const char *str; - if (strbuf_getline(&sb, fp, '\n')) + if (strbuf_getline_lf(&sb, fp)) goto fail; if (!skip_prefix(sb.buf, key, &str)) @@ -573,7 +558,7 @@ static int copy_notes_for_rebase(const struct am_state *state) fp = xfopen(am_path(state, "rewritten"), "r"); - while (!strbuf_getline(&sb, fp, '\n')) { + while (!strbuf_getline_lf(&sb, fp)) { unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ]; if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) { @@ -628,7 +613,7 @@ static int is_mail(FILE *fp) if (regcomp(®ex, header_regex, REG_NOSUB | REG_EXTENDED)) die("invalid pattern: %s", header_regex); - while (!strbuf_getline_crlf(&sb, fp)) { + while (!strbuf_getline(&sb, fp)) { if (!sb.len) break; /* End of header */ @@ -675,7 +660,7 @@ static int detect_patch_format(const char **paths) fp = xfopen(*paths, "r"); - while (!strbuf_getline_crlf(&l1, fp)) { + while (!strbuf_getline(&l1, fp)) { if (l1.len) break; } @@ -696,9 +681,9 @@ static int detect_patch_format(const char **paths) } strbuf_reset(&l2); - strbuf_getline_crlf(&l2, fp); + strbuf_getline(&l2, fp); strbuf_reset(&l3); - strbuf_getline_crlf(&l3, fp); + strbuf_getline(&l3, fp); /* * If the second line is empty and the third is a From, Author or Date @@ -817,7 +802,7 @@ static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr) struct strbuf sb = STRBUF_INIT; int subject_printed = 0; - while (!strbuf_getline(&sb, in, '\n')) { + while (!strbuf_getline_lf(&sb, in)) { const char *str; if (str_isspace(sb.buf)) @@ -875,7 +860,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths, return error(_("could not open '%s' for reading: %s"), *paths, strerror(errno)); - while (!strbuf_getline(&sb, fp, '\n')) { + while (!strbuf_getline_lf(&sb, fp)) { if (*sb.buf == '#') continue; /* skip comment lines */ @@ -900,7 +885,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr) { struct strbuf sb = STRBUF_INIT; - while (!strbuf_getline(&sb, in, '\n')) { + while (!strbuf_getline_lf(&sb, in)) { const char *str; if (skip_prefix(sb.buf, "# User ", &str)) @@ -1317,7 +1302,7 @@ static int parse_mail(struct am_state *state, const char *mail) /* Extract message and author information */ fp = xfopen(am_path(state, "info"), "r"); - while (!strbuf_getline(&sb, fp, '\n')) { + while (!strbuf_getline_lf(&sb, fp)) { const char *x; if (skip_prefix(sb.buf, "Subject: ", &x)) { @@ -1383,7 +1368,7 @@ static int get_mail_commit_sha1(unsigned char *commit_id, const char *mail) FILE *fp = xfopen(mail, "r"); const char *x; - if (strbuf_getline(&sb, fp, '\n')) + if (strbuf_getline_lf(&sb, fp)) return -1; if (!skip_prefix(sb.buf, "From ", &x)) @@ -1657,7 +1642,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa init_revisions(&rev_info, NULL); rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; - diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1); + diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); add_pending_sha1(&rev_info, "HEAD", our_tree, 0); diff_setup_done(&rev_info.diffopt); run_diff_index(&rev_info, 1); @@ -1821,7 +1806,7 @@ static int do_interactive(struct am_state *state) if (!pager) pager = "cat"; - argv_array_push(&cp.args, pager); + prepare_pager_args(&cp, pager); argv_array_push(&cp.args, am_path(state, "patch")); run_command(&cp); } diff --git a/builtin/apply.c b/builtin/apply.c index deb1364fa8..8e4da2e1bd 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -931,22 +931,19 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, return find_name(line, NULL, p_value, TERM_TAB); if (orig_name) { - int len; - const char *name; + int len = strlen(orig_name); char *another; - name = orig_name; - len = strlen(name); if (isnull) - die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr); + die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), + orig_name, linenr); another = find_name(line, NULL, p_value, TERM_TAB); - if (!another || memcmp(another, name, len + 1)) + if (!another || memcmp(another, orig_name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr); free(another); return orig_name; - } - else { + } else { /* expect "/dev/null" */ if (memcmp("/dev/null", line, 9) || line[9] != '\n') die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr); @@ -956,21 +953,15 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, static int gitdiff_oldname(const char *line, struct patch *patch) { - char *orig = patch->old_name; patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, DIFF_OLD_NAME); - if (orig != patch->old_name) - free(orig); return 0; } static int gitdiff_newname(const char *line, struct patch *patch) { - char *orig = patch->new_name; patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, DIFF_NEW_NAME); - if (orig != patch->new_name) - free(orig); return 0; } @@ -1872,6 +1863,11 @@ static struct fragment *parse_binary_hunk(char **buf_p, return NULL; } +/* + * Returns: + * -1 in case of error, + * the length of the parsed binary patch otherwise + */ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) { /* @@ -2017,6 +2013,8 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) linenr++; used = parse_binary(buffer + hd + llen, size - hd - llen, patch); + if (used < 0) + return -1; if (used) patchsize = used + llen; else @@ -2632,7 +2630,7 @@ static void update_image(struct image *img, 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, @@ -4373,8 +4371,10 @@ static int apply_patch(int fd, const char *filename, int options) patch->inaccurate_eof = !!(options & INACCURATE_EOF); patch->recount = !!(options & RECOUNT); nr = parse_chunk(buf.buf + offset, buf.len - offset, patch); - if (nr < 0) + if (nr < 0) { + free_patch(patch); break; + } if (apply_in_reverse) reverse_patches(patch); if (use_patch(patch)) { @@ -4383,6 +4383,8 @@ static int apply_patch(int fd, const char *filename, int options) listp = &patch->next; } else { + if (apply_verbosely) + say_patch_name(stderr, _("Skipped patch '%s'."), patch); free_patch(patch); skipped_patch++; } @@ -4464,16 +4466,6 @@ static int option_parse_p(const struct option *opt, 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) { @@ -4546,9 +4538,9 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) 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 <n> lines of context match")), { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"), diff --git a/builtin/blame.c b/builtin/blame.c index 1df13cf7f4..21f42b0b62 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -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 [<options>] [<rev-opts>] [<rev>] [--] <file>"); @@ -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; @@ -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) { @@ -459,13 +466,11 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin, 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; } @@ -1746,7 +1751,8 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat) * 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; @@ -1758,6 +1764,8 @@ static void found_guilty_entry(struct blame_entry *ent) write_filename_info(suspect->path); maybe_flush_or_die(stdout, "stdout"); } + pi->blamed_lines += ent->num_lines; + display_progress(pi->progress, pi->blamed_lines); } /* @@ -1768,6 +1776,11 @@ static void assign_blame(struct scoreboard *sb, int opt) { 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; @@ -1809,7 +1822,7 @@ static void assign_blame(struct scoreboard *sb, int opt) suspect->guilty = 1; for (;;) { struct blame_entry *next = ent->next; - found_guilty_entry(ent); + found_guilty_entry(ent, &pi); if (next) { ent = next; continue; @@ -1825,6 +1838,8 @@ static void assign_blame(struct scoreboard *sb, int opt) if (DEBUG) /* sanity */ sanity_check_refcnt(sb); } + + stop_progress(&pi.progress); } static const char *format_time(unsigned long time, const char *tz_str, @@ -2042,7 +2057,8 @@ static int prepare_lines(struct scoreboard *sb) 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; @@ -2291,6 +2307,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, unsigned mode; struct strbuf msg = STRBUF_INIT; + read_cache(); time(&now); commit = alloc_commit_node(); commit->object.parsed = 1; @@ -2392,11 +2409,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, 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; @@ -2520,6 +2532,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) 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), @@ -2555,6 +2568,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) 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); @@ -2579,6 +2593,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++; @@ -2828,11 +2849,11 @@ parse_done: read_mailmap(&mailmap, NULL); + assign_blame(&sb, opt); + if (!incremental) setup_pager(); - assign_blame(&sb, opt); - free(final_commit_name); if (incremental) diff --git a/builtin/branch.c b/builtin/branch.c index 3f6c825db1..0adba629d2 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -20,6 +20,7 @@ #include "utf8.h" #include "wt-status.h" #include "ref-filter.h" +#include "worktree.h" static const char * const builtin_branch_usage[] = { N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"), @@ -215,16 +216,21 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, int flags = 0; strbuf_branchname(&bname, argv[i]); - if (kinds == FILTER_REFS_BRANCHES && !strcmp(head, bname.buf)) { - error(_("Cannot delete the branch '%s' " - "which you are currently on."), bname.buf); - ret = 1; - continue; - } - free(name); - name = mkpathdup(fmt, bname.buf); + + if (kinds == FILTER_REFS_BRANCHES) { + char *worktree = find_shared_symref("HEAD", name); + if (worktree) { + error(_("Cannot delete branch '%s' " + "checked out at '%s'"), + bname.buf, worktree); + free(worktree); + ret = 1; + continue; + } + } + target = resolve_ref_unsafe(name, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE @@ -393,22 +399,25 @@ static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth, int current = 0; int color; struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; - const char *prefix = ""; + const char *prefix_to_show = ""; + const char *prefix_to_skip = NULL; const char *desc = item->refname; char *to_free = NULL; switch (item->kind) { case FILTER_REFS_BRANCHES: - skip_prefix(desc, "refs/heads/", &desc); + prefix_to_skip = "refs/heads/"; + skip_prefix(desc, prefix_to_skip, &desc); if (!filter->detached && !strcmp(desc, head)) current = 1; else color = BRANCH_COLOR_LOCAL; break; case FILTER_REFS_REMOTES: - skip_prefix(desc, "refs/remotes/", &desc); + prefix_to_skip = "refs/remotes/"; + skip_prefix(desc, prefix_to_skip, &desc); color = BRANCH_COLOR_REMOTE; - prefix = remote_prefix; + prefix_to_show = remote_prefix; break; case FILTER_REFS_DETACHED_HEAD: desc = to_free = get_head_description(); @@ -425,7 +434,7 @@ static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth, color = BRANCH_COLOR_CURRENT; } - strbuf_addf(&name, "%s%s", prefix, desc); + strbuf_addf(&name, "%s%s", prefix_to_show, desc); if (filter->verbose) { int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf); strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), @@ -436,8 +445,10 @@ static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth, name.buf, branch_get_color(BRANCH_COLOR_RESET)); if (item->symref) { - skip_prefix(item->symref, "refs/remotes/", &desc); - strbuf_addf(&out, " -> %s", desc); + const char *symref = item->symref; + if (prefix_to_skip) + skip_prefix(symref, prefix_to_skip, &symref); + strbuf_addf(&out, " -> %s", symref); } else if (filter->verbose) /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */ @@ -552,8 +563,7 @@ static void rename_branch(const char *oldname, const char *newname, int force) if (recovery) warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11); - /* no need to pass logmsg here as HEAD didn't really move */ - if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL)) + if (replace_each_worktree_head_symref(oldref.buf, newref.buf)) die(_("Branch renamed to %s, but HEAD is not updated!"), newname); strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11); @@ -570,7 +580,6 @@ static const char edit_description[] = "BRANCH_DESCRIPTION"; static int edit_branch_description(const char *branch_name) { - int status; struct strbuf buf = STRBUF_INIT; struct strbuf name = STRBUF_INIT; @@ -595,11 +604,11 @@ static int edit_branch_description(const char *branch_name) strbuf_stripspace(&buf, 1); strbuf_addf(&name, "branch.%s.description", branch_name); - status = git_config_set(name.buf, buf.len ? buf.buf : NULL); + git_config_set(name.buf, buf.len ? buf.buf : NULL); strbuf_release(&name); strbuf_release(&buf); - return status; + return 0; } int cmd_branch(int argc, const char **argv, const char *prefix) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index c0fd8dbb1c..54db1184a0 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -401,7 +401,7 @@ static int batch_objects(struct batch_options *opt) save_warning = warn_on_object_refname_ambiguity; warn_on_object_refname_ambiguity = 0; - while (strbuf_getline(&buf, stdin, '\n') != EOF) { + while (strbuf_getline(&buf, stdin) != EOF) { if (data.split_on_whitespace) { /* * Split at first whitespace, tying off the beginning diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 265c9ba022..53a5a18c16 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -72,23 +72,23 @@ static void check_attr(const char *prefix, int cnt, static void check_attr_stdin_paths(const char *prefix, int cnt, struct git_attr_check *check) { - struct strbuf buf, nbuf; - int line_termination = nul_term_line ? 0 : '\n'; - - strbuf_init(&buf, 0); - strbuf_init(&nbuf, 0); - while (strbuf_getline(&buf, stdin, line_termination) != EOF) { - if (line_termination && buf.buf[0] == '"') { - strbuf_reset(&nbuf); - if (unquote_c_style(&nbuf, buf.buf, NULL)) + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + strbuf_getline_fn getline_fn; + + getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; + while (getline_fn(&buf, stdin) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) die("line is badly quoted"); - strbuf_swap(&buf, &nbuf); + strbuf_swap(&buf, &unquoted); } check_attr(prefix, cnt, check, buf.buf); maybe_flush_or_die(stdout, "attribute to stdout"); } strbuf_release(&buf); - strbuf_release(&nbuf); + strbuf_release(&unquoted); } static NORETURN void error_with_usage(const char *msg) diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 43f361797a..1d73d3ca3d 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -115,19 +115,19 @@ static int check_ignore(struct dir_struct *dir, static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix) { - struct strbuf buf, nbuf; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; char *pathspec[2] = { NULL, NULL }; - int line_termination = nul_term_line ? 0 : '\n'; + strbuf_getline_fn getline_fn; int num_ignored = 0; - strbuf_init(&buf, 0); - strbuf_init(&nbuf, 0); - while (strbuf_getline(&buf, stdin, line_termination) != EOF) { - if (line_termination && buf.buf[0] == '"') { - strbuf_reset(&nbuf); - if (unquote_c_style(&nbuf, buf.buf, NULL)) + getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; + while (getline_fn(&buf, stdin) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) die("line is badly quoted"); - strbuf_swap(&buf, &nbuf); + strbuf_swap(&buf, &unquoted); } pathspec[0] = buf.buf; num_ignored += check_ignore(dir, prefix, @@ -135,7 +135,7 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix) maybe_flush_or_die(stdout, "check-ignore to stdout"); } strbuf_release(&buf); - strbuf_release(&nbuf); + strbuf_release(&unquoted); return num_ignored; } diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index eaaea546d3..cf0f54f6b9 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -54,7 +54,7 @@ int cmd_check_mailmap(int argc, const char **argv, const char *prefix) if (use_stdin) { struct strbuf buf = STRBUF_INIT; - while (strbuf_getline(&buf, stdin, '\n') != EOF) { + while (strbuf_getline_lf(&buf, stdin) != EOF) { check_mailmap(&mailmap, buf.buf); maybe_flush_or_die(stdout, "stdout"); } diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c index fd915d5984..eac499450f 100644 --- a/builtin/check-ref-format.c +++ b/builtin/check-ref-format.c @@ -20,7 +20,7 @@ static const char builtin_check_ref_format_usage[] = */ static char *collapse_slashes(const char *refname) { - char *ret = xmalloc(strlen(refname) + 1); + char *ret = xmallocz(strlen(refname)); char ch; char prev = '/'; char *cp = ret; diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 8028c3768f..92c69672e9 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -11,7 +11,7 @@ #include "parse-options.h" #define CHECKOUT_ALL 4 -static int line_termination = '\n'; +static int nul_term_line; static int checkout_stage; /* default to checkout stage0 */ static int to_tempfile; static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; @@ -35,7 +35,8 @@ static void write_tempfile_record(const char *name, const char *prefix) fputs(topath[checkout_stage], stdout); putchar('\t'); - write_name_quoted_relative(name, prefix, stdout, line_termination); + write_name_quoted_relative(name, prefix, stdout, + nul_term_line ? '\0' : '\n'); for (i = 0; i < 4; i++) { topath[i][0] = 0; @@ -129,36 +130,6 @@ static const char * const builtin_checkout_index_usage[] = { static struct lock_file lock_file; -static int option_parse_u(const struct option *opt, - const char *arg, int unset) -{ - int *newfd = opt->value; - - state.refresh_cache = 1; - state.istate = &the_index; - if (*newfd < 0) - *newfd = hold_locked_index(&lock_file, 1); - 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_prefix(const struct option *opt, - const char *arg, int unset) -{ - state.base_dir = arg; - state.base_dir_len = strlen(arg); - return 0; -} - static int option_parse_stage(const struct option *opt, const char *arg, int unset) { @@ -170,7 +141,7 @@ static int option_parse_stage(const struct option *opt, if ('1' <= ch && ch <= '3') checkout_stage = arg[0] - '0'; else - die("stage should be between 1 and 3 or all"); + die(_("stage should be between 1 and 3 or all")); } return 0; } @@ -183,6 +154,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) int read_from_stdin = 0; int prefix_length; int force = 0, quiet = 0, not_new = 0; + int index_opt = 0; struct option builtin_checkout_index_options[] = { OPT_BOOL('a', "all", &all, N_("check out all files in the index")), @@ -191,22 +163,19 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) N_("no warning for existing files and files not in index")), OPT_BOOL('n', "no-create", ¬_new, N_("don't checkout new files")), - { OPTION_CALLBACK, 'u', "index", &newfd, NULL, - N_("update stat information in the index file"), - PARSE_OPT_NOARG, option_parse_u }, - { OPTION_CALLBACK, 'z', NULL, NULL, NULL, - N_("paths are separated with NUL character"), - PARSE_OPT_NOARG, option_parse_z }, + OPT_BOOL('u', "index", &index_opt, + N_("update stat information in the index file")), + OPT_BOOL('z', NULL, &nul_term_line, + N_("paths are separated with NUL character")), OPT_BOOL(0, "stdin", &read_from_stdin, N_("read list of paths from the standard input")), OPT_BOOL(0, "temp", &to_tempfile, N_("write the content to temporary files")), - OPT_CALLBACK(0, "prefix", NULL, N_("string"), - N_("when creating files, prepend <string>"), - option_parse_prefix), - OPT_CALLBACK(0, "stage", NULL, NULL, + OPT_STRING(0, "prefix", &state.base_dir, N_("string"), + N_("when creating files, prepend <string>")), + { OPTION_CALLBACK, 0, "stage", NULL, "1-3|all", N_("copy out the files from named stage"), - option_parse_stage), + PARSE_OPT_NONEG, option_parse_stage }, OPT_END() }; @@ -214,7 +183,6 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) usage_with_options(builtin_checkout_index_usage, builtin_checkout_index_options); git_config(git_default_config, NULL); - state.base_dir = ""; prefix_length = prefix ? strlen(prefix) : 0; if (read_cache() < 0) { @@ -227,15 +195,17 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) state.quiet = quiet; state.not_new = not_new; - if (state.base_dir_len || to_tempfile) { - /* when --prefix is specified we do not - * want to update cache. - */ - if (state.refresh_cache) { - rollback_lock_file(&lock_file); - newfd = -1; - } - state.refresh_cache = 0; + if (!state.base_dir) + state.base_dir = ""; + state.base_dir_len = strlen(state.base_dir); + + /* + * when --prefix is specified we do not want to update cache. + */ + if (index_opt && !state.base_dir_len && !to_tempfile) { + state.refresh_cache = 1; + state.istate = &the_index; + newfd = hold_locked_index(&lock_file, 1); } /* Check out named files first */ @@ -253,24 +223,27 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) } if (read_from_stdin) { - struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + strbuf_getline_fn getline_fn; if (all) die("git checkout-index: don't mix '--all' and '--stdin'"); - while (strbuf_getline(&buf, stdin, line_termination) != EOF) { + getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; + while (getline_fn(&buf, stdin) != EOF) { char *p; - if (line_termination && buf.buf[0] == '"') { - strbuf_reset(&nbuf); - if (unquote_c_style(&nbuf, buf.buf, NULL)) + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) die("line is badly quoted"); - strbuf_swap(&buf, &nbuf); + strbuf_swap(&buf, &unquoted); } p = prefix_path(prefix, prefix_length, buf.buf); checkout_file(p, prefix); free(p); } - strbuf_release(&nbuf); + strbuf_release(&unquoted); strbuf_release(&buf); } diff --git a/builtin/checkout.c b/builtin/checkout.c index e8110a9243..ea2fe1cf3f 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -242,7 +242,6 @@ static int checkout_paths(const struct checkout_opts *opts, struct checkout state; static char *ps_matched; unsigned char rev[20]; - int flag; struct commit *head; int errs = 0; struct lock_file *lock_file; @@ -375,7 +374,7 @@ static int checkout_paths(const struct checkout_opts *opts, if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); - read_ref_full("HEAD", 0, rev, &flag); + read_ref_full("HEAD", 0, rev, NULL); head = lookup_commit_reference_gently(rev, 1); errs |= post_checkout_hook(head, head, 0); @@ -661,7 +660,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts, describe_detached_head(_("HEAD is now at"), new->commit); } } else if (new->path) { /* Switch branches. */ - create_symref("HEAD", new->path, msg.buf); + if (create_symref("HEAD", new->path, msg.buf) < 0) + die(_("unable to update HEAD")); if (!opts->quiet) { if (old->path && !strcmp(new->path, old->path)) { if (opts->new_branch_force) @@ -981,7 +981,8 @@ static int parse_branchname_arg(int argc, const char **argv, */ int recover_with_dwim = dwim_new_local_branch_ok; - if (check_filename(NULL, arg) && !has_dash_dash) + if (!has_dash_dash && + (check_filename(NULL, arg) || !no_wildcard(arg))) recover_with_dwim = 0; /* * Accept "git checkout foo" and "git checkout foo --" diff --git a/builtin/clean.c b/builtin/clean.c index d7acb94a95..0371010afb 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -147,30 +147,6 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset) return 0; } -/* - * Return 1 if the given path is the root of a git repository or - * submodule else 0. Will not return 1 for bare repositories with the - * exception of creating a bare repository in "foo/.git" and calling - * is_git_repository("foo"). - */ -static int is_git_repository(struct strbuf *path) -{ - int ret = 0; - int gitfile_error; - size_t orig_path_len = path->len; - assert(orig_path_len != 0); - strbuf_complete(path, '/'); - strbuf_addstr(path, ".git"); - if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf)) - ret = 1; - if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED || - gitfile_error == READ_GITFILE_ERR_READ_FAILED) - ret = 1; /* This could be a real .git file, take the - * safe option and avoid cleaning */ - strbuf_setlen(path, orig_path_len); - return ret; -} - static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, int dry_run, int quiet, int *dir_gone) { @@ -182,7 +158,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, *dir_gone = 1; - if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_git_repository(path)) { + if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) { if (!quiet) { quote_path_relative(path->buf, prefix, "ed); printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir), @@ -567,7 +543,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) 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; @@ -594,7 +570,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) 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; @@ -639,7 +615,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) 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; @@ -676,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'); @@ -774,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 --git a/builtin/clone.c b/builtin/clone.c index a0b3cd9e56..5f867e67d8 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -40,6 +40,7 @@ static const char * const builtin_clone_usage[] = { static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; static int option_local = -1, option_no_hardlinks, option_shared, option_recursive; +static int option_shallow_submodules = -1; static char *option_template, *option_depth; static char *option_origin = NULL; static char *option_branch = NULL; @@ -47,9 +48,11 @@ static const char *real_git_dir; static char *option_upload_pack = "git-upload-pack"; static int option_verbosity; static int option_progress = -1; +static enum transport_family family; static struct string_list option_config; static struct string_list option_reference; static int option_dissociate; +static int max_jobs = -1; static struct option builtin_clone_options[] = { OPT__VERBOSITY(&option_verbosity), @@ -72,6 +75,8 @@ static struct option builtin_clone_options[] = { N_("initialize submodules in the clone")), OPT_BOOL(0, "recurse-submodules", &option_recursive, N_("initialize submodules in the clone")), + OPT_INTEGER('j', "jobs", &max_jobs, + N_("number of submodules cloned in parallel")), OPT_STRING(0, "template", &option_template, N_("template-directory"), N_("directory from which templates will be used")), OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"), @@ -88,17 +93,19 @@ static struct option builtin_clone_options[] = { N_("create a shallow clone of that depth")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), + OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules, + N_("any cloned submodules will be shallow")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), N_("separate git dir from working tree")), OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), N_("set config inside the new repository")), + 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() }; -static const char *argv_submodule[] = { - "submodule", "update", "--init", "--recursive", NULL -}; - static const char *get_repo_path_1(struct strbuf *path, int *is_bundle) { static char *suffix[] = { "/.git", "", ".git/.git", ".git" }; @@ -231,8 +238,8 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git"); if (!len || (len == 1 && *start == '/')) - die("No directory name could be guessed.\n" - "Please specify a directory on the command line"); + die(_("No directory name could be guessed.\n" + "Please specify a directory on the command line")); if (is_bare) dir = xstrfmt("%.*s.git", (int)len, start); @@ -339,7 +346,7 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst, FILE *in = fopen(src->buf, "r"); struct strbuf line = STRBUF_INIT; - while (strbuf_getline(&line, in, '\n') != EOF) { + while (strbuf_getline(&line, in) != EOF) { char *abs_path; if (!line.len || line.buf[0] == '#') continue; @@ -636,9 +643,11 @@ static void update_remote_refs(const struct ref *refs, struct strbuf head_ref = STRBUF_INIT; strbuf_addstr(&head_ref, branch_top); strbuf_addstr(&head_ref, "HEAD"); - create_symref(head_ref.buf, - remote_head_points_at->peer_ref->name, - msg); + if (create_symref(head_ref.buf, + remote_head_points_at->peer_ref->name, + msg) < 0) + die(_("unable to update %s"), head_ref.buf); + strbuf_release(&head_ref); } } @@ -648,7 +657,8 @@ static void update_head(const struct ref *our, const struct ref *remote, const char *head; if (our && skip_prefix(our->name, "refs/heads/", &head)) { /* Local default branch link */ - create_symref("HEAD", our->name, NULL); + if (create_symref("HEAD", our->name, NULL) < 0) + die(_("unable to update HEAD")); if (!option_bare) { update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); @@ -724,15 +734,27 @@ static int checkout(void) err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1), sha1_to_hex(sha1), "1", NULL); - if (!err && option_recursive) - err = run_command_v_opt(argv_submodule, RUN_GIT_CMD); + if (!err && option_recursive) { + struct argv_array args = ARGV_ARRAY_INIT; + argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL); + + if (option_shallow_submodules == 1 + || (option_shallow_submodules == -1 && option_depth)) + argv_array_push(&args, "--depth=1"); + + if (max_jobs != -1) + argv_array_pushf(&args, "--jobs=%d", max_jobs); + + err = run_command_v_opt(args.argv, RUN_GIT_CMD); + argv_array_clear(&args); + } return err; } static int write_one_config(const char *key, const char *value, void *data) { - return git_config_set_multivar(key, value ? value : "true", "^$", 0); + return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0); } static void write_config(struct string_list *config) @@ -742,7 +764,7 @@ static void write_config(struct string_list *config) for (i = 0; i < config->nr; i++) { if (git_config_parse_parameter(config->items[i].string, write_one_config, NULL) < 0) - die("unable to write parameters to config file"); + die(_("unable to write parameters to config file")); } } @@ -967,6 +989,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) remote = remote_get(option_origin); transport = transport_get(remote, remote->url[0]); transport_set_verbosity(transport, option_verbosity, option_progress); + transport->family = family; path = get_repo_path(remote->url[0], &is_bundle); is_local = option_local != 0 && path && !is_bundle; diff --git a/builtin/column.c b/builtin/column.c index 449413c8a8..33314b4d71 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -51,7 +51,7 @@ int cmd_column(int argc, const char **argv, const char *prefix) die(_("--command must be the first argument")); } finalize_colopts(&colopts, -1); - while (!strbuf_getline(&sb, stdin, '\n')) + while (!strbuf_getline(&sb, stdin)) string_list_append(&list, sb.buf); print_columns(&list, colopts, &copts); diff --git a/builtin/commit.c b/builtin/commit.c index d054f84960..391126e58d 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -186,6 +186,7 @@ static void status_init_config(struct wt_status *s, config_fn_t fn) gitmodules_config(); git_config(fn, s); determine_whence(s); + init_diff_ui_defaults(); s->hints = advice_status_hints; /* must come after git_config() */ } @@ -694,7 +695,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } } - if (message.len) { + if (have_option_m) { strbuf_addbuf(&sb, &message); hook_arg1 = "message"; } else if (logfile && !strcmp(logfile, "-")) { @@ -726,9 +727,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix, &sb, &ctx); hook_arg1 = "message"; } else if (!stat(git_path_merge_msg(), &statbuf)) { + /* + * prepend SQUASH_MSG here if it exists and a + * "merge --squash" was originally performed + */ + if (!stat(git_path_squash_msg(), &statbuf)) { + if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0) + die_errno(_("could not read SQUASH_MSG")); + hook_arg1 = "squash"; + } else + hook_arg1 = "merge"; if (strbuf_read_file(&sb, git_path_merge_msg(), 0) < 0) die_errno(_("could not read MERGE_MSG")); - hook_arg1 = "merge"; } else if (!stat(git_path_squash_msg(), &statbuf)) { if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0) die_errno(_("could not read SQUASH_MSG")); @@ -761,7 +771,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, hook_arg2 = ""; } - s->fp = fopen(git_path(commit_editmsg), "w"); + s->fp = fopen_for_writing(git_path(commit_editmsg)); if (s->fp == NULL) die_errno(_("could not open '%s'"), git_path(commit_editmsg)); @@ -1162,9 +1172,9 @@ static int parse_and_validate_options(int argc, const char *argv[], f++; if (f > 1) die(_("Only one of -c/-C/-F/--fixup can be used.")); - if (message.len && f > 0) + if (have_option_m && f > 0) die((_("Option -m cannot be combined with -c/-C/-F/--fixup."))); - if (f || message.len) + if (f || have_option_m) template_file = NULL; if (edit_message) use_message = edit_message; @@ -1690,7 +1700,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (fp == NULL) die_errno(_("could not open '%s' for reading"), git_path_merge_head()); - while (strbuf_getline(&m, fp, '\n') != EOF) { + while (strbuf_getline_lf(&m, fp) != EOF) { struct commit *parent; parent = get_merge_parent(m.buf); diff --git a/builtin/config.c b/builtin/config.c index adc772786a..1d7c6ef558 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -3,6 +3,7 @@ #include "color.h" #include "parse-options.h" #include "urlmatch.h" +#include "quote.h" static const char *const builtin_config_usage[] = { N_("git config [<options>]"), @@ -27,6 +28,7 @@ static int actions, types; static const char *get_color_slot, *get_colorbool_slot; static int end_null; static int respect_includes = -1; +static int show_origin; #define ACTION_GET (1<<0) #define ACTION_GET_ALL (1<<1) @@ -81,6 +83,7 @@ static struct option builtin_config_options[] = { OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")), OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")), + OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), OPT_END(), }; @@ -91,8 +94,28 @@ static void check_argc(int argc, int min, int max) { usage_with_options(builtin_config_usage, builtin_config_options); } +static void show_config_origin(struct strbuf *buf) +{ + const char term = end_null ? '\0' : '\t'; + + strbuf_addstr(buf, current_config_origin_type()); + strbuf_addch(buf, ':'); + if (end_null) + strbuf_addstr(buf, current_config_name()); + else + quote_c_style(current_config_name(), buf, NULL, 0); + strbuf_addch(buf, term); +} + static int show_all_config(const char *key_, const char *value_, void *cb) { + if (show_origin) { + struct strbuf buf = STRBUF_INIT; + show_config_origin(&buf); + /* Use fwrite as "buf" can contain \0's if "end_null" is set. */ + fwrite(buf.buf, 1, buf.len, stdout); + strbuf_release(&buf); + } if (!omit_values && value_) printf("%s%c%s%c", key_, delim, value_, term); else @@ -108,6 +131,8 @@ struct strbuf_list { static int format_config(struct strbuf *buf, const char *key_, const char *value_) { + if (show_origin) + show_config_origin(buf); if (show_keys) strbuf_addstr(buf, key_); if (!omit_values) { @@ -352,6 +377,9 @@ static int get_colorbool(const char *var, int print) static void check_write(void) { + if (!given_config_source.file && !startup_info->have_repository) + die("not in a git directory"); + if (given_config_source.use_stdin) die("writing to stdin is not supported"); @@ -389,6 +417,7 @@ static int urlmatch_collect_fn(const char *var, const char *value, void *cb) static int get_urlmatch(const char *var, const char *url) { + int ret; char *section_tail; struct string_list_item *item; struct urlmatch_config config = { STRING_LIST_INIT_DUP }; @@ -415,6 +444,8 @@ static int get_urlmatch(const char *var, const char *url) git_config_with_options(urlmatch_config_entry, &config, &given_config_source, respect_includes); + ret = !values.nr; + for_each_string_list_item(item, &values) { struct urlmatch_current_candidate_value *matched = item->util; struct strbuf buf = STRBUF_INIT; @@ -431,7 +462,7 @@ static int get_urlmatch(const char *var, const char *url) free(config.url.url); free((void *)config.section); - return 0; + return ret; } static char *default_user_config(void) @@ -538,6 +569,14 @@ int cmd_config(int argc, const char **argv, const char *prefix) error("--name-only is only applicable to --list or --get-regexp"); usage_with_options(builtin_config_usage, builtin_config_options); } + + if (show_origin && !(actions & + (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) { + error("--show-origin is only applicable to --get, --get-all, " + "--get-regexp, and --list."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (git_config_with_options(show_all_config, NULL, @@ -582,7 +621,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - ret = git_config_set_in_file(given_config_source.file, argv[0], value); + ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value); if (ret == CONFIG_NOTHING_SET) error("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s.", argv[0]); @@ -592,23 +631,23 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_source.file, - argv[0], value, argv[2], 0); + return git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], value, argv[2], 0); } else if (actions == ACTION_ADD) { check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_source.file, - argv[0], value, - CONFIG_REGEX_NONE, 0); + return git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], value, + CONFIG_REGEX_NONE, 0); } else if (actions == ACTION_REPLACE_ALL) { check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_source.file, - argv[0], value, argv[2], 1); + return git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], value, argv[2], 1); } else if (actions == ACTION_GET) { check_argc(argc, 1, 2); @@ -634,17 +673,17 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_write(); check_argc(argc, 1, 2); if (argc == 2) - return git_config_set_multivar_in_file(given_config_source.file, - argv[0], NULL, argv[1], 0); + return git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], NULL, argv[1], 0); else - return git_config_set_in_file(given_config_source.file, - argv[0], NULL); + return git_config_set_in_file_gently(given_config_source.file, + argv[0], NULL); } else if (actions == ACTION_UNSET_ALL) { check_write(); check_argc(argc, 1, 2); - return git_config_set_multivar_in_file(given_config_source.file, - argv[0], NULL, argv[1], 1); + return git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], NULL, argv[1], 1); } else if (actions == ACTION_RENAME_SECTION) { int ret; diff --git a/builtin/diff.c b/builtin/diff.c index ed0acca91f..343c6b8f25 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -318,6 +318,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!no_index) gitmodules_config(); + init_diff_ui_defaults(); git_config(git_diff_ui_config, NULL); init_revisions(&rev, prefix); @@ -341,7 +342,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } if (no_index) /* If this is a no-index diff, just run it and exit there. */ - diff_no_index(&rev, argc, argv, prefix); + diff_no_index(&rev, argc, argv); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index d9ac5d8410..8164b581a6 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -880,7 +880,7 @@ static void export_marks(char *file) FILE *f; int e = 0; - f = fopen(file, "w"); + f = fopen_for_writing(file); if (!f) die_errno("Unable to open marks file %s for writing.", file); @@ -1021,7 +1021,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) const char **refspecs_str; int i; - refspecs_str = xmalloc(sizeof(*refspecs_str) * refspecs_list.nr); + ALLOC_ARRAY(refspecs_str, refspecs_list.nr); for (i = 0; i < refspecs_list.nr; i++) refspecs_str[i] = refspecs_list.items[i].string; diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index cf3019e05b..bfd0be44a9 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -10,33 +10,34 @@ static const char fetch_pack_usage[] = "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] " "[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]"; -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)) { + if (name[GIT_SHA1_HEXSZ] == ' ') { + /* <sha1> <ref>, find refname */ + name += GIT_SHA1_HEXSZ + 1; + } else if (name[GIT_SHA1_HEXSZ] == '\0') { + ; /* <sha1>, leave sha1 as name */ + } else { + /* <ref>, clear cruft from oid */ + oidclr(&oid); + } + } else { + /* <ref>, clear cruft from get_oid_hex */ + 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 +159,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) 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 --git a/builtin/fetch.c b/builtin/fetch.c index 5b58001a98..f8455bde7a 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -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; @@ -99,6 +101,8 @@ static struct option builtin_fetch_options[] = { 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"), @@ -124,6 +128,10 @@ static struct option builtin_fetch_options[] = { 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() }; @@ -837,7 +845,7 @@ static void check_not_current_branch(struct ref *ref_map) static int truncate_fetch_head(void) { const char *filename = git_path_fetch_head(); - FILE *fp = fopen(filename, "w"); + FILE *fp = fopen_for_writing(filename); if (!fp) return error(_("cannot open %s: %s\n"), filename, strerror(errno)); @@ -861,6 +869,7 @@ static struct transport *prepare_transport(struct remote *remote) 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) @@ -1013,10 +1022,9 @@ static int add_remote_or_group(const char *name, struct string_list *list) 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; @@ -1107,7 +1115,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) 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++; @@ -1213,7 +1221,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) result = fetch_populated_submodules(&options, submodule_prefix, recurse_submodules, - verbosity < 0); + verbosity < 0, + max_children); argv_array_clear(&options); } diff --git a/builtin/fsck.c b/builtin/fsck.c index 55eac756f7..3f27456883 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -493,13 +493,12 @@ static void fsck_object_dir(const char *path) static int fsck_head_link(void) { - int flag; int null_is_error = 0; if (verbose) fprintf(stderr, "Checking HEAD link\n"); - head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, &flag); + head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL); if (!head_points_at) { errors_found |= ERROR_REFS; return error("Invalid HEAD"); diff --git a/builtin/grep.c b/builtin/grep.c index 4229cae390..462e607901 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -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 @@ 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,7 +206,8 @@ static void start_threads(struct grep_opt *opt) 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; @@ -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); @@ -267,6 +270,14 @@ static int grep_cmd_config(const char *var, const char *value, void *cb) 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; } @@ -294,7 +305,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, } #ifndef NO_PTHREADS - if (use_threads) { + if (num_threads) { add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); strbuf_release(&pathbuf); return 0; @@ -323,7 +334,7 @@ static int grep_file(struct grep_opt *opt, const char *filename) 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; @@ -354,17 +365,17 @@ static void append_path(struct grep_opt *opt, const void *data, size_t len) 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) @@ -375,7 +386,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int 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; @@ -427,7 +438,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_add(base, entry.path, te_len); if (S_ISREG(entry.mode)) { - hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len, + hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len, check_attr ? base->buf + tn_len : NULL); } else if (S_ISDIR(entry.mode)) { @@ -436,10 +447,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, void *data; unsigned long size; - data = lock_and_read_sha1_file(entry.sha1, &type, &size); + data = lock_and_read_sha1_file(entry.oid->hash, &type, &size); if (!data) die(_("unable to read tree (%s)"), - sha1_to_hex(entry.sha1)); + oid_to_hex(entry.oid)); strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); @@ -511,12 +522,14 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, } static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, - int exc_std) + int exc_std, int use_index) { struct dir_struct dir; int i, hit = 0; memset(&dir, 0, sizeof(dir)); + if (!use_index) + dir.flags |= DIR_NO_GITLINKS; if (exc_std) setup_standard_excludes(&dir); @@ -562,7 +575,7 @@ static int file_callback(const struct option *opt, const char *arg, int unset) 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; @@ -697,6 +710,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("show <n> context lines before matches")), OPT_INTEGER('A', "after-context", &opt.post_context, N_("show <n> context lines after matches")), + OPT_INTEGER(0, "threads", &num_threads, + N_("use <n> worker threads")), OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), context_callback), OPT_BOOL('p', "show-function", &opt.funcname, @@ -755,9 +770,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix) 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 @@ -786,7 +807,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.output_priv = &path_list; opt.output = append_path; string_list_append(&path_list, show_in_pager); - use_threads = 0; } if (!opt.pattern_list) @@ -817,14 +837,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } #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)) @@ -880,7 +904,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude; if (list.nr) die(_("--no-index or --untracked cannot be used with revs.")); - hit = grep_directory(&opt, &pathspec, use_exclude); + hit = grep_directory(&opt, &pathspec, use_exclude, use_index); } else if (0 <= opt_exclude) { die(_("--[no-]exclude-standard cannot be used for tracked contents.")); } else if (!list.nr) { @@ -894,7 +918,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) 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 --git a/builtin/hash-object.c b/builtin/hash-object.c index 43b098b76c..f7d3567dd0 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -58,20 +58,21 @@ static void hash_object(const char *path, const char *type, const char *vpath, static void hash_stdin_paths(const char *type, int no_filters, unsigned flags, int literally) { - struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; - while (strbuf_getline(&buf, stdin, '\n') != EOF) { + while (strbuf_getline(&buf, stdin) != EOF) { if (buf.buf[0] == '"') { - strbuf_reset(&nbuf); - if (unquote_c_style(&nbuf, buf.buf, NULL)) + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) die("line is badly quoted"); - strbuf_swap(&buf, &nbuf); + strbuf_swap(&buf, &unquoted); } hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags, literally); } strbuf_release(&buf); - strbuf_release(&nbuf); + strbuf_release(&unquoted); } int cmd_hash_object(int argc, const char **argv, const char *prefix) diff --git a/builtin/help.c b/builtin/help.c index 1cd0c1ee44..3c55ce4563 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -171,12 +171,10 @@ static void exec_man_cmd(const char *cmd, const char *page) static void add_man_viewer(const char *name) { struct man_viewer_list **p = &man_viewer_list; - size_t len = strlen(name); while (*p) p = &((*p)->next); - *p = xcalloc(1, (sizeof(**p) + len + 1)); - memcpy((*p)->name, name, len); /* NUL-terminated by xcalloc */ + FLEX_ALLOC_STR(*p, name, name); } static int supported_man_viewer(const char *name, size_t len) @@ -190,9 +188,8 @@ static void do_add_man_viewer_info(const char *name, size_t len, const char *value) { - struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1); - - memcpy(new->name, name, len); /* NUL-terminated by xcalloc */ + struct man_viewer_info_list *new; + FLEX_ALLOC_MEM(new, name, name, len); new->info = xstrdup(value); new->next = man_viewer_info_list; man_viewer_info_list = new; diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 6a01509587..2d1eb8bb8a 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1346,7 +1346,7 @@ static void fix_unresolved_deltas(struct sha1file *f) * before deltas depending on them, a good heuristic is to start * resolving deltas in the same order as their position in the pack. */ - sorted_by_pos = xmalloc(nr_ref_deltas * sizeof(*sorted_by_pos)); + ALLOC_ARRAY(sorted_by_pos, nr_ref_deltas); for (i = 0; i < nr_ref_deltas; i++) sorted_by_pos[i] = &ref_deltas[i]; qsort(sorted_by_pos, nr_ref_deltas, sizeof(*sorted_by_pos), delta_pos_compare); @@ -1514,6 +1514,7 @@ static void read_v2_anomalous_offsets(struct packed_git *p, if (!(off & 0x80000000)) continue; off = off & 0x7fffffff; + check_pack_index_ptr(p, &idx2[off * 2]); if (idx2[off * 2]) continue; /* @@ -1598,6 +1599,18 @@ static void show_pack_info(int stat_only) } } +static const char *derive_filename(const char *pack_name, const char *suffix, + struct strbuf *buf) +{ + size_t len; + if (!strip_suffix(pack_name, ".pack", &len)) + die(_("packfile name '%s' does not end with '.pack'"), + pack_name); + strbuf_add(buf, pack_name, len); + strbuf_addstr(buf, suffix); + return buf->buf; +} + int cmd_index_pack(int argc, const char **argv, const char *prefix) { int i, fix_thin_pack = 0, verify = 0, stat_only = 0; @@ -1706,24 +1719,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) usage(index_pack_usage); if (fix_thin_pack && !from_stdin) die(_("--fix-thin cannot be used without --stdin")); - if (!index_name && pack_name) { - size_t len; - if (!strip_suffix(pack_name, ".pack", &len)) - die(_("packfile name '%s' does not end with '.pack'"), - pack_name); - strbuf_add(&index_name_buf, pack_name, len); - strbuf_addstr(&index_name_buf, ".idx"); - index_name = index_name_buf.buf; - } - if (keep_msg && !keep_name && pack_name) { - size_t len; - if (!strip_suffix(pack_name, ".pack", &len)) - die(_("packfile name '%s' does not end with '.pack'"), - pack_name); - strbuf_add(&keep_name_buf, pack_name, len); - strbuf_addstr(&keep_name_buf, ".idx"); - keep_name = keep_name_buf.buf; - } + if (!index_name && pack_name) + index_name = derive_filename(pack_name, ".idx", &index_name_buf); + if (keep_msg && !keep_name && pack_name) + keep_name = derive_filename(pack_name, ".keep", &keep_name_buf); + if (verify) { if (!index_name) die(_("--verify with no packfile name given")); @@ -1744,9 +1744,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) curr_pack = open_pack_file(pack_name); parse_pack_header(); - objects = xcalloc(nr_objects + 1, sizeof(struct object_entry)); + objects = xcalloc(st_add(nr_objects, 1), sizeof(struct object_entry)); if (show_stat) - obj_stat = xcalloc(nr_objects + 1, sizeof(struct object_stat)); + obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat)); ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry)); parse_pack_objects(pack_sha1); resolve_deltas(); @@ -1759,7 +1759,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (show_stat) show_pack_info(stat_only); - idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *)); + ALLOC_ARRAY(idx_objects, nr_objects); for (i = 0; i < nr_objects; i++) idx_objects[i] = &objects[i].idx; curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_sha1); diff --git a/builtin/init-db.c b/builtin/init-db.c index 07229d60f1..b2d8d40a67 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -95,6 +95,8 @@ static void copy_templates(const char *template_dir) struct strbuf path = STRBUF_INIT; struct strbuf template_path = STRBUF_INIT; size_t template_len; + struct repository_format template_format; + struct strbuf err = STRBUF_INIT; DIR *dir; char *to_free = NULL; @@ -121,17 +123,18 @@ static void copy_templates(const char *template_dir) /* Make sure that template is from the correct vintage */ strbuf_addstr(&template_path, "config"); - repository_format_version = 0; - git_config_from_file(check_repository_format_version, - template_path.buf, NULL); + read_repository_format(&template_format, template_path.buf); strbuf_setlen(&template_path, template_len); - if (repository_format_version && - repository_format_version != GIT_REPO_VERSION) { - warning(_("not copying templates of " - "a wrong format version %d from '%s'"), - repository_format_version, - template_dir); + /* + * No mention of version at all is OK, but anything else should be + * verified. + */ + if (template_format.version >= 0 && + verify_repository_format(&template_format, &err) < 0) { + warning(_("not copying templates from '%s': %s"), + template_dir, err.buf); + strbuf_release(&err); goto close_free_return; } @@ -199,13 +202,13 @@ static int create_default_files(const char *template_path) /* reading existing config may have overwrote it */ if (init_shared_repository != -1) - shared_repository = init_shared_repository; + set_shared_repository(init_shared_repository); /* * We would have created the above under user's umask -- under * shared-repository settings, we would need to fix them up. */ - if (shared_repository) { + if (get_shared_repository()) { adjust_shared_perm(get_git_dir()); adjust_shared_perm(git_path_buf(&buf, "refs")); adjust_shared_perm(git_path_buf(&buf, "refs/heads")); @@ -250,7 +253,7 @@ static int create_default_files(const char *template_path) git_config_set("core.bare", "false"); /* allow template config file to override the default */ if (log_all_ref_updates == -1) - git_config_set("core.logallrefupdates", "true"); + git_config_set("core.logallrefupdates", "true"); if (needs_work_tree_config(get_git_dir(), work_tree)) git_config_set("core.worktree", work_tree); } @@ -322,6 +325,7 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir, set_git_dir(real_path(git_dir)); git_link = NULL; } + startup_info->have_repository = 1; return 0; } @@ -369,7 +373,7 @@ int init_db(const char *template_dir, unsigned int flags) create_object_directory(); - if (shared_repository) { + if (get_shared_repository()) { char buf[10]; /* We do not spell "group" and such, so that * the configuration can be read by older version @@ -377,12 +381,12 @@ int init_db(const char *template_dir, unsigned int flags) * and compatibility values for PERM_GROUP and * PERM_EVERYBODY. */ - if (shared_repository < 0) + if (get_shared_repository() < 0) /* force to the mode value */ - xsnprintf(buf, sizeof(buf), "0%o", -shared_repository); - else if (shared_repository == PERM_GROUP) + xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository()); + else if (get_shared_repository() == PERM_GROUP) xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP); - else if (shared_repository == PERM_EVERYBODY) + else if (get_shared_repository() == PERM_EVERYBODY) xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY); else die("BUG: invalid value for shared_repository"); @@ -398,7 +402,7 @@ int init_db(const char *template_dir, unsigned int flags) "", and the last '%s%s' is the verbatim directory name. */ printf(_("%s%s Git repository in %s%s\n"), reinit ? _("Reinitialized existing") : _("Initialized empty"), - shared_repository ? _(" shared") : "", + get_shared_repository() ? _(" shared") : "", git_dir, len && git_dir[len-1] != '/' ? "/" : ""); } @@ -493,8 +497,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) * and we know shared_repository should always be 0; * but just in case we play safe. */ - saved = shared_repository; - shared_repository = 0; + saved = get_shared_repository(); + set_shared_repository(0); switch (safe_create_leading_directories_const(argv[0])) { case SCLD_OK: case SCLD_PERMS: @@ -506,7 +510,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) die_errno(_("cannot mkdir %s"), argv[0]); break; } - shared_repository = saved; + set_shared_repository(saved); if (mkdir(argv[0], 0777) < 0) die_errno(_("cannot mkdir %s"), argv[0]); mkdir_tried = 1; @@ -524,7 +528,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) } if (init_shared_repository != -1) - shared_repository = init_shared_repository; + set_shared_repository(init_shared_repository); /* * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index 46838d24a9..b99ae4be88 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -12,16 +12,18 @@ #include "trailer.h" static const char * const git_interpret_trailers_usage[] = { - N_("git interpret-trailers [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"), + N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"), NULL }; int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) { + int in_place = 0; int trim_empty = 0; struct string_list trailers = STRING_LIST_INIT_DUP; struct option options[] = { + OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")), OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")), OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"), N_("trailer(s) to add")), @@ -34,9 +36,12 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) if (argc) { int i; for (i = 0; i < argc; i++) - process_trailers(argv[i], trim_empty, &trailers); - } else - process_trailers(NULL, trim_empty, &trailers); + process_trailers(argv[i], in_place, trim_empty, &trailers); + } else { + if (in_place) + die(_("no input file given for in-place editing")); + process_trailers(NULL, in_place, trim_empty, &trailers); + } string_list_clear(&trailers, 0); diff --git a/builtin/log.c b/builtin/log.c index e00cea75cc..dff3fbbb43 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -100,6 +100,12 @@ static int log_line_range_callback(const struct option *option, const char *arg, return 0; } +static void init_log_defaults(void) +{ + init_grep_defaults(); + init_diff_ui_defaults(); +} + static void cmd_log_init_defaults(struct rev_info *rev) { if (fmt_pretty) @@ -416,7 +422,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) struct rev_info rev; struct setup_revision_opt opt; - init_grep_defaults(); + init_log_defaults(); git_config(git_log_config, NULL); init_revisions(&rev, prefix); @@ -527,7 +533,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) struct pathspec match_all; int i, count, ret = 0; - init_grep_defaults(); + init_log_defaults(); git_config(git_log_config, NULL); memset(&match_all, 0, sizeof(match_all)); @@ -608,7 +614,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) struct rev_info rev; struct setup_revision_opt opt; - init_grep_defaults(); + init_log_defaults(); git_config(git_log_config, NULL); init_revisions(&rev, prefix); @@ -647,7 +653,7 @@ int cmd_log(int argc, const char **argv, const char *prefix) struct rev_info rev; struct setup_revision_opt opt; - init_grep_defaults(); + init_log_defaults(); git_config(git_log_config, NULL); init_revisions(&rev, prefix); @@ -699,6 +705,7 @@ static int do_signoff; static const char *signature = git_version_string; static const char *signature_file; static int config_cover_letter; +static const char *config_output_directory; enum { COVER_UNSET, @@ -777,6 +784,8 @@ static int git_format_config(const char *var, const char *value, void *cb) config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; return 0; } + if (!strcmp(var, "format.outputdirectory")) + return git_config_string(&config_output_directory, var, value); return git_log_config(var, value, cb); } @@ -1277,10 +1286,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) extra_hdr.strdup_strings = 1; extra_to.strdup_strings = 1; extra_cc.strdup_strings = 1; - init_grep_defaults(); + init_log_defaults(); git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; + rev.expand_tabs_in_log_default = 0; rev.verbose_header = 1; rev.diff = 1; rev.max_parents = 1; @@ -1391,6 +1401,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (rev.show_notes) init_display_notes(&rev.notes_opt); + if (!output_directory && !use_stdout) + output_directory = config_output_directory; + if (!use_stdout) output_directory = set_outdir(prefix, output_directory); else diff --git a/builtin/ls-files.c b/builtin/ls-files.c index b6a7cb0c7c..f02e3d23bb 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -27,6 +27,7 @@ static int show_killed; static int show_valid_bit; static int line_terminator = '\n'; static int debug_mode; +static int show_eol; static const char *prefix; static int max_prefix_len; @@ -47,6 +48,23 @@ static const char *tag_modified = ""; static const char *tag_skip_worktree = ""; static const char *tag_resolve_undo = ""; +static void write_eolinfo(const struct cache_entry *ce, const char *path) +{ + if (!show_eol) + return; + else { + struct stat st; + const char *i_txt = ""; + const char *w_txt = ""; + const char *a_txt = get_convert_attr_ascii(path); + if (ce && S_ISREG(ce->ce_mode)) + i_txt = get_cached_convert_stats_ascii(ce->name); + if (!lstat(path, &st) && S_ISREG(st.st_mode)) + w_txt = get_wt_convert_stats_ascii(path); + printf("i/%-5s w/%-5s attr/%-17s\t", i_txt, w_txt, a_txt); + } +} + static void write_name(const char *name) { /* @@ -68,6 +86,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) return; fputs(tag, stdout); + write_eolinfo(NULL, ent->name); write_name(ent->name); } @@ -170,6 +189,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) find_unique_abbrev(ce->sha1,abbrev), ce_stage(ce)); } + write_eolinfo(ce, ce->name); write_name(ce->name); if (debug_mode) { const struct stat_data *sd = &ce->ce_stat_data; @@ -359,14 +379,6 @@ static const char * const ls_files_usage[] = { NULL }; -static int option_parse_z(const struct option *opt, - const char *arg, int unset) -{ - line_terminator = unset ? '\n' : '\0'; - - return 0; -} - static int option_parse_exclude(const struct option *opt, const char *arg, int unset) { @@ -408,9 +420,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) struct exclude_list *el; struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct option builtin_ls_files_options[] = { - { 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_terminator, + N_("paths are separated with NUL character"), '\0'), OPT_BOOL('t', NULL, &show_tag, N_("identify the file status with tags")), OPT_BOOL('v', NULL, &show_valid_bit, @@ -433,6 +445,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) OPT_BIT(0, "directory", &dir.flags, N_("show 'other' directories' names only"), DIR_SHOW_OTHER_DIRECTORIES), + OPT_BOOL(0, "eol", &show_eol, N_("show line endings of files")), OPT_NEGBIT(0, "empty-directory", &dir.flags, N_("don't show empty directories"), DIR_HIDE_EMPTY_DIRECTORIES), diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index fa65a8448a..66cdd45cc1 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -3,9 +3,12 @@ #include "transport.h" #include "remote.h" -static const char ls_remote_usage[] = -"git ls-remote [--heads] [--tags] [--upload-pack=<exec>]\n" -" [-q | --quiet] [--exit-code] [--get-url] [<repository> [<refs>...]]"; +static const char * const ls_remote_usage[] = { + N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n" + " [-q | --quiet] [--exit-code] [--get-url]\n" + " [--symref] [<repository> [<refs>...]]"), + NULL +}; /* * Is there one among the list of patterns that match the tail part @@ -30,12 +33,12 @@ static int tail_match(const char **pattern, const char *path) int cmd_ls_remote(int argc, const char **argv, const char *prefix) { - int i; const char *dest = NULL; unsigned flags = 0; int get_url = 0; int quiet = 0; int status = 0; + int show_symref_target = 0; const char *uploadpack = NULL; const char **pattern = NULL; @@ -43,59 +46,36 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) struct transport *transport; const struct ref *ref; - if (argc == 2 && !strcmp("-h", argv[1])) - usage(ls_remote_usage); + struct option options[] = { + OPT__QUIET(&quiet, N_("do not print remote URL")), + OPT_STRING(0, "upload-pack", &uploadpack, N_("exec"), + N_("path of git-upload-pack on the remote host")), + { OPTION_STRING, 0, "exec", &uploadpack, N_("exec"), + N_("path of git-upload-pack on the remote host"), + PARSE_OPT_HIDDEN }, + OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS), + OPT_BIT('h', "heads", &flags, N_("limit to heads"), REF_HEADS), + OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL), + OPT_BOOL(0, "get-url", &get_url, + N_("take url.<base>.insteadOf into account")), + OPT_SET_INT(0, "exit-code", &status, + N_("exit with exit code 2 if no matching refs are found"), 2), + OPT_BOOL(0, "symref", &show_symref_target, + N_("show underlying ref in addition to the object pointed by it")), + OPT_END() + }; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, prefix, options, ls_remote_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + dest = argv[0]; - if (*arg == '-') { - if (starts_with(arg, "--upload-pack=")) { - uploadpack = arg + 14; - continue; - } - if (starts_with(arg, "--exec=")) { - uploadpack = arg + 7; - continue; - } - if (!strcmp("--tags", arg) || !strcmp("-t", arg)) { - flags |= REF_TAGS; - continue; - } - if (!strcmp("--heads", arg) || !strcmp("-h", arg)) { - flags |= REF_HEADS; - continue; - } - if (!strcmp("--refs", arg)) { - flags |= REF_NORMAL; - continue; - } - if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { - quiet = 1; - continue; - } - if (!strcmp("--get-url", arg)) { - get_url = 1; - continue; - } - if (!strcmp("--exit-code", arg)) { - /* return this code if no refs are reported */ - status = 2; - continue; - } - usage(ls_remote_usage); - } - dest = arg; - i++; - break; + if (argc > 1) { + int i; + pattern = xcalloc(argc, sizeof(const char *)); + for (i = 1; i < argc; i++) + pattern[i - 1] = xstrfmt("*/%s", argv[i]); } - if (argv[i]) { - int j; - pattern = xcalloc(argc - i + 1, sizeof(const char *)); - for (j = i; j < argc; j++) - pattern[j - i] = xstrfmt("*/%s", argv[j]); - } remote = remote_get(dest); if (!remote) { if (dest) @@ -125,7 +105,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) continue; if (!tail_match(pattern, ref->name)) continue; - printf("%s %s\n", oid_to_hex(&ref->old_oid), ref->name); + if (show_symref_target && ref->symref) + printf("ref: %s\t%s\n", ref->symref, ref->name); + printf("%s\t%s\n", oid_to_hex(&ref->old_oid), ref->name); status = 0; /* we found something */ } return status; diff --git a/builtin/merge-base.c b/builtin/merge-base.c index a8911626c2..c0d1822eb3 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -252,7 +252,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) if (argc < 2) usage_with_options(merge_base_usage, options); - rev = xmalloc(argc * sizeof(*rev)); + ALLOC_ARRAY(rev, argc); while (argc-- > 0) rev[rev_nr++] = get_commit_reference(*argv++); return show_merge_base(rev, rev_nr, show_all); diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index d4f0cbd451..5b7ab9b967 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -150,15 +150,15 @@ static void show_result(void) /* An empty entry never compares same, not even to another empty entry */ static int same_entry(struct name_entry *a, struct name_entry *b) { - return a->sha1 && - b->sha1 && - !hashcmp(a->sha1, b->sha1) && + return a->oid && + b->oid && + !oidcmp(a->oid, b->oid) && a->mode == b->mode; } static int both_empty(struct name_entry *a, struct name_entry *b) { - return !(a->sha1 || b->sha1); + return !(a->oid || b->oid); } static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path) @@ -174,7 +174,7 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsi static char *traverse_path(const struct traverse_info *info, const struct name_entry *n) { - char *path = xmalloc(traverse_path_len(info, n) + 1); + char *path = xmallocz(traverse_path_len(info, n)); return make_traverse_path(path, info, n); } @@ -188,8 +188,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s return; path = traverse_path(info, result); - orig = create_entry(2, ours->mode, ours->sha1, path); - final = create_entry(0, result->mode, result->sha1, path); + orig = create_entry(2, ours->mode, ours->oid->hash, path); + final = create_entry(0, result->mode, result->oid->hash, path); final->link = orig; @@ -213,7 +213,7 @@ static void unresolved_directory(const struct traverse_info *info, newbase = traverse_path(info, p); -#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->sha1 : NULL) +#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid->hash : NULL) buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0)); buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1)); buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2)); @@ -239,7 +239,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info path = entry->path; else path = traverse_path(info, n); - link = create_entry(stage, n->mode, n->sha1, path); + link = create_entry(stage, n->mode, n->oid->hash, path); link->link = entry; return link; } @@ -314,7 +314,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s } if (same_entry(entry+0, entry+1)) { - if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) { + if (entry[2].oid && !S_ISDIR(entry[2].mode)) { /* We did not touch, they modified -- take theirs */ resolve(info, entry+1, entry+2); return mask; diff --git a/builtin/merge.c b/builtin/merge.c index b98a3489bf..b555a1bf9c 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -64,6 +64,7 @@ static int option_renormalize; static int verbosity; static int allow_rerere_auto; static int abort_current_merge; +static int allow_unrelated_histories; static int show_progress = -1; static int default_to_upstream = 1; static const char *sign_commit; @@ -221,6 +222,8 @@ static struct option builtin_merge_options[] = { OPT__VERBOSITY(&verbosity), OPT_BOOL(0, "abort", &abort_current_merge, N_("abort the current in-progress merge")), + OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories, + N_("allow merging unrelated histories")), OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, @@ -819,6 +822,14 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) { unsigned char result_tree[20], result_commit[20]; struct commit_list *parents, **pptr = &parents; + static struct lock_file lock; + + hold_locked_index(&lock, 1); + refresh_cache(REFRESH_QUIET); + if (active_cache_changed && + write_locked_index(&the_index, &lock, COMMIT_LOCK)) + return error(_("Unable to write index.")); + rollback_lock_file(&lock); write_tree_trivial(result_tree); printf(_("Wonderful.\n")); @@ -939,7 +950,7 @@ static int setup_with_upstream(const char ***argv) if (!branch->merge_nr) die(_("No default upstream defined for the current branch.")); - args = xcalloc(branch->merge_nr + 1, sizeof(char *)); + args = xcalloc(st_add(branch->merge_nr, 1), sizeof(char *)); for (i = 0; i < branch->merge_nr; i++) { if (!branch->merge[i]->dst) die(_("No remote-tracking branch for %s from %s"), @@ -1165,7 +1176,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; - int flag, i, ret = 0, head_subsumed; + int i, ret = 0, head_subsumed; int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; struct commit_list *common = NULL; const char *best_strategy = NULL, *wt_strategy = NULL; @@ -1179,7 +1190,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ - branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag); + branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL); if (branch && starts_with(branch, "refs/heads/")) branch += 11; if (!branch || is_null_sha1(head_sha1)) @@ -1187,6 +1198,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) else head_commit = lookup_commit_or_die(head_sha1, "HEAD"); + init_diff_ui_defaults(); git_config(git_merge_config, NULL); if (branch_mergeoptions) @@ -1257,12 +1269,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix) builtin_merge_options); if (!head_commit) { - struct commit *remote_head; /* * If the merged head is a valid one there is no reason * to forbid "git merge" into a branch yet to be born. * We do the same for "git pull". */ + unsigned char *remote_head_sha1; if (squash) die(_("Squash commit into empty head not supported yet")); if (fast_forward == FF_NO) @@ -1270,13 +1282,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix) "an empty head")); remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv, NULL); - remote_head = remoteheads->item; - if (!remote_head) + if (!remoteheads) die(_("%s - not something we can merge"), argv[0]); if (remoteheads->next) die(_("Can merge only exactly one commit into empty head")); - read_empty(remote_head->object.oid.hash, 0); - update_ref("initial pull", "HEAD", remote_head->object.oid.hash, + remote_head_sha1 = remoteheads->item->object.oid.hash; + read_empty(remote_head_sha1, 0); + update_ref("initial pull", "HEAD", remote_head_sha1, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; } @@ -1397,9 +1409,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix) update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); - if (remoteheads && !common) - ; /* No common ancestors found. We need a real merge. */ - else if (!remoteheads || + if (remoteheads && !common) { + /* No common ancestors found. */ + if (!allow_unrelated_histories) + die(_("refusing to merge unrelated histories")); + /* otherwise, we need a real merge. */ + } else if (!remoteheads || (!remoteheads->next && !common->next && common->item == remoteheads->item)) { /* diff --git a/builtin/mktree.c b/builtin/mktree.c index a964d6be52..4282b62c59 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -19,16 +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 @@ 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 @@ static void mktree_line(char *buf, size_t len, int line_termination, int allow_m *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,23 +142,25 @@ int cmd_mktree(int ac, const char **av, const char *prefix) { 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; } @@ -167,7 +170,7 @@ int cmd_mktree(int ac, const char **av, const char *prefix) 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 --git a/builtin/mv.c b/builtin/mv.c index d1d43168ae..a2014266b6 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -24,7 +24,8 @@ static const char **internal_copy_pathspec(const char *prefix, int count, unsigned flags) { int i; - const char **result = xmalloc((count + 1) * sizeof(const char *)); + const char **result; + ALLOC_ARRAY(result, count + 1); memcpy(result, pathspec, count * sizeof(const char *)); result[count] = NULL; for (i = 0; i < count; i++) { @@ -47,9 +48,9 @@ static const char **internal_copy_pathspec(const char *prefix, static const char *add_slash(const char *path) { - int len = strlen(path); + size_t len = strlen(path); if (path[len - 1] != '/') { - char *with_slash = xmalloc(len + 2); + char *with_slash = xmalloc(st_add(len, 2)); memcpy(with_slash, path, len); with_slash[len++] = '/'; with_slash[len] = 0; @@ -251,15 +252,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); - if (!show_only && mode != INDEX) { - if (rename(src, dst) < 0 && !ignore_errors) - die_errno(_("renaming '%s' failed"), src); - if (submodule_gitfile[i]) { - if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) - connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); - if (!update_path_in_gitmodules(src, dst)) - gitmodules_modified = 1; - } + if (show_only) + continue; + if (mode != INDEX && rename(src, dst) < 0) { + if (ignore_errors) + continue; + die_errno(_("renaming '%s' failed"), src); + } + if (submodule_gitfile[i]) { + if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) + connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); + if (!update_path_in_gitmodules(src, dst)) + gitmodules_modified = 1; } if (mode == WORKING_DIRECTORY) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 092e03c3cc..57be35faf5 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -10,6 +10,7 @@ typedef struct rev_name { const char *tip_name; + unsigned long taggerdate; int generation; int distance; } rev_name; @@ -20,7 +21,8 @@ static long cutoff = LONG_MAX; #define MERGE_TRAVERSAL_WEIGHT 65535 static void name_rev(struct commit *commit, - const char *tip_name, int generation, int distance, + const char *tip_name, unsigned long taggerdate, + int generation, int distance, int deref) { struct rev_name *name = (struct rev_name *)commit->util; @@ -43,9 +45,12 @@ static void name_rev(struct commit *commit, name = xmalloc(sizeof(rev_name)); commit->util = name; goto copy_data; - } else if (name->distance > distance) { + } else if (name->taggerdate > taggerdate || + (name->taggerdate == taggerdate && + name->distance > distance)) { copy_data: name->tip_name = tip_name; + name->taggerdate = taggerdate; name->generation = generation; name->distance = distance; } else @@ -66,11 +71,11 @@ copy_data: new_name = xstrfmt("%.*s^%d", (int)len, tip_name, parent_number); - name_rev(parents->item, new_name, 0, + name_rev(parents->item, new_name, taggerdate, 0, distance + MERGE_TRAVERSAL_WEIGHT, 0); } else { - name_rev(parents->item, tip_name, generation + 1, - distance + 1, 0); + name_rev(parents->item, tip_name, taggerdate, + generation + 1, distance + 1, 0); } } } @@ -140,6 +145,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo struct name_ref_data *data = cb_data; int can_abbreviate_output = data->tags_only && data->name_only; int deref = 0; + unsigned long taggerdate = ULONG_MAX; if (data->tags_only && !starts_with(path, "refs/tags/")) return 0; @@ -164,12 +170,13 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo break; /* broken repository */ o = parse_object(t->tagged->oid.hash); deref = 1; + taggerdate = t->date; } if (o && o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; path = name_ref_abbrev(path, can_abbreviate_output); - name_rev(commit, xstrdup(path), 0, 0, deref); + name_rev(commit, xstrdup(path), taggerdate, 0, 0, deref); } return 0; } diff --git a/builtin/notes.c b/builtin/notes.c index 52aa9af74b..6fd058de92 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -286,11 +286,11 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) if (!c) return 0; } else { - init_notes(NULL, NULL, NULL, 0); + init_notes(NULL, NULL, NULL, NOTES_INIT_WRITABLE); t = &default_notes_tree; } - while (strbuf_getline(&buf, stdin, '\n') != EOF) { + while (strbuf_getline_lf(&buf, stdin) != EOF) { unsigned char from_obj[20], to_obj[20]; struct strbuf **split; int err; @@ -329,15 +329,18 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) return ret; } -static struct notes_tree *init_notes_check(const char *subcommand) +static struct notes_tree *init_notes_check(const char *subcommand, + int flags) { struct notes_tree *t; - init_notes(NULL, NULL, NULL, 0); + const char *ref; + init_notes(NULL, NULL, NULL, flags); t = &default_notes_tree; - if (!starts_with(t->ref, "refs/notes/")) + ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref; + if (!starts_with(ref, "refs/notes/")) die("Refusing to %s notes in %s (outside of refs/notes/)", - subcommand, t->ref); + subcommand, ref); return t; } @@ -360,7 +363,7 @@ static int list(int argc, const char **argv, const char *prefix) usage_with_options(git_notes_list_usage, options); } - t = init_notes_check("list"); + t = init_notes_check("list", 0); if (argc) { if (get_sha1(argv[0], object)) die(_("Failed to resolve '%s' as a valid ref."), argv[0]); @@ -420,7 +423,7 @@ static int add(int argc, const char **argv, const char *prefix) if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check("add"); + t = init_notes_check("add", NOTES_INIT_WRITABLE); note = get_note(t, object); if (note) { @@ -511,7 +514,7 @@ static int copy(int argc, const char **argv, const char *prefix) if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check("copy"); + t = init_notes_check("copy", NOTES_INIT_WRITABLE); note = get_note(t, object); if (note) { @@ -589,7 +592,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check(argv[0]); + t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); note = get_note(t, object); prepare_note_data(object, &d, edit ? note : NULL); @@ -652,7 +655,7 @@ static int show(int argc, const char **argv, const char *prefix) if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check("show"); + t = init_notes_check("show", 0); note = get_note(t, object); if (!note) @@ -741,13 +744,14 @@ static int merge_commit(struct notes_merge_options *o) static int git_config_get_notes_strategy(const char *key, enum notes_merge_strategy *strategy) { - const char *value; + char *value; - if (git_config_get_string_const(key, &value)) + if (git_config_get_string(key, &value)) return 1; if (parse_notes_merge_strategy(value, strategy)) git_die_config(key, "unknown notes merge strategy %s", value); + free(value); return 0; } @@ -806,10 +810,10 @@ static int merge(int argc, const char **argv, const char *prefix) o.local_ref = default_notes_ref(); strbuf_addstr(&remote_ref, argv[0]); - expand_notes_ref(&remote_ref); + expand_loose_notes_ref(&remote_ref); o.remote_ref = remote_ref.buf; - t = init_notes_check("merge"); + t = init_notes_check("merge", NOTES_INIT_WRITABLE); if (strategy) { if (parse_notes_merge_strategy(strategy, &o.strategy)) { @@ -901,7 +905,7 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_notes_remove_usage, 0); - t = init_notes_check("remove"); + t = init_notes_check("remove", NOTES_INIT_WRITABLE); if (!argc && !from_stdin) { retval = remove_one_note(t, "HEAD", flag); @@ -943,7 +947,7 @@ static int prune(int argc, const char **argv, const char *prefix) usage_with_options(git_notes_prune_usage, options); } - t = init_notes_check("prune"); + t = init_notes_check("prune", NOTES_INIT_WRITABLE); prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) | (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) ); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 4dae5b11c2..14dccb5283 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -624,7 +624,7 @@ static struct object_entry **compute_write_order(void) { 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 @@ static struct object_entry **compute_write_order(void) * 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; @@ -758,6 +759,10 @@ static off_t write_reused_pack(struct sha1file *f) return reuse_packfile_offset - sizeof(struct pack_header); } +static const char no_split_warning[] = N_( +"disabling bitmap writing, packs are split due to pack.packSizeLimit" +); + static void write_pack_file(void) { uint32_t i = 0, j; @@ -769,7 +774,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 { @@ -812,7 +817,10 @@ static void write_pack_file(void) fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written, sha1, offset); close(fd); - write_bitmap_index = 0; + if (write_bitmap_index) { + warning(_(no_split_warning)); + write_bitmap_index = 0; + } } if (!pack_to_stdout) { @@ -1185,7 +1193,7 @@ static void add_pbase_object(struct tree_desc *tree, if (cmp < 0) return; if (name[cmplen] != '/') { - add_object_entry(entry.sha1, + add_object_entry(entry.oid->hash, object_type(entry.mode), fullname, 1); return; @@ -1196,7 +1204,7 @@ static void add_pbase_object(struct tree_desc *tree, const char *down = name+cmplen+1; int downlen = name_cmp_len(down); - tree = pbase_tree_get(entry.sha1); + tree = pbase_tree_get(entry.oid->hash); if (!tree) return; init_tree_desc(&sub, tree->tree_data, tree->tree_size); @@ -2129,7 +2137,7 @@ static void prepare_pack(int window, int depth) 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,21 +2292,11 @@ static void show_commit(struct commit *commit, void *data) 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) @@ -2480,8 +2478,7 @@ static int get_object_list_from_bitmap(struct rev_info *revs) } 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 --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index d0532f66b1..72c815844d 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -53,7 +53,7 @@ static inline struct llist_item *llist_item_get(void) free_nodes = free_nodes->next; } else { int i = 1; - new = xmalloc(sizeof(struct llist_item) * BLKSIZE); + ALLOC_ARRAY(new, BLKSIZE); for (; i < BLKSIZE; i++) llist_item_put(&new[i]); } diff --git a/builtin/pull.c b/builtin/pull.c index 5145fc60a0..596b92fc56 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -22,7 +22,8 @@ enum rebase_type { REBASE_INVALID = -1, REBASE_FALSE = 0, REBASE_TRUE, - REBASE_PRESERVE + REBASE_PRESERVE, + REBASE_INTERACTIVE }; /** @@ -42,6 +43,8 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value, return REBASE_TRUE; else if (!strcmp(value, "preserve")) return REBASE_PRESERVE; + else if (!strcmp(value, "interactive")) + return REBASE_INTERACTIVE; if (fatal) die(_("Invalid value for %s: %s"), key, value); @@ -83,9 +86,12 @@ static char *opt_commit; static char *opt_edit; static char *opt_ff; static char *opt_verify_signatures; +static int opt_autostash = -1; +static int config_autostash; static struct argv_array opt_strategies = ARGV_ARRAY_INIT; static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT; static char *opt_gpg_sign; +static int opt_allow_unrelated_histories; /* Options passed to git-fetch */ static char *opt_all; @@ -95,6 +101,7 @@ static int opt_force; static char *opt_tags; static char *opt_prune; static char *opt_recurse_submodules; +static char *max_children; static int opt_dry_run; static char *opt_keep; static char *opt_depth; @@ -112,7 +119,7 @@ static struct option pull_options[] = { /* Options passed to git-merge or git-rebase */ OPT_GROUP(N_("Options related to merging")), { OPTION_CALLBACK, 'r', "rebase", &opt_rebase, - "false|true|preserve", + "false|true|preserve|interactive", N_("incorporate changes by rebasing rather than merging"), PARSE_OPT_OPTARG, parse_opt_rebase }, OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL, @@ -145,6 +152,8 @@ static struct option pull_options[] = { OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL, N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG), + OPT_BOOL(0, "autostash", &opt_autostash, + N_("automatically stash/stash pop before and after rebase")), OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"), N_("merge strategy to use"), 0), @@ -155,6 +164,9 @@ static struct option pull_options[] = { OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG), + OPT_SET_INT(0, "allow-unrelated-histories", + &opt_allow_unrelated_histories, + N_("allow merging unrelated histories"), 1), /* Options passed to git-fetch */ OPT_GROUP(N_("Options related to fetching")), @@ -178,6 +190,9 @@ static struct option pull_options[] = { N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG), + OPT_PASSTHRU('j', "jobs", &max_children, N_("n"), + N_("number of submodules pulled in parallel"), + PARSE_OPT_OPTARG), OPT_BOOL(0, "dry-run", &opt_dry_run, N_("dry run")), OPT_PASSTHRU('k', "keep", &opt_keep, NULL, @@ -299,6 +314,18 @@ static enum rebase_type config_get_rebase(void) } /** + * Read config variables. + */ +static int git_pull_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "rebase.autostash")) { + config_autostash = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, cb); +} + +/** * Returns 1 if there are unstaged changes, 0 otherwise. */ static int has_unstaged_changes(const char *prefix) @@ -378,7 +405,7 @@ static void get_merge_heads(struct sha1_array *merge_heads) if (!(fp = fopen(filename, "r"))) die_errno(_("could not open '%s' for reading"), filename); - while (strbuf_getline(&sb, fp, '\n') != EOF) { + while (strbuf_getline_lf(&sb, fp) != EOF) { if (get_sha1_hex(sb.buf, sha1)) continue; /* invalid line: does not start with SHA1 */ if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t")) @@ -525,6 +552,8 @@ static int run_fetch(const char *repo, const char **refspecs) argv_array_push(&args, opt_prune); if (opt_recurse_submodules) argv_array_push(&args, opt_recurse_submodules); + if (max_children) + argv_array_push(&args, max_children); if (opt_dry_run) argv_array_push(&args, "--dry-run"); if (opt_keep) @@ -603,6 +632,8 @@ static int run_merge(void) argv_array_pushv(&args, opt_strategy_opts.argv); if (opt_gpg_sign) argv_array_push(&args, opt_gpg_sign); + if (opt_allow_unrelated_histories > 0) + argv_array_push(&args, "--allow-unrelated-histories"); argv_array_push(&args, "FETCH_HEAD"); ret = run_command_v_opt(args.argv, RUN_GIT_CMD); @@ -772,12 +803,18 @@ static int run_rebase(const unsigned char *curr_head, /* Options passed to git-rebase */ if (opt_rebase == REBASE_PRESERVE) argv_array_push(&args, "--preserve-merges"); + else if (opt_rebase == REBASE_INTERACTIVE) + argv_array_push(&args, "--interactive"); if (opt_diffstat) argv_array_push(&args, opt_diffstat); argv_array_pushv(&args, opt_strategies.argv); argv_array_pushv(&args, opt_strategy_opts.argv); if (opt_gpg_sign) argv_array_push(&args, opt_gpg_sign); + if (opt_autostash == 0) + argv_array_push(&args, "--no-autostash"); + else if (opt_autostash == 1) + argv_array_push(&args, "--autostash"); argv_array_push(&args, "--onto"); argv_array_push(&args, sha1_to_hex(merge_head)); @@ -812,7 +849,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (opt_rebase < 0) opt_rebase = config_get_rebase(); - git_config(git_default_config, NULL); + git_config(git_pull_config, NULL); if (read_cache_unmerged()) die_resolve_conflict("Pull"); @@ -823,13 +860,17 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (get_sha1("HEAD", orig_head)) hashclr(orig_head); + if (!opt_rebase && opt_autostash != -1) + die(_("--[no-]autostash option is only valid with --rebase.")); + if (opt_rebase) { - int autostash = 0; + int autostash = config_autostash; + if (opt_autostash != -1) + autostash = opt_autostash; if (is_null_sha1(orig_head) && !is_cache_unborn()) die(_("Updating an unborn branch with changes added to the index.")); - git_config_get_bool("rebase.autostash", &autostash); if (!autostash) die_on_unclean_work_tree(prefix); diff --git a/builtin/push.c b/builtin/push.c index 8963dbdf3d..4e9e4dbab2 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -23,6 +23,7 @@ static const char *receivepack; static int verbosity; static int progress = -1; static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; +static enum transport_family family; static struct push_cas_option cas; @@ -204,37 +205,6 @@ static void setup_push_current(struct remote *remote, struct branch *branch) add_refspec(branch->name); } -static char warn_unspecified_push_default_msg[] = -N_("push.default is unset; its implicit value has changed in\n" - "Git 2.0 from 'matching' to 'simple'. To squelch this message\n" - "and maintain the traditional behavior, use:\n" - "\n" - " git config --global push.default matching\n" - "\n" - "To squelch this message and adopt the new behavior now, use:\n" - "\n" - " git config --global push.default simple\n" - "\n" - "When push.default is set to 'matching', git will push local branches\n" - "to the remote branches that already exist with the same name.\n" - "\n" - "Since Git 2.0, Git defaults to the more conservative 'simple'\n" - "behavior, which only pushes the current branch to the corresponding\n" - "remote branch that 'git pull' uses to update the current branch.\n" - "\n" - "See 'git help config' and search for 'push.default' for further information.\n" - "(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode\n" - "'current' instead of 'simple' if you sometimes use older versions of Git)"); - -static void warn_unspecified_push_default_configuration(void) -{ - static int warn_once; - - if (warn_once++) - return; - warning("%s\n", _(warn_unspecified_push_default_msg)); -} - static int is_workflow_triangular(struct remote *remote) { struct remote *fetch_remote = remote_get(NULL); @@ -253,9 +223,6 @@ static void setup_default_push_refspecs(struct remote *remote) break; case PUSH_DEFAULT_UNSPECIFIED: - warn_unspecified_push_default_configuration(); - /* fallthru */ - case PUSH_DEFAULT_SIMPLE: if (triangular) setup_push_current(remote, branch); @@ -346,6 +313,7 @@ static int push_with_options(struct transport *transport, int flags) unsigned int reject_reasons; transport_set_verbosity(transport, verbosity, progress); + transport->family = family; if (receivepack) transport_set_option(transport, @@ -538,7 +506,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL), OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"), (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)), - OPT_BOOL( 0, "delete", &deleterefs, N_("delete refs")), + OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")), OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")), OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN), OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN), @@ -565,6 +533,10 @@ int cmd_push(int argc, const char **argv, const char *prefix) 0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"), PARSE_OPT_OPTARG, option_parse_push_signed }, OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC), + 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() }; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index b3104433ed..a744437b58 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -21,7 +21,10 @@ #include "sigchain.h" #include "fsck.h" -static const char receive_pack_usage[] = "git receive-pack <git-dir>"; +static const char * const receive_pack_usage[] = { + N_("git receive-pack <git-dir>"), + NULL +}; enum deny_action { DENY_UNCONFIGURED, @@ -49,7 +52,7 @@ static int quiet; static int prefer_ofs_delta = 1; static int auto_update_server_info; static int auto_gc = 1; -static int fix_thin = 1; +static int reject_thin; static int stateless_rpc; static const char *service_dir; static const char *head_name; @@ -1031,7 +1034,6 @@ static void run_update_post_hook(struct command *commands) { struct command *cmd; int argc; - const char **argv; struct child_process proc = CHILD_PROCESS_INIT; const char *hook; @@ -1044,21 +1046,16 @@ static void run_update_post_hook(struct command *commands) if (!argc || !hook) return; - argv = xmalloc(sizeof(*argv) * (2 + argc)); - argv[0] = hook; - - for (argc = 1, cmd = commands; cmd; cmd = cmd->next) { + argv_array_push(&proc.args, hook); + for (cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string || cmd->did_not_exist) continue; - argv[argc] = xstrdup(cmd->ref_name); - argc++; + argv_array_push(&proc.args, cmd->ref_name); } - argv[argc] = NULL; proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; - proc.argv = argv; if (!start_command(&proc)) { if (use_sideband) @@ -1087,13 +1084,13 @@ static void check_aliased_update(struct command *cmd, struct string_list *list) if (!(flag & REF_ISSYMREF)) return; - dst_name = strip_namespace(dst_name); if (!dst_name) { rp_error("refusing update to broken symref '%s'", cmd->ref_name); cmd->skip_update = 1; cmd->error_string = "broken symref"; return; } + dst_name = strip_namespace(dst_name); if ((item = string_list_lookup(list, dst_name)) == NULL) return; @@ -1378,7 +1375,7 @@ static struct command **queue_command(struct command **tail, refname = line + 82; reflen = linelen - 82; - cmd = xcalloc(1, sizeof(struct command) + reflen + 1); + cmd = xcalloc(1, st_add3(sizeof(struct command), reflen, 1)); hashcpy(cmd->old_sha1, old_sha1); hashcpy(cmd->new_sha1, new_sha1); memcpy(cmd->ref_name, refname, reflen); @@ -1554,7 +1551,7 @@ static const char *unpack(int err_fd, struct shallow_info *si) if (fsck_objects) argv_array_pushf(&child.args, "--strict%s", fsck_msg_types.buf); - if (fix_thin) + if (!reject_thin) argv_array_push(&child.args, "--fix-thin"); child.out = -1; child.err = err_fd; @@ -1597,8 +1594,7 @@ static void prepare_shallow_update(struct command *commands, { int i, j, k, bitmap_size = (si->ref->nr + 31) / 32; - si->used_shallow = xmalloc(sizeof(*si->used_shallow) * - si->shallow->nr); + ALLOC_ARRAY(si->used_shallow, si->shallow->nr); assign_shallow_commits_to_refs(si, si->used_shallow, NULL); si->need_reachability_test = @@ -1618,7 +1614,7 @@ static void prepare_shallow_update(struct command *commands, continue; si->need_reachability_test[i]++; for (k = 0; k < 32; k++) - if (si->used_shallow[i][j] & (1 << k)) + if (si->used_shallow[i][j] & (1U << k)) si->shallow_ref[j * 32 + k]++; } @@ -1664,7 +1660,7 @@ static void update_shallow_info(struct command *commands, return; } - ref_status = xmalloc(sizeof(*ref_status) * ref->nr); + ALLOC_ARRAY(ref_status, ref->nr); assign_shallow_commits_to_refs(si, NULL, ref_status); for (cmd = commands; cmd; cmd = cmd->next) { if (is_null_sha1(cmd->new_sha1)) @@ -1714,45 +1710,29 @@ static int delete_only(struct command *commands) int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int advertise_refs = 0; - int i; struct command *commands; struct sha1_array shallow = SHA1_ARRAY_INIT; struct sha1_array ref = SHA1_ARRAY_INIT; struct shallow_info si; - packet_trace_identity("receive-pack"); + struct option options[] = { + OPT__QUIET(&quiet, N_("quiet")), + OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL), + OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL), + OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL), + OPT_END() + }; - argv++; - for (i = 1; i < argc; i++) { - const char *arg = *argv++; + packet_trace_identity("receive-pack"); - if (*arg == '-') { - if (!strcmp(arg, "--quiet")) { - quiet = 1; - continue; - } + argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0); - if (!strcmp(arg, "--advertise-refs")) { - advertise_refs = 1; - continue; - } - if (!strcmp(arg, "--stateless-rpc")) { - stateless_rpc = 1; - continue; - } - if (!strcmp(arg, "--reject-thin-pack-for-testing")) { - fix_thin = 0; - continue; - } + if (argc > 1) + usage_msg_opt(_("Too many arguments."), receive_pack_usage, options); + if (argc == 0) + usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options); - usage(receive_pack_usage); - } - if (service_dir) - usage(receive_pack_usage); - service_dir = arg; - } - if (!service_dir) - usage(receive_pack_usage); + service_dir = argv[0]; setup_path(); diff --git a/builtin/reflog.c b/builtin/reflog.c index f39960e5e4..7a7136e53e 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -84,8 +84,8 @@ static int tree_is_complete(const unsigned char *sha1) init_tree_desc(&desc, tree->buffer, tree->size); complete = 1; while (tree_entry(&desc, &entry)) { - if (!has_sha1_file(entry.sha1) || - (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) { + if (!has_sha1_file(entry.oid->hash) || + (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid->hash))) { tree->object.flags |= INCOMPLETE; complete = 0; } @@ -382,11 +382,9 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus { struct collected_reflog *e; struct collect_reflog_cb *cb = cb_data; - size_t namelen = strlen(ref); - e = xmalloc(sizeof(*e) + namelen + 1); + FLEX_ALLOC_STR(e, reflog, ref); hashcpy(e->sha1, oid->hash); - memcpy(e->reflog, ref, namelen + 1); ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); cb->e[cb->nr++] = e; return 0; @@ -396,7 +394,6 @@ static struct reflog_expire_cfg { struct reflog_expire_cfg *next; unsigned long expire_total; unsigned long expire_unreachable; - size_t len; char pattern[FLEX_ARRAY]; } *reflog_expire_cfg, **reflog_expire_cfg_tail; @@ -408,13 +405,11 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len) reflog_expire_cfg_tail = &reflog_expire_cfg; for (ent = reflog_expire_cfg; ent; ent = ent->next) - if (ent->len == len && - !memcmp(ent->pattern, pattern, len)) + if (!strncmp(ent->pattern, pattern, len) && + ent->pattern[len] == '\0') return ent; - ent = xcalloc(1, (sizeof(*ent) + len)); - memcpy(ent->pattern, pattern, len); - ent->len = len; + FLEX_ALLOC_MEM(ent, pattern, pattern, len); *reflog_expire_cfg_tail = ent; reflog_expire_cfg_tail = &(ent->next); return ent; diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index e3cd25d580..7457c743e8 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -114,30 +114,14 @@ static char *strip_escapes(const char *str, const char *service, } } -/* Should be enough... */ -#define MAXARGUMENTS 256 - -static const char **parse_argv(const char *arg, const char *service) +static void parse_argv(struct argv_array *out, const char *arg, const char *service) { - int arguments = 0; - int i; - const char **ret; - char *temparray[MAXARGUMENTS + 1]; - while (*arg) { - char *expanded; - if (arguments == MAXARGUMENTS) - die("remote-ext command has too many arguments"); - expanded = strip_escapes(arg, service, &arg); + char *expanded = strip_escapes(arg, service, &arg); if (expanded) - temparray[arguments++] = expanded; + argv_array_push(out, expanded); + free(expanded); } - - ret = xmalloc((arguments + 1) * sizeof(char *)); - for (i = 0; i < arguments; i++) - ret[i] = temparray[i]; - ret[arguments] = NULL; - return ret; } static void send_git_request(int stdin_fd, const char *serv, const char *repo, @@ -158,7 +142,7 @@ static int run_child(const char *arg, const char *service) child.in = -1; child.out = -1; child.err = 0; - child.argv = parse_argv(arg, service); + parse_argv(&child.args, arg, service); if (start_command(&child) < 0) die("Can't run specified command"); diff --git a/builtin/remote.c b/builtin/remote.c index 6694cf20ef..fda5c2e53d 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -108,8 +108,8 @@ enum { #define MIRROR_PUSH 2 #define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH) -static int add_branch(const char *key, const char *branchname, - const char *remotename, int mirror, struct strbuf *tmp) +static void add_branch(const char *key, const char *branchname, + const char *remotename, int mirror, struct strbuf *tmp) { strbuf_reset(tmp); strbuf_addch(tmp, '+'); @@ -119,7 +119,7 @@ static int add_branch(const char *key, const char *branchname, else strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s", branchname, remotename, branchname); - return git_config_set_multivar(key, tmp->buf, "^$", 0); + git_config_set_multivar(key, tmp->buf, "^$", 0); } static const char mirror_advice[] = @@ -186,10 +186,7 @@ static int add(int argc, const char **argv) url = argv[1]; remote = remote_get(name); - if (remote && (remote->url_nr > 1 || - (strcmp(name, remote->url[0]) && - strcmp(url, remote->url[0])) || - remote->fetch_refspec_nr)) + if (remote_is_configured(remote)) die(_("remote %s already exists."), name); strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name); @@ -197,8 +194,7 @@ static int add(int argc, const char **argv) die(_("'%s' is not a valid remote name"), name); strbuf_addf(&buf, "remote.%s.url", name); - if (git_config_set(buf.buf, url)) - return 1; + git_config_set(buf.buf, url); if (!mirror || mirror & MIRROR_FETCH) { strbuf_reset(&buf); @@ -206,25 +202,22 @@ static int add(int argc, const char **argv) if (track.nr == 0) string_list_append(&track, "*"); for (i = 0; i < track.nr; i++) { - if (add_branch(buf.buf, track.items[i].string, - name, mirror, &buf2)) - return 1; + add_branch(buf.buf, track.items[i].string, + name, mirror, &buf2); } } if (mirror & MIRROR_PUSH) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.mirror", name); - if (git_config_set(buf.buf, "true")) - return 1; + git_config_set(buf.buf, "true"); } if (fetch_tags != TAGS_DEFAULT) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.tagopt", name); - if (git_config_set(buf.buf, - fetch_tags == TAGS_SET ? "--tags" : "--no-tags")) - return 1; + git_config_set(buf.buf, + fetch_tags == TAGS_SET ? "--tags" : "--no-tags"); } if (fetch && fetch_remote(name)) @@ -251,7 +244,7 @@ static int add(int argc, const char **argv) struct branch_info { char *remote_name; struct string_list merge; - int rebase; + enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase; }; static struct string_list branch_list; @@ -311,7 +304,9 @@ static int config_read_branches(const char *key, const char *value, void *cb) if (v >= 0) info->rebase = v; else if (!strcmp(value, "preserve")) - info->rebase = 1; + info->rebase = NORMAL_REBASE; + else if (!strcmp(value, "interactive")) + info->rebase = INTERACTIVE_REBASE; } } return 0; @@ -590,25 +585,20 @@ static int migrate_file(struct remote *remote) strbuf_addf(&buf, "remote.%s.url", remote->name); for (i = 0; i < remote->url_nr; i++) - if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0)) - return error(_("Could not append '%s' to '%s'"), - remote->url[i], buf.buf); + git_config_set_multivar(buf.buf, remote->url[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); for (i = 0; i < remote->push_refspec_nr; i++) - if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0)) - return error(_("Could not append '%s' to '%s'"), - remote->push_refspec[i], buf.buf); + git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); for (i = 0; i < remote->fetch_refspec_nr; i++) - if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0)) - return error(_("Could not append '%s' to '%s'"), - remote->fetch_refspec[i], buf.buf); + git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0); if (remote->origin == REMOTE_REMOTES) unlink_or_warn(git_path("remotes/%s", remote->name)); else if (remote->origin == REMOTE_BRANCHES) unlink_or_warn(git_path("branches/%s", remote->name)); + return 0; } @@ -632,14 +622,14 @@ static int mv(int argc, const char **argv) rename.remote_branches = &remote_branches; oldremote = remote_get(rename.old); - if (!oldremote) + if (!remote_is_configured(oldremote)) die(_("No such remote: %s"), rename.old); if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG) return migrate_file(oldremote); newremote = remote_get(rename.new); - if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr)) + if (remote_is_configured(newremote)) die(_("remote %s already exists."), rename.new); strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new); @@ -655,8 +645,7 @@ static int mv(int argc, const char **argv) strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", rename.new); - if (git_config_set_multivar(buf.buf, NULL, NULL, 1)) - return error(_("Could not remove config section '%s'"), buf.buf); + git_config_set_multivar(buf.buf, NULL, NULL, 1); strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old); for (i = 0; i < oldremote->fetch_refspec_nr; i++) { char *ptr; @@ -676,8 +665,7 @@ static int mv(int argc, const char **argv) "\tPlease update the configuration manually if necessary."), buf2.buf); - if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0)) - return error(_("Could not append '%s'"), buf.buf); + git_config_set_multivar(buf.buf, buf2.buf, "^$", 0); } read_branches(); @@ -687,9 +675,7 @@ static int mv(int argc, const char **argv) if (info->remote_name && !strcmp(info->remote_name, rename.old)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", item->string); - if (git_config_set(buf.buf, rename.new)) { - return error(_("Could not set '%s'"), buf.buf); - } + git_config_set(buf.buf, rename.new); } } @@ -771,7 +757,7 @@ static int rm(int argc, const char **argv) usage_with_options(builtin_remote_rm_usage, options); remote = remote_get(argv[1]); - if (!remote) + if (!remote_is_configured(remote)) die(_("No such remote: %s"), argv[1]); known_remotes.to_delete = remote; @@ -787,10 +773,7 @@ static int rm(int argc, const char **argv) strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.%s", item->string, *k); - if (git_config_set(buf.buf, NULL)) { - strbuf_release(&buf); - return -1; - } + git_config_set(buf.buf, NULL); } } } @@ -980,7 +963,9 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data) printf(" %-*s ", show_info->width, item->string); if (branch_info->rebase) { - printf_ln(_("rebases onto remote %s"), merge->items[0].string); + printf_ln(_(branch_info->rebase == INTERACTIVE_REBASE ? + "rebases interactively onto remote %s" : + "rebases onto remote %s"), merge->items[0].string); return 0; } else if (show_info->any_rebase) { printf_ln(_(" merges with remote %s"), merge->items[0].string); @@ -1409,24 +1394,20 @@ static int update(int argc, const char **argv) static int remove_all_fetch_refspecs(const char *remote, const char *key) { - return git_config_set_multivar(key, NULL, NULL, 1); + return git_config_set_multivar_gently(key, NULL, NULL, 1); } -static int add_branches(struct remote *remote, const char **branches, - const char *key) +static void add_branches(struct remote *remote, const char **branches, + const char *key) { const char *remotename = remote->name; int mirror = remote->mirror; struct strbuf refspec = STRBUF_INIT; for (; *branches; branches++) - if (add_branch(key, *branches, remotename, mirror, &refspec)) { - strbuf_release(&refspec); - return 1; - } + add_branch(key, *branches, remotename, mirror, &refspec); strbuf_release(&refspec); - return 0; } static int set_remote_branches(const char *remotename, const char **branches, @@ -1437,18 +1418,15 @@ static int set_remote_branches(const char *remotename, const char **branches, strbuf_addf(&key, "remote.%s.fetch", remotename); - if (!remote_is_configured(remotename)) - die(_("No such remote '%s'"), remotename); remote = remote_get(remotename); + if (!remote_is_configured(remote)) + die(_("No such remote '%s'"), remotename); if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) { strbuf_release(&key); return 1; } - if (add_branches(remote, branches, key.buf)) { - strbuf_release(&key); - return 1; - } + add_branches(remote, branches, key.buf); strbuf_release(&key); return 0; @@ -1494,9 +1472,9 @@ static int get_url(int argc, const char **argv) remotename = argv[0]; - if (!remote_is_configured(remotename)) - die(_("No such remote '%s'"), remotename); remote = remote_get(remotename); + if (!remote_is_configured(remote)) + die(_("No such remote '%s'"), remotename); url_nr = 0; if (push_mode) { @@ -1562,9 +1540,9 @@ static int set_url(int argc, const char **argv) if (delete_mode) oldurl = newurl; - if (!remote_is_configured(remotename)) - die(_("No such remote '%s'"), remotename); remote = remote_get(remotename); + if (!remote_is_configured(remote)) + die(_("No such remote '%s'"), remotename); if (push_mode) { strbuf_addf(&name_buf, "remote.%s.pushurl", remotename); @@ -1580,10 +1558,11 @@ static int set_url(int argc, const char **argv) if ((!oldurl && !delete_mode) || add_mode) { if (add_mode) git_config_set_multivar(name_buf.buf, newurl, - "^$", 0); + "^$", 0); else git_config_set(name_buf.buf, newurl); strbuf_release(&name_buf); + return 0; } diff --git a/builtin/repack.c b/builtin/repack.c index 945611006a..858db38f52 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -266,7 +266,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) return ret; out = xfdopen(cmd.out, "r"); - while (strbuf_getline(&line, out, '\n') != EOF) { + while (strbuf_getline_lf(&line, out) != EOF) { if (line.len != 40) die("repack: Expecting 40 character sha1 lines only from pack-objects."); string_list_append(&names, line.buf); diff --git a/builtin/replace.c b/builtin/replace.c index 748c6ca954..b58c714cb8 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -440,6 +440,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix) }; check_replace_refs = 0; + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 3aa89a1a3c..275da0d647 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -177,9 +177,7 @@ static void finish_commit(struct commit *commit, void *data) free_commit_buffer(commit); } -static void finish_object(struct object *obj, - const struct name_path *path, const char *name, - void *cb_data) +static void finish_object(struct object *obj, const char *name, void *cb_data) { struct rev_list_info *info = cb_data; if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) @@ -188,15 +186,13 @@ static void finish_object(struct object *obj, parse_object(obj->oid.hash); } -static void show_object(struct object *obj, - const struct name_path *path, const char *component, - void *cb_data) +static void show_object(struct object *obj, const char *name, void *cb_data) { struct rev_list_info *info = cb_data; - finish_object(obj, path, component, cb_data); + finish_object(obj, name, cb_data); if (info->flags & REV_LIST_QUIET) return; - show_object_with_name(stdout, obj, path, component); + show_object_with_name(stdout, obj, name); } static void show_edge(struct commit *commit) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 7e074aad40..c961b74c5a 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -383,7 +383,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) /* get the usage up to the first line with a -- on it */ for (;;) { - if (strbuf_getline(&sb, stdin, '\n') == EOF) + if (strbuf_getline(&sb, stdin) == EOF) die("premature end of input"); ALLOC_GROW(usage, unb + 1, usz); if (!strcmp("--", sb.buf)) { @@ -396,7 +396,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) } /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */ - while (strbuf_getline(&sb, stdin, '\n') != EOF) { + while (strbuf_getline(&sb, stdin) != EOF) { const char *s; const char *help; struct option *o; @@ -505,6 +505,7 @@ N_("git rev-parse --parseopt [<options>] -- [<args>...]\n" int cmd_rev_parse(int argc, const char **argv, const char *prefix) { int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0; + int did_repo_setup = 0; int has_dashdash = 0; int output_prefix = 0; unsigned char sha1[20]; @@ -528,11 +529,40 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } } - prefix = setup_git_directory(); - git_config(git_default_config, NULL); + /* No options; just report on whether we're in a git repo or not. */ + if (argc == 1) { + setup_git_directory(); + git_config(git_default_config, NULL); + return 0; + } + for (i = 1; i < argc; i++) { const char *arg = argv[i]; + if (!strcmp(arg, "--local-env-vars")) { + int i; + for (i = 0; local_repo_env[i]; i++) + printf("%s\n", local_repo_env[i]); + continue; + } + if (!strcmp(arg, "--resolve-git-dir")) { + const char *gitdir = argv[++i]; + if (!gitdir) + die("--resolve-git-dir requires an argument"); + gitdir = resolve_gitdir(gitdir); + if (!gitdir) + die("not a gitdir '%s'", argv[i]); + puts(gitdir); + continue; + } + + /* The rest of the options require a git repository. */ + if (!did_repo_setup) { + prefix = setup_git_directory(); + git_config(git_default_config, NULL); + did_repo_setup = 1; + } + if (!strcmp(arg, "--git-path")) { if (!argv[i + 1]) die("--git-path requires an argument"); @@ -706,12 +736,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) add_ref_exclusion(&ref_excludes, arg + 10); continue; } - if (!strcmp(arg, "--local-env-vars")) { - int i; - for (i = 0; local_repo_env[i]; i++) - printf("%s\n", local_repo_env[i]); - continue; - } if (!strcmp(arg, "--show-toplevel")) { const char *work_tree = get_git_work_tree(); if (work_tree) @@ -763,17 +787,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--git-common-dir")) { - puts(get_git_common_dir()); - continue; - } - if (!strcmp(arg, "--resolve-git-dir")) { - const char *gitdir = argv[++i]; - if (!gitdir) - die("--resolve-git-dir requires an argument"); - gitdir = resolve_gitdir(gitdir); - if (!gitdir) - die("not a gitdir '%s'", argv[i]); - puts(gitdir); + const char *pfx = prefix ? prefix : ""; + puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir())); continue; } if (!strcmp(arg, "--is-inside-git-dir")) { diff --git a/builtin/rm.c b/builtin/rm.c index 80b972f92f..8829b09d0b 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -211,7 +211,7 @@ static int check_local_mod(unsigned char *head, int index_only) * "intent to add" entry. */ if (local_changes && staged_changes) { - if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD)) + if (!index_only || !ce_intent_to_add(ce)) string_list_append(&files_staged, name); } else if (!index_only) { diff --git a/builtin/send-pack.c b/builtin/send-pack.c index f6e5d643c1..1ff5a67538 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -212,7 +212,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) argv_array_push(&all_refspecs, buf); } else { struct strbuf line = STRBUF_INIT; - while (strbuf_getline(&line, stdin, '\n') != EOF) + while (strbuf_getline(&line, stdin) != EOF) argv_array_push(&all_refspecs, line.buf); strbuf_release(&line); } @@ -225,7 +225,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) * --all and --mirror are incompatible; neither makes sense * with any refspecs. */ - if ((refspecs && (send_all || args.send_mirror)) || + if ((nr_refspecs > 0 && (send_all || args.send_mirror)) || (send_all && args.send_mirror)) usage_with_options(send_pack_usage, options); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 35ebd17f80..bfc082e584 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -14,7 +14,26 @@ static char const * const shortlog_usage[] = { NULL }; -static int compare_by_number(const void *a1, const void *a2) +/* + * The util field of our string_list_items will contain one of two things: + * + * - if --summary is not in use, it will point to a string list of the + * oneline subjects assigned to this author + * + * - if --summary is in use, we don't need that list; we only need to know + * its size. So we abuse the pointer slot to store our integer counter. + * + * This macro accesses the latter. + */ +#define UTIL_TO_INT(x) ((intptr_t)(x)->util) + +static int compare_by_counter(const void *a1, const void *a2) +{ + const struct string_list_item *i1 = a1, *i2 = a2; + return UTIL_TO_INT(i2) - UTIL_TO_INT(i1); +} + +static int compare_by_list(const void *a1, const void *a2) { const struct string_list_item *i1 = a1, *i2 = a2; const struct string_list *l1 = i1->util, *l2 = i2->util; @@ -31,13 +50,9 @@ static void insert_one_record(struct shortlog *log, const char *author, const char *oneline) { - const char *dot3 = log->common_repo_prefix; - char *buffer, *p; struct string_list_item *item; const char *mailbuf, *namebuf; size_t namelen, maillen; - const char *eol; - struct strbuf subject = STRBUF_INIT; struct strbuf namemailbuf = STRBUF_INIT; struct ident_split ident; @@ -56,98 +71,95 @@ static void insert_one_record(struct shortlog *log, strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf); item = string_list_insert(&log->list, namemailbuf.buf); - if (item->util == NULL) - item->util = xcalloc(1, sizeof(struct string_list)); - - /* Skip any leading whitespace, including any blank lines. */ - while (*oneline && isspace(*oneline)) - oneline++; - eol = strchr(oneline, '\n'); - if (!eol) - eol = oneline + strlen(oneline); - if (starts_with(oneline, "[PATCH")) { - char *eob = strchr(oneline, ']'); - if (eob && (!eol || eob < eol)) - oneline = eob + 1; - } - while (*oneline && isspace(*oneline) && *oneline != '\n') - oneline++; - format_subject(&subject, oneline, " "); - buffer = strbuf_detach(&subject, NULL); - - if (dot3) { - int dot3len = strlen(dot3); - if (dot3len > 5) { - while ((p = strstr(buffer, dot3)) != NULL) { - int taillen = strlen(p) - dot3len; - memcpy(p, "/.../", 5); - memmove(p + 5, p + dot3len, taillen + 1); + + if (log->summary) + item->util = (void *)(UTIL_TO_INT(item) + 1); + else { + const char *dot3 = log->common_repo_prefix; + char *buffer, *p; + struct strbuf subject = STRBUF_INIT; + const char *eol; + + /* Skip any leading whitespace, including any blank lines. */ + while (*oneline && isspace(*oneline)) + oneline++; + eol = strchr(oneline, '\n'); + if (!eol) + eol = oneline + strlen(oneline); + if (starts_with(oneline, "[PATCH")) { + char *eob = strchr(oneline, ']'); + if (eob && (!eol || eob < eol)) + oneline = eob + 1; + } + while (*oneline && isspace(*oneline) && *oneline != '\n') + oneline++; + format_subject(&subject, oneline, " "); + buffer = strbuf_detach(&subject, NULL); + + if (dot3) { + int dot3len = strlen(dot3); + if (dot3len > 5) { + while ((p = strstr(buffer, dot3)) != NULL) { + int taillen = strlen(p) - dot3len; + memcpy(p, "/.../", 5); + memmove(p + 5, p + dot3len, taillen + 1); + } } } - } - string_list_append(item->util, buffer); + if (item->util == NULL) + item->util = xcalloc(1, sizeof(struct string_list)); + string_list_append(item->util, buffer); + } } static void read_from_stdin(struct shortlog *log) { - char author[1024], oneline[1024]; + struct strbuf author = STRBUF_INIT; + struct strbuf oneline = STRBUF_INIT; - while (fgets(author, sizeof(author), stdin) != NULL) { - if (!(author[0] == 'A' || author[0] == 'a') || - !starts_with(author + 1, "uthor: ")) + while (strbuf_getline_lf(&author, stdin) != EOF) { + const char *v; + if (!skip_prefix(author.buf, "Author: ", &v) && + !skip_prefix(author.buf, "author ", &v)) continue; - while (fgets(oneline, sizeof(oneline), stdin) && - oneline[0] != '\n') + while (strbuf_getline_lf(&oneline, stdin) != EOF && + oneline.len) ; /* discard headers */ - while (fgets(oneline, sizeof(oneline), stdin) && - oneline[0] == '\n') + while (strbuf_getline_lf(&oneline, stdin) != EOF && + !oneline.len) ; /* discard blanks */ - insert_one_record(log, author + 8, oneline); + insert_one_record(log, v, oneline.buf); } + strbuf_release(&author); + strbuf_release(&oneline); } void shortlog_add_commit(struct shortlog *log, struct commit *commit) { - const char *author = NULL, *buffer; - struct strbuf buf = STRBUF_INIT; - struct strbuf ufbuf = STRBUF_INIT; - - pp_commit_easy(CMIT_FMT_RAW, commit, &buf); - buffer = buf.buf; - while (*buffer && *buffer != '\n') { - const char *eol = strchr(buffer, '\n'); - - if (eol == NULL) - eol = buffer + strlen(buffer); + struct strbuf author = STRBUF_INIT; + struct strbuf oneline = STRBUF_INIT; + struct pretty_print_context ctx = {0}; + + ctx.fmt = CMIT_FMT_USERFORMAT; + ctx.abbrev = log->abbrev; + ctx.subject = ""; + ctx.after_subject = ""; + ctx.date_mode.type = DATE_NORMAL; + ctx.output_encoding = get_log_output_encoding(); + + format_commit_message(commit, "%an <%ae>", &author, &ctx); + if (!log->summary) { + if (log->user_format) + pretty_print_commit(&ctx, commit, &oneline); else - eol++; - - if (starts_with(buffer, "author ")) - author = buffer + 7; - buffer = eol; - } - if (!author) { - warning(_("Missing author: %s"), - oid_to_hex(&commit->object.oid)); - return; + format_commit_message(commit, "%s", &oneline, &ctx); } - if (log->user_format) { - struct pretty_print_context ctx = {0}; - ctx.fmt = CMIT_FMT_USERFORMAT; - ctx.abbrev = log->abbrev; - ctx.subject = ""; - ctx.after_subject = ""; - ctx.date_mode.type = DATE_NORMAL; - ctx.output_encoding = get_log_output_encoding(); - pretty_print_commit(&ctx, commit, &ufbuf); - buffer = ufbuf.buf; - } else if (*buffer) { - buffer++; - } - insert_one_record(log, author, !*buffer ? "<none>" : buffer); - strbuf_release(&ufbuf); - strbuf_release(&buf); + + insert_one_record(log, author.buf, oneline.len ? oneline.buf : "<none>"); + + strbuf_release(&author); + strbuf_release(&oneline); } static void get_from_rev(struct rev_info *rev, struct shortlog *log) @@ -294,14 +306,14 @@ void shortlog_output(struct shortlog *log) if (log->sort_by_number) qsort(log->list.items, log->list.nr, sizeof(struct string_list_item), - compare_by_number); + log->summary ? compare_by_counter : compare_by_list); for (i = 0; i < log->list.nr; i++) { - struct string_list *onelines = log->list.items[i].util; - + const struct string_list_item *item = &log->list.items[i]; if (log->summary) { - printf("%6d\t%s\n", onelines->nr, log->list.items[i].string); + printf("%6d\t%s\n", (int)UTIL_TO_INT(item), item->string); } else { - printf("%s (%d):\n", log->list.items[i].string, onelines->nr); + struct string_list *onelines = item->util; + printf("%s (%d):\n", item->string, onelines->nr); for (j = onelines->nr - 1; j >= 0; j--) { const char *msg = onelines->items[j].string; @@ -314,11 +326,11 @@ void shortlog_output(struct shortlog *log) printf(" %s\n", msg); } putchar('\n'); + onelines->strdup_strings = 1; + string_list_clear(onelines, 0); + free(onelines); } - onelines->strdup_strings = 1; - string_list_clear(onelines, 0); - free(onelines); log->list.items[i].util = NULL; } diff --git a/builtin/stripspace.c b/builtin/stripspace.c index 7ff8434f7c..15e716ef43 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -35,7 +35,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) N_("skip and remove all lines starting with comment character"), STRIP_COMMENTS), OPT_CMDMODE('c', "comment-lines", &mode, - N_("prepend comment character and blank to each line"), + N_("prepend comment character and space to each line"), COMMENT_LINES), OPT_END() }; diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f4c3eff179..3bd6883eff 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -22,17 +22,12 @@ static int module_list_compute(int argc, const char **argv, struct module_list *list) { int i, result = 0; - char *max_prefix, *ps_matched = NULL; - int max_prefix_len; + char *ps_matched = NULL; parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, prefix, argv); - /* Find common prefix for all pathspec's */ - max_prefix = common_prefix(pathspec); - max_prefix_len = max_prefix ? strlen(max_prefix) : 0; - if (pathspec->nr) ps_matched = xcalloc(pathspec->nr, 1); @@ -42,9 +37,9 @@ static int module_list_compute(int argc, const char **argv, for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; - if (!S_ISGITLINK(ce->ce_mode) || - !match_pathspec(pathspec, ce->name, ce_namelen(ce), - max_prefix_len, ps_matched, 1)) + if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), + 0, ps_matched, 1) || + !S_ISGITLINK(ce->ce_mode)) continue; ALLOC_GROW(list->entries, list->nr + 1, list->alloc); @@ -57,7 +52,6 @@ static int module_list_compute(int argc, const char **argv, */ i++; } - free(max_prefix); if (ps_matched && report_path_error(ps_matched, pathspec, prefix)) result = -1; @@ -124,6 +118,55 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } + +/* + * Rules to sanitize configuration variables that are Ok to be passed into + * submodule operations from the parent project using "-c". Should only + * include keys which are both (a) safe and (b) necessary for proper + * operation. + */ +static int submodule_config_ok(const char *var) +{ + if (starts_with(var, "credential.")) + return 1; + return 0; +} + +static int sanitize_submodule_config(const char *var, const char *value, void *data) +{ + struct strbuf *out = data; + + if (submodule_config_ok(var)) { + if (out->len) + strbuf_addch(out, ' '); + + if (value) + sq_quotef(out, "%s=%s", var, value); + else + sq_quote_buf(out, var); + } + + return 0; +} + +static void prepare_submodule_repo_env(struct argv_array *out) +{ + const char * const *var; + + for (var = local_repo_env; *var; var++) { + if (!strcmp(*var, CONFIG_DATA_ENVIRONMENT)) { + struct strbuf sanitized_config = STRBUF_INIT; + git_config_from_parameters(sanitize_submodule_config, + &sanitized_config); + argv_array_pushf(out, "%s=%s", *var, sanitized_config.buf); + strbuf_release(&sanitized_config); + } else { + argv_array_push(out, *var); + } + } + +} + static int clone_submodule(const char *path, const char *gitdir, const char *url, const char *depth, const char *reference, int quiet) { @@ -145,7 +188,7 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url argv_array_push(&cp.args, path); cp.git_cmd = 1; - cp.env = local_repo_env; + prepare_submodule_repo_env(&cp.env_array); cp.no_stdin = 1; return run_command(&cp); @@ -153,11 +196,11 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url static int module_clone(int argc, const char **argv, const char *prefix) { - const char *path = NULL, *name = NULL, *url = NULL; + const char *name = NULL, *url = NULL; const char *reference = NULL, *depth = NULL; int quiet = 0; FILE *submodule_dot_git; - char *sm_gitdir, *cwd, *p; + char *p, *path = NULL, *sm_gitdir; struct strbuf rel_path = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; @@ -186,16 +229,27 @@ static int module_clone(int argc, const char **argv, const char *prefix) const char *const git_submodule_helper_usage[] = { N_("git submodule--helper clone [--prefix=<path>] [--quiet] " - "[--reference <repository>] [--name <name>] [--url <url>]" - "[--depth <depth>] [--] [<path>...]"), + "[--reference <repository>] [--name <name>] [--depth <depth>] " + "--url <url> --path <path>"), NULL }; argc = parse_options(argc, argv, prefix, module_clone_options, git_submodule_helper_usage, 0); + if (argc || !url || !path || !*path) + usage_with_options(git_submodule_helper_usage, + module_clone_options); + strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name); - sm_gitdir = strbuf_detach(&sb, NULL); + sm_gitdir = xstrdup(absolute_path(sb.buf)); + strbuf_reset(&sb); + + if (!is_absolute_path(path)) { + strbuf_addf(&sb, "%s/%s", get_git_work_tree(), path); + path = strbuf_detach(&sb, NULL); + } else + path = xstrdup(path); if (!file_exists(sm_gitdir)) { if (safe_create_leading_directories_const(sm_gitdir) < 0) @@ -212,49 +266,301 @@ static int module_clone(int argc, const char **argv, const char *prefix) } /* Write a .git file in the submodule to redirect to the superproject. */ - if (safe_create_leading_directories_const(path) < 0) - die(_("could not create directory '%s'"), path); - - if (path && *path) - strbuf_addf(&sb, "%s/.git", path); - else - strbuf_addstr(&sb, ".git"); - + strbuf_addf(&sb, "%s/.git", path); if (safe_create_leading_directories_const(sb.buf) < 0) die(_("could not create leading directories of '%s'"), sb.buf); submodule_dot_git = fopen(sb.buf, "w"); if (!submodule_dot_git) die_errno(_("cannot open file '%s'"), sb.buf); - fprintf(submodule_dot_git, "gitdir: %s\n", - relative_path(sm_gitdir, path, &rel_path)); + fprintf_or_die(submodule_dot_git, "gitdir: %s\n", + relative_path(sm_gitdir, path, &rel_path)); if (fclose(submodule_dot_git)) die(_("could not close file %s"), sb.buf); strbuf_reset(&sb); strbuf_reset(&rel_path); - cwd = xgetcwd(); /* Redirect the worktree of the submodule in the superproject's config */ - if (!is_absolute_path(sm_gitdir)) { - strbuf_addf(&sb, "%s/%s", cwd, sm_gitdir); - free(sm_gitdir); - sm_gitdir = strbuf_detach(&sb, NULL); - } - - strbuf_addf(&sb, "%s/%s", cwd, path); p = git_pathdup_submodule(path, "config"); if (!p) die(_("could not get submodule directory for '%s'"), path); git_config_set_in_file(p, "core.worktree", - relative_path(sb.buf, sm_gitdir, &rel_path)); + relative_path(path, sm_gitdir, &rel_path)); strbuf_release(&sb); strbuf_release(&rel_path); free(sm_gitdir); - free(cwd); + free(path); free(p); return 0; } +static int module_sanitize_config(int argc, const char **argv, const char *prefix) +{ + struct strbuf sanitized_config = STRBUF_INIT; + + if (argc > 1) + usage(_("git submodule--helper sanitize-config")); + + git_config_from_parameters(sanitize_submodule_config, &sanitized_config); + if (sanitized_config.len) + printf("%s\n", sanitized_config.buf); + + strbuf_release(&sanitized_config); + + return 0; +} + +struct submodule_update_clone { + /* index into 'list', the list of submodules to look into for cloning */ + int current; + struct module_list list; + unsigned warn_if_uninitialized : 1; + + /* update parameter passed via commandline */ + struct submodule_update_strategy update; + + /* configuration parameters which are passed on to the children */ + int quiet; + const char *reference; + const char *depth; + const char *recursive_prefix; + const char *prefix; + + /* Machine-readable status lines to be consumed by git-submodule.sh */ + struct string_list projectlines; + + /* If we want to stop as fast as possible and return an error */ + unsigned quickstop : 1; +}; +#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \ + SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \ + STRING_LIST_INIT_DUP, 0} + +/** + * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to + * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise. + */ +static int prepare_to_clone_next_submodule(const struct cache_entry *ce, + struct child_process *child, + struct submodule_update_clone *suc, + struct strbuf *out) +{ + const struct submodule *sub = NULL; + struct strbuf displaypath_sb = STRBUF_INIT; + struct strbuf sb = STRBUF_INIT; + const char *displaypath = NULL; + char *url = NULL; + int needs_cloning = 0; + + if (ce_stage(ce)) { + if (suc->recursive_prefix) + strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name); + else + strbuf_addf(&sb, "%s", ce->name); + strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf); + strbuf_addch(out, '\n'); + goto cleanup; + } + + sub = submodule_from_path(null_sha1, ce->name); + + if (suc->recursive_prefix) + displaypath = relative_path(suc->recursive_prefix, + ce->name, &displaypath_sb); + else + displaypath = ce->name; + + if (suc->update.type == SM_UPDATE_NONE + || (suc->update.type == SM_UPDATE_UNSPECIFIED + && sub->update_strategy.type == SM_UPDATE_NONE)) { + strbuf_addf(out, _("Skipping submodule '%s'"), displaypath); + strbuf_addch(out, '\n'); + goto cleanup; + } + + /* + * Looking up the url in .git/config. + * We must not fall back to .gitmodules as we only want + * to process configured submodules. + */ + strbuf_reset(&sb); + strbuf_addf(&sb, "submodule.%s.url", sub->name); + git_config_get_string(sb.buf, &url); + if (!url) { + /* + * Only mention uninitialized submodules when their + * path have been specified + */ + if (suc->warn_if_uninitialized) { + strbuf_addf(out, + _("Submodule path '%s' not initialized"), + displaypath); + strbuf_addch(out, '\n'); + strbuf_addstr(out, + _("Maybe you want to use 'update --init'?")); + strbuf_addch(out, '\n'); + } + goto cleanup; + } + + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/.git", ce->name); + needs_cloning = !file_exists(sb.buf); + + strbuf_reset(&sb); + strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode, + sha1_to_hex(ce->sha1), ce_stage(ce), + needs_cloning, ce->name); + string_list_append(&suc->projectlines, sb.buf); + + if (!needs_cloning) + goto cleanup; + + child->git_cmd = 1; + child->no_stdin = 1; + child->stdout_to_stderr = 1; + child->err = -1; + argv_array_push(&child->args, "submodule--helper"); + argv_array_push(&child->args, "clone"); + if (suc->quiet) + argv_array_push(&child->args, "--quiet"); + if (suc->prefix) + argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL); + argv_array_pushl(&child->args, "--path", sub->path, NULL); + argv_array_pushl(&child->args, "--name", sub->name, NULL); + argv_array_pushl(&child->args, "--url", url, NULL); + if (suc->reference) + argv_array_push(&child->args, suc->reference); + if (suc->depth) + argv_array_push(&child->args, suc->depth); + +cleanup: + free(url); + strbuf_reset(&displaypath_sb); + strbuf_reset(&sb); + + return needs_cloning; +} + +static int update_clone_get_next_task(struct child_process *child, + struct strbuf *err, + void *suc_cb, + void **void_task_cb) +{ + struct submodule_update_clone *suc = suc_cb; + + for (; suc->current < suc->list.nr; suc->current++) { + const struct cache_entry *ce = suc->list.entries[suc->current]; + if (prepare_to_clone_next_submodule(ce, child, suc, err)) { + suc->current++; + return 1; + } + } + return 0; +} + +static int update_clone_start_failure(struct strbuf *err, + void *suc_cb, + void *void_task_cb) +{ + struct submodule_update_clone *suc = suc_cb; + suc->quickstop = 1; + return 1; +} + +static int update_clone_task_finished(int result, + struct strbuf *err, + void *suc_cb, + void *void_task_cb) +{ + struct submodule_update_clone *suc = suc_cb; + + if (!result) + return 0; + + suc->quickstop = 1; + return 1; +} + +static int update_clone(int argc, const char **argv, const char *prefix) +{ + const char *update = NULL; + int max_jobs = -1; + struct string_list_item *item; + struct pathspec pathspec; + struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT; + + struct option module_update_clone_options[] = { + OPT_STRING(0, "prefix", &prefix, + N_("path"), + N_("path into the working tree")), + OPT_STRING(0, "recursive-prefix", &suc.recursive_prefix, + N_("path"), + N_("path into the working tree, across nested " + "submodule boundaries")), + OPT_STRING(0, "update", &update, + N_("string"), + N_("rebase, merge, checkout or none")), + OPT_STRING(0, "reference", &suc.reference, N_("repo"), + N_("reference repository")), + OPT_STRING(0, "depth", &suc.depth, "<depth>", + N_("Create a shallow clone truncated to the " + "specified number of revisions")), + OPT_INTEGER('j', "jobs", &max_jobs, + N_("parallel jobs")), + OPT__QUIET(&suc.quiet, N_("don't print cloning progress")), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"), + NULL + }; + suc.prefix = prefix; + + argc = parse_options(argc, argv, prefix, module_update_clone_options, + git_submodule_helper_usage, 0); + + if (update) + if (parse_submodule_update_strategy(update, &suc.update) < 0) + die(_("bad value for update parameter")); + + if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0) + return 1; + + if (pathspec.nr) + suc.warn_if_uninitialized = 1; + + /* Overlay the parsed .gitmodules file with .git/config */ + gitmodules_config(); + git_config(submodule_config, NULL); + + if (max_jobs < 0) + max_jobs = parallel_submodules(); + + run_processes_parallel(max_jobs, + update_clone_get_next_task, + update_clone_start_failure, + update_clone_task_finished, + &suc); + + /* + * We saved the output and put it out all at once now. + * That means: + * - the listener does not have to interleave their (checkout) + * work with our fetching. The writes involved in a + * checkout involve more straightforward sequential I/O. + * - the listener can avoid doing any work if fetching failed. + */ + if (suc.quickstop) + return 1; + + for_each_string_list_item(item, &suc.projectlines) + utf8_fprintf(stdout, "%s", item->string); + + return 0; +} + struct cmd_struct { const char *cmd; int (*fn)(int, const char **, const char *); @@ -264,19 +570,21 @@ static struct cmd_struct commands[] = { {"list", module_list}, {"name", module_name}, {"clone", module_clone}, + {"sanitize-config", module_sanitize_config}, + {"update-clone", update_clone} }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) { int i; if (argc < 2) - die(_("fatal: submodule--helper subcommand must be " + die(_("submodule--helper subcommand must be " "called with a subcommand")); for (i = 0; i < ARRAY_SIZE(commands); i++) if (!strcmp(argv[1], commands[i].cmd)) return commands[i].fn(argc - 1, argv + 1, prefix); - die(_("fatal: '%s' is not a valid submodule--helper " + die(_("'%s' is not a valid submodule--helper " "subcommand"), argv[1]); } diff --git a/builtin/tag.c b/builtin/tag.c index 8db8c87e57..50e4ae5678 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -29,6 +29,7 @@ static const char * const git_tag_usage[] = { }; static unsigned int colopts; +static int force_sign_annotate; static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format) { @@ -44,11 +45,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con if (!format) { if (filter->lines) { to_free = xstrfmt("%s %%(contents:lines=%d)", - "%(align:15)%(refname:short)%(end)", + "%(align:15)%(refname:strip=2)%(end)", filter->lines); format = to_free; } else - format = "%(refname:short)"; + format = "%(refname:strip=2)"; } verify_ref_format(format); @@ -104,13 +105,7 @@ static int delete_tag(const char *name, const char *ref, static int verify_tag(const char *name, const char *ref, const unsigned char *sha1) { - const char *argv_verify_tag[] = {"verify-tag", - "-v", "SHA1_HEX", NULL}; - argv_verify_tag[2] = sha1_to_hex(sha1); - - if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD)) - return error(_("could not verify the tag '%s'"), name); - return 0; + return gpg_verify_tag(sha1, name, GPG_VERIFY_VERBOSE); } static int do_sign(struct strbuf *buffer) @@ -166,6 +161,11 @@ static int git_tag_config(const char *var, const char *value, void *cb) status = git_gpg_config(var, value, cb); if (status) return status; + if (!strcmp(var, "tag.forcesignannotated")) { + force_sign_annotate = git_config_bool(var, value); + return 0; + } + if (starts_with(var, "column.")) return git_column_config(var, value, "tag", &colopts); return git_default_config(var, value, cb); @@ -327,7 +327,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) char *cleanup_arg = NULL; int create_reflog = 0; int annotate = 0, force = 0; - int cmdmode = 0; + int cmdmode = 0, create_tag_object = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; struct ref_transaction *transaction; @@ -385,12 +385,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix) opt.sign = 1; set_signing_key(keyid); } - if (opt.sign) - annotate = 1; + create_tag_object = (opt.sign || annotate || msg.given || msgfile); + if (argc == 0 && !cmdmode) cmdmode = 'l'; - if ((annotate || msg.given || msgfile || force) && (cmdmode != 0)) + if ((create_tag_object || force) && (cmdmode != 0)) usage_with_options(git_tag_usage, options); finalize_colopts(&colopts, -1); @@ -431,7 +431,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (msg.given || msgfile) { if (msg.given && msgfile) die(_("only one -F or -m option is allowed.")); - annotate = 1; if (msg.given) strbuf_addbuf(&buf, &(msg.buf)); else { @@ -474,8 +473,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix) else die(_("Invalid cleanup mode %s"), cleanup_arg); - if (annotate) + if (create_tag_object) { + if (force_sign_annotate && !annotate) + opt.sign = 1; create_tag(object, tag, &buf, &opt, prev, object); + } transaction = ref_transaction_begin(&err); if (!transaction || diff --git a/builtin/update-index.c b/builtin/update-index.c index 7431938fa6..1c94ca59bf 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -35,6 +35,15 @@ static int mark_skip_worktree_only; #define UNMARK_FLAG 2 static struct strbuf mtime_dir = STRBUF_INIT; +/* Untracked cache mode */ +enum uc_mode { + UC_UNSPECIFIED = -1, + UC_DISABLE = 0, + UC_ENABLE, + UC_TEST, + UC_FORCE +}; + __attribute__((format (printf, 1, 2))) static void report(const char *fmt, ...) { @@ -121,7 +130,7 @@ static int test_if_untracked_cache_is_supported(void) if (!mkdtemp(mtime_dir.buf)) die_errno("Could not make temporary directory"); - fprintf(stderr, _("Testing ")); + fprintf(stderr, _("Testing mtime in '%s' "), xgetcwd()); atexit(remove_test_directory); xstat_mtime_dir(&st); fill_stat_data(&base, &st); @@ -468,12 +477,14 @@ static void update_one(const char *path) report("add '%s'", path); } -static void read_index_info(int line_termination) +static void read_index_info(int nul_term_line) { struct strbuf buf = STRBUF_INIT; struct strbuf uq = STRBUF_INIT; + strbuf_getline_fn getline_fn; - while (strbuf_getline(&buf, stdin, line_termination) != EOF) { + getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; + while (getline_fn(&buf, stdin) != EOF) { char *ptr, *tab; char *path_name; unsigned char sha1[20]; @@ -522,7 +533,7 @@ static void read_index_info(int line_termination) goto bad_line; path_name = ptr; - if (line_termination && path_name[0] == '"') { + if (!nul_term_line && path_name[0] == '"') { strbuf_reset(&uq); if (unquote_c_style(&uq, path_name, NULL)) { die("git update-index: bad quoting of path name"); @@ -844,12 +855,12 @@ static int cacheinfo_callback(struct parse_opt_ctx_t *ctx, static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx, const struct option *opt, int unset) { - int *line_termination = opt->value; + int *nul_term_line = opt->value; if (ctx->argc != 1) return error("option '%s' must be the last argument", opt->long_name); allow_add = allow_replace = allow_remove = 1; - read_index_info(*line_termination); + read_index_info(*nul_term_line); return 0; } @@ -901,8 +912,8 @@ static int reupdate_callback(struct parse_opt_ctx_t *ctx, int cmd_update_index(int argc, const char **argv, const char *prefix) { - int newfd, entries, has_errors = 0, line_termination = '\n'; - int untracked_cache = -1; + int newfd, entries, has_errors = 0, nul_term_line = 0; + enum uc_mode untracked_cache = UC_UNSPECIFIED; int read_from_stdin = 0; int prefix_length = prefix ? strlen(prefix) : 0; int preferred_index_format = 0; @@ -912,6 +923,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) int split_index = -1; struct lock_file *lock_file; struct parse_opt_ctx_t ctx; + strbuf_getline_fn getline_fn; int parseopt_state = PARSE_OPT_UNKNOWN; struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, @@ -963,13 +975,13 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("add to index only; do not add content to object database"), 1), OPT_SET_INT(0, "force-remove", &force_remove, N_("remove named paths even if present in worktree"), 1), - OPT_SET_INT('z', NULL, &line_termination, - N_("with --stdin: input lines are terminated by null bytes"), '\0'), + OPT_BOOL('z', NULL, &nul_term_line, + N_("with --stdin: input lines are terminated by null bytes")), {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL, N_("read list of paths to be updated from standard input"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_callback}, - {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL, + {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &nul_term_line, NULL, N_("add entries from standard input to the index"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_cacheinfo_callback}, @@ -996,8 +1008,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("enable or disable split index")), OPT_BOOL(0, "untracked-cache", &untracked_cache, N_("enable/disable untracked cache")), + OPT_SET_INT(0, "test-untracked-cache", &untracked_cache, + N_("test if the filesystem supports untracked cache"), UC_TEST), OPT_SET_INT(0, "force-untracked-cache", &untracked_cache, - N_("enable untracked cache without testing the filesystem"), 2), + N_("enable untracked cache without testing the filesystem"), UC_FORCE), OPT_END() }; @@ -1057,6 +1071,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) } } argc = parse_options_end(&ctx); + + getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; if (preferred_index_format) { if (preferred_index_format < INDEX_FORMAT_LB || INDEX_FORMAT_UB < preferred_index_format) @@ -1070,16 +1086,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) } if (read_from_stdin) { - struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; setup_work_tree(); - while (strbuf_getline(&buf, stdin, line_termination) != EOF) { + while (getline_fn(&buf, stdin) != EOF) { char *p; - if (line_termination && buf.buf[0] == '"') { - strbuf_reset(&nbuf); - if (unquote_c_style(&nbuf, buf.buf, NULL)) + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) die("line is badly quoted"); - strbuf_swap(&buf, &nbuf); + strbuf_swap(&buf, &unquoted); } p = prefix_path(prefix, prefix_length, buf.buf); update_one(p); @@ -1087,7 +1104,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) chmod_path(set_executable_bit, p); free(p); } - strbuf_release(&nbuf); + strbuf_release(&unquoted); strbuf_release(&buf); } @@ -1104,27 +1121,32 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) the_index.split_index = NULL; the_index.cache_changed |= SOMETHING_CHANGED; } - if (untracked_cache > 0) { - struct untracked_cache *uc; - if (untracked_cache < 2) { - setup_work_tree(); - if (!test_if_untracked_cache_is_supported()) - return 1; - } - if (!the_index.untracked) { - 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; - the_index.untracked = uc; - } - add_untracked_ident(the_index.untracked); - the_index.cache_changed |= UNTRACKED_CHANGED; - } else if (!untracked_cache && the_index.untracked) { - the_index.untracked = NULL; - the_index.cache_changed |= UNTRACKED_CHANGED; + switch (untracked_cache) { + case UC_UNSPECIFIED: + break; + case UC_DISABLE: + if (git_config_get_untracked_cache() == 1) + warning("core.untrackedCache is set to true; " + "remove or change it, if you really want to " + "disable the untracked cache"); + remove_untracked_cache(&the_index); + report(_("Untracked cache disabled")); + break; + case UC_TEST: + setup_work_tree(); + return !test_if_untracked_cache_is_supported(); + case UC_ENABLE: + case UC_FORCE: + if (git_config_get_untracked_cache() == 0) + warning("core.untrackedCache is set to false; " + "remove or change it, if you really want to " + "enable the untracked cache"); + add_untracked_cache(&the_index); + report(_("Untracked cache enabled for '%s'"), get_git_work_tree()); + break; + default: + die("Bug: bad untracked_cache value: %d", untracked_cache); } if (active_cache_changed) { diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 00663f6a30..99f8148cf7 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -18,55 +18,6 @@ static const char * const verify_tag_usage[] = { NULL }; -static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags) -{ - struct signature_check sigc; - int len; - int ret; - - memset(&sigc, 0, sizeof(sigc)); - - len = parse_signature(buf, size); - - if (size == len) { - if (flags & GPG_VERIFY_VERBOSE) - write_in_full(1, buf, len); - return error("no signature found"); - } - - ret = check_signature(buf, len, buf + len, size - len, &sigc); - print_signature_buffer(&sigc, flags); - - signature_check_clear(&sigc); - return ret; -} - -static int verify_tag(const char *name, unsigned flags) -{ - enum object_type type; - unsigned char sha1[20]; - char *buf; - unsigned long size; - int ret; - - if (get_sha1(name, sha1)) - return error("tag '%s' not found.", name); - - type = sha1_object_info(sha1, NULL); - if (type != OBJ_TAG) - return error("%s: cannot verify a non-tag object of type %s.", - name, typename(type)); - - buf = read_sha1_file(sha1, &type, &size); - if (!buf) - return error("%s: unable to read file.", name); - - ret = run_gpg_verify(buf, size, flags); - - free(buf); - return ret; -} - static int git_verify_tag_config(const char *var, const char *value, void *cb) { int status = git_gpg_config(var, value, cb); @@ -95,11 +46,13 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) if (verbose) flags |= GPG_VERIFY_VERBOSE; - /* sometimes the program was terminated because this signal - * was received in the process of writing the gpg input: */ - signal(SIGPIPE, SIG_IGN); - while (i < argc) - if (verify_tag(argv[i++], flags)) + while (i < argc) { + unsigned char sha1[20]; + const char *name = argv[i++]; + if (get_sha1(name, sha1)) + had_error = !!error("tag '%s' not found.", name); + else if (gpg_verify_tag(sha1, name, flags)) had_error = 1; + } return had_error; } diff --git a/builtin/worktree.c b/builtin/worktree.c index 475b9581a5..d8e3795dc4 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -21,6 +21,7 @@ static const char * const worktree_usage[] = { struct add_opts { int force; int detach; + int checkout; const char *new_branch; int force_new_branch; }; @@ -52,7 +53,7 @@ static int prune_worktree(const char *id, struct strbuf *reason) 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,9 +202,7 @@ static int add_worktree(const char *path, const char *refname, 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); @@ -286,18 +285,22 @@ static int add_worktree(const char *path, const char *refname, if (ret) goto done; - cp.argv = NULL; - argv_array_clear(&cp.args); - argv_array_pushl(&cp.args, "reset", "--hard", NULL); - cp.env = child_env.argv; - ret = run_command(&cp); - if (!ret) { - is_junk = 0; - free(junk_work_tree); - free(junk_git_dir); - junk_work_tree = NULL; - junk_git_dir = NULL; + if (opts->checkout) { + cp.argv = NULL; + argv_array_clear(&cp.args); + argv_array_pushl(&cp.args, "reset", "--hard", NULL); + cp.env = child_env.argv; + ret = run_command(&cp); + if (ret) + goto done; } + + is_junk = 0; + free(junk_work_tree); + free(junk_git_dir); + junk_work_tree = NULL; + junk_git_dir = NULL; + done: strbuf_reset(&sb); strbuf_addf(&sb, "%s/locked", sb_repo.buf); @@ -322,10 +325,12 @@ static int add(int ac, const char **av, const char *prefix) OPT_STRING('B', NULL, &new_branch_force, N_("branch"), N_("create or reset a branch")), OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")), + OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")), OPT_END() }; memset(&opts, 0, sizeof(opts)); + opts.checkout = 1; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1) die(_("-b, -B, and --detach are mutually exclusive")); @@ -336,9 +341,18 @@ static int add(int ac, const char **av, const char *prefix) 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); |