diff options
Diffstat (limited to 'builtin')
65 files changed, 2165 insertions, 1417 deletions
diff --git a/builtin/add.c b/builtin/add.c index d7e3e44d06..459208a326 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -15,6 +15,7 @@ #include "diffcore.h" #include "revision.h" #include "bulk-checkin.h" +#include "argv-array.h" static const char * const builtin_add_usage[] = { N_("git add [options] [--] <pathspec>..."), @@ -26,55 +27,8 @@ static int take_worktree_changes; struct update_callback_data { int flags; int add_errors; - const char *implicit_dot; - size_t implicit_dot_len; - - /* only needed for 2.0 transition preparation */ - int warn_add_would_remove; }; -static const char *option_with_implicit_dot; -static const char *short_option_with_implicit_dot; - -static void warn_pathless_add(void) -{ - static int shown; - assert(option_with_implicit_dot && short_option_with_implicit_dot); - - if (shown) - return; - shown = 1; - - /* - * To be consistent with "git add -p" and most Git - * commands, we should default to being tree-wide, but - * this is not the original behavior and can't be - * changed until users trained themselves not to type - * "git add -u" or "git add -A". For now, we warn and - * keep the old behavior. Later, the behavior can be changed - * to tree-wide, keeping the warning for a while, and - * eventually we can drop the warning. - */ - warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n" - "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n" - "To add content for the whole tree, run:\n" - "\n" - " git add %s :/\n" - " (or git add %s :/)\n" - "\n" - "To restrict the command to the current directory, run:\n" - "\n" - " git add %s .\n" - " (or git add %s .)\n" - "\n" - "With the current Git version, the command is restricted to " - "the current directory.\n" - ""), - option_with_implicit_dot, short_option_with_implicit_dot, - option_with_implicit_dot, short_option_with_implicit_dot, - option_with_implicit_dot, short_option_with_implicit_dot); -} - static int fix_unmerged_status(struct diff_filepair *p, struct update_callback_data *data) { @@ -96,49 +50,15 @@ static int fix_unmerged_status(struct diff_filepair *p, return DIFF_STATUS_MODIFIED; } -static const char *add_would_remove_warning = N_( - "You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n" -"whose behaviour will change in Git 2.0 with respect to paths you removed.\n" -"Paths like '%s' that are\n" -"removed from your working tree are ignored with this version of Git.\n" -"\n" -"* 'git add --ignore-removal <pathspec>', which is the current default,\n" -" ignores paths you removed from your working tree.\n" -"\n" -"* 'git add --all <pathspec>' will let you also record the removals.\n" -"\n" -"Run 'git status' to check the paths you removed from your working tree.\n"); - -static void warn_add_would_remove(const char *path) -{ - warning(_(add_would_remove_warning), path); -} - static void update_callback(struct diff_queue_struct *q, struct diff_options *opt, void *cbdata) { int i; struct update_callback_data *data = cbdata; - const char *implicit_dot = data->implicit_dot; - size_t implicit_dot_len = data->implicit_dot_len; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; - /* - * Check if "git add -A" or "git add -u" was run from a - * subdirectory with a modified file outside that directory, - * and warn if so. - * - * "git add -u" will behave like "git add -u :/" instead of - * "git add -u ." in the future. This warning prepares for - * that change. - */ - if (implicit_dot && - strncmp_icase(path, implicit_dot, implicit_dot_len)) { - warn_pathless_add(); - continue; - } switch (fix_unmerged_status(p, data)) { default: die(_("unexpected diff status %c"), p->status); @@ -151,10 +71,6 @@ static void update_callback(struct diff_queue_struct *q, } break; case DIFF_STATUS_DELETED: - if (data->warn_add_would_remove) { - warn_add_would_remove(path); - data->warn_add_would_remove = 0; - } if (data->flags & ADD_CACHE_IGNORE_REMOVAL) break; if (!(data->flags & ADD_CACHE_PRETEND)) @@ -166,37 +82,28 @@ static void update_callback(struct diff_queue_struct *q, } } -static void update_files_in_cache(const char *prefix, - const struct pathspec *pathspec, - struct update_callback_data *data) +int add_files_to_cache(const char *prefix, + const struct pathspec *pathspec, int flags) { + struct update_callback_data data; struct rev_info rev; + memset(&data, 0, sizeof(data)); + data.flags = flags; + init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); if (pathspec) copy_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; - rev.diffopt.format_callback_data = data; + rev.diffopt.format_callback_data = &data; rev.max_count = 0; /* do not compare unmerged paths with stage #2 */ run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); -} - -int add_files_to_cache(const char *prefix, - const struct pathspec *pathspec, int flags) -{ - struct update_callback_data data; - - memset(&data, 0, sizeof(data)); - data.flags = flags; - update_files_in_cache(prefix, pathspec, &data); return !!data.add_errors; } -#define WARN_IMPLICIT_DOT (1u << 0) -static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, - int prefix, unsigned flag) +static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix) { char *seen; int i; @@ -208,19 +115,8 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (match_pathspec_depth(pathspec, entry->name, entry->len, - prefix, seen)) + if (dir_path_match(entry, pathspec, prefix, seen)) *dst++ = entry; - else if (flag & WARN_IMPLICIT_DOT) - /* - * "git add -A" was run from a subdirectory with a - * new file outside that directory. - * - * "git add -A" will behave like "git add -A :/" - * instead of "git add -A ." in the future. - * Warn about the coming behavior change. - */ - warn_pathless_add(); } dir->nr = dst - dir->entries; add_pathspec_matches_against_index(pathspec, seen); @@ -246,23 +142,21 @@ static void refresh(int verbose, const struct pathspec *pathspec) int run_add_interactive(const char *revision, const char *patch_mode, const struct pathspec *pathspec) { - int status, ac, i; - const char **args; + int status, i; + struct argv_array argv = ARGV_ARRAY_INIT; - args = xcalloc(sizeof(const char *), (pathspec->nr + 6)); - ac = 0; - args[ac++] = "add--interactive"; + argv_array_push(&argv, "add--interactive"); if (patch_mode) - args[ac++] = patch_mode; + argv_array_push(&argv, patch_mode); if (revision) - args[ac++] = revision; - args[ac++] = "--"; + argv_array_push(&argv, revision); + argv_array_push(&argv, "--"); for (i = 0; i < pathspec->nr; i++) /* pass original pathspec, to be re-parsed */ - args[ac++] = pathspec->items[i].original; + argv_array_push(&argv, pathspec->items[i].original); - status = run_command_v_opt(args, RUN_GIT_CMD); - free(args); + status = run_command_v_opt(argv.argv, RUN_GIT_CMD); + argv_array_clear(&argv); return status; } @@ -339,7 +233,7 @@ N_("The following paths are ignored by one of your .gitignore files:\n"); static int verbose, show_only, ignored_too, refresh_only; static int ignore_add_errors, intent_to_add, ignore_missing; -#define ADDREMOVE_DEFAULT 0 /* Change to 1 in Git 2.0 */ +#define ADDREMOVE_DEFAULT 1 static int addremove = ADDREMOVE_DEFAULT; static int addremove_explicit = -1; /* unspecified */ @@ -412,8 +306,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) int add_new_files; int require_pathspec; char *seen = NULL; - int implicit_dot = 0; - struct update_callback_data update_data; git_config(add_config, NULL); @@ -437,36 +329,17 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); - /* - * Warn when "git add pathspec..." was given without "-u" or "-A" - * and pathspec... covers a removed path. - */ - memset(&update_data, 0, sizeof(update_data)); - if (!take_worktree_changes && addremove_explicit < 0) - update_data.warn_add_would_remove = 1; - if (!take_worktree_changes && addremove_explicit < 0 && argc) - /* - * Turn "git add pathspec..." to "git add -A pathspec..." - * in Git 2.0 but not yet - */ - ; /* addremove = 1; */ + /* Turn "git add pathspec..." to "git add -A pathspec..." */ + addremove = 1; if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); - if (addremove) { - option_with_implicit_dot = "--all"; - short_option_with_implicit_dot = "-A"; - } - if (take_worktree_changes) { - option_with_implicit_dot = "--update"; - short_option_with_implicit_dot = "-u"; - } - if (option_with_implicit_dot && !argc) { - static const char *here[2] = { ".", NULL }; + + if ((0 < addremove_explicit || take_worktree_changes) && !argc) { + static const char *whole[2] = { ":/", NULL }; argc = 1; - argv = here; - implicit_dot = 1; + argv = whole; } add_new_files = !take_worktree_changes && !refresh_only; @@ -479,8 +352,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) - ? ADD_CACHE_IGNORE_REMOVAL : 0)) | - (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0); + ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); @@ -514,18 +386,15 @@ int cmd_add(int argc, const char **argv, const char *prefix) memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); + baselen = fill_directory(&dir, &pathspec); if (pathspec.nr) - seen = prune_directory(&dir, &pathspec, baselen, - implicit_dot ? WARN_IMPLICIT_DOT : 0); + seen = prune_directory(&dir, &pathspec, baselen); } if (refresh_only) { refresh(verbose, &pathspec); goto finish; } - if (implicit_dot && prefix) - refresh_cache(REFRESH_QUIET); if (pathspec.nr) { int i; @@ -540,10 +409,13 @@ int cmd_add(int argc, const char **argv, const char *prefix) PATHSPEC_FROMTOP | PATHSPEC_LITERAL | PATHSPEC_GLOB | - PATHSPEC_ICASE); + PATHSPEC_ICASE | + PATHSPEC_EXCLUDE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; + if (pathspec.items[i].magic & PATHSPEC_EXCLUDE) + continue; if (!seen[i] && path[0] && ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) || @@ -562,21 +434,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) plug_bulk_checkin(); - if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) { - /* - * Check for modified files throughout the worktree so - * update_callback has a chance to warn about changes - * outside the cwd. - */ - update_data.implicit_dot = prefix; - update_data.implicit_dot_len = strlen(prefix); - free_pathspec(&pathspec); - memset(&pathspec, 0, sizeof(pathspec)); - } - update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; - update_files_in_cache(prefix, &pathspec, &update_data); + exit_status |= add_files_to_cache(prefix, &pathspec, flags); - exit_status |= !!update_data.add_errors; if (add_new_files) exit_status |= add_files(&dir, flags); diff --git a/builtin/apply.c b/builtin/apply.c index ef32e4f624..0189523798 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1409,10 +1409,10 @@ static void recount_diff(const char *line, int size, struct fragment *fragment) case '\\': continue; case '@': - ret = size < 3 || prefixcmp(line, "@@ "); + ret = size < 3 || !starts_with(line, "@@ "); break; case 'd': - ret = size < 5 || prefixcmp(line, "diff "); + ret = size < 5 || !starts_with(line, "diff "); break; default: ret = -1; @@ -1798,11 +1798,11 @@ static struct fragment *parse_binary_hunk(char **buf_p, *status_p = 0; - if (!prefixcmp(buffer, "delta ")) { + if (starts_with(buffer, "delta ")) { patch_method = BINARY_DELTA_DEFLATED; origlen = strtoul(buffer + 6, NULL, 10); } - else if (!prefixcmp(buffer, "literal ")) { + else if (starts_with(buffer, "literal ")) { patch_method = BINARY_LITERAL_DEFLATED; origlen = strtoul(buffer + 8, NULL, 10); } @@ -1943,13 +1943,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) size - offset - hdrsize, patch); if (!patchsize) { - static const char *binhdr[] = { - "Binary files ", - "Files ", - NULL, - }; static const char git_binary[] = "GIT binary patch\n"; - int i; int hd = hdrsize + offset; unsigned long llen = linelen(buffer + hd, size - hd); @@ -1965,6 +1959,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) patchsize = 0; } else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) { + static const char *binhdr[] = { + "Binary files ", + "Files ", + NULL, + }; + int i; for (i = 0; binhdr[i]; i++) { int len = strlen(binhdr[i]); if (len < size - hd && @@ -3627,12 +3627,12 @@ static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20 hunk->oldpos == 1 && hunk->oldlines == 1 && /* does preimage begin with the heading? */ (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL && - !prefixcmp(++preimage, heading) && + starts_with(++preimage, heading) && /* does it record full SHA-1? */ !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) && preimage[sizeof(heading) + 40 - 1] == '\n' && /* does the abbreviated name on the index line agree with it? */ - !prefixcmp(preimage + sizeof(heading) - 1, p->old_sha1_prefix)) + starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix)) return 0; /* it all looks fine */ /* we may have full object name on the index line */ @@ -4152,7 +4152,7 @@ static int use_patch(struct patch *p) /* See if it matches any of exclude/include rule */ for (i = 0; i < limit_by_name.nr; i++) { struct string_list_item *it = &limit_by_name.items[i]; - if (!fnmatch(it->string, pathname, 0)) + if (!wildmatch(it->string, pathname, 0, NULL)) return (it->util != NULL); } diff --git a/builtin/archive.c b/builtin/archive.c index 49178f159e..a1e3b940c2 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -57,9 +57,9 @@ static int run_remote_archiver(int argc, const char **argv, if (!buf) die(_("git archive: expected ACK/NAK, got EOF")); if (strcmp(buf, "ACK")) { - if (!prefixcmp(buf, "NACK ")) + if (starts_with(buf, "NACK ")) die(_("git archive: NACK %s"), buf + 5); - if (!prefixcmp(buf, "ERR ")) + if (starts_with(buf, "ERR ")) die(_("remote error: %s"), buf + 4); die(_("git archive: protocol error")); } diff --git a/builtin/blame.c b/builtin/blame.c index 9047b6ef4c..88cb799727 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -74,7 +74,7 @@ static unsigned blame_copy_score; #define BLAME_DEFAULT_MOVE_SCORE 20 #define BLAME_DEFAULT_COPY_SCORE 40 -/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */ +/* Remember to update object flag allocation in object.h */ #define METAINFO_SHOWN (1u<<12) #define MORE_THAN_ONE_PATH (1u<<13) @@ -197,7 +197,6 @@ static void drop_origin_blob(struct origin *o) * scoreboard structure, sorted by the target line number. */ struct blame_entry { - struct blame_entry *prev; struct blame_entry *next; /* the first line of this group in the final image; @@ -256,15 +255,6 @@ struct scoreboard { int *lineno; }; -static inline int same_suspect(struct origin *a, struct origin *b) -{ - if (a == b) - return 1; - if (a->commit != b->commit) - return 0; - return !strcmp(a->path, b->path); -} - static void sanity_check_refcnt(struct scoreboard *); /* @@ -277,13 +267,11 @@ static void coalesce(struct scoreboard *sb) struct blame_entry *ent, *next; for (ent = sb->ent; ent && (next = ent->next); ent = next) { - if (same_suspect(ent->suspect, next->suspect) && + if (ent->suspect == next->suspect && ent->guilty == next->guilty && ent->s_lno + ent->num_lines == next->s_lno) { ent->num_lines += next->num_lines; ent->next = next->next; - if (ent->next) - ent->next->prev = ent; origin_decref(next->suspect); free(next); ent->score = 0; @@ -534,7 +522,7 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e) prev = ent; /* prev, if not NULL, is the last one that is below e */ - e->prev = prev; + if (prev) { e->next = prev->next; prev->next = e; @@ -543,8 +531,6 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e) e->next = sb->ent; sb->ent = e; } - if (e->next) - e->next->prev = e; } /* @@ -555,14 +541,12 @@ static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e) */ static void dup_entry(struct blame_entry *dst, struct blame_entry *src) { - struct blame_entry *p, *n; + struct blame_entry *n; - p = dst->prev; n = dst->next; origin_incref(src->suspect); origin_decref(dst->suspect); memcpy(dst, src, sizeof(*src)); - dst->prev = p; dst->next = n; dst->score = 0; } @@ -742,7 +726,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target) int last_in_target = -1; for (e = sb->ent; e; e = e->next) { - if (e->guilty || !same_suspect(e->suspect, target)) + if (e->guilty || e->suspect != target) continue; if (last_in_target < e->s_lno + e->num_lines) last_in_target = e->s_lno + e->num_lines; @@ -762,7 +746,7 @@ static void blame_chunk(struct scoreboard *sb, struct blame_entry *e; for (e = sb->ent; e; e = e->next) { - if (e->guilty || !same_suspect(e->suspect, target)) + if (e->guilty || e->suspect != target) continue; if (same <= e->s_lno) continue; @@ -939,7 +923,6 @@ static void find_copy_in_blob(struct scoreboard *sb, mmfile_t *file_p) { const char *cp; - int cnt; mmfile_t file_o; struct handle_split_cb_data d; @@ -950,13 +933,7 @@ static void find_copy_in_blob(struct scoreboard *sb, */ cp = nth_line(sb, ent->lno); file_o.ptr = (char *) cp; - cnt = ent->num_lines; - - while (cnt && cp < sb->final_buf + sb->final_buf_size) { - if (*cp++ == '\n') - cnt--; - } - file_o.size = cp - file_o.ptr; + file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp; /* * file_o is a part of final image we are annotating. @@ -992,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb, while (made_progress) { made_progress = 0; for (e = sb->ent; e; e = e->next) { - if (e->guilty || !same_suspect(e->suspect, target) || + if (e->guilty || e->suspect != target || ent_score(sb, e) < blame_move_score) continue; find_copy_in_blob(sb, e, parent, split, &file_p); @@ -1027,14 +1004,14 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb, for (e = sb->ent, num_ents = 0; e; e = e->next) if (!e->scanned && !e->guilty && - same_suspect(e->suspect, target) && + e->suspect == target && min_score < ent_score(sb, e)) num_ents++; if (num_ents) { blame_list = xcalloc(num_ents, sizeof(struct blame_list)); for (e = sb->ent, i = 0; e; e = e->next) if (!e->scanned && !e->guilty && - same_suspect(e->suspect, target) && + e->suspect == target && min_score < ent_score(sb, e)) blame_list[i++].ent = e; } @@ -1178,7 +1155,7 @@ static void pass_whole_blame(struct scoreboard *sb, origin->file.ptr = NULL; } for (e = sb->ent; e; e = e->next) { - if (!same_suspect(e->suspect, origin)) + if (e->suspect != origin) continue; origin_incref(porigin); origin_decref(e->suspect); @@ -1551,8 +1528,7 @@ static void assign_blame(struct scoreboard *sb, int opt) */ origin_incref(suspect); commit = suspect->commit; - if (!commit->object.parsed) - parse_commit(commit); + parse_commit(commit); if (reverse || (!(commit->object.flags & UNINTERESTING) && !(revs->max_age != -1 && commit->date < revs->max_age))) @@ -1568,7 +1544,7 @@ static void assign_blame(struct scoreboard *sb, int opt) /* Take responsibility for the remaining entries */ for (ent = sb->ent; ent; ent = ent->next) - if (same_suspect(ent->suspect, suspect)) + if (ent->suspect == suspect) found_guilty_entry(ent); origin_decref(suspect); @@ -1581,14 +1557,14 @@ static const char *format_time(unsigned long time, const char *tz_str, int show_raw_time) { static char time_buf[128]; - const char *time_str; - int time_len; - int tz; if (show_raw_time) { snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str); } else { + const char *time_str; + int time_len; + int tz; tz = atoi(tz_str); time_str = show_date(time, tz, blame_date_mode); time_len = strlen(time_str); @@ -1773,25 +1749,41 @@ static int prepare_lines(struct scoreboard *sb) { const char *buf = sb->final_buf; unsigned long len = sb->final_buf_size; - int num = 0, incomplete = 0, bol = 1; + const char *end = buf + len; + const char *p; + int *lineno; + int num = 0, incomplete = 0; - if (len && buf[len-1] != '\n') - incomplete++; /* incomplete line at the end */ - while (len--) { - if (bol) { - sb->lineno = xrealloc(sb->lineno, - sizeof(int *) * (num + 1)); - sb->lineno[num] = buf - sb->final_buf; - bol = 0; - } - if (*buf++ == '\n') { + for (p = buf;;) { + p = memchr(p, '\n', end - p); + if (p) { + p++; num++; - bol = 1; + continue; } + break; } - sb->lineno = xrealloc(sb->lineno, - sizeof(int *) * (num + incomplete + 1)); - sb->lineno[num + incomplete] = buf - sb->final_buf; + + if (len && end[-1] != '\n') + incomplete++; /* incomplete line at the end */ + + sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1)); + lineno = sb->lineno; + + *lineno++ = 0; + for (p = buf;;) { + p = memchr(p, '\n', end - p); + if (p) { + p++; + *lineno++ = p - buf; + continue; + } + break; + } + + if (incomplete) + *lineno++ = len; + sb->num_lines = num + incomplete; return sb->num_lines; } @@ -2503,8 +2495,6 @@ parse_done: ent->suspect = o; ent->s_lno = bottom; ent->next = next; - if (next) - next->prev = ent; origin_incref(o); } origin_decref(o); diff --git a/builtin/branch.c b/builtin/branch.c index f157f92f48..652b1d2d14 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -81,13 +81,13 @@ static int parse_branch_color_slot(const char *var, int ofs) static int git_branch_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "branch", &colopts); if (!strcmp(var, "color.branch")) { branch_use_color = git_config_colorbool(var, value); return 0; } - if (!prefixcmp(var, "color.branch.")) { + if (starts_with(var, "color.branch.")) { int slot = parse_branch_color_slot(var, 13); if (slot < 0) return 0; @@ -315,7 +315,7 @@ static int match_patterns(const char **pattern, const char *refname) if (!*pattern) return 1; /* no pattern always matches */ while (*pattern) { - if (!fnmatch(*pattern, refname, 0)) + if (!wildmatch(*pattern, refname, 0, NULL)) return 1; pattern++; } @@ -502,7 +502,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item, const char *sub = _(" **** invalid ref ****"); struct commit *commit = item->commit; - if (commit && !parse_commit(commit)) { + if (!parse_commit(commit)) { pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject); sub = subject.buf; } @@ -868,7 +868,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!strcmp(head, "HEAD")) { detached = 1; } else { - if (prefixcmp(head, "refs/heads/")) + if (!starts_with(head, "refs/heads/")) die(_("HEAD not found below refs/heads!")); head += 11; } diff --git a/builtin/cat-file.c b/builtin/cat-file.c index f8288c830c..707330499f 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -118,6 +118,7 @@ struct expand_data { unsigned long size; unsigned long disk_size; const char *rest; + unsigned char delta_base_sha1[20]; /* * If mark_query is true, we do not expand anything, but rather @@ -174,6 +175,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len, data->split_on_whitespace = 1; else if (data->rest) strbuf_addstr(sb, data->rest); + } else if (is_atom("deltabase", atom, len)) { + if (data->mark_query) + data->info.delta_base_sha1 = data->delta_base_sha1; + else + strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1)); } else die("unknown format element: %.*s", len, atom); } @@ -241,7 +247,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt, return 0; } - if (sha1_object_info_extended(data->sha1, &data->info) < 0) { + if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) { printf("%s missing\n", obj_name); fflush(stdout); return 0; @@ -263,6 +269,8 @@ static int batch_objects(struct batch_options *opt) { struct strbuf buf = STRBUF_INIT; struct expand_data data; + int save_warning; + int retval = 0; if (!opt->format) opt->format = "%(objectname) %(objecttype) %(objectsize)"; @@ -291,11 +299,10 @@ static int batch_objects(struct batch_options *opt) * warn) ends up dwarfing the actual cost of the object lookups * themselves. We can work around it by just turning off the warning. */ + save_warning = warn_on_object_refname_ambiguity; warn_on_object_refname_ambiguity = 0; while (strbuf_getline(&buf, stdin, '\n') != EOF) { - int error; - if (data.split_on_whitespace) { /* * Split at first whitespace, tying off the beginning @@ -310,12 +317,14 @@ static int batch_objects(struct batch_options *opt) data.rest = p; } - error = batch_one_object(buf.buf, opt, &data); - if (error) - return error; + retval = batch_one_object(buf.buf, opt, &data); + if (retval) + break; } - return 0; + strbuf_release(&buf); + warn_on_object_refname_ambiguity = save_warning; + return retval; } static const char * const cat_file_usage[] = { diff --git a/builtin/check-attr.c b/builtin/check-attr.c index e9af7b2bfb..5600ec3f61 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -102,6 +102,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) struct git_attr_check *check; int cnt, i, doubledash, filei; + if (!is_bare_repository()) + setup_work_tree(); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, check_attr_options, diff --git a/builtin/checkout.c b/builtin/checkout.c index 54f80bd38a..1b86d9c868 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -53,10 +53,10 @@ struct checkout_opts { static int post_checkout_hook(struct commit *old, struct commit *new, int changed) { - return run_hook(NULL, "post-checkout", - sha1_to_hex(old ? old->object.sha1 : null_sha1), - sha1_to_hex(new ? new->object.sha1 : null_sha1), - changed ? "1" : "0", NULL); + return run_hook_le(NULL, "post-checkout", + sha1_to_hex(old ? old->object.sha1 : null_sha1), + sha1_to_hex(new ? new->object.sha1 : null_sha1), + changed ? "1" : "0", NULL); /* "new" can be NULL when checking out from the index before a commit exists. */ @@ -297,8 +297,7 @@ static int checkout_paths(const struct checkout_opts *opts, * match_pathspec() for _all_ entries when * opts->source_tree != NULL. */ - if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce), - 0, ps_matched)) + if (ce_path_match(ce, &opts->pathspec, ps_matched)) ce->ce_flags |= CE_MATCHED; } @@ -380,8 +379,8 @@ static void show_local_changes(struct object *head, static void describe_detached_head(const char *msg, struct commit *commit) { struct strbuf sb = STRBUF_INIT; - parse_commit(commit); - pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); + if (!parse_commit(commit)) + pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); fprintf(stderr, "%s %s... %s\n", msg, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf); strbuf_release(&sb); @@ -677,12 +676,12 @@ static int add_pending_uninteresting_ref(const char *refname, static void describe_one_orphan(struct strbuf *sb, struct commit *commit) { - parse_commit(commit); strbuf_addstr(sb, " "); strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); strbuf_addch(sb, ' '); - pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); + if (!parse_commit(commit)) + pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); strbuf_addch(sb, '\n'); } @@ -781,7 +780,7 @@ static int switch_branches(const struct checkout_opts *opts, if (!(flag & REF_ISSYMREF)) old.path = NULL; - if (old.path && !prefixcmp(old.path, "refs/heads/")) + if (old.path && starts_with(old.path, "refs/heads/")) old.name = old.path + strlen("refs/heads/"); if (!new->name) { @@ -789,7 +788,7 @@ static int switch_branches(const struct checkout_opts *opts, new->commit = old.commit; if (!new->commit) die(_("You are on a branch yet to be born")); - parse_commit(new->commit); + parse_commit_or_die(new->commit); } ret = merge_working_tree(opts, &old, new, &writeout_error); @@ -816,7 +815,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb) return 0; } - if (!prefixcmp(var, "submodule.")) + if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); return git_xmerge_config(var, value, NULL); @@ -995,7 +994,7 @@ static int parse_branchname_arg(int argc, const char **argv, /* not a commit */ *source_tree = parse_tree_indirect(rev); } else { - parse_commit(new->commit); + parse_commit_or_die(new->commit); *source_tree = new->commit->tree; } @@ -1151,9 +1150,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) const char *argv0 = argv[0]; if (!argc || !strcmp(argv0, "--")) die (_("--track needs a branch name")); - if (!prefixcmp(argv0, "refs/")) + if (starts_with(argv0, "refs/")) argv0 += 5; - if (!prefixcmp(argv0, "remotes/")) + if (starts_with(argv0, "remotes/")) argv0 += 8; argv0 = strchr(argv0, '/'); if (!argv0 || !argv0[1]) diff --git a/builtin/clean.c b/builtin/clean.c index 615cd57caf..cf76b1f412 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -100,7 +100,7 @@ static int parse_clean_color_slot(const char *var) static int git_clean_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "clean", &colopts); /* honors the color.interactive* config variables which also @@ -109,7 +109,7 @@ static int git_clean_config(const char *var, const char *value, void *cb) clean_use_color = git_config_colorbool(var, value); return 0; } - if (!prefixcmp(var, "color.interactive.")) { + if (starts_with(var, "color.interactive.")) { int slot = parse_clean_color_slot(var + strlen("color.interactive.")); if (slot < 0) @@ -154,7 +154,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, DIR *dir; struct strbuf quoted = STRBUF_INIT; struct dirent *e; - int res = 0, ret = 0, gone = 1, original_len = path->len, len, i; + int res = 0, ret = 0, gone = 1, original_len = path->len, len; unsigned char submodule_head[20]; struct string_list dels = STRING_LIST_INIT_DUP; @@ -242,6 +242,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, } if (!*dir_gone && !quiet) { + int i; for (i = 0; i < dels.nr; i++) printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string); } @@ -933,48 +934,28 @@ int cmd_clean(int argc, const char **argv, const char *prefix) for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; - int len, pos; int matches = 0; - const struct cache_entry *ce; struct stat st; const char *rel; - /* - * Remove the '/' at the end that directory - * walking adds for directory entries. - */ - len = ent->len; - if (len && ent->name[len-1] == '/') - len--; - pos = cache_name_pos(ent->name, len); - if (0 <= pos) - continue; /* exact match */ - pos = -pos - 1; - if (pos < active_nr) { - ce = active_cache[pos]; - if (ce_namelen(ce) == len && - !memcmp(ce->name, ent->name, len)) - continue; /* Yup, this one exists unmerged */ - } + if (!cache_name_is_other(ent->name, ent->len)) + continue; if (lstat(ent->name, &st)) die_errno("Cannot lstat '%s'", ent->name); if (pathspec.nr) - matches = match_pathspec_depth(&pathspec, ent->name, - len, 0, NULL); + matches = dir_path_match(ent, &pathspec, 0, NULL); - if (S_ISDIR(st.st_mode)) { - if (remove_directories || (matches == MATCHED_EXACTLY)) { - rel = relative_path(ent->name, prefix, &buf); - string_list_append(&del_list, rel); - } - } else { - if (pathspec.nr && !matches) - continue; - rel = relative_path(ent->name, prefix, &buf); - string_list_append(&del_list, rel); - } + if (pathspec.nr && !matches) + continue; + + if (S_ISDIR(st.st_mode) && !remove_directories && + matches != MATCHED_EXACTLY) + continue; + + rel = relative_path(ent->name, prefix, &buf); + string_list_append(&del_list, rel); } if (interactive && del_list.nr > 0) diff --git a/builtin/clone.c b/builtin/clone.c index cc11104d42..9b3c04d914 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -252,6 +252,12 @@ static int add_one_reference(struct string_list_item *item, void *cb_data) die(_("reference repository '%s' is not a local repository."), item->string); + if (!access(mkpath("%s/shallow", ref_git), F_OK)) + die(_("reference repository '%s' is shallow"), item->string); + + if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) + die(_("reference repository '%s' is grafted"), item->string); + strbuf_addf(&alternate, "%s/objects", ref_git); add_to_alternates_file(alternate.buf); strbuf_release(&alternate); @@ -508,9 +514,9 @@ static void write_followtags(const struct ref *refs, const char *msg) { const struct ref *ref; for (ref = refs; ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) + if (!starts_with(ref->name, "refs/tags/")) continue; - if (!suffixcmp(ref->name, "^{}")) + if (ends_with(ref->name, "^{}")) continue; if (!has_sha1_file(ref->old_sha1)) continue; @@ -578,7 +584,7 @@ static void update_remote_refs(const struct ref *refs, static void update_head(const struct ref *our, const struct ref *remote, const char *msg) { - if (our && !prefixcmp(our->name, "refs/heads/")) { + if (our && starts_with(our->name, "refs/heads/")) { /* Local default branch link */ create_symref("HEAD", our->name, NULL); if (!option_bare) { @@ -625,7 +631,7 @@ static int checkout(void) if (advice_detached_head) detach_advice(sha1_to_hex(sha1)); } else { - if (prefixcmp(head, "refs/heads/")) + if (!starts_with(head, "refs/heads/")) die(_("HEAD not found below refs/heads!")); } free(head); @@ -654,8 +660,8 @@ static int checkout(void) commit_locked_index(lock_file)) die(_("unable to write new index file")); - err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1), - sha1_to_hex(sha1), "1", NULL); + err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1), + sha1_to_hex(sha1), "1", NULL); if (!err && option_recursive) err = run_command_v_opt(argv_submodule, RUN_GIT_CMD); @@ -791,8 +797,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix) else repo = repo_name; is_local = option_local != 0 && path && !is_bundle; - if (is_local && option_depth) - warning(_("--depth is ignored in local clones; use file:// instead.")); + if (is_local) { + if (option_depth) + warning(_("--depth is ignored in local clones; use file:// instead.")); + if (!access(mkpath("%s/shallow", path), F_OK)) { + if (option_local > 0) + warning(_("source repository is shallow, ignoring --local")); + is_local = 0; + } + } if (option_local > 0 && !is_local) warning(_("--local is ignored")); @@ -887,6 +900,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) remote = remote_get(option_origin); transport = transport_get(remote, remote->url[0]); + transport->cloning = 1; if (!transport->get_refs_list || (!is_local && !transport->fetch)) die(_("Don't know how to clone %s"), transport->url); diff --git a/builtin/column.c b/builtin/column.c index e125a55fc9..75818520e1 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -34,7 +34,7 @@ int cmd_column(int argc, const char **argv, const char *prefix) }; /* This one is special and must be the first one */ - if (argc > 1 && !prefixcmp(argv[1], "--command=")) { + if (argc > 1 && starts_with(argv[1], "--command=")) { command = argv[1] + 10; git_config(column_config, (void *)command); } else diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index f641ff2a89..987a4c3d73 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -12,6 +12,8 @@ static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1> <changelog"; +static const char *sign_commit; + static void new_parent(struct commit *parent, struct commit_list **parents_p) { unsigned char *sha1 = parent->object.sha1; @@ -31,6 +33,10 @@ static int commit_tree_config(const char *var, const char *value, void *cb) int status = git_gpg_config(var, value, NULL); if (status) return status; + if (!strcmp(var, "commit.gpgsign")) { + sign_commit = git_config_bool(var, value) ? "" : NULL; + return 0; + } return git_default_config(var, value, cb); } @@ -41,7 +47,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) unsigned char tree_sha1[20]; unsigned char commit_sha1[20]; struct strbuf buffer = STRBUF_INIT; - const char *sign_commit = NULL; git_config(commit_tree_config, NULL); @@ -66,6 +71,11 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) continue; } + if (!strcmp(arg, "--no-gpg-sign")) { + sign_commit = NULL; + continue; + } + if (!strcmp(arg, "-m")) { if (argc <= ++i) usage(commit_tree_usage); diff --git a/builtin/commit.c b/builtin/commit.c index fedb45af8c..d9550c54d0 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -113,6 +113,7 @@ static char *sign_commit; static enum { CLEANUP_SPACE, CLEANUP_NONE, + CLEANUP_SCISSORS, CLEANUP_ALL } cleanup_mode; static const char *cleanup_arg; @@ -234,7 +235,7 @@ static int list_paths(struct string_list *list, const char *with_tree, if (ce->ce_flags & CE_UPDATE) continue; - if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m)) + if (!ce_path_match(ce, pattern, m)) continue; item = string_list_insert(list, ce->name); if (ce_skip_worktree(ce)) @@ -307,7 +308,6 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int fd; struct string_list partial; struct pathspec pathspec; - char *old_index_env = NULL; int refresh_flags = REFRESH_QUIET; if (is_status) @@ -320,6 +320,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, die(_("index file corrupt")); if (interactive) { + char *old_index_env = NULL; fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); @@ -600,19 +601,17 @@ static int prepare_to_commit(const char *index_file, const char *prefix, { struct stat statbuf; struct strbuf committer_ident = STRBUF_INIT; - int commitable, saved_color_setting; + int commitable; struct strbuf sb = STRBUF_INIT; - char *buffer; const char *hook_arg1 = NULL; const char *hook_arg2 = NULL; - int ident_shown = 0; int clean_message_contents = (cleanup_mode != CLEANUP_NONE); int old_display_comment_prefix; /* This checks and barfs if author is badly specified */ determine_author_info(author_ident); - if (!no_verify && run_hook(index_file, "pre-commit", NULL)) + if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL)) return 0; if (squash_message) { @@ -649,6 +648,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, logfile); hook_arg1 = "message"; } else if (use_message) { + char *buffer; buffer = strstr(use_message_buffer, "\n\n"); if (!use_editor && (!buffer || buffer[2] == '\0')) die(_("commit has empty message")); @@ -733,7 +733,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, eol = nl - sb.buf; else eol = sb.len; - if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) { + if (starts_with(sb.buf + previous, "\nConflicts:\n")) { ignore_footer = sb.len - previous; break; } @@ -753,8 +753,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix, /* This checks if committer ident is explicitly given */ strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT)); if (use_editor && include_status) { + int ident_shown = 0; + int saved_color_setting; char *ai_tmp, *ci_tmp; - if (whence != FROM_COMMIT) + if (whence != FROM_COMMIT) { + if (cleanup_mode == CLEANUP_SCISSORS) + wt_status_add_cut_line(s->fp); status_printf_ln(s, GIT_COLOR_NORMAL, whence == FROM_MERGE ? _("\n" @@ -770,6 +774,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, git_path(whence == FROM_MERGE ? "MERGE_HEAD" : "CHERRY_PICK_HEAD")); + } fprintf(s->fp, "\n"); if (cleanup_mode == CLEANUP_ALL) @@ -777,6 +782,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, _("Please enter the commit message for your changes." " Lines starting\nwith '%c' will be ignored, and an empty" " message aborts the commit.\n"), comment_line_char); + else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT) + wt_status_add_cut_line(s->fp); else /* CLEANUP_SPACE, that is. */ status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." @@ -866,8 +873,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, return 0; } - if (run_hook(index_file, "prepare-commit-msg", - git_path(commit_editmsg), hook_arg1, hook_arg2, NULL)) + if (run_commit_hook(use_editor, index_file, "prepare-commit-msg", + git_path(commit_editmsg), hook_arg1, hook_arg2, NULL)) return 0; if (use_editor) { @@ -883,7 +890,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } if (!no_verify && - run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) { + run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) { return 0; } @@ -904,7 +911,7 @@ static int rest_is_empty(struct strbuf *sb, int start) eol = sb->len; if (strlen(sign_off_header) <= eol - i && - !prefixcmp(sb->buf + i, sign_off_header)) { + starts_with(sb->buf + i, sign_off_header)) { i = eol; continue; } @@ -1067,8 +1074,6 @@ static int parse_and_validate_options(int argc, const char *argv[], use_editor = 0; if (0 <= edit_flag) use_editor = edit_flag; - if (!use_editor) - setenv("GIT_EDITOR", ":", 1); /* Sanity check options */ if (amend && !current_head) @@ -1132,6 +1137,8 @@ static int parse_and_validate_options(int argc, const char *argv[], cleanup_mode = CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "strip")) cleanup_mode = CLEANUP_ALL; + else if (!strcmp(cleanup_arg, "scissors")) + cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE; else die(_("Invalid cleanup mode %s"), cleanup_arg); @@ -1183,7 +1190,7 @@ static int git_status_config(const char *k, const char *v, void *cb) { struct wt_status *s = cb; - if (!prefixcmp(k, "column.")) + if (starts_with(k, "column.")) return git_column_config(k, v, "status", &s->colopts); if (!strcmp(k, "status.submodulesummary")) { int is_bool; @@ -1211,7 +1218,7 @@ static int git_status_config(const char *k, const char *v, void *cb) s->display_comment_prefix = git_config_bool(k, v); return 0; } - if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) { + if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) { int slot = parse_status_slot(k, 13); if (slot < 0) return 0; @@ -1338,7 +1345,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1, commit = lookup_commit(sha1); if (!commit) die(_("couldn't look up newly created commit")); - if (!commit || parse_commit(commit)) + if (parse_commit(commit)) die(_("could not parse newly created commit")); strbuf_addstr(&format, "format:%h] %s"); @@ -1377,7 +1384,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1, head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL); printf("[%s%s ", - !prefixcmp(head, "refs/heads/") ? + starts_with(head, "refs/heads/") ? head + 11 : !strcmp(head, "HEAD") ? _("detached HEAD") : @@ -1406,6 +1413,10 @@ static int git_commit_config(const char *k, const char *v, void *cb) } if (!strcmp(k, "commit.cleanup")) return git_config_string(&cleanup_arg, k, v); + if (!strcmp(k, "commit.gpgsign")) { + sign_commit = git_config_bool(k, v) ? "" : NULL; + return 0; + } status = git_gpg_config(k, v, NULL); if (status) @@ -1445,6 +1456,29 @@ static int run_rewrite_hook(const unsigned char *oldsha1, return finish_command(&proc); } +int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...) +{ + const char *hook_env[3] = { NULL }; + char index[PATH_MAX]; + va_list args; + int ret; + + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); + hook_env[0] = index; + + /* + * Let the hook know that no editor will be launched. + */ + if (!editor_is_used) + hook_env[1] = "GIT_EDITOR=:"; + + va_start(args, name); + ret = run_hook_ve(hook_env, name, args); + va_end(args); + + return ret; +} + int cmd_commit(int argc, const char **argv, const char *prefix) { static struct wt_status s; @@ -1510,7 +1544,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; - int allow_fast_forward = 1; struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; @@ -1525,7 +1558,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) current_head = NULL; else { current_head = lookup_commit_or_die(sha1, "HEAD"); - if (!current_head || parse_commit(current_head)) + if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } argc = parse_and_validate_options(argc, argv, builtin_commit_options, @@ -1558,6 +1591,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; FILE *fp; + int allow_fast_forward = 1; if (!reflog_msg) reflog_msg = "commit (merge)"; @@ -1600,8 +1634,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) die(_("could not read commit message: %s"), strerror(saved_errno)); } - /* Truncate the message just before the diff, if any. */ - if (verbose) + if (verbose || /* Truncate the message just before the diff, if any. */ + cleanup_mode == CLEANUP_SCISSORS) wt_status_truncate_message_at_cut_line(&sb); if (cleanup_mode != CLEANUP_NONE) @@ -1669,7 +1703,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) "not exceeded, and then \"git reset HEAD\" to recover.")); rerere(0); - run_hook(get_index_file(), "post-commit", NULL); + run_commit_hook(use_editor, get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { struct notes_rewrite_cfg *cfg; cfg = init_copy_notes_for_rewrite("amend"); diff --git a/builtin/config.c b/builtin/config.c index 20e89fe4e0..5677c942b6 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -21,8 +21,7 @@ static char key_delim = ' '; static char term = '\n'; static int use_global_config, use_system_config, use_local_config; -static const char *given_config_file; -static const char *given_config_blob; +static struct git_config_source given_config_source; static int actions, types; static const char *get_color_slot, *get_colorbool_slot; static int end_null; @@ -55,8 +54,8 @@ static struct option builtin_config_options[] = { OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), - OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")), - OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")), + OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), + OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")), OPT_GROUP(N_("Action")), OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET), OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL), @@ -224,8 +223,7 @@ static int get_value(const char *key_, const char *regex_) } git_config_with_options(collect_config, &values, - given_config_file, given_config_blob, - respect_includes); + &given_config_source, respect_includes); ret = !values.nr; @@ -309,8 +307,7 @@ static void get_color(const char *def_color) get_color_found = 0; parsed_color[0] = '\0'; git_config_with_options(git_get_color_config, NULL, - given_config_file, given_config_blob, - respect_includes); + &given_config_source, respect_includes); if (!get_color_found && def_color) color_parse(def_color, "command line", parsed_color); @@ -339,8 +336,7 @@ static int get_colorbool(int print) get_diff_color_found = -1; get_color_ui_found = -1; git_config_with_options(git_get_colorbool_config, NULL, - given_config_file, given_config_blob, - respect_includes); + &given_config_source, respect_includes); if (get_colorbool_found < 0) { if (!strcmp(get_colorbool_slot, "color.diff")) @@ -362,9 +358,12 @@ static int get_colorbool(int print) return get_colorbool_found ? 0 : 1; } -static void check_blob_write(void) +static void check_write(void) { - if (given_config_blob) + if (given_config_source.use_stdin) + die("writing to stdin is not supported"); + + if (given_config_source.blob) die("writing config blobs is not supported"); } @@ -435,7 +434,7 @@ static int get_urlmatch(const char *var, const char *url) } git_config_with_options(urlmatch_config_entry, &config, - given_config_file, NULL, respect_includes); + &given_config_source, respect_includes); for_each_string_list_item(item, &values) { struct urlmatch_current_candidate_value *matched = item->util; @@ -464,18 +463,24 @@ int cmd_config(int argc, const char **argv, const char *prefix) int nongit = !startup_info->have_repository; char *value; - given_config_file = getenv(CONFIG_ENVIRONMENT); + given_config_source.file = getenv(CONFIG_ENVIRONMENT); argc = parse_options(argc, argv, prefix, builtin_config_options, builtin_config_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (use_global_config + use_system_config + use_local_config + - !!given_config_file + !!given_config_blob > 1) { + !!given_config_source.file + !!given_config_source.blob > 1) { error("only one config file at a time."); usage_with_options(builtin_config_usage, builtin_config_options); } + if (given_config_source.file && + !strcmp(given_config_source.file, "-")) { + given_config_source.file = NULL; + given_config_source.use_stdin = 1; + } + if (use_global_config) { char *user_config = NULL; char *xdg_config = NULL; @@ -493,24 +498,24 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (access_or_warn(user_config, R_OK, 0) && xdg_config && !access_or_warn(xdg_config, R_OK, 0)) - given_config_file = xdg_config; + given_config_source.file = xdg_config; else - given_config_file = user_config; + given_config_source.file = user_config; } else if (use_system_config) - given_config_file = git_etc_gitconfig(); + given_config_source.file = git_etc_gitconfig(); else if (use_local_config) - given_config_file = git_pathdup("config"); - else if (given_config_file) { - if (!is_absolute_path(given_config_file) && prefix) - given_config_file = + given_config_source.file = git_pathdup("config"); + else if (given_config_source.file) { + if (!is_absolute_path(given_config_source.file) && prefix) + given_config_source.file = xstrdup(prefix_filename(prefix, strlen(prefix), - given_config_file)); + given_config_source.file)); } if (respect_includes == -1) - respect_includes = !given_config_file; + respect_includes = !given_config_source.file; if (end_null) { term = '\0'; @@ -549,57 +554,58 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (git_config_with_options(show_all_config, NULL, - given_config_file, - given_config_blob, + &given_config_source, respect_includes) < 0) { - if (given_config_file) + if (given_config_source.file) die_errno("unable to read config file '%s'", - given_config_file); + given_config_source.file); else die("error processing config file(s)"); } } else if (actions == ACTION_EDIT) { check_argc(argc, 0, 0); - if (!given_config_file && nongit) + if (!given_config_source.file && nongit) die("not in a git directory"); - if (given_config_blob) + if (given_config_source.use_stdin) + die("editing stdin is not supported"); + if (given_config_source.blob) die("editing blobs is not supported"); git_config(git_default_config, NULL); - launch_editor(given_config_file ? - given_config_file : git_path("config"), + launch_editor(given_config_source.file ? + given_config_source.file : git_path("config"), NULL, NULL); } else if (actions == ACTION_SET) { int ret; - check_blob_write(); + check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - ret = git_config_set_in_file(given_config_file, argv[0], value); + ret = git_config_set_in_file(given_config_source.file, argv[0], value); if (ret == CONFIG_NOTHING_SET) error("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s.", argv[0]); return ret; } else if (actions == ACTION_SET_ALL) { - check_blob_write(); + check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], value, argv[2], 0); } else if (actions == ACTION_ADD) { - check_blob_write(); + check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], value, "^$", 0); } else if (actions == ACTION_REPLACE_ALL) { - check_blob_write(); + check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], value, argv[2], 1); } else if (actions == ACTION_GET) { @@ -623,26 +629,26 @@ int cmd_config(int argc, const char **argv, const char *prefix) return get_urlmatch(argv[0], argv[1]); } else if (actions == ACTION_UNSET) { - check_blob_write(); + check_write(); check_argc(argc, 1, 2); if (argc == 2) - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], NULL, argv[1], 0); else - return git_config_set_in_file(given_config_file, + return git_config_set_in_file(given_config_source.file, argv[0], NULL); } else if (actions == ACTION_UNSET_ALL) { - check_blob_write(); + check_write(); check_argc(argc, 1, 2); - return git_config_set_multivar_in_file(given_config_file, + return git_config_set_multivar_in_file(given_config_source.file, argv[0], NULL, argv[1], 1); } else if (actions == ACTION_RENAME_SECTION) { int ret; - check_blob_write(); + check_write(); check_argc(argc, 2, 2); - ret = git_config_rename_section_in_file(given_config_file, + ret = git_config_rename_section_in_file(given_config_source.file, argv[0], argv[1]); if (ret < 0) return ret; @@ -651,9 +657,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) } else if (actions == ACTION_REMOVE_SECTION) { int ret; - check_blob_write(); + check_write(); check_argc(argc, 1, 1); - ret = git_config_rename_section_in_file(given_config_file, + ret = git_config_rename_section_in_file(given_config_source.file, argv[0], NULL); if (ret < 0) return ret; @@ -671,9 +677,3 @@ int cmd_config(int argc, const char **argv, const char *prefix) return 0; } - -int cmd_repo_config(int argc, const char **argv, const char *prefix) -{ - fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n"); - return cmd_config(argc, argv, prefix); -} diff --git a/builtin/describe.c b/builtin/describe.c index 6f62109887..24d740c8b1 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -6,7 +6,7 @@ #include "exec_cmd.h" #include "parse-options.h" #include "diff.h" -#include "hash.h" +#include "hashmap.h" #include "argv-array.h" #define SEEN (1u << 0) @@ -25,7 +25,7 @@ static int longformat; static int first_parent; static int abbrev = -1; /* unspecified */ static int max_candidates = 10; -static struct hash_table names; +static struct hashmap names; static int have_util; static const char *pattern; static int always; @@ -37,7 +37,7 @@ static const char *diff_index_args[] = { }; struct commit_name { - struct commit_name *next; + struct hashmap_entry entry; unsigned char peeled[20]; struct tag *tag; unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */ @@ -50,6 +50,12 @@ static const char *prio_names[] = { "head", "lightweight", "annotated", }; +static int commit_name_cmp(const struct commit_name *cn1, + const struct commit_name *cn2, const void *peeled) +{ + return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled); +} + static inline unsigned int hash_sha1(const unsigned char *sha1) { unsigned int hash; @@ -59,21 +65,9 @@ static inline unsigned int hash_sha1(const unsigned char *sha1) static inline struct commit_name *find_commit_name(const unsigned char *peeled) { - struct commit_name *n = lookup_hash(hash_sha1(peeled), &names); - while (n && !!hashcmp(peeled, n->peeled)) - n = n->next; - return n; -} - -static int set_util(void *chain, void *data) -{ - struct commit_name *n; - for (n = chain; n; n = n->next) { - struct commit *c = lookup_commit_reference_gently(n->peeled, 1); - if (c) - c->util = n; - } - return 0; + struct commit_name key; + hashmap_entry_init(&key, hash_sha1(peeled)); + return hashmap_get(&names, &key, peeled); } static int replace_name(struct commit_name *e, @@ -118,16 +112,10 @@ static void add_to_known_names(const char *path, struct tag *tag = NULL; if (replace_name(e, prio, sha1, &tag)) { if (!e) { - void **pos; e = xmalloc(sizeof(struct commit_name)); hashcpy(e->peeled, peeled); - pos = insert_hash(hash_sha1(peeled), e, &names); - if (pos) { - e->next = *pos; - *pos = e; - } else { - e->next = NULL; - } + hashmap_entry_init(e, hash_sha1(peeled)); + hashmap_add(&names, e); e->path = NULL; } e->tag = tag; @@ -141,7 +129,7 @@ static void add_to_known_names(const char *path, static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data) { - int is_tag = !prefixcmp(path, "refs/tags/"); + int is_tag = starts_with(path, "refs/tags/"); unsigned char peeled[20]; int is_annotated, prio; @@ -150,7 +138,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void return 0; /* Accept only tags that match the pattern, if given */ - if (pattern && (!is_tag || fnmatch(pattern, path + 10, 0))) + if (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL))) return 0; /* Is it annotated? */ @@ -292,7 +280,14 @@ static void describe(const char *arg, int last_one) fprintf(stderr, _("searching to describe %s\n"), arg); if (!have_util) { - for_each_hash(&names, set_util, NULL); + struct hashmap_iter iter; + struct commit *c; + struct commit_name *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; + } have_util = 1; } @@ -463,9 +458,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) return cmd_name_rev(args.argc, args.argv, prefix); } - init_hash(&names); + hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0); for_each_rawref(get_name, NULL); - if (!names.nr && !always) + if (!names.size && !always) die(_("No names found, cannot describe anything.")); if (argc == 0) { diff --git a/builtin/diff.c b/builtin/diff.c index fe0cc7f1b5..0f247d2400 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -16,6 +16,9 @@ #include "submodule.h" #include "sha1-array.h" +#define DIFF_NO_INDEX_EXPLICIT 1 +#define DIFF_NO_INDEX_IMPLICIT 2 + struct blobinfo { unsigned char sha1[20]; const char *name; @@ -259,7 +262,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; struct blobinfo blob[2]; - int nongit; + int nongit = 0, no_index = 0; int result = 0; /* @@ -285,14 +288,59 @@ int cmd_diff(int argc, const char **argv, const char *prefix) * Other cases are errors. */ - prefix = setup_git_directory_gently(&nongit); - gitmodules_config(); + /* Were we asked to do --no-index explicitly? */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + i++; + break; + } + if (!strcmp(argv[i], "--no-index")) + no_index = DIFF_NO_INDEX_EXPLICIT; + if (argv[i][0] != '-') + break; + } + + if (!no_index) + prefix = setup_git_directory_gently(&nongit); + + /* + * Treat git diff with at least one path outside of the + * repo the same as if the command would have been executed + * outside of a git repository. In this case it behaves + * the same way as "git diff --no-index <a> <b>", which acts + * as a colourful "diff" replacement. + */ + if (nongit || ((argc == i + 2) && + (!path_inside_repo(prefix, argv[i]) || + !path_inside_repo(prefix, argv[i + 1])))) + no_index = DIFF_NO_INDEX_IMPLICIT; + + if (!no_index) + gitmodules_config(); git_config(git_diff_ui_config, NULL); init_revisions(&rev, prefix); - /* If this is a no-index diff, just run it and exit there. */ - diff_no_index(&rev, argc, argv, nongit, prefix); + if (no_index && argc != i + 2) { + if (no_index == DIFF_NO_INDEX_IMPLICIT) { + /* + * There was no --no-index and there were not two + * paths. It is possible that the user intended + * to do an inside-repository operation. + */ + fprintf(stderr, "Not a git repository\n"); + fprintf(stderr, + "To compare two paths outside a working tree:\n"); + } + /* Give the usage message for non-repository usage and exit. */ + usagef("git diff %s <path> <path>", + no_index == DIFF_NO_INDEX_EXPLICIT ? + "--no-index" : "[--no-index]"); + + } + if (no_index) + /* If this is a no-index diff, just run it and exit there. */ + diff_no_index(&rev, argc, argv, prefix); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 78250eab08..b8d8a3aaf9 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -287,7 +287,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; - parse_commit(commit); + parse_commit_or_die(commit); author = strstr(commit->buffer, "\nauthor "); if (!author) die ("Could not find author in commit %s", @@ -308,7 +308,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) if (commit->parents && get_object_mark(&commit->parents->item->object) != 0 && !full_tree) { - parse_commit(commit->parents->item); + parse_commit_or_die(commit->parents->item); diff_tree_sha1(commit->parents->item->tree->object.sha1, commit->tree->object.sha1, "", &rev->diffopt); } @@ -476,7 +476,7 @@ static void handle_tag(const char *name, struct tag *tag) } } - if (!prefixcmp(name, "refs/tags/")) + if (starts_with(name, "refs/tags/")) name += 10; printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n", name, tagged_mark, diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 758b5acd55..1262b405f8 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -3,6 +3,7 @@ #include "fetch-pack.h" #include "remote.h" #include "connect.h" +#include "sha1-array.h" static const char fetch_pack_usage[] = "git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] " @@ -13,6 +14,13 @@ static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc, const char *name, int namelen) { struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1); + unsigned char sha1[20]; + + if (namelen > 41 && name[40] == ' ' && !get_sha1_hex(name, sha1)) { + hashcpy(ref->old_sha1, sha1); + name += 41; + namelen -= 41; + } memcpy(ref->name, name, namelen); ref->name[namelen] = '\0'; @@ -39,6 +47,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) char **pack_lockfile_ptr = NULL; struct child_process *conn; struct fetch_pack_args args; + struct sha1_array shallow = SHA1_ARRAY_INIT; packet_trace_identity("fetch-pack"); @@ -48,11 +57,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) for (i = 1; i < argc && *argv[i] == '-'; i++) { const char *arg = argv[i]; - if (!prefixcmp(arg, "--upload-pack=")) { + if (starts_with(arg, "--upload-pack=")) { args.uploadpack = arg + 14; continue; } - if (!prefixcmp(arg, "--exec=")) { + if (starts_with(arg, "--exec=")) { args.uploadpack = arg + 7; continue; } @@ -89,7 +98,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.verbose = 1; continue; } - if (!prefixcmp(arg, "--depth=")) { + if (starts_with(arg, "--depth=")) { args.depth = strtol(arg + 8, NULL, 0); continue; } @@ -110,6 +119,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.check_self_contained_and_connected = 1; continue; } + if (!strcmp("--cloning", arg)) { + args.cloning = 1; + continue; + } + if (!strcmp("--update-shallow", arg)) { + args.update_shallow = 1; + continue; + } usage(fetch_pack_usage); } @@ -158,10 +175,10 @@ 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); + get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow); - ref = fetch_pack(&args, fd, conn, ref, dest, - sought, nr_sought, pack_lockfile_ptr); + ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought, + &shallow, pack_lockfile_ptr); if (pack_lockfile) { printf("lock %s\n", pack_lockfile); fflush(stdout); diff --git a/builtin/fetch.c b/builtin/fetch.c index 5bd00d064a..55f457c04f 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -36,7 +36,7 @@ static int prune = -1; /* unspecified */ static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int tags = TAGS_DEFAULT, unshallow; +static int tags = TAGS_DEFAULT, unshallow, update_shallow; static const char *depth; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; @@ -44,6 +44,7 @@ static struct transport *gtransport; static struct transport *gsecondary; static const char *submodule_prefix = ""; static const char *recurse_submodules_default; +static int shown_url = 0; static int option_parse_recurse_submodules(const struct option *opt, const char *arg, int unset) @@ -104,6 +105,8 @@ static struct option builtin_fetch_options[] = { { OPTION_STRING, 0, "recurse-submodules-default", &recurse_submodules_default, NULL, N_("default mode for recursion"), PARSE_OPT_HIDDEN }, + OPT_BOOL(0, "update-shallow", &update_shallow, + N_("accept refs that update .git/shallow")), OPT_END() }; @@ -160,48 +163,156 @@ static void add_merge_config(struct ref **head, } } +static int add_existing(const char *refname, const unsigned char *sha1, + int flag, void *cbdata) +{ + struct string_list *list = (struct string_list *)cbdata; + struct string_list_item *item = string_list_insert(list, refname); + item->util = xmalloc(20); + hashcpy(item->util, sha1); + return 0; +} + +static int will_fetch(struct ref **head, const unsigned char *sha1) +{ + struct ref *rm = *head; + while (rm) { + if (!hashcmp(rm->old_sha1, sha1)) + return 1; + rm = rm->next; + } + return 0; +} + static void find_non_local_tags(struct transport *transport, struct ref **head, - struct ref ***tail); + struct ref ***tail) +{ + struct string_list existing_refs = STRING_LIST_INIT_DUP; + struct string_list remote_refs = STRING_LIST_INIT_NODUP; + const struct ref *ref; + struct string_list_item *item = NULL; + + for_each_ref(add_existing, &existing_refs); + for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { + if (!starts_with(ref->name, "refs/tags/")) + continue; + + /* + * The peeled ref always follows the matching base + * ref, so if we see a peeled ref that we don't want + * to fetch then we can mark the ref entry in the list + * as one to ignore by setting util to NULL. + */ + if (ends_with(ref->name, "^{}")) { + if (item && !has_sha1_file(ref->old_sha1) && + !will_fetch(head, ref->old_sha1) && + !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + item = NULL; + continue; + } + + /* + * If item is non-NULL here, then we previously saw a + * ref not followed by a peeled reference, so we need + * to check if it is a lightweight tag that we want to + * fetch. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + item = NULL; + + /* skip duplicates and refs that we already have */ + if (string_list_has_string(&remote_refs, ref->name) || + string_list_has_string(&existing_refs, ref->name)) + continue; + + item = string_list_insert(&remote_refs, ref->name); + item->util = (void *)ref->old_sha1; + } + string_list_clear(&existing_refs, 1); + + /* + * We may have a final lightweight tag that needs to be + * checked to see if it needs fetching. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + /* + * For all the tags in the remote_refs string list, + * add them to the list of refs to be fetched + */ + for_each_string_list_item(item, &remote_refs) { + /* Unless we have already decided to ignore this item... */ + if (item->util) + { + struct ref *rm = alloc_ref(item->string); + rm->peer_ref = alloc_ref(item->string); + hashcpy(rm->old_sha1, item->util); + **tail = rm; + *tail = &rm->next; + } + } + + string_list_clear(&remote_refs, 0); +} static struct ref *get_ref_map(struct transport *transport, - struct refspec *refs, int ref_count, int tags, - int *autotags) + struct refspec *refspecs, int refspec_count, + int tags, int *autotags) { int i; struct ref *rm; struct ref *ref_map = NULL; struct ref **tail = &ref_map; - const struct ref *remote_refs = transport_get_remote_refs(transport); + /* opportunistically-updated references: */ + struct ref *orefs = NULL, **oref_tail = &orefs; - if (ref_count || tags == TAGS_SET) { - struct ref **old_tail; + const struct ref *remote_refs = transport_get_remote_refs(transport); - for (i = 0; i < ref_count; i++) { - get_fetch_map(remote_refs, &refs[i], &tail, 0); - if (refs[i].dst && refs[i].dst[0]) + if (refspec_count) { + for (i = 0; i < refspec_count; i++) { + get_fetch_map(remote_refs, &refspecs[i], &tail, 0); + if (refspecs[i].dst && refspecs[i].dst[0]) *autotags = 1; } - /* Merge everything on the command line, but not --tags */ + /* Merge everything on the command line (but not --tags) */ for (rm = ref_map; rm; rm = rm->next) rm->fetch_head_status = FETCH_HEAD_MERGE; - if (tags == TAGS_SET) - get_fetch_map(remote_refs, tag_refspec, &tail, 0); /* - * For any refs that we happen to be fetching via command-line - * arguments, take the opportunity to update their configured - * counterparts. However, we do not want to mention these - * entries in FETCH_HEAD at all, as they would simply be - * duplicates of existing entries. + * For any refs that we happen to be fetching via + * command-line arguments, the destination ref might + * have been missing or have been different than the + * remote-tracking ref that would be derived from the + * configured refspec. In these cases, we want to + * take the opportunity to update their configured + * remote-tracking reference. However, we do not want + * to mention these entries in FETCH_HEAD at all, as + * they would simply be duplicates of existing + * entries, so we set them FETCH_HEAD_IGNORE below. + * + * We compute these entries now, based only on the + * refspecs specified on the command line. But we add + * them to the list following the refspecs resulting + * from the tags option so that one of the latter, + * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed + * by ref_remove_duplicates() in favor of one of these + * opportunistic entries with FETCH_HEAD_IGNORE. */ - old_tail = tail; for (i = 0; i < transport->remote->fetch_refspec_nr; i++) get_fetch_map(ref_map, &transport->remote->fetch[i], - &tail, 1); - for (rm = *old_tail; rm; rm = rm->next) - rm->fetch_head_status = FETCH_HEAD_IGNORE; + &oref_tail, 1); + + if (tags == TAGS_SET) + get_fetch_map(remote_refs, tag_refspec, &tail, 0); } else { /* Use the defaults */ struct remote *remote = transport->remote; @@ -238,11 +349,21 @@ static struct ref *get_ref_map(struct transport *transport, tail = &ref_map->next; } } - if (tags == TAGS_DEFAULT && *autotags) + + if (tags == TAGS_SET) + /* also fetch all tags */ + get_fetch_map(remote_refs, tag_refspec, &tail, 0); + else if (tags == TAGS_DEFAULT && *autotags) find_non_local_tags(transport, &ref_map, &tail); - ref_remove_duplicates(ref_map); - return ref_map; + /* Now append any refs to be updated opportunistically: */ + *tail = orefs; + for (rm = orefs; rm; rm = rm->next) { + rm->fetch_head_status = FETCH_HEAD_IGNORE; + tail = &rm->next; + } + + return ref_remove_duplicates(ref_map); } #define STORE_REF_ERROR_OTHER 1 @@ -313,7 +434,7 @@ static int update_local_ref(struct ref *ref, } if (!is_null_sha1(ref->old_sha1) && - !prefixcmp(ref->name, "refs/tags/")) { + starts_with(ref->name, "refs/tags/")) { int r; r = s_update_ref("updating tag", ref, 0); strbuf_addf(display, "%c %-*s %-*s -> %s%s", @@ -336,10 +457,10 @@ static int update_local_ref(struct ref *ref, * more likely to follow a standard layout. */ const char *name = remote_ref ? remote_ref->name : ""; - if (!prefixcmp(name, "refs/tags/")) { + if (starts_with(name, "refs/tags/")) { msg = "storing tag"; what = _("[new tag]"); - } else if (!prefixcmp(name, "refs/heads/")) { + } else if (starts_with(name, "refs/heads/")) { msg = "storing head"; what = _("[new branch]"); } else { @@ -405,6 +526,8 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20]) struct ref **rm = cb_data; struct ref *ref = *rm; + while (ref && ref->status == REF_STATUS_REJECT_SHALLOW) + ref = ref->next; if (!ref) return -1; /* end of the list */ *rm = ref->next; @@ -417,7 +540,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, { FILE *fp; struct commit *commit; - int url_len, i, shown_url = 0, rc = 0; + int url_len, i, rc = 0; struct strbuf note = STRBUF_INIT; const char *what, *kind; struct ref *rm; @@ -451,6 +574,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, struct ref *ref = NULL; const char *merge_status_marker = ""; + if (rm->status == REF_STATUS_REJECT_SHALLOW) { + if (want_status == FETCH_HEAD_MERGE) + warning(_("reject %s because shallow roots are not allowed to be updated"), + rm->peer_ref ? rm->peer_ref->name : rm->name); + continue; + } + commit = lookup_commit_reference_gently(rm->old_sha1, 1); if (!commit) rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE; @@ -471,15 +601,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, kind = ""; what = ""; } - else if (!prefixcmp(rm->name, "refs/heads/")) { + else if (starts_with(rm->name, "refs/heads/")) { kind = "branch"; what = rm->name + 11; } - else if (!prefixcmp(rm->name, "refs/tags/")) { + else if (starts_with(rm->name, "refs/tags/")) { kind = "tag"; what = rm->name + 10; } - else if (!prefixcmp(rm->name, "refs/remotes/")) { + else if (starts_with(rm->name, "refs/remotes/")) { kind = "remote-tracking branch"; what = rm->name + 13; } @@ -590,17 +720,36 @@ 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) +static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, + const char *raw_url) { - int result = 0; + int url_len, i, result = 0; struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map); + char *url; const char *dangling_msg = dry_run ? _(" (%s will become dangling)") : _(" (%s has become dangling)"); + if (raw_url) + url = transport_anonymize_url(raw_url); + else + url = xstrdup("foreign"); + + url_len = strlen(url); + for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) + ; + + url_len = i + 1; + if (4 < i && !strncmp(".git", url + i - 3, 4)) + url_len = i - 3; + for (ref = stale_refs; ref; ref = ref->next) { if (!dry_run) result |= delete_ref(ref->name, NULL, 0); + if (verbosity >= 0 && !shown_url) { + fprintf(stderr, _("From %.*s\n"), url_len, url); + shown_url = 1; + } if (verbosity >= 0) { fprintf(stderr, " x %-*s %-*s -> %s\n", TRANSPORT_SUMMARY(_("[deleted]")), @@ -608,110 +757,11 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map) warn_dangling_symref(stderr, dangling_msg, ref->name); } } + free(url); free_refs(stale_refs); return result; } -static int add_existing(const char *refname, const unsigned char *sha1, - int flag, void *cbdata) -{ - struct string_list *list = (struct string_list *)cbdata; - struct string_list_item *item = string_list_insert(list, refname); - item->util = xmalloc(20); - hashcpy(item->util, sha1); - return 0; -} - -static int will_fetch(struct ref **head, const unsigned char *sha1) -{ - struct ref *rm = *head; - while (rm) { - if (!hashcmp(rm->old_sha1, sha1)) - return 1; - rm = rm->next; - } - return 0; -} - -static void find_non_local_tags(struct transport *transport, - struct ref **head, - struct ref ***tail) -{ - struct string_list existing_refs = STRING_LIST_INIT_DUP; - struct string_list remote_refs = STRING_LIST_INIT_NODUP; - const struct ref *ref; - struct string_list_item *item = NULL; - - for_each_ref(add_existing, &existing_refs); - for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) - continue; - - /* - * The peeled ref always follows the matching base - * ref, so if we see a peeled ref that we don't want - * to fetch then we can mark the ref entry in the list - * as one to ignore by setting util to NULL. - */ - if (!suffixcmp(ref->name, "^{}")) { - if (item && !has_sha1_file(ref->old_sha1) && - !will_fetch(head, ref->old_sha1) && - !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - item = NULL; - continue; - } - - /* - * If item is non-NULL here, then we previously saw a - * ref not followed by a peeled reference, so we need - * to check if it is a lightweight tag that we want to - * fetch. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - item = NULL; - - /* skip duplicates and refs that we already have */ - if (string_list_has_string(&remote_refs, ref->name) || - string_list_has_string(&existing_refs, ref->name)) - continue; - - item = string_list_insert(&remote_refs, ref->name); - item->util = (void *)ref->old_sha1; - } - string_list_clear(&existing_refs, 1); - - /* - * We may have a final lightweight tag that needs to be - * checked to see if it needs fetching. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - /* - * For all the tags in the remote_refs string list, - * add them to the list of refs to be fetched - */ - for_each_string_list_item(item, &remote_refs) { - /* Unless we have already decided to ignore this item... */ - if (item->util) - { - struct ref *rm = alloc_ref(item->string); - rm->peer_ref = alloc_ref(item->string); - hashcpy(rm->old_sha1, item->util); - **tail = rm; - *tail = &rm->next; - } - } - - string_list_clear(&remote_refs, 0); -} - static void check_not_current_branch(struct ref *ref_map) { struct branch *current_branch = branch_get(NULL); @@ -759,6 +809,8 @@ static struct transport *prepare_transport(struct remote *remote) set_option(transport, TRANS_OPT_KEEP, "yes"); if (depth) set_option(transport, TRANS_OPT_DEPTH, depth); + if (update_shallow) + set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); return transport; } @@ -824,39 +876,26 @@ static int do_fetch(struct transport *transport, if (tags == TAGS_DEFAULT && autotags) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); - if (fetch_refs(transport, ref_map)) { - free_refs(ref_map); - retcode = 1; - goto cleanup; - } if (prune) { /* - * If --tags was specified, pretend that the user gave us - * the canonical tags refspec + * We only prune based on refspecs specified + * explicitly (via command line or configuration); we + * don't care whether --tags was specified. */ - if (tags == TAGS_SET) { - const char *tags_str = "refs/tags/*:refs/tags/*"; - struct refspec *tags_refspec, *refspec; - - /* Copy the refspec and add the tags to it */ - refspec = xcalloc(ref_count + 1, sizeof(struct refspec)); - tags_refspec = parse_fetch_refspec(1, &tags_str); - memcpy(refspec, refs, ref_count * sizeof(struct refspec)); - memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec)); - ref_count++; - - prune_refs(refspec, ref_count, ref_map); - - ref_count--; - /* The rest of the strings belong to fetch_one */ - free_refspec(1, tags_refspec); - free(refspec); - } else if (ref_count) { - prune_refs(refs, ref_count, ref_map); + if (ref_count) { + prune_refs(refs, ref_count, ref_map, transport->url); } else { - prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map); + prune_refs(transport->remote->fetch, + transport->remote->fetch_refspec_nr, + ref_map, + transport->url); } } + if (fetch_refs(transport, ref_map)) { + free_refs(ref_map); + retcode = 1; + goto cleanup; + } free_refs(ref_map); /* if neither --no-tags nor --tags was specified, do automated tag @@ -892,7 +931,7 @@ static int get_remote_group(const char *key, const char *value, void *priv) { struct remote_group_data *g = priv; - if (!prefixcmp(key, "remotes.") && + if (starts_with(key, "remotes.") && !strcmp(key + 8, g->name)) { /* split list by white space */ int space = strcspn(value, " \t\n"); @@ -930,8 +969,8 @@ static void add_options_to_argv(struct argv_array *argv) { if (dry_run) argv_array_push(argv, "--dry-run"); - if (prune > 0) - argv_array_push(argv, "--prune"); + if (prune != -1) + argv_array_push(argv, prune ? "--prune" : "--no-prune"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); if (force) @@ -987,7 +1026,6 @@ static int fetch_multiple(struct string_list *list) static int fetch_one(struct remote *remote, int argc, const char **argv) { - int i; static const char **refs = NULL; struct refspec *refspec; int ref_nr = 0; @@ -1011,6 +1049,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) if (argc > 0) { int j = 0; + int i; refs = xcalloc(argc + 1, sizeof(const char *)); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "tag")) { diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 1c04070869..3906eda877 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -109,7 +109,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents) if (len < 43 || line[40] != '\t') return 1; - if (!prefixcmp(line + 41, "not-for-merge")) + if (starts_with(line + 41, "not-for-merge")) return 0; if (line[41] != '\t') @@ -155,16 +155,16 @@ static int handle_line(char *line, struct merge_parents *merge_parents) if (pulling_head) { origin = src; src_data->head_status |= 1; - } else if (!prefixcmp(line, "branch ")) { + } else if (starts_with(line, "branch ")) { origin_data->is_local_branch = 1; origin = line + 7; string_list_append(&src_data->branch, origin); src_data->head_status |= 2; - } else if (!prefixcmp(line, "tag ")) { + } else if (starts_with(line, "tag ")) { origin = line; string_list_append(&src_data->tag, origin + 4); src_data->head_status |= 2; - } else if (!prefixcmp(line, "remote-tracking branch ")) { + } else if (starts_with(line, "remote-tracking branch ")) { origin = line + strlen("remote-tracking branch "); string_list_append(&src_data->r_branch, origin); src_data->head_status |= 2; @@ -605,7 +605,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, resolve_refdup("HEAD", head_sha1, 1, NULL); if (!current_branch) die("No current branch"); - if (!prefixcmp(current_branch, "refs/heads/")) + if (starts_with(current_branch, "refs/heads/")) current_branch += 11; find_merge_parents(&merge_parents, in, head_sha1); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 7557aa239e..3e1d5c3334 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -9,6 +9,7 @@ #include "quote.h" #include "parse-options.h" #include "remote.h" +#include "color.h" /* Quoting styles */ #define QUOTE_NONE 0 @@ -75,6 +76,8 @@ static struct { { "upstream" }, { "symref" }, { "flag" }, + { "HEAD" }, + { "color" }, }; /* @@ -90,6 +93,7 @@ static struct { static const char **used_atom; static cmp_type *used_atom_type; static int used_atom_cnt, need_tagged, need_symref; +static int need_color_reset_at_eol; /* * Used to parse format string and sort specifiers @@ -176,13 +180,21 @@ static const char *find_next(const char *cp) static int verify_format(const char *format) { const char *cp, *sp; + static const char color_reset[] = "color:reset"; + + need_color_reset_at_eol = 0; for (cp = format; *cp && (sp = find_next(cp)); ) { const char *ep = strchr(sp, ')'); + int at; + if (!ep) return error("malformed format string %s", sp); /* sp points at "%(" and ep points at the closing ")" */ - parse_atom(sp + 2, ep); + at = parse_atom(sp + 2, ep); cp = ep + 1; + + if (!memcmp(used_atom[at], "color:", 6)) + need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset); } return 0; } @@ -441,7 +453,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (name[wholen] != 0 && strcmp(name + wholen, "name") && strcmp(name + wholen, "email") && - prefixcmp(name + wholen, "date")) + !starts_with(name + wholen, "date")) continue; if (!wholine) wholine = find_wholine(who, wholen, buf, sz); @@ -453,7 +465,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru v->s = copy_name(wholine); else if (!strcmp(name + wholen, "email")) v->s = copy_email(wholine); - else if (!prefixcmp(name + wholen, "date")) + else if (starts_with(name + wholen, "date")) grab_date(wholine, v, name); } @@ -475,7 +487,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (deref) name++; - if (!prefixcmp(name, "creatordate")) + if (starts_with(name, "creatordate")) grab_date(wholine, v, name); else if (!strcmp(name, "creator")) v->s = copy_line(wholine); @@ -649,20 +661,20 @@ static void populate_value(struct refinfo *ref) int deref = 0; const char *refname; const char *formatp; + struct branch *branch = NULL; if (*name == '*') { deref = 1; name++; } - if (!prefixcmp(name, "refname")) + if (starts_with(name, "refname")) refname = ref->refname; - else if (!prefixcmp(name, "symref")) + else if (starts_with(name, "symref")) refname = ref->symref ? ref->symref : ""; - else if (!prefixcmp(name, "upstream")) { - struct branch *branch; + else if (starts_with(name, "upstream")) { /* only local branches may have an upstream */ - if (prefixcmp(ref->refname, "refs/heads/")) + if (!starts_with(ref->refname, "refs/heads/")) continue; branch = branch_get(ref->refname + 11); @@ -670,8 +682,13 @@ static void populate_value(struct refinfo *ref) !branch->merge[0]->dst) continue; refname = branch->merge[0]->dst; - } - else if (!strcmp(name, "flag")) { + } else if (starts_with(name, "color:")) { + char color[COLOR_MAXLEN] = ""; + + color_parse(name + 6, "--format", color); + v->s = xstrdup(color); + continue; + } else if (!strcmp(name, "flag")) { char buf[256], *cp = buf; if (ref->flag & REF_ISSYMREF) cp = copy_advance(cp, ",symref"); @@ -684,20 +701,62 @@ static void populate_value(struct refinfo *ref) v->s = xstrdup(buf + 1); } continue; - } - else if (!deref && grab_objectname(name, ref->objectname, v)) + } else if (!deref && grab_objectname(name, ref->objectname, v)) { continue; - else + } else if (!strcmp(name, "HEAD")) { + const char *head; + unsigned char sha1[20]; + + head = resolve_ref_unsafe("HEAD", sha1, 1, NULL); + if (!strcmp(ref->refname, head)) + v->s = "*"; + else + v->s = " "; + continue; + } else continue; formatp = strchr(name, ':'); - /* look for "short" refname format */ if (formatp) { + int num_ours, num_theirs; + formatp++; if (!strcmp(formatp, "short")) refname = shorten_unambiguous_ref(refname, warn_ambiguous_refs); - else + else if (!strcmp(formatp, "track") && + starts_with(name, "upstream")) { + char buf[40]; + + stat_tracking_info(branch, &num_ours, &num_theirs); + if (!num_ours && !num_theirs) + v->s = ""; + else if (!num_ours) { + sprintf(buf, "[behind %d]", num_theirs); + v->s = xstrdup(buf); + } else if (!num_theirs) { + sprintf(buf, "[ahead %d]", num_ours); + v->s = xstrdup(buf); + } else { + sprintf(buf, "[ahead %d, behind %d]", + num_ours, num_theirs); + v->s = xstrdup(buf); + } + continue; + } else if (!strcmp(formatp, "trackshort") && + starts_with(name, "upstream")) { + assert(branch); + stat_tracking_info(branch, &num_ours, &num_theirs); + if (!num_ours && !num_theirs) + v->s = "="; + else if (!num_ours) + v->s = "<"; + else if (!num_theirs) + v->s = ">"; + else + v->s = "<>"; + continue; + } else die("unknown %.*s format %s", (int)(formatp - name), name, formatp); } @@ -805,7 +864,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f refname[plen] == '/' || p[plen-1] == '/')) break; - if (!fnmatch(p, refname, FNM_PATHNAME)) + if (!wildmatch(p, refname, WM_PATHNAME, NULL)) break; } if (!*pattern) @@ -875,11 +934,9 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs); } -static void print_value(struct refinfo *ref, int atom, int quote_style) +static void print_value(struct atom_value *v, int quote_style) { - struct atom_value *v; struct strbuf sb = STRBUF_INIT; - get_value(ref, atom, &v); switch (quote_style) { case QUOTE_NONE: fputs(v->s, stdout); @@ -946,15 +1003,26 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style) const char *cp, *sp, *ep; for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) { + struct atom_value *atomv; + ep = strchr(sp, ')'); if (cp < sp) emit(cp, sp); - print_value(info, parse_atom(sp + 2, ep), quote_style); + get_value(info, parse_atom(sp + 2, ep), &atomv); + print_value(atomv, quote_style); } if (*cp) { sp = cp + strlen(cp); emit(cp, sp); } + if (need_color_reset_at_eol) { + struct atom_value resetv; + char color[COLOR_MAXLEN] = ""; + + color_parse("reset", "--format", color); + resetv.s = color; + print_value(&resetv, quote_style); + } putchar('\n'); } diff --git a/builtin/fsck.c b/builtin/fsck.c index 97ce678c6b..fc150c8821 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -144,7 +144,7 @@ static int traverse_reachable(void) unsigned int nr = 0; int result = 0; if (show_progress) - progress = start_progress_delay("Checking connectivity", 0, 0, 2); + progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); while (pending.nr) { struct object_array_entry *entry; struct object *obj; @@ -442,7 +442,7 @@ static void fsck_dir(int i, char *path) add_sha1_list(sha1, DIRENT_SORT_HINT(de)); continue; } - if (!prefixcmp(de->d_name, "tmp_obj_")) + if (starts_with(de->d_name, "tmp_obj_")) continue; fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); } @@ -484,7 +484,7 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in static int is_branch(const char *refname) { - return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/"); + return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/"); } static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) @@ -541,7 +541,7 @@ static void fsck_object_dir(const char *path) fprintf(stderr, "Checking object directory\n"); if (show_progress) - progress = start_progress("Checking object directories", 256); + progress = start_progress(_("Checking object directories"), 256); for (i = 0; i < 256; i++) { static char dir[4096]; sprintf(dir, "%s/%02x", path, i); @@ -566,7 +566,7 @@ static int fsck_head_link(void) if (!strcmp(head_points_at, "HEAD")) /* detached HEAD */ null_is_error = 1; - else if (prefixcmp(head_points_at, "refs/heads/")) + else if (!starts_with(head_points_at, "refs/heads/")) return error("HEAD points to something strange (%s)", head_points_at); if (is_null_sha1(head_sha1)) { @@ -630,7 +630,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) struct alternate_object_database *alt; errors_found = 0; - read_replace_refs = 0; + check_replace_refs = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); @@ -670,7 +670,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) total += p->num_objects; } - progress = start_progress("Checking objects", total); + progress = start_progress(_("Checking objects"), total); } for (p = packed_git; p; p = p->next) { /* verify gives error messages itself */ diff --git a/builtin/gc.c b/builtin/gc.c index 25f2237c08..11cf295515 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -16,6 +16,7 @@ #include "run-command.h" #include "sigchain.h" #include "argv-array.h" +#include "commit.h" #define FAILED_RUN "failed to run %s" @@ -28,6 +29,7 @@ static int pack_refs = 1; static int aggressive_window = 250; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; +static int detach_auto = 1; static const char *prune_expire = "2.weeks.ago"; static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT; @@ -72,6 +74,10 @@ static int gc_config(const char *var, const char *value, void *cb) gc_auto_pack_limit = git_config_int(var, value); return 0; } + if (!strcmp(var, "gc.autodetach")) { + detach_auto = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "gc.pruneexpire")) { if (value && strcmp(value, "now")) { unsigned long now = approxidate("now"); @@ -178,7 +184,7 @@ static int need_to_gc(void) else if (!too_many_loose_objects()) return 0; - if (run_hook(NULL, "pre-auto-gc", NULL)) + if (run_hook_le(NULL, "pre-auto-gc", NULL)) return 0; return 1; } @@ -187,13 +193,12 @@ static int need_to_gc(void) static const char *lock_repo_for_gc(int force, pid_t* ret_pid) { static struct lock_file lock; - static char locking_host[128]; char my_host[128]; struct strbuf sb = STRBUF_INIT; struct stat st; uintmax_t pid; FILE *fp; - int fd, should_exit; + int fd; if (pidfile) /* already locked */ @@ -205,6 +210,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) fd = hold_lock_file_for_update(&lock, git_path("gc.pid"), LOCK_DIE_ON_ERROR); if (!force) { + static char locking_host[128]; + int should_exit; fp = fopen(git_path("gc.pid"), "r"); memset(locking_host, 0, sizeof(locking_host)); should_exit = @@ -300,11 +307,19 @@ int cmd_gc(int argc, const char **argv, const char *prefix) */ if (!need_to_gc()) return 0; - if (!quiet) - fprintf(stderr, - _("Auto packing the repository for optimum performance. You may also\n" - "run \"git gc\" manually. See " - "\"git help gc\" for more information.\n")); + if (!quiet) { + if (detach_auto) + fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n")); + else + fprintf(stderr, _("Auto packing the repository for optimum performance.\n")); + fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n")); + } + if (detach_auto) + /* + * failure to daemonize is ok, we'll continue + * in foreground + */ + daemonize(); } else add_repack_all_option(); diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c new file mode 100644 index 0000000000..aa72596083 --- /dev/null +++ b/builtin/get-tar-commit-id.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005, 2006 Rene Scharfe + */ +#include "cache.h" +#include "commit.h" +#include "tar.h" +#include "builtin.h" +#include "quote.h" + +static const char builtin_get_tar_commit_id_usage[] = +"git get-tar-commit-id < <tarfile>"; + +/* ustar header + extended global header content */ +#define RECORDSIZE (512) +#define HEADERSIZE (2 * RECORDSIZE) + +int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) +{ + char buffer[HEADERSIZE]; + struct ustar_header *header = (struct ustar_header *)buffer; + char *content = buffer + RECORDSIZE; + ssize_t n; + + if (argc != 1) + usage(builtin_get_tar_commit_id_usage); + + n = read_in_full(0, buffer, HEADERSIZE); + if (n < HEADERSIZE) + die("git get-tar-commit-id: read error"); + if (header->typeflag[0] != 'g') + return 1; + if (memcmp(content, "52 comment=", 11)) + return 1; + + n = write_in_full(1, content + 11, 41); + if (n < 41) + die_errno("git get-tar-commit-id: write error"); + + return 0; +} diff --git a/builtin/grep.c b/builtin/grep.c index 63f86032d9..69ac2d8797 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -379,7 +379,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int const struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) + if (!ce_path_match(ce, pathspec, NULL)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry @@ -524,9 +524,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, fill_directory(&dir, pathspec); for (i = 0; i < dir.nr; i++) { - const char *name = dir.entries[i]->name; - int namelen = strlen(name); - if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL)) + if (!dir_path_match(dir.entries[i], pathspec, 0, NULL)) continue; hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) diff --git a/builtin/help.c b/builtin/help.c index f1e236b912..1fdefeb686 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -100,7 +100,7 @@ static int check_emacsclient_version(void) */ finish_command(&ec_process); - if (prefixcmp(buffer.buf, "emacsclient")) { + if (!starts_with(buffer.buf, "emacsclient")) { strbuf_release(&buffer); return error(_("Failed to parse emacsclient version.")); } @@ -258,7 +258,7 @@ static int add_man_viewer_info(const char *var, const char *value) static int git_help_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "help", &colopts); if (!strcmp(var, "help.format")) { if (!value) @@ -278,7 +278,7 @@ static int git_help_config(const char *var, const char *value, void *cb) add_man_viewer(value); return 0; } - if (!prefixcmp(var, "man.")) + if (starts_with(var, "man.")) return add_man_viewer_info(var, value); return git_default_config(var, value, cb); @@ -288,6 +288,10 @@ static struct cmdnames main_cmds, other_cmds; static int is_git_command(const char *s) { + if (is_builtin(s)) + return 1; + + load_command_list("git-", &main_cmds, &other_cmds); return is_in_cmdlist(&main_cmds, s) || is_in_cmdlist(&other_cmds, s); } @@ -306,7 +310,7 @@ static const char *cmd_to_page(const char *git_cmd) { if (!git_cmd) return "git"; - else if (!prefixcmp(git_cmd, "git")) + else if (starts_with(git_cmd, "git")) return git_cmd; else if (is_git_command(git_cmd)) return prepend("git-", git_cmd); @@ -449,7 +453,6 @@ int cmd_help(int argc, const char **argv, const char *prefix) int nongit; const char *alias; enum help_format parsed_help_format; - load_command_list("git-", &main_cmds, &other_cmds); argc = parse_options(argc, argv, prefix, builtin_help_options, builtin_help_usage, 0); @@ -458,6 +461,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) if (show_all) { git_config(git_help_config, NULL); printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); + load_command_list("git-", &main_cmds, &other_cmds); list_commands(colopts, &main_cmds, &other_cmds); } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 9e9eb4b74e..b9f6e12c0e 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1291,7 +1291,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, if (keep_fd < 0) { if (errno != EEXIST) die_errno(_("cannot write keep file '%s'"), - keep_name); + keep_name ? keep_name : name); } else { if (keep_msg_len > 0) { write_or_die(keep_fd, keep_msg, keep_msg_len); @@ -1299,7 +1299,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } if (close(keep_fd) != 0) die_errno(_("cannot close written keep file '%s'"), - keep_name); + keep_name ? keep_name : name); report = "keep"; } } @@ -1502,7 +1502,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage(index_pack_usage); - read_replace_refs = 0; + check_replace_refs = 0; reset_pack_idx_option(&opts); git_config(git_index_pack_config, &opts); @@ -1534,9 +1534,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) stat_only = 1; } else if (!strcmp(arg, "--keep")) { keep_msg = ""; - } else if (!prefixcmp(arg, "--keep=")) { + } else if (starts_with(arg, "--keep=")) { keep_msg = arg + 7; - } else if (!prefixcmp(arg, "--threads=")) { + } else if (starts_with(arg, "--threads=")) { char *end; nr_threads = strtoul(arg+10, &end, 0); if (!arg[10] || *end || nr_threads < 0) @@ -1547,7 +1547,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) "ignoring %s"), arg); nr_threads = 1; #endif - } else if (!prefixcmp(arg, "--pack_header=")) { + } else if (starts_with(arg, "--pack_header=")) { struct pack_header *hdr; char *c; @@ -1566,7 +1566,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (index_name || (i+1) >= argc) usage(index_pack_usage); index_name = argv[++i]; - } else if (!prefixcmp(arg, "--index-version=")) { + } else if (starts_with(arg, "--index-version=")) { char *c; opts.version = strtoul(arg + 16, &c, 10); if (opts.version > 2) diff --git a/builtin/init-db.c b/builtin/init-db.c index 78aa3872dd..c7c76bbf21 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -266,7 +266,7 @@ static int create_default_files(const char *template_path) /* allow template config file to override the default */ if (log_all_ref_updates == -1) git_config_set("core.logallrefupdates", "true"); - if (prefixcmp(git_dir, work_tree) || + if (!starts_with(git_dir, work_tree) || strcmp(git_dir + strlen(work_tree), "/.git")) { git_config_set("core.worktree", work_tree); } @@ -515,13 +515,14 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) saved = shared_repository; shared_repository = 0; switch (safe_create_leading_directories_const(argv[0])) { - case -3: + case SCLD_OK: + case SCLD_PERMS: + break; + case SCLD_EXISTS: errno = EEXIST; /* fallthru */ - case -1: - die_errno(_("cannot mkdir %s"), argv[0]); - break; default: + die_errno(_("cannot mkdir %s"), argv[0]); break; } shared_repository = saved; diff --git a/builtin/log.c b/builtin/log.c index b708517a35..b97373da3b 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -391,7 +391,7 @@ static int git_log_config(const char *var, const char *value, void *cb) default_show_root = git_config_bool(var, value); return 0; } - if (!prefixcmp(var, "color.decorate.")) + if (starts_with(var, "color.decorate.")) return parse_decorate_color_config(var, 15, value); if (!strcmp(var, "log.mailmap")) { use_mailmap_config = git_config_bool(var, value); @@ -477,7 +477,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev) int new_offset = offset + 1; while (new_offset < size && buf[new_offset++] != '\n') ; /* do nothing */ - if (!prefixcmp(buf + offset, "tagger ")) + if (starts_with(buf + offset, "tagger ")) show_tagger(buf + offset + 7, new_offset - offset - 7, rev); offset = new_offset; @@ -882,7 +882,7 @@ static char *find_branch_name(struct rev_info *rev) ref = rev->cmdline.rev[positive].name; tip_sha1 = rev->cmdline.rev[positive].item->sha1; if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) && - !prefixcmp(full_ref, "refs/heads/") && + starts_with(full_ref, "refs/heads/") && !hashcmp(tip_sha1, branch_sha1)) branch = xstrdup(full_ref + strlen("refs/heads/")); free(full_ref); @@ -1388,7 +1388,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) unsigned char sha1[20]; const char *ref; ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL); - if (ref && !prefixcmp(ref, "refs/heads/")) + if (ref && starts_with(ref, "refs/heads/")) branch_name = xstrdup(ref + strlen("refs/heads/")); else branch_name = xstrdup(""); /* no branch */ diff --git a/builtin/ls-files.c b/builtin/ls-files.c index e1cf6d8547..47c38808a2 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -64,7 +64,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) if (len >= ent->len) die("git ls-files: internal error - directory entry not superset of prefix"); - if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched)) + if (!dir_path_match(ent, &pathspec, len, ps_matched)) return; fputs(tag, stdout); @@ -139,7 +139,9 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) if (len >= ce_namelen(ce)) die("git ls-files: internal error - cache entry not superset of prefix"); - if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched)) + if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce), + len, ps_matched, + S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode))) return; if (tag && *tag && show_valid_bit && @@ -195,7 +197,8 @@ static void show_ru_info(void) len = strlen(path); if (len < max_prefix_len) continue; /* outside of the prefix */ - if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched)) + if (!match_pathspec(&pathspec, path, len, + max_prefix_len, ps_matched, 0)) continue; /* uninterested */ for (i = 0; i < 3; i++) { if (!ui->mode[i]) diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 25e83cfe9d..3e9eefb091 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -22,7 +22,7 @@ static int tail_match(const char **pattern, const char *path) if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf)) return error("insanely long ref %.*s...", 20, path); while ((p = *(pattern++)) != NULL) { - if (!fnmatch(p, pathbuf, 0)) + if (!wildmatch(p, pathbuf, 0, NULL)) return 1; } return 0; @@ -50,11 +50,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) const char *arg = argv[i]; if (*arg == '-') { - if (!prefixcmp(arg, "--upload-pack=")) { + if (starts_with(arg, "--upload-pack=")) { uploadpack = arg + 14; continue; } - if (!prefixcmp(arg, "--exec=")) { + if (starts_with(arg, "--exec=")) { uploadpack = arg + 7; continue; } diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 65ec931846..51184dfa2e 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -171,7 +171,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) * show_recursive() rolls its own matching code and is * generally ignorant of 'struct pathspec'. The magic mask * cannot be lifted until it is converted to use - * match_pathspec_depth() or tree_entry_interesting() + * match_pathspec() or tree_entry_interesting() */ parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE, PATHSPEC_PREFER_CWD, diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 24a772d8e1..2c3cd8eab7 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -328,11 +328,11 @@ static int check_header(const struct strbuf *line, } /* for inbody stuff */ - if (!prefixcmp(line->buf, ">From") && isspace(line->buf[5])) { + if (starts_with(line->buf, ">From") && isspace(line->buf[5])) { ret = 1; /* Should this return 0? */ goto check_header_out; } - if (!prefixcmp(line->buf, "[PATCH]") && isspace(line->buf[7])) { + if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) { for (i = 0; header[i]; i++) { if (!memcmp("Subject", header[i], 7)) { handle_header(&hdr_data[i], line); @@ -361,7 +361,7 @@ static int is_rfc2822_header(const struct strbuf *line) char *cp = line->buf; /* Count mbox From headers as headers */ - if (!prefixcmp(cp, "From ") || !prefixcmp(cp, ">From ")) + if (starts_with(cp, "From ") || starts_with(cp, ">From ")) return 1; while ((ch = *cp++)) { @@ -671,11 +671,11 @@ static inline int patchbreak(const struct strbuf *line) size_t i; /* Beginning of a "diff -" header? */ - if (!prefixcmp(line->buf, "diff -")) + if (starts_with(line->buf, "diff -")) return 1; /* CVS "Index: " line? */ - if (!prefixcmp(line->buf, "Index: ")) + if (starts_with(line->buf, "Index: ")) return 1; /* @@ -685,7 +685,7 @@ static inline int patchbreak(const struct strbuf *line) if (line->len < 4) return 0; - if (!prefixcmp(line->buf, "---")) { + if (starts_with(line->buf, "---")) { /* space followed by a filename? */ if (line->buf[3] == ' ' && !isspace(line->buf[4])) return 1; @@ -986,7 +986,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch) static int git_mailinfo_config(const char *var, const char *value, void *unused) { - if (prefixcmp(var, "mailinfo.")) + if (!starts_with(var, "mailinfo.")) return git_default_config(var, value, unused); if (!strcmp(var, "mailinfo.scissors")) { use_scissors = git_config_bool(var, value); @@ -1020,7 +1020,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix) metainfo_charset = def_charset; else if (!strcmp(argv[1], "-n")) metainfo_charset = NULL; - else if (!prefixcmp(argv[1], "--encoding=")) + else if (starts_with(argv[1], "--encoding=")) metainfo_charset = argv[1] + 11; else if (!strcmp(argv[1], "--scissors")) use_scissors = 1; diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 57ac472716..0ecde8da30 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -1,6 +1,9 @@ #include "builtin.h" #include "cache.h" #include "commit.h" +#include "refs.h" +#include "diff.h" +#include "revision.h" #include "parse-options.h" static int show_merge_base(struct commit **rev, int rev_nr, int show_all) @@ -27,6 +30,7 @@ static const char * const merge_base_usage[] = { N_("git merge-base [-a|--all] --octopus <commit>..."), N_("git merge-base --independent <commit>..."), N_("git merge-base --is-ancestor <commit> <commit>"), + N_("git merge-base --fork-point <ref> [<commit>]"), NULL }; @@ -102,40 +106,152 @@ static int handle_is_ancestor(int argc, const char **argv) return 1; } +struct rev_collect { + struct commit **commit; + int nr; + int alloc; + unsigned int initial : 1; +}; + +static void add_one_commit(unsigned char *sha1, struct rev_collect *revs) +{ + struct commit *commit; + + if (is_null_sha1(sha1)) + return; + + commit = lookup_commit(sha1); + if (!commit || + (commit->object.flags & TMP_MARK) || + parse_commit(commit)) + return; + + ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc); + revs->commit[revs->nr++] = commit; + commit->object.flags |= TMP_MARK; +} + +static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, + const char *ident, unsigned long timestamp, + int tz, const char *message, void *cbdata) +{ + struct rev_collect *revs = cbdata; + + if (revs->initial) { + revs->initial = 0; + add_one_commit(osha1, revs); + } + add_one_commit(nsha1, revs); + return 0; +} + +static int handle_fork_point(int argc, const char **argv) +{ + unsigned char sha1[20]; + char *refname; + const char *commitname; + struct rev_collect revs; + struct commit *derived; + struct commit_list *bases; + int i, ret = 0; + + switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) { + case 0: + die("No such ref: '%s'", argv[0]); + case 1: + break; /* good */ + default: + die("Ambiguous refname: '%s'", argv[0]); + } + + commitname = (argc == 2) ? argv[1] : "HEAD"; + if (get_sha1(commitname, sha1)) + die("Not a valid object name: '%s'", commitname); + + derived = lookup_commit_reference(sha1); + memset(&revs, 0, sizeof(revs)); + revs.initial = 1; + for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); + + for (i = 0; i < revs.nr; i++) + revs.commit[i]->object.flags &= ~TMP_MARK; + + bases = get_merge_bases_many(derived, revs.nr, revs.commit, 0); + + /* + * There should be one and only one merge base, when we found + * a common ancestor among reflog entries. + */ + if (!bases || bases->next) { + ret = 1; + goto cleanup_return; + } + + /* And the found one must be one of the reflog entries */ + for (i = 0; i < revs.nr; i++) + if (&bases->item->object == &revs.commit[i]->object) + break; /* found */ + if (revs.nr <= i) { + ret = 1; /* not found */ + goto cleanup_return; + } + + printf("%s\n", sha1_to_hex(bases->item->object.sha1)); + +cleanup_return: + free_commit_list(bases); + return ret; +} + int cmd_merge_base(int argc, const char **argv, const char *prefix) { struct commit **rev; int rev_nr = 0; int show_all = 0; - int octopus = 0; - int reduce = 0; - int is_ancestor = 0; + int cmdmode = 0; struct option options[] = { OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")), - OPT_BOOL(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")), - OPT_BOOL(0, "independent", &reduce, N_("list revs not reachable from others")), - OPT_BOOL(0, "is-ancestor", &is_ancestor, - N_("is the first one ancestor of the other?")), + OPT_CMDMODE(0, "octopus", &cmdmode, + N_("find ancestors for a single n-way merge"), 'o'), + OPT_CMDMODE(0, "independent", &cmdmode, + N_("list revs not reachable from others"), 'r'), + OPT_CMDMODE(0, "is-ancestor", &cmdmode, + N_("is the first one ancestor of the other?"), 'a'), + OPT_CMDMODE(0, "fork-point", &cmdmode, + N_("find where <commit> forked from reflog of <ref>"), 'f'), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); - if (!octopus && !reduce && argc < 2) - usage_with_options(merge_base_usage, options); - if (is_ancestor && (show_all || octopus || reduce)) - die("--is-ancestor cannot be used with other options"); - if (is_ancestor) + + if (cmdmode == 'a') { + if (argc < 2) + usage_with_options(merge_base_usage, options); + if (show_all) + die("--is-ancestor cannot be used with --all"); return handle_is_ancestor(argc, argv); - if (reduce && (show_all || octopus)) - die("--independent cannot be used with other options"); + } - if (octopus) + if (cmdmode == 'r' && show_all) + die("--independent cannot be used with --all"); + + if (cmdmode == 'o') return handle_octopus(argc, argv, show_all); - else if (reduce) + + if (cmdmode == 'r') return handle_independent(argc, argv); + if (cmdmode == 'f') { + if (argc < 1 || 2 < argc) + usage_with_options(merge_base_usage, options); + return handle_fork_point(argc, argv); + } + + if (argc < 2) + usage_with_options(merge_base_usage, options); + rev = xmalloc(argc * sizeof(*rev)); while (argc-- > 0) rev[rev_nr++] = get_commit_reference(*argv++); diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 3a64f5d0bd..a90f28f34d 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -29,7 +29,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) struct commit *result; init_merge_options(&o); - if (argv[0] && !suffixcmp(argv[0], "-subtree")) + if (argv[0] && ends_with(argv[0], "-subtree")) o.subtree_shift = ""; if (argc < 4) @@ -38,7 +38,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) for (i = 1; i < argc; ++i) { const char *arg = argv[i]; - if (!prefixcmp(arg, "--")) { + if (starts_with(arg, "--")) { if (!arg[2]) break; if (parse_merge_opt(&o, arg + 2)) diff --git a/builtin/merge.c b/builtin/merge.c index 41fb66dec2..e15d0e145a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -367,7 +367,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead sha1_to_hex(commit->object.sha1)); pretty_print_commit(&ctx, commit, &out); } - if (write(fd, out.buf, out.len) < 0) + if (write_in_full(fd, out.buf, out.len) != out.len) die_errno(_("Writing SQUASH_MSG")); if (close(fd)) die_errno(_("Finishing SQUASH_MSG")); @@ -421,7 +421,7 @@ static void finish(struct commit *head_commit, } /* Run a post-merge hook */ - run_hook(NULL, "post-merge", squash ? "1" : "0", NULL); + run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL); strbuf_release(&reflog_message); } @@ -446,17 +446,17 @@ static void merge_name(const char *remote, struct strbuf *msg) die(_("'%s' does not point to a commit"), remote); if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { - if (!prefixcmp(found_ref, "refs/heads/")) { + if (starts_with(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } - if (!prefixcmp(found_ref, "refs/tags/")) { + if (starts_with(found_ref, "refs/tags/")) { strbuf_addf(msg, "%s\t\ttag '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } - if (!prefixcmp(found_ref, "refs/remotes/")) { + if (starts_with(found_ref, "refs/remotes/")) { strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; @@ -570,8 +570,8 @@ static int git_merge_config(const char *k, const char *v, void *cb) { int status; - if (branch && !prefixcmp(k, "branch.") && - !prefixcmp(k + 7, branch) && + if (branch && starts_with(k, "branch.") && + starts_with(k + 7, branch) && !strcmp(k + 7 + strlen(branch), ".mergeoptions")) { free(branch_mergeoptions); branch_mergeoptions = xstrdup(v); @@ -597,6 +597,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) } else if (!strcmp(k, "merge.defaulttoupstream")) { default_to_upstream = git_config_bool(k, v); return 0; + } else if (!strcmp(k, "commit.gpgsign")) { + sign_commit = git_config_bool(k, v) ? "" : NULL; + return 0; } status = fmt_merge_msg_config(k, v, cb); @@ -821,8 +824,8 @@ static void prepare_to_commit(struct commit_list *remoteheads) if (0 < option_edit) strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char); write_merge_msg(&msg); - if (run_hook(get_index_file(), "prepare-commit-msg", - git_path("MERGE_MSG"), "merge", NULL, NULL)) + if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg", + git_path("MERGE_MSG"), "merge", NULL)) abort_commit(remoteheads, NULL); if (0 < option_edit) { if (launch_editor(git_path("MERGE_MSG"), NULL, NULL)) @@ -1106,7 +1109,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * current branch. */ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag); - if (branch && !prefixcmp(branch, "refs/heads/")) + if (branch && starts_with(branch, "refs/heads/")) branch += 11; if (!branch || is_null_sha1(head_sha1)) head_commit = NULL; diff --git a/builtin/mktree.c b/builtin/mktree.c index f92ba404ab..a964d6be52 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -23,10 +23,7 @@ static void append_to_tree(unsigned mode, unsigned char *sha1, char *path) if (strchr(path, '/')) die("path %s contains slash", path); - if (alloc <= used) { - alloc = alloc_nr(used); - entries = xrealloc(entries, sizeof(*entries) * alloc); - } + ALLOC_GROW(entries, used + 1, alloc); ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1); ent->mode = mode; ent->len = len; diff --git a/builtin/mv.c b/builtin/mv.c index 21c46d1636..2a7243f52e 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -162,7 +162,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (strncmp(path, src_w_slash, len_w_slash)) break; } - free((char *)src_w_slash); + if (src_w_slash != src) + free((char *)src_w_slash); if (last - first < 1) bad = _("source directory is empty"); @@ -179,6 +180,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix) modes = xrealloc(modes, (argc + last - first) * sizeof(enum update_mode)); + submodule_gitfile = xrealloc(submodule_gitfile, + (argc + last - first) + * sizeof(char *)); } dst = add_slash(dst); @@ -192,6 +196,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; + submodule_gitfile[argc + j] = NULL; } argc += last - first; } @@ -227,6 +232,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix) memmove(destination + i, destination + i + 1, (argc - i) * sizeof(char *)); + memmove(modes + i, modes + i + 1, + (argc - i) * sizeof(enum update_mode)); + memmove(submodule_gitfile + i, + submodule_gitfile + i + 1, + (argc - i) * sizeof(char *)); i--; } } else diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 20fcf8c696..c824d4ec5f 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -27,8 +27,7 @@ static void name_rev(struct commit *commit, struct commit_list *parents; int parent_number = 1; - if (!commit->object.parsed) - parse_commit(commit); + parse_commit(commit); if (commit->date < cutoff) return; @@ -88,7 +87,7 @@ static int subpath_matches(const char *path, const char *filter) const char *subpath = path; while (subpath) { - if (!fnmatch(filter, subpath, 0)) + if (!wildmatch(filter, subpath, 0, NULL)) return subpath - path; subpath = strchr(subpath, '/'); if (subpath) @@ -101,9 +100,9 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous) { if (shorten_unambiguous) refname = shorten_unambiguous_ref(refname, 0); - else if (!prefixcmp(refname, "refs/heads/")) + else if (starts_with(refname, "refs/heads/")) refname = refname + 11; - else if (!prefixcmp(refname, "refs/")) + else if (starts_with(refname, "refs/")) refname = refname + 5; return refname; } @@ -149,7 +148,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void int can_abbreviate_output = data->tags_only && data->name_only; int deref = 0; - if (data->tags_only && prefixcmp(path, "refs/tags/")) + if (data->tags_only && !starts_with(path, "refs/tags/")) return 0; if (data->ref_filter) { diff --git a/builtin/notes.c b/builtin/notes.c index d459e23c42..bb89930373 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -269,7 +269,11 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) die(_("Failed to resolve '%s' as a valid ref."), arg); if (!(buf = read_sha1_file(object, &type, &len)) || !len) { free(buf); - die(_("Failed to read object '%s'."), arg);; + die(_("Failed to read object '%s'."), arg); + } + if (type != OBJ_BLOB) { + free(buf); + die(_("Cannot read note data from non-blob object '%s'."), arg); } strbuf_add(&(msg->buf), buf, len); free(buf); @@ -347,7 +351,7 @@ static struct notes_tree *init_notes_check(const char *subcommand) init_notes(NULL, NULL, NULL, 0); t = &default_notes_tree; - if (prefixcmp(t->ref, "refs/notes/")) + if (!starts_with(t->ref, "refs/notes/")) die("Refusing to %s notes in %s (outside of refs/notes/)", subcommand, t->ref); return t; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 36273dd6f0..7950c4342f 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -14,10 +14,12 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "pack-objects.h" #include "progress.h" #include "refs.h" #include "streaming.h" #include "thread-utils.h" +#include "pack-bitmap.h" static const char *pack_usage[] = { N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"), @@ -25,42 +27,15 @@ static const char *pack_usage[] = { NULL }; -struct object_entry { - struct pack_idx_entry idx; - unsigned long size; /* uncompressed size */ - struct packed_git *in_pack; /* already in pack */ - off_t in_pack_offset; - struct object_entry *delta; /* delta base object */ - struct object_entry *delta_child; /* deltified objects who bases me */ - struct object_entry *delta_sibling; /* other deltified objects who - * uses the same base as me - */ - void *delta_data; /* cached delta (uncompressed) */ - unsigned long delta_size; /* delta data size (uncompressed) */ - unsigned long z_delta_size; /* delta data size (compressed) */ - enum object_type type; - enum object_type in_pack_type; /* could be delta */ - uint32_t hash; /* name hint hash */ - unsigned char in_pack_header_size; - unsigned preferred_base:1; /* - * we do not pack this, but is available - * to be used as the base object to delta - * objects against. - */ - unsigned no_try_delta:1; - unsigned tagged:1; /* near the very tip of refs */ - unsigned filled:1; /* assigned write-order */ -}; - /* - * Objects we are going to pack are collected in objects array (dynamically - * expanded). nr_objects & nr_alloc controls this array. They are stored - * in the order we see -- typically rev-list --objects order that gives us - * nice "minimum seek" order. + * Objects we are going to pack are collected in the `to_pack` structure. + * It contains an array (dynamically expanded) of the object data, and a map + * that can resolve SHA1s to their position in the array. */ -static struct object_entry *objects; +static struct packing_data to_pack; + static struct pack_idx_entry **written_list; -static uint32_t nr_objects, nr_alloc, nr_result, nr_written; +static uint32_t nr_result, nr_written; static int non_empty; static int reuse_delta = 1, reuse_object = 1; @@ -83,6 +58,14 @@ static struct progress *progress_state; static int pack_compression_level = Z_DEFAULT_COMPRESSION; static int pack_compression_seen; +static struct packed_git *reuse_packfile; +static uint32_t reuse_packfile_objects; +static off_t reuse_packfile_offset; + +static int use_bitmap_index = 1; +static int write_bitmap_index; +static uint16_t write_bitmap_options; + static unsigned long delta_cache_size = 0; static unsigned long max_delta_cache_size = 256 * 1024 * 1024; static unsigned long cache_max_small_delta_size = 1000; @@ -90,20 +73,28 @@ static unsigned long cache_max_small_delta_size = 1000; static unsigned long window_memory_limit = 0; /* - * The object names in objects array are hashed with this hashtable, - * to help looking up the entry by object name. - * This hashtable is built after all the objects are seen. - */ -static int *object_ix; -static int object_ix_hashsz; -static struct object_entry *locate_object_entry(const unsigned char *sha1); - -/* * stats */ static uint32_t written, written_delta; static uint32_t reused, reused_delta; +/* + * Indexed commits + */ +static struct commit **indexed_commits; +static unsigned int indexed_commits_nr; +static unsigned int indexed_commits_alloc; + +static void index_commit_for_bitmap(struct commit *commit) +{ + if (indexed_commits_nr >= indexed_commits_alloc) { + indexed_commits_alloc = (indexed_commits_alloc + 32) * 2; + indexed_commits = xrealloc(indexed_commits, + indexed_commits_alloc * sizeof(struct commit *)); + } + + indexed_commits[indexed_commits_nr++] = commit; +} static void *get_delta(struct object_entry *entry) { @@ -553,12 +544,12 @@ static int mark_tagged(const char *path, const unsigned char *sha1, int flag, void *cb_data) { unsigned char peeled[20]; - struct object_entry *entry = locate_object_entry(sha1); + struct object_entry *entry = packlist_find(&to_pack, sha1, NULL); if (entry) entry->tagged = 1; if (!peel_ref(path, peeled)) { - entry = locate_object_entry(peeled); + entry = packlist_find(&to_pack, peeled, NULL); if (entry) entry->tagged = 1; } @@ -633,9 +624,10 @@ static struct object_entry **compute_write_order(void) { unsigned int i, wo_end, last_untagged; - struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo)); + struct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo)); + struct object_entry *objects = to_pack.objects; - for (i = 0; i < nr_objects; i++) { + for (i = 0; i < to_pack.nr_objects; i++) { objects[i].tagged = 0; objects[i].filled = 0; objects[i].delta_child = NULL; @@ -647,7 +639,7 @@ static struct object_entry **compute_write_order(void) * Make sure delta_sibling is sorted in the original * recency order. */ - for (i = nr_objects; i > 0;) { + for (i = to_pack.nr_objects; i > 0;) { struct object_entry *e = &objects[--i]; if (!e->delta) continue; @@ -665,7 +657,7 @@ static struct object_entry **compute_write_order(void) * Give the objects in the original recency order until * we see a tagged tip. */ - for (i = wo_end = 0; i < nr_objects; i++) { + for (i = wo_end = 0; i < to_pack.nr_objects; i++) { if (objects[i].tagged) break; add_to_write_order(wo, &wo_end, &objects[i]); @@ -675,7 +667,7 @@ static struct object_entry **compute_write_order(void) /* * Then fill all the tagged tips. */ - for (; i < nr_objects; i++) { + for (; i < to_pack.nr_objects; i++) { if (objects[i].tagged) add_to_write_order(wo, &wo_end, &objects[i]); } @@ -683,7 +675,7 @@ static struct object_entry **compute_write_order(void) /* * And then all remaining commits and tags. */ - for (i = last_untagged; i < nr_objects; i++) { + for (i = last_untagged; i < to_pack.nr_objects; i++) { if (objects[i].type != OBJ_COMMIT && objects[i].type != OBJ_TAG) continue; @@ -693,7 +685,7 @@ static struct object_entry **compute_write_order(void) /* * And then all the trees. */ - for (i = last_untagged; i < nr_objects; i++) { + for (i = last_untagged; i < to_pack.nr_objects; i++) { if (objects[i].type != OBJ_TREE) continue; add_to_write_order(wo, &wo_end, &objects[i]); @@ -702,17 +694,70 @@ static struct object_entry **compute_write_order(void) /* * Finally all the rest in really tight order */ - for (i = last_untagged; i < nr_objects; i++) { + for (i = last_untagged; i < to_pack.nr_objects; i++) { if (!objects[i].filled) add_family_to_write_order(wo, &wo_end, &objects[i]); } - if (wo_end != nr_objects) - die("ordered %u objects, expected %"PRIu32, wo_end, nr_objects); + if (wo_end != to_pack.nr_objects) + die("ordered %u objects, expected %"PRIu32, wo_end, to_pack.nr_objects); return wo; } +static off_t write_reused_pack(struct sha1file *f) +{ + unsigned char buffer[8192]; + off_t to_write, total; + int fd; + + if (!is_pack_valid(reuse_packfile)) + die("packfile is invalid: %s", reuse_packfile->pack_name); + + fd = git_open_noatime(reuse_packfile->pack_name); + if (fd < 0) + die_errno("unable to open packfile for reuse: %s", + reuse_packfile->pack_name); + + if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1) + die_errno("unable to seek in reused packfile"); + + if (reuse_packfile_offset < 0) + reuse_packfile_offset = reuse_packfile->pack_size - 20; + + total = to_write = reuse_packfile_offset - sizeof(struct pack_header); + + while (to_write) { + int read_pack = xread(fd, buffer, sizeof(buffer)); + + if (read_pack <= 0) + die_errno("unable to read from reused packfile"); + + if (read_pack > to_write) + read_pack = to_write; + + sha1write(f, buffer, read_pack); + to_write -= read_pack; + + /* + * We don't know the actual number of objects written, + * only how many bytes written, how many bytes total, and + * how many objects total. So we can fake it by pretending all + * objects we are writing are the same size. This gives us a + * smooth progress meter, and at the end it matches the true + * answer. + */ + written = reuse_packfile_objects * + (((double)(total - to_write)) / total); + display_progress(progress_state, written); + } + + close(fd); + written = reuse_packfile_objects; + display_progress(progress_state, written); + return reuse_packfile_offset - sizeof(struct pack_header); +} + static void write_pack_file(void) { uint32_t i = 0, j; @@ -723,8 +768,8 @@ static void write_pack_file(void) struct object_entry **write_order; if (progress > pack_to_stdout) - progress_state = start_progress("Writing objects", nr_result); - written_list = xmalloc(nr_objects * sizeof(*written_list)); + progress_state = start_progress(_("Writing objects"), nr_result); + written_list = xmalloc(to_pack.nr_objects * sizeof(*written_list)); write_order = compute_write_order(); do { @@ -737,10 +782,17 @@ static void write_pack_file(void) f = create_tmp_packfile(&pack_tmp_name); offset = write_pack_header(f, nr_remaining); - if (!offset) - die_errno("unable to write pack header"); + + if (reuse_packfile) { + off_t packfile_size; + assert(pack_to_stdout); + + packfile_size = write_reused_pack(f); + offset += packfile_size; + } + nr_written = 0; - for (; i < nr_objects; i++) { + for (; i < to_pack.nr_objects; i++) { struct object_entry *e = write_order[i]; if (write_one(f, e, &offset) == WRITE_ONE_BREAK) break; @@ -764,7 +816,7 @@ static void write_pack_file(void) if (!pack_to_stdout) { struct stat st; - char tmpname[PATH_MAX]; + struct strbuf tmpname = STRBUF_INIT; /* * Packs are runtime accessed in their mtime @@ -784,16 +836,35 @@ static void write_pack_file(void) utb.modtime = --last_mtime; if (utime(pack_tmp_name, &utb) < 0) warning("failed utime() on %s: %s", - tmpname, strerror(errno)); + pack_tmp_name, strerror(errno)); } - /* Enough space for "-<sha-1>.pack"? */ - if (sizeof(tmpname) <= strlen(base_name) + 50) - die("pack base name '%s' too long", base_name); - snprintf(tmpname, sizeof(tmpname), "%s-", base_name); - finish_tmp_packfile(tmpname, pack_tmp_name, + strbuf_addf(&tmpname, "%s-", base_name); + + if (write_bitmap_index) { + bitmap_writer_set_checksum(sha1); + bitmap_writer_build_type_index(written_list, nr_written); + } + + finish_tmp_packfile(&tmpname, pack_tmp_name, written_list, nr_written, &pack_idx_opts, sha1); + + if (write_bitmap_index) { + strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1)); + + stop_progress(&progress_state); + + bitmap_writer_show_progress(progress); + bitmap_writer_reuse_bitmaps(&to_pack); + bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1); + bitmap_writer_build(&to_pack); + bitmap_writer_finish(written_list, nr_written, + tmpname.buf, write_bitmap_options); + write_bitmap_index = 0; + } + + strbuf_release(&tmpname); free(pack_tmp_name); puts(sha1_to_hex(sha1)); } @@ -803,7 +874,7 @@ static void write_pack_file(void) written_list[j]->offset = (off_t)-1; } nr_remaining -= nr_written; - } while (nr_remaining && i < nr_objects); + } while (nr_remaining && i < to_pack.nr_objects); free(written_list); free(write_order); @@ -813,73 +884,6 @@ static void write_pack_file(void) written, nr_result); } -static int locate_object_entry_hash(const unsigned char *sha1) -{ - int i; - unsigned int ui; - memcpy(&ui, sha1, sizeof(unsigned int)); - i = ui % object_ix_hashsz; - while (0 < object_ix[i]) { - if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1)) - return i; - if (++i == object_ix_hashsz) - i = 0; - } - return -1 - i; -} - -static struct object_entry *locate_object_entry(const unsigned char *sha1) -{ - int i; - - if (!object_ix_hashsz) - return NULL; - - i = locate_object_entry_hash(sha1); - if (0 <= i) - return &objects[object_ix[i]-1]; - return NULL; -} - -static void rehash_objects(void) -{ - uint32_t i; - struct object_entry *oe; - - object_ix_hashsz = nr_objects * 3; - if (object_ix_hashsz < 1024) - object_ix_hashsz = 1024; - object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz); - memset(object_ix, 0, sizeof(int) * object_ix_hashsz); - for (i = 0, oe = objects; i < nr_objects; i++, oe++) { - int ix = locate_object_entry_hash(oe->idx.sha1); - if (0 <= ix) - continue; - ix = -1 - ix; - object_ix[ix] = i + 1; - } -} - -static uint32_t name_hash(const char *name) -{ - uint32_t c, hash = 0; - - if (!name) - return 0; - - /* - * This effectively just creates a sortable number from the - * last sixteen non-whitespace characters. Last characters - * count "most", so things that end in ".c" sort together. - */ - while ((c = *name++) != 0) { - if (isspace(c)) - continue; - hash = (hash >> 2) + (c << 24); - } - return hash; -} - static void setup_delta_attr_check(struct git_attr_check *check) { static struct git_attr *attr_delta; @@ -902,42 +906,69 @@ static int no_try_delta(const char *path) return 0; } -static int add_object_entry(const unsigned char *sha1, enum object_type type, - const char *name, int exclude) +/* + * When adding an object, check whether we have already added it + * to our packing list. If so, we can skip. However, if we are + * being asked to excludei t, but the previous mention was to include + * it, make sure to adjust its flags and tweak our numbers accordingly. + * + * As an optimization, we pass out the index position where we would have + * found the item, since that saves us from having to look it up again a + * few lines later when we want to add the new entry. + */ +static int have_duplicate_entry(const unsigned char *sha1, + int exclude, + uint32_t *index_pos) { struct object_entry *entry; - struct packed_git *p, *found_pack = NULL; - off_t found_offset = 0; - int ix; - uint32_t hash = name_hash(name); - - ix = nr_objects ? locate_object_entry_hash(sha1) : -1; - if (ix >= 0) { - if (exclude) { - entry = objects + object_ix[ix] - 1; - if (!entry->preferred_base) - nr_result--; - entry->preferred_base = 1; - } + + entry = packlist_find(&to_pack, sha1, index_pos); + if (!entry) return 0; + + if (exclude) { + if (!entry->preferred_base) + nr_result--; + entry->preferred_base = 1; } + return 1; +} + +/* + * Check whether we want the object in the pack (e.g., we do not want + * objects found in non-local stores if the "--local" option was used). + * + * As a side effect of this check, we will find the packed version of this + * object, if any. We therefore pass out the pack information to avoid having + * to look it up again later. + */ +static int want_object_in_pack(const unsigned char *sha1, + int exclude, + struct packed_git **found_pack, + off_t *found_offset) +{ + struct packed_git *p; + if (!exclude && local && has_loose_object_nonlocal(sha1)) return 0; + *found_pack = NULL; + *found_offset = 0; + for (p = packed_git; p; p = p->next) { off_t offset = find_pack_entry_one(sha1, p); if (offset) { - if (!found_pack) { + if (!*found_pack) { if (!is_pack_valid(p)) { warning("packfile %s cannot be accessed", p->pack_name); continue; } - found_offset = offset; - found_pack = p; + *found_offset = offset; + *found_pack = p; } if (exclude) - break; + return 1; if (incremental) return 0; if (local && !p->pack_local) @@ -947,14 +978,21 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, } } - if (nr_objects >= nr_alloc) { - nr_alloc = (nr_alloc + 1024) * 3 / 2; - objects = xrealloc(objects, nr_alloc * sizeof(*entry)); - } + return 1; +} + +static void create_object_entry(const unsigned char *sha1, + enum object_type type, + uint32_t hash, + int exclude, + int no_try_delta, + uint32_t index_pos, + struct packed_git *found_pack, + off_t found_offset) +{ + struct object_entry *entry; - entry = objects + nr_objects++; - memset(entry, 0, sizeof(*entry)); - hashcpy(entry->idx.sha1, sha1); + entry = packlist_alloc(&to_pack, sha1, index_pos); entry->hash = hash; if (type) entry->type = type; @@ -967,16 +1005,53 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, entry->in_pack_offset = found_offset; } - if (object_ix_hashsz * 3 <= nr_objects * 4) - rehash_objects(); - else - object_ix[-1 - ix] = nr_objects; + entry->no_try_delta = no_try_delta; +} + +static const char no_closure_warning[] = N_( +"disabling bitmap writing, as some objects are not being packed" +); + +static int add_object_entry(const unsigned char *sha1, enum object_type type, + const char *name, int exclude) +{ + struct packed_git *found_pack; + off_t found_offset; + uint32_t index_pos; + + if (have_duplicate_entry(sha1, exclude, &index_pos)) + return 0; + + if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) { + /* The pack is missing an object, so it will not have closure */ + if (write_bitmap_index) { + warning(_(no_closure_warning)); + write_bitmap_index = 0; + } + return 0; + } + + create_object_entry(sha1, 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; +} - display_progress(progress_state, nr_objects); +static int add_object_entry_from_bitmap(const unsigned char *sha1, + enum object_type type, + int flags, uint32_t name_hash, + struct packed_git *pack, off_t offset) +{ + uint32_t index_pos; + + if (have_duplicate_entry(sha1, 0, &index_pos)) + return 0; - if (name && no_try_delta(name)) - entry->no_try_delta = 1; + create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset); + display_progress(progress_state, nr_result); return 1; } @@ -1158,12 +1233,9 @@ static int check_pbase_path(unsigned hash) if (0 <= pos) return 1; pos = -pos - 1; - if (done_pbase_paths_alloc <= done_pbase_paths_num) { - done_pbase_paths_alloc = alloc_nr(done_pbase_paths_alloc); - done_pbase_paths = xrealloc(done_pbase_paths, - done_pbase_paths_alloc * - sizeof(unsigned)); - } + ALLOC_GROW(done_pbase_paths, + done_pbase_paths_num + 1, + done_pbase_paths_alloc); done_pbase_paths_num++; if (pos < done_pbase_paths_num) memmove(done_pbase_paths + pos + 1, @@ -1177,7 +1249,7 @@ static void add_preferred_base_object(const char *name) { struct pbase_tree *it; int cmplen; - unsigned hash = name_hash(name); + unsigned hash = pack_name_hash(name); if (!num_preferred_base || check_pbase_path(hash)) return; @@ -1329,7 +1401,7 @@ static void check_object(struct object_entry *entry) break; } - if (base_ref && (base_entry = locate_object_entry(base_ref))) { + if (base_ref && (base_entry = packlist_find(&to_pack, base_ref, NULL))) { /* * If base_ref was set above that means we wish to * reuse delta data, and we even found that base @@ -1403,12 +1475,12 @@ static void get_object_details(void) uint32_t i; struct object_entry **sorted_by_offset; - sorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *)); - for (i = 0; i < nr_objects; i++) - sorted_by_offset[i] = objects + i; - qsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort); + 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; + qsort(sorted_by_offset, to_pack.nr_objects, sizeof(*sorted_by_offset), pack_offset_sort); - for (i = 0; i < nr_objects; i++) { + 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) @@ -2032,9 +2104,9 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo { unsigned char peeled[20]; - if (!prefixcmp(path, "refs/tags/") && /* is a tag? */ + if (starts_with(path, "refs/tags/") && /* is a tag? */ !peel_ref(path, peeled) && /* peelable? */ - locate_object_entry(peeled)) /* object packed? */ + packlist_find(&to_pack, peeled, NULL)) /* object packed? */ add_object_entry(sha1, OBJ_TAG, NULL, 0); return 0; } @@ -2057,14 +2129,14 @@ static void prepare_pack(int window, int depth) if (!pack_to_stdout) do_check_packed_object_crc = 1; - if (!nr_objects || !window || !depth) + if (!to_pack.nr_objects || !window || !depth) return; - delta_list = xmalloc(nr_objects * sizeof(*delta_list)); + delta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list)); nr_deltas = n = 0; - for (i = 0; i < nr_objects; i++) { - struct object_entry *entry = objects + i; + for (i = 0; i < to_pack.nr_objects; i++) { + struct object_entry *entry = to_pack.objects + i; if (entry->delta) /* This happens if we decided to reuse existing @@ -2099,7 +2171,7 @@ static void prepare_pack(int window, int depth) if (nr_deltas && n > 1) { unsigned nr_done = 0; if (progress) - progress_state = start_progress("Compressing objects", + progress_state = start_progress(_("Compressing objects"), nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); ll_find_deltas(delta_list, n, window+1, depth, &nr_done); @@ -2142,6 +2214,20 @@ static int git_pack_config(const char *k, const char *v, void *cb) cache_max_small_delta_size = git_config_int(k, v); return 0; } + if (!strcmp(k, "pack.writebitmaps")) { + write_bitmap_index = git_config_bool(k, v); + return 0; + } + if (!strcmp(k, "pack.writebitmaphashcache")) { + if (git_config_bool(k, v)) + write_bitmap_options |= BITMAP_OPT_HASH_CACHE; + else + write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE; + } + if (!strcmp(k, "pack.usebitmaps")) { + use_bitmap_index = git_config_bool(k, v); + return 0; + } if (!strcmp(k, "pack.threads")) { delta_search_threads = git_config_int(k, v); if (delta_search_threads < 0) @@ -2200,6 +2286,9 @@ static void show_commit(struct commit *commit, void *data) { add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0); commit->object.flags |= OBJECT_ADDED; + + if (write_bitmap_index) + index_commit_for_bitmap(commit); } static void show_object(struct object *obj, @@ -2342,7 +2431,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs) for (i = 0; i < p->num_objects; i++) { sha1 = nth_packed_object_sha1(p, i); - if (!locate_object_entry(sha1) && + if (!packlist_find(&to_pack, sha1, NULL) && !has_sha1_pack_kept_or_nonlocal(sha1)) if (force_object_loose(sha1, p->mtime)) die("unable to force loose object"); @@ -2350,6 +2439,24 @@ static void loosen_unused_packed_objects(struct rev_info *revs) } } +static int get_object_list_from_bitmap(struct rev_info *revs) +{ + if (prepare_bitmap_walk(revs) < 0) + return -1; + + if (!reuse_partial_packfile_from_bitmap( + &reuse_packfile, + &reuse_packfile_objects, + &reuse_packfile_offset)) { + assert(reuse_packfile_objects); + nr_result += reuse_packfile_objects; + display_progress(progress_state, nr_result); + } + + traverse_bitmap_commit_list(&add_object_entry_from_bitmap); + return 0; +} + static void get_object_list(int ac, const char **av) { struct rev_info revs; @@ -2360,6 +2467,9 @@ static void get_object_list(int ac, const char **av) save_commit_buffer = 0; setup_revisions(ac, av, &revs, NULL); + /* make sure shallows are read */ + is_repository_shallow(); + while (fgets(line, sizeof(line), stdin) != NULL) { int len = strlen(line); if (len && line[len - 1] == '\n') @@ -2369,6 +2479,14 @@ static void get_object_list(int ac, const char **av) if (*line == '-') { if (!strcmp(line, "--not")) { flags ^= UNINTERESTING; + write_bitmap_index = 0; + continue; + } + if (starts_with(line, "--shallow ")) { + unsigned char sha1[20]; + if (get_sha1_hex(line + 10, sha1)) + die("not an SHA-1 '%s'", line + 10); + register_shallow(sha1); continue; } die("not a rev '%s'", line); @@ -2377,6 +2495,9 @@ static void get_object_list(int ac, const char **av) die("bad revision '%s'", line); } + if (use_bitmap_index && !get_object_list_from_bitmap(&revs)) + return; + if (prepare_revision_walk(&revs)) die("revision walk setup failed"); mark_edges_uninteresting(&revs, show_edge); @@ -2506,10 +2627,14 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("pack compression level")), OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents, N_("do not hide commits by grafts"), 0), + OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index, + N_("use a bitmap index if available to speed up counting objects")), + OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index, + N_("write a bitmap index together with the pack index")), OPT_END(), }; - read_replace_refs = 0; + check_replace_refs = 0; reset_pack_idx_option(&pack_idx_opts); git_config(git_pack_config, NULL); @@ -2572,13 +2697,19 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (keep_unreachable && unpack_unreachable) die("--keep-unreachable and --unpack-unreachable are incompatible."); + if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow()) + use_bitmap_index = 0; + + if (pack_to_stdout || !rev_list_all) + write_bitmap_index = 0; + if (progress && all_progress_implied) progress = 2; prepare_packed_git(); if (progress) - progress_state = start_progress("Counting objects", 0); + progress_state = start_progress(_("Counting objects"), 0); if (!use_internal_rev_list) read_object_list_from_stdin(); else { diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index fa6ce42f44..6879468c46 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -10,58 +10,62 @@ static const char * const prune_packed_usage[] = { static struct progress *progress; -static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) +static void prune_dir(int i, DIR *dir, struct strbuf *pathname, int opts) { struct dirent *de; char hex[40]; + int top_len = pathname->len; sprintf(hex, "%02x", i); while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; if (strlen(de->d_name) != 38) continue; - memcpy(hex+2, de->d_name, 38); + memcpy(hex + 2, de->d_name, 38); if (get_sha1_hex(hex, sha1)) continue; if (!has_sha1_pack(sha1)) continue; - memcpy(pathname + len, de->d_name, 38); + + strbuf_add(pathname, de->d_name, 38); if (opts & PRUNE_PACKED_DRY_RUN) - printf("rm -f %s\n", pathname); + printf("rm -f %s\n", pathname->buf); else - unlink_or_warn(pathname); + unlink_or_warn(pathname->buf); display_progress(progress, i + 1); + strbuf_setlen(pathname, top_len); } } void prune_packed_objects(int opts) { int i; - static char pathname[PATH_MAX]; const char *dir = get_object_directory(); - int len = strlen(dir); + struct strbuf pathname = STRBUF_INIT; + int top_len; + strbuf_addstr(&pathname, dir); if (opts & PRUNE_PACKED_VERBOSE) - progress = start_progress_delay("Removing duplicate objects", + progress = start_progress_delay(_("Removing duplicate objects"), 256, 95, 2); - if (len > PATH_MAX - 42) - die("impossible object directory"); - memcpy(pathname, dir, len); - if (len && pathname[len-1] != '/') - pathname[len++] = '/'; + if (pathname.len && pathname.buf[pathname.len - 1] != '/') + strbuf_addch(&pathname, '/'); + + top_len = pathname.len; for (i = 0; i < 256; i++) { DIR *d; display_progress(progress, i + 1); - sprintf(pathname + len, "%02x/", i); - d = opendir(pathname); + strbuf_setlen(&pathname, top_len); + strbuf_addf(&pathname, "%02x/", i); + d = opendir(pathname.buf); if (!d) continue; - prune_dir(i, d, pathname, len + 3, opts); + prune_dir(i, d, &pathname, opts); closedir(d); - pathname[len + 2] = '\0'; - rmdir(pathname); + strbuf_setlen(&pathname, top_len + 2); + rmdir(pathname.buf); } stop_progress(&progress); } diff --git a/builtin/prune.c b/builtin/prune.c index 6366917c6d..144a3bdb33 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -17,9 +17,8 @@ static int verbose; static unsigned long expire; static int show_progress = -1; -static int prune_tmp_object(const char *path, const char *filename) +static int prune_tmp_file(const char *fullpath) { - const char *fullpath = mkpath("%s/%s", path, filename); struct stat st; if (lstat(fullpath, &st)) return error("Could not stat '%s'", fullpath); @@ -32,9 +31,8 @@ static int prune_tmp_object(const char *path, const char *filename) return 0; } -static int prune_object(char *path, const char *filename, const unsigned char *sha1) +static int prune_object(const char *fullpath, const unsigned char *sha1) { - const char *fullpath = mkpath("%s/%s", path, filename); struct stat st; if (lstat(fullpath, &st)) return error("Could not stat '%s'", fullpath); @@ -50,9 +48,10 @@ static int prune_object(char *path, const char *filename, const unsigned char *s return 0; } -static int prune_dir(int i, char *path) +static int prune_dir(int i, struct strbuf *path) { - DIR *dir = opendir(path); + size_t baselen = path->len; + DIR *dir = opendir(path->buf); struct dirent *de; if (!dir) @@ -77,28 +76,39 @@ static int prune_dir(int i, char *path) if (lookup_object(sha1)) continue; - prune_object(path, de->d_name, sha1); + strbuf_addf(path, "/%s", de->d_name); + prune_object(path->buf, sha1); + strbuf_setlen(path, baselen); continue; } - if (!prefixcmp(de->d_name, "tmp_obj_")) { - prune_tmp_object(path, de->d_name); + if (starts_with(de->d_name, "tmp_obj_")) { + strbuf_addf(path, "/%s", de->d_name); + prune_tmp_file(path->buf); + strbuf_setlen(path, baselen); continue; } - fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); + fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name); } closedir(dir); if (!show_only) - rmdir(path); + rmdir(path->buf); return 0; } static void prune_object_dir(const char *path) { + struct strbuf buf = STRBUF_INIT; + size_t baselen; int i; + + strbuf_addstr(&buf, path); + strbuf_addch(&buf, '/'); + baselen = buf.len; + for (i = 0; i < 256; i++) { - static char dir[4096]; - sprintf(dir, "%s/%02x", path, i); - prune_dir(i, dir); + strbuf_addf(&buf, "%02x", i); + prune_dir(i, &buf); + strbuf_setlen(&buf, baselen); } } @@ -119,8 +129,8 @@ static void remove_temporary_files(const char *path) return; } while ((de = readdir(dir)) != NULL) - if (!prefixcmp(de->d_name, "tmp_")) - prune_tmp_object(path, de->d_name); + if (starts_with(de->d_name, "tmp_")) + prune_tmp_file(mkpath("%s/%s", path, de->d_name)); closedir(dir); } @@ -140,7 +150,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) expire = ULONG_MAX; save_commit_buffer = 0; - read_replace_refs = 0; + check_replace_refs = 0; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); @@ -159,7 +169,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) if (show_progress == -1) show_progress = isatty(2); if (show_progress) - progress = start_progress_delay("Checking connectivity", 0, 0, 2); + progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); mark_reachable_objects(&revs, 1, progress); stop_progress(&progress); @@ -170,5 +180,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix) s = mkpathdup("%s/pack", get_object_directory()); remove_temporary_files(s); free(s); + + if (is_repository_shallow()) + prune_shallow(show_only); + return 0; } diff --git a/builtin/push.c b/builtin/push.c index 7b1b66c36a..f8dfea41e1 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -26,7 +26,6 @@ static struct push_cas_option cas; static const char **refspec; static int refspec_nr; static int refspec_alloc; -static int default_matching_used; static void add_refspec(const char *ref) { @@ -35,35 +34,75 @@ static void add_refspec(const char *ref) refspec[refspec_nr-1] = ref; } -static void set_refspecs(const char **refs, int nr) +static const char *map_refspec(const char *ref, + struct remote *remote, struct ref *local_refs) { + struct ref *matched = NULL; + + /* Does "ref" uniquely name our ref? */ + if (count_refspec_match(ref, local_refs, &matched) != 1) + return ref; + + if (remote->push) { + struct refspec query; + memset(&query, 0, sizeof(struct refspec)); + query.src = matched->name; + if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) && + query.dst) { + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, "%s%s:%s", + query.force ? "+" : "", + query.src, query.dst); + return strbuf_detach(&buf, NULL); + } + } + + if (push_default == PUSH_DEFAULT_UPSTREAM && + starts_with(matched->name, "refs/heads/")) { + struct branch *branch = branch_get(matched->name + 11); + if (branch->merge_nr == 1 && branch->merge[0]->src) { + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, "%s:%s", + ref, branch->merge[0]->src); + return strbuf_detach(&buf, NULL); + } + } + + return ref; +} + +static void set_refspecs(const char **refs, int nr, const char *repo) +{ + struct remote *remote = NULL; + struct ref *local_refs = NULL; int i; + for (i = 0; i < nr; i++) { const char *ref = refs[i]; if (!strcmp("tag", ref)) { - char *tag; - int len; + struct strbuf tagref = STRBUF_INIT; if (nr <= ++i) die(_("tag shorthand without <tag>")); - len = strlen(refs[i]) + 11; - if (deleterefs) { - tag = xmalloc(len+1); - strcpy(tag, ":refs/tags/"); - } else { - tag = xmalloc(len); - strcpy(tag, "refs/tags/"); + ref = refs[i]; + if (deleterefs) + strbuf_addf(&tagref, ":refs/tags/%s", ref); + else + strbuf_addf(&tagref, "refs/tags/%s", ref); + ref = strbuf_detach(&tagref, NULL); + } else if (deleterefs) { + struct strbuf delref = STRBUF_INIT; + if (strchr(ref, ':')) + die(_("--delete only accepts plain target ref names")); + strbuf_addf(&delref, ":%s", ref); + ref = strbuf_detach(&delref, NULL); + } else if (!strchr(ref, ':')) { + if (!remote) { + /* lazily grab remote and local_refs */ + remote = remote_get(repo); + local_refs = get_local_heads(); } - strcat(tag, refs[i]); - ref = tag; - } else if (deleterefs && !strchr(ref, ':')) { - char *delref; - int len = strlen(ref)+1; - delref = xmalloc(len+1); - strcpy(delref, ":"); - strcat(delref, ref); - ref = delref; - } else if (deleterefs) - die(_("--delete only accepts plain target ref names")); + ref = map_refspec(ref, remote, local_refs); + } add_refspec(ref); } } @@ -164,9 +203,9 @@ static void setup_push_current(struct remote *remote, struct branch *branch) } static char warn_unspecified_push_default_msg[] = -N_("push.default is unset; its implicit value is changing in\n" +N_("push.default is unset; its implicit value has changed in\n" "Git 2.0 from 'matching' to 'simple'. To squelch this message\n" - "and maintain the current behavior after the default changes, use:\n" + "and maintain the traditional behavior, use:\n" "\n" " git config --global push.default matching\n" "\n" @@ -174,6 +213,13 @@ N_("push.default is unset; its implicit value is changing in\n" "\n" " git config --global push.default simple\n" "\n" + "When push.default is set to 'matching', git will push local branches\n" + "to the remote branches that already exist with the same name.\n" + "\n" + "Since Git 2.0, Git defaults to the more conservative 'simple'\n" + "behavior, which only pushes the current branch to the corresponding\n" + "remote branch that 'git pull' uses to update the current branch.\n" + "\n" "See 'git help config' and search for 'push.default' for further information.\n" "(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode\n" "'current' instead of 'simple' if you sometimes use older versions of Git)"); @@ -200,14 +246,14 @@ static void setup_default_push_refspecs(struct remote *remote) switch (push_default) { default: - case PUSH_DEFAULT_UNSPECIFIED: - default_matching_used = 1; - warn_unspecified_push_default_configuration(); - /* fallthru */ case PUSH_DEFAULT_MATCHING: add_refspec(":"); break; + case PUSH_DEFAULT_UNSPECIFIED: + warn_unspecified_push_default_configuration(); + /* fallthru */ + case PUSH_DEFAULT_SIMPLE: if (triangular) setup_push_current(remote, branch); @@ -236,12 +282,6 @@ static const char message_advice_pull_before_push[] = "'git pull ...') before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); -static const char message_advice_use_upstream[] = - N_("Updates were rejected because a pushed branch tip is behind its remote\n" - "counterpart. If you did not intend to push that branch, you may want to\n" - "specify branches to push or set the 'push.default' configuration variable\n" - "to 'simple', 'current' or 'upstream' to push only the current branch."); - static const char message_advice_checkout_pull_push[] = N_("Updates were rejected because a pushed branch tip is behind its remote\n" "counterpart. Check out this branch and integrate the remote changes\n" @@ -270,13 +310,6 @@ static void advise_pull_before_push(void) advise(_(message_advice_pull_before_push)); } -static void advise_use_upstream(void) -{ - if (!advice_push_non_ff_default || !advice_push_update_rejected) - return; - advise(_(message_advice_use_upstream)); -} - static void advise_checkout_pull_push(void) { if (!advice_push_non_ff_matching || !advice_push_update_rejected) @@ -338,10 +371,7 @@ static int push_with_options(struct transport *transport, int flags) if (reject_reasons & REJECT_NON_FF_HEAD) { advise_pull_before_push(); } else if (reject_reasons & REJECT_NON_FF_OTHER) { - if (default_matching_used) - advise_use_upstream(); - else - advise_checkout_pull_push(); + advise_checkout_pull_push(); } else if (reject_reasons & REJECT_ALREADY_EXISTS) { advise_ref_already_exists(); } else if (reject_reasons & REJECT_FETCH_FIRST) { @@ -494,7 +524,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) if (argc > 0) { repo = argv[0]; - set_refspecs(argv + 1, argc - 1); + set_refspecs(argv + 1, argc - 1, repo); } rc = do_push(repo, flags); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 67ce1ef105..c3230817db 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -13,6 +13,7 @@ #include "string-list.h" #include "sha1-array.h" #include "connected.h" +#include "argv-array.h" #include "version.h" static const char receive_pack_usage[] = "git receive-pack <git-dir>"; @@ -43,6 +44,8 @@ static int fix_thin = 1; static const char *head_name; static void *head_name_to_free; static int sent_capabilities; +static int shallow_update; +static const char *alt_shallow_file; static enum deny_action parse_deny_action(const char *var, const char *value) { @@ -121,6 +124,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "receive.shallowupdate") == 0) { + shallow_update = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, cb); } @@ -178,6 +186,8 @@ static void write_head_info(void) if (!sent_capabilities) show_ref("capabilities^{}", null_sha1); + advertise_shallow_grafts(1); + /* EOF */ packet_flush(1); } @@ -187,6 +197,7 @@ struct command { const char *error_string; unsigned int skip_update:1, did_not_exist:1; + int index; unsigned char old_sha1[20]; unsigned char new_sha1[20]; char ref_name[FLEX_ARRAY]; /* more */ @@ -418,7 +429,46 @@ static void refuse_unconfigured_deny_delete_current(void) rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]); } -static const char *update(struct command *cmd) +static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]); +static int update_shallow_ref(struct command *cmd, struct shallow_info *si) +{ + static struct lock_file shallow_lock; + struct sha1_array extra = SHA1_ARRAY_INIT; + const char *alt_file; + uint32_t mask = 1 << (cmd->index % 32); + int i; + + trace_printf_key("GIT_TRACE_SHALLOW", + "shallow: update_shallow_ref %s\n", cmd->ref_name); + for (i = 0; i < si->shallow->nr; i++) + if (si->used_shallow[i] && + (si->used_shallow[i][cmd->index / 32] & mask) && + !delayed_reachability_test(si, i)) + sha1_array_append(&extra, si->shallow->sha1[i]); + + setup_alternate_shallow(&shallow_lock, &alt_file, &extra); + if (check_shallow_connected(command_singleton_iterator, + 0, cmd, alt_file)) { + rollback_lock_file(&shallow_lock); + sha1_array_clear(&extra); + return -1; + } + + commit_lock_file(&shallow_lock); + + /* + * Make sure setup_alternate_shallow() for the next ref does + * not lose these new roots.. + */ + for (i = 0; i < extra.nr; i++) + register_shallow(extra.sha1[i]); + + si->shallow_ref[cmd->index] = 0; + sha1_array_clear(&extra); + return 0; +} + +static const char *update(struct command *cmd, struct shallow_info *si) { const char *name = cmd->ref_name; struct strbuf namespaced_name_buf = STRBUF_INIT; @@ -428,7 +478,7 @@ static const char *update(struct command *cmd) struct ref_lock *lock; /* only refs/... are allowed */ - if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) { + if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { rp_error("refusing to create funny ref '%s' remotely", name); return "funny refname"; } @@ -459,7 +509,7 @@ static const char *update(struct command *cmd) } if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) { - if (deny_deletes && !prefixcmp(name, "refs/heads/")) { + if (deny_deletes && starts_with(name, "refs/heads/")) { rp_error("denying ref deletion for %s", name); return "deletion prohibited"; } @@ -483,7 +533,7 @@ static const char *update(struct command *cmd) if (deny_non_fast_forwards && !is_null_sha1(new_sha1) && !is_null_sha1(old_sha1) && - !prefixcmp(name, "refs/heads/")) { + starts_with(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; @@ -526,6 +576,10 @@ static const char *update(struct command *cmd) return NULL; /* good */ } else { + if (shallow_update && si->shallow_ref[cmd->index] && + update_shallow_ref(cmd, si)) + return "shallow error"; + lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0, NULL); if (!lock) { @@ -666,12 +720,16 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]) return 0; } -static void set_connectivity_errors(struct command *commands) +static void set_connectivity_errors(struct command *commands, + struct shallow_info *si) { struct command *cmd; for (cmd = commands; cmd; cmd = cmd->next) { struct command *singleton = cmd; + if (shallow_update && si->shallow_ref[cmd->index]) + /* to be checked in update_shallow_ref() */ + continue; if (!check_everything_connected(command_singleton_iterator, 0, &singleton)) continue; @@ -679,18 +737,26 @@ static void set_connectivity_errors(struct command *commands) } } +struct iterate_data { + struct command *cmds; + struct shallow_info *si; +}; + static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20]) { - struct command **cmd_list = cb_data; + struct iterate_data *data = cb_data; + struct command **cmd_list = &data->cmds; struct command *cmd = *cmd_list; - while (cmd) { - if (!is_null_sha1(cmd->new_sha1)) { + for (; cmd; cmd = cmd->next) { + if (shallow_update && data->si->shallow_ref[cmd->index]) + /* to be checked in update_shallow_ref() */ + continue; + if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) { hashcpy(sha1, cmd->new_sha1); *cmd_list = cmd->next; return 0; } - cmd = cmd->next; } *cmd_list = NULL; return -1; /* end of list */ @@ -710,10 +776,14 @@ static void reject_updates_to_hidden(struct command *commands) } } -static void execute_commands(struct command *commands, const char *unpacker_error) +static void execute_commands(struct command *commands, + const char *unpacker_error, + struct shallow_info *si) { + int checked_connectivity; struct command *cmd; unsigned char sha1[20]; + struct iterate_data data; if (unpacker_error) { for (cmd = commands; cmd; cmd = cmd->next) @@ -721,10 +791,10 @@ static void execute_commands(struct command *commands, const char *unpacker_erro return; } - cmd = commands; - if (check_everything_connected(iterate_receive_command_list, - 0, &cmd)) - set_connectivity_errors(commands); + data.cmds = commands; + data.si = si; + if (check_everything_connected(iterate_receive_command_list, 0, &data)) + set_connectivity_errors(commands, si); reject_updates_to_hidden(commands); @@ -741,6 +811,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro free(head_name_to_free); head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL); + checked_connectivity = 1; for (cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string) continue; @@ -748,11 +819,22 @@ static void execute_commands(struct command *commands, const char *unpacker_erro if (cmd->skip_update) continue; - cmd->error_string = update(cmd); + cmd->error_string = update(cmd, si); + if (shallow_update && !cmd->error_string && + si->shallow_ref[cmd->index]) { + error("BUG: connectivity check has not been run on ref %s", + cmd->ref_name); + checked_connectivity = 0; + } } + + if (shallow_update && !checked_connectivity) + error("BUG: run 'git fsck' for safety.\n" + "If there are errors, try to remove " + "the reported refs above"); } -static struct command *read_head_info(void) +static struct command *read_head_info(struct sha1_array *shallow) { struct command *commands = NULL; struct command **p = &commands; @@ -766,6 +848,14 @@ static struct command *read_head_info(void) line = packet_read_line(0, &len); if (!line) break; + + if (len == 48 && starts_with(line, "shallow ")) { + if (get_sha1_hex(line + 8, old_sha1)) + die("protocol error: expected shallow sha, got '%s'", line + 8); + sha1_array_append(shallow, old_sha1); + continue; + } + if (len < 83 || line[40] != ' ' || line[81] != ' ' || @@ -817,11 +907,14 @@ static const char *parse_pack_header(struct pack_header *hdr) static const char *pack_lockfile; -static const char *unpack(int err_fd) +static const char *unpack(int err_fd, struct shallow_info *si) { struct pack_header hdr; + struct argv_array av = ARGV_ARRAY_INIT; const char *hdr_err; + int status; char hdr_arg[38]; + struct child_process child; int fsck_objects = (receive_fsck_objects >= 0 ? receive_fsck_objects : transfer_fsck_objects >= 0 @@ -838,72 +931,63 @@ static const char *unpack(int err_fd) "--pack_header=%"PRIu32",%"PRIu32, ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); + if (si->nr_ours || si->nr_theirs) { + alt_shallow_file = setup_temporary_shallow(si->shallow); + argv_array_pushl(&av, "--shallow-file", alt_shallow_file, NULL); + } + + memset(&child, 0, sizeof(child)); if (ntohl(hdr.hdr_entries) < unpack_limit) { - int code, i = 0; - struct child_process child; - const char *unpacker[5]; - unpacker[i++] = "unpack-objects"; + argv_array_pushl(&av, "unpack-objects", hdr_arg, NULL); if (quiet) - unpacker[i++] = "-q"; + argv_array_push(&av, "-q"); if (fsck_objects) - unpacker[i++] = "--strict"; - unpacker[i++] = hdr_arg; - unpacker[i++] = NULL; - memset(&child, 0, sizeof(child)); - child.argv = unpacker; + argv_array_push(&av, "--strict"); + child.argv = av.argv; child.no_stdout = 1; child.err = err_fd; child.git_cmd = 1; - code = run_command(&child); - if (!code) - return NULL; - return "unpack-objects abnormal exit"; + status = run_command(&child); + if (status) + return "unpack-objects abnormal exit"; } else { - const char *keeper[7]; - int s, status, i = 0; + int s; char keep_arg[256]; - struct child_process ip; s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid()); if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) strcpy(keep_arg + s, "localhost"); - keeper[i++] = "index-pack"; - keeper[i++] = "--stdin"; + argv_array_pushl(&av, "index-pack", + "--stdin", hdr_arg, keep_arg, NULL); if (fsck_objects) - keeper[i++] = "--strict"; + argv_array_push(&av, "--strict"); if (fix_thin) - keeper[i++] = "--fix-thin"; - keeper[i++] = hdr_arg; - keeper[i++] = keep_arg; - keeper[i++] = NULL; - memset(&ip, 0, sizeof(ip)); - ip.argv = keeper; - ip.out = -1; - ip.err = err_fd; - ip.git_cmd = 1; - status = start_command(&ip); - if (status) { + argv_array_push(&av, "--fix-thin"); + child.argv = av.argv; + child.out = -1; + child.err = err_fd; + child.git_cmd = 1; + status = start_command(&child); + if (status) return "index-pack fork failed"; - } - pack_lockfile = index_pack_lockfile(ip.out); - close(ip.out); - status = finish_command(&ip); - if (!status) { - reprepare_packed_git(); - return NULL; - } - return "index-pack abnormal exit"; + pack_lockfile = index_pack_lockfile(child.out); + close(child.out); + status = finish_command(&child); + if (status) + return "index-pack abnormal exit"; + reprepare_packed_git(); } + return NULL; } -static const char *unpack_with_sideband(void) +static const char *unpack_with_sideband(struct shallow_info *si) { struct async muxer; const char *ret; if (!use_sideband) - return unpack(0); + return unpack(0, si); memset(&muxer, 0, sizeof(muxer)); muxer.proc = copy_to_sideband; @@ -911,12 +995,97 @@ static const char *unpack_with_sideband(void) if (start_async(&muxer)) return NULL; - ret = unpack(muxer.in); + ret = unpack(muxer.in, si); finish_async(&muxer); return ret; } +static void prepare_shallow_update(struct command *commands, + struct shallow_info *si) +{ + int i, j, k, bitmap_size = (si->ref->nr + 31) / 32; + + si->used_shallow = xmalloc(sizeof(*si->used_shallow) * + si->shallow->nr); + assign_shallow_commits_to_refs(si, si->used_shallow, NULL); + + si->need_reachability_test = + xcalloc(si->shallow->nr, sizeof(*si->need_reachability_test)); + si->reachable = + xcalloc(si->shallow->nr, sizeof(*si->reachable)); + si->shallow_ref = xcalloc(si->ref->nr, sizeof(*si->shallow_ref)); + + for (i = 0; i < si->nr_ours; i++) + si->need_reachability_test[si->ours[i]] = 1; + + for (i = 0; i < si->shallow->nr; i++) { + if (!si->used_shallow[i]) + continue; + for (j = 0; j < bitmap_size; j++) { + if (!si->used_shallow[i][j]) + continue; + si->need_reachability_test[i]++; + for (k = 0; k < 32; k++) + if (si->used_shallow[i][j] & (1 << k)) + si->shallow_ref[j * 32 + k]++; + } + + /* + * true for those associated with some refs and belong + * in "ours" list aka "step 7 not done yet" + */ + si->need_reachability_test[i] = + si->need_reachability_test[i] > 1; + } + + /* + * keep hooks happy by forcing a temporary shallow file via + * env variable because we can't add --shallow-file to every + * command. check_everything_connected() will be done with + * true .git/shallow though. + */ + setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1); +} + +static void update_shallow_info(struct command *commands, + struct shallow_info *si, + struct sha1_array *ref) +{ + struct command *cmd; + int *ref_status; + remove_nonexistent_theirs_shallow(si); + if (!si->nr_ours && !si->nr_theirs) { + shallow_update = 0; + return; + } + + for (cmd = commands; cmd; cmd = cmd->next) { + if (is_null_sha1(cmd->new_sha1)) + continue; + sha1_array_append(ref, cmd->new_sha1); + cmd->index = ref->nr - 1; + } + si->ref = ref; + + if (shallow_update) { + prepare_shallow_update(commands, si); + return; + } + + ref_status = xmalloc(sizeof(*ref_status) * ref->nr); + assign_shallow_commits_to_refs(si, NULL, ref_status); + for (cmd = commands; cmd; cmd = cmd->next) { + if (is_null_sha1(cmd->new_sha1)) + continue; + if (ref_status[cmd->index]) { + cmd->error_string = "shallow update not allowed"; + cmd->skip_update = 1; + } + } + free(ref_status); +} + static void report(struct command *commands, const char *unpack_status) { struct command *cmd; @@ -958,6 +1127,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) int i; char *dir = NULL; struct command *commands; + struct sha1_array shallow = SHA1_ARRAY_INIT; + struct sha1_array ref = SHA1_ARRAY_INIT; + struct shallow_info si; packet_trace_identity("receive-pack"); @@ -998,9 +1170,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (!enter_repo(dir, 0)) die("'%s' does not appear to be a git repository", dir); - if (is_repository_shallow()) - die("attempt to push into a shallow repository"); - git_config(receive_pack_config, NULL); if (0 <= transfer_unpack_limit) @@ -1014,12 +1183,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (advertise_refs) return 0; - if ((commands = read_head_info()) != NULL) { + if ((commands = read_head_info(&shallow)) != NULL) { const char *unpack_status = NULL; - if (!delete_only(commands)) - unpack_status = unpack_with_sideband(); - execute_commands(commands, unpack_status); + prepare_shallow_info(&si, &shallow); + if (!si.nr_ours && !si.nr_theirs) + shallow_update = 0; + if (!delete_only(commands)) { + unpack_status = unpack_with_sideband(&si); + update_shallow_info(commands, &si, &ref); + } + execute_commands(commands, unpack_status, &si); if (pack_lockfile) unlink_or_warn(pack_lockfile); if (report_status) @@ -1035,8 +1209,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) } if (auto_update_server_info) update_server_info(0); + clear_shallow_info(&si); } if (use_sideband) packet_flush(1); + sha1_array_clear(&shallow); + sha1_array_clear(&ref); return 0; } diff --git a/builtin/reflog.c b/builtin/reflog.c index 6eb24c8da2..c12a9784e6 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -561,7 +561,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c return; /* both given explicitly -- nothing to tweak */ for (ent = reflog_expire_cfg; ent; ent = ent->next) { - if (!fnmatch(ent->pattern, ref, 0)) { + if (!wildmatch(ent->pattern, ref, 0, NULL)) { if (!(slot & EXPIRE_TOTAL)) cb->expire_total = ent->expire_total; if (!(slot & EXPIRE_UNREACH)) @@ -610,12 +610,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) const char *arg = argv[i]; if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) cb.dry_run = 1; - else if (!prefixcmp(arg, "--expire=")) { + else if (starts_with(arg, "--expire=")) { if (parse_expiry_date(arg + 9, &cb.expire_total)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_TOTAL; } - else if (!prefixcmp(arg, "--expire-unreachable=")) { + else if (starts_with(arg, "--expire-unreachable=")) { if (parse_expiry_date(arg + 21, &cb.expire_unreachable)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_UNREACH; diff --git a/builtin/remote.c b/builtin/remote.c index 4e14891095..b3ab4cf8f6 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -6,6 +6,7 @@ #include "strbuf.h" #include "run-command.h" #include "refs.h" +#include "argv-array.h" static const char * const builtin_remote_usage[] = { N_("git remote [-v | --verbose]"), @@ -77,17 +78,6 @@ static const char * const builtin_remote_seturl_usage[] = { static int verbose; -static int show_all(void); -static int prune_remote(const char *remote, int dry_run); - -static inline int postfixcmp(const char *string, const char *postfix) -{ - int len1 = strlen(string), len2 = strlen(postfix); - if (len1 < len2) - return 1; - return strcmp(string + len1 - len2, postfix); -} - static int fetch_remote(const char *name) { const char *argv[] = { "fetch", name, NULL, NULL }; @@ -269,7 +259,7 @@ static const char *abbrev_ref(const char *name, const char *prefix) static int config_read_branches(const char *key, const char *value, void *cb) { - if (!prefixcmp(key, "branch.")) { + if (starts_with(key, "branch.")) { const char *orig_key = key; char *name; struct string_list_item *item; @@ -277,13 +267,13 @@ static int config_read_branches(const char *key, const char *value, void *cb) enum { REMOTE, MERGE, REBASE } type; key += 7; - if (!postfixcmp(key, ".remote")) { + if (ends_with(key, ".remote")) { name = xstrndup(key, strlen(key) - 7); type = REMOTE; - } else if (!postfixcmp(key, ".merge")) { + } else if (ends_with(key, ".merge")) { name = xstrndup(key, strlen(key) - 6); type = MERGE; - } else if (!postfixcmp(key, ".rebase")) { + } else if (ends_with(key, ".rebase")) { name = xstrndup(key, strlen(key) - 7); type = REBASE; } else @@ -309,8 +299,13 @@ static int config_read_branches(const char *key, const char *value, void *cb) space = strchr(value, ' '); } string_list_append(&info->merge, xstrdup(value)); - } else - info->rebase = git_config_bool(orig_key, value); + } else { + int v = git_config_maybe_bool(orig_key, value); + if (v >= 0) + info->rebase = v; + else if (!strcmp(value, "preserve")) + info->rebase = 1; + } } return 0; } @@ -534,9 +529,9 @@ static int add_branch_for_removal(const char *refname, } /* don't delete non-remote-tracking refs */ - if (prefixcmp(refname, "refs/remotes/")) { + if (!starts_with(refname, "refs/remotes/")) { /* advise user how to delete local branches */ - if (!prefixcmp(refname, "refs/heads/")) + if (starts_with(refname, "refs/heads/")) string_list_append(branches->skipped, abbrev_branch(refname)); /* silently skip over other non-remote refs */ @@ -571,7 +566,7 @@ static int read_remote_branches(const char *refname, const char *symref; strbuf_addf(&buf, "refs/remotes/%s/", rename->old); - if (!prefixcmp(refname, buf.buf)) { + if (starts_with(refname, buf.buf)) { item = string_list_append(rename->remote_branches, xstrdup(refname)); symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag); if (flag & REF_ISSYMREF) @@ -1084,6 +1079,64 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data) return 0; } +static int get_one_entry(struct remote *remote, void *priv) +{ + struct string_list *list = priv; + struct strbuf url_buf = STRBUF_INIT; + const char **url; + int i, url_nr; + + if (remote->url_nr > 0) { + strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]); + string_list_append(list, remote->name)->util = + strbuf_detach(&url_buf, NULL); + } else + string_list_append(list, remote->name)->util = NULL; + if (remote->pushurl_nr) { + url = remote->pushurl; + url_nr = remote->pushurl_nr; + } else { + url = remote->url; + url_nr = remote->url_nr; + } + for (i = 0; i < url_nr; i++) + { + strbuf_addf(&url_buf, "%s (push)", url[i]); + string_list_append(list, remote->name)->util = + strbuf_detach(&url_buf, NULL); + } + + return 0; +} + +static int show_all(void) +{ + struct string_list list = STRING_LIST_INIT_NODUP; + int result; + + list.strdup_strings = 1; + result = for_each_remote(get_one_entry, &list); + + if (!result) { + int i; + + sort_string_list(&list); + for (i = 0; i < list.nr; i++) { + struct string_list_item *item = list.items + i; + if (verbose) + printf("%s\t%s\n", item->string, + item->util ? (const char *)item->util : ""); + else { + if (i && !strcmp((item - 1)->string, item->string)) + continue; + printf("%s\n", item->string); + } + } + } + string_list_clear(&list, 1); + return result; +} + static int show(int argc, const char **argv) { int no_query = 0, result = 0, query_flag = 0; @@ -1246,26 +1299,6 @@ static int set_head(int argc, const char **argv) return result; } -static int prune(int argc, const char **argv) -{ - int dry_run = 0, result = 0; - struct option options[] = { - OPT__DRY_RUN(&dry_run, N_("dry run")), - OPT_END() - }; - - argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, - 0); - - if (argc < 1) - usage_with_options(builtin_remote_prune_usage, options); - - for (; argc; argc--, argv++) - result |= prune_remote(*argv, dry_run); - - return result; -} - static int prune_remote(const char *remote, int dry_run) { int result = 0, i; @@ -1304,6 +1337,26 @@ static int prune_remote(const char *remote, int dry_run) return result; } +static int prune(int argc, const char **argv) +{ + int dry_run = 0, result = 0; + struct option options[] = { + OPT__DRY_RUN(&dry_run, N_("dry run")), + OPT_END() + }; + + argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, + 0); + + if (argc < 1) + usage_with_options(builtin_remote_prune_usage, options); + + for (; argc; argc--, argv++) + result |= prune_remote(*argv, dry_run); + + return result; +} + static int get_remote_default(const char *key, const char *value, void *priv) { if (strcmp(key, "remotes.default") == 0) { @@ -1315,42 +1368,42 @@ static int get_remote_default(const char *key, const char *value, void *priv) static int update(int argc, const char **argv) { - int i, prune = 0; + int i, prune = -1; struct option options[] = { OPT_BOOL('p', "prune", &prune, N_("prune remotes after fetching")), OPT_END() }; - const char **fetch_argv; - int fetch_argc = 0; + struct argv_array fetch_argv = ARGV_ARRAY_INIT; int default_defined = 0; - - fetch_argv = xmalloc(sizeof(char *) * (argc+5)); + int retval; argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage, PARSE_OPT_KEEP_ARGV0); - fetch_argv[fetch_argc++] = "fetch"; + argv_array_push(&fetch_argv, "fetch"); - if (prune) - fetch_argv[fetch_argc++] = "--prune"; + if (prune != -1) + argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune"); if (verbose) - fetch_argv[fetch_argc++] = "-v"; - fetch_argv[fetch_argc++] = "--multiple"; + argv_array_push(&fetch_argv, "-v"); + argv_array_push(&fetch_argv, "--multiple"); if (argc < 2) - fetch_argv[fetch_argc++] = "default"; + argv_array_push(&fetch_argv, "default"); for (i = 1; i < argc; i++) - fetch_argv[fetch_argc++] = argv[i]; + argv_array_push(&fetch_argv, argv[i]); - if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) { + if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) { git_config(get_remote_default, &default_defined); - if (!default_defined) - fetch_argv[fetch_argc-1] = "--all"; + if (!default_defined) { + argv_array_pop(&fetch_argv); + argv_array_push(&fetch_argv, "--all"); + } } - fetch_argv[fetch_argc] = NULL; - - return run_command_v_opt(fetch_argv, RUN_GIT_CMD); + retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD); + argv_array_clear(&fetch_argv); + return retval; } static int remove_all_fetch_refspecs(const char *remote, const char *key) @@ -1505,64 +1558,6 @@ static int set_url(int argc, const char **argv) return 0; } -static int get_one_entry(struct remote *remote, void *priv) -{ - struct string_list *list = priv; - struct strbuf url_buf = STRBUF_INIT; - const char **url; - int i, url_nr; - - if (remote->url_nr > 0) { - strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } else - string_list_append(list, remote->name)->util = NULL; - if (remote->pushurl_nr) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } else { - url = remote->url; - url_nr = remote->url_nr; - } - for (i = 0; i < url_nr; i++) - { - strbuf_addf(&url_buf, "%s (push)", url[i]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } - - return 0; -} - -static int show_all(void) -{ - struct string_list list = STRING_LIST_INIT_NODUP; - int result; - - list.strdup_strings = 1; - result = for_each_remote(get_one_entry, &list); - - if (!result) { - int i; - - sort_string_list(&list); - for (i = 0; i < list.nr; i++) { - struct string_list_item *item = list.items + i; - if (verbose) - printf("%s\t%s\n", item->string, - item->util ? (const char *)item->util : ""); - else { - if (i && !strcmp((item - 1)->string, item->string)) - continue; - printf("%s\n", item->string); - } - } - } - string_list_clear(&list, 1); - return result; -} - int cmd_remote(int argc, const char **argv, const char *prefix) { struct option options[] = { diff --git a/builtin/repack.c b/builtin/repack.c index a2b9dba522..6b0b62dcb2 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -9,6 +9,7 @@ #include "argv-array.h" static int delta_base_offset = 1; +static int pack_kept_objects = -1; static char *packdir, *packtmp; static const char *const git_repack_usage[] = { @@ -22,6 +23,10 @@ static int repack_config(const char *var, const char *value, void *cb) delta_base_offset = git_config_bool(var, value); return 0; } + if (!strcmp(var, "repack.packkeptobjects")) { + pack_kept_objects = git_config_bool(var, value); + return 0; + } return git_default_config(var, value, cb); } @@ -78,7 +83,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list) return; while ((e = readdir(dir)) != NULL) { - if (suffixcmp(e->d_name, ".pack")) + if (!ends_with(e->d_name, ".pack")) continue; len = strlen(e->d_name) - strlen(".pack"); @@ -94,7 +99,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list) static void remove_redundant_pack(const char *dir_name, const char *base_name) { - const char *exts[] = {".pack", ".idx", ".keep"}; + const char *exts[] = {".pack", ".idx", ".keep", ".bitmap"}; int i; struct strbuf buf = STRBUF_INIT; size_t plen; @@ -115,7 +120,14 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name) int cmd_repack(int argc, const char **argv, const char *prefix) { - const char *exts[2] = {".pack", ".idx"}; + struct { + const char *name; + unsigned optional:1; + } exts[] = { + {".pack"}, + {".idx"}, + {".bitmap", 1}, + }; struct child_process cmd; struct string_list_item *item; struct argv_array cmd_args = ARGV_ARRAY_INIT; @@ -137,6 +149,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) int no_update_server_info = 0; int quiet = 0; int local = 0; + int write_bitmap = -1; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -155,6 +168,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) OPT__QUIET(&quiet, N_("be quiet")), OPT_BOOL('l', "local", &local, N_("pass --local to git-pack-objects")), + OPT_BOOL('b', "write-bitmap-index", &write_bitmap, + N_("write bitmap index")), OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"), N_("with -A, do not loosen objects older than this")), OPT_STRING(0, "window", &window, N_("n"), @@ -165,6 +180,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("limits the maximum delta depth")), OPT_STRING(0, "max-pack-size", &max_pack_size, N_("bytes"), N_("maximum size of each packfile")), + OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, + N_("repack objects in packs marked with .keep")), OPT_END() }; @@ -173,6 +190,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_repack_options, git_repack_usage, 0); + if (pack_kept_objects < 0) + pack_kept_objects = write_bitmap; + packdir = mkpathdup("%s/pack", get_object_directory()); packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid()); @@ -180,7 +200,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_push(&cmd_args, "pack-objects"); argv_array_push(&cmd_args, "--keep-true-parents"); - argv_array_push(&cmd_args, "--honor-pack-keep"); + if (!pack_kept_objects) + argv_array_push(&cmd_args, "--honor-pack-keep"); argv_array_push(&cmd_args, "--non-empty"); argv_array_push(&cmd_args, "--all"); argv_array_push(&cmd_args, "--reflog"); @@ -196,6 +217,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_pushf(&cmd_args, "--no-reuse-delta"); if (no_reuse_object) argv_array_pushf(&cmd_args, "--no-reuse-object"); + if (write_bitmap >= 0) + argv_array_pushf(&cmd_args, "--%swrite-bitmap-index", + write_bitmap ? "" : "no-"); if (pack_everything & ALL_INTO_ONE) { get_non_kept_pack_filenames(&existing_packs); @@ -256,17 +280,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix) */ failed = 0; for_each_string_list_item(item, &names) { - for (ext = 0; ext < 2; ext++) { + for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { char *fname, *fname_old; - fname = mkpathdup("%s/%s%s", packdir, - item->string, exts[ext]); + fname = mkpathdup("%s/pack-%s%s", packdir, + item->string, exts[ext].name); if (!file_exists(fname)) { free(fname); continue; } fname_old = mkpath("%s/old-%s%s", packdir, - item->string, exts[ext]); + item->string, exts[ext].name); if (file_exists(fname_old)) if (unlink(fname_old)) failed = 1; @@ -313,19 +337,23 @@ int cmd_repack(int argc, const char **argv, const char *prefix) /* Now the ones with the same name are out of the way... */ for_each_string_list_item(item, &names) { - for (ext = 0; ext < 2; ext++) { + for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { char *fname, *fname_old; struct stat statbuffer; + int exists = 0; fname = mkpathdup("%s/pack-%s%s", - packdir, item->string, exts[ext]); + packdir, item->string, exts[ext].name); fname_old = mkpathdup("%s-%s%s", - packtmp, item->string, exts[ext]); + packtmp, item->string, exts[ext].name); if (!stat(fname_old, &statbuffer)) { statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); chmod(fname_old, statbuffer.st_mode); + exists = 1; + } + if (exists || !exts[ext].optional) { + if (rename(fname_old, fname)) + die_errno(_("renaming '%s' failed"), fname_old); } - if (rename(fname_old, fname)) - die_errno(_("renaming '%s' failed"), fname_old); free(fname); free(fname_old); } @@ -333,12 +361,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) /* Remove the "old-" files */ for_each_string_list_item(item, &names) { - for (ext = 0; ext < 2; ext++) { + for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { char *fname; - fname = mkpath("%s/old-pack-%s%s", + fname = mkpath("%s/old-%s%s", packdir, item->string, - exts[ext]); + exts[ext].name); if (remove_path(fname)) warning(_("removing '%s' failed"), fname); } diff --git a/builtin/replace.c b/builtin/replace.c index b1bd3ef994..b62420a01a 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -16,27 +16,69 @@ static const char * const git_replace_usage[] = { N_("git replace [-f] <object> <replacement>"), N_("git replace -d <object>..."), - N_("git replace -l [<pattern>]"), + N_("git replace [--format=<format>] [-l [<pattern>]]"), NULL }; +enum replace_format { + REPLACE_FORMAT_SHORT, + REPLACE_FORMAT_MEDIUM, + REPLACE_FORMAT_LONG +}; + +struct show_data { + const char *pattern; + enum replace_format format; +}; + static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { - const char *pattern = cb_data; + struct show_data *data = cb_data; - if (!fnmatch(pattern, refname, 0)) - printf("%s\n", refname); + if (!wildmatch(data->pattern, refname, 0, NULL)) { + if (data->format == REPLACE_FORMAT_SHORT) + printf("%s\n", refname); + else if (data->format == REPLACE_FORMAT_MEDIUM) + printf("%s -> %s\n", refname, sha1_to_hex(sha1)); + else { /* data->format == REPLACE_FORMAT_LONG */ + unsigned char object[20]; + enum object_type obj_type, repl_type; + + if (get_sha1(refname, object)) + return error("Failed to resolve '%s' as a valid ref.", refname); + + obj_type = sha1_object_info(object, NULL); + repl_type = sha1_object_info(sha1, NULL); + + printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type), + sha1_to_hex(sha1), typename(repl_type)); + } + } return 0; } -static int list_replace_refs(const char *pattern) +static int list_replace_refs(const char *pattern, const char *format) { + struct show_data data; + if (pattern == NULL) pattern = "*"; + data.pattern = pattern; - for_each_replace_ref(show_reference, (void *) pattern); + if (format == NULL || *format == '\0' || !strcmp(format, "short")) + data.format = REPLACE_FORMAT_SHORT; + else if (!strcmp(format, "medium")) + data.format = REPLACE_FORMAT_MEDIUM; + 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); + + for_each_replace_ref(show_reference, (void *) &data); return 0; } @@ -127,19 +169,27 @@ static int replace_object(const char *object_ref, const char *replace_ref, int cmd_replace(int argc, const char **argv, const char *prefix) { int list = 0, delete = 0, force = 0; + const char *format = NULL; struct option options[] = { OPT_BOOL('l', "list", &list, N_("list replace refs")), OPT_BOOL('d', "delete", &delete, N_("delete replace refs")), OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")), + OPT_STRING(0, "format", &format, N_("format"), N_("use this format")), OPT_END() }; + check_replace_refs = 0; + argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); if (list && delete) usage_msg_opt("-l and -d cannot be used together", git_replace_usage, options); + if (format && delete) + usage_msg_opt("--format and -d cannot be used together", + git_replace_usage, options); + if (force && (list || delete)) usage_msg_opt("-f cannot be used with -d or -l", git_replace_usage, options); @@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix) if (argc != 2) usage_msg_opt("bad number of arguments", git_replace_usage, options); + if (format) + usage_msg_opt("--format cannot be used when not listing", + git_replace_usage, options); return replace_object(argv[0], argv[1], force); } @@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix) usage_msg_opt("-f needs some arguments", git_replace_usage, options); - return list_replace_refs(argv[0]); + return list_replace_refs(argv[0], format); } diff --git a/builtin/reset.c b/builtin/reset.c index 60048030dd..f4e087596b 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -116,25 +116,34 @@ static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; + int intent_to_add = *(int *)data; for (i = 0; i < q->nr; i++) { struct diff_filespec *one = q->queue[i]->one; - if (one->mode && !is_null_sha1(one->sha1)) { - struct cache_entry *ce; - ce = make_cache_entry(one->mode, one->sha1, one->path, - 0, 0); - if (!ce) - die(_("make_cache_entry failed for path '%s'"), - one->path); - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | - ADD_CACHE_OK_TO_REPLACE); - } else + int is_missing = !(one->mode && !is_null_sha1(one->sha1)); + struct cache_entry *ce; + + if (is_missing && !intent_to_add) { remove_file_from_cache(one->path); + continue; + } + + ce = make_cache_entry(one->mode, one->sha1, one->path, + 0, 0); + if (!ce) + die(_("make_cache_entry failed for path '%s'"), + one->path); + if (is_missing) { + ce->ce_flags |= CE_INTENT_TO_ADD; + set_object_name_for_intent_to_add_entry(ce); + } + add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); } } static int read_from_tree(const struct pathspec *pathspec, - unsigned char *tree_sha1) + unsigned char *tree_sha1, + int intent_to_add) { struct diff_options opt; @@ -142,6 +151,7 @@ static int read_from_tree(const struct pathspec *pathspec, copy_pathspec(&opt.pathspec, pathspec); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; + opt.format_callback_data = &intent_to_add; if (do_diff_cache(tree_sha1, &opt)) return 1; @@ -258,6 +268,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) const char *rev; unsigned char sha1[20]; struct pathspec pathspec; + int intent_to_add = 0; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, @@ -270,6 +281,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_BOOL('N', "intent-to-add", &intent_to_add, + N_("record only the fact that removed paths will be added later")), OPT_END() }; @@ -320,13 +333,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type == NONE) reset_type = MIXED; /* by default */ - if (reset_type != SOFT && reset_type != MIXED) + if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree())) setup_work_tree(); if (reset_type == MIXED && is_bare_repository()) die(_("%s reset is not allowed in a bare repository"), _(reset_type_names[reset_type])); + if (intent_to_add && reset_type != MIXED) + die(_("-N can only be used with --mixed")); + /* Soft reset does not touch the index file nor the working tree * at all, but requires them in a good order. Other resets reset * the index file to the tree object we are switching to. */ @@ -338,10 +354,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix) int newfd = hold_locked_index(lock, 1); if (reset_type == MIXED) { int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; - if (read_from_tree(&pathspec, sha1)) + if (read_from_tree(&pathspec, sha1, intent_to_add)) return 1; - refresh_index(&the_index, flags, NULL, NULL, - _("Unstaged changes after reset:")); + if (get_git_work_tree()) + refresh_index(&the_index, flags, NULL, NULL, + _("Unstaged changes after reset:")); } else { int err = reset_index(sha1, reset_type, quiet); if (reset_type == KEEP && !err) diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 0745e2d053..9f92905379 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -3,6 +3,8 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "pack.h" +#include "pack-bitmap.h" #include "builtin.h" #include "log-tree.h" #include "graph.h" @@ -257,6 +259,18 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all) return 0; } +static int show_object_fast( + const unsigned char *sha1, + enum object_type type, + int exclude, + uint32_t name_hash, + struct packed_git *found_pack, + off_t found_offset) +{ + fprintf(stdout, "%s\n", sha1_to_hex(sha1)); + return 1; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -265,6 +279,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int bisect_list = 0; int bisect_show_vars = 0; int bisect_find_all = 0; + int use_bitmap_index = 0; git_config(git_default_config, NULL); init_revisions(&revs, prefix); @@ -306,6 +321,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) bisect_show_vars = 1; continue; } + if (!strcmp(arg, "--use-bitmap-index")) { + use_bitmap_index = 1; + continue; + } + if (!strcmp(arg, "--test-bitmap")) { + test_bitmap_walk(&revs); + return 0; + } usage(rev_list_usage); } @@ -333,6 +356,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) revs.limited = 1; + if (use_bitmap_index) { + if (revs.count && !revs.left_right && !revs.cherry_mark) { + uint32_t commit_count; + if (!prepare_bitmap_walk(&revs)) { + count_bitmap_commit_list(&commit_count, NULL, NULL, NULL); + printf("%d\n", commit_count); + return 0; + } + } else if (revs.tag_objects && revs.tree_objects && revs.blob_objects) { + if (!prepare_bitmap_walk(&revs)) { + traverse_bitmap_commit_list(&show_object_fast); + return 0; + } + } + } + if (prepare_revision_walk(&revs)) die("revision walk setup failed"); if (revs.tree_objects) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index c4b768ffda..1a6122d3ae 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -9,6 +9,8 @@ #include "quote.h" #include "builtin.h" #include "parse-options.h" +#include "diff.h" +#include "revision.h" #define DO_REVS 1 #define DO_NOREV 2 @@ -30,6 +32,9 @@ static int abbrev_ref; static int abbrev_ref_strict; static int output_sq; +static int stuck_long; +static struct string_list *ref_excludes; + /* * Some arguments are relevant "revision" arguments, * others are about output format or other details. @@ -185,6 +190,8 @@ static int show_default(void) static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { + if (ref_excluded(ref_excludes, refname)) + return 0; show_rev(NORMAL, sha1, refname); return 0; } @@ -324,12 +331,15 @@ static int parseopt_dump(const struct option *o, const char *arg, int unset) struct strbuf *parsed = o->value; if (unset) strbuf_addf(parsed, " --no-%s", o->long_name); - else if (o->short_name) + else if (o->short_name && (o->long_name == NULL || !stuck_long)) strbuf_addf(parsed, " -%c", o->short_name); else strbuf_addf(parsed, " --%s", o->long_name); if (arg) { - strbuf_addch(parsed, ' '); + if (!stuck_long) + strbuf_addch(parsed, ' '); + else if (o->long_name) + strbuf_addch(parsed, '='); sq_quote_buf(parsed, arg); } return 0; @@ -355,6 +365,8 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option, N_("stop parsing after the " "first non-option argument")), + OPT_BOOL(0, "stuck-long", &stuck_long, + N_("output in stuck long form")), OPT_END(), }; @@ -383,9 +395,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) usage[unb++] = strbuf_detach(&sb, NULL); } - /* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */ + /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */ while (strbuf_getline(&sb, stdin, '\n') != EOF) { const char *s; + const char *end; struct option *o; if (!sb.len) @@ -407,6 +420,16 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) o->value = &parsed; o->flags = PARSE_OPT_NOARG; o->callback = &parseopt_dump; + + /* Possible argument name hint */ + end = s; + while (s > sb.buf && strchr("*=?!", s[-1]) == NULL) + --s; + if (s != sb.buf && s != end) + o->argh = xmemdupz(s, end - s); + if (s == sb.buf) + s = end; + while (s > sb.buf && strchr("*=?!", s[-1])) { switch (*--s) { case '=': @@ -520,7 +543,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } continue; } - if (!prefixcmp(arg, "-n")) { + if (starts_with(arg, "-n")) { if ((filter & DO_FLAGS) && (filter & DO_REVS)) show(arg); continue; @@ -535,15 +558,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--default")) { - def = argv[i+1]; - i++; + def = argv[++i]; + if (!def) + die("--default requires an argument"); continue; } if (!strcmp(arg, "--prefix")) { - prefix = argv[i+1]; + prefix = argv[++i]; + if (!prefix) + die("--prefix requires an argument"); startup_info->prefix = prefix; output_prefix = 1; - i++; continue; } if (!strcmp(arg, "--revs-only")) { @@ -572,7 +597,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--short") || - !prefixcmp(arg, "--short=")) { + starts_with(arg, "--short=")) { filter &= ~(DO_FLAGS|DO_NOREV); verify = 1; abbrev = DEFAULT_ABBREV; @@ -600,7 +625,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) symbolic = SHOW_SYMBOLIC_FULL; continue; } - if (!prefixcmp(arg, "--abbrev-ref") && + if (starts_with(arg, "--abbrev-ref") && (!arg[12] || arg[12] == '=')) { abbrev_ref = 1; abbrev_ref_strict = warn_ambiguous_refs; @@ -618,7 +643,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for_each_ref(show_reference, NULL); continue; } - if (!prefixcmp(arg, "--disambiguate=")) { + if (starts_with(arg, "--disambiguate=")) { for_each_abbrev(arg + 15, show_abbrev, NULL); continue; } @@ -627,35 +652,46 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for_each_ref_in("refs/bisect/good", anti_reference, NULL); continue; } - if (!prefixcmp(arg, "--branches=")) { + if (starts_with(arg, "--branches=")) { for_each_glob_ref_in(show_reference, arg + 11, "refs/heads/", NULL); + clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--branches")) { for_each_branch_ref(show_reference, NULL); + clear_ref_exclusion(&ref_excludes); continue; } - if (!prefixcmp(arg, "--tags=")) { + if (starts_with(arg, "--tags=")) { for_each_glob_ref_in(show_reference, arg + 7, "refs/tags/", NULL); + clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--tags")) { for_each_tag_ref(show_reference, NULL); + clear_ref_exclusion(&ref_excludes); continue; } - if (!prefixcmp(arg, "--glob=")) { + if (starts_with(arg, "--glob=")) { for_each_glob_ref(show_reference, arg + 7, NULL); + clear_ref_exclusion(&ref_excludes); continue; } - if (!prefixcmp(arg, "--remotes=")) { + if (starts_with(arg, "--remotes=")) { for_each_glob_ref_in(show_reference, arg + 10, "refs/remotes/", NULL); + clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--remotes")) { for_each_remote_ref(show_reference, NULL); + clear_ref_exclusion(&ref_excludes); + continue; + } + if (starts_with(arg, "--exclude=")) { + add_ref_exclusion(&ref_excludes, arg + 10); continue; } if (!strcmp(arg, "--local-env-vars")) { @@ -715,9 +751,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--resolve-git-dir")) { - const char *gitdir = resolve_gitdir(argv[i+1]); + const char *gitdir = argv[++i]; + if (!gitdir) + die("--resolve-git-dir requires an argument"); + gitdir = resolve_gitdir(gitdir); if (!gitdir) - die("not a gitdir '%s'", argv[i+1]); + die("not a gitdir '%s'", argv[i]); puts(gitdir); continue; } @@ -736,19 +775,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) : "false"); continue; } - if (!prefixcmp(arg, "--since=")) { + if (starts_with(arg, "--since=")) { show_datestring("--max-age=", arg+8); continue; } - if (!prefixcmp(arg, "--after=")) { + if (starts_with(arg, "--after=")) { show_datestring("--max-age=", arg+8); continue; } - if (!prefixcmp(arg, "--before=")) { + if (starts_with(arg, "--before=")) { show_datestring("--min-age=", arg+9); continue; } - if (!prefixcmp(arg, "--until=")) { + if (starts_with(arg, "--until=")) { show_datestring("--min-age=", arg+8); continue; } diff --git a/builtin/revert.c b/builtin/revert.c index 87659c9fdb..065d88dd05 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -89,6 +89,8 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")), OPT_CALLBACK('X', "strategy-option", &opts, N_("option"), N_("option for merge strategy"), option_parse_x), + { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key id"), + N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_END(), OPT_END(), OPT_END(), diff --git a/builtin/rm.c b/builtin/rm.c index 3a0e0eaab7..960634dd0c 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -308,10 +308,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix) for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; - if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen)) + if (!ce_path_match(ce, &pathspec, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); - list.entry[list.nr].name = ce->name; + list.entry[list.nr].name = xstrdup(ce->name); list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); if (list.entry[list.nr++].is_submodule && !is_staging_gitmodules_ok()) diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 4482f16efb..f420b74665 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -10,6 +10,7 @@ #include "quote.h" #include "transport.h" #include "version.h" +#include "sha1-array.h" static const char send_pack_usage[] = "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n" @@ -99,7 +100,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) const char *dest = NULL; int fd[2]; struct child_process *conn; - struct extra_have_objects extra_have; + struct sha1_array extra_have = SHA1_ARRAY_INIT; + struct sha1_array shallow = SHA1_ARRAY_INIT; struct ref *remote_refs, *local_refs; int ret; int helper_status = 0; @@ -115,15 +117,15 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) const char *arg = *argv; if (*arg == '-') { - if (!prefixcmp(arg, "--receive-pack=")) { + if (starts_with(arg, "--receive-pack=")) { receivepack = arg + 15; continue; } - if (!prefixcmp(arg, "--exec=")) { + if (starts_with(arg, "--exec=")) { receivepack = arg + 7; continue; } - if (!prefixcmp(arg, "--remote=")) { + if (starts_with(arg, "--remote=")) { remote_name = arg + 9; continue; } @@ -181,7 +183,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) exit(1); continue; } - if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) { + if (starts_with(arg, "--" CAS_OPT_NAME "=")) { if (parse_push_cas_option(&cas, strchr(arg, '=') + 1, 0) < 0) exit(1); @@ -228,9 +230,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.verbose ? CONNECT_VERBOSE : 0); } - memset(&extra_have, 0, sizeof(extra_have)); - - get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, &extra_have); + get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, + &extra_have, &shallow); transport_verify_remote_names(nr_refspecs, refspecs); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index c226f767aa..4b7e53623f 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -65,7 +65,7 @@ static void insert_one_record(struct shortlog *log, eol = strchr(oneline, '\n'); if (!eol) eol = oneline + strlen(oneline); - if (!prefixcmp(oneline, "[PATCH")) { + if (starts_with(oneline, "[PATCH")) { char *eob = strchr(oneline, ']'); if (eob && (!eol || eob < eol)) oneline = eob + 1; @@ -95,7 +95,7 @@ static void read_from_stdin(struct shortlog *log) while (fgets(author, sizeof(author), stdin) != NULL) { if (!(author[0] == 'A' || author[0] == 'a') || - prefixcmp(author + 1, "uthor: ")) + !starts_with(author + 1, "uthor: ")) continue; while (fgets(oneline, sizeof(oneline), stdin) && oneline[0] != '\n') @@ -123,7 +123,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) else eol++; - if (!prefixcmp(buffer, "author ")) + if (starts_with(buffer, "author ")) author = buffer + 7; buffer = eol; } diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 001f29ca1b..d87317290c 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -227,8 +227,7 @@ static void join_revs(struct commit_list **list_p, parents = parents->next; if ((this_flag & flags) == flags) continue; - if (!p->object.parsed) - parse_commit(p); + parse_commit(p); if (mark_seen(p, seen_p) && !still_interesting) extra--; p->object.flags |= flags; @@ -285,7 +284,7 @@ static void show_one_commit(struct commit *commit, int no_name) pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); pretty_str = pretty.buf; } - if (!prefixcmp(pretty_str, "[PATCH] ")) + if (starts_with(pretty_str, "[PATCH] ")) pretty_str += 8; if (!no_name) { @@ -396,7 +395,7 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, int f { unsigned char tmp[20]; int ofs = 11; - if (prefixcmp(refname, "refs/heads/")) + if (!starts_with(refname, "refs/heads/")) return 0; /* If both heads/foo and tags/foo exists, get_sha1 would * get confused. @@ -410,7 +409,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int { unsigned char tmp[20]; int ofs = 13; - if (prefixcmp(refname, "refs/remotes/")) + if (!starts_with(refname, "refs/remotes/")) return 0; /* If both heads/foo and tags/foo exists, get_sha1 would * get confused. @@ -422,7 +421,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { - if (prefixcmp(refname, "refs/tags/")) + if (!starts_with(refname, "refs/tags/")) return 0; return append_ref(refname + 5, sha1, 0); } @@ -451,11 +450,11 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i slash--; if (!*tail) return 0; - if (fnmatch(match_ref_pattern, tail, 0)) + if (wildmatch(match_ref_pattern, tail, 0, NULL)) return 0; - if (!prefixcmp(refname, "refs/heads/")) + if (starts_with(refname, "refs/heads/")) return append_head_ref(refname, sha1, flag, cb_data); - if (!prefixcmp(refname, "refs/tags/")) + if (starts_with(refname, "refs/tags/")) return append_tag_ref(refname, sha1, flag, cb_data); return append_ref(refname, sha1, 0); } @@ -480,11 +479,11 @@ static int rev_is_head(char *head, int headlen, char *name, if ((!head[0]) || (head_sha1 && sha1 && hashcmp(head_sha1, sha1))) return 0; - if (!prefixcmp(head, "refs/heads/")) + if (starts_with(head, "refs/heads/")) head += 11; - if (!prefixcmp(name, "refs/heads/")) + if (starts_with(name, "refs/heads/")) name += 11; - else if (!prefixcmp(name, "heads/")) + else if (starts_with(name, "heads/")) name += 6; return !strcmp(head, name); } @@ -813,7 +812,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) has_head++; } if (!has_head) { - int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0; + int offset = starts_with(head, "refs/heads/") ? 11 : 0; append_one_rev(head + offset); } } diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 9f3f5e370b..5ba1f30838 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -37,8 +37,8 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo if (tags_only || heads_only) { int match; - match = heads_only && !prefixcmp(refname, "refs/heads/"); - match |= tags_only && !prefixcmp(refname, "refs/tags/"); + match = heads_only && starts_with(refname, "refs/heads/"); + match |= tags_only && starts_with(refname, "refs/tags/"); if (!match) return 0; } @@ -210,7 +210,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) while (*pattern) { unsigned char sha1[20]; - if (!prefixcmp(*pattern, "refs/") && + if (starts_with(*pattern, "refs/") && !read_ref(*pattern, sha1)) { if (!quiet) show_one(*pattern, sha1); diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 71286b4fae..b6a711d319 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -65,7 +65,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) break; case 2: if (!strcmp(argv[0], "HEAD") && - prefixcmp(argv[1], "refs/")) + !starts_with(argv[1], "refs/")) die("Refusing to point HEAD outside of refs/"); create_symref(argv[0], argv[1], msg); break; diff --git a/builtin/tag.c b/builtin/tag.c index ea55f1d1bd..40356e3e41 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -27,9 +27,16 @@ static const char * const git_tag_usage[] = { NULL }; +#define STRCMP_SORT 0 /* must be zero */ +#define VERCMP_SORT 1 +#define SORT_MASK 0x7fff +#define REVERSE_SORT 0x8000 + struct tag_filter { const char **patterns; int lines; + int sort; + struct string_list tags; struct commit_list *with_commit; }; @@ -42,7 +49,7 @@ static int match_pattern(const char **patterns, const char *ref) if (!*patterns) return 1; for (; *patterns; patterns++) - if (!fnmatch(*patterns, ref, 0)) + if (!wildmatch(*patterns, ref, 0, NULL)) return 1; return 0; } @@ -166,7 +173,10 @@ static int show_reference(const char *refname, const unsigned char *sha1, return 0; if (!filter->lines) { - printf("%s\n", refname); + if (filter->sort) + string_list_append(&filter->tags, refname); + else + printf("%s\n", refname); return 0; } printf("%-15s ", refname); @@ -177,17 +187,39 @@ static int show_reference(const char *refname, const unsigned char *sha1, return 0; } +static int sort_by_version(const void *a_, const void *b_) +{ + const struct string_list_item *a = a_; + const struct string_list_item *b = b_; + return versioncmp(a->string, b->string); +} + static int list_tags(const char **patterns, int lines, - struct commit_list *with_commit) + struct commit_list *with_commit, int sort) { struct tag_filter filter; filter.patterns = patterns; filter.lines = lines; + filter.sort = sort; filter.with_commit = with_commit; + memset(&filter.tags, 0, sizeof(filter.tags)); + filter.tags.strdup_strings = 1; for_each_tag_ref(show_reference, (void *) &filter); - + if (sort) { + int i; + if ((sort & SORT_MASK) == VERCMP_SORT) + qsort(filter.tags.items, filter.tags.nr, + sizeof(struct string_list_item), sort_by_version); + if (sort & REVERSE_SORT) + for (i = filter.tags.nr - 1; i >= 0; i--) + printf("%s\n", filter.tags.items[i].string); + else + for (i = 0; i < filter.tags.nr; i++) + printf("%s\n", filter.tags.items[i].string); + string_list_clear(&filter.tags, 0); + } return 0; } @@ -259,7 +291,7 @@ static int git_tag_config(const char *var, const char *value, void *cb) int status = git_gpg_config(var, value, cb); if (status) return status; - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "tag", &colopts); return git_default_config(var, value, cb); } @@ -427,6 +459,29 @@ static int parse_opt_points_at(const struct option *opt __attribute__((unused)), return 0; } +static int parse_opt_sort(const struct option *opt, const char *arg, int unset) +{ + int *sort = opt->value; + int flags = 0; + + if (*arg == '-') { + flags |= REVERSE_SORT; + arg++; + } + if (starts_with(arg, "version:")) { + *sort = VERCMP_SORT; + arg += 8; + } else if (starts_with(arg, "v:")) { + *sort = VERCMP_SORT; + arg += 2; + } else + *sort = STRCMP_SORT; + if (strcmp(arg, "refname")) + die(_("unsupported sort specification %s"), arg); + *sort |= flags; + return 0; +} + int cmd_tag(int argc, const char **argv, const char *prefix) { struct strbuf buf = STRBUF_INIT; @@ -437,7 +492,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct create_tag_options opt; char *cleanup_arg = NULL; int annotate = 0, force = 0, lines = -1; - int cmdmode = 0; + int cmdmode = 0, sort = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; struct commit_list *with_commit = NULL; @@ -462,6 +517,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix) N_("use another key to sign the tag")), OPT__FORCE(&force, N_("replace the tag if exists")), OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), + { + OPTION_CALLBACK, 0, "sort", &sort, N_("type"), N_("sort tags"), + PARSE_OPT_NONEG, parse_opt_sort + }, OPT_GROUP(N_("Tag listing options")), { @@ -471,6 +530,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix) parse_opt_with_commit, (intptr_t)"HEAD", }, { + OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"), + N_("print only tags that contain the commit"), + PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT, + parse_opt_with_commit, (intptr_t)"HEAD", + }, + { OPTION_CALLBACK, 0, "points-at", NULL, N_("object"), N_("print only tags of the object"), 0, parse_opt_points_at }, @@ -509,7 +574,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix) copts.padding = 2; run_column_filter(colopts, &copts); } - ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit); + if (lines != -1 && sort) + die(_("--sort and -n are incompatible")); + ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, sort); if (column_active(colopts)) stop_column_filter(); return ret; diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c deleted file mode 100644 index ba3ffe69a9..0000000000 --- a/builtin/tar-tree.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2005, 2006 Rene Scharfe - */ -#include "cache.h" -#include "commit.h" -#include "tar.h" -#include "builtin.h" -#include "quote.h" - -static const char tar_tree_usage[] = -"git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n" -"*** Note that this command is now deprecated; use \"git archive\" instead."; - -static const char builtin_get_tar_commit_id_usage[] = -"git get-tar-commit-id < <tarfile>"; - -int cmd_tar_tree(int argc, const char **argv, const char *prefix) -{ - /* - * "git tar-tree" is now a wrapper around "git archive --format=tar" - * - * $0 --remote=<repo> arg... ==> - * git archive --format=tar --remote=<repo> arg... - * $0 tree-ish ==> - * git archive --format=tar tree-ish - * $0 tree-ish basedir ==> - * git archive --format-tar --prefix=basedir tree-ish - */ - const char **nargv = xcalloc(sizeof(*nargv), argc + 3); - struct strbuf sb = STRBUF_INIT; - char *basedir_arg; - int nargc = 0; - - nargv[nargc++] = "archive"; - nargv[nargc++] = "--format=tar"; - - if (2 <= argc && !prefixcmp(argv[1], "--remote=")) { - nargv[nargc++] = argv[1]; - argv++; - argc--; - } - - /* - * Because it's just a compatibility wrapper, tar-tree supports only - * the old behaviour of reading attributes from the work tree. - */ - nargv[nargc++] = "--worktree-attributes"; - - switch (argc) { - default: - usage(tar_tree_usage); - break; - case 3: - /* base-path */ - basedir_arg = xmalloc(strlen(argv[2]) + 11); - sprintf(basedir_arg, "--prefix=%s/", argv[2]); - nargv[nargc++] = basedir_arg; - /* fallthru */ - case 2: - /* tree-ish */ - nargv[nargc++] = argv[1]; - } - nargv[nargc] = NULL; - - fprintf(stderr, - "*** \"git tar-tree\" is now deprecated.\n" - "*** Running \"git archive\" instead.\n***"); - sq_quote_argv(&sb, nargv, 0); - strbuf_addch(&sb, '\n'); - fputs(sb.buf, stderr); - strbuf_release(&sb); - return cmd_archive(nargc, nargv, prefix); -} - -/* ustar header + extended global header content */ -#define RECORDSIZE (512) -#define HEADERSIZE (2 * RECORDSIZE) - -int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) -{ - char buffer[HEADERSIZE]; - struct ustar_header *header = (struct ustar_header *)buffer; - char *content = buffer + RECORDSIZE; - ssize_t n; - - if (argc != 1) - usage(builtin_get_tar_commit_id_usage); - - n = read_in_full(0, buffer, HEADERSIZE); - if (n < HEADERSIZE) - die("git get-tar-commit-id: read error"); - if (header->typeflag[0] != 'g') - return 1; - if (memcmp(content, "52 comment=", 11)) - return 1; - - n = write_in_full(1, content + 11, 41); - if (n < 41) - die_errno("git get-tar-commit-id: write error"); - - return 0; -} diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 2217d7b3ae..99cde45879 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -480,7 +480,7 @@ static void unpack_all(void) use(sizeof(struct pack_header)); if (!quiet) - progress = start_progress("Unpacking objects", nr_objects); + progress = start_progress(_("Unpacking objects"), nr_objects); obj_list = xcalloc(nr_objects, sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); @@ -497,7 +497,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) int i; unsigned char sha1[20]; - read_replace_refs = 0; + check_replace_refs = 0; git_config(git_default_config, NULL); @@ -523,7 +523,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) strict = 1; continue; } - if (!prefixcmp(arg, "--pack_header=")) { + if (starts_with(arg, "--pack_header=")) { struct pack_header *hdr; char *c; diff --git a/builtin/update-index.c b/builtin/update-index.c index e3a10d706d..d12ad95f3e 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -12,6 +12,7 @@ #include "resolve-undo.h" #include "parse-options.h" #include "pathspec.h" +#include "dir.h" /* * Default to not allowing changes to the list of files. The @@ -274,36 +275,32 @@ static void chmod_path(int flip, const char *path) die("git update-index: cannot chmod %cx '%s'", flip, path); } -static void update_one(const char *path, const char *prefix, int prefix_length) +static void update_one(const char *path) { - const char *p = prefix_path(prefix, prefix_length, path); - if (!verify_path(p)) { + if (!verify_path(path)) { fprintf(stderr, "Ignoring path %s\n", path); - goto free_return; + return; } if (mark_valid_only) { - if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG)) + if (mark_ce_flags(path, CE_VALID, mark_valid_only == MARK_FLAG)) die("Unable to mark file %s", path); - goto free_return; + return; } if (mark_skip_worktree_only) { - if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG)) + if (mark_ce_flags(path, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG)) die("Unable to mark file %s", path); - goto free_return; + return; } if (force_remove) { - if (remove_file_from_cache(p)) + if (remove_file_from_cache(path)) die("git update-index: unable to remove %s", path); report("remove '%s'", path); - goto free_return; + return; } - if (process_path(p)) + if (process_path(path)) die("Unable to process path %s", path); report("add '%s'", path); - free_return: - if (p < path || p > path + strlen(path)) - free((char *)p); } static void read_index_info(int line_termination) @@ -563,8 +560,9 @@ static int do_reupdate(int ac, const char **av, const struct cache_entry *ce = active_cache[pos]; struct cache_entry *old = NULL; int save_nr; + char *path; - if (ce_stage(ce) || !ce_path_match(ce, &pathspec)) + if (ce_stage(ce) || !ce_path_match(ce, &pathspec, NULL)) continue; if (has_head) old = read_one_ent(NULL, head_sha1, @@ -579,7 +577,9 @@ static int do_reupdate(int ac, const char **av, * or worse yet 'allow_replace', active_nr may decrease. */ save_nr = active_nr; - update_one(ce->name + prefix_length, prefix, prefix_length); + path = xstrdup(ce->name); + update_one(path); + free(path); if (save_nr != active_nr) goto redo; } @@ -836,11 +836,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) setup_work_tree(); p = prefix_path(prefix, prefix_length, path); - update_one(p, NULL, 0); + update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); - if (p < path || p > path + strlen(path)) - free((char *)p); + free((char *)p); ctx.argc--; ctx.argv++; break; @@ -879,11 +878,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) strbuf_swap(&buf, &nbuf); } p = prefix_path(prefix, prefix_length, buf.buf); - update_one(p, NULL, 0); + update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); - if (p < buf.buf || p > buf.buf + buf.len) - free((char *)p); + free((char *)p); } strbuf_release(&nbuf); strbuf_release(&buf); diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 702e90db2a..1292cfea11 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -229,15 +229,15 @@ static void update_refs_stdin(void) die("empty command in input"); else if (isspace(*cmd.buf)) die("whitespace before command: %s", cmd.buf); - else if (!prefixcmp(cmd.buf, "update ")) + else if (starts_with(cmd.buf, "update ")) parse_cmd_update(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "create ")) + else if (starts_with(cmd.buf, "create ")) parse_cmd_create(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "delete ")) + else if (starts_with(cmd.buf, "delete ")) parse_cmd_delete(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "verify ")) + else if (starts_with(cmd.buf, "verify ")) parse_cmd_verify(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "option ")) + else if (starts_with(cmd.buf, "option ")) parse_cmd_option(cmd.buf + 7); else die("unknown command: %s", cmd.buf); diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c index af2da35e7d..32ab94cd06 100644 --- a/builtin/upload-archive.c +++ b/builtin/upload-archive.c @@ -37,7 +37,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix) if (sent_argv.argc > MAX_ARGS) die("Too many options (>%d)", MAX_ARGS - 1); - if (prefixcmp(buf, arg_cmd)) + if (!starts_with(buf, arg_cmd)) die("'argument' token or flush expected"); argv_array_push(&sent_argv, buf + strlen(arg_cmd)); } |