diff options
Diffstat (limited to 'builtin')
70 files changed, 1050 insertions, 696 deletions
diff --git a/builtin/add.c b/builtin/add.c index 1074e32349..3390933d68 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -19,7 +19,7 @@ #include "argv-array.h" static const char * const builtin_add_usage[] = { - N_("git add [options] [--] <pathspec>..."), + N_("git add [<options>] [--] <pathspec>..."), NULL }; static int patch_interactive, add_interactive, edit_interactive; diff --git a/builtin/apply.c b/builtin/apply.c index 6696ea4c3f..65b97eee69 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -51,11 +51,12 @@ static int apply_verbosely; static int allow_overlap; static int no_add; static int threeway; +static int unsafe_paths; static const char *fake_ancestor; static int line_termination = '\n'; static unsigned int p_context = UINT_MAX; static const char * const apply_usage[] = { - N_("git apply [options] [<patch>...]"), + N_("git apply [<options>] [<patch>...]"), NULL }; @@ -657,11 +658,6 @@ static size_t diff_timestamp_len(const char *line, size_t len) return line + len - end; } -static char *null_strdup(const char *s) -{ - return s ? xstrdup(s) : NULL; -} - static char *find_name_common(const char *line, const char *def, int p_value, const char *end, int terminate) { @@ -684,10 +680,10 @@ static char *find_name_common(const char *line, const char *def, start = line; } if (!start) - return squash_slash(null_strdup(def)); + return squash_slash(xstrdup_or_null(def)); len = line - start; if (!len) - return squash_slash(null_strdup(def)); + return squash_slash(xstrdup_or_null(def)); /* * Generally we prefer the shorter name, especially @@ -909,7 +905,7 @@ static void parse_traditional_patch(const char *first, const char *second, struc patch->old_name = name; } else { patch->old_name = name; - patch->new_name = null_strdup(name); + patch->new_name = xstrdup_or_null(name); } } if (!name) @@ -998,7 +994,7 @@ static int gitdiff_delete(const char *line, struct patch *patch) { patch->is_delete = 1; free(patch->old_name); - patch->old_name = null_strdup(patch->def_name); + patch->old_name = xstrdup_or_null(patch->def_name); return gitdiff_oldmode(line, patch); } @@ -1006,7 +1002,7 @@ static int gitdiff_newfile(const char *line, struct patch *patch) { patch->is_new = 1; free(patch->new_name); - patch->new_name = null_strdup(patch->def_name); + patch->new_name = xstrdup_or_null(patch->def_name); return gitdiff_newmode(line, patch); } @@ -1605,6 +1601,9 @@ static int parse_fragment(const char *line, unsigned long size, if (!deleted && !added) leading++; trailing++; + if (!apply_in_reverse && + ws_error_action == correct_ws_error) + check_whitespace(line, len, patch->ws_rule); break; case '-': if (apply_in_reverse && @@ -2235,6 +2234,12 @@ static void update_pre_post_images(struct image *preimage, ctx++; } + if (postlen + ? postlen < new - postimage->buf + : postimage->len < new - postimage->buf) + die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d", + (int)postlen, (int) postimage->len, (int)(new - postimage->buf)); + /* Fix the length of the whole thing */ postimage->len = new - postimage->buf; postimage->nr -= reduced; @@ -2390,10 +2395,27 @@ static int match_fragment(struct image *img, /* * The hunk does not apply byte-by-byte, but the hash says - * it might with whitespace fuzz. We haven't been asked to + * it might with whitespace fuzz. We weren't asked to * ignore whitespace, we were asked to correct whitespace * errors, so let's try matching after whitespace correction. * + * While checking the preimage against the target, whitespace + * errors in both fixed, we count how large the corresponding + * postimage needs to be. The postimage prepared by + * apply_one_fragment() has whitespace errors fixed on added + * lines already, but the common lines were propagated as-is, + * which may become longer when their whitespace errors are + * fixed. + */ + + /* First count added lines in postimage */ + postlen = 0; + for (i = 0; i < postimage->nr; i++) { + if (!(postimage->line[i].flag & LINE_COMMON)) + postlen += postimage->line[i].len; + } + + /* * The preimage may extend beyond the end of the file, * but in this loop we will only handle the part of the * preimage that falls within the file. @@ -2401,7 +2423,6 @@ static int match_fragment(struct image *img, strbuf_init(&fixed, preimage->len + 1); orig = preimage->buf; target = img->buf + try; - postlen = 0; for (i = 0; i < preimage_limit; i++) { size_t oldlen = preimage->line[i].len; size_t tgtlen = img->line[try_lno + i].len; @@ -2429,7 +2450,10 @@ static int match_fragment(struct image *img, match = (tgtfix.len == fixed.len - fixstart && !memcmp(tgtfix.buf, fixed.buf + fixstart, fixed.len - fixstart)); - postlen += tgtfix.len; + + /* Add the length if this is common with the postimage */ + if (preimage->line[i].flag & LINE_COMMON) + postlen += tgtfix.len; strbuf_release(&tgtfix); if (!match) @@ -3201,7 +3225,7 @@ static int load_patch_target(struct strbuf *buf, const char *name, unsigned expected_mode) { - if (cached) { + if (cached || check_index) { if (read_file_or_gitlink(ce, buf)) return error(_("read of %s failed"), name); } else if (name) { @@ -3210,6 +3234,8 @@ static int load_patch_target(struct strbuf *buf, return read_file_or_gitlink(ce, buf); else return SUBMODULE_PATCH_WITHOUT_INDEX; + } else if (has_symlink_leading_path(name, strlen(name))) { + return error(_("reading from '%s' beyond a symbolic link"), name); } else { if (read_old_data(st, name, buf)) return error(_("read of %s failed"), name); @@ -3550,6 +3576,121 @@ static int check_to_create(const char *new_name, int ok_if_exists) } /* + * We need to keep track of how symlinks in the preimage are + * manipulated by the patches. A patch to add a/b/c where a/b + * is a symlink should not be allowed to affect the directory + * the symlink points at, but if the same patch removes a/b, + * it is perfectly fine, as the patch removes a/b to make room + * to create a directory a/b so that a/b/c can be created. + */ +static struct string_list symlink_changes; +#define SYMLINK_GOES_AWAY 01 +#define SYMLINK_IN_RESULT 02 + +static uintptr_t register_symlink_changes(const char *path, uintptr_t what) +{ + struct string_list_item *ent; + + ent = string_list_lookup(&symlink_changes, path); + if (!ent) { + ent = string_list_insert(&symlink_changes, path); + ent->util = (void *)0; + } + ent->util = (void *)(what | ((uintptr_t)ent->util)); + return (uintptr_t)ent->util; +} + +static uintptr_t check_symlink_changes(const char *path) +{ + struct string_list_item *ent; + + ent = string_list_lookup(&symlink_changes, path); + if (!ent) + return 0; + return (uintptr_t)ent->util; +} + +static void prepare_symlink_changes(struct patch *patch) +{ + for ( ; patch; patch = patch->next) { + if ((patch->old_name && S_ISLNK(patch->old_mode)) && + (patch->is_rename || patch->is_delete)) + /* the symlink at patch->old_name is removed */ + register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY); + + if (patch->new_name && S_ISLNK(patch->new_mode)) + /* the symlink at patch->new_name is created or remains */ + register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT); + } +} + +static int path_is_beyond_symlink_1(struct strbuf *name) +{ + do { + unsigned int change; + + while (--name->len && name->buf[name->len] != '/') + ; /* scan backwards */ + if (!name->len) + break; + name->buf[name->len] = '\0'; + change = check_symlink_changes(name->buf); + if (change & SYMLINK_IN_RESULT) + return 1; + if (change & SYMLINK_GOES_AWAY) + /* + * This cannot be "return 0", because we may + * see a new one created at a higher level. + */ + continue; + + /* otherwise, check the preimage */ + if (check_index) { + struct cache_entry *ce; + + ce = cache_file_exists(name->buf, name->len, ignore_case); + if (ce && S_ISLNK(ce->ce_mode)) + return 1; + } else { + struct stat st; + if (!lstat(name->buf, &st) && S_ISLNK(st.st_mode)) + return 1; + } + } while (1); + return 0; +} + +static int path_is_beyond_symlink(const char *name_) +{ + int ret; + struct strbuf name = STRBUF_INIT; + + assert(*name_ != '\0'); + strbuf_addstr(&name, name_); + ret = path_is_beyond_symlink_1(&name); + strbuf_release(&name); + + return ret; +} + +static void die_on_unsafe_path(struct patch *patch) +{ + const char *old_name = NULL; + const char *new_name = NULL; + if (patch->is_delete) + old_name = patch->old_name; + else if (!patch->is_new && !patch->is_copy) + old_name = patch->old_name; + if (!patch->is_delete) + new_name = patch->new_name; + + if (old_name && !verify_path(old_name)) + die(_("invalid path '%s'"), old_name); + if (new_name && !verify_path(new_name)) + die(_("invalid path '%s'"), new_name); +} + +/* * Check and apply the patch in-core; leave the result in patch->result * for the caller to write it out to the final destination. */ @@ -3636,6 +3777,22 @@ static int check_patch(struct patch *patch) } } + if (!unsafe_paths) + die_on_unsafe_path(patch); + + /* + * An attempt to read from or delete a path that is beyond a + * symbolic link will be prevented by load_patch_target() that + * is called at the beginning of apply_data() so we do not + * have to worry about a patch marked with "is_delete" bit + * here. We however need to make sure that the patch result + * is not deposited to a path that is beyond a symbolic link + * here. + */ + if (!patch->is_delete && path_is_beyond_symlink(patch->new_name)) + return error(_("affected file '%s' is beyond a symbolic link"), + patch->new_name); + if (apply_data(patch, &st, ce) < 0) return error(_("%s: patch does not apply"), name); patch->rejected = 0; @@ -3646,6 +3803,7 @@ static int check_patch_list(struct patch *patch) { int err = 0; + prepare_symlink_changes(patch); prepare_fn_table(patch); while (patch) { if (apply_verbosely) @@ -3728,7 +3886,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename) if (!preimage_sha1_in_gitlink_patch(patch, sha1)) ; /* ok, the textual part looks sane */ else - die("sha1 information is lacking or useless for submoule %s", + die("sha1 information is lacking or useless for submodule %s", name); } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) { ; /* ok */ @@ -4180,7 +4338,7 @@ static int write_out_results(struct patch *list) if (cpath.nr) { struct string_list_item *item; - sort_string_list(&cpath); + string_list_sort(&cpath); for_each_string_list_item(item, &cpath) fprintf(stderr, "U %s\n", item->string); string_list_clear(&cpath, 0); @@ -4384,6 +4542,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) N_("make sure the patch is applicable to the current index")), OPT_BOOL(0, "cached", &cached, N_("apply a patch without touching the working tree")), + OPT_BOOL(0, "unsafe-paths", &unsafe_paths, + N_("accept a patch that touches outside the working area")), OPT_BOOL(0, "apply", &force_apply, N_("also apply the patch (use with --stat/--summary/--check)")), OPT_BOOL('3', "3way", &threeway, @@ -4456,6 +4616,9 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) die(_("--cached outside a repository")); check_index = 1; } + if (check_index) + unsafe_paths = 0; + for (i = 0; i < argc; i++) { const char *arg = argv[i]; int fd; diff --git a/builtin/blame.c b/builtin/blame.c index 303e217ae9..06484c2e0e 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -27,12 +27,12 @@ #include "line-range.h" #include "line-log.h" -static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file"); +static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] file"); static const char *blame_opt_usage[] = { blame_usage, "", - N_("[rev-opts] are documented in git-rev-list(1)"), + N_("<rev-opts> are documented in git-rev-list(1)"), NULL }; @@ -2085,7 +2085,6 @@ static void find_alignment(struct scoreboard *sb, int *option) for (e = sb->ent; e; e = e->next) { struct origin *suspect = e->suspect; - struct commit_info ci; int num; if (compute_auto_abbrev) @@ -2096,6 +2095,7 @@ static void find_alignment(struct scoreboard *sb, int *option) if (longest_file < num) longest_file = num; if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { + struct commit_info ci; suspect->commit->object.flags |= METAINFO_SHOWN; get_commit_info(suspect->commit, &ci, 1); if (*option & OUTPUT_SHOW_EMAIL) @@ -2104,6 +2104,7 @@ static void find_alignment(struct scoreboard *sb, int *option) num = utf8_strwidth(ci.author.buf); if (longest_author < num) longest_author = num; + commit_info_destroy(&ci); } num = e->s_lno + e->num_lines; if (longest_src_lines < num) @@ -2113,8 +2114,6 @@ static void find_alignment(struct scoreboard *sb, int *option) longest_dst_lines = num; if (largest_score < ent_score(sb, e)) largest_score = ent_score(sb, e); - - commit_info_destroy(&ci); } max_orig_digits = decimal_width(longest_src_lines); max_digits = decimal_width(longest_dst_lines); @@ -2390,7 +2389,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, return commit; } -static const char *prepare_final(struct scoreboard *sb) +static char *prepare_final(struct scoreboard *sb) { int i; const char *final_commit_name = NULL; @@ -2415,10 +2414,10 @@ static const char *prepare_final(struct scoreboard *sb) sb->final = (struct commit *) obj; final_commit_name = revs->pending.objects[i].name; } - return final_commit_name; + return xstrdup_or_null(final_commit_name); } -static const char *prepare_initial(struct scoreboard *sb) +static char *prepare_initial(struct scoreboard *sb) { int i; const char *final_commit_name = NULL; @@ -2445,7 +2444,7 @@ static const char *prepare_initial(struct scoreboard *sb) } if (!final_commit_name) die("No commit to dig down to?"); - return final_commit_name; + return xstrdup(final_commit_name); } static int blame_copy_callback(const struct option *option, const char *arg, int unset) @@ -2489,7 +2488,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) struct origin *o; struct blame_entry *ent = NULL; long dashdash_pos, lno; - const char *final_commit_name = NULL; + char *final_commit_name = NULL; enum object_type type; static struct string_list range_list; @@ -2786,6 +2785,8 @@ parse_done: assign_blame(&sb, opt); + free(final_commit_name); + if (incremental) return 0; diff --git a/builtin/branch.c b/builtin/branch.c index 3b79c5087f..1d150378e9 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -21,10 +21,10 @@ #include "wt-status.h" static const char * const builtin_branch_usage[] = { - N_("git branch [options] [-r | -a] [--merged | --no-merged]"), - N_("git branch [options] [-l] [-f] <branchname> [<start-point>]"), - N_("git branch [options] [-r] (-d | -D) <branchname>..."), - N_("git branch [options] (-m | -M) [<oldbranch>] <newbranch>"), + N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"), + N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"), + N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."), + N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"), NULL }; @@ -589,9 +589,16 @@ static char *get_head_description(void) else if (state.bisect_in_progress) strbuf_addf(&desc, _("(no branch, bisect started on %s)"), state.branch); - else if (state.detached_from) - strbuf_addf(&desc, _("(detached from %s)"), - state.detached_from); + else if (state.detached_from) { + /* TRANSLATORS: make sure these match _("HEAD detached at ") + and _("HEAD detached from ") in wt-status.c */ + if (state.detached_at) + strbuf_addf(&desc, _("(HEAD detached at %s)"), + state.detached_from); + else + strbuf_addf(&desc, _("(HEAD detached from %s)"), + state.detached_from); + } else strbuf_addstr(&desc, _("(no branch)")); free(state.branch); @@ -800,7 +807,7 @@ static int edit_branch_description(const char *branch_name) int cmd_branch(int argc, const char **argv, const char *prefix) { - int delete = 0, rename = 0, force_create = 0, list = 0; + int delete = 0, rename = 0, force = 0, list = 0; int verbose = 0, abbrev = -1, detached = 0; int reflog = 0, edit_description = 0; int quiet = 0, unset_upstream = 0; @@ -848,7 +855,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")), OPT_BOOL(0, "edit-description", &edit_description, N_("edit the description for the branch")), - OPT__FORCE(&force_create, N_("force creation (when already exists)")), + OPT__FORCE(&force, N_("force creation, move/rename, deletion")), { OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, N_("commit"), N_("print only not merged branches"), @@ -891,7 +898,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (with_commit || merge_filter != NO_FILTER) list = 1; - if (!!delete + !!rename + !!force_create + !!new_upstream + + if (!!delete + !!rename + !!new_upstream + list + unset_upstream > 1) usage_with_options(builtin_branch_usage, options); @@ -904,6 +911,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) colopts = 0; } + if (force) { + delete *= 2; + rename *= 2; + } + if (delete) { if (!argc) die(_("branch name required")); @@ -1020,7 +1032,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) branch_existed = ref_exists(branch->refname); create_branch(head, argv[0], (argc == 2) ? argv[1] : head, - force_create, reflog, 0, quiet, track); + force, reflog, 0, quiet, track); /* * We only show the instructions if the user gave us diff --git a/builtin/cat-file.c b/builtin/cat-file.c index f8d81291b9..df99df4db1 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -4,12 +4,8 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" -#include "exec_cmd.h" -#include "tag.h" -#include "tree.h" #include "builtin.h" #include "parse-options.h" -#include "diff.h" #include "userdiff.h" #include "streaming.h" @@ -79,8 +75,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) if (type_from_string(exp_type) == OBJ_BLOB) { unsigned char blob_sha1[20]; if (sha1_object_info(sha1, NULL) == OBJ_TAG) { - enum object_type type; - unsigned long size; char *buffer = read_sha1_file(sha1, &type, &size); const char *target; if (!skip_prefix(buffer, "object ", &target) || @@ -329,8 +323,8 @@ static int batch_objects(struct batch_options *opt) } static const char * const cat_file_usage[] = { - N_("git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"), - N_("git cat-file (--batch|--batch-check) < <list_of_objects>"), + N_("git cat-file (-t | -s | -e | -p | <type> | --textconv) <object>"), + N_("git cat-file (--batch | --batch-check) < <list-of-objects>"), NULL }; diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 5600ec3f61..21d2bedcc9 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -8,8 +8,8 @@ static int all_attrs; static int cached_attrs; static int stdin_paths; static const char * const check_attr_usage[] = { -N_("git check-attr [-a | --all | attr...] [--] pathname..."), -N_("git check-attr --stdin [-z] [-a | --all | attr...] < <list-of-paths>"), +N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."), +N_("git check-attr --stdin [-z] [-a | --all | <attr>...] < <list-of-paths>"), NULL }; diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 594463a11b..dc8d97c56c 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -7,8 +7,8 @@ static int quiet, verbose, stdin_paths, show_non_matching, no_index; static const char * const check_ignore_usage[] = { -"git check-ignore [options] pathname...", -"git check-ignore [options] --stdin < <list-of-paths>", +"git check-ignore [<options>] <pathname>...", +"git check-ignore [<options>] --stdin < <list-of-paths>", NULL }; diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index 8f4d809bd8..eaaea546d3 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -5,7 +5,7 @@ static int use_stdin; static const char * const check_mailmap_usage[] = { -N_("git check-mailmap [options] <contact>..."), +N_("git check-mailmap [<options>] <contact>..."), NULL }; diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c index 28a7320271..fd915d5984 100644 --- a/builtin/check-ref-format.c +++ b/builtin/check-ref-format.c @@ -8,7 +8,7 @@ #include "strbuf.h" static const char builtin_check_ref_format_usage[] = -"git check-ref-format [--normalize] [options] <refname>\n" +"git check-ref-format [--normalize] [<options>] <refname>\n" " or: git check-ref-format --branch <branchname-shorthand>"; /* diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 383dccf93e..9ca2da1583 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -18,7 +18,7 @@ static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; static struct checkout state; -static void write_tempfile_record(const char *name, int prefix_length) +static void write_tempfile_record(const char *name, const char *prefix) { int i; @@ -35,14 +35,14 @@ static void write_tempfile_record(const char *name, int prefix_length) fputs(topath[checkout_stage], stdout); putchar('\t'); - write_name_quoted(name + prefix_length, stdout, line_termination); + write_name_quoted_relative(name, prefix, stdout, line_termination); for (i = 0; i < 4; i++) { topath[i][0] = 0; } } -static int checkout_file(const char *name, int prefix_length) +static int checkout_file(const char *name, const char *prefix) { int namelen = strlen(name); int pos = cache_name_pos(name, namelen); @@ -71,7 +71,7 @@ static int checkout_file(const char *name, int prefix_length) if (did_checkout) { if (to_tempfile) - write_tempfile_record(name, prefix_length); + write_tempfile_record(name, prefix); return errs > 0 ? -1 : 0; } @@ -106,7 +106,7 @@ static void checkout_all(const char *prefix, int prefix_length) if (last_ce && to_tempfile) { if (ce_namelen(last_ce) != ce_namelen(ce) || memcmp(last_ce->name, ce->name, ce_namelen(ce))) - write_tempfile_record(last_ce->name, prefix_length); + write_tempfile_record(last_ce->name, prefix); } if (checkout_entry(ce, &state, to_tempfile ? topath[ce_stage(ce)] : NULL) < 0) @@ -114,7 +114,7 @@ static void checkout_all(const char *prefix, int prefix_length) last_ce = ce; } if (last_ce && to_tempfile) - write_tempfile_record(last_ce->name, prefix_length); + write_tempfile_record(last_ce->name, prefix); if (errs) /* we have already done our error reporting. * exit with the same code as die(). @@ -123,7 +123,7 @@ static void checkout_all(const char *prefix, int prefix_length) } static const char * const builtin_checkout_index_usage[] = { - N_("git checkout-index [options] [--] [<file>...]"), + N_("git checkout-index [<options>] [--] [<file>...]"), NULL }; @@ -248,7 +248,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) if (read_from_stdin) die("git checkout-index: don't mix '--stdin' and explicit filenames"); p = prefix_path(prefix, prefix_length, arg); - checkout_file(p, prefix_length); + checkout_file(p, prefix); if (p < arg || p > arg + strlen(arg)) free((char *)p); } @@ -268,7 +268,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) strbuf_swap(&buf, &nbuf); } p = prefix_path(prefix, prefix_length, buf.buf); - checkout_file(p, prefix_length); + checkout_file(p, prefix); if (p < buf.buf || p > buf.buf + buf.len) free((char *)p); } diff --git a/builtin/checkout.c b/builtin/checkout.c index 5a78758036..3e141fc149 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -22,8 +22,8 @@ #include "argv-array.h" static const char * const checkout_usage[] = { - N_("git checkout [options] <branch>"), - N_("git checkout [options] [<branch>] -- <file>..."), + N_("git checkout [<options>] <branch>"), + N_("git checkout [<options>] [<branch>] -- <file>..."), NULL, }; @@ -62,7 +62,7 @@ static int post_checkout_hook(struct commit *old, struct commit *new, } -static int update_some(const unsigned char *sha1, const char *base, int baselen, +static int update_some(const unsigned char *sha1, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { int len; @@ -72,11 +72,11 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, if (S_ISDIR(mode)) return READ_TREE_RECURSIVE; - len = baselen + strlen(pathname); + len = base->len + strlen(pathname); ce = xcalloc(1, cache_entry_size(len)); hashcpy(ce->sha1, sha1); - memcpy(ce->name, base, baselen); - memcpy(ce->name + baselen, pathname, len - baselen); + memcpy(ce->name, base->buf, base->len); + memcpy(ce->name + base->len, pathname, len - base->len); ce->ce_flags = create_ce_flags(0) | CE_UPDATE; ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); @@ -746,7 +746,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs) _( "If you want to keep them by creating a new branch, " "this may be a good time\nto do so with:\n\n" - " git branch new_branch_name %s\n\n"), + " git branch <new-branch-name> %s\n\n"), find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); } @@ -1127,7 +1127,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree, N_("do not limit pathspecs to sparse entries only")), OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch, - N_("second guess 'git checkout no-such-branch'")), + N_("second guess 'git checkout <no-such-branch>'")), OPT_END(), }; diff --git a/builtin/clean.c b/builtin/clean.c index 7e7fdcfe54..98c103fa8b 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -754,7 +754,8 @@ static int ask_each_cmd(void) /* Ctrl-D should stop removing files */ if (!eof) { qname = quote_path_relative(item->string, NULL, &buf); - printf(_("remove %s? "), qname); + /* TRANSLATORS: Make sure to keep [y/N] as is */ + printf(_("Remove %s [y/N]? "), qname); if (strbuf_getline(&confirm, stdin, '\n') != EOF) { strbuf_trim(&confirm); } else { diff --git a/builtin/clone.c b/builtin/clone.c index d5e7532105..53a2e5af35 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -34,7 +34,7 @@ * */ static const char * const builtin_clone_usage[] = { - N_("git clone [options] [--] <repo> [<dir>]"), + N_("git clone [<options>] [--] <repo> [<dir>]"), NULL }; @@ -49,6 +49,7 @@ static int option_verbosity; static int option_progress = -1; static struct string_list option_config; static struct string_list option_reference; +static int option_dissociate; static int opt_parse_reference(const struct option *opt, const char *arg, int unset) { @@ -94,6 +95,8 @@ static struct option builtin_clone_options[] = { N_("create a shallow clone of that depth")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), + OPT_BOOL(0, "dissociate", &option_dissociate, + N_("use --reference only while cloning")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), N_("separate git dir from working tree")), OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), @@ -735,6 +738,16 @@ static void write_refspec_config(const char *src_ref_prefix, strbuf_release(&value); } +static void dissociate_from_references(void) +{ + static const char* argv[] = { "repack", "-a", "-d", NULL }; + + if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN)) + die(_("cannot repack to clean up")); + if (unlink(git_path("objects/info/alternates")) && errno != ENOENT) + die_errno(_("cannot unlink temporary alternates file")); +} + int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0, is_local; @@ -829,20 +842,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix) git_dir = mkpathdup("%s/.git", dir); } + atexit(remove_junk); + sigchain_push_common(remove_junk_on_signal); + if (!option_bare) { - junk_work_tree = work_tree; if (safe_create_leading_directories_const(work_tree) < 0) die_errno(_("could not create leading directories of '%s'"), work_tree); if (!dest_exists && mkdir(work_tree, 0777)) - die_errno(_("could not create work tree dir '%s'."), + die_errno(_("could not create work tree dir '%s'"), work_tree); + junk_work_tree = work_tree; set_git_work_tree(work_tree); } - junk_git_dir = git_dir; - atexit(remove_junk); - sigchain_push_common(remove_junk_on_signal); + junk_git_dir = git_dir; if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); @@ -880,6 +894,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_reference.nr) setup_reference(); + else if (option_dissociate) { + warning(_("--dissociate given, but there is no --reference")); + option_dissociate = 0; + } fetch_pattern = value.buf; refspec = parse_fetch_refspec(1, &fetch_pattern); @@ -993,6 +1011,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_unlock_pack(transport); transport_disconnect(transport); + if (option_dissociate) + dissociate_from_references(); + junk_mode = JUNK_LEAVE_REPO; err = checkout(); diff --git a/builtin/column.c b/builtin/column.c index 75818520e1..449413c8a8 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -6,7 +6,7 @@ #include "column.h" static const char * const builtin_column_usage[] = { - N_("git column [options]"), + N_("git column [<options>]"), NULL }; static unsigned int colopts; diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 8a66c74e0f..25aa2cdef3 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -66,10 +66,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) continue; } - if (!memcmp(arg, "-S", 2)) { - sign_commit = arg + 2; + if (skip_prefix(arg, "-S", &sign_commit)) continue; - } if (!strcmp(arg, "--no-gpg-sign")) { sign_commit = NULL; diff --git a/builtin/commit.c b/builtin/commit.c index e108c53015..961e467242 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -34,12 +34,12 @@ #include "mailmap.h" static const char * const builtin_commit_usage[] = { - N_("git commit [options] [--] <pathspec>..."), + N_("git commit [<options>] [--] <pathspec>..."), NULL }; static const char * const builtin_status_usage[] = { - N_("git status [options] [--] <pathspec>..."), + N_("git status [<options>] [--] <pathspec>..."), NULL }; @@ -522,6 +522,12 @@ static int is_a_merge(const struct commit *current_head) return !!(current_head->parents && current_head->parents->next); } +static void assert_split_ident(struct ident_split *id, const struct strbuf *buf) +{ + if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin) + die("BUG: unable to parse our own ident: %s", buf->buf); +} + static void export_one(const char *var, const char *s, const char *e, int hack) { struct strbuf buf = STRBUF_INIT; @@ -532,20 +538,6 @@ static void export_one(const char *var, const char *s, const char *e, int hack) strbuf_release(&buf); } -static int sane_ident_split(struct ident_split *person) -{ - if (!person->name_begin || !person->name_end || - person->name_begin == person->name_end) - return 0; /* no human readable name */ - if (!person->mail_begin || !person->mail_end || - person->mail_begin == person->mail_end) - return 0; /* no usable mail */ - if (!person->date_begin || !person->date_end || - !person->tz_begin || !person->tz_end) - return 0; - return 1; -} - static int parse_force_date(const char *in, struct strbuf *out) { strbuf_addch(out, '@'); @@ -567,20 +559,14 @@ static void set_ident_var(char **buf, char *val) *buf = val; } -static char *envdup(const char *var) -{ - const char *val = getenv(var); - return val ? xstrdup(val) : NULL; -} - static void determine_author_info(struct strbuf *author_ident) { char *name, *email, *date; struct ident_split author; - name = envdup("GIT_AUTHOR_NAME"); - email = envdup("GIT_AUTHOR_EMAIL"); - date = envdup("GIT_AUTHOR_DATE"); + name = xstrdup_or_null(getenv("GIT_AUTHOR_NAME")); + email = xstrdup_or_null(getenv("GIT_AUTHOR_EMAIL")); + date = xstrdup_or_null(getenv("GIT_AUTHOR_DATE")); if (author_message) { struct ident_split ident; @@ -623,25 +609,15 @@ static void determine_author_info(struct strbuf *author_ident) } strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT)); - if (!split_ident_line(&author, author_ident->buf, author_ident->len) && - sane_ident_split(&author)) { - export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0); - export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0); - export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@'); - } - + assert_split_ident(&author, author_ident); + export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0); + export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0); + export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@'); free(name); free(email); free(date); } -static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf) -{ - if (split_ident_line(id, buf->buf, buf->len) || - !sane_ident_split(id)) - die(_("Malformed ident string: '%s'"), buf->buf); -} - static int author_date_is_interesting(void) { return author_message || force_date; @@ -800,32 +776,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (clean_message_contents) stripspace(&sb, 0); - if (signoff) { - /* - * See if we have a Conflicts: block at the end. If yes, count - * its size, so we can ignore it. - */ - int ignore_footer = 0; - int i, eol, previous = 0; - const char *nl; - - for (i = 0; i < sb.len; i++) { - nl = memchr(sb.buf + i, '\n', sb.len - i); - if (nl) - eol = nl - sb.buf; - else - eol = sb.len; - if (starts_with(sb.buf + previous, "\nConflicts:\n")) { - ignore_footer = sb.len - previous; - break; - } - while (i < eol) - i++; - previous = eol; - } - - append_signoff(&sb, ignore_footer, 0); - } + if (signoff) + append_signoff(&sb, ignore_non_trailer(&sb), 0); if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); @@ -880,8 +832,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix, status_printf_ln(s, GIT_COLOR_NORMAL, "%s", only_include_assumed); - split_ident_or_die(&ai, author_ident); - split_ident_or_die(&ci, &committer_ident); + /* + * These should never fail because they come from our own + * fmt_ident. They may fail the sane_ident test, but we know + * that the name and mail pointers will at least be valid, + * which is enough for our tests and printing here. + */ + assert_split_ident(&ai, author_ident); + assert_split_ident(&ci, &committer_ident); if (ident_cmp(&ai, &ci)) status_printf_ln(s, GIT_COLOR_NORMAL, @@ -1092,7 +1050,7 @@ static const char *find_author_by_nickname(const char *name) clear_mailmap(&mailmap); return strbuf_detach(&buf, NULL); } - die(_("No existing author found with '%s'"), name); + die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name); } @@ -1808,8 +1766,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!transaction || ref_transaction_update(transaction, "HEAD", sha1, current_head - ? current_head->object.sha1 : NULL, - 0, !!current_head, sb.buf, &err) || + ? current_head->object.sha1 : null_sha1, + 0, sb.buf, &err) || ref_transaction_commit(transaction, &err)) { rollback_index_files(); die("%s", err.buf); diff --git a/builtin/config.c b/builtin/config.c index fddafbba36..d32c5327e5 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -5,7 +5,7 @@ #include "urlmatch.h" static const char *const builtin_config_usage[] = { - N_("git config [options]"), + N_("git config [<options>]"), NULL }; @@ -565,8 +565,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) } } else if (actions == ACTION_EDIT) { - const char *config_file = given_config_source.file ? - given_config_source.file : git_path("config"); + char *config_file; + check_argc(argc, 0, 0); if (!given_config_source.file && nongit) die("not in a git directory"); @@ -575,6 +575,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (given_config_source.blob) die("editing blobs is not supported"); git_config(git_default_config, NULL); + config_file = xstrdup(given_config_source.file ? + given_config_source.file : git_path("config")); if (use_global_config) { int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666); if (fd) { @@ -587,6 +589,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) die_errno(_("cannot create configuration file %s"), config_file); } launch_editor(config_file, NULL, NULL); + free(config_file); } else if (actions == ACTION_SET) { int ret; diff --git a/builtin/describe.c b/builtin/describe.c index 9103193b4f..e00a75b121 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -14,8 +14,8 @@ #define MAX_TAGS (FLAG_BITS - 1) static const char * const describe_usage[] = { - N_("git describe [options] <commit-ish>*"), - N_("git describe [options] --dirty"), + N_("git describe [<options>] [<commit-ish>...]"), + N_("git describe [<options>] --dirty"), NULL }; diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 9200069363..8ed2eb8813 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -11,7 +11,7 @@ #include "submodule.h" static const char diff_files_usage[] = -"git diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]" +"git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]" COMMON_DIFF_OPTIONS_HELP; int cmd_diff_files(int argc, const char **argv, const char *prefix) diff --git a/builtin/diff-index.c b/builtin/diff-index.c index ce15b23042..d979824f93 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -7,7 +7,7 @@ static const char diff_cache_usage[] = "git diff-index [-m] [--cached] " -"[<common diff options>] <tree-ish> [<path>...]" +"[<common-diff-options>] <tree-ish> [<path>...]" COMMON_DIFF_OPTIONS_HELP; int cmd_diff_index(int argc, const char **argv, const char *prefix) diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 1c4ad6223e..12b683d021 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -82,7 +82,7 @@ static int diff_tree_stdin(char *line) static const char diff_tree_usage[] = "git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] " -"[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n" +"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n" " -r diff recursively\n" " --root include the initial commit as diff against /dev/null\n" COMMON_DIFF_OPTIONS_HELP; diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 1262b405f8..4a6b340ab6 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -6,7 +6,7 @@ #include "sha1-array.h" static const char fetch_pack_usage[] = -"git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] " +"git fetch-pack [--all] [--stdin] [--quiet | -q] [--keep | -k] [--thin] " "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] " "[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]"; diff --git a/builtin/fetch.c b/builtin/fetch.c index 7b84d35d83..f9512652cf 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -11,7 +11,6 @@ #include "run-command.h" #include "parse-options.h" #include "sigchain.h" -#include "transport.h" #include "submodule.h" #include "connected.h" #include "argv-array.h" @@ -416,8 +415,10 @@ static int s_update_ref(const char *action, transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, ref->name, ref->new_sha1, - ref->old_sha1, 0, check_old, msg, &err)) + ref_transaction_update(transaction, ref->name, + ref->new_sha1, + check_old ? ref->old_sha1 : NULL, + 0, msg, &err)) goto fail; ret = ref_transaction_commit(transaction, &err); diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 37177c6c29..1d962dc569 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -10,7 +10,7 @@ #include "gpg-interface.h" static const char * const fmt_merge_msg_usage[] = { - N_("git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"), + N_("git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"), NULL }; @@ -216,8 +216,7 @@ static void add_branch_desc(struct strbuf *out, const char *name) strbuf_addf(out, " : %.*s", (int)(ep - bp), bp); bp = ep; } - if (out->buf[out->len - 1] != '\n') - strbuf_addch(out, '\n'); + strbuf_complete_line(out); } strbuf_release(&desc); } diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 603a90e29b..83f9cf9163 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -178,11 +178,10 @@ 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, ')'); + const char *color, *ep = strchr(sp, ')'); int at; if (!ep) @@ -191,8 +190,8 @@ static int verify_format(const char *format) at = parse_atom(sp + 2, ep); cp = ep + 1; - if (starts_with(used_atom[at], "color:")) - need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset); + if (skip_prefix(used_atom[at], "color:", &color)) + need_color_reset_at_eol = !!strcmp(color, "reset"); } return 0; } @@ -717,7 +716,10 @@ static void populate_value(struct refinfo *ref) starts_with(name, "upstream")) { char buf[40]; - stat_tracking_info(branch, &num_ours, &num_theirs); + if (stat_tracking_info(branch, &num_ours, + &num_theirs) != 1) + continue; + if (!num_ours && !num_theirs) v->s = ""; else if (!num_ours) { @@ -735,7 +737,11 @@ static void populate_value(struct refinfo *ref) } else if (!strcmp(formatp, "trackshort") && starts_with(name, "upstream")) { assert(branch); - stat_tracking_info(branch, &num_ours, &num_theirs); + + if (stat_tracking_info(branch, &num_ours, + &num_theirs) != 1) + continue; + if (!num_ours && !num_theirs) v->s = "="; else if (!num_ours) @@ -1054,7 +1060,7 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset) } static char const * const for_each_ref_usage[] = { - N_("git for-each-ref [options] [<pattern>]"), + N_("git for-each-ref [<options>] [<pattern>]"), NULL }; @@ -1075,7 +1081,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_BIT(0 , "python", "e_style, N_("quote placeholders suitably for python"), QUOTE_PYTHON), OPT_BIT(0 , "tcl", "e_style, - N_("quote placeholders suitably for tcl"), QUOTE_TCL), + N_("quote placeholders suitably for Tcl"), QUOTE_TCL), OPT_GROUP(""), OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), diff --git a/builtin/fsck.c b/builtin/fsck.c index a27515aeaa..0c757862e8 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -600,7 +600,7 @@ static int fsck_cache_tree(struct cache_tree *it) } static char const * const fsck_usage[] = { - N_("git fsck [options] [<object>...]"), + N_("git fsck [<options>] [<object>...]"), NULL }; diff --git a/builtin/gc.c b/builtin/gc.c index 005adbebea..5c634afc00 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -21,7 +21,7 @@ #define FAILED_RUN "failed to run %s" static const char * const builtin_gc_usage[] = { - N_("git gc [options]"), + N_("git gc [<options>]"), NULL }; diff --git a/builtin/grep.c b/builtin/grep.c index 4063882f06..abc440023f 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -20,7 +20,7 @@ #include "pathspec.h" static char const * const grep_usage[] = { - N_("git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"), + N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"), NULL }; @@ -641,7 +641,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "untracked", &untracked, N_("search in both tracked and untracked files")), OPT_SET_INT(0, "exclude-standard", &opt_exclude, - N_("search also in ignored files"), 1), + N_("ignore files specified via '.gitignore'"), 1), OPT_GROUP(""), OPT_BOOL('v', "invert-match", &opt.invert, N_("show non-matching lines")), @@ -885,7 +885,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } } - if (!show_in_pager) + if (!show_in_pager && !opt.status_only) setup_pager(); if (!use_index && (untracked || cached)) diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 6158363318..207b90c7b1 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -79,7 +79,7 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags, int cmd_hash_object(int argc, const char **argv, const char *prefix) { static const char * const hash_object_usage[] = { - N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."), + N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."), N_("git hash-object --stdin-paths < <list-of-paths>"), NULL }; diff --git a/builtin/help.c b/builtin/help.c index b3c818ee01..3422e73079 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -49,7 +49,7 @@ static struct option builtin_help_options[] = { }; static const char * const builtin_help_usage[] = { - N_("git help [--all] [--guides] [--man|--web|--info] [command]"), + N_("git help [--all] [--guides] [--man | --web | --info] [<command>]"), NULL }; @@ -171,7 +171,7 @@ static void exec_man_cmd(const char *cmd, const char *page) { struct strbuf shell_cmd = STRBUF_INIT; strbuf_addf(&shell_cmd, "%s %s", cmd, page); - execl("/bin/sh", "sh", "-c", shell_cmd.buf, (char *)NULL); + execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL); warning(_("failed to exec '%s': %s"), cmd, strerror(errno)); } @@ -321,16 +321,18 @@ static void setup_man_path(void) { struct strbuf new_path = STRBUF_INIT; const char *old_path = getenv("MANPATH"); + char *git_man_path = system_path(GIT_MAN_PATH); /* We should always put ':' after our path. If there is no * old_path, the ':' at the end will let 'man' to try * system-wide paths after ours to find the manual page. If * there is old_path, we need ':' as delimiter. */ - strbuf_addstr(&new_path, system_path(GIT_MAN_PATH)); + strbuf_addstr(&new_path, git_man_path); strbuf_addch(&new_path, ':'); if (old_path) strbuf_addstr(&new_path, old_path); + free(git_man_path); setenv("MANPATH", new_path.buf, 1); strbuf_release(&new_path); @@ -380,8 +382,10 @@ static void show_info_page(const char *git_cmd) static void get_html_page_path(struct strbuf *page_path, const char *page) { struct stat st; + char *to_free = NULL; + if (!html_path) - html_path = system_path(GIT_HTML_PATH); + html_path = to_free = system_path(GIT_HTML_PATH); /* Check that we have a git documentation directory. */ if (!strstr(html_path, "://")) { @@ -392,6 +396,7 @@ static void get_html_page_path(struct strbuf *page_path, const char *page) strbuf_init(page_path, 0); strbuf_addf(page_path, "%s/%s.html", html_path, page); + free(to_free); } /* @@ -451,7 +456,7 @@ static void list_common_guides_help(void) int cmd_help(int argc, const char **argv, const char *prefix) { int nongit; - const char *alias; + char *alias; enum help_format parsed_help_format; argc = parse_options(argc, argv, prefix, builtin_help_options, @@ -494,6 +499,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) alias = alias_lookup(argv[0]); if (alias && !is_git_command(argv[0])) { printf_ln(_("`git %s' is aliased to `%s'"), argv[0], alias); + free(alias); return 0; } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index a369f55353..cf654df09b 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -447,7 +447,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size, if (type == OBJ_BLOB && size > big_file_threshold) buf = fixed_buf; else - buf = xmalloc(size); + buf = xmallocz(size); memset(&stream, 0, sizeof(stream)); git_inflate_init(&stream); @@ -552,7 +552,7 @@ static void *unpack_data(struct object_entry *obj, git_zstream stream; int status; - data = xmalloc(consume ? 64*1024 : obj->size); + data = xmallocz(consume ? 64*1024 : obj->size); inbuf = xmalloc((len < 64*1024) ? len : 64*1024); memset(&stream, 0, sizeof(stream)); @@ -1204,7 +1204,6 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size) int status; unsigned char outbuf[4096]; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, zlib_compression_level); stream.next_in = in; stream.avail_in = size; diff --git a/builtin/init-db.c b/builtin/init-db.c index aab44d2e45..6723d39c3b 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -119,15 +119,18 @@ static void copy_templates(const char *template_dir) DIR *dir; const char *git_dir = get_git_dir(); int len = strlen(git_dir); + char *to_free = NULL; if (!template_dir) template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); if (!template_dir) template_dir = init_db_template_dir; if (!template_dir) - template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR); - if (!template_dir[0]) + template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR); + if (!template_dir[0]) { + free(to_free); return; + } template_len = strlen(template_dir); if (PATH_MAX <= (template_len+strlen("/config"))) die(_("insanely long template path %s"), template_dir); @@ -139,7 +142,7 @@ static void copy_templates(const char *template_dir) dir = opendir(template_path); if (!dir) { warning(_("templates not found %s"), template_dir); - return; + goto free_return; } /* Make sure that template is from the correct vintage */ @@ -155,8 +158,7 @@ static void copy_templates(const char *template_dir) "a wrong format version %d from '%s'"), repository_format_version, template_dir); - closedir(dir); - return; + goto close_free_return; } memcpy(path, git_dir, len); @@ -166,7 +168,10 @@ static void copy_templates(const char *template_dir) copy_templates_1(path, len, template_path, template_len, dir); +close_free_return: closedir(dir); +free_return: + free(to_free); } static int git_init_db_config(const char *k, const char *v, void *cb) @@ -256,6 +261,8 @@ static int create_default_files(const char *template_path) !lstat(path, &st2) && st1.st_mode != st2.st_mode && !chmod(path, st1.st_mode)); + if (filemode && !reinit && (st1.st_mode & S_IXUSR)) + filemode = 0; } git_config_set("core.filemode", filemode ? "true" : "false"); @@ -465,7 +472,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset) } static const char *const init_db_usage[] = { - N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]"), + N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [<directory>]"), NULL }; diff --git a/builtin/log.c b/builtin/log.c index 734aab3a73..dd8f3fcfc4 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -38,8 +38,8 @@ static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; static const char * const builtin_log_usage[] = { - N_("git log [<options>] [<revision range>] [[--] <path>...]\n") - N_(" or: git show [options] <object>..."), + N_("git log [<options>] [<revision range>] [[--] <path>...]"), + N_("git show [<options>] <object>..."), NULL }; @@ -489,7 +489,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev) } static int show_tree_object(const unsigned char *sha1, - const char *base, int baselen, + struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : ""); @@ -705,7 +705,7 @@ static int git_format_config(const char *var, const char *value, void *cb) return 0; } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") || - !strcmp(var, "color.ui")) { + !strcmp(var, "color.ui") || !strcmp(var, "diff.submodule")) { return 0; } if (!strcmp(var, "format.numbered")) { @@ -1023,7 +1023,7 @@ static const char *set_outdir(const char *prefix, const char *output_directory) } static const char * const builtin_format_patch_usage[] = { - N_("git format-patch [options] [<since> | <revision range>]"), + N_("git format-patch [<options>] [<since> | <revision-range>]"), NULL }; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 99cee20fb0..914054d367 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -398,7 +398,7 @@ int report_path_error(const char *ps_matched, } static const char * const ls_files_usage[] = { - N_("git ls-files [options] [<file>...]"), + N_("git ls-files [<options>] [<file>...]"), NULL }; diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index b2a4b92992..4554dbc8a9 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -5,7 +5,7 @@ static const char ls_remote_usage[] = "git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n" -" [-q|--quiet] [--exit-code] [--get-url] [<repository> [<refs>...]]"; +" [-q | --quiet] [--exit-code] [--get-url] [<repository> [<refs>...]]"; /* * Is there one among the list of patterns that match the tail part diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 51184dfa2e..3b04a0f082 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -61,10 +61,11 @@ static int show_recursive(const char *base, int baselen, const char *pathname) } } -static int show_tree(const unsigned char *sha1, const char *base, int baselen, +static int show_tree(const unsigned char *sha1, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { int retval = 0; + int baselen; const char *type = blob_type; if (S_ISGITLINK(mode)) { @@ -79,7 +80,7 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, */ type = commit_type; } else if (S_ISDIR(mode)) { - if (show_recursive(base, baselen, pathname)) { + if (show_recursive(base->buf, base->len, pathname)) { retval = READ_TREE_RECURSIVE; if (!(ls_options & LS_SHOW_TREES)) return retval; @@ -89,10 +90,6 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, else if (ls_options & LS_TREE_ONLY) return 0; - if (chomp_prefix && - (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix))) - return 0; - if (!(ls_options & LS_NAME_ONLY)) { if (ls_options & LS_SHOW_SIZE) { char size_text[24]; @@ -112,8 +109,12 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, printf("%06o %s %s\t", mode, type, find_unique_abbrev(sha1, abbrev)); } - write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix, - pathname, stdout, line_termination); + baselen = base->len; + strbuf_addstr(base, pathname); + write_name_quoted_relative(base->buf, + chomp_prefix ? ls_tree_prefix : NULL, + stdout, line_termination); + strbuf_setlen(base, baselen); return retval; } @@ -173,7 +174,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) * cannot be lifted until it is converted to use * match_pathspec() or tree_entry_interesting() */ - parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE, + parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE | + PATHSPEC_EXCLUDE, PATHSPEC_PREFER_CWD, prefix, argv + 1); for (i = 0; i < pathspec.nr; i++) diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 6a14d2985d..999a5250fb 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -15,6 +15,7 @@ static const char *metainfo_charset; static struct strbuf line = STRBUF_INIT; static struct strbuf name = STRBUF_INIT; static struct strbuf email = STRBUF_INIT; +static char *message_id; static enum { TE_DONTCARE, TE_QP, TE_BASE64 @@ -24,6 +25,7 @@ static struct strbuf charset = STRBUF_INIT; static int patch_lines; static struct strbuf **p_hdr_data, **s_hdr_data; static int use_scissors; +static int add_message_id; static int use_inbody_headers = 1; #define MAX_HDR_PARSED 10 @@ -198,6 +200,12 @@ static void handle_content_type(struct strbuf *line) } } +static void handle_message_id(const struct strbuf *line) +{ + if (add_message_id) + message_id = strdup(line->buf); +} + static void handle_content_transfer_encoding(const struct strbuf *line) { if (strcasestr(line->buf, "base64")) @@ -342,6 +350,14 @@ static int check_header(const struct strbuf *line, ret = 1; goto check_header_out; } + if (cmp_header(line, "Message-Id")) { + len = strlen("Message-Id: "); + strbuf_add(&sb, line->buf + len, line->len - len); + decode_header(&sb); + handle_message_id(&sb); + ret = 1; + goto check_header_out; + } /* for inbody stuff */ if (starts_with(line->buf, ">From") && isspace(line->buf[5])) { @@ -816,6 +832,8 @@ static int handle_commit_msg(struct strbuf *line) } if (patchbreak(line)) { + if (message_id) + fprintf(cmitmsg, "Message-Id: %s\n", message_id); fclose(cmitmsg); cmitmsg = NULL; return 1; @@ -1013,7 +1031,7 @@ static int git_mailinfo_config(const char *var, const char *value, void *unused) } static const char mailinfo_usage[] = - "git mailinfo [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info"; + "git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info"; int cmd_mailinfo(int argc, const char **argv, const char *prefix) { @@ -1032,6 +1050,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix) keep_subject = 1; else if (!strcmp(argv[1], "-b")) keep_non_patch_brackets_in_subject = 1; + else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id")) + add_message_id = 1; else if (!strcmp(argv[1], "-u")) metainfo_charset = def_charset; else if (!strcmp(argv[1], "-n")) diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 0ecde8da30..08a8217890 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -10,7 +10,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { struct commit_list *result; - result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0); + result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1); if (!result) return 1; @@ -26,8 +26,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) } static const char * const merge_base_usage[] = { - N_("git merge-base [-a|--all] <commit> <commit>..."), - N_("git merge-base [-a|--all] --octopus <commit>..."), + N_("git merge-base [-a | --all] <commit> <commit>..."), + 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>]"), @@ -176,7 +176,7 @@ static int handle_fork_point(int argc, const char **argv) 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); + bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit); /* * There should be one and only one merge base, when we found diff --git a/builtin/merge-file.c b/builtin/merge-file.c index 844f84f40b..ea8093f676 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -5,7 +5,7 @@ #include "parse-options.h" static const char *const merge_file_usage[] = { - N_("git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2"), + N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"), NULL }; @@ -42,7 +42,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) N_("for conflicts, use this marker size")), OPT__QUIET(&quiet, N_("do not warn about conflicts")), OPT_CALLBACK('L', NULL, names, N_("name"), - N_("set labels for file1/orig_file/file2"), &label_cb), + N_("set labels for file1/orig-file/file2"), &label_cb), OPT_END(), }; @@ -90,7 +90,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) if (ret >= 0) { const char *filename = argv[0]; - FILE *f = to_stdout ? stdout : fopen(filename, "wb"); + const char *fpath = prefix_filename(prefix, prefixlen, argv[0]); + FILE *f = to_stdout ? stdout : fopen(fpath, "wb"); if (!f) ret = error("Could not open %s for writing", filename); diff --git a/builtin/merge-index.c b/builtin/merge-index.c index b416d92849..1a1eafa6fd 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -75,7 +75,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix) signal(SIGCHLD, SIG_DFL); if (argc < 3) - usage("git merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)"); + usage("git merge-index [-o] [-q] <merge-program> (-a | [--] [<filename>...])"); read_cache(); diff --git a/builtin/merge.c b/builtin/merge.c index bebbe5b308..3b0f8f96d4 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -29,6 +29,7 @@ #include "remote.h" #include "fmt-merge-msg.h" #include "gpg-interface.h" +#include "sequencer.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -41,8 +42,8 @@ struct strategy { }; static const char * const builtin_merge_usage[] = { - N_("git merge [options] [<commit>...]"), - N_("git merge [options] <msg> HEAD <commit>"), + N_("git merge [<options>] [<commit>...]"), + N_("git merge [<options>] <msg> HEAD <commit>"), N_("git merge --abort"), NULL }; @@ -880,28 +881,20 @@ static int finish_automerge(struct commit *head, return 0; } -static int suggest_conflicts(int renormalizing) +static int suggest_conflicts(void) { const char *filename; FILE *fp; - int pos; + struct strbuf msgbuf = STRBUF_INIT; filename = git_path("MERGE_MSG"); fp = fopen(filename, "a"); if (!fp) die_errno(_("Could not open '%s' for writing"), filename); - fprintf(fp, "\nConflicts:\n"); - for (pos = 0; pos < active_nr; pos++) { - const struct cache_entry *ce = active_cache[pos]; - - if (ce_stage(ce)) { - fprintf(fp, "\t%s\n", ce->name); - while (pos + 1 < active_nr && - !strcmp(ce->name, - active_cache[pos + 1]->name)) - pos++; - } - } + + append_conflicts_hint(&msgbuf); + fputs(msgbuf.buf, fp); + strbuf_release(&msgbuf); fclose(fp); rerere(allow_rerere_auto); printf(_("Automatic merge failed; " @@ -1320,7 +1313,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!remoteheads) ; /* already up-to-date */ else if (!remoteheads->next) - common = get_merge_bases(head_commit, remoteheads->item, 1); + common = get_merge_bases(head_commit, remoteheads->item); else { struct commit_list *list = remoteheads; commit_list_insert(head_commit, &list); @@ -1417,7 +1410,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = get_merge_bases(head_commit, j->item, 1); + common_one = get_merge_bases(head_commit, j->item); if (hashcmp(common_one->item->object.sha1, j->item->object.sha1)) { up_to_date = 0; @@ -1550,7 +1543,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) fprintf(stderr, _("Automatic merge went well; " "stopped before committing as requested\n")); else - ret = suggest_conflicts(option_renormalize); + ret = suggest_conflicts(); done: free(branch_to_free); diff --git a/builtin/mv.c b/builtin/mv.c index 563d05ba1a..d1d43168ae 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -12,7 +12,7 @@ #include "submodule.h" static const char * const builtin_mv_usage[] = { - N_("git mv [options] <source>... <destination>"), + N_("git mv [<options>] <source>... <destination>"), NULL }; diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 3c8f319be6..9736d4452f 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -252,9 +252,9 @@ static void show_name(const struct object *obj, } static char const * const name_rev_usage[] = { - N_("git name-rev [options] <commit>..."), - N_("git name-rev [options] --all"), - N_("git name-rev [options] --stdin"), + N_("git name-rev [<options>] <commit>..."), + N_("git name-rev [<options>] --all"), + N_("git name-rev [<options>] --stdin"), NULL }; diff --git a/builtin/notes.c b/builtin/notes.c index 68b6cd8cc1..63f95fc554 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -21,18 +21,18 @@ #include "notes-utils.h" static const char * const git_notes_usage[] = { - N_("git notes [--ref <notes_ref>] [list [<object>]]"), - N_("git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), - N_("git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"), - N_("git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), - N_("git notes [--ref <notes_ref>] edit [<object>]"), - N_("git notes [--ref <notes_ref>] show [<object>]"), - N_("git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"), + N_("git notes [--ref <notes-ref>] [list [<object>]]"), + N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), + N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"), + N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), + N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"), + N_("git notes [--ref <notes-ref>] show [<object>]"), + N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"), N_("git notes merge --commit [-v | -q]"), N_("git notes merge --abort [-v | -q]"), - N_("git notes [--ref <notes_ref>] remove [<object>...]"), - N_("git notes [--ref <notes_ref>] prune [-n | -v]"), - N_("git notes [--ref <notes_ref>] get-ref"), + N_("git notes [--ref <notes-ref>] remove [<object>...]"), + N_("git notes [--ref <notes-ref>] prune [-n | -v]"), + N_("git notes [--ref <notes-ref>] get-ref"), NULL }; @@ -68,7 +68,7 @@ static const char * const git_notes_show_usage[] = { }; static const char * const git_notes_merge_usage[] = { - N_("git notes merge [<options>] <notes_ref>"), + N_("git notes merge [<options>] <notes-ref>"), N_("git notes merge --commit [<options>]"), N_("git notes merge --abort [<options>]"), NULL @@ -92,12 +92,22 @@ static const char * const git_notes_get_ref_usage[] = { static const char note_template[] = "\nWrite/edit the notes for the following object:\n"; -struct msg_arg { +struct note_data { int given; int use_editor; + char *edit_path; struct strbuf buf; }; +static void free_note_data(struct note_data *d) +{ + if (d->edit_path) { + unlink_or_warn(d->edit_path); + free(d->edit_path); + } + strbuf_release(&d->buf); +} + static int list_each_note(const unsigned char *object_sha1, const unsigned char *note_sha1, char *note_path, void *cb_data) @@ -106,7 +116,7 @@ static int list_each_note(const unsigned char *object_sha1, return 0; } -static void write_note_data(int fd, const unsigned char *sha1) +static void copy_obj_to_fd(int fd, const unsigned char *sha1) { unsigned long size; enum object_type type; @@ -149,26 +159,23 @@ static void write_commented_object(int fd, const unsigned char *object) sha1_to_hex(object)); } -static void create_note(const unsigned char *object, struct msg_arg *msg, - int append_only, const unsigned char *prev, - unsigned char *result) +static void prepare_note_data(const unsigned char *object, struct note_data *d, + const unsigned char *old_note) { - char *path = NULL; - - if (msg->use_editor || !msg->given) { + if (d->use_editor || !d->given) { int fd; struct strbuf buf = STRBUF_INIT; /* write the template message before editing: */ - path = git_pathdup("NOTES_EDITMSG"); - fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + d->edit_path = git_pathdup("NOTES_EDITMSG"); + fd = open(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd < 0) - die_errno(_("could not create file '%s'"), path); + die_errno(_("could not create file '%s'"), d->edit_path); - if (msg->given) - write_or_die(fd, msg->buf.buf, msg->buf.len); - else if (prev && !append_only) - write_note_data(fd, prev); + if (d->given) + write_or_die(fd, d->buf.buf, d->buf.len); + else if (old_note) + copy_obj_to_fd(fd, old_note); strbuf_addch(&buf, '\n'); strbuf_add_commented_lines(&buf, note_template, strlen(note_template)); @@ -179,94 +186,71 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, close(fd); strbuf_release(&buf); - strbuf_reset(&(msg->buf)); - - if (launch_editor(path, &(msg->buf), NULL)) { - die(_("Please supply the note contents using either -m" \ - " or -F option")); - } - stripspace(&(msg->buf), 1); - } - - if (prev && append_only) { - /* Append buf to previous note contents */ - unsigned long size; - enum object_type type; - char *prev_buf = read_sha1_file(prev, &type, &size); + strbuf_reset(&d->buf); - strbuf_grow(&(msg->buf), size + 1); - if (msg->buf.len && prev_buf && size) - strbuf_insert(&(msg->buf), 0, "\n", 1); - if (prev_buf && size) - strbuf_insert(&(msg->buf), 0, prev_buf, size); - free(prev_buf); - } - - if (!msg->buf.len) { - fprintf(stderr, _("Removing note for object %s\n"), - sha1_to_hex(object)); - hashclr(result); - } else { - if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) { - error(_("unable to write note object")); - if (path) - error(_("The note contents have been left in %s"), - path); - exit(128); + if (launch_editor(d->edit_path, &d->buf, NULL)) { + die(_("Please supply the note contents using either -m or -F option")); } + stripspace(&d->buf, 1); } +} - if (path) { - unlink_or_warn(path); - free(path); +static void write_note_data(struct note_data *d, unsigned char *sha1) +{ + if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, sha1)) { + error(_("unable to write note object")); + if (d->edit_path) + error(_("The note contents have been left in %s"), + d->edit_path); + exit(128); } } static int parse_msg_arg(const struct option *opt, const char *arg, int unset) { - struct msg_arg *msg = opt->value; + struct note_data *d = opt->value; - strbuf_grow(&(msg->buf), strlen(arg) + 2); - if (msg->buf.len) - strbuf_addch(&(msg->buf), '\n'); - strbuf_addstr(&(msg->buf), arg); - stripspace(&(msg->buf), 0); + strbuf_grow(&d->buf, strlen(arg) + 2); + if (d->buf.len) + strbuf_addch(&d->buf, '\n'); + strbuf_addstr(&d->buf, arg); + stripspace(&d->buf, 0); - msg->given = 1; + d->given = 1; return 0; } static int parse_file_arg(const struct option *opt, const char *arg, int unset) { - struct msg_arg *msg = opt->value; + struct note_data *d = opt->value; - if (msg->buf.len) - strbuf_addch(&(msg->buf), '\n'); + if (d->buf.len) + strbuf_addch(&d->buf, '\n'); if (!strcmp(arg, "-")) { - if (strbuf_read(&(msg->buf), 0, 1024) < 0) + if (strbuf_read(&d->buf, 0, 1024) < 0) die_errno(_("cannot read '%s'"), arg); - } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0) + } else if (strbuf_read_file(&d->buf, arg, 1024) < 0) die_errno(_("could not open or read '%s'"), arg); - stripspace(&(msg->buf), 0); + stripspace(&d->buf, 0); - msg->given = 1; + d->given = 1; return 0; } static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) { - struct msg_arg *msg = opt->value; + struct note_data *d = opt->value; char *buf; unsigned char object[20]; enum object_type type; unsigned long len; - if (msg->buf.len) - strbuf_addch(&(msg->buf), '\n'); + if (d->buf.len) + strbuf_addch(&d->buf, '\n'); if (get_sha1(arg, object)) die(_("Failed to resolve '%s' as a valid ref."), arg); - if (!(buf = read_sha1_file(object, &type, &len)) || !len) { + if (!(buf = read_sha1_file(object, &type, &len))) { free(buf); die(_("Failed to read object '%s'."), arg); } @@ -274,17 +258,17 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) free(buf); die(_("Cannot read note data from non-blob object '%s'."), arg); } - strbuf_add(&(msg->buf), buf, len); + strbuf_add(&d->buf, buf, len); free(buf); - msg->given = 1; + d->given = 1; return 0; } static int parse_reedit_arg(const struct option *opt, const char *arg, int unset) { - struct msg_arg *msg = opt->value; - msg->use_editor = 1; + struct note_data *d = opt->value; + d->use_editor = 1; return parse_reuse_arg(opt, arg, unset); } @@ -397,26 +381,27 @@ static int append_edit(int argc, const char **argv, const char *prefix); static int add(int argc, const char **argv, const char *prefix) { - int retval = 0, force = 0; + int force = 0, allow_empty = 0; const char *object_ref; struct notes_tree *t; unsigned char object[20], new_note[20]; - char logmsg[100]; const unsigned char *note; - struct msg_arg msg = { 0, 0, STRBUF_INIT }; + struct note_data d = { 0, 0, NULL, STRBUF_INIT }; struct option options[] = { - { OPTION_CALLBACK, 'm', "message", &msg, N_("message"), + { OPTION_CALLBACK, 'm', "message", &d, N_("message"), N_("note contents as a string"), PARSE_OPT_NONEG, parse_msg_arg}, - { OPTION_CALLBACK, 'F', "file", &msg, N_("file"), + { OPTION_CALLBACK, 'F', "file", &d, N_("file"), N_("note contents in a file"), PARSE_OPT_NONEG, parse_file_arg}, - { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"), + { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"), N_("reuse and edit specified note object"), PARSE_OPT_NONEG, parse_reedit_arg}, - { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"), + { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"), N_("reuse specified note object"), PARSE_OPT_NONEG, parse_reuse_arg}, + OPT_BOOL(0, "allow-empty", &allow_empty, + N_("allow storing empty note")), OPT__FORCE(&force, N_("replace existing notes")), OPT_END() }; @@ -439,41 +424,44 @@ static int add(int argc, const char **argv, const char *prefix) if (note) { if (!force) { - if (!msg.given) { - /* - * Redirect to "edit" subcommand. - * - * We only end up here if none of -m/-F/-c/-C - * or -f are given. The original args are - * therefore still in argv[0-1]. - */ - argv[0] = "edit"; - free_notes(t); - return append_edit(argc, argv, prefix); + free_notes(t); + if (d.given) { + free_note_data(&d); + return error(_("Cannot add notes. " + "Found existing notes for object %s. " + "Use '-f' to overwrite existing notes"), + sha1_to_hex(object)); } - retval = error(_("Cannot add notes. Found existing notes " - "for object %s. Use '-f' to overwrite " - "existing notes"), sha1_to_hex(object)); - goto out; + /* + * Redirect to "edit" subcommand. + * + * We only end up here if none of -m/-F/-c/-C or -f are + * given. The original args are therefore still in + * argv[0-1]. + */ + argv[0] = "edit"; + return append_edit(argc, argv, prefix); } fprintf(stderr, _("Overwriting existing notes for object %s\n"), sha1_to_hex(object)); } - create_note(object, &msg, 0, note, new_note); - - if (is_null_sha1(new_note)) + prepare_note_data(object, &d, note); + if (d.buf.len || allow_empty) { + write_note_data(&d, new_note); + if (add_note(t, object, new_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); + commit_notes(t, "Notes added by 'git notes add'"); + } else { + fprintf(stderr, _("Removing note for object %s\n"), + sha1_to_hex(object)); remove_note(t, object); - else if (add_note(t, object, new_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + commit_notes(t, "Notes removed by 'git notes add'"); + } - snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", - is_null_sha1(new_note) ? "removed" : "added", "add"); - commit_notes(t, logmsg); -out: + free_note_data(&d); free_notes(t); - strbuf_release(&(msg.buf)); - return retval; + return 0; } static int copy(int argc, const char **argv, const char *prefix) @@ -554,26 +542,29 @@ out: static int append_edit(int argc, const char **argv, const char *prefix) { + int allow_empty = 0; const char *object_ref; struct notes_tree *t; unsigned char object[20], new_note[20]; const unsigned char *note; char logmsg[100]; const char * const *usage; - struct msg_arg msg = { 0, 0, STRBUF_INIT }; + struct note_data d = { 0, 0, NULL, STRBUF_INIT }; struct option options[] = { - { OPTION_CALLBACK, 'm', "message", &msg, N_("message"), + { OPTION_CALLBACK, 'm', "message", &d, N_("message"), N_("note contents as a string"), PARSE_OPT_NONEG, parse_msg_arg}, - { OPTION_CALLBACK, 'F', "file", &msg, N_("file"), + { OPTION_CALLBACK, 'F', "file", &d, N_("file"), N_("note contents in a file"), PARSE_OPT_NONEG, parse_file_arg}, - { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"), + { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"), N_("reuse and edit specified note object"), PARSE_OPT_NONEG, parse_reedit_arg}, - { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"), + { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"), N_("reuse specified note object"), PARSE_OPT_NONEG, parse_reuse_arg}, + OPT_BOOL(0, "allow-empty", &allow_empty, + N_("allow storing empty note")), OPT_END() }; int edit = !strcmp(argv[0], "edit"); @@ -587,7 +578,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) usage_with_options(usage, options); } - if (msg.given && edit) + if (d.given && edit) fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated " "for the 'edit' subcommand.\n" "Please use 'git notes add -f -m/-F/-c/-C' instead.\n")); @@ -600,18 +591,39 @@ static int append_edit(int argc, const char **argv, const char *prefix) t = init_notes_check(argv[0]); note = get_note(t, object); - create_note(object, &msg, !edit, note, new_note); + prepare_note_data(object, &d, edit ? note : NULL); - if (is_null_sha1(new_note)) - remove_note(t, object); - else if (add_note(t, object, new_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + if (note && !edit) { + /* Append buf to previous note contents */ + unsigned long size; + enum object_type type; + char *prev_buf = read_sha1_file(note, &type, &size); + + strbuf_grow(&d.buf, size + 1); + if (d.buf.len && prev_buf && size) + strbuf_insert(&d.buf, 0, "\n", 1); + if (prev_buf && size) + strbuf_insert(&d.buf, 0, prev_buf, size); + free(prev_buf); + } - snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", - is_null_sha1(new_note) ? "removed" : "added", argv[0]); + if (d.buf.len || allow_empty) { + write_note_data(&d, new_note); + if (add_note(t, object, new_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); + snprintf(logmsg, sizeof(logmsg), "Notes added by 'git notes %s'", + argv[0]); + } else { + fprintf(stderr, _("Removing note for object %s\n"), + sha1_to_hex(object)); + remove_note(t, object); + snprintf(logmsg, sizeof(logmsg), "Notes removed by 'git notes %s'", + argv[0]); + } commit_notes(t, logmsg); + + free_note_data(&d); free_notes(t); - strbuf_release(&(msg.buf)); return 0; } @@ -939,7 +951,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix) const char *override_notes_ref = NULL; struct option options[] = { OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"), - N_("use notes from <notes_ref>")), + N_("use notes from <notes-ref>")), OPT_END() }; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 3f9f5c7760..c3a75166bd 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -125,7 +125,6 @@ static unsigned long do_compress(void **pptr, unsigned long size) void *in, *out; unsigned long maxsize; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, pack_compression_level); maxsize = git_deflate_bound(&stream, size); @@ -153,7 +152,6 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct sha1fi unsigned char obuf[1024 * 16]; unsigned long olen = 0; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, pack_compression_level); for (;;) { @@ -2613,6 +2611,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) { int use_internal_rev_list = 0; int thin = 0; + int shallow = 0; int all_progress_implied = 0; struct argv_array rp = ARGV_ARRAY_INIT; int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0; @@ -2677,6 +2676,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) PARSE_OPT_OPTARG, option_parse_unpack_unreachable }, OPT_BOOL(0, "thin", &thin, N_("create thin packs")), + OPT_BOOL(0, "shallow", &shallow, + N_("create packs suitable for shallow fetches")), OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep, N_("ignore packs that have companion .keep file")), OPT_INTEGER(0, "compression", &pack_compression_level, @@ -2711,7 +2712,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) argv_array_push(&rp, "pack-objects"); if (thin) { use_internal_rev_list = 1; - argv_array_push(&rp, "--objects-edge"); + argv_array_push(&rp, shallow + ? "--objects-edge-aggressive" + : "--objects-edge"); } else argv_array_push(&rp, "--objects"); diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 649c3aaa93..d0532f66b1 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -11,7 +11,7 @@ #define BLKSIZE 512 static const char pack_redundant_usage[] = -"git pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>"; +"git pack-redundant [--verbose] [--alt-odb] (--all | <filename.pack>...)"; static int load_all_packs, verbose, alt_odb; diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c index b20b1ec4c1..39f9a55d16 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -3,7 +3,7 @@ #include "refs.h" static char const * const pack_refs_usage[] = { - N_("git pack-refs [options]"), + N_("git pack-refs [<options>]"), NULL }; diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index f24a2c2bdc..7cf900ea07 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -4,7 +4,7 @@ #include "parse-options.h" static const char * const prune_packed_usage[] = { - N_("git prune-packed [-n|--dry-run] [-q|--quiet]"), + N_("git prune-packed [-n | --dry-run] [-q | --quiet]"), NULL }; diff --git a/builtin/push.c b/builtin/push.c index 7aedf6f533..57c138bd7b 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -473,13 +473,22 @@ static int option_parse_recurse_submodules(const struct option *opt, static int git_push_config(const char *k, const char *v, void *cb) { - struct wt_status *s = cb; + int *flags = cb; int status; status = git_gpg_config(k, v, NULL); if (status) return status; - return git_default_config(k, v, s); + + if (!strcmp(k, "push.followtags")) { + if (git_config_bool(k, v)) + *flags |= TRANSPORT_PUSH_FOLLOW_TAGS; + else + *flags &= ~TRANSPORT_PUSH_FOLLOW_TAGS; + return 0; + } + + return git_default_config(k, v, NULL); } int cmd_push(int argc, const char **argv, const char *prefix) @@ -503,7 +512,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) 0, CAS_OPT_NAME, &cas, N_("refname>:<expect"), N_("require old value of ref to be at this value"), PARSE_OPT_OPTARG, parseopt_push_cas_option }, - { OPTION_CALLBACK, 0, "recurse-submodules", &flags, N_("check"), + { OPTION_CALLBACK, 0, "recurse-submodules", &flags, "check|on-demand", N_("control recursive pushing of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")), @@ -518,11 +527,12 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"), TRANSPORT_PUSH_FOLLOW_TAGS), OPT_BIT(0, "signed", &flags, N_("GPG sign the push"), TRANSPORT_PUSH_CERT), + OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC), OPT_END() }; packet_trace_identity("push"); - git_config(git_push_config, NULL); + git_config(git_push_config, &flags); argc = parse_options(argc, argv, prefix, options, push_usage, 0); if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index e908d079ba..70e9ce5f96 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -26,7 +26,8 @@ enum deny_action { DENY_UNCONFIGURED, DENY_IGNORE, DENY_WARN, - DENY_REFUSE + DENY_REFUSE, + DENY_UPDATE_INSTEAD }; static int deny_deletes; @@ -37,9 +38,11 @@ static int receive_fsck_objects = -1; static int transfer_fsck_objects = -1; static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; +static int advertise_atomic_push = 1; static int unpack_limit = 100; static int report_status; static int use_sideband; +static int use_atomic; static int quiet; static int prefer_ofs_delta = 1; static int auto_update_server_info; @@ -66,6 +69,7 @@ static const char *NONCE_SLOP = "SLOP"; static const char *nonce_status; static long nonce_stamp_slop; static unsigned long nonce_stamp_slop_limit; +static struct ref_transaction *transaction; static enum deny_action parse_deny_action(const char *var, const char *value) { @@ -76,6 +80,8 @@ static enum deny_action parse_deny_action(const char *var, const char *value) return DENY_WARN; if (!strcasecmp(value, "refuse")) return DENY_REFUSE; + if (!strcasecmp(value, "updateinstead")) + return DENY_UPDATE_INSTEAD; } if (git_config_bool(var, value)) return DENY_REFUSE; @@ -157,6 +163,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "receive.advertiseatomic") == 0) { + advertise_atomic_push = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, cb); } @@ -172,6 +183,8 @@ static void show_ref(const char *path, const unsigned char *sha1) strbuf_addstr(&cap, "report-status delete-refs side-band-64k quiet"); + if (advertise_atomic_push) + strbuf_addstr(&cap, " atomic"); if (prefer_ofs_delta) strbuf_addstr(&cap, " ofs-delta"); if (push_cert_nonce) @@ -730,11 +743,109 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si) return 0; } +static const char *push_to_deploy(unsigned char *sha1, + struct argv_array *env, + const char *work_tree) +{ + const char *update_refresh[] = { + "update-index", "-q", "--ignore-submodules", "--refresh", NULL + }; + const char *diff_files[] = { + "diff-files", "--quiet", "--ignore-submodules", "--", NULL + }; + const char *diff_index[] = { + "diff-index", "--quiet", "--cached", "--ignore-submodules", + "HEAD", "--", NULL + }; + const char *read_tree[] = { + "read-tree", "-u", "-m", NULL, NULL + }; + struct child_process child = CHILD_PROCESS_INIT; + + child.argv = update_refresh; + child.env = env->argv; + child.dir = work_tree; + child.no_stdin = 1; + child.stdout_to_stderr = 1; + child.git_cmd = 1; + if (run_command(&child)) + return "Up-to-date check failed"; + + /* run_command() does not clean up completely; reinitialize */ + child_process_init(&child); + child.argv = diff_files; + child.env = env->argv; + child.dir = work_tree; + child.no_stdin = 1; + child.stdout_to_stderr = 1; + child.git_cmd = 1; + if (run_command(&child)) + return "Working directory has unstaged changes"; + + child_process_init(&child); + child.argv = diff_index; + child.env = env->argv; + child.no_stdin = 1; + child.no_stdout = 1; + child.stdout_to_stderr = 0; + child.git_cmd = 1; + if (run_command(&child)) + return "Working directory has staged changes"; + + read_tree[3] = sha1_to_hex(sha1); + child_process_init(&child); + child.argv = read_tree; + child.env = env->argv; + child.dir = work_tree; + child.no_stdin = 1; + child.no_stdout = 1; + child.stdout_to_stderr = 0; + child.git_cmd = 1; + if (run_command(&child)) + return "Could not update working tree to new HEAD"; + + return NULL; +} + +static const char *push_to_checkout_hook = "push-to-checkout"; + +static const char *push_to_checkout(unsigned char *sha1, + struct argv_array *env, + const char *work_tree) +{ + argv_array_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree)); + if (run_hook_le(env->argv, push_to_checkout_hook, + sha1_to_hex(sha1), NULL)) + return "push-to-checkout hook declined"; + else + return NULL; +} + +static const char *update_worktree(unsigned char *sha1) +{ + const char *retval; + const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : ".."; + struct argv_array env = ARGV_ARRAY_INIT; + + if (is_bare_repository()) + return "denyCurrentBranch = updateInstead needs a worktree"; + + argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir())); + + if (!find_hook(push_to_checkout_hook)) + retval = push_to_deploy(sha1, &env, work_tree); + else + retval = push_to_checkout(sha1, &env, work_tree); + + argv_array_clear(&env); + return retval; +} + static const char *update(struct command *cmd, struct shallow_info *si) { const char *name = cmd->ref_name; struct strbuf namespaced_name_buf = STRBUF_INIT; - const char *namespaced_name; + const char *namespaced_name, *ret; unsigned char *old_sha1 = cmd->old_sha1; unsigned char *new_sha1 = cmd->new_sha1; @@ -760,6 +871,11 @@ static const char *update(struct command *cmd, struct shallow_info *si) if (deny_current_branch == DENY_UNCONFIGURED) refuse_unconfigured_deny(); return "branch is currently checked out"; + case DENY_UPDATE_INSTEAD: + ret = update_worktree(new_sha1); + if (ret) + return ret; + break; } } @@ -784,10 +900,13 @@ static const char *update(struct command *cmd, struct shallow_info *si) break; case DENY_REFUSE: case DENY_UNCONFIGURED: + case DENY_UPDATE_INSTEAD: if (deny_delete_current == DENY_UNCONFIGURED) refuse_unconfigured_deny_delete_current(); rp_error("refusing to delete the current branch: %s", name); return "deletion of the current branch prohibited"; + default: + return "Invalid denyDeleteCurrent setting"; } } } @@ -821,6 +940,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) } if (is_null_sha1(new_sha1)) { + struct strbuf err = STRBUF_INIT; if (!parse_object(old_sha1)) { old_sha1 = NULL; if (ref_exists(name)) { @@ -830,35 +950,35 @@ static const char *update(struct command *cmd, struct shallow_info *si) cmd->did_not_exist = 1; } } - if (delete_ref(namespaced_name, old_sha1, 0)) { - rp_error("failed to delete %s", name); + if (ref_transaction_delete(transaction, + namespaced_name, + old_sha1, + 0, "push", &err)) { + rp_error("%s", err.buf); + strbuf_release(&err); return "failed to delete"; } + strbuf_release(&err); return NULL; /* good */ } else { struct strbuf err = STRBUF_INIT; - struct ref_transaction *transaction; - if (shallow_update && si->shallow_ref[cmd->index] && update_shallow_ref(cmd, si)) return "shallow error"; - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, namespaced_name, - new_sha1, old_sha1, 0, 1, "push", - &err) || - ref_transaction_commit(transaction, &err)) { - ref_transaction_free(transaction); - + if (ref_transaction_update(transaction, + namespaced_name, + new_sha1, old_sha1, + 0, "push", + &err)) { rp_error("%s", err.buf); strbuf_release(&err); + return "failed to update ref"; } - - ref_transaction_free(transaction); strbuf_release(&err); + return NULL; /* good */ } } @@ -964,7 +1084,7 @@ static void check_aliased_updates(struct command *commands) string_list_append(&ref_list, cmd->ref_name); item->util = (void *)cmd; } - sort_string_list(&ref_list); + string_list_sort(&ref_list); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) @@ -1042,11 +1162,105 @@ static void reject_updates_to_hidden(struct command *commands) } } +static int should_process_cmd(struct command *cmd) +{ + return !cmd->error_string && !cmd->skip_update; +} + +static void warn_if_skipped_connectivity_check(struct command *commands, + struct shallow_info *si) +{ + struct command *cmd; + int checked_connectivity = 1; + + for (cmd = commands; cmd; cmd = cmd->next) { + if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) { + error("BUG: connectivity check has not been run on ref %s", + cmd->ref_name); + checked_connectivity = 0; + } + } + if (!checked_connectivity) + die("BUG: connectivity check skipped???"); +} + +static void execute_commands_non_atomic(struct command *commands, + struct shallow_info *si) +{ + struct command *cmd; + struct strbuf err = STRBUF_INIT; + + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + transaction = ref_transaction_begin(&err); + if (!transaction) { + rp_error("%s", err.buf); + strbuf_reset(&err); + cmd->error_string = "transaction failed to start"; + continue; + } + + cmd->error_string = update(cmd, si); + + if (!cmd->error_string + && ref_transaction_commit(transaction, &err)) { + rp_error("%s", err.buf); + strbuf_reset(&err); + cmd->error_string = "failed to update ref"; + } + ref_transaction_free(transaction); + } + strbuf_release(&err); +} + +static void execute_commands_atomic(struct command *commands, + struct shallow_info *si) +{ + struct command *cmd; + struct strbuf err = STRBUF_INIT; + const char *reported_error = "atomic push failure"; + + transaction = ref_transaction_begin(&err); + if (!transaction) { + rp_error("%s", err.buf); + strbuf_reset(&err); + reported_error = "transaction failed to start"; + goto failure; + } + + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + cmd->error_string = update(cmd, si); + + if (cmd->error_string) + goto failure; + } + + if (ref_transaction_commit(transaction, &err)) { + rp_error("%s", err.buf); + reported_error = "atomic transaction failed"; + goto failure; + } + goto cleanup; + +failure: + for (cmd = commands; cmd; cmd = cmd->next) + if (!cmd->error_string) + cmd->error_string = reported_error; + +cleanup: + ref_transaction_free(transaction); + strbuf_release(&err); +} + 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; @@ -1077,27 +1291,13 @@ static void execute_commands(struct command *commands, free(head_name_to_free); head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL); - checked_connectivity = 1; - for (cmd = commands; cmd; cmd = cmd->next) { - if (cmd->error_string) - continue; - - if (cmd->skip_update) - continue; - - 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 (use_atomic) + execute_commands_atomic(commands, si); + else + execute_commands_non_atomic(commands, si); - if (shallow_update && !checked_connectivity) - error("BUG: run 'git fsck' for safety.\n" - "If there are errors, try to remove " - "the reported refs above"); + if (shallow_update) + warn_if_skipped_connectivity_check(commands, si); } static struct command **queue_command(struct command **tail, @@ -1179,6 +1379,9 @@ static struct command *read_head_info(struct sha1_array *shallow) use_sideband = LARGE_PACKET_MAX; if (parse_feature_request(feature_list, "quiet")) quiet = 1; + if (advertise_atomic_push + && parse_feature_request(feature_list, "atomic")) + use_atomic = 1; } if (!strcmp(line, "push-cert")) { diff --git a/builtin/reflog.c b/builtin/reflog.c index 2d85d260ca..8182b648b9 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -8,32 +8,24 @@ #include "revision.h" #include "reachable.h" -/* - * reflog expire - */ - +/* NEEDSWORK: switch to using parse_options */ static const char reflog_expire_usage[] = -"git reflog expire [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>..."; +"git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all] <refs>..."; static const char reflog_delete_usage[] = -"git reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>..."; +"git reflog delete [--rewrite] [--updateref] [--dry-run | -n] [--verbose] <refs>..."; static unsigned long default_reflog_expire; static unsigned long default_reflog_expire_unreachable; struct cmd_reflog_expire_cb { struct rev_info revs; - int dry_run; int stalefix; - int rewrite; - int updateref; - int verbose; unsigned long expire_total; unsigned long expire_unreachable; int recno; }; -struct expire_reflog_cb { - FILE *newlog; +struct expire_reflog_policy_cb { enum { UE_NORMAL, UE_ALWAYS, @@ -41,14 +33,16 @@ struct expire_reflog_cb { } unreachable_expire_kind; struct commit_list *mark_list; unsigned long mark_limit; - struct cmd_reflog_expire_cb *cmd; - unsigned char last_kept_sha1[20]; + struct cmd_reflog_expire_cb cmd; + struct commit *tip_commit; + struct commit_list *tips; }; struct collected_reflog { unsigned char sha1[20]; char reflog[FLEX_ARRAY]; }; + struct collect_reflog_cb { struct collected_reflog **e; int alloc; @@ -220,7 +214,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1) * the expire_limit and queue them back, so that the caller can call * us again to restart the traversal with longer expire_limit. */ -static void mark_reachable(struct expire_reflog_cb *cb) +static void mark_reachable(struct expire_reflog_policy_cb *cb) { struct commit *commit; struct commit_list *pending; @@ -259,7 +253,7 @@ static void mark_reachable(struct expire_reflog_cb *cb) cb->mark_list = leftover; } -static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1) +static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, unsigned char *sha1) { /* * We may or may not have the commit yet - if not, look it @@ -288,55 +282,39 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig return !(commit->object.flags & REACHABLE); } -static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, - const char *email, unsigned long timestamp, int tz, - const char *message, void *cb_data) +/* + * Return true iff the specified reflog entry should be expired. + */ +static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) { - struct expire_reflog_cb *cb = cb_data; + struct expire_reflog_policy_cb *cb = cb_data; struct commit *old, *new; - if (timestamp < cb->cmd->expire_total) - goto prune; - - if (cb->cmd->rewrite) - osha1 = cb->last_kept_sha1; + if (timestamp < cb->cmd.expire_total) + return 1; old = new = NULL; - if (cb->cmd->stalefix && + if (cb->cmd.stalefix && (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1))) - goto prune; + return 1; - if (timestamp < cb->cmd->expire_unreachable) { + if (timestamp < cb->cmd.expire_unreachable) { if (cb->unreachable_expire_kind == UE_ALWAYS) - goto prune; + return 1; if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1)) - goto prune; + return 1; } - if (cb->cmd->recno && --(cb->cmd->recno) == 0) - goto prune; - - if (cb->newlog) { - char sign = (tz < 0) ? '-' : '+'; - int zone = (tz < 0) ? (-tz) : tz; - fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s", - sha1_to_hex(osha1), sha1_to_hex(nsha1), - email, timestamp, sign, zone, - message); - hashcpy(cb->last_kept_sha1, nsha1); - } - if (cb->cmd->verbose) - printf("keep %s", message); - return 0; - prune: - if (!cb->newlog) - printf("would prune %s", message); - else if (cb->cmd->verbose) - printf("prune %s", message); + if (cb->cmd.recno && --(cb->cmd.recno) == 0) + return 1; + return 0; } -static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data) +static int push_tip_to_list(const char *refname, const unsigned char *sha1, + int flags, void *cb_data) { struct commit_list **list = cb_data; struct commit *tip_commit; @@ -349,104 +327,56 @@ static int push_tip_to_list(const char *refname, const unsigned char *sha1, int return 0; } -static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data) +static void reflog_expiry_prepare(const char *refname, + const unsigned char *sha1, + void *cb_data) { - struct cmd_reflog_expire_cb *cmd = cb_data; - struct expire_reflog_cb cb; - struct ref_lock *lock; - char *log_file, *newlog_path = NULL; - struct commit *tip_commit; - struct commit_list *tips; - int status = 0; - - memset(&cb, 0, sizeof(cb)); - - /* - * we take the lock for the ref itself to prevent it from - * getting updated. - */ - lock = lock_any_ref_for_update(ref, sha1, 0, NULL); - if (!lock) - return error("cannot lock ref '%s'", ref); - log_file = git_pathdup("logs/%s", ref); - if (!reflog_exists(ref)) - goto finish; - if (!cmd->dry_run) { - newlog_path = git_pathdup("logs/%s.lock", ref); - cb.newlog = fopen(newlog_path, "w"); - } + struct expire_reflog_policy_cb *cb = cb_data; - cb.cmd = cmd; - - if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) { - tip_commit = NULL; - cb.unreachable_expire_kind = UE_HEAD; + if (!cb->cmd.expire_unreachable || !strcmp(refname, "HEAD")) { + cb->tip_commit = NULL; + cb->unreachable_expire_kind = UE_HEAD; } else { - tip_commit = lookup_commit_reference_gently(sha1, 1); - if (!tip_commit) - cb.unreachable_expire_kind = UE_ALWAYS; + cb->tip_commit = lookup_commit_reference_gently(sha1, 1); + if (!cb->tip_commit) + cb->unreachable_expire_kind = UE_ALWAYS; else - cb.unreachable_expire_kind = UE_NORMAL; + cb->unreachable_expire_kind = UE_NORMAL; } - if (cmd->expire_unreachable <= cmd->expire_total) - cb.unreachable_expire_kind = UE_ALWAYS; + if (cb->cmd.expire_unreachable <= cb->cmd.expire_total) + cb->unreachable_expire_kind = UE_ALWAYS; - cb.mark_list = NULL; - tips = NULL; - if (cb.unreachable_expire_kind != UE_ALWAYS) { - if (cb.unreachable_expire_kind == UE_HEAD) { + cb->mark_list = NULL; + cb->tips = NULL; + if (cb->unreachable_expire_kind != UE_ALWAYS) { + if (cb->unreachable_expire_kind == UE_HEAD) { struct commit_list *elem; - for_each_ref(push_tip_to_list, &tips); - for (elem = tips; elem; elem = elem->next) - commit_list_insert(elem->item, &cb.mark_list); + for_each_ref(push_tip_to_list, &cb->tips); + for (elem = cb->tips; elem; elem = elem->next) + commit_list_insert(elem->item, &cb->mark_list); } else { - commit_list_insert(tip_commit, &cb.mark_list); + commit_list_insert(cb->tip_commit, &cb->mark_list); } - cb.mark_limit = cmd->expire_total; - mark_reachable(&cb); + cb->mark_limit = cb->cmd.expire_total; + mark_reachable(cb); } +} - for_each_reflog_ent(ref, expire_reflog_ent, &cb); +static void reflog_expiry_cleanup(void *cb_data) +{ + struct expire_reflog_policy_cb *cb = cb_data; - if (cb.unreachable_expire_kind != UE_ALWAYS) { - if (cb.unreachable_expire_kind == UE_HEAD) { + if (cb->unreachable_expire_kind != UE_ALWAYS) { + if (cb->unreachable_expire_kind == UE_HEAD) { struct commit_list *elem; - for (elem = tips; elem; elem = elem->next) + for (elem = cb->tips; elem; elem = elem->next) clear_commit_marks(elem->item, REACHABLE); - free_commit_list(tips); + free_commit_list(cb->tips); } else { - clear_commit_marks(tip_commit, REACHABLE); + clear_commit_marks(cb->tip_commit, REACHABLE); } } - finish: - if (cb.newlog) { - if (fclose(cb.newlog)) { - status |= error("%s: %s", strerror(errno), - newlog_path); - unlink(newlog_path); - } else if (cmd->updateref && - (write_in_full(lock->lock_fd, - sha1_to_hex(cb.last_kept_sha1), 40) != 40 || - write_str_in_full(lock->lock_fd, "\n") != 1 || - close_ref(lock) < 0)) { - status |= error("Couldn't write %s", - lock->lk->filename.buf); - unlink(newlog_path); - } else if (rename(newlog_path, log_file)) { - status |= error("cannot rename %s to %s", - newlog_path, log_file); - unlink(newlog_path); - } else if (cmd->updateref && commit_ref(lock)) { - status |= error("Couldn't set %s", lock->ref_name); - } else { - adjust_shared_perm(log_file); - } - } - free(newlog_path); - free(log_file); - unlock_ref(lock); - return status; } static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data) @@ -590,10 +520,11 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) { - struct cmd_reflog_expire_cb cb; + struct expire_reflog_policy_cb cb; unsigned long now = time(NULL); int i, status, do_all; int explicit_expiry = 0; + unsigned int flags = 0; default_reflog_expire_unreachable = now - 30 * 24 * 3600; default_reflog_expire = now - 90 * 24 * 3600; @@ -603,33 +534,33 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) do_all = status = 0; memset(&cb, 0, sizeof(cb)); - cb.expire_total = default_reflog_expire; - cb.expire_unreachable = default_reflog_expire_unreachable; + cb.cmd.expire_total = default_reflog_expire; + cb.cmd.expire_unreachable = default_reflog_expire_unreachable; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) - cb.dry_run = 1; + flags |= EXPIRE_REFLOGS_DRY_RUN; else if (starts_with(arg, "--expire=")) { - if (parse_expiry_date(arg + 9, &cb.expire_total)) + if (parse_expiry_date(arg + 9, &cb.cmd.expire_total)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_TOTAL; } else if (starts_with(arg, "--expire-unreachable=")) { - if (parse_expiry_date(arg + 21, &cb.expire_unreachable)) + if (parse_expiry_date(arg + 21, &cb.cmd.expire_unreachable)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_UNREACH; } else if (!strcmp(arg, "--stale-fix")) - cb.stalefix = 1; + cb.cmd.stalefix = 1; else if (!strcmp(arg, "--rewrite")) - cb.rewrite = 1; + flags |= EXPIRE_REFLOGS_REWRITE; else if (!strcmp(arg, "--updateref")) - cb.updateref = 1; + flags |= EXPIRE_REFLOGS_UPDATE_REF; else if (!strcmp(arg, "--all")) do_all = 1; else if (!strcmp(arg, "--verbose")) - cb.verbose = 1; + flags |= EXPIRE_REFLOGS_VERBOSE; else if (!strcmp(arg, "--")) { i++; break; @@ -645,12 +576,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) * even in older repository. We cannot trust what's reachable * from reflog if the repository was pruned with older git. */ - if (cb.stalefix) { - init_revisions(&cb.revs, prefix); - if (cb.verbose) + if (cb.cmd.stalefix) { + init_revisions(&cb.cmd.revs, prefix); + if (flags & EXPIRE_REFLOGS_VERBOSE) printf("Marking reachable objects..."); - mark_reachable_objects(&cb.revs, 0, 0, NULL); - if (cb.verbose) + mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL); + if (flags & EXPIRE_REFLOGS_VERBOSE) putchar('\n'); } @@ -662,8 +593,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) for_each_reflog(collect_reflog, &collected); for (i = 0; i < collected.nr; i++) { struct collected_reflog *e = collected.e[i]; - set_reflog_expiry_param(&cb, explicit_expiry, e->reflog); - status |= expire_reflog(e->reflog, e->sha1, 0, &cb); + set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); + status |= reflog_expire(e->reflog, e->sha1, flags, + reflog_expiry_prepare, + should_expire_reflog_ent, + reflog_expiry_cleanup, + &cb); free(e); } free(collected.e); @@ -676,8 +611,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) status |= error("%s points nowhere!", argv[i]); continue; } - set_reflog_expiry_param(&cb, explicit_expiry, ref); - status |= expire_reflog(ref, sha1, 0, &cb); + set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref); + status |= reflog_expire(ref, sha1, flags, + reflog_expiry_prepare, + should_expire_reflog_ent, + reflog_expiry_cleanup, + &cb); } return status; } @@ -686,29 +625,30 @@ static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { - struct cmd_reflog_expire_cb *cb = cb_data; - if (!cb->expire_total || timestamp < cb->expire_total) - cb->recno++; + struct expire_reflog_policy_cb *cb = cb_data; + if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total) + cb->cmd.recno++; return 0; } static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) { - struct cmd_reflog_expire_cb cb; + struct expire_reflog_policy_cb cb; int i, status = 0; + unsigned int flags = 0; memset(&cb, 0, sizeof(cb)); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) - cb.dry_run = 1; + flags |= EXPIRE_REFLOGS_DRY_RUN; else if (!strcmp(arg, "--rewrite")) - cb.rewrite = 1; + flags |= EXPIRE_REFLOGS_REWRITE; else if (!strcmp(arg, "--updateref")) - cb.updateref = 1; + flags |= EXPIRE_REFLOGS_UPDATE_REF; else if (!strcmp(arg, "--verbose")) - cb.verbose = 1; + flags |= EXPIRE_REFLOGS_VERBOSE; else if (!strcmp(arg, "--")) { i++; break; @@ -740,15 +680,19 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) recno = strtoul(spec + 2, &ep, 10); if (*ep == '}') { - cb.recno = -recno; + cb.cmd.recno = -recno; for_each_reflog_ent(ref, count_reflog_ent, &cb); } else { - cb.expire_total = approxidate(spec + 2); + cb.cmd.expire_total = approxidate(spec + 2); for_each_reflog_ent(ref, count_reflog_ent, &cb); - cb.expire_total = 0; + cb.cmd.expire_total = 0; } - status |= expire_reflog(ref, sha1, 0, &cb); + status |= reflog_expire(ref, sha1, flags, + reflog_expiry_prepare, + should_expire_reflog_ent, + reflog_expiry_cleanup, + &cb); free(ref); } return status; diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92a37..5d3ab906bc 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -10,10 +10,10 @@ static const char * const builtin_remote_usage[] = { N_("git remote [-v | --verbose]"), - N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>"), + N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>"), N_("git remote rename <old> <new>"), N_("git remote remove <name>"), - N_("git remote set-head <name> (-a | --auto | -d | --delete |<branch>)"), + N_("git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"), N_("git remote [-v | --verbose] show [-n] <name>"), N_("git remote prune [-n | --dry-run] <name>"), N_("git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"), @@ -180,7 +180,9 @@ static int add(int argc, const char **argv) url = argv[1]; remote = remote_get(name); - if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) || + if (remote && (remote->url_nr > 1 || + (strcmp(name, remote->url[0]) && + strcmp(url, remote->url[0])) || remote->fetch_refspec_nr)) die(_("remote %s already exists."), name); @@ -352,9 +354,9 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat free_refs(stale_refs); free_refs(fetch_map); - sort_string_list(&states->new); - sort_string_list(&states->tracked); - sort_string_list(&states->stale); + string_list_sort(&states->new); + string_list_sort(&states->tracked); + string_list_sort(&states->stale); return 0; } @@ -750,16 +752,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -914,7 +911,7 @@ static int get_remote_ref_states(const char *name, get_push_ref_states(remote_refs, states); } else { for_each_ref(append_ref_to_tracked_list, states); - sort_string_list(&states->tracked); + string_list_sort(&states->tracked); get_push_ref_states_noquery(states); } @@ -1133,7 +1130,7 @@ static int show_all(void) if (!result) { int i; - sort_string_list(&list); + string_list_sort(&list); for (i = 0; i < list.nr; i++) { struct string_list_item *item = list.items + i; if (verbose) @@ -1314,10 +1311,10 @@ static int set_head(int argc, const char **argv) static int prune_remote(const char *remote, int dry_run) { - int result = 0, i; + int result = 0; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; + struct string_list refs_to_prune = STRING_LIST_INIT_NODUP; + struct string_list_item *item; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,30 +1322,30 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); - if (states.stale.nr) { - printf_ln(_("Pruning %s"), remote); - printf_ln(_("URL: %s"), - states.remote->url_nr - ? states.remote->url[0] - : _("(no URL)")); - - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; - if (!dry_run) { - struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) - result |= error("%s", err.buf); - strbuf_release(&err); - } - free(delete_refs); + if (!states.stale.nr) { + free_remote_ref_states(&states); + return 0; } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; + printf_ln(_("Pruning %s"), remote); + printf_ln(_("URL: %s"), + states.remote->url_nr + ? states.remote->url[0] + : _("(no URL)")); + + for_each_string_list_item(item, &states.stale) + string_list_append(&refs_to_prune, item->util); + string_list_sort(&refs_to_prune); + + if (!dry_run) { + struct strbuf err = STRBUF_INIT; + if (repack_without_refs(&refs_to_prune, &err)) + result |= error("%s", err.buf); + strbuf_release(&err); + } - string_list_insert(&delete_refs_list, refname); + for_each_string_list_item(item, &states.stale) { + const char *refname = item->util; if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1361,9 +1358,9 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune); + string_list_clear(&refs_to_prune, 0); free_remote_ref_states(&states); return result; } diff --git a/builtin/repack.c b/builtin/repack.c index 28456206c5..28fbc7099a 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -14,7 +14,7 @@ static int write_bitmaps; static char *packdir, *packtmp; static const char *const git_repack_usage[] = { - N_("git repack [options]"), + N_("git repack [<options>]"), NULL }; @@ -135,7 +135,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix) }; struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; - struct argv_array cmd_args = ARGV_ARRAY_INIT; struct string_list names = STRING_LIST_INIT_DUP; struct string_list rollback = STRING_LIST_INIT_NODUP; struct string_list existing_packs = STRING_LIST_INIT_DUP; @@ -202,56 +201,55 @@ int cmd_repack(int argc, const char **argv, const char *prefix) sigchain_push_common(remove_pack_on_signal); - argv_array_push(&cmd_args, "pack-objects"); - argv_array_push(&cmd_args, "--keep-true-parents"); + argv_array_push(&cmd.args, "pack-objects"); + argv_array_push(&cmd.args, "--keep-true-parents"); 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"); - argv_array_push(&cmd_args, "--indexed-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"); + argv_array_push(&cmd.args, "--indexed-objects"); if (window) - argv_array_pushf(&cmd_args, "--window=%s", window); + argv_array_pushf(&cmd.args, "--window=%s", window); if (window_memory) - argv_array_pushf(&cmd_args, "--window-memory=%s", window_memory); + argv_array_pushf(&cmd.args, "--window-memory=%s", window_memory); if (depth) - argv_array_pushf(&cmd_args, "--depth=%s", depth); + argv_array_pushf(&cmd.args, "--depth=%s", depth); if (max_pack_size) - argv_array_pushf(&cmd_args, "--max-pack-size=%s", max_pack_size); + argv_array_pushf(&cmd.args, "--max-pack-size=%s", max_pack_size); if (no_reuse_delta) - argv_array_pushf(&cmd_args, "--no-reuse-delta"); + argv_array_pushf(&cmd.args, "--no-reuse-delta"); if (no_reuse_object) - argv_array_pushf(&cmd_args, "--no-reuse-object"); + argv_array_pushf(&cmd.args, "--no-reuse-object"); if (write_bitmaps) - argv_array_push(&cmd_args, "--write-bitmap-index"); + argv_array_push(&cmd.args, "--write-bitmap-index"); if (pack_everything & ALL_INTO_ONE) { get_non_kept_pack_filenames(&existing_packs); if (existing_packs.nr && delete_redundant) { if (unpack_unreachable) - argv_array_pushf(&cmd_args, + argv_array_pushf(&cmd.args, "--unpack-unreachable=%s", unpack_unreachable); else if (pack_everything & LOOSEN_UNREACHABLE) - argv_array_push(&cmd_args, + argv_array_push(&cmd.args, "--unpack-unreachable"); } } else { - argv_array_push(&cmd_args, "--unpacked"); - argv_array_push(&cmd_args, "--incremental"); + argv_array_push(&cmd.args, "--unpacked"); + argv_array_push(&cmd.args, "--incremental"); } if (local) - argv_array_push(&cmd_args, "--local"); + argv_array_push(&cmd.args, "--local"); if (quiet) - argv_array_push(&cmd_args, "--quiet"); + argv_array_push(&cmd.args, "--quiet"); if (delta_base_offset) - argv_array_push(&cmd_args, "--delta-base-offset"); + argv_array_push(&cmd.args, "--delta-base-offset"); - argv_array_push(&cmd_args, packtmp); + argv_array_push(&cmd.args, packtmp); - cmd.argv = cmd_args.argv; cmd.git_cmd = 1; cmd.out = -1; cmd.no_stdin = 1; @@ -270,7 +268,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix) ret = finish_command(&cmd); if (ret) return ret; - argv_array_clear(&cmd_args); if (!names.nr && !quiet) printf("Nothing new to pack.\n"); @@ -379,7 +376,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant) { int opts = 0; - sort_string_list(&names); + string_list_sort(&names); for_each_string_list_item(item, &existing_packs) { char *sha1; size_t len = strlen(item->string); diff --git a/builtin/replace.c b/builtin/replace.c index 85d39b58d8..54bf01acb4 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -172,7 +172,7 @@ static int replace_object_sha1(const char *object_ref, transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, ref, repl, prev, - 0, 1, NULL, &err) || + 0, NULL, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); diff --git a/builtin/rerere.c b/builtin/rerere.c index 98eb8c5404..7afadd2ead 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -9,7 +9,7 @@ #include "pathspec.h" static const char * const rerere_usage[] = { - N_("git rerere [clear | forget path... | status | remaining | diff | gc]"), + N_("git rerere [clear | forget <path>... | status | remaining | diff | gc]"), NULL, }; diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 35d3c43ed6..3626c61da6 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -279,7 +279,7 @@ static int try_difference(const char *arg) struct commit *a, *b; a = lookup_commit_reference(sha1); b = lookup_commit_reference(end); - exclude = get_merge_bases(a, b, 1); + exclude = get_merge_bases(a, b); while (exclude) { struct commit_list *n = exclude->next; show_rev(REVERSED, @@ -358,7 +358,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) { static int keep_dashdash = 0, stop_at_non_option = 0; static char const * const parseopt_usage[] = { - N_("git rev-parse --parseopt [options] -- [<args>...]"), + N_("git rev-parse --parseopt [<options>] -- [<args>...]"), NULL }; static struct option parseopt_opts[] = { @@ -496,9 +496,9 @@ static void die_no_single_rev(int quiet) } static const char builtin_rev_parse_usage[] = -N_("git rev-parse --parseopt [options] -- [<args>...]\n" +N_("git rev-parse --parseopt [<options>] -- [<args>...]\n" " or: git rev-parse --sq-quote [<arg>...]\n" - " or: git rev-parse [options] [<arg>...]\n" + " or: git rev-parse [<options>] [<arg>...]\n" "\n" "Run \"git rev-parse --parseopt -h\" for more information on the first usage."); diff --git a/builtin/revert.c b/builtin/revert.c index f9ed5bd5d0..56a2c36669 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -19,13 +19,13 @@ */ static const char * const revert_usage[] = { - N_("git revert [options] <commit-ish>..."), + N_("git revert [<options>] <commit-ish>..."), N_("git revert <subcommand>"), NULL }; static const char * const cherry_pick_usage[] = { - N_("git cherry-pick [options] <commit-ish>..."), + N_("git cherry-pick [<options>] <commit-ish>..."), N_("git cherry-pick <subcommand>"), NULL }; diff --git a/builtin/rm.c b/builtin/rm.c index d8a9c86dd1..3304bff42a 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -14,7 +14,7 @@ #include "pathspec.h" static const char * const builtin_rm_usage[] = { - N_("git rm [options] [--] <file>..."), + N_("git rm [<options>] [--] <file>..."), NULL }; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index b564a77845..b961e5ae78 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -13,7 +13,7 @@ #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" +"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]\n" " --all and explicit <ref> specification are mutually exclusive."; static struct send_pack_args args; @@ -170,6 +170,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.use_thin_pack = 1; continue; } + if (!strcmp(arg, "--atomic")) { + args.atomic = 1; + continue; + } if (!strcmp(arg, "--stateless-rpc")) { args.stateless_rpc = 1; continue; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 4b7e53623f..c0bab6aaa9 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -10,7 +10,7 @@ #include "parse-options.h" static char const * const shortlog_usage[] = { - N_("git shortlog [<options>] [<revision range>] [[--] [<path>...]]"), + N_("git shortlog [<options>] [<revision-range>] [[--] [<path>...]]"), NULL }; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 270e39c6c1..f3fb5fb2bf 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -6,8 +6,11 @@ #include "parse-options.h" static const char* show_branch_usage[] = { - N_("git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"), - N_("git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"), + N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" + " [--current] [--color[=<when>] | --no-color] [--sparse]\n" + " [--more=<n> | --list | --independent | --merge-base]\n" + " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"), + N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"), NULL }; diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 5ba1f30838..afb10309d6 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -7,7 +7,7 @@ #include "parse-options.h" static const char * const show_ref_usage[] = { - N_("git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] "), + N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"), N_("git show-ref --exclude-existing[=pattern] < ref-list"), NULL }; diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 29fb3f1c20..ce0fde705c 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -4,8 +4,8 @@ #include "parse-options.h" static const char * const git_symbolic_ref_usage[] = { - N_("git symbolic-ref [options] name [ref]"), - N_("git symbolic-ref -d [-q] name"), + N_("git symbolic-ref [<options>] <name> [<ref>]"), + N_("git symbolic-ref -d [-q] <name>"), NULL }; diff --git a/builtin/tag.c b/builtin/tag.c index e633f4efdb..6f07ac6b93 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -19,9 +19,9 @@ #include "column.h" static const char * const git_tag_usage[] = { - N_("git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"), + N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"), N_("git tag -d <tagname>..."), - N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] " + N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]" "\n\t\t[<pattern>...]"), N_("git tag -v <tagname>..."), NULL @@ -605,13 +605,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_STRING('u', "local-user", &keyid, N_("key-id"), N_("use another key to sign the tag")), OPT__FORCE(&force, N_("replace the tag if exists")), + + OPT_GROUP(N_("Tag listing options")), OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), { OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"), PARSE_OPT_NONEG, parse_opt_sort }, - - OPT_GROUP(N_("Tag listing options")), { OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"), N_("print only tags that contain the commit"), @@ -733,7 +733,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, ref.buf, object, prev, - 0, 1, NULL, &err) || + 0, NULL, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); ref_transaction_free(transaction); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 855d94b90b..ac6667242c 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -91,7 +91,7 @@ static void use(int bytes) static void *get_data(unsigned long size) { git_zstream stream; - void *buf = xmalloc(size); + void *buf = xmallocz(size); memset(&stream, 0, sizeof(stream)); diff --git a/builtin/update-index.c b/builtin/update-index.c index b0e3dc9105..587898624c 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -400,7 +400,7 @@ static void read_index_info(int line_termination) } static const char * const update_index_usage[] = { - N_("git update-index [options] [--] [<file>...]"), + N_("git update-index [<options>] [--] [<file>...]"), NULL }; diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 6c9be05128..3d79a46b03 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -6,9 +6,9 @@ #include "argv-array.h" static const char * const git_update_ref_usage[] = { - N_("git update-ref [options] -d <refname> [<oldval>]"), - N_("git update-ref [options] <refname> <newval> [<oldval>]"), - N_("git update-ref [options] --stdin [-z]"), + N_("git update-ref [<options>] -d <refname> [<old-val>]"), + N_("git update-ref [<options>] <refname> <new-val> [<old-val>]"), + N_("git update-ref [<options>] --stdin [-z]"), NULL }; @@ -198,8 +198,9 @@ static const char *parse_cmd_update(struct ref_transaction *transaction, if (*next != line_termination) die("update %s: extra input: %s", refname, next); - if (ref_transaction_update(transaction, refname, new_sha1, old_sha1, - update_flags, have_old, msg, &err)) + if (ref_transaction_update(transaction, refname, + new_sha1, have_old ? old_sha1 : NULL, + update_flags, msg, &err)) die("%s", err.buf); update_flags = 0; @@ -264,8 +265,9 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction, if (*next != line_termination) die("delete %s: extra input: %s", refname, next); - if (ref_transaction_delete(transaction, refname, old_sha1, - update_flags, have_old, msg, &err)) + if (ref_transaction_delete(transaction, refname, + have_old ? old_sha1 : NULL, + update_flags, msg, &err)) die("%s", err.buf); update_flags = 0; @@ -280,28 +282,21 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction, { struct strbuf err = STRBUF_INIT; char *refname; - unsigned char new_sha1[20]; unsigned char old_sha1[20]; - int have_old; refname = parse_refname(input, &next); if (!refname) die("verify: missing <ref>"); if (parse_next_sha1(input, &next, old_sha1, "verify", refname, - PARSE_SHA1_OLD)) { - hashclr(new_sha1); - have_old = 0; - } else { - hashcpy(new_sha1, old_sha1); - have_old = 1; - } + PARSE_SHA1_OLD)) + hashclr(old_sha1); if (*next != line_termination) die("verify %s: extra input: %s", refname, next); - if (ref_transaction_update(transaction, refname, new_sha1, old_sha1, - update_flags, have_old, msg, &err)) + if (ref_transaction_verify(transaction, refname, old_sha1, + update_flags, &err)) die("%s", err.buf); update_flags = 0; @@ -357,7 +352,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) { const char *refname, *oldval; unsigned char sha1[20], oldsha1[20]; - int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0; + int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0; + unsigned int flags = 0; struct option options[] = { OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")), OPT_BOOL('d', NULL, &delete, N_("delete the reference")), diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index b0f85042b2..ec0c4e3d83 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -14,7 +14,7 @@ #include "gpg-interface.h" static const char * const verify_commit_usage[] = { - N_("git verify-commit [-v|--verbose] <commit>..."), + N_("git verify-commit [-v | --verbose] <commit>..."), NULL }; diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index 7747537beb..c94e156932 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -51,7 +51,7 @@ static int verify_one_pack(const char *path, unsigned int flags) } static const char * const verify_pack_usage[] = { - N_("git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."), + N_("git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."), NULL }; diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 9cdf332333..53c68fce3a 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -14,7 +14,7 @@ #include "gpg-interface.h" static const char * const verify_tag_usage[] = { - N_("git verify-tag [-v|--verbose] <tag>..."), + N_("git verify-tag [-v | --verbose] <tag>..."), NULL }; |