diff options
52 files changed, 793 insertions, 280 deletions
diff --git a/Documentation/RelNotes/1.7.8.1.txt b/Documentation/RelNotes/1.7.8.1.txt index 0e8bd9f5e..33dc948b9 100644 --- a/Documentation/RelNotes/1.7.8.1.txt +++ b/Documentation/RelNotes/1.7.8.1.txt @@ -1,17 +1,38 @@ Git v1.7.8.1 Release Notes ========================== -Fixes since v1.7.8.1 --------------------- +Fixes since v1.7.8 +------------------ * In some codepaths (notably, checkout and merge), the ignore patterns recorded in $GIT_DIR/info/exclude were not honored. They now are. - * After fetching from a remote that has very long refname, the reporting - output could have corrupted by overrunning a static buffer. + * "git apply --check" did not error out when given an empty input + without any patch. + + * "git archive" mistakenly allowed remote clients to ask for commits + that are not at the tip of any ref. * "git checkout" and "git merge" treated in-tree .gitignore and exclude file in $GIT_DIR/info/ directory inconsistently when deciding which untracked files are ignored and expendable. + * LF-to-CRLF streaming filter used when checking out a large-ish blob + fell into an infinite loop with a rare input. + + * The function header pattern for files with "diff=cpp" attribute did + not consider "type *funcname(type param1,..." as the beginning of a + function. + + * The error message from "git diff" and "git status" when they fail + to inspect changes in submodules did not report which submodule they + had trouble with. + + * After fetching from a remote that has very long refname, the reporting + output could have corrupted by overrunning a static buffer. + + * "git pack-objects" avoids creating cyclic dependencies among deltas + when seeing a broken packfile that records the same object in both + the deflated form and as a delta. + Also contains minor fixes and documentation updates. diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index b8db37396..e3c844861 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -15,8 +15,8 @@ DESCRIPTION ----------- This script is used to move or rename a file, directory or symlink. - git mv [-f] [-n] <source> <destination> - git mv [-f] [-n] [-k] <source> ... <destination directory> + git mv [-v] [-f] [-n] [-k] <source> <destination> + git mv [-v] [-f] [-n] [-k] <source> ... <destination directory> In the first form, it renames <source>, which must exist and be either a file, symlink or directory, to <destination>. @@ -40,6 +40,10 @@ OPTIONS --dry-run:: Do nothing; only show what would happen +-v:: +--verbose:: + Report the names of files as they are moved. + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt index b78f031cd..a80d94650 100644 --- a/Documentation/git-stripspace.txt +++ b/Documentation/git-stripspace.txt @@ -3,26 +3,83 @@ git-stripspace(1) NAME ---- -git-stripspace - Filter out empty lines +git-stripspace - Remove unnecessary whitespace SYNOPSIS -------- [verse] -'git stripspace' [-s | --strip-comments] < <stream> +'git stripspace' [-s | --strip-comments] < input DESCRIPTION ----------- -Remove multiple empty lines, and empty lines at beginning and end. + +Clean the input in the manner used by 'git' for text such as commit +messages, notes, tags and branch descriptions. + +With no arguments, this will: + +- remove trailing whitespace from all lines +- collapse multiple consecutive empty lines into one empty line +- remove empty lines from the beginning and end of the input +- add a missing '\n' to the last line if necessary. + +In the case where the input consists entirely of whitespace characters, no +output will be produced. + +*NOTE*: This is intended for cleaning metadata, prefer the `--whitespace=fix` +mode of linkgit:git-apply[1] for correcting whitespace of patches or files in +the repository. OPTIONS ------- -s:: --strip-comments:: - In addition to empty lines, also strip lines starting with '#'. + Skip and remove all lines starting with '#'. + +EXAMPLES +-------- + +Given the following noisy input with '$' indicating the end of a line: -<stream>:: - Byte stream to act on. +-------- +|A brief introduction $ +| $ +|$ +|A new paragraph$ +|# with a commented-out line $ +|explaining lots of stuff.$ +|$ +|# An old paragraph, also commented-out. $ +| $ +|The end.$ +| $ +--------- + +Use 'git stripspace' with no arguments to obtain: + +-------- +|A brief introduction$ +|$ +|A new paragraph$ +|# with a commented-out line$ +|explaining lots of stuff.$ +|$ +|# An old paragraph, also commented-out.$ +|$ +|The end.$ +--------- + +Use 'git stripspace --strip-comments' to obtain: + +-------- +|A brief introduction$ +|$ +|A new paragraph$ +|explaining lots of stuff.$ +|$ +|The end.$ +--------- GIT --- @@ -57,8 +57,8 @@ all:: # # Define NO_STRLCPY if you don't have strlcpy. # -# Define NO_STRTOUMAX if you don't have strtoumax in the C library. -# If your compiler also does not support long long or does not have +# Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the +# C library. If your compiler also does not support long long or does not have # strtoull, define NO_STRTOULL. # # Define NO_SETENV if you don't have setenv in the C library. @@ -1478,7 +1478,7 @@ ifdef NO_STRLCPY endif ifdef NO_STRTOUMAX COMPAT_CFLAGS += -DNO_STRTOUMAX - COMPAT_OBJS += compat/strtoumax.o + COMPAT_OBJS += compat/strtoumax.o compat/strtoimax.o endif ifdef NO_STRTOULL COMPAT_CFLAGS += -DNO_STRTOULL @@ -247,7 +247,8 @@ static void parse_pathspec_arg(const char **pathspec, } static void parse_treeish_arg(const char **argv, - struct archiver_args *ar_args, const char *prefix) + struct archiver_args *ar_args, const char *prefix, + int remote) { const char *name = argv[0]; const unsigned char *commit_sha1; @@ -256,8 +257,17 @@ static void parse_treeish_arg(const char **argv, const struct commit *commit; unsigned char sha1[20]; - if (get_sha1(name, sha1)) - die("Not a valid object name"); + /* Remotes are only allowed to fetch actual refs */ + if (remote) { + char *ref = NULL; + if (!dwim_ref(name, strlen(name), sha1, &ref)) + die("no such ref: %s", name); + free(ref); + } + else { + if (get_sha1(name, sha1)) + die("Not a valid object name"); + } commit = lookup_commit_reference_gently(sha1, 1); if (commit) { @@ -414,7 +424,7 @@ int write_archive(int argc, const char **argv, const char *prefix, setup_git_directory(); } - parse_treeish_arg(argv, &args, prefix); + parse_treeish_arg(argv, &args, prefix, remote); parse_pathspec_arg(argv + 1, &args); return ar->write_archive(ar, &args); @@ -3,7 +3,6 @@ #include "refs.h" #include "remote.h" #include "commit.h" -#include "sequencer.h" struct tracking { struct refspec spec; @@ -160,7 +159,8 @@ int validate_new_branchname(const char *name, struct strbuf *ref, void create_branch(const char *head, const char *name, const char *start_name, - int force, int reflog, enum branch_track track) + int force, int reflog, int clobber_head, + enum branch_track track) { struct ref_lock *lock = NULL; struct commit *commit; @@ -175,7 +175,8 @@ void create_branch(const char *head, explicit_tracking = 1; if (validate_new_branchname(name, &ref, force, - track == BRANCH_TRACK_OVERRIDE)) { + track == BRANCH_TRACK_OVERRIDE || + clobber_head)) { if (!force) dont_change_ref = 1; else @@ -247,5 +248,4 @@ void remove_branch_state(void) unlink(git_path("MERGE_MSG")); unlink(git_path("MERGE_MODE")); unlink(git_path("SQUASH_MSG")); - remove_sequencer_state(0); } @@ -13,7 +13,8 @@ * branch for (if any). */ void create_branch(const char *head, const char *name, const char *start_name, - int force, int reflog, enum branch_track track); + int force, int reflog, + int clobber_head, enum branch_track track); /* * Validates that the requested branch may be created, returning the diff --git a/builtin/apply.c b/builtin/apply.c index b3b59db53..c24dc546d 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3587,15 +3587,12 @@ static int write_out_one_reject(struct patch *patch) return -1; } -static int write_out_results(struct patch *list, int skipped_patch) +static int write_out_results(struct patch *list) { int phase; int errs = 0; struct patch *l; - if (!list && !skipped_patch) - return error("No changes"); - for (phase = 0; phase < 2; phase++) { l = list; while (l) { @@ -3721,6 +3718,9 @@ static int apply_patch(int fd, const char *filename, int options) offset += nr; } + if (!list && !skipped_patch) + die("unrecognized input"); + if (whitespace_error && (ws_error_action == die_on_ws_error)) apply = 0; @@ -3738,7 +3738,7 @@ static int apply_patch(int fd, const char *filename, int options) !apply_with_reject) exit(1); - if (apply && write_out_results(list, skipped_patch)) + if (apply && write_out_results(list)) exit(1); if (fake_ancestor) diff --git a/builtin/branch.c b/builtin/branch.c index 55cad766c..df908ed8f 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -568,6 +568,7 @@ static void rename_branch(const char *oldname, const char *newname, int force) unsigned char sha1[20]; struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT; int recovery = 0; + int clobber_head_ok; if (!oldname) die(_("cannot rename the current branch while not on any.")); @@ -583,7 +584,13 @@ static void rename_branch(const char *oldname, const char *newname, int force) die(_("Invalid branch name: '%s'"), oldname); } - validate_new_branchname(newname, &newref, force, 0); + /* + * A command like "git branch -M currentbranch currentbranch" cannot + * cause the worktree to become inconsistent with HEAD, so allow it. + */ + clobber_head_ok = !strcmp(oldname, newname); + + validate_new_branchname(newname, &newref, force, clobber_head_ok); strbuf_addf(&logmsg, "Branch: renamed %s to %s", oldref.buf, newref.buf); @@ -730,7 +737,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (kinds != REF_LOCAL_BRANCH) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); create_branch(head, argv[0], (argc == 2) ? argv[1] : head, - force_create, reflog, track); + force_create, reflog, 0, track); } else usage_with_options(builtin_branch_usage, options); diff --git a/builtin/checkout.c b/builtin/checkout.c index 51840b978..44e73b5a4 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -540,7 +540,9 @@ static void update_refs_for_switch(struct checkout_opts *opts, else create_branch(old->name, opts->new_branch, new->name, opts->new_branch_force ? 1 : 0, - opts->new_branch_log, opts->track); + opts->new_branch_log, + opts->new_branch_force ? 1 : 0, + opts->track); new->name = opts->new_branch; setup_branch_path(new); } @@ -565,8 +567,12 @@ static void update_refs_for_switch(struct checkout_opts *opts, create_symref("HEAD", new->path, msg.buf); if (!opts->quiet) { if (old->path && !strcmp(new->path, old->path)) { - fprintf(stderr, _("Already on '%s'\n"), - new->name); + if (opts->new_branch_force) + fprintf(stderr, _("Reset branch '%s'\n"), + new->name); + else + fprintf(stderr, _("Already on '%s'\n"), + new->name); } else if (opts->new_branch) { if (opts->branch_exists) fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name); @@ -1057,7 +1063,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) struct strbuf buf = STRBUF_INIT; opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, - !!opts.new_branch_force, 0); + !!opts.new_branch_force, + !!opts.new_branch_force); strbuf_release(&buf); } diff --git a/builtin/clone.c b/builtin/clone.c index efe8b6cce..86db95473 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -84,8 +84,8 @@ static struct option builtin_clone_options[] = { "directory from which templates will be used"), OPT_CALLBACK(0 , "reference", &option_reference, "repo", "reference repository", &opt_parse_reference), - OPT_STRING('o', "origin", &option_origin, "branch", - "use <branch> instead of 'origin' to track upstream"), + OPT_STRING('o', "origin", &option_origin, "name", + "use <name> instead of 'origin' to track upstream"), OPT_STRING('b', "branch", &option_branch, "branch", "checkout <branch> instead of the remote's HEAD"), OPT_STRING('u', "upload-pack", &option_upload_pack, "path", diff --git a/builtin/commit.c b/builtin/commit.c index 8f2bebecf..b02e2c4e8 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -138,7 +138,7 @@ static struct option builtin_commit_options[] = { OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"), OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"), OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"), - OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"), + OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"), OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"), OPT_FILENAME('t', "template", &template_file, "use specified template file"), OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"), diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index c6bc8eb0a..6207ecd29 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -556,11 +556,16 @@ static void filter_refs(struct ref **refs, int nr_match, char **match) continue; } else { - int order = path_match(ref->name, nr_match, match); - if (order) { - return_refs[order-1] = ref; - continue; /* we will link it later */ + int i; + for (i = 0; i < nr_match; i++) { + if (!strcmp(ref->name, match[i])) { + match[i][0] = '\0'; + return_refs[i] = ref; + break; + } } + if (i < nr_match) + continue; /* we will link it later */ } free(ref); } @@ -976,7 +981,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.verbose ? CONNECT_VERBOSE : 0); } - get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL); + get_remote_heads(fd[0], &ref, 0, NULL); ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, pack_lockfile_ptr); diff --git a/builtin/init-db.c b/builtin/init-db.c index d07554c88..0dacb8b79 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -351,7 +351,7 @@ static void separate_git_dir(const char *git_dir) else if (S_ISDIR(st.st_mode)) src = git_link; else - die(_("unable to handle file type %d"), st.st_mode); + die(_("unable to handle file type %d"), (int)st.st_mode); if (rename(src, git_dir)) die_errno(_("unable to move %s to %s"), src, git_dir); diff --git a/builtin/log.c b/builtin/log.c index f5d493059..56bc555d1 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -72,8 +72,6 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse static void cmd_log_init_defaults(struct rev_info *rev) { - rev->abbrev = DEFAULT_ABBREV; - rev->commit_format = CMIT_FMT_DEFAULT; if (fmt_pretty) get_commit_format(fmt_pretty, rev); rev->verbose_header = 1; diff --git a/builtin/mv.c b/builtin/mv.c index 5efe6c576..2a144b011 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -59,6 +59,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int i, newfd; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { + OPT__VERBOSE(&verbose, "be verbose"), OPT__DRY_RUN(&show_only, "dry run"), OPT__FORCE(&force, "force move/rename even if target exists"), OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"), @@ -93,7 +94,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) destination = copy_pathspec(dest_path[0], argv, argc, 1); } else { if (argc != 1) - usage_with_options(builtin_mv_usage, builtin_mv_options); + die("destination '%s' is not a directory", dest_path[0]); destination = dest_path; } @@ -176,7 +177,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - warning(_("%s; will overwrite!"), bad); + if (verbose) + warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); diff --git a/builtin/revert.c b/builtin/revert.c index 1ea525c10..028bcbcd7 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -60,13 +60,14 @@ struct replay_opts { int allow_rerere_auto; int mainline; - int commit_argc; - const char **commit_argv; /* Merge strategy */ const char *strategy; const char **xopts; size_t xopts_nr, xopts_alloc; + + /* Only used by REPLAY_NONE */ + struct rev_info *revs; }; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -169,9 +170,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) die(_("program error")); } - opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str, - PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN); + argc = parse_options(argc, argv, NULL, options, usage_str, + PARSE_OPT_KEEP_ARGV0 | + PARSE_OPT_KEEP_UNKNOWN); /* Check for incompatible subcommands */ verify_opt_mutually_compatible(me, @@ -213,9 +214,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) NULL); } - else if (opts->commit_argc < 2) - usage_with_options(usage_str, options); - if (opts->allow_ff) verify_opt_compatible(me, "--ff", "--signoff", opts->signoff, @@ -223,7 +221,20 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) "-x", opts->record_origin, "--edit", opts->edit, NULL); - opts->commit_argv = argv; + + if (opts->subcommand != REPLAY_NONE) { + opts->revs = NULL; + } else { + opts->revs = xmalloc(sizeof(*opts->revs)); + init_revisions(opts->revs, NULL); + opts->revs->no_walk = 1; + if (argc < 2) + usage_with_options(usage_str, options); + argc = setup_revisions(argc, argv, opts->revs, NULL); + } + + if (argc > 1) + usage_with_options(usage_str, options); } struct commit_message { @@ -631,23 +642,15 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) return res; } -static void prepare_revs(struct rev_info *revs, struct replay_opts *opts) +static void prepare_revs(struct replay_opts *opts) { - int argc; - - init_revisions(revs, NULL); - revs->no_walk = 1; if (opts->action != REVERT) - revs->reverse = 1; - - argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL); - if (argc > 1) - usage(*revert_or_cherry_pick_usage(opts)); + opts->revs->reverse ^= 1; - if (prepare_revision_walk(revs)) + if (prepare_revision_walk(opts->revs)) die(_("revision walk setup failed")); - if (!revs->commits) + if (!opts->revs->commits) die(_("empty commit set passed")); } @@ -844,14 +847,13 @@ static void read_populate_opts(struct replay_opts **opts_ptr) static void walk_revs_populate_todo(struct commit_list **todo_list, struct replay_opts *opts) { - struct rev_info revs; struct commit *commit; struct commit_list **next; - prepare_revs(&revs, opts); + prepare_revs(opts); next = todo_list; - while ((commit = get_revision(&revs))) + while ((commit = get_revision(opts->revs))) next = commit_list_append(commit, next); } @@ -942,7 +944,7 @@ static int sequencer_rollback(struct replay_opts *opts) } if (reset_for_rollback(sha1)) goto fail; - remove_sequencer_state(1); + remove_sequencer_state(); strbuf_release(&buf); return 0; fail: @@ -1016,33 +1018,64 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts) for (cur = todo_list; cur; cur = cur->next) { save_todo(cur, opts); res = do_pick_commit(cur->item, opts); - if (res) { - if (!cur->next) - /* - * An error was encountered while - * picking the last commit; the - * sequencer state is useless now -- - * the user simply needs to resolve - * the conflict and commit - */ - remove_sequencer_state(0); + if (res) return res; - } } /* * Sequence of picks finished successfully; cleanup by * removing the .git/sequencer directory */ - remove_sequencer_state(1); + remove_sequencer_state(); return 0; } +static int continue_single_pick(void) +{ + const char *argv[] = { "commit", NULL }; + + if (!file_exists(git_path("CHERRY_PICK_HEAD")) && + !file_exists(git_path("REVERT_HEAD"))) + return error(_("no cherry-pick or revert in progress")); + return run_command_v_opt(argv, RUN_GIT_CMD); +} + +static int sequencer_continue(struct replay_opts *opts) +{ + struct commit_list *todo_list = NULL; + + if (!file_exists(git_path(SEQ_TODO_FILE))) + return continue_single_pick(); + read_populate_opts(&opts); + read_populate_todo(&todo_list, opts); + + /* Verify that the conflict has been resolved */ + if (file_exists(git_path("CHERRY_PICK_HEAD")) || + file_exists(git_path("REVERT_HEAD"))) { + int ret = continue_single_pick(); + if (ret) + return ret; + } + if (index_differs_from("HEAD", 0)) + return error_dirty_index(opts); + todo_list = todo_list->next; + return pick_commits(todo_list, opts); +} + +static int single_pick(struct commit *cmit, struct replay_opts *opts) +{ + setenv(GIT_REFLOG_ACTION, action_name(opts), 0); + return do_pick_commit(cmit, opts); +} + static int pick_revisions(struct replay_opts *opts) { struct commit_list *todo_list = NULL; unsigned char sha1[20]; + if (opts->subcommand == REPLAY_NONE) + assert(opts->revs); + read_and_refresh_cache(opts); /* @@ -1051,21 +1084,32 @@ static int pick_revisions(struct replay_opts *opts) * one that is being continued */ if (opts->subcommand == REPLAY_REMOVE_STATE) { - remove_sequencer_state(1); + remove_sequencer_state(); return 0; } if (opts->subcommand == REPLAY_ROLLBACK) return sequencer_rollback(opts); - if (opts->subcommand == REPLAY_CONTINUE) { - if (!file_exists(git_path(SEQ_TODO_FILE))) - return error(_("No %s in progress"), action_name(opts)); - read_populate_opts(&opts); - read_populate_todo(&todo_list, opts); - - /* Verify that the conflict has been resolved */ - if (!index_differs_from("HEAD", 0)) - todo_list = todo_list->next; - return pick_commits(todo_list, opts); + if (opts->subcommand == REPLAY_CONTINUE) + return sequencer_continue(opts); + + /* + * If we were called as "git cherry-pick <commit>", just + * cherry-pick/revert it, set CHERRY_PICK_HEAD / + * REVERT_HEAD, and don't touch the sequencer state. + * This means it is possible to cherry-pick in the middle + * of a cherry-pick sequence. + */ + if (opts->revs->cmdline.nr == 1 && + opts->revs->cmdline.rev->whence == REV_CMD_REV && + opts->revs->no_walk && + !opts->revs->cmdline.rev->flags) { + struct commit *cmit; + if (prepare_revision_walk(opts->revs)) + die(_("revision walk setup failed")); + cmit = get_revision(opts->revs); + if (!cmit || get_revision(opts->revs)) + die("BUG: expected exactly one commit from walk"); + return single_pick(cmit, opts); } /* diff --git a/builtin/send-pack.c b/builtin/send-pack.c index e0b8030f2..cd1115ffc 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -494,8 +494,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) memset(&extra_have, 0, sizeof(extra_have)); - get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL, - &extra_have); + get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have); transport_verify_remote_names(nr_refspecs, refspecs); diff --git a/builtin/stripspace.c b/builtin/stripspace.c index 1288ffcc5..f16986c0a 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -75,7 +75,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) !strcmp(argv[1], "--strip-comments"))) strip_comments = 1; else if (argc > 1) - usage("git stripspace [-s | --strip-comments] < <stream>"); + usage("git stripspace [-s | --strip-comments] < input"); if (strbuf_read(&buf, 0, 1024) < 0) die_errno("could not read the input"); @@ -1028,12 +1028,11 @@ extern char *git_getpass(const char *prompt); extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); extern int finish_connect(struct child_process *conn); extern int git_connection_is_socket(struct child_process *conn); -extern int path_match(const char *path, int nr, char **match); struct extra_have_objects { int nr, alloc; unsigned char (*array)[20]; }; -extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *); +extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *); extern int server_supports(const char *feature); extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path); diff --git a/compat/snprintf.c b/compat/snprintf.c index e1e0e7543..42ea1ac11 100644 --- a/compat/snprintf.c +++ b/compat/snprintf.c @@ -19,11 +19,14 @@ #undef vsnprintf int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap) { + va_list cp; char *s; int ret = -1; if (maxsize > 0) { - ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap); + va_copy(cp, ap); + ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp); + va_end(cp); if (ret == maxsize-1) ret = -1; /* Windows does not NUL-terminate if result fills buffer */ @@ -42,7 +45,9 @@ int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap) if (! str) break; s = str; - ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap); + va_copy(cp, ap); + ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp); + va_end(cp); if (ret == maxsize-1) ret = -1; } diff --git a/compat/strtoimax.c b/compat/strtoimax.c new file mode 100644 index 000000000..ac09ed89e --- /dev/null +++ b/compat/strtoimax.c @@ -0,0 +1,10 @@ +#include "../git-compat-util.h" + +intmax_t gitstrtoimax (const char *nptr, char **endptr, int base) +{ +#if defined(NO_STRTOULL) + return strtol(nptr, endptr, base); +#else + return strtoll(nptr, endptr, base); +#endif +} @@ -333,7 +333,7 @@ static int git_parse_file(config_fn_t fn, void *data) die("bad config file line %d in %s", cf->linenr, cf->name); } -static int parse_unit_factor(const char *end, unsigned long *val) +static int parse_unit_factor(const char *end, uintmax_t *val) { if (!*end) return 1; @@ -356,11 +356,23 @@ static int git_parse_long(const char *value, long *ret) { if (value && *value) { char *end; - long val = strtol(value, &end, 0); - unsigned long factor = 1; + intmax_t val; + uintmax_t uval; + uintmax_t factor = 1; + + errno = 0; + val = strtoimax(value, &end, 0); + if (errno == ERANGE) + return 0; if (!parse_unit_factor(end, &factor)) return 0; - *ret = val * factor; + uval = abs(val); + uval *= factor; + if ((uval > maximum_signed_value_of_type(long)) || + (abs(val) > uval)) + return 0; + val *= factor; + *ret = val; return 1; } return 0; @@ -370,9 +382,19 @@ int git_parse_ulong(const char *value, unsigned long *ret) { if (value && *value) { char *end; - unsigned long val = strtoul(value, &end, 0); + uintmax_t val; + uintmax_t oldval; + + errno = 0; + val = strtoumax(value, &end, 0); + if (errno == ERANGE) + return 0; + oldval = val; if (!parse_unit_factor(end, &val)) return 0; + if ((val > maximum_unsigned_value_of_type(long)) || + (oldval > val)) + return 0; *ret = val; return 1; } @@ -553,7 +575,7 @@ static int git_default_core_config(const char *var, const char *value) if (!strcmp(var, "core.packedgitwindowsize")) { int pgsz_x2 = getpagesize() * 2; - packed_git_window_size = git_config_int(var, value); + packed_git_window_size = git_config_ulong(var, value); /* This value must be multiple of (pagesize * 2) */ packed_git_window_size /= pgsz_x2; @@ -564,18 +586,17 @@ static int git_default_core_config(const char *var, const char *value) } if (!strcmp(var, "core.bigfilethreshold")) { - long n = git_config_int(var, value); - big_file_threshold = 0 < n ? n : 0; + big_file_threshold = git_config_ulong(var, value); return 0; } if (!strcmp(var, "core.packedgitlimit")) { - packed_git_limit = git_config_int(var, value); + packed_git_limit = git_config_ulong(var, value); return 0; } if (!strcmp(var, "core.deltabasecachelimit")) { - delta_base_cache_limit = git_config_int(var, value); + delta_base_cache_limit = git_config_ulong(var, value); return 0; } @@ -53,7 +53,6 @@ static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1 * Read all the refs from the other end */ struct ref **get_remote_heads(int in, struct ref **list, - int nr_match, char **match, unsigned int flags, struct extra_have_objects *extra_have) { @@ -92,8 +91,6 @@ struct ref **get_remote_heads(int in, struct ref **list, if (!check_ref(name, name_len, flags)) continue; - if (nr_match && !path_match(name, nr_match, match)) - continue; ref = alloc_ref(buffer + 41); hashcpy(ref->old_sha1, old_sha1); *list = ref; @@ -108,27 +105,6 @@ int server_supports(const char *feature) strstr(server_capabilities, feature) != NULL; } -int path_match(const char *path, int nr, char **match) -{ - int i; - int pathlen = strlen(path); - - for (i = 0; i < nr; i++) { - char *s = match[i]; - int len = strlen(s); - - if (!len || len > pathlen) - continue; - if (memcmp(path + pathlen - len, s, len)) - continue; - if (pathlen > len && path[pathlen - len - 1] != '/') - continue; - *s = 0; - return (i + 1); - } - return 0; -} - enum protocol { PROTO_LOCAL = 1, PROTO_SSH, @@ -175,6 +151,15 @@ static void get_host_and_port(char **host, const char **port) } } +static void enable_keepalive(int sockfd) +{ + int ka = 1; + + if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) + fprintf(stderr, "unable to set SO_KEEPALIVE on socket: %s\n", + strerror(errno)); +} + #ifndef NO_IPV6 static const char *ai_name(const struct addrinfo *ai) @@ -239,6 +224,8 @@ static int git_tcp_connect_sock(char *host, int flags) if (sockfd < 0) die("unable to connect to %s:\n%s", host, error_message.buf); + enable_keepalive(sockfd); + if (flags & CONNECT_VERBOSE) fprintf(stderr, "done.\n"); @@ -312,6 +299,8 @@ static int git_tcp_connect_sock(char *host, int flags) if (sockfd < 0) die("unable to connect to %s:\n%s", host, error_message.buf); + enable_keepalive(sockfd); + if (flags & CONNECT_VERBOSE) fprintf(stderr, "done.\n"); @@ -876,24 +876,42 @@ int is_null_stream_filter(struct stream_filter *filter) /* * LF-to-CRLF filter */ + +struct lf_to_crlf_filter { + struct stream_filter filter; + unsigned want_lf:1; +}; + static int lf_to_crlf_filter_fn(struct stream_filter *filter, const char *input, size_t *isize_p, char *output, size_t *osize_p) { - size_t count; + size_t count, o = 0; + struct lf_to_crlf_filter *lf_to_crlf = (struct lf_to_crlf_filter *)filter; + + /* Output a pending LF if we need to */ + if (lf_to_crlf->want_lf) { + output[o++] = '\n'; + lf_to_crlf->want_lf = 0; + } + + /* We are told to drain */ + if (!input) { + *osize_p -= o; + return 0; + } - if (!input) - return 0; /* we do not keep any states */ count = *isize_p; if (count) { - size_t i, o; - for (i = o = 0; o < *osize_p && i < count; i++) { + size_t i; + for (i = 0; o < *osize_p && i < count; i++) { char ch = input[i]; if (ch == '\n') { - if (o + 1 < *osize_p) - output[o++] = '\r'; - else - break; + output[o++] = '\r'; + if (o >= *osize_p) { + lf_to_crlf->want_lf = 1; + continue; /* We need to increase i */ + } } output[o++] = ch; } @@ -904,15 +922,23 @@ static int lf_to_crlf_filter_fn(struct stream_filter *filter, return 0; } +static void lf_to_crlf_free_fn(struct stream_filter *filter) +{ + free(filter); +} + static struct stream_filter_vtbl lf_to_crlf_vtbl = { lf_to_crlf_filter_fn, - null_free_fn, + lf_to_crlf_free_fn, }; -static struct stream_filter lf_to_crlf_filter_singleton = { - &lf_to_crlf_vtbl, -}; +static struct stream_filter *lf_to_crlf_filter(void) +{ + struct lf_to_crlf_filter *lf_to_crlf = xcalloc(1, sizeof(*lf_to_crlf)); + lf_to_crlf->filter.vtbl = &lf_to_crlf_vtbl; + return (struct stream_filter *)lf_to_crlf; +} /* * Cascade filter @@ -1194,7 +1220,7 @@ struct stream_filter *get_stream_filter(const char *path, const unsigned char *s else if (output_eol(crlf_action) == EOL_CRLF && !(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS)) - filter = cascade_filter(filter, &lf_to_crlf_filter_singleton); + filter = cascade_filter(filter, lf_to_crlf_filter()); return filter; } diff --git a/fast-import.c b/fast-import.c index 8d8ea3c45..f4bfe0f66 100644 --- a/fast-import.c +++ b/fast-import.c @@ -2173,6 +2173,11 @@ static uintmax_t do_change_note_fanout( if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) { /* This is a note entry */ + if (fanout == 0xff) { + /* Counting mode, no rename */ + num_notes++; + continue; + } construct_path_with_fanout(hex_sha1, fanout, realpath); if (!strcmp(fullpath, realpath)) { /* Note entry is in correct location */ @@ -2379,7 +2384,7 @@ static void file_change_cr(struct branch *b, int rename) leaf.tree); } -static void note_change_n(struct branch *b, unsigned char old_fanout) +static void note_change_n(struct branch *b, unsigned char *old_fanout) { const char *p = command_buf.buf + 2; static struct strbuf uq = STRBUF_INIT; @@ -2390,6 +2395,23 @@ static void note_change_n(struct branch *b, unsigned char old_fanout) uint16_t inline_data = 0; unsigned char new_fanout; + /* + * When loading a branch, we don't traverse its tree to count the real + * number of notes (too expensive to do this for all non-note refs). + * This means that recently loaded notes refs might incorrectly have + * b->num_notes == 0, and consequently, old_fanout might be wrong. + * + * Fix this by traversing the tree and counting the number of notes + * when b->num_notes == 0. If the notes tree is truly empty, the + * calculation should not take long. + */ + if (b->num_notes == 0 && *old_fanout == 0) { + /* Invoke change_note_fanout() in "counting mode". */ + b->num_notes = change_note_fanout(&b->branch_tree, 0xff); + *old_fanout = convert_num_notes_to_fanout(b->num_notes); + } + + /* Now parse the notemodify command. */ /* <dataref> or 'inline' */ if (*p == ':') { char *x; @@ -2450,7 +2472,7 @@ static void note_change_n(struct branch *b, unsigned char old_fanout) typename(type), command_buf.buf); } - construct_path_with_fanout(sha1_to_hex(commit_sha1), old_fanout, path); + construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path); if (tree_content_remove(&b->branch_tree, path, NULL)) b->num_notes--; @@ -2637,7 +2659,7 @@ static void parse_new_commit(void) else if (!prefixcmp(command_buf.buf, "C ")) file_change_cr(b, 0); else if (!prefixcmp(command_buf.buf, "N ")) - note_change_n(b, prev_fanout); + note_change_n(b, &prev_fanout); else if (!strcmp("deleteall", command_buf.buf)) file_change_deleteall(b); else if (!prefixcmp(command_buf.buf, "ls ")) diff --git a/git-compat-util.h b/git-compat-util.h index 8b4dd5c02..230e198fc 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -351,6 +351,8 @@ extern size_t gitstrlcpy(char *, const char *, size_t); #ifdef NO_STRTOUMAX #define strtoumax gitstrtoumax extern uintmax_t gitstrtoumax(const char *, char **, int); +#define strtoimax gitstrtoimax +extern intmax_t gitstrtoimax(const char *, char **, int); #endif #ifdef NO_STRTOK_R diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 804001bb4..5812222eb 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -143,6 +143,21 @@ die_with_patch () { die "$2" } +exit_with_patch () { + echo "$1" > "$state_dir"/stopped-sha + make_patch $1 + git rev-parse --verify HEAD > "$amend" + warn "You can amend the commit now, with" + warn + warn " git commit --amend" + warn + warn "Once you are satisfied with your changes, run" + warn + warn " git rebase --continue" + warn + exit $2 +} + die_abort () { rm -rf "$state_dir" die "$1" @@ -408,7 +423,13 @@ do_next () { mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" - git commit --amend --no-post-rewrite + git commit --amend --no-post-rewrite || { + warn "Could not amend commit after successfully picking $sha1... $rest" + warn "This is most likely due to an empty commit message, or the pre-commit hook" + warn "failed. If the pre-commit hook failed, you may need to resolve the issue before" + warn "you are able to reword the commit." + exit_with_patch $sha1 1 + } record_in_rewritten $sha1 ;; edit|e) @@ -417,19 +438,8 @@ do_next () { mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" - echo "$sha1" > "$state_dir"/stopped-sha - make_patch $sha1 - git rev-parse --verify HEAD > "$amend" warn "Stopped at $sha1... $rest" - warn "You can amend the commit now, with" - warn - warn " git commit --amend" - warn - warn "Once you are satisfied with your changes, run" - warn - warn " git rebase --continue" - warn - exit 0 + exit_with_patch $sha1 0 ;; squash|s|fixup|f) case "$command" in diff --git a/http-fetch.c b/http-fetch.c index 69299b7bd..94d47cbb2 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -67,7 +67,7 @@ int main(int argc, const char **argv) git_config(git_default_config, NULL); - http_init(NULL, url); + http_init(NULL, url, 0); walker = get_http_walker(url); walker->get_tree = get_tree; walker->get_history = get_history; diff --git a/http-push.c b/http-push.c index edd553b7f..cdfdd4f79 100644 --- a/http-push.c +++ b/http-push.c @@ -1820,7 +1820,7 @@ int main(int argc, char **argv) memset(remote_dir_exists, -1, 256); - http_init(NULL, repo->url); + http_init(NULL, repo->url, 1); #ifdef USE_CURL_MULTI is_running_queue = 0; @@ -42,6 +42,7 @@ static int curl_ftp_no_epsv; static const char *curl_http_proxy; static const char *curl_cookie_file; static char *user_name, *user_pass, *description; +static int http_proactive_auth; static const char *user_agent; #if LIBCURL_VERSION_NUM >= 0x071700 @@ -276,6 +277,9 @@ static CURL *get_curl_handle(void) curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY); #endif + if (http_proactive_auth) + init_curl_http_auth(result); + if (ssl_cert != NULL) curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); if (has_cert_password()) @@ -364,7 +368,7 @@ static void set_from_env(const char **var, const char *envname) *var = val; } -void http_init(struct remote *remote, const char *url) +void http_init(struct remote *remote, const char *url, int proactive_auth) { char *low_speed_limit; char *low_speed_time; @@ -375,6 +379,8 @@ void http_init(struct remote *remote, const char *url) curl_global_init(CURL_GLOBAL_ALL); + http_proactive_auth = proactive_auth; + if (remote && remote->http_proxy) curl_http_proxy = xstrdup(remote->http_proxy); @@ -85,7 +85,8 @@ extern void add_fill_function(void *data, int (*fill)(void *)); extern void step_active_slots(void); #endif -extern void http_init(struct remote *remote, const char *url); +extern void http_init(struct remote *remote, const char *url, + int proactive_auth); extern void http_cleanup(void); extern int active_requests; diff --git a/imap-send.c b/imap-send.c index e1ad1a48c..80e0e8c05 100644 --- a/imap-send.c +++ b/imap-send.c @@ -161,7 +161,6 @@ static struct imap_server_conf server = { struct imap_store_conf { struct store_conf gen; struct imap_server_conf *server; - unsigned use_namespace:1; }; #define NIL (void *)0x1 diff --git a/read-cache.c b/read-cache.c index 5790a9104..27e9fc6ee 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1001,7 +1001,8 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti */ static struct cache_entry *refresh_cache_ent(struct index_state *istate, struct cache_entry *ce, - unsigned int options, int *err) + unsigned int options, int *err, + int *changed_ret) { struct stat st; struct cache_entry *updated; @@ -1033,6 +1034,8 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, } changed = ie_match_stat(istate, ce, &st, options); + if (changed_ret) + *changed_ret = changed; if (!changed) { /* * The path is unchanged. If we were told to ignore @@ -1102,14 +1105,21 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int first = 1; int in_porcelain = (flags & REFRESH_IN_PORCELAIN); unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; - const char *needs_update_fmt; - const char *needs_merge_fmt; - - needs_update_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n"); - needs_merge_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n"); + const char *modified_fmt; + const char *deleted_fmt; + const char *typechange_fmt; + const char *added_fmt; + const char *unmerged_fmt; + + modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n"); + deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n"); + typechange_fmt = (in_porcelain ? "T\t%s\n" : "%s needs update\n"); + added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n"); + unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n"); for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce, *new; int cache_errno = 0; + int changed = 0; ce = istate->cache[i]; if (ignore_submodules && S_ISGITLINK(ce->ce_mode)) @@ -1122,7 +1132,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p i--; if (allow_unmerged) continue; - show_file(needs_merge_fmt, ce->name, in_porcelain, &first, header_msg); + show_file(unmerged_fmt, ce->name, in_porcelain, &first, header_msg); has_errors = 1; continue; } @@ -1130,10 +1140,12 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen)) continue; - new = refresh_cache_ent(istate, ce, options, &cache_errno); + new = refresh_cache_ent(istate, ce, options, &cache_errno, &changed); if (new == ce) continue; if (!new) { + const char *fmt; + if (not_new && cache_errno == ENOENT) continue; if (really && cache_errno == EINVAL) { @@ -1145,7 +1157,17 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p } if (quiet) continue; - show_file(needs_update_fmt, ce->name, in_porcelain, &first, header_msg); + + if (cache_errno == ENOENT) + fmt = deleted_fmt; + else if (ce->ce_flags & CE_INTENT_TO_ADD) + fmt = added_fmt; /* must be before other checks */ + else if (changed & TYPE_CHANGED) + fmt = typechange_fmt; + else + fmt = modified_fmt; + show_file(fmt, + ce->name, in_porcelain, &first, header_msg); has_errors = 1; continue; } @@ -1157,7 +1179,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really) { - return refresh_cache_ent(&the_index, ce, really, NULL); + return refresh_cache_ent(&the_index, ce, really, NULL, NULL); } static int verify_hdr(struct cache_header *hdr, unsigned long size) diff --git a/remote-curl.c b/remote-curl.c index 0e720ee8b..6a352de7b 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -200,7 +200,7 @@ static struct ref *parse_git_refs(struct discovery *heads) if (start_async(&async)) die("cannot start thread to parse advertised refs"); - get_remote_heads(async.out, &list, 0, NULL, 0, NULL); + get_remote_heads(async.out, &list, 0, NULL); close(async.out); if (finish_async(&async)) die("ref parsing thread failed"); @@ -859,7 +859,7 @@ int main(int argc, const char **argv) url = strbuf_detach(&buf, NULL); - http_init(remote, url); + http_init(remote, url, 0); do { if (strbuf_getline(&buf, stdin, '\n') == EOF) { diff --git a/sequencer.c b/sequencer.c index bc2c046aa..d1f28a694 100644 --- a/sequencer.c +++ b/sequencer.c @@ -3,17 +3,11 @@ #include "strbuf.h" #include "dir.h" -void remove_sequencer_state(int aggressive) +void remove_sequencer_state(void) { struct strbuf seq_dir = STRBUF_INIT; - struct strbuf seq_old_dir = STRBUF_INIT; strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR)); - strbuf_addf(&seq_old_dir, "%s", git_path(SEQ_OLD_DIR)); - remove_dir_recursively(&seq_old_dir, 0); - rename(git_path(SEQ_DIR), git_path(SEQ_OLD_DIR)); - if (aggressive) - remove_dir_recursively(&seq_old_dir, 0); + remove_dir_recursively(&seq_dir, 0); strbuf_release(&seq_dir); - strbuf_release(&seq_old_dir); } diff --git a/sequencer.h b/sequencer.h index f435fdb4b..2d4528f29 100644 --- a/sequencer.h +++ b/sequencer.h @@ -2,19 +2,11 @@ #define SEQUENCER_H #define SEQ_DIR "sequencer" -#define SEQ_OLD_DIR "sequencer-old" #define SEQ_HEAD_FILE "sequencer/head" #define SEQ_TODO_FILE "sequencer/todo" #define SEQ_OPTS_FILE "sequencer/opts" -/* - * Removes SEQ_OLD_DIR and renames SEQ_DIR to SEQ_OLD_DIR, ignoring - * any errors. Intended to be used by 'git reset'. - * - * With the aggressive flag, it additionally removes SEQ_OLD_DIR, - * ignoring any errors. Inteded to be used by the sequencer's - * '--quit' subcommand. - */ -void remove_sequencer_state(int aggressive); +/* Removes SEQ_DIR. */ +extern void remove_sequencer_state(void); #endif diff --git a/submodule.c b/submodule.c index 52cdcc6a6..68c1ba90b 100644 --- a/submodule.c +++ b/submodule.c @@ -689,7 +689,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked) cp.out = -1; cp.dir = path; if (start_command(&cp)) - die("Could not run git status --porcelain"); + die("Could not run 'git status --porcelain' in submodule %s", path); len = strbuf_read(&buf, cp.out, 1024); line = buf.buf; @@ -714,7 +714,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked) close(cp.out); if (finish_command(&cp)) - die("git status --porcelain failed"); + die("'git status --porcelain' failed in submodule %s", path); strbuf_release(&buf); return dirty_submodule; diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 0a4cdfa93..3c12b05d6 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -92,6 +92,9 @@ SSLEngine On <Location /dumb/> Dav on </Location> + <Location /auth/dumb> + Dav on + </Location> </IfDefine> <IfDefine SVN> diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh index 75874e85d..274126236 100755 --- a/t/t2018-checkout-branch.sh +++ b/t/t2018-checkout-branch.sh @@ -189,12 +189,13 @@ test_expect_success 'checkout -b <describe>' ' test_cmp expect actual ' -test_expect_success 'checkout -B to the current branch fails before merging' ' +test_expect_success 'checkout -B to the current branch works' ' git checkout branch1 && + git checkout -B branch1-scratch && + setup_dirty_mergeable && - git commit -mfooble && - test_must_fail git checkout -B branch1 initial && - test_must_fail test_dirty_mergeable + git checkout -B branch1-scratch initial && + test_dirty_mergeable ' test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index bc73c2099..76903323a 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -115,6 +115,22 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou git branch -M baz bam ' +test_expect_success 'git branch -M master should work when master is checked out' ' + git checkout master && + git branch -M master +' + +test_expect_success 'git branch -M master master should work when master is checked out' ' + git checkout master && + git branch -M master master +' + +test_expect_success 'git branch -M master2 master2 should work when master is checked out' ' + git checkout master && + git branch master2 && + git branch -M master2 master2 +' + test_expect_success 'git branch -v -d t should work' ' git branch t && test_path_is_file .git/refs/heads/t && diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 2c4c1c851..e80050e1f 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -2,6 +2,7 @@ test_description='Test cherry-pick continuation features + + conflicting: rewrites unrelated to conflicting + yetanotherpick: rewrites foo to e + anotherpick: rewrites foo to d + picked: rewrites foo to c @@ -27,6 +28,7 @@ test_cmp_rev () { } test_expect_success setup ' + git config advice.detachedhead false echo unrelated >unrelated && git add unrelated && test_commit initial foo a && @@ -35,8 +37,8 @@ test_expect_success setup ' test_commit picked foo c && test_commit anotherpick foo d && test_commit yetanotherpick foo e && - git config advice.detachedhead false - + pristine_detach initial && + test_commit conflicting unrelated ' test_expect_success 'cherry-pick persists data on failure' ' @@ -48,6 +50,18 @@ test_expect_success 'cherry-pick persists data on failure' ' test_path_is_file .git/sequencer/opts ' +test_expect_success 'cherry-pick mid-cherry-pick-sequence' ' + pristine_detach initial && + test_must_fail git cherry-pick base..anotherpick && + test_cmp_rev picked CHERRY_PICK_HEAD && + # "oops, I forgot that these patches rely on the change from base" + git checkout HEAD foo && + git cherry-pick base && + git cherry-pick picked && + git cherry-pick --continue && + git diff --exit-code anotherpick +' + test_expect_success 'cherry-pick persists opts correctly' ' pristine_detach initial && test_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick && @@ -189,10 +203,10 @@ test_expect_success '--abort refuses to clobber unrelated change, harder case' ' test_cmp_rev initial HEAD ' -test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' ' +test_expect_success 'cherry-pick still writes sequencer state when one commit is left' ' pristine_detach initial && test_must_fail git cherry-pick base..picked && - test_path_is_missing .git/sequencer && + test_path_is_dir .git/sequencer && echo "resolved" >foo && git add foo && git commit && @@ -213,7 +227,7 @@ test_expect_success 'cherry-pick cleans up sequencer state when one commit is le test_cmp expect actual ' -test_expect_failure '--abort after last commit in sequence' ' +test_expect_success '--abort after last commit in sequence' ' pristine_detach initial && test_must_fail git cherry-pick base..picked && git cherry-pick --abort && @@ -243,7 +257,66 @@ test_expect_success '--continue complains when there are unresolved conflicts' ' test_must_fail git cherry-pick --continue ' -test_expect_success '--continue continues after conflicts are resolved' ' +test_expect_success '--continue of single cherry-pick' ' + pristine_detach initial && + echo c >expect && + test_must_fail git cherry-pick picked && + echo c >foo && + git add foo && + git cherry-pick --continue && + + test_cmp expect foo && + test_cmp_rev initial HEAD^ && + git diff --exit-code HEAD && + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD +' + +test_expect_success '--continue of single revert' ' + pristine_detach initial && + echo resolved >expect && + echo "Revert \"picked\"" >expect.msg && + test_must_fail git revert picked && + echo resolved >foo && + git add foo && + git cherry-pick --continue && + + git diff --exit-code HEAD && + test_cmp expect foo && + test_cmp_rev initial HEAD^ && + git diff-tree -s --pretty=tformat:%s HEAD >msg && + test_cmp expect.msg msg && + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD && + test_must_fail git rev-parse --verify REVERT_HEAD +' + +test_expect_success '--continue after resolving conflicts' ' + pristine_detach initial && + echo d >expect && + cat >expect.log <<-\EOF && + OBJID + :100644 100644 OBJID OBJID M foo + OBJID + :100644 100644 OBJID OBJID M foo + OBJID + :100644 100644 OBJID OBJID M unrelated + OBJID + :000000 100644 OBJID OBJID A foo + :000000 100644 OBJID OBJID A unrelated + EOF + test_must_fail git cherry-pick base..anotherpick && + echo c >foo && + git add foo && + git cherry-pick --continue && + { + git rev-list HEAD | + git diff-tree --root --stdin | + sed "s/$_x40/OBJID/g" + } >actual.log && + test_cmp expect foo && + test_cmp expect.log actual.log +' + +test_expect_success '--continue after resolving conflicts and committing' ' pristine_detach initial && test_must_fail git cherry-pick base..anotherpick && echo "c" >foo && @@ -270,6 +343,29 @@ test_expect_success '--continue continues after conflicts are resolved' ' test_cmp expect actual ' +test_expect_success '--continue asks for help after resolving patch to nil' ' + pristine_detach conflicting && + test_must_fail git cherry-pick initial..picked && + + test_cmp_rev unrelatedpick CHERRY_PICK_HEAD && + git checkout HEAD -- unrelated && + test_must_fail git cherry-pick --continue 2>msg && + test_i18ngrep "The previous cherry-pick is now empty" msg +' + +test_expect_success 'follow advice and skip nil patch' ' + pristine_detach conflicting && + test_must_fail git cherry-pick initial..picked && + + git checkout HEAD -- unrelated && + test_must_fail git cherry-pick --continue && + git reset && + git cherry-pick --continue && + + git rev-list initial..HEAD >commits && + test_line_count = 3 commits +' + test_expect_success '--continue respects opts' ' pristine_detach initial && test_must_fail git cherry-pick -x base..anotherpick && @@ -288,6 +384,29 @@ test_expect_success '--continue respects opts' ' grep "cherry picked from" anotherpick_msg ' +test_expect_success '--continue of single-pick respects -x' ' + pristine_detach initial && + test_must_fail git cherry-pick -x picked && + echo c >foo && + git add foo && + git cherry-pick --continue && + test_path_is_missing .git/sequencer && + git cat-file commit HEAD >msg && + grep "cherry picked from" msg +' + +test_expect_success '--continue respects -x in first commit in multi-pick' ' + pristine_detach initial && + test_must_fail git cherry-pick -x picked anotherpick && + echo c >foo && + git add foo && + git cherry-pick --continue && + test_path_is_missing .git/sequencer && + git cat-file commit HEAD^ >msg && + picked=$(git rev-parse --verify picked) && + grep "cherry picked from.*$picked" msg +' + test_expect_success '--signoff is not automatically propagated to resolved conflict' ' pristine_detach initial && test_must_fail git cherry-pick --signoff base..anotherpick && @@ -306,6 +425,32 @@ test_expect_success '--signoff is not automatically propagated to resolved confl grep "Signed-off-by:" anotherpick_msg ' +test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' ' + pristine_detach initial && + test_must_fail git cherry-pick -s picked anotherpick && + echo c >foo && + git add foo && + git cherry-pick --continue && + + git diff --exit-code HEAD && + test_cmp_rev initial HEAD^^ && + git cat-file commit HEAD^ >msg && + ! grep Signed-off-by: msg +' + +test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' ' + pristine_detach initial && + test_must_fail git cherry-pick -s picked && + echo c >foo && + git add foo && + git cherry-pick --continue && + + git diff --exit-code HEAD && + test_cmp_rev initial HEAD^ && + git cat-file commit HEAD >msg && + ! grep Signed-off-by: msg +' + test_expect_success 'malformed instruction sheet 1' ' pristine_detach initial && test_must_fail git cherry-pick base..anotherpick && @@ -328,4 +473,9 @@ test_expect_success 'malformed instruction sheet 2' ' test_must_fail git cherry-pick --continue ' +test_expect_success 'empty commit set' ' + pristine_detach initial && + test_expect_code 128 git cherry-pick base..base +' + test_done diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh index 94373ca9a..b1361ce54 100755 --- a/t/t4131-apply-fake-ancestor.sh +++ b/t/t4131-apply-fake-ancestor.sh @@ -11,7 +11,7 @@ test_expect_success 'setup' ' test_commit 1 && test_commit 2 && mkdir sub && - test_commit 3 sub/3 && + test_commit 3 sub/3.t && test_commit 4 ' diff --git a/t/t4136-apply-check.sh b/t/t4136-apply-check.sh new file mode 100755 index 000000000..a321f7c24 --- /dev/null +++ b/t/t4136-apply-check.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +test_description='git apply should exit non-zero with unrecognized input.' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit 1 +' + +test_expect_success 'apply --check exits non-zero with unrecognized input' ' + test_must_fail git apply --check - <<-\EOF + I am not a patch + I look nothing like a patch + git apply must fail + EOF +' + +test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index d9068981f..c05c676ca 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -242,6 +242,14 @@ test_expect_success \ 'git archive --list outside of a git repo' \ 'GIT_DIR=some/non-existing/directory git archive --list' +test_expect_success 'clients cannot access unreachable commits' ' + test_commit unreachable && + sha1=`git rev-parse HEAD` && + git reset --hard HEAD^ && + git archive $sha1 >remote.tar && + test_must_fail git archive --remote=. $sha1 >remote.tar +' + test_expect_success 'git-archive --prefix=olde-' ' git archive --prefix=olde- >h.tar HEAD && ( diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index bafcca765..9bf69e9a0 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -97,7 +97,7 @@ test_expect_success 'setup' ' git symbolic-ref HEAD refs/heads/B ' -pull_to_client 1st "B A" $((11*3)) +pull_to_client 1st "refs/heads/B refs/heads/A" $((11*3)) test_expect_success 'post 1st pull setup' ' add A11 $A10 && @@ -110,9 +110,9 @@ test_expect_success 'post 1st pull setup' ' done ' -pull_to_client 2nd "B" $((64*3)) +pull_to_client 2nd "refs/heads/B" $((64*3)) -pull_to_client 3rd "A" $((1*3)) +pull_to_client 3rd "refs/heads/A" $((1*3)) test_expect_success 'clone shallow' ' git clone --depth 2 "file://$(pwd)/." shallow diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh new file mode 100755 index 000000000..edea9f957 --- /dev/null +++ b/t/t5527-fetch-odd-refs.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +test_description='test fetching of oddly-named refs' +. ./test-lib.sh + +# afterwards we will have: +# HEAD - two +# refs/for/refs/heads/master - one +# refs/heads/master - three +test_expect_success 'setup repo with odd suffix ref' ' + echo content >file && + git add . && + git commit -m one && + git update-ref refs/for/refs/heads/master HEAD && + echo content >>file && + git commit -a -m two && + echo content >>file && + git commit -a -m three && + git checkout HEAD^ +' + +test_expect_success 'suffix ref is ignored during fetch' ' + git clone --bare file://"$PWD" suffix && + echo three >expect && + git --git-dir=suffix log -1 --format=%s refs/heads/master >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 64767d870..1eea64765 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -40,6 +40,22 @@ test_expect_success 'setup remote repository' ' mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH" ' +test_expect_success 'create password-protected repository' ' + mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" && + cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" +' + +test_expect_success 'setup askpass helper' ' + cat >askpass <<-\EOF && + #!/bin/sh + echo user@host + EOF + chmod +x askpass && + GIT_ASKPASS="$PWD/askpass" && + export GIT_ASKPASS +' + test_expect_success 'clone remote repository' ' cd "$ROOT_PATH" && git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone @@ -144,6 +160,24 @@ test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' ' test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \ "$ROOT_PATH"/test_repo_clone master +test_expect_success 'push to password-protected repository (user in URL)' ' + test_commit pw-user && + git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD && + git rev-parse --verify HEAD >expect && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \ + rev-parse --verify HEAD >actual && + test_cmp expect actual +' + +test_expect_failure 'push to password-protected repository (no user in URL)' ' + test_commit pw-nouser && + git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD && + git rev-parse --verify HEAD >expect && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \ + rev-parse --verify HEAD >actual && + test_cmp expect actual +' + stop_httpd test_done diff --git a/t/t7106-reset-sequence.sh b/t/t7106-reset-sequence.sh deleted file mode 100755 index 83f7ea59c..000000000 --- a/t/t7106-reset-sequence.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh - -test_description='Test interaction of reset --hard with sequencer - - + anotherpick: rewrites foo to d - + picked: rewrites foo to c - + unrelatedpick: rewrites unrelated to reallyunrelated - + base: rewrites foo to b - + initial: writes foo as a, unrelated as unrelated -' - -. ./test-lib.sh - -pristine_detach () { - git cherry-pick --quit && - git checkout -f "$1^0" && - git read-tree -u --reset HEAD && - git clean -d -f -f -q -x -} - -test_expect_success setup ' - echo unrelated >unrelated && - git add unrelated && - test_commit initial foo a && - test_commit base foo b && - test_commit unrelatedpick unrelated reallyunrelated && - test_commit picked foo c && - test_commit anotherpick foo d && - git config advice.detachedhead false - -' - -test_expect_success 'reset --hard cleans up sequencer state, providing one-level undo' ' - pristine_detach initial && - test_must_fail git cherry-pick base..anotherpick && - test_path_is_dir .git/sequencer && - git reset --hard && - test_path_is_missing .git/sequencer && - test_path_is_dir .git/sequencer-old && - git reset --hard && - test_path_is_missing .git/sequencer-old -' - -test_expect_success 'cherry-pick --abort does not leave sequencer-old dir' ' - pristine_detach initial && - test_must_fail git cherry-pick base..anotherpick && - git cherry-pick --abort && - test_path_is_missing .git/sequencer && - test_path_is_missing .git/sequencer-old -' - -test_done diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh index 463254c72..83acf68bc 100755 --- a/t/t9301-fast-import-notes.sh +++ b/t/t9301-fast-import-notes.sh @@ -505,9 +505,63 @@ test_expect_success 'verify that non-notes are untouched by a fanout change' ' test_cmp expect_non-note3 actual ' + +# Change the notes for the three top commits +test_tick +cat >input <<INPUT_END +commit refs/notes/many_notes +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +changing notes for the top three commits +COMMIT +from refs/notes/many_notes^0 +INPUT_END + +rm expect +i=$num_commits +j=0 +while test $j -lt 3 +do + cat >>input <<INPUT_END +N inline refs/heads/many_commits~$j +data <<EOF +changed note for commit #$i +EOF +INPUT_END + cat >>expect <<EXPECT_END + commit #$i + changed note for commit #$i +EXPECT_END + i=$(($i - 1)) + j=$(($j + 1)) +done + +test_expect_success 'change a few existing notes' ' + + git fast-import <input && + GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits | + grep "^ " > actual && + test_cmp expect actual + +' + +test_expect_success 'verify that changing notes respect existing fanout' ' + + # None of the entries in the top-level notes tree should be a full SHA1 + git ls-tree --name-only refs/notes/many_notes | + while read path + do + if test $(expr length "$path") -ge 40 + then + return 1 + fi + done + +' + remaining_notes=10 test_tick -cat >>input <<INPUT_END +cat >input <<INPUT_END commit refs/notes/many_notes committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE data <<COMMIT @@ -516,12 +570,11 @@ COMMIT from refs/notes/many_notes^0 INPUT_END -i=$remaining_notes -while test $i -lt $num_commits +i=$(($num_commits - $remaining_notes)) +for sha1 in $(git rev-list -n $i refs/heads/many_commits) do - i=$(($i + 1)) cat >>input <<INPUT_END -N 0000000000000000000000000000000000000000 :$i +N 0000000000000000000000000000000000000000 $sha1 INPUT_END done diff --git a/transport.c b/transport.c index 51814b5da..c2245d4f0 100644 --- a/transport.c +++ b/transport.c @@ -502,7 +502,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus struct ref *refs; connect_setup(transport, for_push, 0); - get_remote_heads(data->fd[0], &refs, 0, NULL, + get_remote_heads(data->fd[0], &refs, for_push ? REF_NORMAL : 0, &data->extra_have); data->got_remote_heads = 1; @@ -537,7 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport, if (!data->got_remote_heads) { connect_setup(transport, 0, 0); - get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL); + get_remote_heads(data->fd[0], &refs_tmp, 0, NULL); data->got_remote_heads = 1; } @@ -772,8 +772,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re struct ref *tmp_refs; connect_setup(transport, 1, 0); - get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL, - NULL); + get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL); data->got_remote_heads = 1; } diff --git a/userdiff.c b/userdiff.c index bf553ad91..7244aac74 100644 --- a/userdiff.c +++ b/userdiff.c @@ -115,7 +115,7 @@ PATTERNS("cpp", /* Jump targets or access declarations */ "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n" /* C/++ functions/methods at top level */ - "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n" + "^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n" /* compound type at top level */ "^((struct|class|enum)[^;]*)$", /* -- */ |