diff options
Diffstat (limited to 'builtin')
61 files changed, 2400 insertions, 902 deletions
diff --git a/builtin/add.c b/builtin/add.c index 9ef7fb02d5..8a155dd41e 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -9,7 +9,7 @@ #include "lockfile.h" #include "dir.h" #include "pathspec.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "cache-tree.h" #include "run-command.h" #include "parse-options.h" @@ -265,8 +265,6 @@ static int edit_patch(int argc, const char **argv, const char *prefix) return 0; } -static struct lock_file lock_file; - static const char ignore_error[] = N_("The following paths are ignored by one of your .gitignore files:\n"); @@ -393,6 +391,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) int add_new_files; int require_pathspec; char *seen = NULL; + struct lock_file lock_file = LOCK_INIT; git_config(add_config, NULL); diff --git a/builtin/am.c b/builtin/am.c index 9c82603f70..6273ea5195 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -6,7 +6,7 @@ #include "cache.h" #include "config.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "dir.h" #include "run-command.h" @@ -403,11 +403,11 @@ static void am_load(struct am_state *state) struct strbuf sb = STRBUF_INIT; if (read_state_file(&sb, state, "next", 1) < 0) - die("BUG: state file 'next' does not exist"); + BUG("state file 'next' does not exist"); state->cur = strtol(sb.buf, NULL, 10); if (read_state_file(&sb, state, "last", 1) < 0) - die("BUG: state file 'last' does not exist"); + BUG("state file 'last' does not exist"); state->last = strtol(sb.buf, NULL, 10); if (read_author_script(state) < 0) @@ -986,7 +986,7 @@ static int split_mail(struct am_state *state, enum patch_format patch_format, case PATCH_FORMAT_MBOXRD: return split_mail_mbox(state, paths, keep_cr, 1); default: - die("BUG: invalid patch_format"); + BUG("invalid patch_format"); } return -1; } @@ -1041,7 +1041,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, str = "b"; break; default: - die("BUG: invalid value for state->keep"); + BUG("invalid value for state->keep"); } write_state_text(state, "keep", str); @@ -1058,7 +1058,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, str = "t"; break; default: - die("BUG: invalid value for state->scissors"); + BUG("invalid value for state->scissors"); } write_state_text(state, "scissors", str); @@ -1216,7 +1216,7 @@ static int parse_mail(struct am_state *state, const char *mail) mi.keep_non_patch_brackets_in_subject = 1; break; default: - die("BUG: invalid value for state->keep"); + BUG("invalid value for state->keep"); } if (state->message_id) @@ -1232,7 +1232,7 @@ static int parse_mail(struct am_state *state, const char *mail) mi.use_scissors = 1; break; default: - die("BUG: invalid value for state->scissors"); + BUG("invalid value for state->scissors"); } mi.input = xfopen(mail, "r"); @@ -1463,7 +1463,7 @@ static int run_apply(const struct am_state *state, const char *index_file) int options = 0; if (init_apply_state(&apply_state, NULL)) - die("BUG: init_apply_state() failed"); + BUG("init_apply_state() failed"); argv_array_push(&apply_opts, "apply"); argv_array_pushv(&apply_opts, state->git_apply_opts.argv); @@ -1489,7 +1489,7 @@ static int run_apply(const struct am_state *state, const char *index_file) apply_state.apply_verbosity = verbosity_silent; if (check_apply_state(&apply_state, force_apply)) - die("BUG: check_apply_state() failed"); + BUG("check_apply_state() failed"); argv_array_push(&apply_paths, am_path(state, "patch")); @@ -1542,7 +1542,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa char *their_tree_name; if (get_oid("HEAD", &our_tree) < 0) - hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&our_tree, the_hash_algo->empty_tree); if (build_fake_ancestor(state, index_path)) return error("could not build fake ancestor"); @@ -1827,15 +1827,11 @@ static void am_run(struct am_state *state, int resume) } if (apply_status) { - int advice_amworkdir = 1; - printf_ln(_("Patch failed at %s %.*s"), msgnum(state), linelen(state->msg), state->msg); - git_config_get_bool("advice.amworkdir", &advice_amworkdir); - if (advice_amworkdir) - printf_ln(_("Use 'git am --show-current-patch' to see the failed patch")); + advise(_("Use 'git am --show-current-patch' to see the failed patch")); die_user_resolve(state); } @@ -2042,7 +2038,7 @@ static void am_skip(struct am_state *state) am_rerere_clear(); if (get_oid("HEAD", &head)) - hashcpy(head.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&head, the_hash_algo->empty_tree); if (clean_index(&head, &head)) die(_("failed to clean index")); @@ -2105,11 +2101,11 @@ static void am_abort(struct am_state *state) curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL); has_curr_head = curr_branch && !is_null_oid(&curr_head); if (!has_curr_head) - hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&curr_head, the_hash_algo->empty_tree); has_orig_head = !get_oid("ORIG_HEAD", &orig_head); if (!has_orig_head) - hashcpy(orig_head.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&orig_head, the_hash_algo->empty_tree); clean_index(&curr_head, &orig_head); @@ -2231,12 +2227,12 @@ int cmd_am(int argc, const char **argv, const char *prefix) N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH), OPT_BOOL('m', "message-id", &state.message_id, N_("pass -m flag to git-mailinfo")), - { OPTION_SET_INT, 0, "keep-cr", &keep_cr, NULL, - N_("pass --keep-cr flag to git-mailsplit for mbox format"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1}, - { OPTION_SET_INT, 0, "no-keep-cr", &keep_cr, NULL, - N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0}, + OPT_SET_INT_F(0, "keep-cr", &keep_cr, + N_("pass --keep-cr flag to git-mailsplit for mbox format"), + 1, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "no-keep-cr", &keep_cr, + N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"), + 0, PARSE_OPT_NONEG), OPT_BOOL('c', "scissors", &state.scissors, N_("strip everything before a scissors line")), OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"), @@ -2407,7 +2403,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) ret = show_patch(&state); break; default: - die("BUG: invalid resume value"); + BUG("invalid resume value"); } am_state_release(&state); diff --git a/builtin/blame.c b/builtin/blame.c index 7a07bff242..921d127f29 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -7,6 +7,7 @@ #include "cache.h" #include "config.h" +#include "color.h" #include "builtin.h" #include "repository.h" #include "commit.h" @@ -25,6 +26,7 @@ #include "progress.h" #include "object-store.h" #include "blame.h" +#include "string-list.h" static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"); @@ -48,6 +50,8 @@ static int xdl_opts; static int abbrev = -1; static int no_whole_file_rename; static int show_progress; +static char repeated_meta_color[COLOR_MAXLEN]; +static int coloring_mode; static struct date_mode blame_date_mode = { DATE_ISO8601 }; static size_t blame_date_width; @@ -318,10 +322,12 @@ static const char *format_time(timestamp_t time, const char *tz_str, #define OUTPUT_PORCELAIN 010 #define OUTPUT_SHOW_NAME 020 #define OUTPUT_SHOW_NUMBER 040 -#define OUTPUT_SHOW_SCORE 0100 -#define OUTPUT_NO_AUTHOR 0200 +#define OUTPUT_SHOW_SCORE 0100 +#define OUTPUT_NO_AUTHOR 0200 #define OUTPUT_SHOW_EMAIL 0400 -#define OUTPUT_LINE_PORCELAIN 01000 +#define OUTPUT_LINE_PORCELAIN 01000 +#define OUTPUT_COLOR_LINE 02000 +#define OUTPUT_SHOW_AGE_WITH_COLOR 04000 static void emit_porcelain_details(struct blame_origin *suspect, int repeat) { @@ -369,6 +375,64 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, putchar('\n'); } +static struct color_field { + timestamp_t hop; + char col[COLOR_MAXLEN]; +} *colorfield; +static int colorfield_nr, colorfield_alloc; + +static void parse_color_fields(const char *s) +{ + struct string_list l = STRING_LIST_INIT_DUP; + struct string_list_item *item; + enum { EXPECT_DATE, EXPECT_COLOR } next = EXPECT_COLOR; + + colorfield_nr = 0; + + /* Ideally this would be stripped and split at the same time? */ + string_list_split(&l, s, ',', -1); + ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); + + for_each_string_list_item(item, &l) { + switch (next) { + case EXPECT_DATE: + colorfield[colorfield_nr].hop = approxidate(item->string); + next = EXPECT_COLOR; + colorfield_nr++; + ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); + break; + case EXPECT_COLOR: + if (color_parse(item->string, colorfield[colorfield_nr].col)) + die(_("expecting a color: %s"), item->string); + next = EXPECT_DATE; + break; + } + } + + if (next == EXPECT_COLOR) + die (_("must end with a color")); + + colorfield[colorfield_nr].hop = TIME_MAX; + string_list_clear(&l, 0); +} + +static void setup_default_color_by_age(void) +{ + parse_color_fields("blue,12 month ago,white,1 month ago,red"); +} + +static void determine_line_heat(struct blame_entry *ent, const char **dest_color) +{ + int i = 0; + struct commit_info ci; + get_commit_info(ent->suspect->commit, &ci, 1); + + while (i < colorfield_nr && ci.author_time > colorfield[i].hop) + i++; + + *dest_color = colorfield[i].col; +} + static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) { int cnt; @@ -377,15 +441,35 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int struct commit_info ci; char hex[GIT_MAX_HEXSZ + 1]; int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); + const char *default_color = NULL, *color = NULL, *reset = NULL; get_commit_info(suspect->commit, &ci, 1); oid_to_hex_r(hex, &suspect->commit->object.oid); cp = blame_nth_line(sb, ent->lno); + + if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) { + determine_line_heat(ent, &default_color); + color = default_color; + reset = GIT_COLOR_RESET; + } + for (cnt = 0; cnt < ent->num_lines; cnt++) { char ch; int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev; + if (opt & OUTPUT_COLOR_LINE) { + if (cnt > 0) { + color = repeated_meta_color; + reset = GIT_COLOR_RESET; + } else { + color = default_color ? default_color : NULL; + reset = default_color ? GIT_COLOR_RESET : NULL; + } + } + if (color) + fputs(color, stdout); + if (suspect->commit->object.flags & UNINTERESTING) { if (blank_boundary) memset(hex, ' ', length); @@ -435,6 +519,8 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int printf(" %*d) ", max_digits, ent->lno + 1 + cnt); } + if (reset) + fputs(reset, stdout); do { ch = *cp++; putchar(ch); @@ -459,7 +545,7 @@ static void output(struct blame_scoreboard *sb, int option) struct commit *commit = ent->suspect->commit; if (commit->object.flags & MORE_THAN_ONE_PATH) continue; - for (suspect = commit->util; suspect; suspect = suspect->next) { + for (suspect = get_blame_suspects(commit); suspect; suspect = suspect->next) { if (suspect->guilty && count++) { commit->object.flags |= MORE_THAN_ONE_PATH; break; @@ -609,6 +695,30 @@ static int git_blame_config(const char *var, const char *value, void *cb) parse_date_format(value, &blame_date_mode); return 0; } + if (!strcmp(var, "color.blame.repeatedlines")) { + if (color_parse_mem(value, strlen(value), repeated_meta_color)) + warning(_("invalid color '%s' in color.blame.repeatedLines"), + value); + return 0; + } + if (!strcmp(var, "color.blame.highlightrecent")) { + parse_color_fields(value); + return 0; + } + + if (!strcmp(var, "blame.coloring")) { + if (!strcmp(value, "repeatedLines")) { + coloring_mode |= OUTPUT_COLOR_LINE; + } else if (!strcmp(value, "highlightRecent")) { + coloring_mode |= OUTPUT_SHOW_AGE_WITH_COLOR; + } else if (!strcmp(value, "none")) { + coloring_mode &= ~(OUTPUT_COLOR_LINE | + OUTPUT_SHOW_AGE_WITH_COLOR); + } else { + warning(_("invalid value for blame.coloring")); + return 0; + } + } if (git_diff_heuristic_config(var, value, cb) < 0) return -1; @@ -692,6 +802,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR), OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL), OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE), + OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE), + OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR), /* * The following two options are parsed by parse_revision_opt() @@ -716,6 +828,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) unsigned int range_i; long anchor; + setup_default_color_by_age(); git_config(git_blame_config, &output_option); init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; @@ -951,8 +1064,17 @@ parse_done: blame_coalesce(&sb); - if (!(output_option & OUTPUT_PORCELAIN)) + if (!(output_option & (OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR))) + output_option |= coloring_mode; + + if (!(output_option & OUTPUT_PORCELAIN)) { find_alignment(&sb, &output_option); + if (!*repeated_meta_color && + (output_option & OUTPUT_COLOR_LINE)) + strcpy(repeated_meta_color, GIT_COLOR_CYAN); + } + if (output_option & OUTPUT_ANNOTATE_COMPAT) + output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR); output(&sb, output_option); free((void *)sb.final_buf); diff --git a/builtin/branch.c b/builtin/branch.c index 5bd2a0dd48..1876ca9e79 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -22,6 +22,7 @@ #include "wt-status.h" #include "ref-filter.h" #include "worktree.h" +#include "help.h" static const char * const builtin_branch_usage[] = { N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"), @@ -55,25 +56,19 @@ enum color_branch { BRANCH_COLOR_UPSTREAM = 5 }; +static const char *color_branch_slots[] = { + [BRANCH_COLOR_RESET] = "reset", + [BRANCH_COLOR_PLAIN] = "plain", + [BRANCH_COLOR_REMOTE] = "remote", + [BRANCH_COLOR_LOCAL] = "local", + [BRANCH_COLOR_CURRENT] = "current", + [BRANCH_COLOR_UPSTREAM] = "upstream", +}; + static struct string_list output = STRING_LIST_INIT_DUP; static unsigned int colopts; -static int parse_branch_color_slot(const char *slot) -{ - if (!strcasecmp(slot, "plain")) - return BRANCH_COLOR_PLAIN; - if (!strcasecmp(slot, "reset")) - return BRANCH_COLOR_RESET; - if (!strcasecmp(slot, "remote")) - return BRANCH_COLOR_REMOTE; - if (!strcasecmp(slot, "local")) - return BRANCH_COLOR_LOCAL; - if (!strcasecmp(slot, "current")) - return BRANCH_COLOR_CURRENT; - if (!strcasecmp(slot, "upstream")) - return BRANCH_COLOR_UPSTREAM; - return -1; -} +define_list_config_array(color_branch_slots); static int git_branch_config(const char *var, const char *value, void *cb) { @@ -86,7 +81,7 @@ static int git_branch_config(const char *var, const char *value, void *cb) return 0; } if (skip_prefix(var, "color.branch.", &slot_name)) { - int slot = parse_branch_color_slot(slot_name); + int slot = LOOKUP_CONFIG(color_branch_slots, slot_name); if (slot < 0) return 0; if (!value) @@ -391,7 +386,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin struct ref_array array; int maxwidth = 0; const char *remote_prefix = ""; - struct strbuf out = STRBUF_INIT; char *to_free = NULL; /* @@ -419,7 +413,10 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin ref_array_sort(sorting, &array); for (i = 0; i < array.nr; i++) { - format_ref_array_item(array.items[i], format, &out); + struct strbuf out = STRBUF_INIT; + struct strbuf err = STRBUF_INIT; + if (format_ref_array_item(array.items[i], format, &out, &err)) + die("%s", err.buf); if (column_active(colopts)) { assert(!filter->verbose && "--column and --verbose are incompatible"); /* format to a string_list to let print_columns() do its job */ @@ -428,6 +425,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin fwrite(out.buf, 1, out.len, stdout); putchar('\n'); } + strbuf_release(&err); strbuf_release(&out); } @@ -497,7 +495,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) || !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) { - die("BUG: expected prefix missing for refs"); + BUG("expected prefix missing for refs"); } if (copy) @@ -589,8 +587,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT__QUIET(&quiet, N_("suppress informational messages")), OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"), BRANCH_TRACK_EXPLICIT), - { OPTION_SET_INT, 0, "set-upstream", &track, NULL, N_("do not use"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, BRANCH_TRACK_OVERRIDE }, + OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"), + BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN), OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")), OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")), OPT__COLOR(&branch_use_color, N_("use colored output")), @@ -698,7 +696,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) * If no sorting parameter is given then we default to sorting * by 'refname'. This would give us an alphabetically sorted * array with the 'HEAD' ref at the beginning followed by - * local branches 'refs/heads/...' and finally remote-tacking + * local branches 'refs/heads/...' and finally remote-tracking * branches 'refs/remotes/...'. */ if (!sorting) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 91e7764243..4a44b2404f 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -313,7 +313,7 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d die("could not convert '%s' %s", oid_to_hex(oid), data->rest); } else - die("BUG: invalid cmdmode: %c", opt->cmdmode); + BUG("invalid cmdmode: %c", opt->cmdmode); batch_write(opt, contents, size); free(contents); } else if (stream_blob_to_fd(1, oid, NULL, 0) < 0) @@ -388,7 +388,7 @@ static void batch_one_object(const char *obj_name, struct batch_options *opt, (uintmax_t)strlen(obj_name), obj_name); break; default: - die("BUG: unknown get_sha1_with_context result %d\n", + BUG("unknown get_sha1_with_context result %d\n", result); break; } diff --git a/builtin/checkout.c b/builtin/checkout.c index 105e07981f..28627650cd 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -485,7 +485,8 @@ static int merge_working_tree(const struct checkout_opts *opts, resolve_undo_clear(); if (opts->force) { - ret = reset_tree(new_branch_info->commit->tree, opts, 1, writeout_error); + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 1, writeout_error); if (ret) return ret; } else { @@ -527,6 +528,7 @@ static int merge_working_tree(const struct checkout_opts *opts, init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); + clear_unpack_trees_porcelain(&topts); if (ret == -1) { /* * Unpack couldn't do a trivial merge; either @@ -571,18 +573,23 @@ static int merge_working_tree(const struct checkout_opts *opts, o.verbosity = 0; work = write_tree_from_memory(&o); - ret = reset_tree(new_branch_info->commit->tree, opts, 1, + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 1, writeout_error); if (ret) return ret; o.ancestor = old_branch_info->name; o.branch1 = new_branch_info->name; o.branch2 = "local"; - ret = merge_trees(&o, new_branch_info->commit->tree, work, - old_branch_info->commit->tree, &result); + ret = merge_trees(&o, + get_commit_tree(new_branch_info->commit), + work, + get_commit_tree(old_branch_info->commit), + &result); if (ret < 0) exit(128); - ret = reset_tree(new_branch_info->commit->tree, opts, 0, + ret = reset_tree(get_commit_tree(new_branch_info->commit), + opts, 0, writeout_error); strbuf_release(&o.obuf); if (ret) @@ -1003,7 +1010,7 @@ static int parse_branchname_arg(int argc, const char **argv, *source_tree = parse_tree_indirect(rev); } else { parse_commit_or_die(new_branch_info->commit); - *source_tree = new_branch_info->commit->tree; + *source_tree = get_commit_tree(new_branch_info->commit); } if (!*source_tree) /* case (1): want a tree */ @@ -1114,10 +1121,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"), BRANCH_TRACK_EXPLICIT), OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")), - OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"), - 2), - OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"), - 3), + OPT_SET_INT_F('2', "ours", &opts.writeout_stage, + N_("checkout our version for unmerged files"), + 2, PARSE_OPT_NONEG), + OPT_SET_INT_F('3', "theirs", &opts.writeout_stage, + N_("checkout their version for unmerged files"), + 3, PARSE_OPT_NONEG), OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"), PARSE_OPT_NOCOMPLETE), OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), diff --git a/builtin/clean.c b/builtin/clean.c index fad533a0a7..ab402c204c 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -16,6 +16,7 @@ #include "column.h" #include "color.h" #include "pathspec.h" +#include "help.h" static int force = -1; /* unset */ static int interactive; @@ -42,6 +43,15 @@ enum color_clean { CLEAN_COLOR_ERROR = 5 }; +static const char *color_interactive_slots[] = { + [CLEAN_COLOR_ERROR] = "error", + [CLEAN_COLOR_HEADER] = "header", + [CLEAN_COLOR_HELP] = "help", + [CLEAN_COLOR_PLAIN] = "plain", + [CLEAN_COLOR_PROMPT] = "prompt", + [CLEAN_COLOR_RESET] = "reset", +}; + static int clean_use_color = -1; static char clean_colors[][COLOR_MAXLEN] = { [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED, @@ -82,22 +92,7 @@ struct menu_stuff { void *stuff; }; -static int parse_clean_color_slot(const char *var) -{ - if (!strcasecmp(var, "reset")) - return CLEAN_COLOR_RESET; - if (!strcasecmp(var, "plain")) - return CLEAN_COLOR_PLAIN; - if (!strcasecmp(var, "prompt")) - return CLEAN_COLOR_PROMPT; - if (!strcasecmp(var, "header")) - return CLEAN_COLOR_HEADER; - if (!strcasecmp(var, "help")) - return CLEAN_COLOR_HELP; - if (!strcasecmp(var, "error")) - return CLEAN_COLOR_ERROR; - return -1; -} +define_list_config_array(color_interactive_slots); static int git_clean_config(const char *var, const char *value, void *cb) { @@ -113,7 +108,7 @@ static int git_clean_config(const char *var, const char *value, void *cb) return 0; } if (skip_prefix(var, "color.interactive.", &slot_name)) { - int slot = parse_clean_color_slot(slot_name); + int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name); if (slot < 0) return 0; if (!value) diff --git a/builtin/clone.c b/builtin/clone.c index 29998c02ec..1d939af9d8 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -14,6 +14,7 @@ #include "parse-options.h" #include "fetch-pack.h" #include "refs.h" +#include "refspec.h" #include "object-store.h" #include "tree.h" #include "tree-walk.h" @@ -547,7 +548,7 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch } static struct ref *wanted_peer_refs(const struct ref *refs, - struct refspec *refspec) + struct refspec_item *refspec) { struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); struct ref *local_refs = head; @@ -824,7 +825,7 @@ static void write_refspec_config(const char *src_ref_prefix, } else if (remote_head_points_at) { const char *head = remote_head_points_at->name; if (!skip_prefix(head, "refs/heads/", &head)) - die("BUG: remote HEAD points at non-head?"); + BUG("remote HEAD points at non-head?"); strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name, branch_top->buf, head); @@ -895,8 +896,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int err = 0, complete_refs_before_fetch = 1; int submodule_progress; - struct refspec *refspec; - const char *fetch_pattern; + struct refspec_item refspec; fetch_if_missing = 0; @@ -1078,8 +1078,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_required_reference.nr || option_optional_reference.nr) setup_reference(); - fetch_pattern = value.buf; - refspec = parse_fetch_refspec(1, &fetch_pattern); + refspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH); strbuf_reset(&value); @@ -1136,10 +1135,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (transport->smart_options && !deepen && !filter_options.choice) transport->smart_options->check_self_contained_and_connected = 1; - refs = transport_get_remote_refs(transport); + refs = transport_get_remote_refs(transport, NULL); if (refs) { - mapped_refs = wanted_peer_refs(refs, refspec); + mapped_refs = wanted_peer_refs(refs, &refspec); /* * transport_get_remote_refs() may return refs with null sha-1 * in mapped_refs (see struct transport->get_refs_list @@ -1233,6 +1232,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_release(&value); junk_mode = JUNK_LEAVE_ALL; - free(refspec); + refspec_item_clear(&refspec); return err; } diff --git a/builtin/column.c b/builtin/column.c index 0c3223d64b..5228ccf37a 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -42,7 +42,6 @@ int cmd_column(int argc, const char **argv, const char *prefix) git_config(column_config, NULL); memset(&copts, 0, sizeof(copts)); - copts.width = term_columns(); copts.padding = 1; argc = parse_options(argc, argv, "", options, builtin_column_usage, 0); if (argc) diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c new file mode 100644 index 0000000000..37420ae0fd --- /dev/null +++ b/builtin/commit-graph.c @@ -0,0 +1,171 @@ +#include "builtin.h" +#include "config.h" +#include "dir.h" +#include "lockfile.h" +#include "parse-options.h" +#include "commit-graph.h" + +static char const * const builtin_commit_graph_usage[] = { + N_("git commit-graph [--object-dir <objdir>]"), + N_("git commit-graph read [--object-dir <objdir>]"), + N_("git commit-graph write [--object-dir <objdir>] [--append] [--stdin-packs|--stdin-commits]"), + NULL +}; + +static const char * const builtin_commit_graph_read_usage[] = { + N_("git commit-graph read [--object-dir <objdir>]"), + NULL +}; + +static const char * const builtin_commit_graph_write_usage[] = { + N_("git commit-graph write [--object-dir <objdir>] [--append] [--stdin-packs|--stdin-commits]"), + NULL +}; + +static struct opts_commit_graph { + const char *obj_dir; + int stdin_packs; + int stdin_commits; + int append; +} opts; + +static int graph_read(int argc, const char **argv) +{ + struct commit_graph *graph = NULL; + char *graph_name; + + static struct option builtin_commit_graph_read_options[] = { + OPT_STRING(0, "object-dir", &opts.obj_dir, + N_("dir"), + N_("The object directory to store the graph")), + OPT_END(), + }; + + argc = parse_options(argc, argv, NULL, + builtin_commit_graph_read_options, + builtin_commit_graph_read_usage, 0); + + if (!opts.obj_dir) + opts.obj_dir = get_object_directory(); + + graph_name = get_commit_graph_filename(opts.obj_dir); + graph = load_commit_graph_one(graph_name); + + if (!graph) + die("graph file %s does not exist", graph_name); + FREE_AND_NULL(graph_name); + + printf("header: %08x %d %d %d %d\n", + ntohl(*(uint32_t*)graph->data), + *(unsigned char*)(graph->data + 4), + *(unsigned char*)(graph->data + 5), + *(unsigned char*)(graph->data + 6), + *(unsigned char*)(graph->data + 7)); + printf("num_commits: %u\n", graph->num_commits); + printf("chunks:"); + + if (graph->chunk_oid_fanout) + printf(" oid_fanout"); + if (graph->chunk_oid_lookup) + printf(" oid_lookup"); + if (graph->chunk_commit_data) + printf(" commit_metadata"); + if (graph->chunk_large_edges) + printf(" large_edges"); + printf("\n"); + + return 0; +} + +static int graph_write(int argc, const char **argv) +{ + const char **pack_indexes = NULL; + int packs_nr = 0; + const char **commit_hex = NULL; + int commits_nr = 0; + const char **lines = NULL; + int lines_nr = 0; + int lines_alloc = 0; + + static struct option builtin_commit_graph_write_options[] = { + OPT_STRING(0, "object-dir", &opts.obj_dir, + N_("dir"), + N_("The object directory to store the graph")), + OPT_BOOL(0, "stdin-packs", &opts.stdin_packs, + N_("scan pack-indexes listed by stdin for commits")), + OPT_BOOL(0, "stdin-commits", &opts.stdin_commits, + N_("start walk at commits listed by stdin")), + OPT_BOOL(0, "append", &opts.append, + N_("include all commits already in the commit-graph file")), + OPT_END(), + }; + + argc = parse_options(argc, argv, NULL, + builtin_commit_graph_write_options, + builtin_commit_graph_write_usage, 0); + + if (opts.stdin_packs && opts.stdin_commits) + die(_("cannot use both --stdin-commits and --stdin-packs")); + if (!opts.obj_dir) + opts.obj_dir = get_object_directory(); + + if (opts.stdin_packs || opts.stdin_commits) { + struct strbuf buf = STRBUF_INIT; + lines_nr = 0; + lines_alloc = 128; + ALLOC_ARRAY(lines, lines_alloc); + + while (strbuf_getline(&buf, stdin) != EOF) { + ALLOC_GROW(lines, lines_nr + 1, lines_alloc); + lines[lines_nr++] = strbuf_detach(&buf, NULL); + } + + if (opts.stdin_packs) { + pack_indexes = lines; + packs_nr = lines_nr; + } + if (opts.stdin_commits) { + commit_hex = lines; + commits_nr = lines_nr; + } + } + + write_commit_graph(opts.obj_dir, + pack_indexes, + packs_nr, + commit_hex, + commits_nr, + opts.append); + + return 0; +} + +int cmd_commit_graph(int argc, const char **argv, const char *prefix) +{ + static struct option builtin_commit_graph_options[] = { + OPT_STRING(0, "object-dir", &opts.obj_dir, + N_("dir"), + N_("The object directory to store the graph")), + OPT_END(), + }; + + if (argc == 2 && !strcmp(argv[1], "-h")) + usage_with_options(builtin_commit_graph_usage, + builtin_commit_graph_options); + + git_config(git_default_config, NULL); + argc = parse_options(argc, argv, prefix, + builtin_commit_graph_options, + builtin_commit_graph_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (argc > 0) { + if (!strcmp(argv[0], "read")) + return graph_read(argc, argv); + if (!strcmp(argv[0], "write")) + return graph_write(argc, argv); + } + + usage_with_options(builtin_commit_graph_usage, + builtin_commit_graph_options); +} diff --git a/builtin/commit.c b/builtin/commit.c index 7c22879777..158e3f843a 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -32,6 +32,7 @@ #include "column.h" #include "sequencer.h" #include "mailmap.h" +#include "help.h" static const char * const builtin_commit_usage[] = { N_("git commit [<options>] [--] <pathspec>..."), @@ -66,6 +67,18 @@ N_("If you wish to skip this commit, use:\n" "Then \"git cherry-pick --continue\" will resume cherry-picking\n" "the remaining commits.\n"); +static const char *color_status_slots[] = { + [WT_STATUS_HEADER] = "header", + [WT_STATUS_UPDATED] = "updated", + [WT_STATUS_CHANGED] = "changed", + [WT_STATUS_UNTRACKED] = "untracked", + [WT_STATUS_NOBRANCH] = "noBranch", + [WT_STATUS_UNMERGED] = "unmerged", + [WT_STATUS_LOCAL_BRANCH] = "localBranch", + [WT_STATUS_REMOTE_BRANCH] = "remoteBranch", + [WT_STATUS_ONBRANCH] = "branch", +}; + static const char *use_message_buffer; static struct lock_file index_lock; /* real index */ static struct lock_file false_lock; /* used only for partial commits */ @@ -143,6 +156,16 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset) return 0; } +static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset) +{ + const char **value = opt->value; + if (arg != NULL && *arg == '=') + arg = arg + 1; + + *value = arg; + return 0; +} + static void determine_whence(struct wt_status *s) { if (file_exists(git_path_merge_head(the_repository))) @@ -161,9 +184,9 @@ static void determine_whence(struct wt_status *s) static void status_init_config(struct wt_status *s, config_fn_t fn) { wt_status_prepare(s); + init_diff_ui_defaults(); git_config(fn, s); determine_whence(s); - init_diff_ui_defaults(); s->hints = advice_status_hints; /* must come after git_config() */ } @@ -218,8 +241,7 @@ static int list_paths(struct string_list *list, const char *with_tree, if (with_tree) { char *max_prefix = common_prefix(pattern); - overlay_tree_on_index(&the_index, with_tree, - max_prefix ? max_prefix : prefix); + overlay_tree_on_index(&the_index, with_tree, max_prefix); free(max_prefix); } @@ -496,7 +518,7 @@ static int is_a_merge(const struct commit *current_head) static void assert_split_ident(struct ident_split *id, const struct strbuf *buf) { if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin) - die("BUG: unable to parse our own ident: %s", buf->buf); + BUG("unable to parse our own ident: %s", buf->buf); } static void export_one(const char *var, const char *s, const char *e, int hack) @@ -1174,27 +1196,14 @@ static int dry_run_commit(int argc, const char **argv, const char *prefix, return commitable ? 0 : 1; } +define_list_config_array_extra(color_status_slots, {"added"}); + static int parse_status_slot(const char *slot) { - if (!strcasecmp(slot, "header")) - return WT_STATUS_HEADER; - if (!strcasecmp(slot, "branch")) - return WT_STATUS_ONBRANCH; - if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added")) + if (!strcasecmp(slot, "added")) return WT_STATUS_UPDATED; - if (!strcasecmp(slot, "changed")) - return WT_STATUS_CHANGED; - if (!strcasecmp(slot, "untracked")) - return WT_STATUS_UNTRACKED; - if (!strcasecmp(slot, "nobranch")) - return WT_STATUS_NOBRANCH; - if (!strcasecmp(slot, "unmerged")) - return WT_STATUS_UNMERGED; - if (!strcasecmp(slot, "localBranch")) - return WT_STATUS_LOCAL_BRANCH; - if (!strcasecmp(slot, "remoteBranch")) - return WT_STATUS_REMOTE_BRANCH; - return -1; + + return LOOKUP_CONFIG(color_status_slots, slot); } static int git_status_config(const char *k, const char *v, void *cb) @@ -1260,11 +1269,31 @@ static int git_status_config(const char *k, const char *v, void *cb) return error(_("Invalid untracked files mode '%s'"), v); return 0; } + if (!strcmp(k, "diff.renamelimit")) { + if (s->rename_limit == -1) + s->rename_limit = git_config_int(k, v); + return 0; + } + if (!strcmp(k, "status.renamelimit")) { + s->rename_limit = git_config_int(k, v); + return 0; + } + if (!strcmp(k, "diff.renames")) { + if (s->detect_rename == -1) + s->detect_rename = git_config_rename(k, v); + return 0; + } + if (!strcmp(k, "status.renames")) { + s->detect_rename = git_config_rename(k, v); + return 0; + } return git_diff_ui_config(k, v, NULL); } int cmd_status(int argc, const char **argv, const char *prefix) { + static int no_renames = -1; + static const char *rename_score_arg = (const char *)-1; static struct wt_status s; int fd; struct object_id oid; @@ -1298,6 +1327,10 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")), + OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")), + { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg, + N_("n"), N_("detect renames, optionally set similarity index"), + PARSE_OPT_OPTARG, opt_parse_rename_score }, OPT_END(), }; @@ -1337,6 +1370,14 @@ int cmd_status(int argc, const char **argv, const char *prefix) s.ignore_submodule_arg = ignore_submodule_arg; s.status_format = status_format; s.verbose = verbose; + if (no_renames != -1) + s.detect_rename = !no_renames; + if ((intptr_t)rename_score_arg != -1) { + if (s.detect_rename < DIFF_DETECT_RENAME) + s.detect_rename = DIFF_DETECT_RENAME; + if (rename_score_arg) + s.rename_score = parse_rename_score(&rename_score_arg); + } wt_status_collect(&s); diff --git a/builtin/config.c b/builtin/config.c index 01169dd628..b29d26dede 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -25,7 +25,8 @@ static char term = '\n'; static int use_global_config, use_system_config, use_local_config; static struct git_config_source given_config_source; -static int actions, types; +static int actions, type; +static char *default_value; static int end_null; static int respect_includes_opt = -1; static struct config_options config_options; @@ -55,11 +56,68 @@ static int show_origin; #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \ ACTION_GET_REGEXP | ACTION_GET_URLMATCH) -#define TYPE_BOOL (1<<0) -#define TYPE_INT (1<<1) -#define TYPE_BOOL_OR_INT (1<<2) -#define TYPE_PATH (1<<3) -#define TYPE_EXPIRY_DATE (1<<4) +#define TYPE_BOOL 1 +#define TYPE_INT 2 +#define TYPE_BOOL_OR_INT 3 +#define TYPE_PATH 4 +#define TYPE_EXPIRY_DATE 5 +#define TYPE_COLOR 6 + +#define OPT_CALLBACK_VALUE(s, l, v, h, i) \ + { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \ + PARSE_OPT_NONEG, option_parse_type, (i) } + +static struct option builtin_config_options[]; + +static int option_parse_type(const struct option *opt, const char *arg, + int unset) +{ + int new_type, *to_type; + + if (unset) { + *((int *) opt->value) = 0; + return 0; + } + + /* + * To support '--<type>' style flags, begin with new_type equal to + * opt->defval. + */ + new_type = opt->defval; + if (!new_type) { + if (!strcmp(arg, "bool")) + new_type = TYPE_BOOL; + else if (!strcmp(arg, "int")) + new_type = TYPE_INT; + else if (!strcmp(arg, "bool-or-int")) + new_type = TYPE_BOOL_OR_INT; + else if (!strcmp(arg, "path")) + new_type = TYPE_PATH; + else if (!strcmp(arg, "expiry-date")) + new_type = TYPE_EXPIRY_DATE; + else if (!strcmp(arg, "color")) + new_type = TYPE_COLOR; + else + die(_("unrecognized --type argument, %s"), arg); + } + + to_type = opt->value; + if (*to_type && *to_type != new_type) { + /* + * Complain when there is a new type not equal to the old type. + * This allows for combinations like '--int --type=int' and + * '--type=int --type=int', but disallows ones like '--type=bool + * --int' and '--type=bool + * --type=int'. + */ + error("only one type at a time."); + usage_with_options(builtin_config_usage, + builtin_config_options); + } + *to_type = new_type; + + return 0; +} static struct option builtin_config_options[] = { OPT_GROUP(N_("Config file location")), @@ -84,16 +142,18 @@ static struct option builtin_config_options[] = { OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR), OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL), OPT_GROUP(N_("Type")), - OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL), - OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT), - OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), - OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH), - OPT_BIT(0, "expiry-date", &types, N_("value is an expiry date"), TYPE_EXPIRY_DATE), + OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type), + OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), + OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), + OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), + OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), + OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE), OPT_GROUP(N_("Other")), 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_opt, 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_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")), OPT_END(), }; @@ -149,30 +209,35 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value if (show_keys) strbuf_addch(buf, key_delim); - if (types == TYPE_INT) + if (type == TYPE_INT) strbuf_addf(buf, "%"PRId64, git_config_int64(key_, value_ ? value_ : "")); - else if (types == TYPE_BOOL) + else if (type == TYPE_BOOL) strbuf_addstr(buf, git_config_bool(key_, value_) ? "true" : "false"); - else if (types == TYPE_BOOL_OR_INT) { + else if (type == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key_, value_, &is_bool); if (is_bool) strbuf_addstr(buf, v ? "true" : "false"); else strbuf_addf(buf, "%d", v); - } else if (types == TYPE_PATH) { + } else if (type == TYPE_PATH) { const char *v; if (git_config_pathname(&v, key_, value_) < 0) return -1; strbuf_addstr(buf, v); free((char *)v); - } else if (types == TYPE_EXPIRY_DATE) { + } else if (type == TYPE_EXPIRY_DATE) { timestamp_t t; if (git_config_expiry_date(&t, key_, value_) < 0) return -1; strbuf_addf(buf, "%"PRItime, t); + } else if (type == TYPE_COLOR) { + char v[COLOR_MAXLEN]; + if (git_config_color(v, key_, value_) < 0) + return -1; + strbuf_addstr(buf, v); } else if (value_) { strbuf_addstr(buf, value_); } else { @@ -258,6 +323,16 @@ static int get_value(const char *key_, const char *regex_) config_with_options(collect_config, &values, &given_config_source, &config_options); + if (!values.nr && default_value) { + struct strbuf *item; + ALLOC_GROW(values.items, values.nr + 1, values.alloc); + item = &values.items[values.nr++]; + strbuf_init(item, 0); + if (format_config(item, key_, default_value) < 0) + die(_("failed to format default config value: %s"), + default_value); + } + ret = !values.nr; for (i = 0; i < values.nr; i++) { @@ -287,7 +362,7 @@ static char *normalize_value(const char *key, const char *value) if (!value) return NULL; - if (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE) + if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE) /* * We don't do normalization for TYPE_PATH here: If * the path is like ~/foobar/, we prefer to store @@ -296,11 +371,11 @@ static char *normalize_value(const char *key, const char *value) * Also don't do normalization for expiry dates. */ return xstrdup(value); - if (types == TYPE_INT) + if (type == TYPE_INT) return xstrfmt("%"PRId64, git_config_int64(key, value)); - if (types == TYPE_BOOL) + if (type == TYPE_BOOL) return xstrdup(git_config_bool(key, value) ? "true" : "false"); - if (types == TYPE_BOOL_OR_INT) { + if (type == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key, value, &is_bool); if (!is_bool) @@ -308,8 +383,22 @@ static char *normalize_value(const char *key, const char *value) else return xstrdup(v ? "true" : "false"); } + if (type == TYPE_COLOR) { + char v[COLOR_MAXLEN]; + if (git_config_color(v, key, value)) + die("cannot parse color '%s'", value); - die("BUG: cannot normalize type %d", types); + /* + * The contents of `v` now contain an ANSI escape + * sequence, not suitable for including within a + * configuration file. Treat the above as a + * "sanity-check", and return the given value, which we + * know is representable as valid color code. + */ + return xstrdup(value); + } + + BUG("cannot normalize type %d", type); } static int get_color_found; @@ -513,6 +602,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (use_local_config && nongit) die(_("--local can only be used inside a git repository")); + if (given_config_source.blob && nongit) + die(_("--blob can only be used inside a git repository")); + if (given_config_source.file && !strcmp(given_config_source.file, "-")) { given_config_source.file = NULL; @@ -566,12 +658,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) key_delim = '\n'; } - if (HAS_MULTI_BITS(types)) { - error("only one type at a time."); - usage_with_options(builtin_config_usage, builtin_config_options); - } - - if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) { + if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) { error("--get-color and variable type are incoherent"); usage_with_options(builtin_config_usage, builtin_config_options); } @@ -601,6 +688,12 @@ int cmd_config(int argc, const char **argv, const char *prefix) usage_with_options(builtin_config_usage, builtin_config_options); } + if (default_value && !(actions & ACTION_GET)) { + error("--default is only applicable to --get"); + usage_with_options(builtin_config_usage, + builtin_config_options); + } + if (actions & PAGING_ACTIONS) setup_auto_pager("config", 1); diff --git a/builtin/count-objects.c b/builtin/count-objects.c index b054713e1a..d51e2ce1ec 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -66,7 +66,7 @@ static int count_loose(const struct object_id *oid, const char *path, void *data else { loose_size += on_disk_bytes(st); loose++; - if (verbose && has_sha1_pack(oid->hash)) + if (verbose && has_object_pack(oid)) packed_loose++; } return 0; diff --git a/builtin/describe.c b/builtin/describe.c index 65b0edc473..1e87f68d5e 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -6,7 +6,7 @@ #include "blob.h" #include "refs.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "revision.h" #include "diff.h" @@ -16,9 +16,12 @@ #include "object-store.h" #include "revision.h" #include "list-objects.h" +#include "commit-slab.h" #define MAX_TAGS (FLAG_BITS - 1) +define_commit_slab(commit_names, struct commit_name *); + static const char * const describe_usage[] = { N_("git describe [<options>] [<commit-ish>...]"), N_("git describe [<options>] --dirty"), @@ -38,6 +41,7 @@ static struct string_list patterns = STRING_LIST_INIT_NODUP; static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP; static int always; static const char *suffix, *dirty, *broken; +static struct commit_names commit_names; /* diff-index command arguments to check if working tree is dirty. */ static const char *diff_index_args[] = { @@ -322,11 +326,14 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) if (!have_util) { struct hashmap_iter iter; struct commit *c; - struct commit_name *n = hashmap_iter_first(&names, &iter); + struct commit_name *n; + + init_commit_names(&commit_names); + n = hashmap_iter_first(&names, &iter); for (; n; n = hashmap_iter_next(&iter)) { c = lookup_commit_reference_gently(&n->peeled, 1); if (c) - c->util = n; + *commit_names_at(&commit_names, c) = n; } have_util = 1; } @@ -337,8 +344,11 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) while (list) { struct commit *c = pop_commit(&list); struct commit_list *parents = c->parents; + struct commit_name **slot; + seen_commits++; - n = c->util; + slot = commit_names_peek(&commit_names, c); + n = slot ? *slot : NULL; if (n) { if (!tags && !all && n->prio < 2) { unannotated_cnt++; @@ -613,7 +623,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) suffix = broken; } } else if (dirty) { - static struct lock_file index_lock; + struct lock_file index_lock = LOCK_INIT; struct rev_info revs; struct argv_array args = ARGV_ARRAY_INIT; int fd, result; diff --git a/builtin/diff.c b/builtin/diff.c index 16bfb22f73..b709b6e984 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -352,6 +352,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix) rev.diffopt.flags.allow_external = 1; rev.diffopt.flags.allow_textconv = 1; + /* + * Default to intent-to-add entries invisible in the + * index. This makes them show up as new files in diff-files + * and not at all in diff-cached. + */ + rev.diffopt.ita_invisible_in_index = 1; + if (nongit) die(_("Not a git repository")); argc = setup_revisions(argc, argv, &rev, NULL); @@ -398,7 +405,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!obj) die(_("invalid object '%s' given."), name); if (obj->type == OBJ_COMMIT) - obj = &((struct commit *)obj)->tree->object; + obj = &get_commit_tree(((struct commit *)obj))->object; if (obj->type == OBJ_TREE) { obj->flags |= flags; diff --git a/builtin/difftool.c b/builtin/difftool.c index df7e75f797..51f6c9cdb4 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -15,7 +15,7 @@ #include "config.h" #include "builtin.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "argv-array.h" #include "strbuf.h" @@ -611,7 +611,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, continue; if (!indices_loaded) { - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; strbuf_reset(&buf); strbuf_addf(&buf, "%s/wtindex", tmpdir); if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 || @@ -696,12 +696,11 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) N_("use `diff.guitool` instead of `diff.tool`")), OPT_BOOL('d', "dir-diff", &dir_diff, N_("perform a full-directory diff")), - { OPTION_SET_INT, 'y', "no-prompt", &prompt, NULL, + OPT_SET_INT_F('y', "no-prompt", &prompt, N_("do not prompt before launching a diff tool"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0}, - { OPTION_SET_INT, 0, "prompt", &prompt, NULL, NULL, - PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN, - NULL, 1 }, + 0, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "prompt", &prompt, NULL, + 1, PARSE_OPT_NONEG | PARSE_OPT_HIDDEN), OPT_BOOL(0, "symlinks", &symlinks, N_("use symlinks in dir-diff mode")), OPT_STRING('t', "tool", &difftool_cmd, N_("<tool>"), diff --git a/builtin/fast-export.c b/builtin/fast-export.c index f593e57b9d..9ee6a4d2e8 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -7,6 +7,7 @@ #include "cache.h" #include "config.h" #include "refs.h" +#include "refspec.h" #include "object-store.h" #include "commit.h" #include "object.h" @@ -22,6 +23,7 @@ #include "quote.h" #include "remote.h" #include "blob.h" +#include "commit-slab.h" static const char *fast_export_usage[] = { N_("git fast-export [rev-list-opts]"), @@ -36,9 +38,9 @@ static int use_done_feature; static int no_data; static int full_tree; static struct string_list extra_refs = STRING_LIST_INIT_NODUP; -static struct refspec *refspecs; -static int refspecs_nr; +static struct refspec refspecs = REFSPEC_INIT_FETCH; static int anonymize; +static struct revision_sources revision_sources; static int parse_opt_signed_tag_mode(const struct option *opt, const char *arg, int unset) @@ -157,15 +159,14 @@ static void anonymize_path(struct strbuf *out, const char *path, } } -/* Since intptr_t is C99, we do not use it here */ -static inline uint32_t *mark_to_ptr(uint32_t mark) +static inline void *mark_to_ptr(uint32_t mark) { - return ((uint32_t *)NULL) + mark; + return (void *)(uintptr_t)mark; } static inline uint32_t ptr_to_mark(void * mark) { - return (uint32_t *)mark - (uint32_t *)NULL; + return (uint32_t)(uintptr_t)mark; } static inline void mark_object(struct object *object, uint32_t mark) @@ -518,7 +519,7 @@ static void anonymize_ident_line(const char **beg, const char **end) /* skip "committer", "author", "tagger", etc */ end_of_header = strchr(*beg, ' '); if (!end_of_header) - die("BUG: malformed line fed to anonymize_ident_line: %.*s", + BUG("malformed line fed to anonymize_ident_line: %.*s", (int)(*end - *beg), *beg); end_of_header++; strbuf_add(out, *beg, end_of_header - *beg); @@ -579,11 +580,11 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, get_object_mark(&commit->parents->item->object) != 0 && !full_tree) { parse_commit_or_die(commit->parents->item); - diff_tree_oid(&commit->parents->item->tree->object.oid, - &commit->tree->object.oid, "", &rev->diffopt); + diff_tree_oid(get_commit_tree_oid(commit->parents->item), + get_commit_tree_oid(commit), "", &rev->diffopt); } else - diff_root_tree_oid(&commit->tree->object.oid, + diff_root_tree_oid(get_commit_tree_oid(commit), "", &rev->diffopt); /* Export the referenced blobs, and remember the marks. */ @@ -591,7 +592,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode)) export_blob(&diff_queued_diff.queue[i]->two->oid); - refname = commit->util; + refname = *revision_sources_at(&revision_sources, commit); if (anonymize) { refname = anonymize_refname(refname); anonymize_ident_line(&committer, &committer_end); @@ -652,8 +653,11 @@ static void handle_tail(struct object_array *commits, struct rev_info *revs, struct commit *commit; while (commits->nr) { commit = (struct commit *)object_array_pop(commits); - if (has_unshown_parent(commit)) + if (has_unshown_parent(commit)) { + /* Queue again, to be handled later */ + add_object_array(&commit->object, NULL, commits); return; + } handle_commit(commit, revs, paths_of_changed_objects); } } @@ -827,9 +831,9 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1) continue; - if (refspecs) { + if (refspecs.nr) { char *private; - private = apply_refspecs(refspecs, refspecs_nr, full_name); + private = apply_refspecs(&refspecs, full_name); if (private) { free(full_name); full_name = private; @@ -860,10 +864,11 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) * This ref will not be updated through a commit, lets make * sure it gets properly updated eventually. */ - if (commit->util || commit->object.flags & SHOWN) + if (*revision_sources_at(&revision_sources, commit) || + commit->object.flags & SHOWN) string_list_append(&extra_refs, full_name)->util = commit; - if (!commit->util) - commit->util = full_name; + if (!*revision_sources_at(&revision_sources, commit)) + *revision_sources_at(&revision_sources, commit) = full_name; } } @@ -975,8 +980,8 @@ static void import_marks(char *input_file) static void handle_deletes(void) { int i; - for (i = 0; i < refspecs_nr; i++) { - struct refspec *refspec = &refspecs[i]; + for (i = 0; i < refspecs.nr; i++) { + struct refspec_item *refspec = &refspecs.items[i]; if (*refspec->src) continue; @@ -1027,8 +1032,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); init_revisions(&revs, prefix); + init_revision_sources(&revision_sources); revs.topo_order = 1; - revs.show_source = 1; + revs.sources = &revision_sources; revs.rewrite_parents = 1; argc = parse_options(argc, argv, prefix, options, fast_export_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN); @@ -1037,18 +1043,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) usage_with_options (fast_export_usage, options); if (refspecs_list.nr) { - const char **refspecs_str; int i; - ALLOC_ARRAY(refspecs_str, refspecs_list.nr); for (i = 0; i < refspecs_list.nr; i++) - refspecs_str[i] = refspecs_list.items[i].string; - - refspecs_nr = refspecs_list.nr; - refspecs = parse_fetch_refspec(refspecs_nr, refspecs_str); + refspec_append(&refspecs, refspecs_list.items[i].string); string_list_clear(&refspecs_list, 1); - free(refspecs_str); } if (use_done_feature) @@ -1087,7 +1087,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (use_done_feature) printf("done\n"); - free_refspec(refspecs_nr, refspecs); + refspec_clear(&refspecs); return 0; } diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index a7bc1366ab..1a1bc63566 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -4,6 +4,7 @@ #include "remote.h" #include "connect.h" #include "sha1-array.h" +#include "protocol.h" static const char fetch_pack_usage[] = "git fetch-pack [--all] [--stdin] [--quiet | -q] [--keep | -k] [--thin] " @@ -52,6 +53,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) struct fetch_pack_args args; struct oid_array shallow = OID_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; + struct packet_reader reader; fetch_if_missing = 0; @@ -211,10 +213,24 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) if (!conn) return args.diag_url ? 0 : 1; } - get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow); + + packet_reader_init(&reader, fd[0], NULL, 0, + PACKET_READ_CHOMP_NEWLINE | + PACKET_READ_GENTLE_ON_EOF); + + switch (discover_version(&reader)) { + case protocol_v2: + die("support for protocol v2 not implemented yet"); + case protocol_v1: + case protocol_v0: + get_remote_heads(&reader, &ref, 0, NULL, &shallow); + break; + case protocol_unknown_version: + BUG("unknown protocol version"); + } ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought, - &shallow, pack_lockfile_ptr); + &shallow, pack_lockfile_ptr, protocol_v0); if (pack_lockfile) { printf("lock %s\n", pack_lockfile); fflush(stdout); diff --git a/builtin/fetch.c b/builtin/fetch.c index 41d74c7e8d..83f36d7cde 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -5,6 +5,7 @@ #include "config.h" #include "repository.h" #include "refs.h" +#include "refspec.h" #include "object-store.h" #include "commit.h" #include "builtin.h" @@ -60,9 +61,9 @@ static const char *submodule_prefix = ""; static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; static int shown_url = 0; -static int refmap_alloc, refmap_nr; -static const char **refmap_array; +static struct refspec refmap = REFSPEC_INIT_FETCH; static struct list_objects_filter_options filter_options; +static struct string_list server_options = STRING_LIST_INIT_DUP; static int git_fetch_config(const char *k, const char *v, void *cb) { @@ -108,14 +109,12 @@ static int gitmodules_fetch_config(const char *var, const char *value, void *cb) static int parse_refmap_arg(const struct option *opt, const char *arg, int unset) { - ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc); - /* * "git fetch --refmap='' origin foo" * can be used to tell the command not to store anywhere */ - if (*arg) - refmap_array[refmap_nr++] = arg; + refspec_append(&refmap, arg); + return 0; } @@ -157,9 +156,9 @@ static struct option builtin_fetch_options[] = { N_("deepen history of shallow clone, excluding rev")), OPT_INTEGER(0, "deepen", &deepen_relative, N_("deepen history of shallow clone")), - { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL, - N_("convert to a complete repository"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 }, + OPT_SET_INT_F(0, "unshallow", &unshallow, + N_("convert to a complete repository"), + 1, PARSE_OPT_NONEG), { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, { OPTION_CALLBACK, 0, "recurse-submodules-default", @@ -171,6 +170,7 @@ 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_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), 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"), @@ -203,7 +203,7 @@ static void add_merge_config(struct ref **head, for (i = 0; i < branch->merge_nr; i++) { struct ref *rm, **old_tail = *tail; - struct refspec refspec; + struct refspec_item refspec; for (rm = *head; rm; rm = rm->next) { if (branch_merge_matches(branch, i, rm->name)) { @@ -265,7 +265,7 @@ static void find_non_local_tags(struct transport *transport, struct string_list_item *item = NULL; for_each_ref(add_existing, &existing_refs); - for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { + for (ref = transport_get_remote_refs(transport, NULL); ref; ref = ref->next) { if (!starts_with(ref->name, "refs/tags/")) continue; @@ -340,26 +340,40 @@ static void find_non_local_tags(struct transport *transport, } static struct ref *get_ref_map(struct transport *transport, - struct refspec *refspecs, int refspec_count, + struct refspec *rs, int tags, int *autotags) { int i; struct ref *rm; struct ref *ref_map = NULL; struct ref **tail = &ref_map; + struct argv_array ref_prefixes = ARGV_ARRAY_INIT; /* opportunistically-updated references: */ struct ref *orefs = NULL, **oref_tail = &orefs; - const struct ref *remote_refs = transport_get_remote_refs(transport); + const struct ref *remote_refs; + + if (rs->nr) + refspec_ref_prefixes(rs, &ref_prefixes); + else if (transport->remote && transport->remote->fetch.nr) + refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes); + + if (ref_prefixes.argc && + (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) { + argv_array_push(&ref_prefixes, "refs/tags/"); + } + + remote_refs = transport_get_remote_refs(transport, &ref_prefixes); - if (refspec_count) { + argv_array_clear(&ref_prefixes); + + if (rs->nr) { struct refspec *fetch_refspec; - int fetch_refspec_nr; - for (i = 0; i < refspec_count; i++) { - get_fetch_map(remote_refs, &refspecs[i], &tail, 0); - if (refspecs[i].dst && refspecs[i].dst[0]) + for (i = 0; i < rs->nr; i++) { + get_fetch_map(remote_refs, &rs->items[i], &tail, 0); + if (rs->items[i].dst && rs->items[i].dst[0]) *autotags = 1; } /* Merge everything on the command line (but not --tags) */ @@ -386,17 +400,14 @@ static struct ref *get_ref_map(struct transport *transport, * by ref_remove_duplicates() in favor of one of these * opportunistic entries with FETCH_HEAD_IGNORE. */ - if (refmap_array) { - fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array); - fetch_refspec_nr = refmap_nr; - } else { - fetch_refspec = transport->remote->fetch; - fetch_refspec_nr = transport->remote->fetch_refspec_nr; - } + if (refmap.nr) + fetch_refspec = &refmap; + else + fetch_refspec = &transport->remote->fetch; - for (i = 0; i < fetch_refspec_nr; i++) - get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1); - } else if (refmap_array) { + for (i = 0; i < fetch_refspec->nr; i++) + get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1); + } else if (refmap.nr) { die("--refmap option is only meaningful with command-line refspec(s)."); } else { /* Use the defaults */ @@ -404,16 +415,16 @@ static struct ref *get_ref_map(struct transport *transport, struct branch *branch = branch_get(NULL); int has_merge = branch_has_merge_config(branch); if (remote && - (remote->fetch_refspec_nr || + (remote->fetch.nr || /* Note: has_merge implies non-NULL branch->remote_name */ (has_merge && !strcmp(branch->remote_name, remote->name)))) { - for (i = 0; i < remote->fetch_refspec_nr; i++) { - get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0); - if (remote->fetch[i].dst && - remote->fetch[i].dst[0]) + for (i = 0; i < remote->fetch.nr; i++) { + get_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0); + if (remote->fetch.items[i].dst && + remote->fetch.items[i].dst[0]) *autotags = 1; if (!i && !has_merge && ref_map && - !remote->fetch[0].pattern) + !remote->fetch.items[0].pattern) ref_map->fetch_head_status = FETCH_HEAD_MERGE; } /* @@ -948,11 +959,11 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map) return ret; } -static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, - const char *raw_url) +static int prune_refs(struct refspec *rs, struct ref *ref_map, + const char *raw_url) { int url_len, i, result = 0; - struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map); + struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map); char *url; int summary_width = transport_summary_width(stale_refs); const char *dangling_msg = dry_run @@ -1098,7 +1109,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map) } static int do_fetch(struct transport *transport, - struct refspec *refs, int ref_count) + struct refspec *rs) { struct string_list existing_refs = STRING_LIST_INIT_DUP; struct ref *ref_map; @@ -1122,7 +1133,7 @@ static int do_fetch(struct transport *transport, goto cleanup; } - ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); + ref_map = get_ref_map(transport, rs, tags, &autotags); if (!update_head_ok) check_not_current_branch(ref_map); @@ -1146,11 +1157,10 @@ static int do_fetch(struct transport *transport, * explicitly (via command line or configuration); we * don't care whether --tags was specified. */ - if (ref_count) { - prune_refs(refs, ref_count, ref_map, transport->url); + if (rs->nr) { + prune_refs(rs, ref_map, transport->url); } else { - prune_refs(transport->remote->fetch, - transport->remote->fetch_refspec_nr, + prune_refs(&transport->remote->fetch, ref_map, transport->url); } @@ -1339,10 +1349,8 @@ static inline void fetch_one_setup_partial(struct remote *remote) static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok) { - static const char **refs = NULL; - struct refspec *refspec; - int ref_nr = 0; - int j = 0; + struct refspec rs = REFSPEC_INIT_FETCH; + int i; int exit_code; int maybe_prune_tags; int remote_via_config = remote_is_configured(remote, 0); @@ -1375,37 +1383,34 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru maybe_prune_tags = prune_tags_ok && prune_tags; if (maybe_prune_tags && remote_via_config) - add_prune_tags_to_fetch_refspec(remote); - - if (argc > 0 || (maybe_prune_tags && !remote_via_config)) { - size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1); - refs = xcalloc(nr_alloc, sizeof(const char *)); - if (maybe_prune_tags) { - refs[j++] = xstrdup("refs/tags/*:refs/tags/*"); - ref_nr++; + refspec_append(&remote->fetch, TAG_REFSPEC); + + if (maybe_prune_tags && (argc || !remote_via_config)) + refspec_append(&rs, TAG_REFSPEC); + + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "tag")) { + char *tag; + i++; + if (i >= argc) + die(_("You need to specify a tag name.")); + + tag = xstrfmt("refs/tags/%s:refs/tags/%s", + argv[i], argv[i]); + refspec_append(&rs, tag); + free(tag); + } else { + refspec_append(&rs, argv[i]); } } - if (argc > 0) { - int i; - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "tag")) { - i++; - if (i >= argc) - die(_("You need to specify a tag name.")); - refs[j++] = xstrfmt("refs/tags/%s:refs/tags/%s", - argv[i], argv[i]); - } else - refs[j++] = argv[i]; - ref_nr++; - } - } + if (server_options.nr) + gtransport->server_options = &server_options; sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); - refspec = parse_fetch_refspec(ref_nr, refs); - exit_code = do_fetch(gtransport, refspec, ref_nr); - free_refspec(ref_nr, refspec); + exit_code = do_fetch(gtransport, &rs); + refspec_clear(&rs); transport_disconnect(gtransport); gtransport = NULL; return exit_code; diff --git a/builtin/fsck.c b/builtin/fsck.c index 9d59d7d5a2..3ad4f160f9 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -228,7 +228,7 @@ static void check_reachable_object(struct object *obj) if (!(obj->flags & HAS_OBJ)) { if (is_promisor_object(&obj->oid)) return; - if (has_sha1_pack(obj->oid.hash)) + if (has_object_pack(&obj->oid)) return; /* it is in pack - forget about it */ printf("missing %s %s\n", printable_type(obj), describe_object(obj)); @@ -340,7 +340,7 @@ static void check_connectivity(void) } } -static int fsck_obj(struct object *obj) +static int fsck_obj(struct object *obj, void *buffer, unsigned long size) { int err; @@ -354,7 +354,7 @@ static int fsck_obj(struct object *obj) if (fsck_walk(obj, NULL, &fsck_obj_options)) objerror(obj, "broken links"); - err = fsck_object(obj, NULL, 0, &fsck_obj_options); + err = fsck_object(obj, buffer, size, &fsck_obj_options); if (err) goto out; @@ -399,7 +399,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - return fsck_obj(obj); + return fsck_obj(obj, buffer, size); } static int default_refs; @@ -507,44 +507,42 @@ static void get_default_heads(void) } } -static struct object *parse_loose_object(const struct object_id *oid, - const char *path) +static int fsck_loose(const struct object_id *oid, const char *path, void *data) { struct object *obj; - void *contents; enum object_type type; unsigned long size; + void *contents; int eaten; - if (read_loose_object(path, oid, &type, &size, &contents) < 0) - return NULL; + if (read_loose_object(path, oid, &type, &size, &contents) < 0) { + errors_found |= ERROR_OBJECT; + error("%s: object corrupt or missing: %s", + oid_to_hex(oid), path); + return 0; /* keep checking other objects */ + } if (!contents && type != OBJ_BLOB) - die("BUG: read_loose_object streamed a non-blob"); + BUG("read_loose_object streamed a non-blob"); obj = parse_object_buffer(oid, type, size, contents, &eaten); - - if (!eaten) - free(contents); - return obj; -} - -static int fsck_loose(const struct object_id *oid, const char *path, void *data) -{ - struct object *obj = parse_loose_object(oid, path); - if (!obj) { errors_found |= ERROR_OBJECT; - error("%s: object corrupt or missing: %s", + error("%s: object could not be parsed: %s", oid_to_hex(oid), path); + if (!eaten) + free(contents); return 0; /* keep checking other objects */ } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - if (fsck_obj(obj)) + if (fsck_obj(obj, contents, size)) errors_found |= ERROR_OBJECT; - return 0; + + if (!eaten) + free(contents); + return 0; /* keep checking other objects, even if we saw an error */ } static int fsck_cruft(const char *basename, const char *path, void *data) @@ -756,6 +754,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } stop_progress(&progress); } + + if (fsck_finish(&fsck_obj_options)) + errors_found |= ERROR_OBJECT; } for (i = 0; i < argc; i++) { diff --git a/builtin/gc.c b/builtin/gc.c index 3e67124eaa..ccfb1ceaeb 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -22,6 +22,10 @@ #include "commit.h" #include "packfile.h" #include "object-store.h" +#include "pack.h" +#include "pack-objects.h" +#include "blob.h" +#include "tree.h" #define FAILED_RUN "failed to run %s" @@ -41,6 +45,8 @@ static timestamp_t gc_log_expire_time; static const char *gc_log_expire = "1.day.ago"; static const char *prune_expire = "2.weeks.ago"; static const char *prune_worktrees_expire = "3.months.ago"; +static unsigned long big_pack_threshold; +static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT; static struct argv_array reflog = ARGV_ARRAY_INIT; @@ -128,6 +134,9 @@ static void gc_config(void) git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire); git_config_get_expiry("gc.logexpiry", &gc_log_expire); + git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold); + git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size); + git_config(git_default_config, NULL); } @@ -166,6 +175,28 @@ static int too_many_loose_objects(void) return needed; } +static struct packed_git *find_base_packs(struct string_list *packs, + unsigned long limit) +{ + struct packed_git *p, *base = NULL; + + for (p = get_packed_git(the_repository); p; p = p->next) { + if (!p->pack_local) + continue; + if (limit) { + if (p->pack_size >= limit) + string_list_append(packs, p->pack_name); + } else if (!base || base->pack_size < p->pack_size) { + base = p; + } + } + + if (base) + string_list_append(packs, base->pack_name); + + return base; +} + static int too_many_packs(void) { struct packed_git *p; @@ -188,7 +219,86 @@ static int too_many_packs(void) return gc_auto_pack_limit < cnt; } -static void add_repack_all_option(void) +static uint64_t total_ram(void) +{ +#if defined(HAVE_SYSINFO) + struct sysinfo si; + + if (!sysinfo(&si)) + return si.totalram; +#elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM)) + int64_t physical_memory; + int mib[2]; + size_t length; + + mib[0] = CTL_HW; +# if defined(HW_MEMSIZE) + mib[1] = HW_MEMSIZE; +# else + mib[1] = HW_PHYSMEM; +# endif + length = sizeof(int64_t); + if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) + return physical_memory; +#elif defined(GIT_WINDOWS_NATIVE) + MEMORYSTATUSEX memInfo; + + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&memInfo)) + return memInfo.ullTotalPhys; +#endif + return 0; +} + +static uint64_t estimate_repack_memory(struct packed_git *pack) +{ + unsigned long nr_objects = approximate_object_count(); + size_t os_cache, heap; + + if (!pack || !nr_objects) + return 0; + + /* + * First we have to scan through at least one pack. + * Assume enough room in OS file cache to keep the entire pack + * or we may accidentally evict data of other processes from + * the cache. + */ + os_cache = pack->pack_size + pack->index_size; + /* then pack-objects needs lots more for book keeping */ + heap = sizeof(struct object_entry) * nr_objects; + /* + * internal rev-list --all --objects takes up some memory too, + * let's say half of it is for blobs + */ + heap += sizeof(struct blob) * nr_objects / 2; + /* + * and the other half is for trees (commits and tags are + * usually insignificant) + */ + heap += sizeof(struct tree) * nr_objects / 2; + /* and then obj_hash[], underestimated in fact */ + heap += sizeof(struct object *) * nr_objects; + /* revindex is used also */ + heap += sizeof(struct revindex_entry) * nr_objects; + /* + * read_sha1_file() (either at delta calculation phase, or + * writing phase) also fills up the delta base cache + */ + heap += delta_base_cache_limit; + /* and of course pack-objects has its own delta cache */ + heap += max_delta_cache_size; + + return os_cache + heap; +} + +static int keep_one_pack(struct string_list_item *item, void *data) +{ + argv_array_pushf(&repack, "--keep-pack=%s", basename(item->string)); + return 0; +} + +static void add_repack_all_option(struct string_list *keep_pack) { if (prune_expire && !strcmp(prune_expire, "now")) argv_array_push(&repack, "-a"); @@ -197,6 +307,9 @@ static void add_repack_all_option(void) if (prune_expire) argv_array_pushf(&repack, "--unpack-unreachable=%s", prune_expire); } + + if (keep_pack) + for_each_string_list(keep_pack, keep_one_pack, NULL); } static void add_repack_incremental_option(void) @@ -219,9 +332,35 @@ static int need_to_gc(void) * we run "repack -A -d -l". Otherwise we tell the caller * there is no need. */ - if (too_many_packs()) - add_repack_all_option(); - else if (too_many_loose_objects()) + if (too_many_packs()) { + struct string_list keep_pack = STRING_LIST_INIT_NODUP; + + if (big_pack_threshold) { + find_base_packs(&keep_pack, big_pack_threshold); + if (keep_pack.nr >= gc_auto_pack_limit) { + big_pack_threshold = 0; + string_list_clear(&keep_pack, 0); + find_base_packs(&keep_pack, 0); + } + } else { + struct packed_git *p = find_base_packs(&keep_pack, 0); + uint64_t mem_have, mem_want; + + mem_have = total_ram(); + mem_want = estimate_repack_memory(p); + + /* + * Only allow 1/2 of memory for pack-objects, leave + * the rest for the OS and other processes in the + * system. + */ + if (!mem_have || mem_want < mem_have / 2) + string_list_clear(&keep_pack, 0); + } + + add_repack_all_option(&keep_pack); + string_list_clear(&keep_pack, 0); + } else if (too_many_loose_objects()) add_repack_incremental_option(); else return 0; @@ -234,7 +373,7 @@ static int need_to_gc(void) /* return NULL on success, else hostname running the gc */ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) { - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; char my_host[HOST_NAME_MAX + 1]; struct strbuf sb = STRBUF_INIT; struct stat st; @@ -354,6 +493,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) const char *name; pid_t pid; int daemonized = 0; + int keep_base_pack = -1; + timestamp_t dummy; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), @@ -366,6 +507,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) OPT_BOOL_F(0, "force", &force, N_("force running gc even if there may be another gc running"), PARSE_OPT_NOCOMPLETE), + OPT_BOOL(0, "keep-largest-pack", &keep_base_pack, + N_("repack all other packs except the largest pack")), OPT_END() }; @@ -382,7 +525,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) /* default expiry time, overwritten in gc_config */ gc_config(); if (parse_expiry_date(gc_log_expire, &gc_log_expire_time)) - die(_("Failed to parse gc.logexpiry value %s"), gc_log_expire); + die(_("failed to parse gc.logexpiry value %s"), gc_log_expire); if (pack_refs < 0) pack_refs = !is_bare_repository(); @@ -392,6 +535,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (argc > 0) usage_with_options(builtin_gc_usage, builtin_gc_options); + if (prune_expire && parse_expiry_date(prune_expire, &dummy)) + die(_("failed to parse prune expiry value %s"), prune_expire); + if (aggressive) { argv_array_push(&repack, "-f"); if (aggressive_depth > 0) @@ -431,8 +577,19 @@ int cmd_gc(int argc, const char **argv, const char *prefix) */ daemonized = !daemonize(); } - } else - add_repack_all_option(); + } else { + struct string_list keep_pack = STRING_LIST_INIT_NODUP; + + if (keep_base_pack != -1) { + if (keep_base_pack) + find_base_packs(&keep_pack, 0); + } else if (big_pack_threshold) { + find_base_packs(&keep_pack, big_pack_threshold); + } + + add_repack_all_option(&keep_pack); + string_list_clear(&keep_pack, 0); + } name = lock_repo_for_gc(force, &pid); if (name) { diff --git a/builtin/grep.c b/builtin/grep.c index 5f32d2ce84..ee753a403e 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -488,7 +488,8 @@ static int grep_cache(struct grep_opt *opt, struct repository *repo, strbuf_addstr(&name, repo->submodule_prefix); } - repo_read_index(repo); + if (repo_read_index(repo) < 0) + die("index file corrupt"); for (nr = 0; nr < repo->index->cache_nr; nr++) { const struct cache_entry *ce = repo->index->cache[nr]; @@ -602,8 +603,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, } static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, - struct object *obj, const char *name, const char *path, - struct repository *repo) + struct object *obj, const char *name, const char *path) { if (obj->type == OBJ_BLOB) return grep_oid(opt, &obj->oid, name, 0, path); @@ -630,7 +630,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, } init_tree_desc(&tree, data, size); hit = grep_tree(opt, pathspec, &tree, &base, base.len, - obj->type == OBJ_COMMIT, repo); + obj->type == OBJ_COMMIT, the_repository); strbuf_release(&base); free(data); return hit; @@ -639,7 +639,6 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, } static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, - struct repository *repo, const struct object_array *list) { unsigned int i; @@ -652,11 +651,11 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, /* load the gitmodules file for this rev */ if (recurse_submodules) { - submodule_free(); + submodule_free(the_repository); gitmodules_config_oid(&real_obj->oid); } - if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path, - repo)) { + if (grep_object(opt, pathspec, real_obj, list->objects[i].name, + list->objects[i].path)) { hit = 1; if (opt->status_only) break; @@ -887,9 +886,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("indicate hit with exit status without output")), OPT_BOOL(0, "all-match", &opt.all_match, N_("show only matches from files that match all patterns")), - { OPTION_SET_INT, 0, "debug", &opt.debug, NULL, - N_("show parse tree for grep expression"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 }, + OPT_SET_INT_F(0, "debug", &opt.debug, + N_("show parse tree for grep expression"), + 1, PARSE_OPT_HIDDEN), OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), @@ -1108,7 +1107,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (cached) die(_("both --cached and trees are given.")); - hit = grep_objects(&opt, &pathspec, the_repository, &list); + hit = grep_objects(&opt, &pathspec, &list); } if (num_threads) diff --git a/builtin/hash-object.c b/builtin/hash-object.c index d5c018eabd..9ada4f4dfd 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -10,7 +10,7 @@ #include "blob.h" #include "quote.h" #include "parse-options.h" -#include "exec_cmd.h" +#include "exec-cmd.h" /* * This is to create corrupt objects for debugging and as such it diff --git a/builtin/help.c b/builtin/help.c index 598867cfea..8d4f6dd301 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -4,11 +4,12 @@ #include "cache.h" #include "config.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #include "run-command.h" #include "column.h" #include "help.h" +#include "alias.h" #ifndef DEFAULT_HELP_FORMAT #define DEFAULT_HELP_FORMAT "man" @@ -36,6 +37,8 @@ static const char *html_path; static int show_all = 0; static int show_guides = 0; +static int show_config; +static int verbose; static unsigned int colopts; static enum help_format help_format = HELP_FORMAT_NONE; static int exclude_guides; @@ -43,11 +46,14 @@ static struct option builtin_help_options[] = { OPT_BOOL('a', "all", &show_all, N_("print all available commands")), OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")), OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")), + OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")), + OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN), OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN), OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"), HELP_FORMAT_WEB), OPT_SET_INT('i', "info", &help_format, N_("show info page"), HELP_FORMAT_INFO), + OPT__VERBOSE(&verbose, N_("print command description")), OPT_END(), }; @@ -400,38 +406,6 @@ static void show_html_page(const char *git_cmd) open_html(page_path.buf); } -static struct { - const char *name; - const char *help; -} common_guides[] = { - { "attributes", N_("Defining attributes per path") }, - { "everyday", N_("Everyday Git With 20 Commands Or So") }, - { "glossary", N_("A Git glossary") }, - { "ignore", N_("Specifies intentionally untracked files to ignore") }, - { "modules", N_("Defining submodule properties") }, - { "revisions", N_("Specifying revisions and ranges for Git") }, - { "tutorial", N_("A tutorial introduction to Git (for version 1.5.1 or newer)") }, - { "workflows", N_("An overview of recommended workflows with Git") }, -}; - -static void list_common_guides_help(void) -{ - int i, longest = 0; - - for (i = 0; i < ARRAY_SIZE(common_guides); i++) { - if (longest < strlen(common_guides[i].name)) - longest = strlen(common_guides[i].name); - } - - puts(_("The common Git guides are:\n")); - for (i = 0; i < ARRAY_SIZE(common_guides); i++) { - printf(" %s ", common_guides[i].name); - mput_char(' ', longest - strlen(common_guides[i].name)); - puts(_(common_guides[i].help)); - } - putchar('\n'); -} - static const char *check_git_cmd(const char* cmd) { char *alias; @@ -463,11 +437,29 @@ int cmd_help(int argc, const char **argv, const char *prefix) if (show_all) { git_config(git_help_config, NULL); + if (verbose) { + setup_pager(); + list_all_cmds_help(); + return 0; + } printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); load_command_list("git-", &main_cmds, &other_cmds); list_commands(colopts, &main_cmds, &other_cmds); } + if (show_config) { + int for_human = show_config == 1; + + if (!for_human) { + list_config_help(for_human); + return 0; + } + setup_pager(); + list_config_help(for_human); + printf("\n%s\n", _("'git help config' for more information")); + return 0; + } + if (show_guides) list_common_guides_help(); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 2d04a596f5..74fe2973e1 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -9,7 +9,7 @@ #include "tree.h" #include "progress.h" #include "fsck.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "streaming.h" #include "thread-utils.h" #include "packfile.h" @@ -837,6 +837,9 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, blob->object.flags |= FLAG_CHECKED; else die(_("invalid blob object %s"), oid_to_hex(oid)); + if (do_fsck_object && + fsck_object(&blob->object, (void *)data, size, &fsck_options)) + die(_("fsck error in packed object")); } else { struct object *obj; int eaten; @@ -854,7 +857,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, die(_("invalid %s"), type_name(type)); if (do_fsck_object && fsck_object(obj, buf, size, &fsck_options)) - die(_("Error in object")); + die(_("fsck error in packed object")); if (strict && fsck_walk(obj, NULL, &fsck_options)) die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid)); @@ -866,7 +869,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (obj->type == OBJ_COMMIT) { struct commit *commit = (struct commit *) obj; if (detach_commit_buffer(commit, NULL) != data) - die("BUG: parse_object_buffer transmogrified our buffer"); + BUG("parse_object_buffer transmogrified our buffer"); } obj->flags |= FLAG_CHECKED; } @@ -1015,7 +1018,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base, if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA, base->obj->real_type)) - die("BUG: child->real_type != OBJ_REF_DELTA"); + BUG("child->real_type != OBJ_REF_DELTA"); resolve_delta(child, base, result); if (base->ref_first == base->ref_last && base->ofs_last == -1) @@ -1271,7 +1274,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha nr_objects - nr_objects_initial); stop_progress_msg(&progress, msg.buf); strbuf_release(&msg); - hashclose(f, tail_hash, 0); + finalize_hashfile(f, tail_hash, 0); hashcpy(read_hash, pack_hash); fixup_pack_header_footer(output_fd, pack_hash, curr_pack, nr_objects, @@ -1479,6 +1482,13 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } else chmod(final_index_name, 0444); + if (do_fsck_object) { + struct packed_git *p; + p = add_packed_git(final_index_name, strlen(final_index_name), 0); + if (p) + install_packed_git(the_repository, p); + } + if (!from_stdin) { printf("%s\n", sha1_to_hex(hash)); } else { @@ -1543,12 +1553,13 @@ static void read_v2_anomalous_offsets(struct packed_git *p, { const uint32_t *idx1, *idx2; uint32_t i; + const uint32_t hashwords = the_hash_algo->rawsz / sizeof(uint32_t); /* The address of the 4-byte offset table */ idx1 = (((const uint32_t *)p->index_data) + 2 /* 8-byte header */ + 256 /* fan out */ - + 5 * p->num_objects /* 20-byte SHA-1 table */ + + hashwords * p->num_objects /* object ID table */ + p->num_objects /* CRC32 table */ ); @@ -1593,7 +1604,7 @@ static void read_idx_option(struct pack_idx_option *opts, const char *pack_name) /* * Get rid of the idx file as we do not need it anymore. * NEEDSWORK: extract this bit from free_pack_by_name() in - * sha1_file.c, perhaps? It shouldn't matter very much as we + * sha1-file.c, perhaps? It shouldn't matter very much as we * know we haven't installed this pack (hence we never have * read anything from it). */ @@ -1820,6 +1831,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) pack_hash); else close(input_fd); + + if (do_fsck_object && fsck_finish(&fsck_options)) + die(_("fsck error in pack objects")); + free(objects); strbuf_release(&index_name_buf); if (pack_name == NULL) diff --git a/builtin/init-db.c b/builtin/init-db.c index 68ff4ad75a..4ecf909368 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -7,7 +7,7 @@ #include "config.h" #include "refs.h" #include "builtin.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "parse-options.h" #ifndef DEFAULT_GIT_TEMPLATE_DIR @@ -117,7 +117,7 @@ static void copy_templates(const char *template_dir) dir = opendir(template_path.buf); if (!dir) { - warning(_("templates not found %s"), template_dir); + warning(_("templates not found in %s"), template_dir); goto free_return; } @@ -391,7 +391,7 @@ int init_db(const char *git_dir, const char *real_git_dir, else if (get_shared_repository() == PERM_EVERYBODY) xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY); else - die("BUG: invalid value for shared_repository"); + BUG("invalid value for shared_repository"); git_config_set("core.sharedrepository", buf); git_config_set("receive.denyNonFastforwards", "true"); } diff --git a/builtin/log.c b/builtin/log.c index 9656578f58..c77af79755 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -29,6 +29,7 @@ #include "mailmap.h" #include "gpg-interface.h" #include "progress.h" +#include "commit-slab.h" #define MAIL_DEFAULT_WRAP 72 @@ -149,6 +150,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; struct decoration_filter decoration_filter = {&decorate_refs_include, &decorate_refs_exclude}; + static struct revision_sources revision_sources; const struct option builtin_log_options[] = { OPT__QUIET(&quiet, N_("suppress diff output")), @@ -195,8 +197,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, rev->diffopt.filter || rev->diffopt.flags.follow_renames) rev->always_show_header = 0; - if (source) - rev->show_source = 1; + if (source) { + init_revision_sources(&revision_sources); + rev->sources = &revision_sources; + } if (mailmap) { rev->mailmap = xcalloc(1, sizeof(struct string_list)); @@ -1020,7 +1024,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet)) return; - log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte); + log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0); for (i = 0; !need_8bit_cte && i < nr; i++) { const char *buf = get_commit_buffer(list[i], NULL); @@ -1068,8 +1072,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, diff_setup_done(&opts); - diff_tree_oid(&origin->tree->object.oid, - &head->tree->object.oid, + diff_tree_oid(get_commit_tree_oid(origin), + get_commit_tree_oid(head), "", &opts); diffcore_std(&opts); diff_flush(&opts); @@ -1338,6 +1342,8 @@ static struct commit *get_base_commit(const char *base_commit, return base; } +define_commit_slab(commit_base, int); + static void prepare_bases(struct base_tree_info *bases, struct commit *base, struct commit **list, @@ -1346,11 +1352,13 @@ static void prepare_bases(struct base_tree_info *bases, struct commit *commit; struct rev_info revs; struct diff_options diffopt; + struct commit_base commit_base; int i; if (!base) return; + init_commit_base(&commit_base); diff_setup(&diffopt); diffopt.flags.recursive = 1; diff_setup_done(&diffopt); @@ -1363,7 +1371,7 @@ static void prepare_bases(struct base_tree_info *bases, for (i = 0; i < total; i++) { list[i]->object.flags &= ~UNINTERESTING; add_pending_object(&revs, &list[i]->object, "rev_list"); - list[i]->util = (void *)1; + *commit_base_at(&commit_base, list[i]) = 1; } base->object.flags |= UNINTERESTING; add_pending_object(&revs, &base->object, "base"); @@ -1377,7 +1385,7 @@ static void prepare_bases(struct base_tree_info *bases, while ((commit = get_revision(&revs)) != NULL) { struct object_id oid; struct object_id *patch_id; - if (commit->util) + if (*commit_base_at(&commit_base, commit)) continue; if (commit_patch_id(commit, &diffopt, &oid, 0)) die(_("cannot get patch id")); @@ -1386,6 +1394,7 @@ static void prepare_bases(struct base_tree_info *bases, oidcpy(patch_id, &oid); bases->nr_patch_id++; } + clear_commit_base(&commit_base); } static void print_bases(struct base_tree_info *bases, FILE *file) @@ -1475,9 +1484,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("output all-zero hash in From header")), OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream, N_("don't include a patch matching a commit upstream")), - { OPTION_SET_INT, 'p', "no-stat", &use_patch_format, NULL, - N_("show patch format instead of default (patch + stat)"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1}, + OPT_SET_INT_F('p', "no-stat", &use_patch_format, + N_("show patch format instead of default (patch + stat)"), + 1, PARSE_OPT_NONEG), OPT_GROUP(N_("Messaging")), { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"), N_("add email header"), 0, header_callback }, diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a71f6bd088..88bb2019ad 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -166,7 +166,7 @@ static void show_killed_files(const struct index_state *istate, */ pos = index_name_pos(istate, ent->name, ent->len); if (0 <= pos) - die("BUG: killed-file %.*s not found", + BUG("killed-file %.*s not found", ent->len, ent->name); pos = -pos - 1; while (pos < istate->cache_nr && @@ -556,9 +556,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL, N_("add the standard git exclusions"), PARSE_OPT_NOARG, option_parse_exclude_standard }, - { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL, - N_("make the output relative to the project top directory"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL }, + OPT_SET_INT_F(0, "full-name", &prefix_len, + N_("make the output relative to the project top directory"), + 0, PARSE_OPT_NONEG), OPT_BOOL(0, "recurse-submodules", &recurse_submodules, N_("recurse through submodules")), OPT_BOOL(0, "error-unmatch", &error_unmatch, diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 540d56429f..1a25df7ee1 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -1,7 +1,9 @@ #include "builtin.h" #include "cache.h" #include "transport.h" +#include "ref-filter.h" #include "remote.h" +#include "refs.h" static const char * const ls_remote_usage[] = { N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n" @@ -43,10 +45,15 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) int show_symref_target = 0; const char *uploadpack = NULL; const char **pattern = NULL; + struct argv_array ref_prefixes = ARGV_ARRAY_INIT; + int i; + struct string_list server_options = STRING_LIST_INIT_DUP; struct remote *remote; struct transport *transport; const struct ref *ref; + struct ref_array ref_array; + static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; struct option options[] = { OPT__QUIET(&quiet, N_("do not print remote URL")), @@ -60,14 +67,19 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) 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_CALLBACK(0 , "sort", sorting_tail, N_("key"), + N_("field name to sort on"), &parse_opt_ref_sorting), OPT_SET_INT_F(0, "exit-code", &status, N_("exit with exit code 2 if no matching refs are found"), 2, PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "symref", &show_symref_target, N_("show underlying ref in addition to the object pointed by it")), + OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), OPT_END() }; + memset(&ref_array, 0, sizeof(ref_array)); + argc = parse_options(argc, argv, prefix, options, ls_remote_usage, PARSE_OPT_STOP_AT_NON_OPTION); dest = argv[0]; @@ -75,8 +87,17 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (argc > 1) { int i; pattern = xcalloc(argc, sizeof(const char *)); - for (i = 1; i < argc; i++) + for (i = 1; i < argc; i++) { + const char *glob; pattern[i - 1] = xstrfmt("*/%s", argv[i]); + + glob = strchr(argv[i], '*'); + if (glob) + argv_array_pushf(&ref_prefixes, "%.*s", + (int)(glob - argv[i]), argv[i]); + else + expand_ref_prefix(&ref_prefixes, argv[i]); + } } remote = remote_get(dest); @@ -90,28 +111,46 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (get_url) { printf("%s\n", *remote->url); + UNLEAK(sorting); return 0; } transport = transport_get(remote, NULL); if (uploadpack != NULL) transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); + if (server_options.nr) + transport->server_options = &server_options; - ref = transport_get_remote_refs(transport); - if (transport_disconnect(transport)) + ref = transport_get_remote_refs(transport, &ref_prefixes); + if (transport_disconnect(transport)) { + UNLEAK(sorting); return 1; + } if (!dest && !quiet) fprintf(stderr, "From %s\n", *remote->url); for ( ; ref; ref = ref->next) { + struct ref_array_item *item; if (!check_ref_type(ref, flags)) continue; if (!tail_match(pattern, ref->name)) continue; + item = ref_array_push(&ref_array, ref->name, &ref->old_oid); + item->symref = xstrdup_or_null(ref->symref); + } + + if (sorting) + ref_array_sort(sorting, &ref_array); + + for (i = 0; i < ref_array.nr; i++) { + const struct ref_array_item *ref = ref_array.items[i]; 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); + printf("ref: %s\t%s\n", ref->symref, ref->refname); + printf("%s\t%s\n", oid_to_hex(&ref->objectname), ref->refname); status = 0; /* we found something */ } + + UNLEAK(sorting); + UNLEAK(ref_array); return status; } diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 1b702d44c9..8a8d579752 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -3,7 +3,7 @@ #include "xdiff-interface.h" #include "object-store.h" #include "blob.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "merge-blobs.h" static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>"; diff --git a/builtin/merge.c b/builtin/merge.c index e9057c1657..d1b547d973 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -14,6 +14,7 @@ #include "run-command.h" #include "diff.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "diffcore.h" #include "revision.h" @@ -34,6 +35,7 @@ #include "string-list.h" #include "packfile.h" #include "tag.h" +#include "alias.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -213,9 +215,9 @@ static struct option builtin_merge_options[] = { OPT_BOOL('e', "edit", &option_edit, N_("edit message before committing")), OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW), - { OPTION_SET_INT, 0, "ff-only", &fast_forward, NULL, - N_("abort if fast-forward is not possible"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, FF_ONLY }, + OPT_SET_INT_F(0, "ff-only", &fast_forward, + N_("abort if fast-forward is not possible"), + FF_ONLY, PARSE_OPT_NONEG), OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_BOOL(0, "verify-signatures", &verify_signatures, N_("verify that the named commit has a valid GPG signature")), @@ -280,7 +282,7 @@ out: return rc; } -static void read_empty(unsigned const char *sha1, int verbose) +static void read_empty(const struct object_id *oid, int verbose) { int i = 0; const char *args[7]; @@ -290,15 +292,15 @@ static void read_empty(unsigned const char *sha1, int verbose) args[i++] = "-v"; args[i++] = "-m"; args[i++] = "-u"; - args[i++] = EMPTY_TREE_SHA1_HEX; - args[i++] = sha1_to_hex(sha1); + args[i++] = empty_tree_oid_hex(); + args[i++] = oid_to_hex(oid); args[i] = NULL; if (run_command_v_opt(args, RUN_GIT_CMD)) die(_("read-tree failed")); } -static void reset_hard(unsigned const char *sha1, int verbose) +static void reset_hard(const struct object_id *oid, int verbose) { int i = 0; const char *args[6]; @@ -308,7 +310,7 @@ static void reset_hard(unsigned const char *sha1, int verbose) args[i++] = "-v"; args[i++] = "--reset"; args[i++] = "-u"; - args[i++] = sha1_to_hex(sha1); + args[i++] = oid_to_hex(oid); args[i] = NULL; if (run_command_v_opt(args, RUN_GIT_CMD)) @@ -324,7 +326,7 @@ static void restore_state(const struct object_id *head, if (is_null_oid(stash)) return; - reset_hard(head->hash, 1); + reset_hard(head, 1); args[2] = oid_to_hex(stash); @@ -443,6 +445,7 @@ static void merge_name(const char *remote, struct strbuf *msg) struct object_id branch_head; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; + struct merge_remote_desc *desc; const char *ptr; char *found_ref; int len, early; @@ -515,16 +518,13 @@ static void merge_name(const char *remote, struct strbuf *msg) strbuf_release(&truname); } - if (remote_head->util) { - struct merge_remote_desc *desc; - desc = merge_remote_util(remote_head); - if (desc && desc->obj && desc->obj->type == OBJ_TAG) { - strbuf_addf(msg, "%s\t\t%s '%s'\n", - oid_to_hex(&desc->obj->oid), - type_name(desc->obj->type), - remote); - goto cleanup; - } + desc = merge_remote_util(remote_head); + if (desc && desc->obj && desc->obj->type == OBJ_TAG) { + strbuf_addf(msg, "%s\t\t%s '%s'\n", + oid_to_hex(&desc->obj->oid), + type_name(desc->obj->type), + remote); + goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", @@ -647,7 +647,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, struct commit_list *remoteheads, struct commit *head) { - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; const char *head_arg = "HEAD"; hold_locked_index(&lock, LOCK_DIE_ON_ERROR); @@ -805,7 +805,7 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) { struct object_id result_tree, result_commit; struct commit_list *parents, **pptr = &parents; - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; hold_locked_index(&lock, LOCK_DIE_ON_ERROR); refresh_cache(REFRESH_QUIET); @@ -932,8 +932,11 @@ static void write_merge_heads(struct commit_list *remoteheads) for (j = remoteheads; j; j = j->next) { struct object_id *oid; struct commit *c = j->item; - if (c->util && merge_remote_util(c)->obj) { - oid = &merge_remote_util(c)->obj->oid; + struct merge_remote_desc *desc; + + desc = merge_remote_util(c); + if (desc && desc->obj) { + oid = &desc->obj->oid; } else { oid = &c->object.oid; } @@ -1184,14 +1187,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix) branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL); if (branch) skip_prefix(branch, "refs/heads/", &branch); + + init_diff_ui_defaults(); + git_config(git_merge_config, NULL); + if (!branch || is_null_oid(&head_oid)) head_commit = NULL; else head_commit = lookup_commit_or_die(&head_oid, "HEAD"); - init_diff_ui_defaults(); - git_config(git_merge_config, NULL); - if (branch_mergeoptions) parse_branch_merge_options(branch_mergeoptions); argc = parse_options(argc, argv, prefix, builtin_merge_options, @@ -1298,7 +1302,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (remoteheads->next) die(_("Can merge only exactly one commit into empty head")); remote_head_oid = &remoteheads->item->object.oid; - read_empty(remote_head_oid->hash, 0); + read_empty(remote_head_oid, 0); update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; diff --git a/builtin/mv.c b/builtin/mv.c index 6d141f7a53..80bb967a63 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -72,7 +72,6 @@ static const char *add_slash(const char *path) return path; } -static struct lock_file lock_file; #define SUBMODULE_WITH_GITDIR ((const char *)1) static void prepare_move_submodule(const char *src, int first, @@ -131,6 +130,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; + struct lock_file lock_file = LOCK_INIT; git_config(git_default_config, NULL); @@ -276,10 +276,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix) 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 (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) + connect_work_tree_and_git_dir(dst, + submodule_gitfile[i], + 1); } if (mode == WORKING_DIRECTORY) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 387ddf85d2..0eb440359d 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -6,6 +6,7 @@ #include "refs.h" #include "parse-options.h" #include "sha1-lookup.h" +#include "commit-slab.h" #define CUTOFF_DATE_SLOP 86400 /* one day */ @@ -17,11 +18,26 @@ typedef struct rev_name { int from_tag; } rev_name; +define_commit_slab(commit_rev_name, struct rev_name *); + static timestamp_t cutoff = TIME_MAX; +static struct commit_rev_name rev_names; /* How many generations are maximally preferred over _one_ merge traversal? */ #define MERGE_TRAVERSAL_WEIGHT 65535 +static struct rev_name *get_commit_rev_name(struct commit *commit) +{ + struct rev_name **slot = commit_rev_name_peek(&rev_names, commit); + + return slot ? *slot : NULL; +} + +static void set_commit_rev_name(struct commit *commit, struct rev_name *name) +{ + *commit_rev_name_at(&rev_names, commit) = name; +} + static int is_better_name(struct rev_name *name, const char *tip_name, timestamp_t taggerdate, @@ -65,7 +81,7 @@ static void name_rev(struct commit *commit, int generation, int distance, int from_tag, int deref) { - struct rev_name *name = (struct rev_name *)commit->util; + struct rev_name *name = get_commit_rev_name(commit); struct commit_list *parents; int parent_number = 1; char *to_free = NULL; @@ -84,7 +100,7 @@ static void name_rev(struct commit *commit, if (name == NULL) { name = xmalloc(sizeof(rev_name)); - commit->util = name; + set_commit_rev_name(commit, name); goto copy_data; } else if (is_better_name(name, tip_name, taggerdate, generation, distance, from_tag)) { @@ -296,7 +312,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf) if (o->type != OBJ_COMMIT) return get_exact_ref_match(o); c = (struct commit *) o; - n = c->util; + n = get_commit_rev_name(c); if (!n) return NULL; @@ -413,6 +429,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_END(), }; + init_commit_rev_name(&rev_names); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); if (all + transform_stdin + !!argc > 1) { diff --git a/builtin/notes.c b/builtin/notes.c index 73b680ee13..a0a1840040 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -15,7 +15,7 @@ #include "blob.h" #include "pretty.h" #include "refs.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "parse-options.h" #include "string-list.h" @@ -462,7 +462,7 @@ static int add(int argc, const char **argv, const char *prefix) if (d.buf.len || allow_empty) { write_note_data(&d, &new_note); if (add_note(t, &object, &new_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes add'"); } else { fprintf(stderr, _("Removing note for object %s\n"), @@ -545,7 +545,7 @@ static int copy(int argc, const char **argv, const char *prefix) } if (add_note(t, &object, from_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes copy'"); out: free_notes(t); @@ -622,7 +622,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) if (d.buf.len || allow_empty) { write_note_data(&d, &new_note); if (add_note(t, &object, &new_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]); } else { fprintf(stderr, _("Removing note for object %s\n"), @@ -779,13 +779,13 @@ static int merge(int argc, const char **argv, const char *prefix) N_("resolve notes conflicts using the given strategy " "(manual/ours/theirs/union/cat_sort_uniq)")), OPT_GROUP(N_("Committing unmerged notes")), - { OPTION_SET_INT, 0, "commit", &do_commit, NULL, - N_("finalize notes merge by committing unmerged notes"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1}, + OPT_SET_INT_F(0, "commit", &do_commit, + N_("finalize notes merge by committing unmerged notes"), + 1, PARSE_OPT_NONEG), OPT_GROUP(N_("Aborting notes merge resolution")), - { OPTION_SET_INT, 0, "abort", &do_abort, NULL, - N_("abort notes merge"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1}, + OPT_SET_INT_F(0, "abort", &do_abort, + N_("abort notes merge"), + 1, PARSE_OPT_NONEG), OPT_END() }; @@ -832,7 +832,7 @@ static int merge(int argc, const char **argv, const char *prefix) const char *short_ref = NULL; if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref)) - die("BUG: local ref %s is outside of refs/notes/", + BUG("local ref %s is outside of refs/notes/", o.local_ref); strbuf_addf(&merge_key, "notes.%s.mergeStrategy", short_ref); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0f1eec2eec..69d3d7b82a 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -30,6 +30,19 @@ #include "list.h" #include "packfile.h" #include "object-store.h" +#include "dir.h" + +#define IN_PACK(obj) oe_in_pack(&to_pack, obj) +#define SIZE(obj) oe_size(&to_pack, obj) +#define SET_SIZE(obj,size) oe_set_size(&to_pack, obj, size) +#define DELTA_SIZE(obj) oe_delta_size(&to_pack, obj) +#define DELTA(obj) oe_delta(&to_pack, obj) +#define DELTA_CHILD(obj) oe_delta_child(&to_pack, obj) +#define DELTA_SIBLING(obj) oe_delta_sibling(&to_pack, obj) +#define SET_DELTA(obj, val) oe_set_delta(&to_pack, obj, val) +#define SET_DELTA_SIZE(obj, val) oe_set_delta_size(&to_pack, obj, val) +#define SET_DELTA_CHILD(obj, val) oe_set_delta_child(&to_pack, obj, val) +#define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val) static const char *pack_usage[] = { N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"), @@ -45,7 +58,7 @@ static const char *pack_usage[] = { static struct packing_data to_pack; static struct pack_idx_entry **written_list; -static uint32_t nr_result, nr_written; +static uint32_t nr_result, nr_written, nr_seen; static int non_empty; static int reuse_delta = 1, reuse_object = 1; @@ -55,7 +68,8 @@ static int pack_loose_unreachable; static int local; static int have_non_local_packs; static int incremental; -static int ignore_packed_keep; +static int ignore_packed_keep_on_disk; +static int ignore_packed_keep_in_core; static int allow_ofs_delta; static struct pack_idx_option pack_idx_opts; static const char *base_name; @@ -80,7 +94,7 @@ static uint16_t write_bitmap_options; static int exclude_promisor_objects; static unsigned long delta_cache_size = 0; -static unsigned long max_delta_cache_size = 256 * 1024 * 1024; +static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; static unsigned long cache_max_small_delta_size = 1000; static unsigned long window_memory_limit = 0; @@ -127,13 +141,14 @@ static void *get_delta(struct object_entry *entry) buf = read_object_file(&entry->idx.oid, &type, &size); if (!buf) die("unable to read %s", oid_to_hex(&entry->idx.oid)); - base_buf = read_object_file(&entry->delta->idx.oid, &type, &base_size); + base_buf = read_object_file(&DELTA(entry)->idx.oid, &type, + &base_size); if (!base_buf) die("unable to read %s", - oid_to_hex(&entry->delta->idx.oid)); + oid_to_hex(&DELTA(entry)->idx.oid)); delta_buf = diff_delta(base_buf, base_size, buf, size, &delta_size, 0); - if (!delta_buf || delta_size != entry->delta_size) + if (!delta_buf || delta_size != DELTA_SIZE(entry)) die("delta size changed"); free(buf); free(base_buf); @@ -264,10 +279,11 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent enum object_type type; void *buf; struct git_istream *st = NULL; + const unsigned hashsz = the_hash_algo->rawsz; if (!usable_delta) { - if (entry->type == OBJ_BLOB && - entry->size > big_file_threshold && + if (oe_type(entry) == OBJ_BLOB && + oe_size_greater_than(&to_pack, entry, big_file_threshold) && (st = open_istream(&entry->idx.oid, &type, &size, NULL)) != NULL) buf = NULL; else { @@ -283,15 +299,15 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent FREE_AND_NULL(entry->delta_data); entry->z_delta_size = 0; } else if (entry->delta_data) { - size = entry->delta_size; + size = DELTA_SIZE(entry); buf = entry->delta_data; entry->delta_data = NULL; - type = (allow_ofs_delta && entry->delta->idx.offset) ? + type = (allow_ofs_delta && DELTA(entry)->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } else { buf = get_delta(entry); - size = entry->delta_size; - type = (allow_ofs_delta && entry->delta->idx.offset) ? + size = DELTA_SIZE(entry); + type = (allow_ofs_delta && DELTA(entry)->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } @@ -315,12 +331,12 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent * encoding of the relative offset for the delta * base from this object's position in the pack. */ - off_t ofs = entry->idx.offset - entry->delta->idx.offset; + off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset; unsigned pos = sizeof(dheader) - 1; dheader[pos] = ofs & 127; while (ofs >>= 7) dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { + if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) { if (st) close_istream(st); free(buf); @@ -332,19 +348,19 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent } else if (type == OBJ_REF_DELTA) { /* * Deltas with a base reference contain - * an additional 20 bytes for the base sha1. + * additional bytes for the base object ID. */ - if (limit && hdrlen + 20 + datalen + 20 >= limit) { + if (limit && hdrlen + hashsz + datalen + hashsz >= limit) { if (st) close_istream(st); free(buf); return 0; } hashwrite(f, header, hdrlen); - hashwrite(f, entry->delta->idx.oid.hash, 20); - hdrlen += 20; + hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz); + hdrlen += hashsz; } else { - if (limit && hdrlen + datalen + 20 >= limit) { + if (limit && hdrlen + datalen + hashsz >= limit) { if (st) close_istream(st); free(buf); @@ -367,21 +383,23 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, unsigned long limit, int usable_delta) { - struct packed_git *p = entry->in_pack; + struct packed_git *p = IN_PACK(entry); struct pack_window *w_curs = NULL; struct revindex_entry *revidx; off_t offset; - enum object_type type = entry->type; + enum object_type type = oe_type(entry); off_t datalen; unsigned char header[MAX_PACK_OBJECT_HEADER], dheader[MAX_PACK_OBJECT_HEADER]; unsigned hdrlen; + const unsigned hashsz = the_hash_algo->rawsz; + unsigned long entry_size = SIZE(entry); - if (entry->delta) - type = (allow_ofs_delta && entry->delta->idx.offset) ? + if (DELTA(entry)) + type = (allow_ofs_delta && DELTA(entry)->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; hdrlen = encode_in_pack_object_header(header, sizeof(header), - type, entry->size); + type, entry_size); offset = entry->in_pack_offset; revidx = find_pack_revindex(p, offset); @@ -398,7 +416,7 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, datalen -= entry->in_pack_header_size; if (!pack_to_stdout && p->index_version == 1 && - check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) { + check_pack_inflate(p, &w_curs, offset, datalen, entry_size)) { error("corrupt packed object for %s", oid_to_hex(&entry->idx.oid)); unuse_pack(&w_curs); @@ -406,12 +424,12 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, } if (type == OBJ_OFS_DELTA) { - off_t ofs = entry->idx.offset - entry->delta->idx.offset; + off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset; unsigned pos = sizeof(dheader) - 1; dheader[pos] = ofs & 127; while (ofs >>= 7) dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { + if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) { unuse_pack(&w_curs); return 0; } @@ -420,16 +438,16 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, hdrlen += sizeof(dheader) - pos; reused_delta++; } else if (type == OBJ_REF_DELTA) { - if (limit && hdrlen + 20 + datalen + 20 >= limit) { + if (limit && hdrlen + hashsz + datalen + hashsz >= limit) { unuse_pack(&w_curs); return 0; } hashwrite(f, header, hdrlen); - hashwrite(f, entry->delta->idx.oid.hash, 20); - hdrlen += 20; + hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz); + hdrlen += hashsz; reused_delta++; } else { - if (limit && hdrlen + datalen + 20 >= limit) { + if (limit && hdrlen + datalen + hashsz >= limit) { unuse_pack(&w_curs); return 0; } @@ -465,28 +483,29 @@ static off_t write_object(struct hashfile *f, else limit = pack_size_limit - write_offset; - if (!entry->delta) + if (!DELTA(entry)) usable_delta = 0; /* no delta */ else if (!pack_size_limit) usable_delta = 1; /* unlimited packfile */ - else if (entry->delta->idx.offset == (off_t)-1) + else if (DELTA(entry)->idx.offset == (off_t)-1) usable_delta = 0; /* base was written to another pack */ - else if (entry->delta->idx.offset) + else if (DELTA(entry)->idx.offset) usable_delta = 1; /* base already exists in this pack */ else usable_delta = 0; /* base could end up in another pack */ if (!reuse_object) to_reuse = 0; /* explicit */ - else if (!entry->in_pack) + else if (!IN_PACK(entry)) to_reuse = 0; /* can't reuse what we don't have */ - else if (entry->type == OBJ_REF_DELTA || entry->type == OBJ_OFS_DELTA) + else if (oe_type(entry) == OBJ_REF_DELTA || + oe_type(entry) == OBJ_OFS_DELTA) /* check_object() decided it for us ... */ to_reuse = usable_delta; /* ... but pack split may override that */ - else if (entry->type != entry->in_pack_type) + else if (oe_type(entry) != entry->in_pack_type) to_reuse = 0; /* pack has delta which is unusable */ - else if (entry->delta) + else if (DELTA(entry)) to_reuse = 0; /* we want to pack afresh */ else to_reuse = 1; /* we have it in-pack undeltified, @@ -538,12 +557,12 @@ static enum write_one_status write_one(struct hashfile *f, } /* if we are deltified, write out base object first. */ - if (e->delta) { + if (DELTA(e)) { e->idx.offset = 1; /* now recurse */ - switch (write_one(f, e->delta, offset)) { + switch (write_one(f, DELTA(e), offset)) { case WRITE_ONE_RECURSIVE: /* we cannot depend on this one */ - e->delta = NULL; + SET_DELTA(e, NULL); break; default: break; @@ -605,34 +624,34 @@ static void add_descendants_to_write_order(struct object_entry **wo, /* add this node... */ add_to_write_order(wo, endp, e); /* all its siblings... */ - for (s = e->delta_sibling; s; s = s->delta_sibling) { + for (s = DELTA_SIBLING(e); s; s = DELTA_SIBLING(s)) { add_to_write_order(wo, endp, s); } } /* drop down a level to add left subtree nodes if possible */ - if (e->delta_child) { + if (DELTA_CHILD(e)) { add_to_order = 1; - e = e->delta_child; + e = DELTA_CHILD(e); } else { add_to_order = 0; /* our sibling might have some children, it is next */ - if (e->delta_sibling) { - e = e->delta_sibling; + if (DELTA_SIBLING(e)) { + e = DELTA_SIBLING(e); continue; } /* go back to our parent node */ - e = e->delta; - while (e && !e->delta_sibling) { + e = DELTA(e); + while (e && !DELTA_SIBLING(e)) { /* we're on the right side of a subtree, keep * going up until we can go right again */ - e = e->delta; + e = DELTA(e); } if (!e) { /* done- we hit our original root node */ return; } /* pass it off to sibling at this level */ - e = e->delta_sibling; + e = DELTA_SIBLING(e); } }; } @@ -643,7 +662,7 @@ static void add_family_to_write_order(struct object_entry **wo, { struct object_entry *root; - for (root = e; root->delta; root = root->delta) + for (root = e; DELTA(root); root = DELTA(root)) ; /* nothing */ add_descendants_to_write_order(wo, endp, root); } @@ -658,8 +677,8 @@ static struct object_entry **compute_write_order(void) for (i = 0; i < to_pack.nr_objects; i++) { objects[i].tagged = 0; objects[i].filled = 0; - objects[i].delta_child = NULL; - objects[i].delta_sibling = NULL; + SET_DELTA_CHILD(&objects[i], NULL); + SET_DELTA_SIBLING(&objects[i], NULL); } /* @@ -669,11 +688,11 @@ static struct object_entry **compute_write_order(void) */ for (i = to_pack.nr_objects; i > 0;) { struct object_entry *e = &objects[--i]; - if (!e->delta) + if (!DELTA(e)) continue; /* Mark me as the first child */ - e->delta_sibling = e->delta->delta_child; - e->delta->delta_child = e; + e->delta_sibling_idx = DELTA(e)->delta_child_idx; + SET_DELTA_CHILD(DELTA(e), e); } /* @@ -705,8 +724,8 @@ static struct object_entry **compute_write_order(void) * And then all remaining commits and tags. */ for (i = last_untagged; i < to_pack.nr_objects; i++) { - if (objects[i].type != OBJ_COMMIT && - objects[i].type != OBJ_TAG) + if (oe_type(&objects[i]) != OBJ_COMMIT && + oe_type(&objects[i]) != OBJ_TAG) continue; add_to_write_order(wo, &wo_end, &objects[i]); } @@ -715,7 +734,7 @@ static struct object_entry **compute_write_order(void) * And then all the trees. */ for (i = last_untagged; i < to_pack.nr_objects; i++) { - if (objects[i].type != OBJ_TREE) + if (oe_type(&objects[i]) != OBJ_TREE) continue; add_to_write_order(wo, &wo_end, &objects[i]); } @@ -752,7 +771,7 @@ static off_t write_reused_pack(struct hashfile *f) die_errno("unable to seek in reused packfile"); if (reuse_packfile_offset < 0) - reuse_packfile_offset = reuse_packfile->pack_size - 20; + reuse_packfile_offset = reuse_packfile->pack_size - the_hash_algo->rawsz; total = to_write = reuse_packfile_offset - sizeof(struct pack_header); @@ -837,11 +856,11 @@ static void write_pack_file(void) * If so, rewrite it like in fast-import */ if (pack_to_stdout) { - hashclose(f, oid.hash, CSUM_CLOSE); + finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE); } else if (nr_written == nr_remaining) { - hashclose(f, oid.hash, CSUM_FSYNC); + finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE); } else { - int fd = hashclose(f, oid.hash, 0); + int fd = finalize_hashfile(f, oid.hash, 0); fixup_pack_header_footer(fd, oid.hash, pack_tmp_name, nr_written, oid.hash, offset); close(fd); @@ -878,7 +897,8 @@ static void write_pack_file(void) if (write_bitmap_index) { bitmap_writer_set_checksum(oid.hash); - bitmap_writer_build_type_index(written_list, nr_written); + bitmap_writer_build_type_index( + &to_pack, written_list, nr_written); } finish_tmp_packfile(&tmpname, pack_tmp_name, @@ -982,13 +1002,16 @@ static int want_found_object(int exclude, struct packed_git *p) * Otherwise, we signal "-1" at the end to tell the caller that we do * not know either way, and it needs to check more packs. */ - if (!ignore_packed_keep && + if (!ignore_packed_keep_on_disk && + !ignore_packed_keep_in_core && (!local || !have_non_local_packs)) return 1; if (local && !p->pack_local) return 0; - if (ignore_packed_keep && p->pack_local && p->pack_keep) + if (p->pack_local && + ((ignore_packed_keep_on_disk && p->pack_keep) || + (ignore_packed_keep_in_core && p->pack_keep_in_core))) return 0; /* we don't know yet; keep looking for more packs */ @@ -1012,7 +1035,7 @@ static int want_object_in_pack(const struct object_id *oid, int want; struct list_head *pos; - if (!exclude && local && has_loose_object_nonlocal(oid->hash)) + if (!exclude && local && has_loose_object_nonlocal(oid)) return 0; /* @@ -1066,14 +1089,13 @@ static void create_object_entry(const struct object_id *oid, entry = packlist_alloc(&to_pack, oid->hash, index_pos); entry->hash = hash; - if (type) - entry->type = type; + oe_set_type(entry, type); if (exclude) entry->preferred_base = 1; else nr_result++; if (found_pack) { - entry->in_pack = found_pack; + oe_set_in_pack(&to_pack, entry, found_pack); entry->in_pack_offset = found_offset; } @@ -1091,6 +1113,8 @@ static int add_object_entry(const struct object_id *oid, enum object_type type, off_t found_offset = 0; uint32_t index_pos; + display_progress(progress_state, ++nr_seen); + if (have_duplicate_entry(oid, exclude, &index_pos)) return 0; @@ -1106,8 +1130,6 @@ static int add_object_entry(const struct object_id *oid, enum object_type type, create_object_entry(oid, type, pack_name_hash(name), exclude, name && no_try_delta(name), index_pos, found_pack, found_offset); - - display_progress(progress_state, nr_result); return 1; } @@ -1118,6 +1140,8 @@ static int add_object_entry_from_bitmap(const struct object_id *oid, { uint32_t index_pos; + display_progress(progress_state, ++nr_seen); + if (have_duplicate_entry(oid, 0, &index_pos)) return 0; @@ -1125,8 +1149,6 @@ static int add_object_entry_from_bitmap(const struct object_id *oid, return 0; create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset); - - display_progress(progress_state, nr_result); return 1; } @@ -1398,8 +1420,10 @@ static void cleanup_preferred_base(void) static void check_object(struct object_entry *entry) { - if (entry->in_pack) { - struct packed_git *p = entry->in_pack; + unsigned long canonical_size; + + if (IN_PACK(entry)) { + struct packed_git *p = IN_PACK(entry); struct pack_window *w_curs = NULL; const unsigned char *base_ref = NULL; struct object_entry *base_entry; @@ -1407,6 +1431,8 @@ static void check_object(struct object_entry *entry) unsigned long avail; off_t ofs; unsigned char *buf, c; + enum object_type type; + unsigned long in_pack_size; buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail); @@ -1415,11 +1441,15 @@ static void check_object(struct object_entry *entry) * since non-delta representations could still be reused. */ used = unpack_object_header_buffer(buf, avail, - &entry->in_pack_type, - &entry->size); + &type, + &in_pack_size); if (used == 0) goto give_up; + if (type < 0) + BUG("invalid type %d", type); + entry->in_pack_type = type; + /* * Determine if this is a delta and if so whether we can * reuse it or not. Otherwise let's find out as cheaply as @@ -1428,9 +1458,10 @@ static void check_object(struct object_entry *entry) switch (entry->in_pack_type) { default: /* Not a delta hence we've already got all we need. */ - entry->type = entry->in_pack_type; + oe_set_type(entry, entry->in_pack_type); + SET_SIZE(entry, in_pack_size); entry->in_pack_header_size = used; - if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB) + if (oe_type(entry) < OBJ_COMMIT || oe_type(entry) > OBJ_BLOB) goto give_up; unuse_pack(&w_curs); return; @@ -1438,7 +1469,7 @@ static void check_object(struct object_entry *entry) if (reuse_delta && !entry->preferred_base) base_ref = use_pack(p, &w_curs, entry->in_pack_offset + used, NULL); - entry->in_pack_header_size = used + 20; + entry->in_pack_header_size = used + the_hash_algo->rawsz; break; case OBJ_OFS_DELTA: buf = use_pack(p, &w_curs, @@ -1484,25 +1515,29 @@ static void check_object(struct object_entry *entry) * deltify other objects against, in order to avoid * circular deltas. */ - entry->type = entry->in_pack_type; - entry->delta = base_entry; - entry->delta_size = entry->size; - entry->delta_sibling = base_entry->delta_child; - base_entry->delta_child = entry; + oe_set_type(entry, entry->in_pack_type); + SET_SIZE(entry, in_pack_size); /* delta size */ + SET_DELTA(entry, base_entry); + SET_DELTA_SIZE(entry, in_pack_size); + entry->delta_sibling_idx = base_entry->delta_child_idx; + SET_DELTA_CHILD(base_entry, entry); unuse_pack(&w_curs); return; } - if (entry->type) { + if (oe_type(entry)) { + off_t delta_pos; + /* * This must be a delta and we already know what the * final object type is. Let's extract the actual * object size from the delta header. */ - entry->size = get_size_from_delta(p, &w_curs, - entry->in_pack_offset + entry->in_pack_header_size); - if (entry->size == 0) + delta_pos = entry->in_pack_offset + entry->in_pack_header_size; + canonical_size = get_size_from_delta(p, &w_curs, delta_pos); + if (canonical_size == 0) goto give_up; + SET_SIZE(entry, canonical_size); unuse_pack(&w_curs); return; } @@ -1516,28 +1551,34 @@ static void check_object(struct object_entry *entry) unuse_pack(&w_curs); } - entry->type = oid_object_info(the_repository, &entry->idx.oid, - &entry->size); - /* - * The error condition is checked in prepare_pack(). This is - * to permit a missing preferred base object to be ignored - * as a preferred base. Doing so can result in a larger - * pack file, but the transfer will still take place. - */ + oe_set_type(entry, + oid_object_info(the_repository, &entry->idx.oid, &canonical_size)); + if (entry->type_valid) { + SET_SIZE(entry, canonical_size); + } else { + /* + * Bad object type is checked in prepare_pack(). This is + * to permit a missing preferred base object to be ignored + * as a preferred base. Doing so can result in a larger + * pack file, but the transfer will still take place. + */ + } } static int pack_offset_sort(const void *_a, const void *_b) { const struct object_entry *a = *(struct object_entry **)_a; const struct object_entry *b = *(struct object_entry **)_b; + const struct packed_git *a_in_pack = IN_PACK(a); + const struct packed_git *b_in_pack = IN_PACK(b); /* avoid filesystem trashing with loose objects */ - if (!a->in_pack && !b->in_pack) + if (!a_in_pack && !b_in_pack) return oidcmp(&a->idx.oid, &b->idx.oid); - if (a->in_pack < b->in_pack) + if (a_in_pack < b_in_pack) return -1; - if (a->in_pack > b->in_pack) + if (a_in_pack > b_in_pack) return 1; return a->in_pack_offset < b->in_pack_offset ? -1 : (a->in_pack_offset > b->in_pack_offset); @@ -1558,31 +1599,37 @@ static int pack_offset_sort(const void *_a, const void *_b) */ static void drop_reused_delta(struct object_entry *entry) { - struct object_entry **p = &entry->delta->delta_child; + unsigned *idx = &to_pack.objects[entry->delta_idx - 1].delta_child_idx; struct object_info oi = OBJECT_INFO_INIT; + enum object_type type; + unsigned long size; + + while (*idx) { + struct object_entry *oe = &to_pack.objects[*idx - 1]; - while (*p) { - if (*p == entry) - *p = (*p)->delta_sibling; + if (oe == entry) + *idx = oe->delta_sibling_idx; else - p = &(*p)->delta_sibling; + idx = &oe->delta_sibling_idx; } - entry->delta = NULL; + SET_DELTA(entry, NULL); entry->depth = 0; - oi.sizep = &entry->size; - oi.typep = &entry->type; - if (packed_object_info(the_repository, entry->in_pack, - entry->in_pack_offset, &oi) < 0) { + oi.sizep = &size; + oi.typep = &type; + if (packed_object_info(the_repository, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { /* * We failed to get the info from this pack for some reason; * fall back to sha1_object_info, which may find another copy. - * And if that fails, the error will be recorded in entry->type + * And if that fails, the error will be recorded in oe_type(entry) * and dealt with in prepare_pack(). */ - entry->type = oid_object_info(the_repository, &entry->idx.oid, - &entry->size); + oe_set_type(entry, + oid_object_info(the_repository, &entry->idx.oid, &size)); + } else { + oe_set_type(entry, type); } + SET_SIZE(entry, size); } /* @@ -1606,7 +1653,7 @@ static void break_delta_chains(struct object_entry *entry) for (cur = entry, total_depth = 0; cur; - cur = cur->delta, total_depth++) { + cur = DELTA(cur), total_depth++) { if (cur->dfs_state == DFS_DONE) { /* * We've already seen this object and know it isn't @@ -1623,7 +1670,7 @@ static void break_delta_chains(struct object_entry *entry) * is a bug. */ if (cur->dfs_state != DFS_NONE) - die("BUG: confusing delta dfs state in first pass: %d", + BUG("confusing delta dfs state in first pass: %d", cur->dfs_state); /* @@ -1631,7 +1678,7 @@ static void break_delta_chains(struct object_entry *entry) * it's not a delta, we're done traversing, but we'll mark it * done to save time on future traversals. */ - if (!cur->delta) { + if (!DELTA(cur)) { cur->dfs_state = DFS_DONE; break; } @@ -1654,7 +1701,7 @@ static void break_delta_chains(struct object_entry *entry) * We keep all commits in the chain that we examined. */ cur->dfs_state = DFS_ACTIVE; - if (cur->delta->dfs_state == DFS_ACTIVE) { + if (DELTA(cur)->dfs_state == DFS_ACTIVE) { drop_reused_delta(cur); cur->dfs_state = DFS_DONE; break; @@ -1669,7 +1716,7 @@ static void break_delta_chains(struct object_entry *entry) * an extra "next" pointer to keep going after we reset cur->delta. */ for (cur = entry; cur; cur = next) { - next = cur->delta; + next = DELTA(cur); /* * We should have a chain of zero or more ACTIVE states down to @@ -1680,7 +1727,7 @@ static void break_delta_chains(struct object_entry *entry) if (cur->dfs_state == DFS_DONE) break; else if (cur->dfs_state != DFS_ACTIVE) - die("BUG: confusing delta dfs state in second pass: %d", + BUG("confusing delta dfs state in second pass: %d", cur->dfs_state); /* @@ -1714,6 +1761,10 @@ static void get_object_details(void) uint32_t i; struct object_entry **sorted_by_offset; + if (progress) + progress_state = start_progress(_("Counting objects"), + to_pack.nr_objects); + sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *)); for (i = 0; i < to_pack.nr_objects; i++) sorted_by_offset[i] = to_pack.objects + i; @@ -1722,9 +1773,12 @@ static void get_object_details(void) for (i = 0; i < to_pack.nr_objects; i++) { struct object_entry *entry = sorted_by_offset[i]; check_object(entry); - if (big_file_threshold < entry->size) + if (entry->type_valid && + oe_size_greater_than(&to_pack, entry, big_file_threshold)) entry->no_try_delta = 1; + display_progress(progress_state, i + 1); } + stop_progress(&progress_state); /* * This must happen in a second pass, since we rely on the delta @@ -1749,10 +1803,14 @@ static int type_size_sort(const void *_a, const void *_b) { const struct object_entry *a = *(struct object_entry **)_a; const struct object_entry *b = *(struct object_entry **)_b; + enum object_type a_type = oe_type(a); + enum object_type b_type = oe_type(b); + unsigned long a_size = SIZE(a); + unsigned long b_size = SIZE(b); - if (a->type > b->type) + if (a_type > b_type) return -1; - if (a->type < b->type) + if (a_type < b_type) return 1; if (a->hash > b->hash) return -1; @@ -1762,9 +1820,9 @@ static int type_size_sort(const void *_a, const void *_b) return -1; if (a->preferred_base < b->preferred_base) return 1; - if (a->size > b->size) + if (a_size > b_size) return -1; - if (a->size < b->size) + if (a_size < b_size) return 1; return a < b ? -1 : (a > b); /* newest first */ } @@ -1817,6 +1875,46 @@ static pthread_mutex_t progress_mutex; #endif +/* + * Return the size of the object without doing any delta + * reconstruction (so non-deltas are true object sizes, but deltas + * return the size of the delta data). + */ +unsigned long oe_get_size_slow(struct packing_data *pack, + const struct object_entry *e) +{ + struct packed_git *p; + struct pack_window *w_curs; + unsigned char *buf; + enum object_type type; + unsigned long used, avail, size; + + if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) { + read_lock(); + if (oid_object_info(the_repository, &e->idx.oid, &size) < 0) + die(_("unable to get size of %s"), + oid_to_hex(&e->idx.oid)); + read_unlock(); + return size; + } + + p = oe_in_pack(pack, e); + if (!p) + BUG("when e->type is a delta, it must belong to a pack"); + + read_lock(); + w_curs = NULL; + buf = use_pack(p, &w_curs, e->in_pack_offset, &avail); + used = unpack_object_header_buffer(buf, avail, &type, &size); + if (used == 0) + die(_("unable to parse object header of %s"), + oid_to_hex(&e->idx.oid)); + + unuse_pack(&w_curs); + read_unlock(); + return size; +} + static int try_delta(struct unpacked *trg, struct unpacked *src, unsigned max_depth, unsigned long *mem_usage) { @@ -1828,7 +1926,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, void *delta_buf; /* Don't bother doing diffs between different types */ - if (trg_entry->type != src_entry->type) + if (oe_type(trg_entry) != oe_type(src_entry)) return -1; /* @@ -1839,8 +1937,8 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, * it, we will still save the transfer cost, as we already know * the other side has it and we won't send src_entry at all. */ - if (reuse_delta && trg_entry->in_pack && - trg_entry->in_pack == src_entry->in_pack && + if (reuse_delta && IN_PACK(trg_entry) && + IN_PACK(trg_entry) == IN_PACK(src_entry) && !src_entry->preferred_base && trg_entry->in_pack_type != OBJ_REF_DELTA && trg_entry->in_pack_type != OBJ_OFS_DELTA) @@ -1851,19 +1949,19 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, return 0; /* Now some size filtering heuristics. */ - trg_size = trg_entry->size; - if (!trg_entry->delta) { - max_size = trg_size/2 - 20; + trg_size = SIZE(trg_entry); + if (!DELTA(trg_entry)) { + max_size = trg_size/2 - the_hash_algo->rawsz; ref_depth = 1; } else { - max_size = trg_entry->delta_size; + max_size = DELTA_SIZE(trg_entry); ref_depth = trg->depth; } max_size = (uint64_t)max_size * (max_depth - src->depth) / (max_depth - ref_depth + 1); if (max_size == 0) return 0; - src_size = src_entry->size; + src_size = SIZE(src_entry); sizediff = src_size < trg_size ? trg_size - src_size : 0; if (sizediff >= max_size) return 0; @@ -1925,10 +2023,14 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size); if (!delta_buf) return 0; + if (delta_size >= (1U << OE_DELTA_SIZE_BITS)) { + free(delta_buf); + return 0; + } - if (trg_entry->delta) { + if (DELTA(trg_entry)) { /* Prefer only shallower same-sized deltas. */ - if (delta_size == trg_entry->delta_size && + if (delta_size == DELTA_SIZE(trg_entry) && src->depth + 1 >= trg->depth) { free(delta_buf); return 0; @@ -1943,7 +2045,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, free(trg_entry->delta_data); cache_lock(); if (trg_entry->delta_data) { - delta_cache_size -= trg_entry->delta_size; + delta_cache_size -= DELTA_SIZE(trg_entry); trg_entry->delta_data = NULL; } if (delta_cacheable(src_size, trg_size, delta_size)) { @@ -1955,8 +2057,8 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, free(delta_buf); } - trg_entry->delta = src_entry; - trg_entry->delta_size = delta_size; + SET_DELTA(trg_entry, src_entry); + SET_DELTA_SIZE(trg_entry, delta_size); trg->depth = src->depth + 1; return 1; @@ -1964,13 +2066,13 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, static unsigned int check_delta_limit(struct object_entry *me, unsigned int n) { - struct object_entry *child = me->delta_child; + struct object_entry *child = DELTA_CHILD(me); unsigned int m = n; while (child) { unsigned int c = check_delta_limit(child, n + 1); if (m < c) m = c; - child = child->delta_sibling; + child = DELTA_SIBLING(child); } return m; } @@ -1981,7 +2083,7 @@ static unsigned long free_unpacked(struct unpacked *n) free_delta_index(n->index); n->index = NULL; if (n->data) { - freed_mem += n->entry->size; + freed_mem += SIZE(n->entry); FREE_AND_NULL(n->data); } n->entry = NULL; @@ -2039,7 +2141,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, * otherwise they would become too deep. */ max_depth = depth; - if (entry->delta_child) { + if (DELTA_CHILD(entry)) { max_depth -= check_delta_limit(entry, 0); if (max_depth <= 0) goto next; @@ -2077,19 +2179,26 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, * between writes at that moment. */ if (entry->delta_data && !pack_to_stdout) { - entry->z_delta_size = do_compress(&entry->delta_data, - entry->delta_size); - cache_lock(); - delta_cache_size -= entry->delta_size; - delta_cache_size += entry->z_delta_size; - cache_unlock(); + unsigned long size; + + size = do_compress(&entry->delta_data, DELTA_SIZE(entry)); + if (size < (1U << OE_Z_DELTA_BITS)) { + entry->z_delta_size = size; + cache_lock(); + delta_cache_size -= DELTA_SIZE(entry); + delta_cache_size += entry->z_delta_size; + cache_unlock(); + } else { + FREE_AND_NULL(entry->delta_data); + entry->z_delta_size = 0; + } } /* if we made n a delta, and if n is already at max * depth, leaving it in the window is pointless. we * should evict it first. */ - if (entry->delta && max_depth <= n->depth) + if (DELTA(entry) && max_depth <= n->depth) continue; /* @@ -2097,7 +2206,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, * currently deltified object, to keep it longer. It will * be the first base object to be attempted next. */ - if (entry->delta) { + if (DELTA(entry)) { struct unpacked swap = array[best_base]; int dist = (window + idx - best_base) % window; int dst = best_base; @@ -2418,13 +2527,14 @@ static void prepare_pack(int window, int depth) for (i = 0; i < to_pack.nr_objects; i++) { struct object_entry *entry = to_pack.objects + i; - if (entry->delta) + if (DELTA(entry)) /* This happens if we decided to reuse existing * delta from a pack. "reuse_delta &&" is implied. */ continue; - if (entry->size < 50) + if (!entry->type_valid || + oe_size_less_than(&to_pack, entry, 50)) continue; if (entry->no_try_delta) @@ -2432,11 +2542,11 @@ static void prepare_pack(int window, int depth) if (!entry->preferred_base) { nr_deltas++; - if (entry->type < 0) + if (oe_type(entry) < 0) die("unable to get type of object %s", oid_to_hex(&entry->idx.oid)); } else { - if (entry->type < 0) { + if (oe_type(entry) < 0) { /* * This object is not found, but we * don't have to include it anyway. @@ -2545,7 +2655,7 @@ static void read_object_list_from_stdin(void) die("expected object ID, got garbage:\n %s", line); add_preferred_base_object(p + 1); - add_object_entry(&oid, 0, p + 1, 0); + add_object_entry(&oid, OBJ_NONE, p + 1, 0); } } @@ -2678,7 +2788,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) struct object_id oid; struct object *o; - if (!p->pack_local || p->pack_keep) + if (!p->pack_local || p->pack_keep || p->pack_keep_in_core) continue; if (open_pack_index(p)) die("cannot open pack index"); @@ -2741,7 +2851,8 @@ static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid) get_packed_git(the_repository); while (p) { - if ((!p->pack_local || p->pack_keep) && + if ((!p->pack_local || p->pack_keep || + p->pack_keep_in_core) && find_pack_entry_one(oid->hash, p)) { last_found = p; return 1; @@ -2784,7 +2895,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs) struct object_id oid; for (p = get_packed_git(the_repository); p; p = p->next) { - if (!p->pack_local || p->pack_keep) + if (!p->pack_local || p->pack_keep || p->pack_keep_in_core) continue; if (open_pack_index(p)) @@ -2810,7 +2921,8 @@ static int pack_options_allow_reuse(void) { return pack_to_stdout && allow_ofs_delta && - !ignore_packed_keep && + !ignore_packed_keep_on_disk && + !ignore_packed_keep_in_core && (!local || !have_non_local_packs) && !incremental; } @@ -2919,6 +3031,32 @@ static void get_object_list(int ac, const char **av) oid_array_clear(&recent_objects); } +static void add_extra_kept_packs(const struct string_list *names) +{ + struct packed_git *p; + + if (!names->nr) + return; + + for (p = get_packed_git(the_repository); p; p = p->next) { + const char *name = basename(p->pack_name); + int i; + + if (!p->pack_local) + continue; + + for (i = 0; i < names->nr; i++) + if (!fspathcmp(name, names->items[i].string)) + break; + + if (i < names->nr) { + p->pack_keep_in_core = 1; + ignore_packed_keep_in_core = 1; + continue; + } + } +} + static int option_parse_index_version(const struct option *opt, const char *arg, int unset) { @@ -2958,6 +3096,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) struct argv_array rp = ARGV_ARRAY_INIT; int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0; int rev_list_index = 0; + struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; struct option pack_objects_options[] = { OPT_SET_INT('q', "quiet", &progress, N_("do not show progress meter"), 0), @@ -2995,18 +3134,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("do not create an empty pack output")), OPT_BOOL(0, "revs", &use_internal_rev_list, N_("read revision arguments from standard input")), - { OPTION_SET_INT, 0, "unpacked", &rev_list_unpacked, NULL, - N_("limit the objects to those that are not yet packed"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, - { OPTION_SET_INT, 0, "all", &rev_list_all, NULL, - N_("include objects reachable from any reference"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, - { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL, - N_("include objects referred by reflog entries"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, - { OPTION_SET_INT, 0, "indexed-objects", &rev_list_index, NULL, - N_("include objects referred to by the index"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, + OPT_SET_INT_F(0, "unpacked", &rev_list_unpacked, + N_("limit the objects to those that are not yet packed"), + 1, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "all", &rev_list_all, + N_("include objects reachable from any reference"), + 1, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "reflog", &rev_list_reflog, + N_("include objects referred by reflog entries"), + 1, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "indexed-objects", &rev_list_index, + N_("include objects referred to by the index"), + 1, PARSE_OPT_NONEG), OPT_BOOL(0, "stdout", &pack_to_stdout, N_("output pack to stdout")), OPT_BOOL(0, "include-tag", &include_tag, @@ -3022,8 +3161,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("create thin packs")), OPT_BOOL(0, "shallow", &shallow, N_("create packs suitable for shallow fetches")), - OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep, + OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep_on_disk, N_("ignore packs that have companion .keep file")), + OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), + N_("ignore this pack")), OPT_INTEGER(0, "compression", &pack_compression_level, N_("pack compression level")), OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents, @@ -3041,6 +3182,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) OPT_END(), }; + if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS)) + BUG("too many dfs states, increase OE_DFS_STATE_BITS"); + check_replace_refs = 0; reset_pack_idx_option(&pack_idx_opts); @@ -3057,6 +3201,17 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (pack_to_stdout != !base_name || argc) usage_with_options(pack_usage, pack_objects_options); + if (depth >= (1 << OE_DEPTH_BITS)) { + warning(_("delta chain depth %d is too deep, forcing %d"), + depth, (1 << OE_DEPTH_BITS) - 1); + depth = (1 << OE_DEPTH_BITS) - 1; + } + if (cache_max_small_delta_size >= (1U << OE_Z_DELTA_BITS)) { + warning(_("pack.deltaCacheLimit is too high, forcing %d"), + (1U << OE_Z_DELTA_BITS) - 1); + cache_max_small_delta_size = (1U << OE_Z_DELTA_BITS) - 1; + } + argv_array_push(&rp, "pack-objects"); if (thin) { use_internal_rev_list = 1; @@ -3088,6 +3243,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) fetch_if_missing = 0; argv_array_push(&rp, "--exclude-promisor-objects"); } + if (unpack_unreachable || keep_unreachable || pack_loose_unreachable) + use_internal_rev_list = 1; if (!reuse_object) reuse_delta = 0; @@ -3151,19 +3308,20 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (progress && all_progress_implied) progress = 2; - if (ignore_packed_keep) { + add_extra_kept_packs(&keep_pack_list); + if (ignore_packed_keep_on_disk) { struct packed_git *p; for (p = get_packed_git(the_repository); p; p = p->next) if (p->pack_local && p->pack_keep) break; if (!p) /* no keep-able packs found */ - ignore_packed_keep = 0; + ignore_packed_keep_on_disk = 0; } if (local) { /* - * unlike ignore_packed_keep above, we do not want to - * unset "local" based on looking at packs, as it - * also covers non-local objects + * unlike ignore_packed_keep_on_disk above, we do not + * want to unset "local" based on looking at packs, as + * it also covers non-local objects */ struct packed_git *p; for (p = get_packed_git(the_repository); p; p = p->next) { @@ -3174,8 +3332,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } } + prepare_packing_data(&to_pack); + if (progress) - progress_state = start_progress(_("Counting objects"), 0); + progress_state = start_progress(_("Enumerating objects"), 0); if (!use_internal_rev_list) read_object_list_from_stdin(); else { diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 354478a127..0494dceff7 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -20,7 +20,7 @@ static int load_all_packs, verbose, alt_odb; struct llist_item { struct llist_item *next; - const unsigned char *sha1; + const struct object_id *oid; }; static struct llist { struct llist_item *front; @@ -90,14 +90,14 @@ static struct llist * llist_copy(struct llist *list) return ret; new_item = ret->front = llist_item_get(); - new_item->sha1 = list->front->sha1; + new_item->oid = list->front->oid; old_item = list->front->next; while (old_item) { prev = new_item; new_item = llist_item_get(); prev->next = new_item; - new_item->sha1 = old_item->sha1; + new_item->oid = old_item->oid; old_item = old_item->next; } new_item->next = NULL; @@ -108,10 +108,10 @@ static struct llist * llist_copy(struct llist *list) static inline struct llist_item *llist_insert(struct llist *list, struct llist_item *after, - const unsigned char *sha1) + const struct object_id *oid) { struct llist_item *new_item = llist_item_get(); - new_item->sha1 = sha1; + new_item->oid = oid; new_item->next = NULL; if (after != NULL) { @@ -131,21 +131,21 @@ static inline struct llist_item *llist_insert(struct llist *list, } static inline struct llist_item *llist_insert_back(struct llist *list, - const unsigned char *sha1) + const struct object_id *oid) { - return llist_insert(list, list->back, sha1); + return llist_insert(list, list->back, oid); } static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, - const unsigned char *sha1, struct llist_item *hint) + const struct object_id *oid, struct llist_item *hint) { struct llist_item *prev = NULL, *l; l = (hint == NULL) ? list->front : hint; while (l) { - int cmp = hashcmp(l->sha1, sha1); + int cmp = oidcmp(l->oid, oid); if (cmp > 0) { /* we insert before this entry */ - return llist_insert(list, prev, sha1); + return llist_insert(list, prev, oid); } if (!cmp) { /* already exists */ return l; @@ -154,11 +154,11 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, l = l->next; } /* insert at the end */ - return llist_insert_back(list, sha1); + return llist_insert_back(list, oid); } /* returns a pointer to an item in front of sha1 */ -static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint) +static inline struct llist_item * llist_sorted_remove(struct llist *list, const struct object_id *oid, struct llist_item *hint) { struct llist_item *prev, *l; @@ -166,7 +166,7 @@ redo_from_start: l = (hint == NULL) ? list->front : hint; prev = NULL; while (l) { - int cmp = hashcmp(l->sha1, sha1); + int cmp = oidcmp(l->oid, oid); if (cmp > 0) /* not in list, since sorted */ return prev; if (!cmp) { /* found */ @@ -201,7 +201,7 @@ static void llist_sorted_difference_inplace(struct llist *A, b = B->front; while (b) { - hint = llist_sorted_remove(A, b->sha1, hint); + hint = llist_sorted_remove(A, b->oid, hint); b = b->next; } } @@ -252,13 +252,14 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step; const unsigned char *p1_base, *p2_base; struct llist_item *p1_hint = NULL, *p2_hint = NULL; + const unsigned int hashsz = the_hash_algo->rawsz; p1_base = p1->pack->index_data; p2_base = p2->pack->index_data; p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8); p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8); - p1_step = (p1->pack->index_version < 2) ? 24 : 20; - p2_step = (p2->pack->index_version < 2) ? 24 : 20; + p1_step = hashsz + ((p1->pack->index_version < 2) ? 4 : 0); + p2_step = hashsz + ((p2->pack->index_version < 2) ? 4 : 0); while (p1_off < p1->pack->num_objects * p1_step && p2_off < p2->pack->num_objects * p2_step) @@ -267,9 +268,11 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) /* cmp ~ p1 - p2 */ if (cmp == 0) { p1_hint = llist_sorted_remove(p1->unique_objects, - p1_base + p1_off, p1_hint); + (const struct object_id *)(p1_base + p1_off), + p1_hint); p2_hint = llist_sorted_remove(p2->unique_objects, - p1_base + p1_off, p2_hint); + (const struct object_id *)(p1_base + p1_off), + p2_hint); p1_off += p1_step; p2_off += p2_step; continue; @@ -359,13 +362,14 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) size_t ret = 0; unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step; const unsigned char *p1_base, *p2_base; + const unsigned int hashsz = the_hash_algo->rawsz; p1_base = p1->index_data; p2_base = p2->index_data; p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8); p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8); - p1_step = (p1->index_version < 2) ? 24 : 20; - p2_step = (p2->index_version < 2) ? 24 : 20; + p1_step = hashsz + ((p1->index_version < 2) ? 4 : 0); + p2_step = hashsz + ((p2->index_version < 2) ? 4 : 0); while (p1_off < p1->num_objects * p1_step && p2_off < p2->num_objects * p2_step) @@ -499,7 +503,7 @@ static void load_all_objects(void) l = pl->all_objects->front; while (l) { hint = llist_insert_sorted_unique(all_objects, - l->sha1, hint); + l->oid, hint); l = l->next; } pl = pl->next; @@ -558,9 +562,9 @@ static struct pack_list * add_pack(struct packed_git *p) base = p->index_data; base += 256 * 4 + ((p->index_version < 2) ? 4 : 8); - step = (p->index_version < 2) ? 24 : 20; + step = the_hash_algo->rawsz + ((p->index_version < 2) ? 4 : 0); while (off < p->num_objects * step) { - llist_insert_back(l.all_objects, base + off); + llist_insert_back(l.all_objects, (const struct object_id *)(base + off)); off += step; } /* this list will be pruned in cmp_two_packs later */ @@ -601,8 +605,8 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix) int i; struct pack_list *min, *red, *pl; struct llist *ignore; - unsigned char *sha1; - char buf[42]; /* 40 byte sha1 + \n + \0 */ + struct object_id *oid; + char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */ if (argc == 2 && !strcmp(argv[1], "-h")) usage(pack_redundant_usage); @@ -650,10 +654,10 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix) llist_init(&ignore); if (!isatty(0)) { while (fgets(buf, sizeof(buf), stdin)) { - sha1 = xmalloc(20); - if (get_sha1_hex(buf, sha1)) - die("Bad sha1 on stdin: %s", buf); - llist_insert_sorted_unique(ignore, sha1, NULL); + oid = xmalloc(sizeof(*oid)); + if (get_oid_hex(buf, oid)) + die("Bad object ID on stdin: %s", buf); + llist_insert_sorted_unique(ignore, oid, NULL); } } llist_sorted_difference_inplace(all_objects, ignore); diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index 419238171d..4ff525e50f 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -25,7 +25,7 @@ static int prune_object(const struct object_id *oid, const char *path, { int *opts = data; - if (!has_sha1_pack(oid->hash)) + if (!has_object_pack(oid)) return 0; if (*opts & PRUNE_PACKED_DRY_RUN) diff --git a/builtin/pull.c b/builtin/pull.c index d21239cd9f..7197b22b16 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -9,12 +9,13 @@ #include "config.h" #include "builtin.h" #include "parse-options.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "run-command.h" #include "sha1-array.h" #include "remote.h" #include "dir.h" #include "refs.h" +#include "refspec.h" #include "revision.h" #include "submodule.h" #include "submodule-config.h" @@ -27,14 +28,16 @@ enum rebase_type { REBASE_FALSE = 0, REBASE_TRUE, REBASE_PRESERVE, + REBASE_MERGES, REBASE_INTERACTIVE }; /** * Parses the value of --rebase. If value is a false value, returns * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is - * "preserve", returns REBASE_PRESERVE. If value is a invalid value, dies with - * a fatal error if fatal is true, otherwise returns REBASE_INVALID. + * "merges", returns REBASE_MERGES. If value is "preserve", returns + * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if + * fatal is true, otherwise returns REBASE_INVALID. */ static enum rebase_type parse_config_rebase(const char *key, const char *value, int fatal) @@ -47,6 +50,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, "merges")) + return REBASE_MERGES; else if (!strcmp(value, "interactive")) return REBASE_INTERACTIVE; @@ -130,7 +135,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|interactive", + "false|true|merges|preserve|interactive", N_("incorporate changes by rebasing rather than merging"), PARSE_OPT_OPTARG, parse_opt_rebase }, OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL, @@ -539,7 +544,7 @@ static int run_fetch(const char *repo, const char **refspecs) argv_array_push(&args, repo); argv_array_pushv(&args, refspecs); } else if (*refspecs) - die("BUG: refspecs without repo?"); + BUG("refspecs without repo?"); ret = run_command_v_opt(args.argv, RUN_GIT_CMD); argv_array_clear(&args); return ret; @@ -668,19 +673,19 @@ static const char *get_upstream_branch(const char *remote) } /** - * Derives the remote tracking branch from the remote and refspec. + * Derives the remote-tracking branch from the remote and refspec. * * FIXME: The current implementation assumes the default mapping of * refs/heads/<branch_name> to refs/remotes/<remote_name>/<branch_name>. */ static const char *get_tracking_branch(const char *remote, const char *refspec) { - struct refspec *spec; + struct refspec_item spec; const char *spec_src; const char *merge_branch; - spec = parse_fetch_refspec(1, &refspec); - spec_src = spec->src; + refspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH); + spec_src = spec.src; if (!*spec_src || !strcmp(spec_src, "HEAD")) spec_src = "HEAD"; else if (skip_prefix(spec_src, "heads/", &spec_src)) @@ -700,13 +705,13 @@ static const char *get_tracking_branch(const char *remote, const char *refspec) } else merge_branch = NULL; - free_refspec(1, spec); + refspec_item_clear(&spec); return merge_branch; } /** * Given the repo and refspecs, sets fork_point to the point at which the - * current branch forked from its remote tracking branch. Returns 0 on success, + * current branch forked from its remote-tracking branch. Returns 0 on success, * -1 on failure. */ static int get_rebase_fork_point(struct object_id *fork_point, const char *repo, @@ -800,7 +805,9 @@ static int run_rebase(const struct object_id *curr_head, argv_push_verbosity(&args); /* Options passed to git-rebase */ - if (opt_rebase == REBASE_PRESERVE) + if (opt_rebase == REBASE_MERGES) + argv_array_push(&args, "--rebase-merges"); + else if (opt_rebase == REBASE_PRESERVE) argv_array_push(&args, "--preserve-merges"); else if (opt_rebase == REBASE_INTERACTIVE) argv_array_push(&args, "--interactive"); diff --git a/builtin/push.c b/builtin/push.c index 013c20d616..9cd8e8cd56 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -4,6 +4,7 @@ #include "cache.h" #include "config.h" #include "refs.h" +#include "refspec.h" #include "run-command.h" #include "builtin.h" #include "remote.h" @@ -12,12 +13,40 @@ #include "submodule.h" #include "submodule-config.h" #include "send-pack.h" +#include "color.h" static const char * const push_usage[] = { N_("git push [<options>] [<repository> [<refspec>...]]"), NULL, }; +static int push_use_color = -1; +static char push_colors[][COLOR_MAXLEN] = { + GIT_COLOR_RESET, + GIT_COLOR_RED, /* ERROR */ +}; + +enum color_push { + PUSH_COLOR_RESET = 0, + PUSH_COLOR_ERROR = 1 +}; + +static int parse_push_color_slot(const char *slot) +{ + if (!strcasecmp(slot, "reset")) + return PUSH_COLOR_RESET; + if (!strcasecmp(slot, "error")) + return PUSH_COLOR_ERROR; + return -1; +} + +static const char *push_get_color(enum color_push ix) +{ + if (want_color_stderr(push_use_color)) + return push_colors[ix]; + return ""; +} + static int thin = 1; static int deleterefs; static const char *receivepack; @@ -28,19 +57,10 @@ static enum transport_family family; static struct push_cas_option cas; -static const char **refspec; -static int refspec_nr; -static int refspec_alloc; +static struct refspec rs = REFSPEC_INIT_PUSH; static struct string_list push_options_config = STRING_LIST_INIT_DUP; -static void add_refspec(const char *ref) -{ - refspec_nr++; - ALLOC_GROW(refspec, refspec_nr, refspec_alloc); - refspec[refspec_nr-1] = ref; -} - static const char *map_refspec(const char *ref, struct remote *remote, struct ref *local_refs) { @@ -50,12 +70,11 @@ static const char *map_refspec(const char *ref, if (count_refspec_match(ref, local_refs, &matched) != 1) return ref; - if (remote->push) { - struct refspec query; - memset(&query, 0, sizeof(struct refspec)); + if (remote->push.nr) { + struct refspec_item query; + memset(&query, 0, sizeof(struct refspec_item)); query.src = matched->name; - if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) && - query.dst) { + if (!query_refspecs(&remote->push, &query) && query.dst) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%s%s:%s", query.force ? "+" : "", @@ -110,7 +129,7 @@ static void set_refspecs(const char **refs, int nr, const char *repo) } ref = map_refspec(ref, remote, local_refs); } - add_refspec(ref); + refspec_append(&rs, ref); } } @@ -198,7 +217,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch, } strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src); - add_refspec(refspec.buf); + refspec_append(&rs, refspec.buf); } static void setup_push_current(struct remote *remote, struct branch *branch) @@ -208,7 +227,7 @@ static void setup_push_current(struct remote *remote, struct branch *branch) if (!branch) die(_(message_detached_head_die), remote->name); strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname); - add_refspec(refspec.buf); + refspec_append(&rs, refspec.buf); } static int is_workflow_triangular(struct remote *remote) @@ -225,7 +244,7 @@ static void setup_default_push_refspecs(struct remote *remote) switch (push_default) { default: case PUSH_DEFAULT_MATCHING: - add_refspec(":"); + refspec_append(&rs, ":"); break; case PUSH_DEFAULT_UNSPECIFIED: @@ -313,7 +332,8 @@ static void advise_ref_needs_force(void) advise(_(message_advice_ref_needs_force)); } -static int push_with_options(struct transport *transport, int flags) +static int push_with_options(struct transport *transport, struct refspec *rs, + int flags) { int err; unsigned int reject_reasons; @@ -335,10 +355,12 @@ static int push_with_options(struct transport *transport, int flags) if (verbosity > 0) fprintf(stderr, _("Pushing to %s\n"), transport->url); - err = transport_push(transport, refspec_nr, refspec, flags, - &reject_reasons); - if (err != 0) + err = transport_push(transport, rs, flags, &reject_reasons); + if (err != 0) { + fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR)); error(_("failed to push some refs to '%s'"), transport->url); + fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET)); + } err |= transport_disconnect(transport); if (!err) @@ -366,6 +388,7 @@ static int do_push(const char *repo, int flags, struct remote *remote = pushremote_get(repo); const char **url; int url_nr; + struct refspec *push_refspec = &rs; if (!remote) { if (repo) @@ -386,27 +409,9 @@ static int do_push(const char *repo, int flags, if (push_options->nr) flags |= TRANSPORT_PUSH_OPTIONS; - if ((flags & TRANSPORT_PUSH_ALL) && refspec) { - if (!strcmp(*refspec, "refs/tags/*")) - return error(_("--all and --tags are incompatible")); - return error(_("--all can't be combined with refspecs")); - } - - if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) { - if (!strcmp(*refspec, "refs/tags/*")) - return error(_("--mirror and --tags are incompatible")); - return error(_("--mirror can't be combined with refspecs")); - } - - if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) == - (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) { - return error(_("--all and --mirror are incompatible")); - } - - if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) { - if (remote->push_refspec_nr) { - refspec = remote->push_refspec; - refspec_nr = remote->push_refspec_nr; + if (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) { + if (remote->push.nr) { + push_refspec = &remote->push; } else if (!(flags & TRANSPORT_PUSH_MIRROR)) setup_default_push_refspecs(remote); } @@ -418,7 +423,7 @@ static int do_push(const char *repo, int flags, transport_get(remote, url[i]); if (flags & TRANSPORT_PUSH_OPTIONS) transport->push_options = push_options; - if (push_with_options(transport, flags)) + if (push_with_options(transport, push_refspec, flags)) errs++; } } else { @@ -426,7 +431,7 @@ static int do_push(const char *repo, int flags, transport_get(remote, NULL); if (flags & TRANSPORT_PUSH_OPTIONS) transport->push_options = push_options; - if (push_with_options(transport, flags)) + if (push_with_options(transport, push_refspec, flags)) errs++; } return !!errs; @@ -467,6 +472,7 @@ static void set_push_cert_flags(int *flags, int v) static int git_push_config(const char *k, const char *v, void *cb) { + const char *slot_name; int *flags = cb; int status; @@ -514,6 +520,16 @@ static int git_push_config(const char *k, const char *v, void *cb) else string_list_append(&push_options_config, v); return 0; + } else if (!strcmp(k, "color.push")) { + push_use_color = git_config_colorbool(k, v); + return 0; + } else if (skip_prefix(k, "color.push.", &slot_name)) { + int slot = parse_push_color_slot(slot_name); + if (slot < 0) + return 0; + if (!v) + return config_error_nonbool(k); + return color_parse(v, push_colors[slot]); } return git_default_config(k, v, NULL); @@ -583,6 +599,20 @@ int cmd_push(int argc, const char **argv, const char *prefix) die(_("--delete is incompatible with --all, --mirror and --tags")); if (deleterefs && argc < 2) die(_("--delete doesn't make sense without any refs")); + if (flags & TRANSPORT_PUSH_ALL) { + if (tags) + die(_("--all and --tags are incompatible")); + if (argc >= 2) + die(_("--all can't be combined with refspecs")); + } + if (flags & TRANSPORT_PUSH_MIRROR) { + if (tags) + die(_("--mirror and --tags are incompatible")); + if (argc >= 2) + die(_("--mirror can't be combined with refspecs")); + } + if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR)) + die(_("--all and --mirror are incompatible")); if (recurse_submodules == RECURSE_SUBMODULES_CHECK) flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK; @@ -592,7 +622,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) flags |= TRANSPORT_RECURSE_SUBMODULES_ONLY; if (tags) - add_refspec("refs/tags/*"); + refspec_append(&rs, "refs/tags/*"); if (argc > 0) { repo = argv[0]; diff --git a/builtin/read-tree.c b/builtin/read-tree.c index bf87a2710b..ebc43eb805 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -107,8 +107,6 @@ static int git_read_tree_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static struct lock_file lock_file; - int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, stage = 0; @@ -116,6 +114,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) struct tree_desc t[MAX_UNPACK_TREES]; struct unpack_trees_options opts; int prefix_set = 0; + struct lock_file lock_file = LOCK_INIT; const struct option read_tree_options[] = { { OPTION_CALLBACK, 0, "index-output", NULL, N_("file"), N_("write resulting index to <file>"), diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c index ad074705bb..f7c2a5fdc8 100644 --- a/builtin/rebase--helper.c +++ b/builtin/rebase--helper.c @@ -12,8 +12,8 @@ static const char * const builtin_rebase_helper_usage[] = { int cmd_rebase__helper(int argc, const char **argv, const char *prefix) { struct replay_opts opts = REPLAY_OPTS_INIT; - unsigned flags = 0, keep_empty = 0; - int abbreviate_commands = 0; + unsigned flags = 0, keep_empty = 0, rebase_merges = 0; + int abbreviate_commands = 0, rebase_cousins = -1; enum { CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH, @@ -24,6 +24,9 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")), OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message, N_("allow commits with empty messages")), + OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")), + OPT_BOOL(0, "rebase-cousins", &rebase_cousins, + N_("keep original branch points of cousins")), OPT_CMDMODE(0, "continue", &command, N_("continue rebase"), CONTINUE), OPT_CMDMODE(0, "abort", &command, N_("abort rebase"), @@ -57,8 +60,14 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix) flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0; flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; + flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0; + flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; + if (rebase_cousins >= 0 && !rebase_merges) + warning(_("--[no-]rebase-cousins has no effect without " + "--rebase-merges")); + if (command == CONTINUE && argc == 1) return !!sequencer_continue(&opts); if (command == ABORT && argc == 1) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c666820b69..44c7c9ee82 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -7,7 +7,7 @@ #include "pkt-line.h" #include "sideband.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "commit.h" #include "object.h" #include "remote.h" @@ -455,21 +455,21 @@ static void hmac_sha1(unsigned char *out, /* RFC 2104 2. (6) & (7) */ git_SHA1_Init(&ctx); git_SHA1_Update(&ctx, k_opad, sizeof(k_opad)); - git_SHA1_Update(&ctx, out, 20); + git_SHA1_Update(&ctx, out, GIT_SHA1_RAWSZ); git_SHA1_Final(out, &ctx); } static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) { struct strbuf buf = STRBUF_INIT; - unsigned char sha1[20]; + unsigned char sha1[GIT_SHA1_RAWSZ]; strbuf_addf(&buf, "%s:%"PRItime, path, stamp); hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));; strbuf_release(&buf); /* RFC 2104 5. HMAC-SHA1-80 */ - strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1)); + strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, GIT_SHA1_HEXSZ, sha1_to_hex(sha1)); return strbuf_detach(&buf, NULL); } @@ -877,7 +877,7 @@ static void refuse_unconfigured_deny_delete_current(void) static int command_singleton_iterator(void *cb_data, struct object_id *oid); static int update_shallow_ref(struct command *cmd, struct shallow_info *si) { - static struct lock_file shallow_lock; + struct lock_file shallow_lock = LOCK_INIT; struct oid_array extra = OID_ARRAY_INIT; struct check_connected_options opt = CHECK_CONNECTED_INIT; uint32_t mask = 1 << (cmd->index % 32); @@ -969,7 +969,7 @@ static const char *push_to_deploy(unsigned char *sha1, return "Working directory has unstaged changes"; /* diff-index with either HEAD or an empty tree */ - diff_index[4] = head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX; + diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex(); child_process_init(&child); child.argv = diff_index; @@ -1379,7 +1379,7 @@ static void warn_if_skipped_connectivity_check(struct command *commands, } } if (!checked_connectivity) - die("BUG: connectivity check skipped???"); + BUG("connectivity check skipped???"); } static void execute_commands_non_atomic(struct command *commands, @@ -1966,6 +1966,12 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) unpack_limit = receive_unpack_limit; switch (determine_protocol_version_server()) { + case protocol_v2: + /* + * push support for protocol v2 has not been implemented yet, + * so ignore the request to use v2 and fallback to using v0. + */ + break; case protocol_v1: /* * v1 is just the original protocol with a version string, diff --git a/builtin/reflog.c b/builtin/reflog.c index bee4795a93..0091192995 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -155,7 +155,7 @@ static int commit_is_complete(struct commit *commit) for (i = 0; i < found.nr; i++) { struct commit *c = (struct commit *)found.objects[i].item; - if (!tree_is_complete(&c->tree->object.oid)) { + if (!tree_is_complete(get_commit_tree_oid(c))) { is_incomplete = 1; c->object.flags |= INCOMPLETE; } diff --git a/builtin/remote.c b/builtin/remote.c index 9487c01909..c74ee88690 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -7,6 +7,7 @@ #include "strbuf.h" #include "run-command.h" #include "refs.h" +#include "refspec.h" #include "object-store.h" #include "argv-array.h" @@ -246,7 +247,9 @@ static int add(int argc, const char **argv) struct branch_info { char *remote_name; struct string_list merge; - enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase; + enum { + NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE, REBASE_MERGES + } rebase; }; static struct string_list branch_list = STRING_LIST_INIT_NODUP; @@ -307,6 +310,8 @@ static int config_read_branches(const char *key, const char *value, void *cb) info->rebase = v; else if (!strcmp(value, "preserve")) info->rebase = NORMAL_REBASE; + else if (!strcmp(value, "merges")) + info->rebase = REBASE_MERGES; else if (!strcmp(value, "interactive")) info->rebase = INTERACTIVE_REBASE; } @@ -333,10 +338,10 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat struct ref *ref, *stale_refs; int i; - for (i = 0; i < states->remote->fetch_refspec_nr; i++) - if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1)) + for (i = 0; i < states->remote->fetch.nr; i++) + if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1)) die(_("Could not get fetch map for refspec %s"), - states->remote->fetch_refspec[i]); + states->remote->fetch.raw[i]); states->new_refs.strdup_strings = 1; states->tracked.strdup_strings = 1; @@ -347,8 +352,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat else string_list_append(&states->tracked, abbrev_branch(ref->name)); } - stale_refs = get_stale_heads(states->remote->fetch, - states->remote->fetch_refspec_nr, fetch_map); + stale_refs = get_stale_heads(&states->remote->fetch, fetch_map); for (ref = stale_refs; ref; ref = ref->next) { struct string_list_item *item = string_list_append(&states->stale, abbrev_branch(ref->name)); @@ -388,8 +392,7 @@ static int get_push_ref_states(const struct ref *remote_refs, local_refs = get_local_heads(); push_map = copy_ref_list(remote_refs); - match_push_refs(local_refs, &push_map, remote->push_refspec_nr, - remote->push_refspec, MATCH_REFS_NONE); + match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE); states->push.strdup_strings = 1; for (ref = push_map; ref; ref = ref->next) { @@ -435,14 +438,14 @@ static int get_push_ref_states_noquery(struct ref_states *states) return 0; states->push.strdup_strings = 1; - if (!remote->push_refspec_nr) { + if (!remote->push.nr) { item = string_list_append(&states->push, _("(matching)")); info = item->util = xcalloc(1, sizeof(struct push_info)); info->status = PUSH_STATUS_NOTQUERIED; info->dest = xstrdup(item->string); } - for (i = 0; i < remote->push_refspec_nr; i++) { - struct refspec *spec = remote->push + i; + for (i = 0; i < remote->push.nr; i++) { + const struct refspec_item *spec = &remote->push.items[i]; if (spec->matching) item = string_list_append(&states->push, _("(matching)")); else if (strlen(spec->src)) @@ -462,7 +465,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat { struct ref *ref, *matches; struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; - struct refspec refspec; + struct refspec_item refspec; refspec.force = 0; refspec.pattern = 1; @@ -515,7 +518,7 @@ static int add_branch_for_removal(const char *refname, const struct object_id *oid, int flags, void *cb_data) { struct branches_for_remote *branches = cb_data; - struct refspec refspec; + struct refspec_item refspec; struct known_remote *kr; memset(&refspec, 0, sizeof(refspec)); @@ -586,12 +589,12 @@ static int migrate_file(struct remote *remote) 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++) - git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0); + for (i = 0; i < remote->push.raw_nr; i++) + git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); - for (i = 0; i < remote->fetch_refspec_nr; i++) - git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0); + for (i = 0; i < remote->fetch.raw_nr; i++) + git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0); if (remote->origin == REMOTE_REMOTES) unlink_or_warn(git_path("remotes/%s", remote->name)); else if (remote->origin == REMOTE_BRANCHES) @@ -646,11 +649,11 @@ static int mv(int argc, const char **argv) strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); git_config_set_multivar(buf.buf, NULL, NULL, 1); strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch_refspec_nr; i++) { + for (i = 0; i < oldremote->fetch.raw_nr; i++) { char *ptr; strbuf_reset(&buf2); - strbuf_addstr(&buf2, oldremote->fetch_refspec[i]); + strbuf_addstr(&buf2, oldremote->fetch.raw[i]); ptr = strstr(buf2.buf, old_remote_context.buf); if (ptr) { refspec_updated = 1; @@ -834,7 +837,7 @@ static int append_ref_to_tracked_list(const char *refname, const struct object_id *oid, int flags, void *cb_data) { struct ref_states *states = cb_data; - struct refspec refspec; + struct refspec_item refspec; if (flags & REF_ISSYMREF) return 0; @@ -863,7 +866,7 @@ static int get_remote_ref_states(const char *name, if (query) { transport = transport_get(states->remote, states->remote->url_nr > 0 ? states->remote->url[0] : NULL); - remote_refs = transport_get_remote_refs(transport); + remote_refs = transport_get_remote_refs(transport, NULL); transport_disconnect(transport); states->queried = 1; @@ -964,9 +967,15 @@ 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(branch_info->rebase == INTERACTIVE_REBASE - ? _("rebases interactively onto remote %s") - : _("rebases onto remote %s"), merge->items[0].string); + const char *msg; + if (branch_info->rebase == INTERACTIVE_REBASE) + msg = _("rebases interactively onto remote %s"); + else if (branch_info->rebase == REBASE_MERGES) + msg = _("rebases interactively (with merges) onto " + "remote %s"); + else + msg = _("rebases onto remote %s"); + printf_ln(msg, merge->items[0].string); return 0; } else if (show_info->any_rebase) { printf_ln(_(" merges with remote %s"), merge->items[0].string); diff --git a/builtin/repack.c b/builtin/repack.c index 7bdb40142f..6c636e159e 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -86,7 +86,8 @@ static void remove_pack_on_signal(int signo) * have a corresponding .keep or .promisor file. These packs are not to * be kept if we are going to pack everything into one file. */ -static void get_non_kept_pack_filenames(struct string_list *fname_list) +static void get_non_kept_pack_filenames(struct string_list *fname_list, + const struct string_list *extra_keep) { DIR *dir; struct dirent *e; @@ -97,6 +98,14 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list) while ((e = readdir(dir)) != NULL) { size_t len; + int i; + + for (i = 0; i < extra_keep->nr; i++) + if (!fspathcmp(e->d_name, extra_keep->items[i].string)) + break; + if (extra_keep->nr > 0 && i < extra_keep->nr) + continue; + if (!strip_suffix(e->d_name, ".pack", &len)) continue; @@ -148,7 +157,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list rollback = STRING_LIST_INIT_NODUP; struct string_list existing_packs = STRING_LIST_INIT_DUP; struct strbuf line = STRBUF_INIT; - int ext, ret, failed; + int i, ext, ret, failed; FILE *out; /* variables to be filled by option parsing */ @@ -160,6 +169,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) const char *depth = NULL; const char *threads = NULL; const char *max_pack_size = NULL; + struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; int no_reuse_delta = 0, no_reuse_object = 0; int no_update_server_info = 0; int quiet = 0; @@ -200,6 +210,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("maximum size of each packfile")), OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, N_("repack objects in packs marked with .keep")), + OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), + N_("do not repack this pack")), OPT_END() }; @@ -230,6 +242,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_push(&cmd.args, "--keep-true-parents"); if (!pack_kept_objects) argv_array_push(&cmd.args, "--honor-pack-keep"); + for (i = 0; i < keep_pack_list.nr; i++) + argv_array_pushf(&cmd.args, "--keep-pack=%s", + keep_pack_list.items[i].string); argv_array_push(&cmd.args, "--non-empty"); argv_array_push(&cmd.args, "--all"); argv_array_push(&cmd.args, "--reflog"); @@ -254,7 +269,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_push(&cmd.args, "--write-bitmap-index"); if (pack_everything & ALL_INTO_ONE) { - get_non_kept_pack_filenames(&existing_packs); + get_non_kept_pack_filenames(&existing_packs, &keep_pack_list); if (existing_packs.nr && delete_redundant) { if (unpack_unreachable) { diff --git a/builtin/replace.c b/builtin/replace.c index 14e142d5a8..deabda2101 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -22,6 +22,7 @@ static const char * const git_replace_usage[] = { N_("git replace [-f] <object> <replacement>"), N_("git replace [-f] --edit <object>"), N_("git replace [-f] --graft <commit> [<parent>...]"), + N_("git replace [-f] --convert-graft-file"), N_("git replace -d <object>..."), N_("git replace [--format=<format>] [-l [<pattern>]]"), NULL @@ -82,9 +83,9 @@ static int list_replace_refs(const char *pattern, const char *format) else if (!strcmp(format, "long")) data.format = REPLACE_FORMAT_LONG; else - die("invalid replace format '%s'\n" - "valid formats are 'short', 'medium' and 'long'\n", - format); + return error("invalid replace format '%s'\n" + "valid formats are 'short', 'medium' and 'long'\n", + format); for_each_replace_ref(the_repository, show_reference, (void *)&data); @@ -137,7 +138,7 @@ static int delete_replace_ref(const char *name, const char *ref, return 0; } -static void check_ref_valid(struct object_id *object, +static int check_ref_valid(struct object_id *object, struct object_id *prev, struct strbuf *ref, int force) @@ -145,12 +146,13 @@ static void check_ref_valid(struct object_id *object, strbuf_reset(ref); strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object)); if (check_refname_format(ref->buf, 0)) - die("'%s' is not a valid ref name.", ref->buf); + return error("'%s' is not a valid ref name.", ref->buf); if (read_ref(ref->buf, prev)) oidclr(prev); else if (!force) - die("replace ref '%s' already exists", ref->buf); + return error("replace ref '%s' already exists", ref->buf); + return 0; } static int replace_object_oid(const char *object_ref, @@ -164,28 +166,33 @@ static int replace_object_oid(const char *object_ref, struct strbuf ref = STRBUF_INIT; struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; + int res = 0; obj_type = oid_object_info(the_repository, object, NULL); repl_type = oid_object_info(the_repository, repl, NULL); if (!force && obj_type != repl_type) - die("Objects must be of the same type.\n" - "'%s' points to a replaced object of type '%s'\n" - "while '%s' points to a replacement object of type '%s'.", - object_ref, type_name(obj_type), - replace_ref, type_name(repl_type)); - - check_ref_valid(object, &prev, &ref, force); + return error("Objects must be of the same type.\n" + "'%s' points to a replaced object of type '%s'\n" + "while '%s' points to a replacement object of " + "type '%s'.", + object_ref, type_name(obj_type), + replace_ref, type_name(repl_type)); + + if (check_ref_valid(object, &prev, &ref, force)) { + strbuf_release(&ref); + return -1; + } transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, ref.buf, repl, &prev, 0, NULL, &err) || ref_transaction_commit(transaction, &err)) - die("%s", err.buf); + res = error("%s", err.buf); ref_transaction_free(transaction); strbuf_release(&ref); - return 0; + return res; } static int replace_object(const char *object_ref, const char *replace_ref, int force) @@ -193,9 +200,11 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f struct object_id object, repl; if (get_oid(object_ref, &object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + return error("Failed to resolve '%s' as a valid ref.", + object_ref); if (get_oid(replace_ref, &repl)) - die("Failed to resolve '%s' as a valid ref.", replace_ref); + return error("Failed to resolve '%s' as a valid ref.", + replace_ref); return replace_object_oid(object_ref, &object, replace_ref, &repl, force); } @@ -205,7 +214,7 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f * If "raw" is true, then the object's raw contents are printed according to * "type". Otherwise, we pretty-print the contents for human editing. */ -static void export_object(const struct object_id *oid, enum object_type type, +static int export_object(const struct object_id *oid, enum object_type type, int raw, const char *filename) { struct child_process cmd = CHILD_PROCESS_INIT; @@ -213,7 +222,7 @@ static void export_object(const struct object_id *oid, enum object_type type, fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) - die_errno("unable to open %s for writing", filename); + return error_errno("unable to open %s for writing", filename); argv_array_push(&cmd.args, "--no-replace-objects"); argv_array_push(&cmd.args, "cat-file"); @@ -226,7 +235,8 @@ static void export_object(const struct object_id *oid, enum object_type type, cmd.out = fd; if (run_command(&cmd)) - die("cat-file reported failure"); + return error("cat-file reported failure"); + return 0; } /* @@ -234,14 +244,14 @@ static void export_object(const struct object_id *oid, enum object_type type, * interpreting it as "type", and writing the result to the object database. * The sha1 of the written object is returned via sha1. */ -static void import_object(struct object_id *oid, enum object_type type, +static int import_object(struct object_id *oid, enum object_type type, int raw, const char *filename) { int fd; fd = open(filename, O_RDONLY); if (fd < 0) - die_errno("unable to open %s for reading", filename); + return error_errno("unable to open %s for reading", filename); if (!raw && type == OBJ_TREE) { const char *argv[] = { "mktree", NULL }; @@ -253,27 +263,40 @@ static void import_object(struct object_id *oid, enum object_type type, cmd.in = fd; cmd.out = -1; - if (start_command(&cmd)) - die("unable to spawn mktree"); + if (start_command(&cmd)) { + close(fd); + return error("unable to spawn mktree"); + } - if (strbuf_read(&result, cmd.out, 41) < 0) - die_errno("unable to read from mktree"); + if (strbuf_read(&result, cmd.out, 41) < 0) { + error_errno("unable to read from mktree"); + close(fd); + close(cmd.out); + return -1; + } close(cmd.out); - if (finish_command(&cmd)) - die("mktree reported failure"); - if (get_oid_hex(result.buf, oid) < 0) - die("mktree did not return an object name"); + if (finish_command(&cmd)) { + strbuf_release(&result); + return error("mktree reported failure"); + } + if (get_oid_hex(result.buf, oid) < 0) { + strbuf_release(&result); + return error("mktree did not return an object name"); + } strbuf_release(&result); } else { struct stat st; int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT; - if (fstat(fd, &st) < 0) - die_errno("unable to fstat %s", filename); + if (fstat(fd, &st) < 0) { + error_errno("unable to fstat %s", filename); + close(fd); + return -1; + } if (index_fd(oid, fd, &st, type, NULL, flags) < 0) - die("unable to write object to database"); + return error("unable to write object to database"); /* index_fd close()s fd for us */ } @@ -281,30 +304,43 @@ static void import_object(struct object_id *oid, enum object_type type, * No need to close(fd) here; both run-command and index-fd * will have done it for us. */ + return 0; } static int edit_and_replace(const char *object_ref, int force, int raw) { - char *tmpfile = git_pathdup("REPLACE_EDITOBJ"); + char *tmpfile; enum object_type type; struct object_id old_oid, new_oid, prev; struct strbuf ref = STRBUF_INIT; if (get_oid(object_ref, &old_oid) < 0) - die("Not a valid object name: '%s'", object_ref); + return error("Not a valid object name: '%s'", object_ref); type = oid_object_info(the_repository, &old_oid, NULL); if (type < 0) - die("unable to get object type for %s", oid_to_hex(&old_oid)); + return error("unable to get object type for %s", + oid_to_hex(&old_oid)); - check_ref_valid(&old_oid, &prev, &ref, force); + if (check_ref_valid(&old_oid, &prev, &ref, force)) { + strbuf_release(&ref); + return -1; + } strbuf_release(&ref); - export_object(&old_oid, type, raw, tmpfile); - if (launch_editor(tmpfile, NULL, NULL) < 0) - die("editing object file failed"); - import_object(&new_oid, type, raw, tmpfile); - + tmpfile = git_pathdup("REPLACE_EDITOBJ"); + if (export_object(&old_oid, type, raw, tmpfile)) { + free(tmpfile); + return -1; + } + if (launch_editor(tmpfile, NULL, NULL) < 0) { + free(tmpfile); + return error("editing object file failed"); + } + if (import_object(&new_oid, type, raw, tmpfile)) { + free(tmpfile); + return -1; + } free(tmpfile); if (!oidcmp(&old_oid, &new_oid)) @@ -313,7 +349,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw) return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force); } -static void replace_parents(struct strbuf *buf, int argc, const char **argv) +static int replace_parents(struct strbuf *buf, int argc, const char **argv) { struct strbuf new_parents = STRBUF_INIT; const char *parent_start, *parent_end; @@ -330,9 +366,15 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv) /* prepare new parents */ for (i = 0; i < argc; i++) { struct object_id oid; - if (get_oid(argv[i], &oid) < 0) - die(_("Not a valid object name: '%s'"), argv[i]); - lookup_commit_or_die(&oid, argv[i]); + if (get_oid(argv[i], &oid) < 0) { + strbuf_release(&new_parents); + return error(_("Not a valid object name: '%s'"), + argv[i]); + } + if (!lookup_commit_reference(&oid)) { + strbuf_release(&new_parents); + return error(_("could not parse %s"), argv[i]); + } strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid)); } @@ -341,6 +383,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv) new_parents.buf, new_parents.len); strbuf_release(&new_parents); + return 0; } struct check_mergetag_data { @@ -348,7 +391,7 @@ struct check_mergetag_data { const char **argv; }; -static void check_one_mergetag(struct commit *commit, +static int check_one_mergetag(struct commit *commit, struct commit_extra_header *extra, void *data) { @@ -361,33 +404,35 @@ static void check_one_mergetag(struct commit *commit, hash_object_file(extra->value, extra->len, type_name(OBJ_TAG), &tag_oid); tag = lookup_tag(&tag_oid); if (!tag) - die(_("bad mergetag in commit '%s'"), ref); + return error(_("bad mergetag in commit '%s'"), ref); if (parse_tag_buffer(tag, extra->value, extra->len)) - die(_("malformed mergetag in commit '%s'"), ref); + return error(_("malformed mergetag in commit '%s'"), ref); /* iterate over new parents */ for (i = 1; i < mergetag_data->argc; i++) { struct object_id oid; if (get_oid(mergetag_data->argv[i], &oid) < 0) - die(_("Not a valid object name: '%s'"), mergetag_data->argv[i]); + return error(_("Not a valid object name: '%s'"), + mergetag_data->argv[i]); if (!oidcmp(&tag->tagged->oid, &oid)) - return; /* found */ + return 0; /* found */ } - die(_("original commit '%s' contains mergetag '%s' that is discarded; " - "use --edit instead of --graft"), ref, oid_to_hex(&tag_oid)); + return error(_("original commit '%s' contains mergetag '%s' that is " + "discarded; use --edit instead of --graft"), ref, + oid_to_hex(&tag_oid)); } -static void check_mergetags(struct commit *commit, int argc, const char **argv) +static int check_mergetags(struct commit *commit, int argc, const char **argv) { struct check_mergetag_data mergetag_data; mergetag_data.argc = argc; mergetag_data.argv = argv; - for_each_mergetag(check_one_mergetag, commit, &mergetag_data); + return for_each_mergetag(check_one_mergetag, commit, &mergetag_data); } -static int create_graft(int argc, const char **argv, int force) +static int create_graft(int argc, const char **argv, int force, int gentle) { struct object_id old_oid, new_oid; const char *old_ref = argv[0]; @@ -397,33 +442,81 @@ static int create_graft(int argc, const char **argv, int force) unsigned long size; if (get_oid(old_ref, &old_oid) < 0) - die(_("Not a valid object name: '%s'"), old_ref); - commit = lookup_commit_or_die(&old_oid, old_ref); + return error(_("Not a valid object name: '%s'"), old_ref); + commit = lookup_commit_reference(&old_oid); + if (!commit) + return error(_("could not parse %s"), old_ref); buffer = get_commit_buffer(commit, &size); strbuf_add(&buf, buffer, size); unuse_commit_buffer(commit, buffer); - replace_parents(&buf, argc - 1, &argv[1]); + if (replace_parents(&buf, argc - 1, &argv[1]) < 0) { + strbuf_release(&buf); + return -1; + } if (remove_signature(&buf)) { warning(_("the original commit '%s' has a gpg signature."), old_ref); warning(_("the signature will be removed in the replacement commit!")); } - check_mergetags(commit, argc, argv); + if (check_mergetags(commit, argc, argv)) { + strbuf_release(&buf); + return -1; + } - if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) - die(_("could not write replacement commit for: '%s'"), old_ref); + if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) { + strbuf_release(&buf); + return error(_("could not write replacement commit for: '%s'"), + old_ref); + } strbuf_release(&buf); - if (!oidcmp(&old_oid, &new_oid)) + if (!oidcmp(&old_oid, &new_oid)) { + if (gentle) { + warning("graft for '%s' unnecessary", oid_to_hex(&old_oid)); + return 0; + } return error("new commit is the same as the old one: '%s'", oid_to_hex(&old_oid)); + } return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force); } +static int convert_graft_file(int force) +{ + const char *graft_file = get_graft_file(the_repository); + FILE *fp = fopen_or_warn(graft_file, "r"); + struct strbuf buf = STRBUF_INIT, err = STRBUF_INIT; + struct argv_array args = ARGV_ARRAY_INIT; + + if (!fp) + return -1; + + while (strbuf_getline(&buf, fp) != EOF) { + if (*buf.buf == '#') + continue; + + argv_array_split(&args, buf.buf); + if (args.argc && create_graft(args.argc, args.argv, force, 1)) + strbuf_addf(&err, "\n\t%s", buf.buf); + argv_array_clear(&args); + } + fclose(fp); + + strbuf_release(&buf); + + if (!err.len) + return unlink_or_warn(graft_file); + + warning(_("could not convert the following graft(s):\n%s"), err.buf); + strbuf_release(&err); + + return -1; +} + int cmd_replace(int argc, const char **argv, const char *prefix) { int force = 0; @@ -435,6 +528,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix) MODE_DELETE, MODE_EDIT, MODE_GRAFT, + MODE_CONVERT_GRAFT_FILE, MODE_REPLACE } cmdmode = MODE_UNSPECIFIED; struct option options[] = { @@ -442,6 +536,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix) OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE), OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT), OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT), + OPT_CMDMODE(0, "convert-graft-file", &cmdmode, N_("convert existing graft file"), MODE_CONVERT_GRAFT_FILE), OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"), PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")), @@ -464,7 +559,8 @@ int cmd_replace(int argc, const char **argv, const char *prefix) if (force && cmdmode != MODE_REPLACE && cmdmode != MODE_EDIT && - cmdmode != MODE_GRAFT) + cmdmode != MODE_GRAFT && + cmdmode != MODE_CONVERT_GRAFT_FILE) usage_msg_opt("-f only makes sense when writing a replacement", git_replace_usage, options); @@ -495,7 +591,13 @@ int cmd_replace(int argc, const char **argv, const char *prefix) if (argc < 1) usage_msg_opt("-g needs at least one argument", git_replace_usage, options); - return create_graft(argc, argv, force); + return create_graft(argc, argv, force, 0); + + case MODE_CONVERT_GRAFT_FILE: + if (argc != 0) + usage_msg_opt("--convert-graft-file takes no argument", + git_replace_usage, options); + return !!convert_graft_file(force); case MODE_LIST: if (argc > 1) @@ -504,6 +606,6 @@ int cmd_replace(int argc, const char **argv, const char *prefix) return list_replace_refs(argv[0], format); default: - die("BUG: invalid cmdmode %d", (int)cmdmode); + BUG("invalid cmdmode %d", (int)cmdmode); } } diff --git a/builtin/reset.c b/builtin/reset.c index a9136a163c..ffe41c924b 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -314,7 +314,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ - hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&oid, the_hash_algo->empty_tree); } else if (!pathspec.nr) { struct commit *commit; if (get_oid_committish(rev, &oid)) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index a8a9b506ff..2a6cb298bd 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -282,6 +282,10 @@ static int try_difference(const char *arg) struct commit *a, *b; a = lookup_commit_reference(&start_oid); b = lookup_commit_reference(&end_oid); + if (!a || !b) { + *dotdot = '.'; + return 0; + } exclude = get_merge_bases(a, b); while (exclude) { struct commit *commit = pop_commit(&exclude); @@ -328,12 +332,12 @@ static int try_parent_shorthands(const char *arg) return 0; *dotdot = 0; - if (get_oid_committish(arg, &oid)) { + if (get_oid_committish(arg, &oid) || + !(commit = lookup_commit_reference(&oid))) { *dotdot = '^'; return 0; } - commit = lookup_commit_reference(&oid); if (exclude_parent && exclude_parent > commit_list_count(commit->parents)) { *dotdot = '^'; @@ -888,8 +892,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die(_("Could not read the index")); if (the_index.split_index) { - const unsigned char *sha1 = the_index.split_index->base_sha1; - const char *path = git_path("sharedindex.%s", sha1_to_hex(sha1)); + const struct object_id *oid = &the_index.split_index->base_oid; + const char *path = git_path("sharedindex.%s", oid_to_hex(oid)); strbuf_reset(&buf); puts(relative_path(path, prefix, &buf)); } diff --git a/builtin/rm.c b/builtin/rm.c index 5b6fc7ee81..65b448ef8e 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -233,8 +233,6 @@ static int check_local_mod(struct object_id *head, int index_only) return errs; } -static struct lock_file lock_file; - static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; static int ignore_unmatch = 0; @@ -251,6 +249,7 @@ static struct option builtin_rm_options[] = { int cmd_rm(int argc, const char **argv, const char *prefix) { + struct lock_file lock_file = LOCK_INIT; int i; struct pathspec pathspec; char *seen; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index fc4f0bb5fb..42fd8d1a35 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -14,6 +14,7 @@ #include "sha1-array.h" #include "gpg-interface.h" #include "gettext.h" +#include "protocol.h" static const char * const send_pack_usage[] = { N_("git send-pack [--all | --mirror] [--dry-run] [--force] " @@ -120,13 +121,12 @@ static int send_pack_config(const char *k, const char *v, void *cb) } } } - return 0; + return git_default_config(k, v, cb); } int cmd_send_pack(int argc, const char **argv, const char *prefix) { - int i, nr_refspecs = 0; - const char **refspecs = NULL; + struct refspec rs = REFSPEC_INIT_PUSH; const char *remote_name = NULL; struct remote *remote = NULL; const char *dest = NULL; @@ -154,6 +154,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) int progress = -1; int from_stdin = 0; struct push_cas_option cas = {0}; + struct packet_reader reader; struct option options[] = { OPT__VERBOSITY(&verbose), @@ -187,8 +188,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0); if (argc > 0) { dest = argv[0]; - refspecs = (const char **)(argv + 1); - nr_refspecs = argc - 1; + refspec_appendn(&rs, argv + 1, argc - 1); } if (!dest) @@ -207,31 +207,23 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.push_options = push_options.nr ? &push_options : NULL; if (from_stdin) { - struct argv_array all_refspecs = ARGV_ARRAY_INIT; - - for (i = 0; i < nr_refspecs; i++) - argv_array_push(&all_refspecs, refspecs[i]); - if (args.stateless_rpc) { const char *buf; while ((buf = packet_read_line(0, NULL))) - argv_array_push(&all_refspecs, buf); + refspec_append(&rs, buf); } else { struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, stdin) != EOF) - argv_array_push(&all_refspecs, line.buf); + refspec_append(&rs, line.buf); strbuf_release(&line); } - - refspecs = all_refspecs.argv; - nr_refspecs = all_refspecs.argc; } /* * --all and --mirror are incompatible; neither makes sense * with any refspecs. */ - if ((nr_refspecs > 0 && (send_all || args.send_mirror)) || + if ((rs.nr > 0 && (send_all || args.send_mirror)) || (send_all && args.send_mirror)) usage_with_options(send_pack_usage, options); @@ -256,10 +248,22 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.verbose ? CONNECT_VERBOSE : 0); } - get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, - &extra_have, &shallow); - - transport_verify_remote_names(nr_refspecs, refspecs); + packet_reader_init(&reader, fd[0], NULL, 0, + PACKET_READ_CHOMP_NEWLINE | + PACKET_READ_GENTLE_ON_EOF); + + switch (discover_version(&reader)) { + case protocol_v2: + die("support for protocol v2 not implemented yet"); + break; + case protocol_v1: + case protocol_v0: + get_remote_heads(&reader, &remote_refs, REF_NORMAL, + &extra_have, &shallow); + break; + case protocol_unknown_version: + BUG("unknown protocol version"); + } local_refs = get_local_heads(); @@ -271,7 +275,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) flags |= MATCH_REFS_MIRROR; /* match them up */ - if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags)) + if (match_push_refs(local_refs, &remote_refs, &rs, flags)) return -1; if (!is_empty_cas(&cas)) diff --git a/builtin/serve.c b/builtin/serve.c new file mode 100644 index 0000000000..d3fd240bb3 --- /dev/null +++ b/builtin/serve.c @@ -0,0 +1,30 @@ +#include "cache.h" +#include "builtin.h" +#include "parse-options.h" +#include "serve.h" + +static char const * const serve_usage[] = { + N_("git serve [<options>]"), + NULL +}; + +int cmd_serve(int argc, const char **argv, const char *prefix) +{ + struct serve_options opts = SERVE_OPTIONS_INIT; + + struct option options[] = { + OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc, + N_("quit after a single request/response exchange")), + OPT_BOOL(0, "advertise-capabilities", &opts.advertise_capabilities, + N_("exit immediately after advertising capabilities")), + OPT_END() + }; + + /* ignore all unknown cmdline switches for now */ + argc = parse_options(argc, argv, prefix, options, serve_usage, + PARSE_OPT_KEEP_DASHDASH | + PARSE_OPT_KEEP_UNKNOWN); + serve(&opts); + + return 0; +} diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 6c2148b71d..f2e985c00a 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -7,6 +7,7 @@ #include "argv-array.h" #include "parse-options.h" #include "dir.h" +#include "commit-slab.h" static const char* show_branch_usage[] = { N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" @@ -21,6 +22,11 @@ static int showbranch_use_color = -1; static struct argv_array default_args = ARGV_ARRAY_INIT; +/* + * TODO: convert this use of commit->object.flags to commit-slab + * instead to store a pointer to ref name directly. Then use the same + * UNINTERESTING definition from revision.h here. + */ #define UNINTERESTING 01 #define REV_SHIFT 2 @@ -59,15 +65,27 @@ struct commit_name { int generation; /* how many parents away from head_name */ }; +define_commit_slab(commit_name_slab, struct commit_name *); +static struct commit_name_slab name_slab; + +static struct commit_name *commit_to_name(struct commit *commit) +{ + return *commit_name_slab_at(&name_slab, commit); +} + + /* Name the commit as nth generation ancestor of head_name; * we count only the first-parent relationship for naming purposes. */ static void name_commit(struct commit *commit, const char *head_name, int nth) { struct commit_name *name; - if (!commit->util) - commit->util = xmalloc(sizeof(struct commit_name)); - name = commit->util; + + name = *commit_name_slab_at(&name_slab, commit); + if (!name) { + name = xmalloc(sizeof(*name)); + *commit_name_slab_at(&name_slab, commit) = name; + } name->head_name = head_name; name->generation = nth; } @@ -79,8 +97,8 @@ static void name_commit(struct commit *commit, const char *head_name, int nth) */ static void name_parent(struct commit *commit, struct commit *parent) { - struct commit_name *commit_name = commit->util; - struct commit_name *parent_name = parent->util; + struct commit_name *commit_name = commit_to_name(commit); + struct commit_name *parent_name = commit_to_name(parent); if (!commit_name) return; if (!parent_name || @@ -94,12 +112,12 @@ static int name_first_parent_chain(struct commit *c) int i = 0; while (c) { struct commit *p; - if (!c->util) + if (!commit_to_name(c)) break; if (!c->parents) break; p = c->parents->item; - if (!p->util) { + if (!commit_to_name(p)) { name_parent(c, p); i++; } @@ -122,7 +140,7 @@ static void name_commits(struct commit_list *list, /* First give names to the given heads */ for (cl = list; cl; cl = cl->next) { c = cl->item; - if (c->util) + if (commit_to_name(c)) continue; for (i = 0; i < num_rev; i++) { if (rev[i] == c) { @@ -148,9 +166,9 @@ static void name_commits(struct commit_list *list, struct commit_name *n; int nth; c = cl->item; - if (!c->util) + if (!commit_to_name(c)) continue; - n = c->util; + n = commit_to_name(c); parents = c->parents; nth = 0; while (parents) { @@ -158,7 +176,7 @@ static void name_commits(struct commit_list *list, struct strbuf newname = STRBUF_INIT; parents = parents->next; nth++; - if (p->util) + if (commit_to_name(p)) continue; switch (n->generation) { case 0: @@ -271,7 +289,7 @@ static void show_one_commit(struct commit *commit, int no_name) { struct strbuf pretty = STRBUF_INIT; const char *pretty_str = "(unavailable)"; - struct commit_name *name = commit->util; + struct commit_name *name = commit_to_name(commit); if (commit->object.parsed) { pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); @@ -660,6 +678,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) OPT_END() }; + init_commit_name_slab(&name_slab); + git_config(git_show_branch_config, NULL); /* If nothing is specified, try the default first */ diff --git a/builtin/show-index.c b/builtin/show-index.c new file mode 100644 index 0000000000..a6e678809e --- /dev/null +++ b/builtin/show-index.c @@ -0,0 +1,86 @@ +#include "builtin.h" +#include "cache.h" +#include "pack.h" + +static const char show_index_usage[] = +"git show-index"; + +int cmd_show_index(int argc, const char **argv, const char *prefix) +{ + int i; + unsigned nr; + unsigned int version; + static unsigned int top_index[256]; + + if (argc != 1) + usage(show_index_usage); + if (fread(top_index, 2 * 4, 1, stdin) != 1) + die("unable to read header"); + if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) { + version = ntohl(top_index[1]); + if (version < 2 || version > 2) + die("unknown index version"); + if (fread(top_index, 256 * 4, 1, stdin) != 1) + die("unable to read index"); + } else { + version = 1; + if (fread(&top_index[2], 254 * 4, 1, stdin) != 1) + die("unable to read index"); + } + nr = 0; + for (i = 0; i < 256; i++) { + unsigned n = ntohl(top_index[i]); + if (n < nr) + die("corrupt index file"); + nr = n; + } + if (version == 1) { + for (i = 0; i < nr; i++) { + unsigned int offset, entry[6]; + + if (fread(entry, 4 + 20, 1, stdin) != 1) + die("unable to read entry %u/%u", i, nr); + offset = ntohl(entry[0]); + printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1))); + } + } else { + unsigned off64_nr = 0; + struct { + unsigned char sha1[20]; + uint32_t crc; + uint32_t off; + } *entries; + ALLOC_ARRAY(entries, nr); + for (i = 0; i < nr; i++) + if (fread(entries[i].sha1, 20, 1, stdin) != 1) + die("unable to read sha1 %u/%u", i, nr); + for (i = 0; i < nr; i++) + if (fread(&entries[i].crc, 4, 1, stdin) != 1) + die("unable to read crc %u/%u", i, nr); + for (i = 0; i < nr; i++) + if (fread(&entries[i].off, 4, 1, stdin) != 1) + die("unable to read 32b offset %u/%u", i, nr); + for (i = 0; i < nr; i++) { + uint64_t offset; + uint32_t off = ntohl(entries[i].off); + if (!(off & 0x80000000)) { + offset = off; + } else { + uint32_t off64[2]; + if ((off & 0x7fffffff) != off64_nr) + die("inconsistent 64b offset index"); + if (fread(off64, 8, 1, stdin) != 1) + die("unable to read 64b offset %u", off64_nr); + offset = (((uint64_t)ntohl(off64[0])) << 32) | + ntohl(off64[1]); + off64_nr++; + } + printf("%" PRIuMAX " %s (%08"PRIx32")\n", + (uintmax_t) offset, + sha1_to_hex(entries[i].sha1), + ntohl(entries[i].crc)); + } + free(entries); + } + return 0; +} diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a404df3ea4..20ae9191ca 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -12,6 +12,7 @@ #include "run-command.h" #include "remote.h" #include "refs.h" +#include "refspec.h" #include "connect.h" #include "revision.h" #include "diffcore.h" @@ -54,7 +55,7 @@ static char *get_default_remote(void) static int print_default_remote(int argc, const char **argv, const char *prefix) { - const char *remote; + char *remote; if (argc != 1) die(_("submodule--helper print-default-remote takes no arguments")); @@ -63,6 +64,7 @@ static int print_default_remote(int argc, const char **argv, const char *prefix) if (remote) printf("%s\n", remote); + free(remote); return 0; } @@ -439,6 +441,149 @@ static void for_each_listed_submodule(const struct module_list *list, fn(list->entries[i], cb_data); } +struct cb_foreach { + int argc; + const char **argv; + const char *prefix; + int quiet; + int recursive; +}; +#define CB_FOREACH_INIT { 0 } + +static void runcommand_in_submodule_cb(const struct cache_entry *list_item, + void *cb_data) +{ + struct cb_foreach *info = cb_data; + const char *path = list_item->name; + const struct object_id *ce_oid = &list_item->oid; + + const struct submodule *sub; + struct child_process cp = CHILD_PROCESS_INIT; + char *displaypath; + + displaypath = get_submodule_displaypath(path, info->prefix); + + sub = submodule_from_path(the_repository, &null_oid, path); + + if (!sub) + die(_("No url found for submodule path '%s' in .gitmodules"), + displaypath); + + if (!is_submodule_populated_gently(path, NULL)) + goto cleanup; + + prepare_submodule_repo_env(&cp.env_array); + + /* + * For the purpose of executing <command> in the submodule, + * separate shell is used for the purpose of running the + * child process. + */ + cp.use_shell = 1; + cp.dir = path; + + /* + * NEEDSWORK: the command currently has access to the variables $name, + * $sm_path, $displaypath, $sha1 and $toplevel only when the command + * contains a single argument. This is done for maintaining a faithful + * translation from shell script. + */ + if (info->argc == 1) { + char *toplevel = xgetcwd(); + struct strbuf sb = STRBUF_INIT; + + argv_array_pushf(&cp.env_array, "name=%s", sub->name); + argv_array_pushf(&cp.env_array, "sm_path=%s", path); + argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath); + argv_array_pushf(&cp.env_array, "sha1=%s", + oid_to_hex(ce_oid)); + argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel); + + /* + * Since the path variable was accessible from the script + * before porting, it is also made available after porting. + * The environment variable "PATH" has a very special purpose + * on windows. And since environment variables are + * case-insensitive in windows, it interferes with the + * existing PATH variable. Hence, to avoid that, we expose + * path via the args argv_array and not via env_array. + */ + sq_quote_buf(&sb, path); + argv_array_pushf(&cp.args, "path=%s; %s", + sb.buf, info->argv[0]); + strbuf_release(&sb); + free(toplevel); + } else { + argv_array_pushv(&cp.args, info->argv); + } + + if (!info->quiet) + printf(_("Entering '%s'\n"), displaypath); + + if (info->argv[0] && run_command(&cp)) + die(_("run_command returned non-zero status for %s\n."), + displaypath); + + if (info->recursive) { + struct child_process cpr = CHILD_PROCESS_INIT; + + cpr.git_cmd = 1; + cpr.dir = path; + prepare_submodule_repo_env(&cpr.env_array); + + argv_array_pushl(&cpr.args, "--super-prefix", NULL); + argv_array_pushf(&cpr.args, "%s/", displaypath); + argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive", + NULL); + + if (info->quiet) + argv_array_push(&cpr.args, "--quiet"); + + argv_array_pushv(&cpr.args, info->argv); + + if (run_command(&cpr)) + die(_("run_command returned non-zero status while" + "recursing in the nested submodules of %s\n."), + displaypath); + } + +cleanup: + free(displaypath); +} + +static int module_foreach(int argc, const char **argv, const char *prefix) +{ + struct cb_foreach info = CB_FOREACH_INIT; + struct pathspec pathspec; + struct module_list list = MODULE_LIST_INIT; + + struct option module_foreach_options[] = { + OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")), + OPT_BOOL(0, "recursive", &info.recursive, + N_("Recurse into nested submodules")), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule--helper foreach [--quiet] [--recursive] <command>"), + NULL + }; + + argc = parse_options(argc, argv, prefix, module_foreach_options, + git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN); + + if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) + return 1; + + info.argc = argc; + info.argv = argv; + info.prefix = prefix; + + for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info); + + return 0; +} + struct init_cb { const char *prefix; unsigned int flags; @@ -455,7 +600,7 @@ static void init_submodule(const char *path, const char *prefix, displaypath = get_submodule_displaypath(path, prefix); - sub = submodule_from_path(&null_oid, path); + sub = submodule_from_path(the_repository, &null_oid, path); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), @@ -596,8 +741,12 @@ static void print_status(unsigned int flags, char state, const char *path, printf("%c%s %s", state, oid_to_hex(oid), displaypath); - if (state == ' ' || state == '+') - printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); + if (state == ' ' || state == '+') { + const char *name = compute_rev_name(path, oid_to_hex(oid)); + + if (name) + printf(" (%s)", name); + } printf("\n"); } @@ -622,7 +771,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, struct rev_info rev; int diff_files_result; - if (!submodule_from_path(&null_oid, path)) + if (!submodule_from_path(the_repository, &null_oid, path)) die(_("no submodule mapping found in .gitmodules for path '%s'"), path); @@ -746,7 +895,7 @@ static int module_name(int argc, const char **argv, const char *prefix) if (argc != 2) usage(_("git submodule--helper name <path>")); - sub = submodule_from_path(&null_oid, argv[1]); + sub = submodule_from_path(the_repository, &null_oid, argv[1]); if (!sub) die(_("no submodule mapping found in .gitmodules for path '%s'"), @@ -777,7 +926,7 @@ static void sync_submodule(const char *path, const char *prefix, if (!is_submodule_active(the_repository, path)) return; - sub = submodule_from_path(&null_oid, path); + sub = submodule_from_path(the_repository, &null_oid, path); if (sub && sub->url) { if (starts_with_dot_dot_slash(sub->url) || @@ -930,7 +1079,7 @@ static void deinit_submodule(const char *path, const char *prefix, struct strbuf sb_config = STRBUF_INIT; char *sub_git_dir = xstrfmt("%s/.git", path); - sub = submodule_from_path(&null_oid, path); + sub = submodule_from_path(the_repository, &null_oid, path); if (!sub || !sub->name) goto cleanup; @@ -1061,7 +1210,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix) } static int clone_submodule(const char *path, const char *gitdir, const char *url, - const char *depth, struct string_list *reference, + const char *depth, struct string_list *reference, int dissociate, int quiet, int progress) { struct child_process cp = CHILD_PROCESS_INIT; @@ -1080,6 +1229,8 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url argv_array_pushl(&cp.args, "--reference", item->string, NULL); } + if (dissociate) + argv_array_push(&cp.args, "--dissociate"); if (gitdir && *gitdir) argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL); @@ -1195,6 +1346,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) char *p, *path = NULL, *sm_gitdir; struct strbuf sb = STRBUF_INIT; struct string_list reference = STRING_LIST_INIT_NODUP; + int dissociate = 0; char *sm_alternate = NULL, *error_strategy = NULL; struct option module_clone_options[] = { @@ -1213,6 +1365,8 @@ static int module_clone(int argc, const char **argv, const char *prefix) OPT_STRING_LIST(0, "reference", &reference, N_("repo"), N_("reference repository")), + OPT_BOOL(0, "dissociate", &dissociate, + N_("use --reference only while cloning")), OPT_STRING(0, "depth", &depth, N_("string"), N_("depth for shallow clones")), @@ -1252,7 +1406,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) prepare_possible_alternates(name, &reference); - if (clone_submodule(path, sm_gitdir, url, depth, &reference, + if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate, quiet, progress)) die(_("clone of '%s' into submodule path '%s' failed"), url, path); @@ -1264,8 +1418,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) strbuf_reset(&sb); } - /* Connect module worktree and git dir */ - connect_work_tree_and_git_dir(path, sm_gitdir); + connect_work_tree_and_git_dir(path, sm_gitdir, 0); p = git_pathdup_submodule(path, "config"); if (!p) @@ -1305,6 +1458,7 @@ struct submodule_update_clone { int quiet; int recommend_shallow; struct string_list references; + int dissociate; const char *depth; const char *recursive_prefix; const char *prefix; @@ -1320,7 +1474,7 @@ struct submodule_update_clone { int failed_clones_nr, failed_clones_alloc; }; #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \ - SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \ + SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \ NULL, NULL, NULL, \ STRING_LIST_INIT_DUP, 0, NULL, 0, 0} @@ -1372,7 +1526,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, goto cleanup; } - sub = submodule_from_path(&null_oid, ce->name); + sub = submodule_from_path(the_repository, &null_oid, ce->name); if (suc->recursive_prefix) displaypath = relative_path(suc->recursive_prefix, @@ -1447,6 +1601,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, for_each_string_list_item(item, &suc->references) argv_array_pushl(&child->args, "--reference", item->string, NULL); } + if (suc->dissociate) + argv_array_push(&child->args, "--dissociate"); if (suc->depth) argv_array_push(&child->args, suc->depth); @@ -1580,6 +1736,8 @@ static int update_clone(int argc, const char **argv, const char *prefix) N_("rebase, merge, checkout or none")), OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"), N_("reference repository")), + OPT_BOOL(0, "dissociate", &suc.dissociate, + N_("use --reference only while cloning")), OPT_STRING(0, "depth", &suc.depth, "<depth>", N_("Create a shallow clone truncated to the " "specified number of revisions")), @@ -1655,7 +1813,7 @@ static const char *remote_submodule_branch(const char *path) const char *branch = NULL; char *key; - sub = submodule_from_path(&null_oid, path); + sub = submodule_from_path(the_repository, &null_oid, path); if (!sub) return NULL; @@ -1740,13 +1898,14 @@ static int push_check(int argc, const char **argv, const char *prefix) /* Check the refspec */ if (argc > 2) { - int i, refspec_nr = argc - 2; + int i; struct ref *local_refs = get_local_heads(); - struct refspec *refspec = parse_push_refspec(refspec_nr, - argv + 2); + struct refspec refspec = REFSPEC_INIT_PUSH; - for (i = 0; i < refspec_nr; i++) { - struct refspec *rs = refspec + i; + refspec_appendn(&refspec, argv + 2, argc - 2); + + for (i = 0; i < refspec.nr; i++) { + const struct refspec_item *rs = &refspec.items[i]; if (rs->pattern || rs->matching) continue; @@ -1773,7 +1932,7 @@ static int push_check(int argc, const char **argv, const char *prefix) rs->src); } } - free_refspec(refspec_nr, refspec); + refspec_clear(&refspec); } free(head); @@ -1822,6 +1981,29 @@ static int is_active(int argc, const char **argv, const char *prefix) return !is_submodule_active(the_repository, argv[1]); } +/* + * Exit non-zero if any of the submodule names given on the command line is + * invalid. If no names are given, filter stdin to print only valid names + * (which is primarily intended for testing). + */ +static int check_name(int argc, const char **argv, const char *prefix) +{ + if (argc > 1) { + while (*++argv) { + if (check_submodule_name(*argv) < 0) + return 1; + } + } else { + struct strbuf buf = STRBUF_INIT; + while (strbuf_getline(&buf, stdin) != EOF) { + if (!check_submodule_name(buf.buf)) + printf("%s\n", buf.buf); + } + strbuf_release(&buf); + } + return 0; +} + #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct { @@ -1838,6 +2020,7 @@ static struct cmd_struct commands[] = { {"relative-path", resolve_relative_path, 0}, {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, + {"foreach", module_foreach, SUPPORT_SUPER_PREFIX}, {"init", module_init, SUPPORT_SUPER_PREFIX}, {"status", module_status, SUPPORT_SUPER_PREFIX}, {"print-default-remote", print_default_remote, 0}, @@ -1847,6 +2030,7 @@ static struct cmd_struct commands[] = { {"push-check", push_check, 0}, {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, {"is-active", is_active, 0}, + {"check-name", check_name, 0}, }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) diff --git a/builtin/tag.c b/builtin/tag.c index f872f14f65..9919b03b2d 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -119,7 +119,7 @@ static int verify_tag(const char *name, const char *ref, return -1; if (format->format) - pretty_print_ref(name, oid->hash, format); + pretty_print_ref(name, oid, format); return 0; } diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index ded798b72f..cf585fcc5e 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -211,7 +211,7 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op if (!obj_buf) die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid)); if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options)) - die("Error in object"); + die("fsck error in packed object"); fsck_options.walk = check_object; if (fsck_walk(obj, NULL, &fsck_options)) die("Error on reachable objects of %s", oid_to_hex(&obj->oid)); @@ -573,8 +573,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) unpack_all(); the_hash_algo->update_fn(&ctx, buffer, offset); the_hash_algo->final_fn(oid.hash, &ctx); - if (strict) + if (strict) { write_rest(); + if (fsck_finish(&fsck_options)) + die(_("fsck error in pack objects")); + } if (hashcmp(fill(the_hash_algo->rawsz), oid.hash)) die("final sha1 did not match"); use(the_hash_algo->rawsz); diff --git a/builtin/update-index.c b/builtin/update-index.c index 10d070a76f..a8709a26ec 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -364,10 +364,9 @@ static int process_directory(const char *path, int len, struct stat *st) return error("%s: is a directory - add files inside instead", path); } -static int process_path(const char *path) +static int process_path(const char *path, struct stat *st, int stat_errno) { int pos, len; - struct stat st; const struct cache_entry *ce; len = strlen(path); @@ -391,13 +390,13 @@ static int process_path(const char *path) * First things first: get the stat information, to decide * what to do about the pathname! */ - if (lstat(path, &st) < 0) - return process_lstat_error(path, errno); + if (stat_errno) + return process_lstat_error(path, stat_errno); - if (S_ISDIR(st.st_mode)) - return process_directory(path, len, &st); + if (S_ISDIR(st->st_mode)) + return process_directory(path, len, st); - return add_one_path(ce, path, len, &st); + return add_one_path(ce, path, len, st); } static int add_cacheinfo(unsigned int mode, const struct object_id *oid, @@ -406,7 +405,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid, int size, len, option; struct cache_entry *ce; - if (!verify_path(path)) + if (!verify_path(path, mode)) return error("Invalid path '%s'", path); len = strlen(path); @@ -449,7 +448,18 @@ static void chmod_path(char flip, const char *path) static void update_one(const char *path) { - if (!verify_path(path)) { + int stat_errno = 0; + struct stat st; + + if (mark_valid_only || mark_skip_worktree_only || force_remove || + mark_fsmonitor_only) + st.st_mode = 0; + else if (lstat(path, &st) < 0) { + st.st_mode = 0; + stat_errno = errno; + } /* else stat is valid */ + + if (!verify_path(path, st.st_mode)) { fprintf(stderr, "Ignoring path %s\n", path); return; } @@ -475,7 +485,7 @@ static void update_one(const char *path) report("remove '%s'", path); return; } - if (process_path(path)) + if (process_path(path, &st, stat_errno)) die("Unable to process path %s", path); report("add '%s'", path); } @@ -545,7 +555,7 @@ static void read_index_info(int nul_term_line) path_name = uq.buf; } - if (!verify_path(path_name)) { + if (!verify_path(path_name, mode)) { fprintf(stderr, "Ignoring path %s\n", path_name); continue; } @@ -1164,7 +1174,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) report(_("Untracked cache enabled for '%s'"), get_git_work_tree()); break; default: - die("BUG: bad untracked_cache value: %d", untracked_cache); + BUG("bad untracked_cache value: %d", untracked_cache); } if (fsmonitor > 0) { diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 4b4714b3fd..4fa3c0a86f 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -311,11 +311,12 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction, static const char *parse_cmd_option(struct strbuf *input, const char *next) { - if (!strncmp(next, "no-deref", 8) && next[8] == line_termination) + const char *rest; + if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination) update_flags |= REF_NO_DEREF; else die("option unknown: %s", next); - return next + 8; + return rest; } static void update_refs_stdin(struct ref_transaction *transaction) @@ -332,16 +333,16 @@ static void update_refs_stdin(struct ref_transaction *transaction) die("empty command in input"); else if (isspace(*next)) die("whitespace before command: %s", next); - else if (starts_with(next, "update ")) - next = parse_cmd_update(transaction, &input, next + 7); - else if (starts_with(next, "create ")) - next = parse_cmd_create(transaction, &input, next + 7); - else if (starts_with(next, "delete ")) - next = parse_cmd_delete(transaction, &input, next + 7); - else if (starts_with(next, "verify ")) - next = parse_cmd_verify(transaction, &input, next + 7); - else if (starts_with(next, "option ")) - next = parse_cmd_option(&input, next + 7); + else if (skip_prefix(next, "update ", &next)) + next = parse_cmd_update(transaction, &input, next); + else if (skip_prefix(next, "create ", &next)) + next = parse_cmd_create(transaction, &input, next); + else if (skip_prefix(next, "delete ", &next)) + next = parse_cmd_delete(transaction, &input, next); + else if (skip_prefix(next, "verify ", &next)) + next = parse_cmd_verify(transaction, &input, next); + else if (skip_prefix(next, "option ", &next)) + next = parse_cmd_option(&input, next); else die("unknown command: %s", next); diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c new file mode 100644 index 0000000000..decde5a3b1 --- /dev/null +++ b/builtin/upload-pack.c @@ -0,0 +1,74 @@ +#include "cache.h" +#include "builtin.h" +#include "exec-cmd.h" +#include "pkt-line.h" +#include "parse-options.h" +#include "protocol.h" +#include "upload-pack.h" +#include "serve.h" + +static const char * const upload_pack_usage[] = { + N_("git upload-pack [<options>] <dir>"), + NULL +}; + +int cmd_upload_pack(int argc, const char **argv, const char *prefix) +{ + const char *dir; + int strict = 0; + struct upload_pack_options opts = { 0 }; + struct serve_options serve_opts = SERVE_OPTIONS_INIT; + struct option options[] = { + OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc, + N_("quit after a single request/response exchange")), + OPT_BOOL(0, "advertise-refs", &opts.advertise_refs, + N_("exit immediately after initial ref advertisement")), + OPT_BOOL(0, "strict", &strict, + N_("do not try <directory>/.git/ if <directory> is no Git directory")), + OPT_INTEGER(0, "timeout", &opts.timeout, + N_("interrupt transfer after <n> seconds of inactivity")), + OPT_END() + }; + + packet_trace_identity("upload-pack"); + check_replace_refs = 0; + + argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0); + + if (argc != 1) + usage_with_options(upload_pack_usage, options); + + if (opts.timeout) + opts.daemon_mode = 1; + + setup_path(); + + dir = argv[0]; + + if (!enter_repo(dir, strict)) + die("'%s' does not appear to be a git repository", dir); + + switch (determine_protocol_version_server()) { + case protocol_v2: + serve_opts.advertise_capabilities = opts.advertise_refs; + serve_opts.stateless_rpc = opts.stateless_rpc; + serve(&serve_opts); + break; + case protocol_v1: + /* + * v1 is just the original protocol with a version string, + * so just fall through after writing the version string. + */ + if (opts.advertise_refs || !opts.stateless_rpc) + packet_write_fmt(1, "version 1\n"); + + /* fallthrough */ + case protocol_v0: + upload_pack(&opts); + break; + case protocol_unknown_version: + BUG("unknown protocol version"); + } + + return 0; +} diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index ad7b79fa5c..6fa04b751a 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -72,7 +72,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) } if (format.format) - pretty_print_ref(name, oid.hash, &format); + pretty_print_ref(name, &oid, &format); } return had_error; } diff --git a/builtin/worktree.c b/builtin/worktree.c index 40a438ed6c..5c7d2bb180 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -29,8 +29,6 @@ struct add_opts { int detach; int checkout; int keep_locked; - const char *new_branch; - int force_new_branch; }; static int show_only; @@ -298,8 +296,6 @@ static int add_worktree(const char *path, const char *refname, strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); - fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name); - argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); cp.git_cmd = 1; @@ -366,18 +362,75 @@ done: return ret; } +static void print_preparing_worktree_line(int detach, + const char *branch, + const char *new_branch, + int force_new_branch) +{ + if (force_new_branch) { + struct commit *commit = lookup_commit_reference_by_name(new_branch); + if (!commit) + printf_ln(_("Preparing worktree (new branch '%s')"), new_branch); + else + printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"), + new_branch, + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); + } else if (new_branch) { + printf_ln(_("Preparing worktree (new branch '%s')"), new_branch); + } else { + struct strbuf s = STRBUF_INIT; + if (!detach && !strbuf_check_branch_ref(&s, branch) && + ref_exists(s.buf)) + printf_ln(_("Preparing worktree (checking out '%s')"), + branch); + else { + struct commit *commit = lookup_commit_reference_by_name(branch); + if (!commit) + die(_("invalid reference: %s"), branch); + printf_ln(_("Preparing worktree (detached HEAD %s)"), + find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); + } + strbuf_release(&s); + } +} + +static const char *dwim_branch(const char *path, const char **new_branch) +{ + int n; + const char *s = worktree_basename(path, &n); + const char *branchname = xstrndup(s, n); + struct strbuf ref = STRBUF_INIT; + + UNLEAK(branchname); + if (!strbuf_check_branch_ref(&ref, branchname) && + ref_exists(ref.buf)) { + strbuf_release(&ref); + return branchname; + } + + *new_branch = branchname; + if (guess_remote) { + struct object_id oid; + const char *remote = + unique_tracking_name(*new_branch, &oid); + return remote; + } + return NULL; +} + static int add(int ac, const char **av, const char *prefix) { struct add_opts opts; const char *new_branch_force = NULL; char *path; const char *branch; + const char *new_branch = NULL; const char *opt_track = NULL; struct option options[] = { OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree"), PARSE_OPT_NOCOMPLETE), - OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), + OPT_STRING('b', NULL, &new_branch, N_("branch"), N_("create a new branch")), OPT_STRING('B', NULL, &new_branch_force, N_("branch"), N_("create or reset a branch")), @@ -395,7 +448,7 @@ static int add(int ac, const char **av, const char *prefix) 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) + if (!!opts.detach + !!new_branch + !!new_branch_force > 1) die(_("-b, -B, and --detach are mutually exclusive")); if (ac < 1 || ac > 2) usage_with_options(worktree_usage, options); @@ -406,33 +459,25 @@ static int add(int ac, const char **av, const char *prefix) if (!strcmp(branch, "-")) branch = "@{-1}"; - opts.force_new_branch = !!new_branch_force; - if (opts.force_new_branch) { + if (new_branch_force) { struct strbuf symref = STRBUF_INIT; - opts.new_branch = new_branch_force; + new_branch = new_branch_force; if (!opts.force && - !strbuf_check_branch_ref(&symref, opts.new_branch) && + !strbuf_check_branch_ref(&symref, new_branch) && ref_exists(symref.buf)) die_if_checked_out(symref.buf, 0); strbuf_release(&symref); } - if (ac < 2 && !opts.new_branch && !opts.detach) { - int n; - const char *s = worktree_basename(path, &n); - opts.new_branch = xstrndup(s, n); - if (guess_remote) { - struct object_id oid; - const char *remote = - unique_tracking_name(opts.new_branch, &oid); - if (remote) - branch = remote; - } + if (ac < 2 && !new_branch && !opts.detach) { + const char *s = dwim_branch(path, &new_branch); + if (s) + branch = s; } - if (ac == 2 && !opts.new_branch && !opts.detach) { + if (ac == 2 && !new_branch && !opts.detach) { struct object_id oid; struct commit *commit; const char *remote; @@ -441,25 +486,27 @@ static int add(int ac, const char **av, const char *prefix) if (!commit) { remote = unique_tracking_name(branch, &oid); if (remote) { - opts.new_branch = branch; + new_branch = branch; branch = remote; } } } - if (opts.new_branch) { + print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force); + + if (new_branch) { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; argv_array_push(&cp.args, "branch"); - if (opts.force_new_branch) + if (new_branch_force) argv_array_push(&cp.args, "--force"); - argv_array_push(&cp.args, opts.new_branch); + argv_array_push(&cp.args, new_branch); argv_array_push(&cp.args, branch); if (opt_track) argv_array_push(&cp.args, opt_track); if (run_command(&cp)) return -1; - branch = opts.new_branch; + branch = new_branch; } else if (opt_track) { die(_("--[no-]track can only be used if a new branch is created")); } @@ -783,8 +830,9 @@ static int remove_worktree(int ac, const char **av, const char *prefix) { int force = 0; struct option options[] = { - OPT_BOOL(0, "force", &force, - N_("force removing even if the worktree is dirty")), + OPT__FORCE(&force, + N_("force removing even if the worktree is dirty"), + PARSE_OPT_NOCOMPLETE), OPT_END() }; struct worktree **worktrees, *wt; |