diff options
72 files changed, 1141 insertions, 290 deletions
diff --git a/Documentation/RelNotes/2.0.1.txt b/Documentation/RelNotes/2.0.1.txt new file mode 100644 index 0000000000..ce5579db3e --- /dev/null +++ b/Documentation/RelNotes/2.0.1.txt @@ -0,0 +1,115 @@ +Git v2.0.1 Release Notes +======================== + + * We used to unconditionally disable the pager in the pager process + we spawn to feed out output, but that prevented people who want to + run "less" within "less" from doing so. + + * Tools that read diagnostic output in our standard error stream do + not want to see terminal control sequence (e.g. erase-to-eol). + Detect them by checking if the standard error stream is connected + to a tty. + * Reworded the error message given upon a failure to open an existing + loose object file due to e.g. permission issues; it was reported as + the object being corrupt, but that is not quite true. + + * "git log -2master" is a common typo that shows two commits starting + from whichever random branch that is not 'master' that happens to + be checked out currently. + + * The "%<(10,trunc)%s" pretty format specifier in the log family of + commands is used to truncate the string to a given length (e.g. 10 + in the example) with padding to column-align the output, but did + not take into account that number of bytes and number of display + columns are different. + + * The "mailmap.file" configuration option did not support the tilde + expansion (i.e. ~user/path and ~/path). + + * The completion scripts (in contrib/) did not know about quite a few + options that are common between "git merge" and "git pull", and a + couple of options unique to "git merge". + + * "--ignore-space-change" option of "git apply" ignored the spaces + at the beginning of line too aggressively, which is inconsistent + with the option of the same name "diff" and "git diff" have. + + * "git blame" miscounted number of columns needed to show localized + timestamps, resulting in jaggy left-side-edge of the source code + lines in its output. + + * "git blame" assigned the blame to the copy in the working-tree if + the repository is set to core.autocrlf=input and the file used CRLF + line endings. + + * "git commit --allow-empty-message -C $commit" did not work when the + commit did not have any log message. + + * "git diff --find-copies-harder" sometimes pretended as if the mode + bits have changed for paths that are marked with assume-unchanged + bit. + + * "git format-patch" did not enforce the rule that the "--follow" + option from the log/diff family of commands must be used with + exactly one pathspec. + + * "git gc --auto" was recently changed to run in the background to + give control back early to the end-user sitting in front of the + terminal, but it forgot that housekeeping involving reflogs should + be done without other processes competing for accesses to the refs. + + * "git grep -O" to show the lines that hit in the pager did not work + well with case insensitive search. We now spawn "less" with its + "-I" option when it is used as the pager (which is the default). + + * We used to disable threaded "git index-pack" on platforms without + thread-safe pread(); use a different workaround for such + platforms to allow threaded "git index-pack". + + * The error reporting from "git index-pack" has been improved to + distinguish missing objects from type errors. + + * "git mailinfo" used to read beyond the end of header string while + parsing an incoming e-mail message to extract the patch. + + * On a case insensitive filesystem, merge-recursive incorrectly + deleted the file that is to be renamed to a name that is the same + except for case differences. + + * "git pack-objects" unnecessarily copied the previous contents when + extending the hashtable, even though it will populate the table + from scratch anyway. + + * "git rerere forget" did not work well when merge.conflictstyle + was set to a non-default value. + + * "git remote rm" and "git remote prune" can involve removing many + refs at once, which is not a very efficient thing to do when very + many refs exist in the packed-refs file. + + * "git log --exclude=<glob> --all | git shortlog" worked as expected, + but "git shortlog --exclude=<glob> --all", which is supposed to be + identical to the above pipeline, was not accepted at the command + line argument parser level. + + * The autostash mode of "git rebase -i" did not restore the dirty + working tree state if the user aborted the interactive rebase by + emptying the insn sheet. + + * "git show -s" (i.e. show log message only) used to incorrectly emit + an extra blank line after a merge commit. + + * "git status", even though it is a read-only operation, tries to + update the index with refreshed lstat(2) info to optimize future + accesses to the working tree opportunistically, but this could + race with a "read-write" operation that modify the index while it + is running. Detect such a race and avoid overwriting the index. + + * "git status" (and "git commit") behaved as if changes in a modified + submodule are not there if submodule.*.ignore configuration is set, + which was misleading. The configuration is only to unclutter diff + output during the course of development, and should not to hide + changes in the "status" output to cause the users forget to commit + them. + + * The mode to run tests with HTTP server tests disabled was broken. diff --git a/Documentation/RelNotes/2.0.2.txt b/Documentation/RelNotes/2.0.2.txt new file mode 100644 index 0000000000..c48c0aab27 --- /dev/null +++ b/Documentation/RelNotes/2.0.2.txt @@ -0,0 +1,13 @@ +Git v2.0.2 Release Notes +======================== + + * Documentation for "git submodule sync" forgot to say that the subcommand + can take the "--recursive" option. + + * Mishandling of patterns in .gitignore that has trailing SPs quoted + with backslashes (e.g. ones that end with "\ ") have been + corrected. + + * Recent updates to "git repack" started to duplicate objects that + are in packfiles marked with .keep flag into the new packfile by + mistake. diff --git a/Documentation/config.txt b/Documentation/config.txt index 1932e9b9a2..c08286e968 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2293,7 +2293,9 @@ status.submodulesummary:: --summary-limit option of linkgit:git-submodule[1]). Please note that the summary output command will be suppressed for all submodules when `diff.ignoreSubmodules` is set to 'all' or only - for those submodules where `submodule.<name>.ignore=all`. To + for those submodules where `submodule.<name>.ignore=all`. The only + exception to that rule is that status and commit will show staged + submodule changes. To also view the summary for ignored submodules you can either use the --ignore-submodules=dirty command line option or the 'git submodule summary' command, which shows a similar output but does @@ -2324,7 +2326,9 @@ submodule.<name>.fetchRecurseSubmodules:: submodule.<name>.ignore:: Defines under what circumstances "git status" and the diff family show a submodule as modified. When set to "all", it will never be considered - modified, "dirty" will ignore all changes to the submodules work tree and + modified (but it will nonetheless show up in the output of status and + commit when it has been staged), "dirty" will ignore all changes + to the submodules work tree and takes only differences between the HEAD of the submodule and the commit recorded in the superproject into account. "untracked" will additionally let submodules with modified tracked files in their work tree show up. diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 89c4d3e394..8e6af65da0 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -20,7 +20,7 @@ SYNOPSIS 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>] [commit] [--] [<path>...] 'git submodule' [--quiet] foreach [--recursive] <command> -'git submodule' [--quiet] sync [--] [<path>...] +'git submodule' [--quiet] sync [--recursive] [--] [<path>...] DESCRIPTION diff --git a/Documentation/git.txt b/Documentation/git.txt index b075e0bed5..3d9c6d1743 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of Git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v2.0.0/git.html[documentation for release 2.0] +* link:v2.0.1/git.html[documentation for release 2.0.1] * release notes for + link:RelNotes/2.0.1.txt[2.0.1], link:RelNotes/2.0.0.txt[2.0.0]. * link:v1.9.4/git.html[documentation for release 1.9.4] diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index 347a9f76ee..f6c0dfd029 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -67,7 +67,9 @@ submodule.<name>.fetchRecurseSubmodules:: submodule.<name>.ignore:: Defines under what circumstances "git status" and the diff family show a submodule as modified. When set to "all", it will never be considered - modified, "dirty" will ignore all changes to the submodules work tree and + modified (but will nonetheless show up in the output of status and + commit when it has been staged), "dirty" will ignore all changes + to the submodules work tree and takes only differences between the HEAD of the submodule and the commit recorded in the superproject into account. "untracked" will additionally let submodules with modified tracked files in their work tree show up. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 5d6dc5b4c6..0986062473 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.0.0 +DEF_VER=v2.0.1 LF=' ' @@ -183,9 +183,6 @@ all:: # Define NO_STRUCT_ITIMERVAL if you don't have struct itimerval # This also implies NO_SETITIMER # -# Define NO_THREAD_SAFE_PREAD if your pread() implementation is not -# thread-safe. (e.g. compat/pread.c or cygwin) -# # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is # generally faster on your platform than accessing the working directory. # @@ -1339,10 +1336,6 @@ endif ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD COMPAT_OBJS += compat/pread.o - NO_THREAD_SAFE_PREAD = YesPlease -endif -ifdef NO_THREAD_SAFE_PREAD - BASIC_CFLAGS += -DNO_THREAD_SAFE_PREAD endif ifdef NO_FAST_WORKING_DIRECTORY BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY @@ -1 +1 @@ -Documentation/RelNotes/2.0.0.txt
\ No newline at end of file +Documentation/RelNotes/2.0.2.txt
\ No newline at end of file diff --git a/builtin/annotate.c b/builtin/annotate.c index fc43eed36b..da413ae0d1 100644 --- a/builtin/annotate.c +++ b/builtin/annotate.c @@ -5,20 +5,18 @@ */ #include "git-compat-util.h" #include "builtin.h" +#include "argv-array.h" int cmd_annotate(int argc, const char **argv, const char *prefix) { - const char **nargv; + struct argv_array args = ARGV_ARRAY_INIT; int i; - nargv = xmalloc(sizeof(char *) * (argc + 2)); - nargv[0] = "annotate"; - nargv[1] = "-c"; + argv_array_pushl(&args, "annotate", "-c", NULL); for (i = 1; i < argc; i++) { - nargv[i+1] = argv[i]; + argv_array_push(&args, argv[i]); } - nargv[argc + 1] = NULL; - return cmd_blame(argc + 1, nargv, prefix); + return cmd_blame(args.argc, args.argv, prefix); } diff --git a/builtin/apply.c b/builtin/apply.c index 87439fad11..9c5724eacc 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -300,11 +300,13 @@ static int fuzzy_matchlines(const char *s1, size_t n1, while ((*last2 == '\r') || (*last2 == '\n')) last2--; - /* skip leading whitespace */ - while (isspace(*s1) && (s1 <= last1)) - s1++; - while (isspace(*s2) && (s2 <= last2)) - s2++; + /* skip leading whitespaces, if both begin with whitespace */ + if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) { + while (isspace(*s1) && (s1 <= last1)) + s1++; + while (isspace(*s2) && (s2 <= last2)) + s2++; + } /* early return if both lines are empty */ if ((s1 > last1) && (s2 > last2)) return 1; diff --git a/builtin/blame.c b/builtin/blame.c index b84e375b5c..ef7cb1d254 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1556,22 +1556,29 @@ static void assign_blame(struct scoreboard *sb, int opt) static const char *format_time(unsigned long time, const char *tz_str, int show_raw_time) { - static char time_buf[128]; + static struct strbuf time_buf = STRBUF_INIT; + strbuf_reset(&time_buf); if (show_raw_time) { - snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str); + strbuf_addf(&time_buf, "%lu %s", time, tz_str); } else { const char *time_str; - int time_len; + size_t time_width; int tz; tz = atoi(tz_str); time_str = show_date(time, tz, blame_date_mode); - time_len = strlen(time_str); - memcpy(time_buf, time_str, time_len); - memset(time_buf + time_len, ' ', blame_date_width - time_len); + strbuf_addstr(&time_buf, time_str); + /* + * Add space paddings to time_buf to display a fixed width + * string, and use time_width for display width calibration. + */ + for (time_width = utf8_strwidth(time_str); + time_width < blame_date_width; + time_width++) + strbuf_addch(&time_buf, ' '); } - return time_buf; + return time_buf.buf; } #define OUTPUT_ANNOTATE_COMPAT 001 @@ -2100,7 +2107,6 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, if (strbuf_read(&buf, 0, 0) < 0) die_errno("failed to read from stdin"); } - convert_to_git(path, buf.buf, buf.len, &buf, 0); origin->file.ptr = buf.buf; origin->file.size = buf.len; pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1); @@ -2343,7 +2349,14 @@ parse_done: blame_date_width = sizeof("2006-10-19"); break; case DATE_RELATIVE: - /* "normal" is used as the fallback for "relative" */ + /* TRANSLATORS: This string is used to tell us the maximum + display width for a relative timestamp in "git blame" + output. For C locale, "4 years, 11 months ago", which + takes 22 places, is the longest among various forms of + relative timestamps, but your language may need more or + fewer display columns. */ + blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */ + break; case DATE_LOCAL: case DATE_NORMAL: blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700"); diff --git a/builtin/clean.c b/builtin/clean.c index 9a9151575d..27701d222c 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -48,7 +48,7 @@ enum color_clean { CLEAN_COLOR_PROMPT = 2, CLEAN_COLOR_HEADER = 3, CLEAN_COLOR_HELP = 4, - CLEAN_COLOR_ERROR = 5, + CLEAN_COLOR_ERROR = 5 }; #define MENU_OPTS_SINGLETON 01 diff --git a/builtin/commit.c b/builtin/commit.c index 8b85df475b..39cf8976e3 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -650,9 +650,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } else if (use_message) { char *buffer; buffer = strstr(use_message_buffer, "\n\n"); - if (!use_editor && (!buffer || buffer[2] == '\0')) - die(_("commit has empty message")); - strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); + if (buffer) + strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); hook_arg1 = "commit"; hook_arg2 = use_message; } else if (fixup_message) { @@ -833,8 +832,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (get_sha1(parent, sha1)) commitable = !!active_nr; - else - commitable = index_differs_from(parent, 0); + else { + /* + * Unless the user did explicitly request a submodule + * ignore mode by passing a command line option we do + * not ignore any changed submodule SHA-1s when + * comparing index and parent, no matter what is + * configured. Otherwise we won't commit any + * submodules which were manually staged, which would + * be really confusing. + */ + int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG; + if (ignore_submodule_arg && + !strcmp(ignore_submodule_arg, "all")) + diff_flags |= DIFF_OPT_IGNORE_SUBMODULES; + commitable = index_differs_from(parent, diff_flags); + } } strbuf_release(&committer_ident); diff --git a/builtin/gc.c b/builtin/gc.c index 85f5c2bc62..8d219d8c42 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -26,6 +26,7 @@ static const char * const builtin_gc_usage[] = { }; static int pack_refs = 1; +static int prune_reflogs = 1; static int aggressive_depth = 250; static int aggressive_window = 250; static int gc_auto_threshold = 6700; @@ -258,6 +259,19 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) return NULL; } +static int gc_before_repack(void) +{ + if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD)) + return error(FAILED_RUN, pack_refs_cmd.argv[0]); + + if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD)) + return error(FAILED_RUN, reflog.argv[0]); + + pack_refs = 0; + prune_reflogs = 0; + return 0; +} + int cmd_gc(int argc, const char **argv, const char *prefix) { int aggressive = 0; @@ -320,12 +334,15 @@ int cmd_gc(int argc, const char **argv, const char *prefix) fprintf(stderr, _("Auto packing the repository for optimum performance.\n")); fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n")); } - if (detach_auto) + if (detach_auto) { + if (gc_before_repack()) + return -1; /* * failure to daemonize is ok, we'll continue * in foreground */ daemonize(); + } } else add_repack_all_option(); @@ -337,11 +354,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) name, (uintmax_t)pid); } - if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD)) - return error(FAILED_RUN, pack_refs_cmd.argv[0]); - - if (run_command_v_opt(reflog.argv, RUN_GIT_CMD)) - return error(FAILED_RUN, reflog.argv[0]); + if (gc_before_repack()) + return -1; if (run_command_v_opt(repack.argv, RUN_GIT_CMD)) return error(FAILED_RUN, repack.argv[0]); diff --git a/builtin/grep.c b/builtin/grep.c index 69ac2d8797..b8d440d0e0 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -874,6 +874,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (len > 4 && is_dir_sep(pager[len - 5])) pager += len - 4; + if (opt.ignore_case && !strcmp("less", pager)) + string_list_append(&path_list, "-I"); + if (!strcmp("less", pager) || !strcmp("vi", pager)) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "+/%s%s", diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 459b9f07bb..8b3bd29dbc 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -40,17 +40,13 @@ struct base_data { int ofs_first, ofs_last; }; -#if !defined(NO_PTHREADS) && defined(NO_THREAD_SAFE_PREAD) -/* pread() emulation is not thread-safe. Disable threading. */ -#define NO_PTHREADS -#endif - struct thread_local { #ifndef NO_PTHREADS pthread_t thread; #endif struct base_data *base_cache; size_t base_cache_used; + int pack_fd; }; /* @@ -91,7 +87,8 @@ static off_t consumed_bytes; static unsigned deepest_delta; static git_SHA_CTX input_ctx; static uint32_t input_crc32; -static int input_fd, output_fd, pack_fd; +static int input_fd, output_fd; +static const char *curr_pack; #ifndef NO_PTHREADS @@ -134,6 +131,7 @@ static inline void unlock_mutex(pthread_mutex_t *mutex) */ static void init_thread(void) { + int i; init_recursive_mutex(&read_mutex); pthread_mutex_init(&counter_mutex, NULL); pthread_mutex_init(&work_mutex, NULL); @@ -141,11 +139,18 @@ static void init_thread(void) pthread_mutex_init(&deepest_delta_mutex, NULL); pthread_key_create(&key, NULL); thread_data = xcalloc(nr_threads, sizeof(*thread_data)); + for (i = 0; i < nr_threads; i++) { + thread_data[i].pack_fd = open(curr_pack, O_RDONLY); + if (thread_data[i].pack_fd == -1) + die_errno(_("unable to open %s"), curr_pack); + } + threads_active = 1; } static void cleanup_thread(void) { + int i; if (!threads_active) return; threads_active = 0; @@ -154,6 +159,8 @@ static void cleanup_thread(void) pthread_mutex_destroy(&work_mutex); if (show_stat) pthread_mutex_destroy(&deepest_delta_mutex); + for (i = 0; i < nr_threads; i++) + close(thread_data[i].pack_fd); pthread_key_delete(key); free(thread_data); } @@ -200,8 +207,13 @@ static unsigned check_object(struct object *obj) if (!(obj->flags & FLAG_CHECKED)) { unsigned long size; int type = sha1_object_info(obj->sha1, &size); - if (type != obj->type || type <= 0) - die(_("object of unexpected type")); + if (type <= 0) + die(_("did not receive expected object %s"), + sha1_to_hex(obj->sha1)); + if (type != obj->type) + die(_("object %s: expected type %s, found %s"), + sha1_to_hex(obj->sha1), + typename(obj->type), typename(type)); obj->flags |= FLAG_CHECKED; return 1; } @@ -288,13 +300,13 @@ static const char *open_pack_file(const char *pack_name) output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600); if (output_fd < 0) die_errno(_("unable to create '%s'"), pack_name); - pack_fd = output_fd; + nothread_data.pack_fd = output_fd; } else { input_fd = open(pack_name, O_RDONLY); if (input_fd < 0) die_errno(_("cannot open packfile '%s'"), pack_name); output_fd = -1; - pack_fd = input_fd; + nothread_data.pack_fd = input_fd; } git_SHA1_Init(&input_ctx); return pack_name; @@ -542,7 +554,7 @@ static void *unpack_data(struct object_entry *obj, do { ssize_t n = (len < 64*1024) ? len : 64*1024; - n = pread(pack_fd, inbuf, n, from); + n = xpread(get_thread_data()->pack_fd, inbuf, n, from); if (n < 0) die_errno(_("cannot pread pack file")); if (!n) @@ -1491,7 +1503,7 @@ static void show_pack_info(int stat_only) int cmd_index_pack(int argc, const char **argv, const char *prefix) { int i, fix_thin_pack = 0, verify = 0, stat_only = 0; - const char *curr_pack, *curr_index; + const char *curr_index; const char *index_name = NULL, *pack_name = NULL; const char *keep_name = NULL, *keep_msg = NULL; char *index_name_buf = NULL, *keep_name_buf = NULL; diff --git a/builtin/log.c b/builtin/log.c index c599eacf72..4f678136d1 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -158,13 +158,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, if (rev->show_notes) init_display_notes(&rev->notes_opt); - if (rev->diffopt.pickaxe || rev->diffopt.filter) + if (rev->diffopt.pickaxe || rev->diffopt.filter || + DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) rev->always_show_header = 0; - if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { - rev->always_show_header = 0; - if (rev->diffopt.pathspec.nr != 1) - usage("git logs can only follow renames on one pathname at a time"); - } if (source) rev->show_source = 1; diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 2c3cd8eab7..cf11c8d607 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -334,7 +334,7 @@ static int check_header(const struct strbuf *line, } if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) { for (i = 0; header[i]; i++) { - if (!memcmp("Subject", header[i], 7)) { + if (!strcmp("Subject", header[i])) { handle_header(&hdr_data[i], line); ret = 1; goto check_header_out; @@ -929,13 +929,13 @@ static void handle_info(void) else continue; - if (!memcmp(header[i], "Subject", 7)) { + if (!strcmp(header[i], "Subject")) { if (!keep_subject) { cleanup_subject(hdr); cleanup_space(hdr); } output_header_lines(fout, "Subject", hdr); - } else if (!memcmp(header[i], "From", 4)) { + } else if (!strcmp(header[i], "From")) { cleanup_space(hdr); handle_from(hdr); fprintf(fout, "Author: %s\n", name.buf); diff --git a/builtin/mv.c b/builtin/mv.c index 2a7243f52e..180ef99127 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -203,7 +203,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); - else if (lstat(dst, &st) == 0) { + else if (lstat(dst, &st) == 0 && + (!ignore_case || strcasecmp(src, dst))) { bad = _("destination exists"); if (force) { /* diff --git a/builtin/remote.c b/builtin/remote.c index b3ab4cf8f6..9b3e368983 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -749,15 +749,23 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { + const char **branch_names; int i, result = 0; + + branch_names = xmalloc(branches->nr * sizeof(*branch_names)); + for (i = 0; i < branches->nr; i++) + branch_names[i] = branches->items[i].string; + result |= repack_without_refs(branch_names, branches->nr); + free(branch_names); + for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; const char *refname = item->string; - unsigned char *sha1 = item->util; - if (delete_ref(refname, sha1, 0)) + if (delete_ref(refname, NULL, 0)) result |= error(_("Could not remove branch %s"), refname); } + return result; } @@ -789,10 +797,6 @@ static int rm(int argc, const char **argv) known_remotes.to_delete = remote; for_each_remote(add_known_remote, &known_remotes); - strbuf_addf(&buf, "remote.%s", remote->name); - if (git_config_rename_section(buf.buf, NULL) < 1) - return error(_("Could not remove config section '%s'"), buf.buf); - read_branches(); for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; @@ -837,6 +841,12 @@ static int rm(int argc, const char **argv) } string_list_clear(&skipped, 0); + if (!result) { + strbuf_addf(&buf, "remote.%s", remote->name); + if (git_config_rename_section(buf.buf, NULL) < 1) + return error(_("Could not remove config section '%s'"), buf.buf); + } + return result; } @@ -1303,6 +1313,8 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; + struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; + const char **delete_refs; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1316,11 +1328,20 @@ static int prune_remote(const char *remote, int dry_run) states.remote->url_nr ? states.remote->url[0] : _("(no URL)")); + + delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); + for (i = 0; i < states.stale.nr; i++) + delete_refs[i] = states.stale.items[i].util; + if (!dry_run) + result |= repack_without_refs(delete_refs, states.stale.nr); + free(delete_refs); } for (i = 0; i < states.stale.nr; i++) { const char *refname = states.stale.items[i].util; + string_list_insert(&delete_refs_list, refname); + if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1330,9 +1351,11 @@ static int prune_remote(const char *remote, int dry_run) else printf_ln(_(" * [pruned] %s"), abbrev_ref(refname, "refs/remotes/")); - warn_dangling_symref(stdout, dangling_msg, refname); } + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); + string_list_clear(&delete_refs_list, 0); + free_remote_ref_states(&states); return result; } diff --git a/builtin/repack.c b/builtin/repack.c index 6b0b62dcb2..36c1cf9c25 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -10,6 +10,7 @@ static int delta_base_offset = 1; static int pack_kept_objects = -1; +static int write_bitmaps = -1; static char *packdir, *packtmp; static const char *const git_repack_usage[] = { @@ -27,6 +28,10 @@ static int repack_config(const char *var, const char *value, void *cb) pack_kept_objects = git_config_bool(var, value); return 0; } + if (!strcmp(var, "pack.writebitmaps")) { + write_bitmaps = git_config_bool(var, value); + return 0; + } return git_default_config(var, value, cb); } @@ -149,7 +154,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix) int no_update_server_info = 0; int quiet = 0; int local = 0; - int write_bitmap = -1; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -168,7 +172,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) OPT__QUIET(&quiet, N_("be quiet")), OPT_BOOL('l', "local", &local, N_("pass --local to git-pack-objects")), - OPT_BOOL('b', "write-bitmap-index", &write_bitmap, + OPT_BOOL('b', "write-bitmap-index", &write_bitmaps, N_("write bitmap index")), OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"), N_("with -A, do not loosen objects older than this")), @@ -191,7 +195,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) git_repack_usage, 0); if (pack_kept_objects < 0) - pack_kept_objects = write_bitmap; + pack_kept_objects = write_bitmaps > 0; packdir = mkpathdup("%s/pack", get_object_directory()); packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid()); @@ -217,9 +221,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_pushf(&cmd_args, "--no-reuse-delta"); if (no_reuse_object) argv_array_pushf(&cmd_args, "--no-reuse-object"); - if (write_bitmap >= 0) + if (write_bitmaps >= 0) argv_array_pushf(&cmd_args, "--%swrite-bitmap-index", - write_bitmap ? "" : "no-"); + write_bitmaps ? "" : "no-"); if (pack_everything & ALL_INTO_ONE) { get_non_kept_pack_filenames(&existing_packs); diff --git a/builtin/rerere.c b/builtin/rerere.c index 4e51addb3e..98eb8c5404 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -60,6 +60,8 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, rerere_usage, 0); + git_config(git_xmerge_config, NULL); + if (autoupdate == 1) flags = RERERE_AUTOUPDATE; if (autoupdate == 0) diff --git a/builtin/update-index.c b/builtin/update-index.c index ba54e19cd5..ebea285e1b 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -637,6 +637,9 @@ static int parse_new_style_cacheinfo(const char *arg, unsigned long ul; char *endp; + if (!arg) + return -1; + errno = 0; ul = strtoul(arg, &endp, 8); if (errno || endp == arg || *endp != ',' || (unsigned int) ul != ul) @@ -279,6 +279,7 @@ struct index_state { initialized : 1; struct hashmap name_hash; struct hashmap dir_hash; + unsigned char sha1[20]; }; extern struct index_state the_index; @@ -1322,6 +1323,8 @@ extern void fsync_or_die(int fd, const char *); extern ssize_t read_in_full(int fd, void *buf, size_t count); extern ssize_t write_in_full(int fd, const void *buf, size_t count); +extern ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset); + static inline ssize_t write_str_in_full(int fd, const char *str) { return write_in_full(fd, str, strlen(str)); diff --git a/combine-diff.c b/combine-diff.c index 24ca7e2334..fd6d63c703 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1339,7 +1339,8 @@ void diff_tree_combined(const unsigned char *sha1, if (show_log_first && i == 0) { show_log(rev); - if (rev->verbose_header && opt->output_format) + if (rev->verbose_header && opt->output_format && + opt->output_format != DIFF_FORMAT_NO_OUTPUT) printf("%s%c", diff_line_prefix(opt), opt->line_termination); } diff --git a/compat/mmap.c b/compat/mmap.c index c9d46d1742..7f662fef7b 100644 --- a/compat/mmap.c +++ b/compat/mmap.c @@ -14,7 +14,7 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of } while (n < length) { - ssize_t count = pread(fd, (char *)start + n, length - n, offset + n); + ssize_t count = xpread(fd, (char *)start + n, length - n, offset + n); if (count == 0) { memset((char *)start+n, 0, length-n); @@ -22,8 +22,6 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of } if (count < 0) { - if (errno == EAGAIN || errno == EINTR) - continue; free(start); errno = EACCES; return MAP_FAILED; @@ -952,7 +952,7 @@ static int git_default_push_config(const char *var, const char *value) static int git_default_mailmap_config(const char *var, const char *value) { if (!strcmp(var, "mailmap.file")) - return git_config_string(&git_mailmap_file, var, value); + return git_config_pathname(&git_mailmap_file, var, value); if (!strcmp(var, "mailmap.blob")) return git_config_string(&git_mailmap_blob, var, value); diff --git a/config.mak.uname b/config.mak.uname index 23a8803656..7846bd7657 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -157,7 +157,6 @@ ifeq ($(uname_O),Cygwin) NO_SYMLINK_HEAD = YesPlease NO_IPV6 = YesPlease OLD_ICONV = UnfortunatelyYes - NO_THREAD_SAFE_PREAD = YesPlease # There are conflicting reports about this. # On some boxes NO_MMAP is needed, and not so elsewhere. # Try commenting this out if you suspect MMAP is more efficient diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2c59a76bc2..019026efcb 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1472,9 +1472,12 @@ _git_log () __git_complete_revlist } +# Common merge options shared by git-merge(1) and git-pull(1). __git_merge_options=" --no-commit --no-stat --log --no-log --squash --strategy --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit + --verify-signatures --no-verify-signatures --gpg-sign + --quiet --verbose --progress --no-progress " _git_merge () @@ -1483,7 +1486,8 @@ _git_merge () case "$cur" in --*) - __gitcomp "$__git_merge_options" + __gitcomp "$__git_merge_options + --rerere-autoupdate --no-rerere-autoupdate --abort" return esac __gitcomp_nl "$(__git_refs)" diff --git a/diff-lib.c b/diff-lib.c index 044872935c..875aff8643 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -97,7 +97,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option) diff_unmerged_stage = 2; entries = active_nr; for (i = 0; i < entries; i++) { - struct stat st; unsigned int oldmode, newmode; struct cache_entry *ce = active_cache[i]; int changed; @@ -115,6 +114,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) unsigned int wt_mode = 0; int num_compare_stages = 0; size_t path_len; + struct stat st; path_len = ce_namelen(ce); @@ -195,26 +195,35 @@ int run_diff_files(struct rev_info *revs, unsigned int option) continue; /* If CE_VALID is set, don't look at workdir for file removal */ - changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st); - if (changed) { - if (changed < 0) { - perror(ce->name); + if (ce->ce_flags & CE_VALID) { + changed = 0; + newmode = ce->ce_mode; + } else { + struct stat st; + + changed = check_removed(ce, &st); + if (changed) { + if (changed < 0) { + perror(ce->name); + continue; + } + diff_addremove(&revs->diffopt, '-', ce->ce_mode, + ce->sha1, !is_null_sha1(ce->sha1), + ce->name, 0); continue; } - diff_addremove(&revs->diffopt, '-', ce->ce_mode, - ce->sha1, !is_null_sha1(ce->sha1), - ce->name, 0); - continue; + + changed = match_stat_with_submodule(&revs->diffopt, ce, &st, + ce_option, &dirty_submodule); + newmode = ce_mode_from_stat(ce, st.st_mode); } - changed = match_stat_with_submodule(&revs->diffopt, ce, &st, - ce_option, &dirty_submodule); + if (!changed && !dirty_submodule) { ce_mark_uptodate(ce); if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) continue; } oldmode = ce->ce_mode; - newmode = ce_mode_from_stat(ce, st.st_mode); diff_change(&revs->diffopt, oldmode, newmode, ce->sha1, (changed ? null_sha1 : ce->sha1), !is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)), @@ -3325,6 +3325,9 @@ void diff_setup_done(struct diff_options *options) } options->diff_path_counter = 0; + + if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1) + die(_("--follow requires exactly one pathspec")); } static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) @@ -508,21 +508,25 @@ void clear_exclude_list(struct exclude_list *el) static void trim_trailing_spaces(char *buf) { - int i, last_space = -1, nr_spaces, len = strlen(buf); - for (i = 0; i < len; i++) - if (buf[i] == '\\') - i++; - else if (buf[i] == ' ') { - if (last_space == -1) { - last_space = i; - nr_spaces = 1; - } else - nr_spaces++; - } else - last_space = -1; - - if (last_space != -1 && last_space + nr_spaces == len) - buf[last_space] = '\0'; + char *p, *last_space = NULL; + + for (p = buf; *p; p++) + switch (*p) { + case ' ': + if (!last_space) + last_space = p; + break; + case '\\': + p++; + if (!*p) + return; + /* fallthrough */ + default: + last_space = NULL; + } + + if (last_space) + *last_space = '\0'; } int add_excludes_from_file_to_list(const char *fname, diff --git a/git-compat-util.h b/git-compat-util.h index f6d3a46622..e6a4159a25 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -531,6 +531,7 @@ extern void *xcalloc(size_t nmemb, size_t size); extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern ssize_t xread(int fd, void *buf, size_t len); extern ssize_t xwrite(int fd, const void *buf, size_t len); +extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset); extern int xdup(int fd); extern FILE *xfdopen(int fd, const char *mode); extern int xmkstemp(char *template); diff --git a/git-pull.sh b/git-pull.sh index 6cd8ebc534..7358fac864 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -58,11 +58,9 @@ pull_ff=$(git config pull.ff) case "$pull_ff" in false) no_ff=--no-ff - break ;; only) ff_only=--ff-only - break ;; esac diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 6ec9d3cb40..f267d8b6c3 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -1049,14 +1049,14 @@ fi has_action "$todo" || - die_abort "Nothing to do" + return 2 cp "$todo" "$todo".backup git_sequence_editor "$todo" || die_abort "Could not execute editor" has_action "$todo" || - die_abort "Nothing to do" + return 2 expand_todo_ids diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh index 71429fd743..66c64d7ef2 100644 --- a/git-rebase--merge.sh +++ b/git-rebase--merge.sh @@ -53,11 +53,12 @@ continue_merge () { } call_merge () { - cmt="$(cat "$state_dir/cmt.$1")" + msgnum="$1" + echo "$msgnum" >"$state_dir/msgnum" + cmt="$(cat "$state_dir/cmt.$msgnum")" echo "$cmt" > "$state_dir/current" hd=$(git rev-parse --verify HEAD) cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD) - msgnum=$(cat "$state_dir/msgnum") eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' eval GITHEAD_$hd='$onto_name' export GITHEAD_$cmt GITHEAD_$hd diff --git a/git-rebase.sh b/git-rebase.sh index 4543815ffd..47ca3b990b 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -155,7 +155,7 @@ move_to_original_branch () { esac } -finish_rebase () { +apply_autostash () { if test -f "$state_dir/autostash" then stash_sha1=$(cat "$state_dir/autostash") @@ -171,6 +171,10 @@ You can run "git stash pop" or "git stash drop" at any time. ' fi fi +} + +finish_rebase () { + apply_autostash && git gc --auto && rm -rf "$state_dir" } @@ -186,6 +190,11 @@ run_specific_rebase () { if test $ret -eq 0 then finish_rebase + elif test $ret -eq 2 # special exit status for rebase -i + then + apply_autostash && + rm -rf "$state_dir" && + die "Nothing to do" fi exit $ret } diff --git a/merge-recursive.c b/merge-recursive.c index a9ab328923..d38a3b2eb5 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -591,6 +591,12 @@ static int remove_file(struct merge_options *o, int clean, return -1; } if (update_working_directory) { + if (ignore_case) { + struct cache_entry *ce; + ce = cache_file_exists(path, strlen(path), ignore_case); + if (ce && ce_stage(ce) == 0) + return 0; + } if (remove_path(path)) return -1; } diff --git a/pack-objects.c b/pack-objects.c index d01d851ce9..4f36c32045 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -47,8 +47,8 @@ static void rehash_objects(struct packing_data *pdata) if (pdata->index_size < 1024) pdata->index_size = 1024; - pdata->index = xrealloc(pdata->index, sizeof(uint32_t) * pdata->index_size); - memset(pdata->index, 0, sizeof(int) * pdata->index_size); + free(pdata->index); + pdata->index = xcalloc(pdata->index_size, sizeof(*pdata->index)); entry = pdata->objects; @@ -64,7 +64,7 @@ void setup_pager(void) { const char *pager = git_pager(isatty(1)); - if (!pager || pager_in_use()) + if (!pager) return; /* @@ -274,7 +274,7 @@ static void add_rfc822_quoted(struct strbuf *out, const char *s, int len) enum rfc2047_type { RFC2047_SUBJECT, - RFC2047_ADDRESS, + RFC2047_ADDRESS }; static int is_rfc2047_special(char ch, enum rfc2047_type type) @@ -1488,13 +1488,18 @@ void format_commit_message(const struct commit *commit, context.commit = commit; context.pretty_ctx = pretty_ctx; context.wrap_start = sb->len; + /* + * convert a commit message to UTF-8 first + * as far as 'format_commit_item' assumes it in UTF-8 + */ context.message = logmsg_reencode(commit, &context.commit_encoding, - output_enc); + utf8); strbuf_expand(sb, format, format_commit_item, &context); rewrap_message_tail(sb, &context, 0, 0, 0); + /* then convert a commit message to an actual output encoding */ if (output_enc) { if (same_encoding(utf8, output_enc)) output_enc = NULL; diff --git a/read-cache.c b/read-cache.c index ba13353b37..7f5645e745 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1477,6 +1477,7 @@ int read_index_from(struct index_state *istate, const char *path) if (verify_hdr(hdr, mmap_size) < 0) goto unmap; + hashcpy(istate->sha1, (unsigned char *)hdr + mmap_size - 20); istate->version = ntohl(hdr->hdr_version); istate->cache_nr = ntohl(hdr->hdr_entries); istate->cache_alloc = alloc_nr(istate->cache_nr); @@ -1760,6 +1761,50 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce, return result; } +/* + * This function verifies if index_state has the correct sha1 of the + * index file. Don't die if we have any other failure, just return 0. + */ +static int verify_index_from(const struct index_state *istate, const char *path) +{ + int fd; + ssize_t n; + struct stat st; + unsigned char sha1[20]; + + if (!istate->initialized) + return 0; + + fd = open(path, O_RDONLY); + if (fd < 0) + return 0; + + if (fstat(fd, &st)) + goto out; + + if (st.st_size < sizeof(struct cache_header) + 20) + goto out; + + n = pread_in_full(fd, sha1, 20, st.st_size - 20); + if (n != 20) + goto out; + + if (hashcmp(istate->sha1, sha1)) + goto out; + + close(fd); + return 1; + +out: + close(fd); + return 0; +} + +static int verify_index(const struct index_state *istate) +{ + return verify_index_from(istate, get_index_file()); +} + static int has_racy_timestamp(struct index_state *istate) { int entries = istate->cache_nr; @@ -1779,7 +1824,7 @@ static int has_racy_timestamp(struct index_state *istate) void update_index_if_able(struct index_state *istate, struct lock_file *lockfile) { if ((istate->cache_changed || has_racy_timestamp(istate)) && - !write_index(istate, lockfile->fd)) + verify_index(istate) && !write_index(istate, lockfile->fd)) commit_locked_index(lockfile); else rollback_lock_file(lockfile); @@ -1611,6 +1611,7 @@ int peel_ref(const char *refname, unsigned char *sha1) struct warn_if_dangling_data { FILE *fp; const char *refname; + const struct string_list *refnames; const char *msg_fmt; }; @@ -1625,8 +1626,12 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha return 0; resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL); - if (!resolves_to || strcmp(resolves_to, d->refname)) + if (!resolves_to + || (d->refname + ? strcmp(resolves_to, d->refname) + : !string_list_has_string(d->refnames, resolves_to))) { return 0; + } fprintf(d->fp, d->msg_fmt, refname); fputc('\n', d->fp); @@ -1639,6 +1644,18 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname) data.fp = fp; data.refname = refname; + data.refnames = NULL; + data.msg_fmt = msg_fmt; + for_each_rawref(warn_if_dangling_symref, &data); +} + +void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames) +{ + struct warn_if_dangling_data data; + + data.fp = fp; + data.refname = NULL; + data.refnames = refnames; data.msg_fmt = msg_fmt; for_each_rawref(warn_if_dangling_symref, &data); } @@ -2431,7 +2448,7 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -static int repack_without_refs(const char **refnames, int n) +int repack_without_refs(const char **refnames, int n) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; @@ -89,6 +89,7 @@ static inline const char *has_glob_specials(const char *pattern) extern int for_each_rawref(each_ref_fn, void *); extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname); +extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list* refnames); /* * Lock the packed-refs file for writing. Flags is passed to @@ -132,6 +133,8 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); +extern int repack_without_refs(const char **refnames, int n); + extern int ref_exists(const char *); /* diff --git a/revision.c b/revision.c index 1cc91e5911..2571ada6bf 100644 --- a/revision.c +++ b/revision.c @@ -1633,6 +1633,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg !strcmp(arg, "--reflog") || !strcmp(arg, "--not") || !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") || !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") || + starts_with(arg, "--exclude=") || starts_with(arg, "--branches=") || starts_with(arg, "--tags=") || starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk=")) { @@ -1648,8 +1649,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->skip_count = atoi(optarg); return argcount; } else if ((*arg == '-') && isdigit(arg[1])) { - /* accept -<digit>, like traditional "head" */ - revs->max_count = atoi(arg + 1); + /* accept -<digit>, like traditional "head" */ + if (strtol_i(arg + 1, 10, &revs->max_count) < 0 || + revs->max_count < 0) + die("'%s': not a non-negative integer", arg + 1); revs->no_walk = 0; } else if (!strcmp(arg, "-n")) { if (argc <= 1) diff --git a/sha1_file.c b/sha1_file.c index 3e9f55f1bb..34d527f670 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1437,19 +1437,23 @@ static int open_sha1_file(const unsigned char *sha1) { int fd; struct alternate_object_database *alt; + int most_interesting_errno; fd = git_open_noatime(sha1_file_name(sha1)); if (fd >= 0) return fd; + most_interesting_errno = errno; prepare_alt_odb(); - errno = ENOENT; for (alt = alt_odb_list; alt; alt = alt->next) { fill_sha1_path(alt->name, sha1); fd = git_open_noatime(alt->base); if (fd >= 0) return fd; + if (most_interesting_errno == ENOENT) + most_interesting_errno = errno; } + errno = most_interesting_errno; return -1; } diff --git a/sideband.c b/sideband.c index d1125f5c52..7f9dc229fb 100644 --- a/sideband.c +++ b/sideband.c @@ -30,7 +30,7 @@ int recv_sideband(const char *me, int in_stream, int out) memcpy(buf, PREFIX, pf); term = getenv("TERM"); - if (term && strcmp(term, "dumb")) + if (isatty(2) && term && strcmp(term, "dumb")) suffix = ANSI_SUFFIX; else suffix = DUMB_SUFFIX; diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index 63beb99828..39e55a13c8 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -806,4 +806,29 @@ test_expect_success !MINGW 'quoting allows trailing whitespace' ' test_cmp err.expect err ' +test_expect_success NOT_MINGW,NOT_CYGWIN 'correct handling of backslashes' ' + rm -rf whitespace && + mkdir whitespace && + >"whitespace/trailing 1 " && + >"whitespace/trailing 2 \\\\" && + >"whitespace/trailing 3 \\\\" && + >"whitespace/trailing 4 \\ " && + >"whitespace/trailing 5 \\ \\ " && + >"whitespace/trailing 6 \\a\\" && + >whitespace/untracked && + sed -e "s/Z$//" >ignore <<-\EOF && + whitespace/trailing 1 \ Z + whitespace/trailing 2 \\\\Z + whitespace/trailing 3 \\\\ Z + whitespace/trailing 4 \\\ Z + whitespace/trailing 5 \\ \\\ Z + whitespace/trailing 6 \\a\\Z + EOF + echo whitespace/untracked >expect && + >err.expect && + git ls-files -o -X ignore whitespace >actual 2>err && + test_cmp expect actual && + test_cmp err.expect err +' + test_done diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh index 178694ee63..1978947c41 100755 --- a/t/t1507-rev-parse-upstream.sh +++ b/t/t1507-rev-parse-upstream.sh @@ -121,7 +121,7 @@ test_expect_success 'merge my-side@{u} records the correct name' ' git branch -D new ;# can fail but is ok git branch -t new my-side@{u} && git merge -s ours new@{u} && - git show -s --pretty=format:%s >actual && + git show -s --pretty=tformat:%s >actual && echo "Merge remote-tracking branch ${sq}origin/side${sq}" >expect && test_cmp expect actual ) diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index fe2fb17102..1bafb9098c 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -29,6 +29,10 @@ test_expect_success 'update-index -h with corrupt index' ' test_i18ngrep "[Uu]sage: git update-index" broken/usage ' +test_expect_success '--cacheinfo complains of missing arguments' ' + test_must_fail git update-index --cacheinfo +' + test_expect_success '--cacheinfo does not accept blob null sha1' ' echo content >file && git add file && diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index be8c1d5ef9..5a27ec9b5e 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -33,6 +33,7 @@ test_expect_success setup ' tr "[a-z]" "[A-Z]" <original >newfile && git add newfile && git commit -a -m"side edits further." && + git branch second-side && tr "[a-m]" "[A-M]" <original >newfile && rm -f original && @@ -41,6 +42,7 @@ test_expect_success setup ' git branch test-rebase side && git branch test-rebase-pick side && git branch test-reference-pick side && + git branch test-conflicts side && git checkout -b test-merge side ' @@ -138,4 +140,17 @@ test_expect_success 'rebase -s funny -Xopt' ' test -f funny.was.run ' +test_expect_success 'rebase --skip works with two conflicts in a row' ' + git checkout second-side && + tr "[A-Z]" "[a-z]" <newfile >tmp && + mv tmp newfile && + git commit -a -m"edit conflicting with side" && + tr "[d-f]" "[D-F]" <newfile >tmp && + mv tmp newfile && + git commit -a -m"another edit conflicting with side" && + test_must_fail git rebase --merge test-conflicts && + test_must_fail git rebase --skip && + git rebase --skip +' + test_done diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh index 90eb26493c..d783f03d3f 100755 --- a/t/t3420-rebase-autostash.sh +++ b/t/t3420-rebase-autostash.sh @@ -167,4 +167,19 @@ testrebase "" .git/rebase-apply testrebase " --merge" .git/rebase-merge testrebase " --interactive" .git/rebase-merge +test_expect_success 'abort rebase -i with --autostash' ' + test_when_finished "git reset --hard" && + echo uncommited-content >file0 && + ( + write_script abort-editor.sh <<-\EOF && + echo >"$1" + EOF + test_set_editor "$(pwd)/abort-editor.sh" && + test_must_fail git rebase -i --autostash HEAD^ && + rm -f abort-editor.sh + ) && + echo uncommited-content >expected && + test_cmp expected file0 +' + test_done diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh index 9d9498bd95..23c0e357a7 100755 --- a/t/t4039-diff-assume-unchanged.sh +++ b/t/t4039-diff-assume-unchanged.sh @@ -28,4 +28,15 @@ test_expect_success 'diff-files does not examine assume-unchanged entries' ' test -z "$(git diff-files -- one)" ' +test_expect_success POSIXPERM 'find-copies-harder is not confused by mode bits' ' + echo content >exec && + chmod +x exec && + git add exec && + git commit -m exec && + git update-index --assume-unchanged exec && + >expect && + git diff-files --find-copies-harder -- exec >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh index 1751c83307..463d63bde0 100755 --- a/t/t4041-diff-submodule-option.sh +++ b/t/t4041-diff-submodule-option.sh @@ -11,6 +11,9 @@ This test tries to verify the sanity of the --submodule option of git diff. . ./test-lib.sh +# Tested non-UTF-8 encoding +test_encoding="ISO8859-1" + # String "added" in German (translated with Google Translate), encoded in UTF-8, # used in sample commit log messages in add_file() function below. added=$(printf "hinzugef\303\274gt") @@ -23,8 +26,8 @@ add_file () { echo "$name" >"$name" && git add "$name" && test_tick && - msg_added_iso88591=$(echo "Add $name ($added $name)" | iconv -f utf-8 -t iso8859-1) && - git -c 'i18n.commitEncoding=iso8859-1' commit -m "$msg_added_iso88591" + msg_added_iso88591=$(echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding) && + git -c "i18n.commitEncoding=$test_encoding" commit -m "$msg_added_iso88591" done >/dev/null && git rev-parse --short --verify HEAD ) diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh index b04fc8fc12..9e29b5262d 100755 --- a/t/t4107-apply-ignore-whitespace.sh +++ b/t/t4107-apply-ignore-whitespace.sh @@ -111,7 +111,6 @@ sed -e 's/T/ /g' > main.c.final <<\EOF #include <stdio.h> void print_int(int num); -T/* a comment */ int func(int num); int main() { @@ -154,7 +153,8 @@ test_expect_success 'patch2 reverse applies with --ignore-space-change' ' git config apply.ignorewhitespace change test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' ' - git apply patch2.patch + git apply patch2.patch && + test_cmp main.c.final main.c ' test_expect_success 'patch3 fails (missing string at EOL)' ' @@ -165,12 +165,8 @@ test_expect_success 'patch4 fails (missing EOL at EOF)' ' test_must_fail git apply patch4.patch ' -test_expect_success 'patch5 applies (leading whitespace)' ' - git apply patch5.patch -' - -test_expect_success 'patches do not mangle whitespace' ' - test_cmp main.c main.c.final +test_expect_success 'patch5 fails (leading whitespace differences matter)' ' + test_must_fail git apply patch5.patch ' test_expect_success 're-create file (with --ignore-whitespace)' ' diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 42866992cf..97fcb31d6e 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -188,4 +188,10 @@ test_expect_success 'shortlog ignores commits with missing authors' ' test_cmp expect actual ' +test_expect_success 'shortlog with revision pseudo options' ' + git shortlog --all && + git shortlog --branches && + git shortlog --exclude=refs/heads/m* --all +' + test_done diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 2a6278bb33..c84ec9ae61 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -7,6 +7,9 @@ test_description='Test pretty formats' . ./test-lib.sh +# Tested non-UTF-8 encoding +test_encoding="ISO8859-1" + sample_utf8_part=$(printf "f\303\244ng") commit_msg () { @@ -27,8 +30,8 @@ test_expect_success 'set up basic repos' ' >bar && git add foo && test_tick && - git config i18n.commitEncoding iso8859-1 && - git commit -m "$(commit_msg iso8859-1)" && + git config i18n.commitEncoding $test_encoding && + git commit -m "$(commit_msg $test_encoding)" && git add bar && test_tick && git commit -m "add bar" && @@ -56,8 +59,8 @@ test_expect_success 'alias user-defined format' ' test_cmp expected actual ' -test_expect_success 'alias user-defined tformat with %s (iso8859-1 encoding)' ' - git config i18n.logOutputEncoding iso8859-1 && +test_expect_success 'alias user-defined tformat with %s (ISO8859-1 encoding)' ' + git config i18n.logOutputEncoding $test_encoding && git log --oneline >expected-s && git log --pretty="tformat:%h %s" >actual-s && git config --unset i18n.logOutputEncoding && @@ -141,9 +144,7 @@ test_expect_success 'setup more commits' ' ' test_expect_success 'left alignment formatting' ' - git log --pretty="format:%<(40)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%<(40)%s" >actual && qz_to_tab_space <<EOF >expected && message two Z message one Z @@ -153,10 +154,19 @@ EOF test_cmp expected actual ' +test_expect_success 'left alignment formatting. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(40)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +message two Z +message one Z +add bar Z +$(commit_msg) Z +EOF + test_cmp expected actual +' + test_expect_success 'left alignment formatting at the nth column' ' - git log --pretty="format:%h %<|(40)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%h %<|(40)%s" >actual && qz_to_tab_space <<EOF >expected && $head1 message two Z $head2 message one Z @@ -166,10 +176,19 @@ EOF test_cmp expected actual ' +test_expect_success 'left alignment formatting at the nth column. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %<|(40)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +$head1 message two Z +$head2 message one Z +$head3 add bar Z +$head4 $(commit_msg) Z +EOF + test_cmp expected actual +' + test_expect_success 'left alignment formatting with no padding' ' - git log --pretty="format:%<(1)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%<(1)%s" >actual && cat <<EOF >expected && message two message one @@ -179,10 +198,19 @@ EOF test_cmp expected actual ' +test_expect_success 'left alignment formatting with no padding. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(1)%s" >actual && + cat <<EOF | iconv -f utf-8 -t $test_encoding >expected && +message two +message one +add bar +$(commit_msg) +EOF + test_cmp expected actual +' + test_expect_success 'left alignment formatting with trunc' ' - git log --pretty="format:%<(10,trunc)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%<(10,trunc)%s" >actual && qz_to_tab_space <<EOF >expected && message .. message .. @@ -192,10 +220,19 @@ EOF test_cmp expected actual ' +test_expect_success 'left alignment formatting with trunc. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +message .. +message .. +add bar Z +initial... +EOF + test_cmp expected actual +' + test_expect_success 'left alignment formatting with ltrunc' ' - git log --pretty="format:%<(10,ltrunc)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%<(10,ltrunc)%s" >actual && qz_to_tab_space <<EOF >expected && ..sage two ..sage one @@ -205,10 +242,19 @@ EOF test_cmp expected actual ' +test_expect_success 'left alignment formatting with ltrunc. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,ltrunc)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +..sage two +..sage one +add bar Z +..${sample_utf8_part}lich +EOF + test_cmp expected actual +' + test_expect_success 'left alignment formatting with mtrunc' ' - git log --pretty="format:%<(10,mtrunc)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%<(10,mtrunc)%s" >actual && qz_to_tab_space <<EOF >expected && mess.. two mess.. one @@ -218,10 +264,19 @@ EOF test_cmp expected actual ' +test_expect_success 'left alignment formatting with mtrunc. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,mtrunc)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +mess.. two +mess.. one +add bar Z +init..lich +EOF + test_cmp expected actual +' + test_expect_success 'right alignment formatting' ' - git log --pretty="format:%>(40)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%>(40)%s" >actual && qz_to_tab_space <<EOF >expected && Z message two Z message one @@ -231,10 +286,19 @@ EOF test_cmp expected actual ' +test_expect_success 'right alignment formatting. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(40)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +Z message two +Z message one +Z add bar +Z $(commit_msg) +EOF + test_cmp expected actual +' + test_expect_success 'right alignment formatting at the nth column' ' - git log --pretty="format:%h %>|(40)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%h %>|(40)%s" >actual && qz_to_tab_space <<EOF >expected && $head1 message two $head2 message one @@ -244,10 +308,19 @@ EOF test_cmp expected actual ' +test_expect_success 'right alignment formatting at the nth column. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %>|(40)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +$head1 message two +$head2 message one +$head3 add bar +$head4 $(commit_msg) +EOF + test_cmp expected actual +' + test_expect_success 'right alignment formatting with no padding' ' - git log --pretty="format:%>(1)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%>(1)%s" >actual && cat <<EOF >expected && message two message one @@ -257,10 +330,19 @@ EOF test_cmp expected actual ' +test_expect_success 'right alignment formatting with no padding. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(1)%s" >actual && + cat <<EOF | iconv -f utf-8 -t $test_encoding >expected && +message two +message one +add bar +$(commit_msg) +EOF + test_cmp expected actual +' + test_expect_success 'center alignment formatting' ' - git log --pretty="format:%><(40)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%><(40)%s" >actual && qz_to_tab_space <<EOF >expected && Z message two Z Z message one Z @@ -270,10 +352,18 @@ EOF test_cmp expected actual ' +test_expect_success 'center alignment formatting. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(40)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +Z message two Z +Z message one Z +Z add bar Z +Z $(commit_msg) Z +EOF + test_cmp expected actual +' test_expect_success 'center alignment formatting at the nth column' ' - git log --pretty="format:%h %><|(40)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%h %><|(40)%s" >actual && qz_to_tab_space <<EOF >expected && $head1 message two Z $head2 message one Z @@ -283,10 +373,19 @@ EOF test_cmp expected actual ' +test_expect_success 'center alignment formatting at the nth column. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %><|(40)%s" >actual && + qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected && +$head1 message two Z +$head2 message one Z +$head3 add bar Z +$head4 $(commit_msg) Z +EOF + test_cmp expected actual +' + test_expect_success 'center alignment formatting with no padding' ' - git log --pretty="format:%><(1)%s" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%><(1)%s" >actual && cat <<EOF >expected && message two message one @@ -296,11 +395,23 @@ EOF test_cmp expected actual ' +# save HEAD's SHA-1 digest (with no abbreviations) to use it below +# as far as the next test amends HEAD +old_head1=$(git rev-parse --verify HEAD~0) +test_expect_success 'center alignment formatting with no padding. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(1)%s" >actual && + cat <<EOF | iconv -f utf-8 -t $test_encoding >expected && +message two +message one +add bar +$(commit_msg) +EOF + test_cmp expected actual +' + test_expect_success 'left/right alignment formatting with stealing' ' git commit --amend -m short --author "long long long <long@me.com>" && - git log --pretty="format:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual && - # complete the incomplete line at the end - echo >>actual && + git log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual && cat <<EOF >expected && short long long long message .. A U Thor @@ -309,6 +420,20 @@ initial... A U Thor EOF test_cmp expected actual ' +test_expect_success 'left/right alignment formatting with stealing. i18n.logOutputEncoding' ' + git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual && + cat <<EOF | iconv -f utf-8 -t $test_encoding >expected && +short long long long +message .. A U Thor +add bar A U Thor +initial... A U Thor +EOF + test_cmp expected actual +' + +# get new digests (with no abbreviations) +head1=$(git rev-parse --verify HEAD~0) && +head2=$(git rev-parse --verify HEAD~1) && test_expect_success 'log decoration properly follows tag chain' ' git tag -a tag1 -m tag1 && @@ -317,9 +442,9 @@ test_expect_success 'log decoration properly follows tag chain' ' git commit --amend -m shorter && git log --no-walk --tags --pretty="%H %d" --decorate=full >actual && cat <<EOF >expected && -6a908c10688b2503073c39c9ba26322c73902bb5 (tag: refs/tags/tag2) -9f716384d92283fb915a4eee5073f030638e05f9 (tag: refs/tags/message-one) -b87e4cccdb77336ea79d89224737be7ea8e95367 (tag: refs/tags/message-two) +$head1 (tag: refs/tags/tag2) +$head2 (tag: refs/tags/message-one) +$old_head1 (tag: refs/tags/message-two) EOF sort actual >actual1 && test_cmp expected actual1 diff --git a/t/t5538-push-shallow.sh b/t/t5538-push-shallow.sh index 8e54ac5746..ceee95b8a4 100755 --- a/t/t5538-push-shallow.sh +++ b/t/t5538-push-shallow.sh @@ -120,63 +120,4 @@ EOF git cat-file blob `echo 1|git hash-object --stdin` >/dev/null ) ' - -if test -n "$NO_CURL" -o -z "$GIT_TEST_HTTPD"; then - say 'skipping remaining tests, git built without http support' - test_done -fi - -. "$TEST_DIRECTORY"/lib-httpd.sh -start_httpd - -test_expect_success 'push to shallow repo via http' ' - git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && - ( - cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && - git config http.receivepack true - ) && - ( - cd full && - commit 9 && - git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master - ) && - ( - cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && - git fsck && - git log --format=%s top/master >actual && - cat <<EOF >expect && -9 -4 -3 -EOF - test_cmp expect actual - ) -' - -test_expect_success 'push from shallow repo via http' ' - mv "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" shallow-upstream.git && - git clone --bare --no-local full "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && - ( - cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && - git config http.receivepack true - ) && - commit 10 && - git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master && - ( - cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && - git fsck && - git log --format=%s top/master >actual && - cat <<EOF >expect && -10 -1 -4 -3 -2 -1 -EOF - test_cmp expect actual - ) -' - -stop_httpd test_done diff --git a/t/t5542-push-http-shallow.sh b/t/t5542-push-http-shallow.sh new file mode 100755 index 0000000000..2a691e09eb --- /dev/null +++ b/t/t5542-push-http-shallow.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +test_description='push from/to a shallow clone over http' + +. ./test-lib.sh + +if test -n "$NO_CURL"; then + say 'skipping test, git built without http support' + test_done +fi + +. "$TEST_DIRECTORY"/lib-httpd.sh +start_httpd + +commit() { + echo "$1" >tracked && + git add tracked && + git commit -m "$1" +} + +test_expect_success 'setup' ' + git config --global transfer.fsckObjects true && + commit 1 && + commit 2 && + commit 3 && + commit 4 && + git clone . full && + ( + git init full-abc && + cd full-abc && + commit a && + commit b && + commit c + ) && + git clone --no-local --depth=2 .git shallow && + git --git-dir=shallow/.git log --format=%s >actual && + cat <<EOF >expect && +4 +3 +EOF + test_cmp expect actual && + git clone --no-local --depth=2 full-abc/.git shallow2 && + git --git-dir=shallow2/.git log --format=%s >actual && + cat <<EOF >expect && +c +b +EOF + test_cmp expect actual +' + +test_expect_success 'push to shallow repo via http' ' + git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + ( + cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git config http.receivepack true + ) && + ( + cd full && + commit 9 && + git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master + ) && + ( + cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git fsck && + git log --format=%s top/master >actual && + cat <<EOF >expect && +9 +4 +3 +EOF + test_cmp expect actual + ) +' + +test_expect_success 'push from shallow repo via http' ' + mv "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" shallow-upstream.git && + git clone --bare --no-local full "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + ( + cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git config http.receivepack true + ) && + commit 10 && + git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master && + ( + cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git fsck && + git log --format=%s top/master >actual && + cat <<EOF >expect && +10 +4 +3 +2 +1 +EOF + test_cmp expect actual + ) +' + +stop_httpd +test_done diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 9d9d9de08e..c277db64f7 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -9,19 +9,32 @@ test_description='git rev-list --pretty=format test' . "$TEST_DIRECTORY"/lib-terminal.sh test_tick +# Tested non-UTF-8 encoding +test_encoding="ISO8859-1" + # String "added" in German # (translated with Google Translate), # encoded in UTF-8, used as a commit log message below. -added=$(printf "added (hinzugef\303\274gt) foo") -added_iso88591=$(echo "$added" | iconv -f utf-8 -t iso8859-1) +added_utf8_part=$(printf "\303\274") +added_utf8_part_iso88591=$(echo "$added_utf8_part" | iconv -f utf-8 -t $test_encoding) +added=$(printf "added (hinzugef${added_utf8_part}gt) foo") +added_iso88591=$(echo "$added" | iconv -f utf-8 -t $test_encoding) # same but "changed" -changed=$(printf "changed (ge\303\244ndert) foo") -changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t iso8859-1) +changed_utf8_part=$(printf "\303\244") +changed_utf8_part_iso88591=$(echo "$changed_utf8_part" | iconv -f utf-8 -t $test_encoding) +changed=$(printf "changed (ge${changed_utf8_part}ndert) foo") +changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t $test_encoding) + +# Count of char to truncate +# Number is chosen so, that non-ACSII characters +# (see $added_utf8_part and $changed_utf8_part) +# fall into truncated parts of appropriate words both from left and right +truncate_count=20 test_expect_success 'setup' ' : >foo && git add foo && - git config i18n.commitEncoding iso8859-1 && + git config i18n.commitEncoding $test_encoding && git commit -m "$added_iso88591" && head1=$(git rev-parse --verify HEAD) && head1_short=$(git rev-parse --verify --short $head1) && @@ -124,9 +137,9 @@ EOF test_format encoding %e <<EOF commit $head2 -iso8859-1 +$test_encoding commit $head1 -iso8859-1 +$test_encoding EOF test_format subject %s <<EOF @@ -136,6 +149,13 @@ commit $head1 $added EOF +test_format subject-truncated "%<($truncate_count,trunc)%s" <<EOF +commit $head2 +changed (ge${changed_utf8_part}ndert).. +commit $head1 +added (hinzugef${added_utf8_part}gt.. +EOF + test_format body %b <<EOF commit $head2 commit $head1 @@ -203,16 +223,16 @@ test_expect_success '%C(auto) respects --color=auto (stdout not tty)' ' ) ' -iconv -f utf-8 -t iso8859-1 > commit-msg <<EOF +iconv -f utf-8 -t $test_encoding > commit-msg <<EOF Test printing of complex bodies This commit message is much longer than the others, -and it will be encoded in iso8859-1. We should therefore -include an iso8859 character: ¡bueno! +and it will be encoded in $test_encoding. We should therefore +include an ISO8859 character: ¡bueno! EOF test_expect_success 'setup complex body' ' - git config i18n.commitencoding iso8859-1 && + git config i18n.commitencoding $test_encoding && echo change2 >foo && git commit -a -F commit-msg && head3=$(git rev-parse --verify HEAD) && head3_short=$(git rev-parse --short $head3) @@ -220,11 +240,11 @@ test_expect_success 'setup complex body' ' test_format complex-encoding %e <<EOF commit $head3 -iso8859-1 +$test_encoding commit $head2 -iso8859-1 +$test_encoding commit $head1 -iso8859-1 +$test_encoding EOF test_format complex-subject %s <<EOF @@ -236,20 +256,47 @@ commit $head1 $added_iso88591 EOF +test_format complex-subject-trunc "%<($truncate_count,trunc)%s" <<EOF +commit $head3 +Test printing of c.. +commit $head2 +changed (ge${changed_utf8_part_iso88591}ndert).. +commit $head1 +added (hinzugef${added_utf8_part_iso88591}gt.. +EOF + +test_format complex-subject-mtrunc "%<($truncate_count,mtrunc)%s" <<EOF +commit $head3 +Test prin..ex bodies +commit $head2 +changed (..dert) foo +commit $head1 +added (hi..f${added_utf8_part_iso88591}gt) foo +EOF + +test_format complex-subject-ltrunc "%<($truncate_count,ltrunc)%s" <<EOF +commit $head3 +.. of complex bodies +commit $head2 +..ged (ge${changed_utf8_part_iso88591}ndert) foo +commit $head1 +.. (hinzugef${added_utf8_part_iso88591}gt) foo +EOF + test_expect_success 'prepare expected messages (for test %b)' ' cat <<-EOF >expected.utf-8 && commit $head3 This commit message is much longer than the others, - and it will be encoded in iso8859-1. We should therefore - include an iso8859 character: ¡bueno! + and it will be encoded in $test_encoding. We should therefore + include an ISO8859 character: ¡bueno! commit $head2 commit $head1 EOF - iconv -f utf-8 -t iso8859-1 expected.utf-8 >expected.iso8859-1 + iconv -f utf-8 -t $test_encoding expected.utf-8 >expected.ISO8859-1 ' -test_format complex-body %b <expected.iso8859-1 +test_format complex-body %b <expected.ISO8859-1 # Git uses i18n.commitEncoding if no i18n.logOutputEncoding set # so unset i18n.commitEncoding to test encoding conversion @@ -264,6 +311,33 @@ commit $head1 $added EOF +test_format complex-subject-commitencoding-unset-trunc "%<($truncate_count,trunc)%s" <<EOF +commit $head3 +Test printing of c.. +commit $head2 +changed (ge${changed_utf8_part}ndert).. +commit $head1 +added (hinzugef${added_utf8_part}gt.. +EOF + +test_format complex-subject-commitencoding-unset-mtrunc "%<($truncate_count,mtrunc)%s" <<EOF +commit $head3 +Test prin..ex bodies +commit $head2 +changed (..dert) foo +commit $head1 +added (hi..f${added_utf8_part}gt) foo +EOF + +test_format complex-subject-commitencoding-unset-ltrunc "%<($truncate_count,ltrunc)%s" <<EOF +commit $head3 +.. of complex bodies +commit $head2 +..ged (ge${changed_utf8_part}ndert) foo +commit $head1 +.. (hinzugef${added_utf8_part}gt) foo +EOF + test_format complex-body-commitencoding-unset %b <expected.utf-8 test_expect_success '%x00 shows NUL' ' diff --git a/t/t6039-merge-ignorecase.sh b/t/t6039-merge-ignorecase.sh new file mode 100755 index 0000000000..a977653147 --- /dev/null +++ b/t/t6039-merge-ignorecase.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +test_description='git-merge with case-changing rename on case-insensitive file system' + +. ./test-lib.sh + +if ! test_have_prereq CASE_INSENSITIVE_FS +then + skip_all='skipping case insensitive tests - case sensitive file system' + test_done +fi + +test_expect_success 'merge with case-changing rename' ' + test $(git config core.ignorecase) = true && + >TestCase && + git add TestCase && + git commit -m "add TestCase" && + git tag baseline + git checkout -b with-camel && + >foo && + git add foo && + git commit -m "intervening commit" && + git checkout master && + git rm TestCase && + >testcase && + git add testcase && + git commit -m "rename to testcase" && + git checkout with-camel && + git merge master -m "merge" && + test_path_is_file testcase +' + +test_expect_success 'merge with case-changing rename on both sides' ' + git checkout master && + git reset --hard baseline && + git branch -D with-camel && + git checkout -b with-camel && + git mv TestCase testcase && + git commit -m "recase on branch" && + >foo && + git add foo && + git commit -m "intervening commit" && + git checkout master && + git rm TestCase && + >testcase && + git add testcase && + git commit -m "rename to testcase" && + git checkout with-camel && + git merge master -m "merge" && + test_path_is_file testcase +' + +test_done diff --git a/t/t7007-show.sh b/t/t7007-show.sh index e41fa00b80..1b824fe5ed 100755 --- a/t/t7007-show.sh +++ b/t/t7007-show.sh @@ -24,7 +24,8 @@ test_expect_success 'set up a bit of history' ' git tag -m "annotated tag" annotated && git checkout -b side HEAD^^ && test_commit side2 && - test_commit side3 + test_commit side3 && + test_merge merge main3 ' test_expect_success 'showing two commits' ' @@ -109,8 +110,11 @@ test_expect_success 'showing range' ' ' test_expect_success '-s suppresses diff' ' - echo main3 >expect && - git show -s --format=%s main3 >actual && + cat >expect <<-\EOF && + merge + main3 + EOF + git show -s --format=%s merge main3 >actual && test_cmp expect actual ' diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 450529404c..ee703bed64 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -22,6 +22,9 @@ commit_msg () { fi } +# Tested non-UTF-8 encoding +test_encoding="ISO8859-1" + test_expect_success 'creating initial files and commits' ' test_tick && echo "1st file" >first && @@ -41,7 +44,7 @@ test_expect_success 'creating initial files and commits' ' echo "1st line 2nd file" >secondfile && echo "2nd line 2nd file" >>secondfile && - git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" && + git -c "i18n.commitEncoding=$test_encoding" commit -a -m "$(commit_msg $test_encoding)" && head5=$(git rev-parse --verify HEAD) ' # git log --pretty=oneline # to see those SHA1 involved @@ -64,10 +67,10 @@ test_expect_success 'reset --hard message' ' test_cmp .expected .actual ' -test_expect_success 'reset --hard message (iso8859-1 logoutputencoding)' ' +test_expect_success 'reset --hard message (ISO8859-1 logoutputencoding)' ' hex=$(git log -1 --format="%h") && - git -c "i18n.logOutputEncoding=iso8859-1" reset --hard > .actual && - echo HEAD is now at $hex $(commit_msg iso8859-1) > .expected && + git -c "i18n.logOutputEncoding=$test_encoding" reset --hard > .actual && + echo HEAD is now at $hex $(commit_msg $test_encoding) > .expected && test_cmp .expected .actual ' @@ -331,7 +334,7 @@ test_expect_success 'redoing the last two commits should succeed' ' echo "1st line 2nd file" >secondfile && echo "2nd line 2nd file" >>secondfile && - git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" && + git -c "i18n.commitEncoding=$test_encoding" commit -a -m "$(commit_msg $test_encoding)" && check_changes $head5 ' diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 74de814aec..04118ad75b 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -426,10 +426,10 @@ test_expect_success SANITY 'removal failure' ' mkdir foo && touch foo/bar && + test_when_finished "chmod 755 foo" && (exec <foo/bar && chmod 0 foo && - test_must_fail git clean -f -d && - chmod 755 foo) + test_must_fail git clean -f -d) ' test_expect_success 'nested git work tree' ' diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index bdc1f29503..116885a260 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -223,7 +223,8 @@ test_expect_success 'Commit without message is allowed with --allow-empty-messag git add foo && >empty && git commit --allow-empty-message <empty && - commit_msg_is "" + commit_msg_is "" && + git tag empty-message-commit ' test_expect_success 'Commit without message is no-no without --allow-empty-message' ' @@ -240,6 +241,14 @@ test_expect_success 'Commit a message with --allow-empty-message' ' commit_msg_is "hello there" ' +test_expect_success 'commit -C empty respects --allow-empty-message' ' + echo more >>foo && + git add foo && + test_must_fail git commit -C empty-message-commit && + git commit -C empty-message-commit --allow-empty-message && + commit_msg_is "" +' + commit_for_rebase_autosquash_setup () { echo "first content line" >>foo && git add foo && diff --git a/t/t7508-status.sh b/t/t7508-status.sh index c987b5ed65..d48006960e 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -1380,7 +1380,32 @@ EOF test_i18ncmp expect output ' -test_expect_success '.gitmodules ignore=all suppresses submodule summary' ' +test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' ' + cat > expect << EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + .gitmodules + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF git config --add -f .gitmodules submodule.subname.ignore all && git config --add -f .gitmodules submodule.subname.path sm && git status > output && @@ -1388,7 +1413,7 @@ test_expect_success '.gitmodules ignore=all suppresses submodule summary' ' git config -f .gitmodules --remove-section submodule.subname ' -test_expect_success '.git/config ignore=all suppresses submodule summary' ' +test_expect_success '.git/config ignore=all suppresses unstaged submodule summary' ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore all && @@ -1461,4 +1486,49 @@ test_expect_success 'Restore default test environment' ' git config --unset status.showUntrackedFiles ' +test_expect_success 'git commit will commit a staged but ignored submodule' ' + git config --add -f .gitmodules submodule.subname.ignore all && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore all && + git status -s --ignore-submodules=dirty >output && + test_i18ngrep "^M. sm" output && + GIT_EDITOR="echo hello >>\"\$1\"" && + export GIT_EDITOR && + git commit -uno && + git status -s --ignore-submodules=dirty >output && + test_i18ngrep ! "^M. sm" output +' + +test_expect_success 'git commit --dry-run will show a staged but ignored submodule' ' + git reset HEAD^ && + git add sm && + cat >expect << EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files not listed (use -u option to show untracked files) +EOF + git commit -uno --dry-run >output && + test_i18ncmp expect output && + git status -s --ignore-submodules=dirty >output && + test_i18ngrep "^M. sm" output +' + +test_expect_success 'git commit -m will commit a staged but ignored submodule' ' + git commit -uno -m message && + git status -s --ignore-submodules=dirty >output && + test_i18ngrep ! "^M. sm" output && + git config --remove-section submodule.subname && + git config -f .gitmodules --remove-section submodule.subname +' + test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 10aa028017..b16462132f 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -57,11 +57,10 @@ create_merge_msgs () { git log --no-merges ^HEAD c2 c3 } >squash.1-5-9 && : >msg.nologff && - echo >msg.nolognoff && + : >msg.nolognoff && { echo "* tag 'c3':" && - echo " commit 3" && - echo + echo " commit 3" } >msg.log } @@ -71,7 +70,7 @@ verify_merge () { git diff --exit-code && if test -n "$3" then - git show -s --pretty=format:%s HEAD >msg.act && + git show -s --pretty=tformat:%s HEAD >msg.act && test_cmp "$3" msg.act fi } @@ -620,10 +619,10 @@ test_expect_success 'merge early part of c2' ' git tag c6 && git branch -f c5-branch c5 && git merge c5-branch~1 && - git show -s --pretty=format:%s HEAD >actual.branch && + git show -s --pretty=tformat:%s HEAD >actual.branch && git reset --keep HEAD^ && git merge c5~1 && - git show -s --pretty=format:%s HEAD >actual.tag && + git show -s --pretty=tformat:%s HEAD >actual.tag && test_cmp expected.branch actual.branch && test_cmp expected.tag actual.tag ' diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index 284018e3cd..61e6ed37aa 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -35,9 +35,25 @@ test_expect_success 'objects in packs marked .keep are not repacked' ' test -z "$found_duplicate_object" ' -test_expect_success 'writing bitmaps can duplicate .keep objects' ' +test_expect_success 'writing bitmaps via command-line can duplicate .keep objects' ' # build on $objsha1, $packsha1, and .keep state from previous - git repack -Adl && + git repack -Adbl && + test_when_finished "found_duplicate_object=" && + for p in .git/objects/pack/*.idx; do + idx=$(basename $p) + test "pack-$packsha1.idx" = "$idx" && continue + if git verify-pack -v $p | egrep "^$objsha1"; then + found_duplicate_object=1 + echo "DUPLICATE OBJECT FOUND" + break + fi + done && + test "$found_duplicate_object" = 1 +' + +test_expect_success 'writing bitmaps via config can duplicate .keep objects' ' + # build on $objsha1, $packsha1, and .keep state from previous + git -c pack.writebitmaps=true repack -Adl && test_when_finished "found_duplicate_object=" && for p in .git/objects/pack/*.idx; do idx=$(basename $p) diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh index e7cac1db55..2a3469bcbe 100755 --- a/t/t8003-blame-corner-cases.sh +++ b/t/t8003-blame-corner-cases.sh @@ -191,4 +191,13 @@ test_expect_success 'indent of line numbers, ten lines' ' test $(grep -c " " actual) = 9 ' +test_expect_success 'blaming files with CRLF newlines' ' + git config core.autocrlf false && + printf "testcase\r\n" >crlffile && + git add crlffile && + git commit -m testcase && + git -c core.autocrlf=input blame crlffile >actual && + grep "A U Thor" actual +' + test_done @@ -174,6 +174,24 @@ ssize_t xwrite(int fd, const void *buf, size_t len) } } +/* + * xpread() is the same as pread(), but it automatically restarts pread() + * operations with a recoverable error (EAGAIN and EINTR). xpread() DOES + * NOT GUARANTEE that "len" bytes is read even if the data is available. + */ +ssize_t xpread(int fd, void *buf, size_t len, off_t offset) +{ + ssize_t nr; + if (len > MAX_IO_SIZE) + len = MAX_IO_SIZE; + while (1) { + nr = pread(fd, buf, len, offset); + if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) + continue; + return nr; + } +} + ssize_t read_in_full(int fd, void *buf, size_t count) { char *p = buf; @@ -214,6 +232,26 @@ ssize_t write_in_full(int fd, const void *buf, size_t count) return total; } +ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset) +{ + char *p = buf; + ssize_t total = 0; + + while (count > 0) { + ssize_t loaded = xpread(fd, p, count, offset); + if (loaded < 0) + return -1; + if (loaded == 0) + return total; + count -= loaded; + p += loaded; + total += loaded; + offset += loaded; + } + + return total; +} + int xdup(int fd) { int ret = dup(fd); diff --git a/wt-status.c b/wt-status.c index ec7344e508..86fec8986f 100644 --- a/wt-status.c +++ b/wt-status.c @@ -519,9 +519,19 @@ static void wt_status_collect_changes_index(struct wt_status *s) opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference; setup_revisions(0, NULL, &rev, &opt); + DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG); if (s->ignore_submodule_arg) { - DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg); + } else { + /* + * Unless the user did explicitly request a submodule ignore + * mode by passing a command line option we do not ignore any + * changed submodule SHA-1s when comparing index and HEAD, no + * matter what is configured. Otherwise the user won't be + * shown any submodules she manually added (and which are + * staged to be committed), which would be really confusing. + */ + handle_ignore_submodules_arg(&rev.diffopt, "dirty"); } rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; |