diff options
55 files changed, 858 insertions, 312 deletions
diff --git a/Documentation/RelNotes/1.7.8.1.txt b/Documentation/RelNotes/1.7.8.1.txt index 0e8bd9f5ee..33dc948b94 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/config.txt b/Documentation/config.txt index 5a841da6d4..f22d926777 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -115,35 +115,32 @@ in the appropriate manual page. You will find a description of non-core porcelain configuration variables in the respective porcelain documentation. advice.*:: - When set to 'true', display the given optional help message. - When set to 'false', do not display. The configuration variables - are: + These variables control various optional help messages designed to + aid new users. All 'advice.*' variables default to 'true', and you + can tell Git that you do not need help by setting these to 'false': + -- pushNonFastForward:: Advice shown when linkgit:git-push[1] refuses - non-fast-forward refs. Default: true. + non-fast-forward refs. statusHints:: Directions on how to stage/unstage/add shown in the output of linkgit:git-status[1] and the template shown - when writing commit messages. Default: true. + when writing commit messages. commitBeforeMerge:: Advice shown when linkgit:git-merge[1] refuses to merge to avoid overwriting local changes. - Default: true. resolveConflict:: Advices shown by various commands when conflicts prevent the operation from being performed. - Default: true. implicitIdentity:: Advice on how to set your identity configuration when your information is guessed from the system username and - domain name. Default: true. - + domain name. detachedHead:: - Advice shown when you used linkgit::git-checkout[1] to + Advice shown when you used linkgit:git-checkout[1] to move to the detach HEAD state, to instruct how to create - a local branch after the fact. Default: true. + a local branch after the fact. -- core.fileMode:: diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt index a2a508dc28..55b33d7031 100644 --- a/Documentation/git-fsck.txt +++ b/Documentation/git-fsck.txt @@ -72,30 +72,20 @@ index file, all SHA1 references in .git/refs/*, and all reflogs (unless a blob, the contents are written into the file, rather than its object name. -It tests SHA1 and general object sanity, and it does full tracking of -the resulting reachability and everything else. It prints out any -corruption it finds (missing or bad objects), and if you use the -'--unreachable' flag it will also print out objects that exist but -that aren't reachable from any of the specified head nodes. - -So for example - - git fsck --unreachable HEAD \ - $(git for-each-ref --format="%(objectname)" refs/heads) +DISCUSSION +---------- -will do quite a _lot_ of verification on the tree. There are a few -extra validity tests to be added (make sure that tree objects are -sorted properly etc), but on the whole if 'git fsck' is happy, you -do have a valid tree. +git-fsck tests SHA1 and general object sanity, and it does full tracking +of the resulting reachability and everything else. It prints out any +corruption it finds (missing or bad objects), and if you use the +'--unreachable' flag it will also print out objects that exist but that +aren't reachable from any of the specified head nodes (or the default +set, as mentioned above). Any corrupt objects you will have to find in backups or other archives (i.e., you can just remove them and do an 'rsync' with some other site in the hopes that somebody else has the object you have corrupted). -Of course, "valid tree" doesn't mean that it wasn't generated by some -evil person, and the end result might be crap. git is a revision -tracking system, not a quality assurance system ;) - Extracted Diagnostics --------------------- diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index b8db373964..e3c8448614 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 b78f031cd4..a80d94650d 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 b3b59db534..c24dc546d0 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 55cad766c7..df908ed8f5 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 51840b9784..44e73b5a4f 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 efe8b6cce5..86db954730 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 8f2bebecf3..b02e2c4e89 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 c6bc8eb0aa..6207ecd298 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 d07554c884..0dacb8b79c 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 f5d4930590..56bc555d11 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 5efe6c5760..2a144b011c 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 1ea525c10e..028bcbcd75 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 e0b8030f2b..cd1115ffc6 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 1288ffcc52..f16986c0ae 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/setenv.c b/compat/setenv.c index 3a22ea7b75..fc1439a643 100644 --- a/compat/setenv.c +++ b/compat/setenv.c @@ -6,7 +6,10 @@ int gitsetenv(const char *name, const char *value, int replace) size_t namelen, valuelen; char *envstr; - if (!name || !value) return -1; + if (!name || strchr(name, '=') || !value) { + errno = EINVAL; + return -1; + } if (!replace) { char *oldval = NULL; oldval = getenv(name); @@ -16,7 +19,10 @@ int gitsetenv(const char *name, const char *value, int replace) namelen = strlen(name); valuelen = strlen(value); envstr = malloc((namelen + valuelen + 2)); - if (!envstr) return -1; + if (!envstr) { + errno = ENOMEM; + return -1; + } memcpy(envstr, name, namelen); envstr[namelen] = '='; diff --git a/compat/snprintf.c b/compat/snprintf.c index e1e0e7543d..42ea1ac110 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 0000000000..ac09ed89e7 --- /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,43 +876,109 @@ int is_null_stream_filter(struct stream_filter *filter) /* * LF-to-CRLF filter */ + +struct lf_to_crlf_filter { + struct stream_filter filter; + unsigned has_held:1; + char held; +}; + 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; + + /* + * We may be holding onto the CR to see if it is followed by a + * LF, in which case we would need to go to the main loop. + * Otherwise, just emit it to the output stream. + */ + if (lf_to_crlf->has_held && (lf_to_crlf->held != '\r' || !input)) { + output[o++] = lf_to_crlf->held; + lf_to_crlf->has_held = 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++) { + if (count || lf_to_crlf->has_held) { + size_t i; + int was_cr = 0; + + if (lf_to_crlf->has_held) { + was_cr = 1; + lf_to_crlf->has_held = 0; + } + + 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'; + } else if (was_cr) { + /* + * Previous round saw CR and it is not followed + * by a LF; emit the CR before processing the + * current character. + */ + output[o++] = '\r'; + } + + /* + * We may have consumed the last output slot, + * in which case we need to break out of this + * loop; hold the current character before + * returning. + */ + if (*osize_p <= o) { + lf_to_crlf->has_held = 1; + lf_to_crlf->held = ch; + continue; /* break but increment i */ } + + if (ch == '\r') { + was_cr = 1; + continue; + } + + was_cr = 0; output[o++] = ch; } *osize_p -= o; *isize_p -= i; + + if (!lf_to_crlf->has_held && was_cr) { + lf_to_crlf->has_held = 1; + lf_to_crlf->held = '\r'; + } } 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 +1260,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 8d8ea3c45c..f4bfe0f665 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 8b4dd5c022..230e198fc3 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 804001bb4e..5812222eb9 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 69299b7bd2..94d47cbb28 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 edd553b7f6..cdfdd4f791 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 e1ad1a48ce..80e0e8c051 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 5790a91044..27e9fc6ee8 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 b780ba5ca8..48c20b86f3 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -200,7 +200,7 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push) if (start_async(&async)) die("cannot start thread to parse advertised refs"); - get_remote_heads(async.out, &list, 0, NULL, + get_remote_heads(async.out, &list, for_push ? REF_NORMAL : 0, NULL); close(async.out); if (finish_async(&async)) @@ -860,7 +860,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 bc2c046aab..d1f28a6945 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 f435fdb4b1..2d4528f292 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 52cdcc6a63..68c1ba90b9 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 0a4cdfa93e..3c12b05d60 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 75874e85df..2741262369 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 bc73c2099b..76903323af 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 2c4c1c851d..e80050e1fe 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 94373ca9a0..b1361ce546 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 0000000000..a321f7c245 --- /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 d9068981f8..c05c676ca2 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 bafcca765e..9bf69e9a0f 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 0000000000..edea9f957e --- /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 64767d8705..1eea647656 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 83f7ea59c9..0000000000 --- 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 463254c727..83acf68bc3 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 51814b5da3..c2245d4f0d 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 bf553ad91b..7244aac746 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)[^;]*)$", /* -- */ |