diff options
-rw-r--r-- | Documentation/RelNotes/2.1.1.txt | 44 | ||||
-rw-r--r-- | Documentation/git.txt | 8 | ||||
-rw-r--r-- | Documentation/technical/pack-protocol.txt | 2 | ||||
-rwxr-xr-x | GIT-VERSION-GEN | 2 | ||||
l--------- | RelNotes | 2 | ||||
-rw-r--r-- | builtin/apply.c | 131 | ||||
-rw-r--r-- | builtin/fetch.c | 10 | ||||
-rw-r--r-- | bundle.c | 4 | ||||
-rw-r--r-- | combine-diff.c | 3 | ||||
-rw-r--r-- | commit.h | 1 | ||||
-rw-r--r-- | config.c | 12 | ||||
-rw-r--r-- | contrib/completion/git-prompt.sh | 3 | ||||
-rw-r--r-- | fast-import.c | 21 | ||||
-rw-r--r-- | log-tree.c | 5 | ||||
-rw-r--r-- | pretty.c | 9 | ||||
-rw-r--r-- | read-cache.c | 8 | ||||
-rw-r--r-- | refs.c | 3 | ||||
-rw-r--r-- | revision.c | 2 | ||||
-rwxr-xr-x | t/t1300-repo-config.sh | 11 | ||||
-rwxr-xr-x | t/t3210-pack-refs.sh | 7 | ||||
-rwxr-xr-x | t/t4119-apply-config.sh | 17 | ||||
-rwxr-xr-x | t/t4124-apply-ws-rule.sh | 11 | ||||
-rwxr-xr-x | t/t5704-bundle.sh | 5 | ||||
-rwxr-xr-x | t/t7201-co.sh | 17 | ||||
-rwxr-xr-x | t/t7515-status-symlinks.sh | 43 | ||||
-rw-r--r-- | unpack-trees.c | 31 | ||||
-rw-r--r-- | upload-pack.c | 4 |
27 files changed, 302 insertions, 114 deletions
diff --git a/Documentation/RelNotes/2.1.1.txt b/Documentation/RelNotes/2.1.1.txt new file mode 100644 index 0000000000..830fc3cc6d --- /dev/null +++ b/Documentation/RelNotes/2.1.1.txt @@ -0,0 +1,44 @@ +Git v2.1.1 Release Notes +======================== + + * Git 2.0 had a regression where "git fetch" into a shallowly + cloned repository from a repository with bitmap object index + enabled did not work correctly. This has been corrected. + + * Git 2.0 had a regression which broke (rarely used) "git diff-tree + -t". This has been corrected. + + * "git log --pretty/format=" with an empty format string did not + mean the more obvious "No output whatsoever" but "Use default + format", which was counterintuitive. Now it means "nothing shown + for the log message part". + + * "git -c section.var command" and "git -c section.var= command" + should pass the configuration differently (the former should be a + boolean true, the latter should be an empty string), but they + didn't work that way. Now it does. + + * Applying a patch not generated by Git in a subdirectory used to + check the whitespace breakage using the attributes for incorrect + paths. Also whitespace checks were performed even for paths + excluded via "git apply --exclude=<path>" mechanism. + + * "git bundle create" with date-range specification were meant to + exclude tags outside the range, but it did not work correctly. + + * "git add x" where x that used to be a directory has become a + symbolic link to a directory misbehaved. + + * The prompt script checked $GIT_DIR/ref/stash file to see if there + is a stash, which was a no-no. + + * "git checkout -m" did not switch to another branch while carrying + the local changes forward when a path was deleted from the index. + + * With sufficiently long refnames, fast-import could have overflown + an on-stack buffer. + + * After "pack-refs --prune" packed refs at the top-level, it failed + to prune them. + + * "git gc --auto" triggered from "git fetch --quiet" was not quiet. diff --git a/Documentation/git.txt b/Documentation/git.txt index de7b870a35..8b2c5424c4 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of Git, that is available from the 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v2.1.0/git.html[documentation for release 2.1] +* link:v2.1.1/git.html[documentation for release 2.1.1] * release notes for + link:RelNotes/2.1.1.txt[2.1.1], link:RelNotes/2.1.0.txt[2.1]. * link:v2.0.4/git.html[documentation for release 2.0.4] @@ -452,6 +453,11 @@ example the following invocations are equivalent: given will override values from configuration files. The <name> is expected in the same format as listed by 'git config' (subkeys separated by dots). ++ +Note that omitting the `=` in `git -c foo.bar ...` is allowed and sets +`foo.bar` to the boolean true value (just like `[foo]bar` would in a +config file). Including the equals but with an empty value (like `git -c +foo.bar= ...`) sets `foo.bar` to the empty string. --exec-path[=<path>]:: Path to wherever your core Git programs are installed. diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt index 18dea8d15f..569c48a352 100644 --- a/Documentation/technical/pack-protocol.txt +++ b/Documentation/technical/pack-protocol.txt @@ -467,7 +467,7 @@ references. ---- update-request = *shallow command-list [pack-file] - shallow = PKT-LINE("shallow" SP obj-id) + shallow = PKT-LINE("shallow" SP obj-id LF) command-list = PKT-LINE(command NUL capability-list LF) *PKT-LINE(command LF) diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index a4cdfbf7f6..c76c8d6dfd 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.1.0 +DEF_VER=v2.1.1 LF=' ' @@ -1 +1 @@ -Documentation/RelNotes/2.1.0.txt
\ No newline at end of file +Documentation/RelNotes/2.1.1.txt
\ No newline at end of file diff --git a/builtin/apply.c b/builtin/apply.c index be2b4ce2fd..6b7c764918 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1920,6 +1920,66 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) return used; } +static void prefix_one(char **name) +{ + char *old_name = *name; + if (!old_name) + return; + *name = xstrdup(prefix_filename(prefix, prefix_length, *name)); + free(old_name); +} + +static void prefix_patch(struct patch *p) +{ + if (!prefix || p->is_toplevel_relative) + return; + prefix_one(&p->new_name); + prefix_one(&p->old_name); +} + +/* + * include/exclude + */ + +static struct string_list limit_by_name; +static int has_include; +static void add_name_limit(const char *name, int exclude) +{ + struct string_list_item *it; + + it = string_list_append(&limit_by_name, name); + it->util = exclude ? NULL : (void *) 1; +} + +static int use_patch(struct patch *p) +{ + const char *pathname = p->new_name ? p->new_name : p->old_name; + int i; + + /* Paths outside are not touched regardless of "--include" */ + if (0 < prefix_length) { + int pathlen = strlen(pathname); + if (pathlen <= prefix_length || + memcmp(prefix, pathname, prefix_length)) + return 0; + } + + /* See if it matches any of exclude/include rule */ + for (i = 0; i < limit_by_name.nr; i++) { + struct string_list_item *it = &limit_by_name.items[i]; + if (!wildmatch(it->string, pathname, 0, NULL)) + return (it->util != NULL); + } + + /* + * If we had any include, a path that does not match any rule is + * not used. Otherwise, we saw bunch of exclude rules (or none) + * and such a path is used. + */ + return !has_include; +} + + /* * Read the patch text in "buffer" that extends for "size" bytes; stop * reading after seeing a single patch (i.e. changes to a single file). @@ -1935,9 +1995,14 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) if (offset < 0) return offset; - patch->ws_rule = whitespace_rule(patch->new_name - ? patch->new_name - : patch->old_name); + prefix_patch(patch); + + if (!use_patch(patch)) + patch->ws_rule = 0; + else + patch->ws_rule = whitespace_rule(patch->new_name + ? patch->new_name + : patch->old_name); patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch); @@ -4127,64 +4192,6 @@ static int write_out_results(struct patch *list) static struct lock_file lock_file; -static struct string_list limit_by_name; -static int has_include; -static void add_name_limit(const char *name, int exclude) -{ - struct string_list_item *it; - - it = string_list_append(&limit_by_name, name); - it->util = exclude ? NULL : (void *) 1; -} - -static int use_patch(struct patch *p) -{ - const char *pathname = p->new_name ? p->new_name : p->old_name; - int i; - - /* Paths outside are not touched regardless of "--include" */ - if (0 < prefix_length) { - int pathlen = strlen(pathname); - if (pathlen <= prefix_length || - memcmp(prefix, pathname, prefix_length)) - return 0; - } - - /* See if it matches any of exclude/include rule */ - for (i = 0; i < limit_by_name.nr; i++) { - struct string_list_item *it = &limit_by_name.items[i]; - if (!wildmatch(it->string, pathname, 0, NULL)) - return (it->util != NULL); - } - - /* - * If we had any include, a path that does not match any rule is - * not used. Otherwise, we saw bunch of exclude rules (or none) - * and such a path is used. - */ - return !has_include; -} - - -static void prefix_one(char **name) -{ - char *old_name = *name; - if (!old_name) - return; - *name = xstrdup(prefix_filename(prefix, prefix_length, *name)); - free(old_name); -} - -static void prefix_patches(struct patch *p) -{ - if (!prefix || p->is_toplevel_relative) - return; - for ( ; p; p = p->next) { - prefix_one(&p->new_name); - prefix_one(&p->old_name); - } -} - #define INACCURATE_EOF (1<<0) #define RECOUNT (1<<1) @@ -4210,8 +4217,6 @@ static int apply_patch(int fd, const char *filename, int options) break; if (apply_in_reverse) reverse_patches(patch); - if (prefix) - prefix_patches(patch); if (use_patch(patch)) { patch_stats(patch); *listp = patch; diff --git a/builtin/fetch.c b/builtin/fetch.c index e8d0cca3e4..159fb7e916 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1110,9 +1110,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) struct string_list list = STRING_LIST_INIT_NODUP; struct remote *remote; int result = 0; - static const char *argv_gc_auto[] = { - "gc", "--auto", NULL, - }; + struct argv_array argv_gc_auto = ARGV_ARRAY_INIT; packet_trace_identity("fetch"); @@ -1198,7 +1196,11 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) list.strdup_strings = 1; string_list_clear(&list, 0); - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL); + if (verbosity < 0) + argv_array_push(&argv_gc_auto, "--quiet"); + run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD); + argv_array_clear(&argv_gc_auto); return result; } @@ -221,8 +221,8 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs) line = memmem(buf, size, "\ntagger ", 8); if (!line++) return 1; - lineend = memchr(line, buf + size - line, '\n'); - line = memchr(line, lineend ? lineend - line : buf + size - line, '>'); + lineend = memchr(line, '\n', buf + size - line); + line = memchr(line, '>', lineend ? lineend - line : buf + size - line); if (!line++) return 1; date = strtoul(line, NULL, 10); diff --git a/combine-diff.c b/combine-diff.c index 60cb4f81f9..91edce58e1 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1407,7 +1407,8 @@ void diff_tree_combined(const unsigned char *sha1, show_log(rev); if (rev->verbose_header && opt->output_format && - opt->output_format != DIFF_FORMAT_NO_OUTPUT) + opt->output_format != DIFF_FORMAT_NO_OUTPUT && + !commit_format_is_empty(rev->commit_format)) printf("%s%c", diff_line_prefix(opt), opt->line_termination); } @@ -159,6 +159,7 @@ extern void get_commit_format(const char *arg, struct rev_info *); extern const char *format_subject(struct strbuf *sb, const char *msg, const char *line_separator); extern void userformat_find_requirements(const char *fmt, struct userformat_want *w); +extern int commit_format_is_empty(enum cmit_fmt); extern void format_commit_message(const struct commit *commit, const char *format, struct strbuf *sb, const struct pretty_print_context *context); @@ -162,19 +162,27 @@ void git_config_push_parameter(const char *text) int git_config_parse_parameter(const char *text, config_fn_t fn, void *data) { + const char *value; struct strbuf **pair; + pair = strbuf_split_str(text, '=', 2); if (!pair[0]) return error("bogus config parameter: %s", text); - if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') + + if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') { strbuf_setlen(pair[0], pair[0]->len - 1); + value = pair[1] ? pair[1]->buf : ""; + } else { + value = NULL; + } + strbuf_trim(pair[0]); if (!pair[0]->len) { strbuf_list_free(pair); return error("bogus config parameter: %s", text); } strbuf_tolower(pair[0]); - if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) { + if (fn(pair[0]->buf, value, data) < 0) { strbuf_list_free(pair); return -1; } diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 9d684b10a6..c5473dc8db 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -468,7 +468,8 @@ __git_ps1 () fi fi if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] && - [ -r "$g/refs/stash" ]; then + git rev-parse --verify --quiet refs/stash >/dev/null + then s="$" fi diff --git a/fast-import.c b/fast-import.c index d73f58cbe3..a1479e980d 100644 --- a/fast-import.c +++ b/fast-import.c @@ -946,10 +946,12 @@ static void unkeep_all_packs(void) static void end_packfile(void) { - struct packed_git *old_p = pack_data, *new_p; + if (!pack_data) + return; clear_delta_base_cache(); if (object_count) { + struct packed_git *new_p; unsigned char cur_pack_sha1[20]; char *idx_name; int i; @@ -991,10 +993,11 @@ static void end_packfile(void) pack_id++; } else { - close(old_p->pack_fd); - unlink_or_warn(old_p->pack_name); + close(pack_data->pack_fd); + unlink_or_warn(pack_data->pack_name); } - free(old_p); + free(pack_data); + pack_data = NULL; /* We can't carry a delta across packfiles. */ strbuf_release(&last_blob.data); @@ -1731,14 +1734,16 @@ static void dump_tags(void) static const char *msg = "fast-import"; struct tag *t; struct ref_lock *lock; - char ref_name[PATH_MAX]; + struct strbuf ref_name = STRBUF_INIT; for (t = first_tag; t; t = t->next_tag) { - sprintf(ref_name, "tags/%s", t->name); - lock = lock_ref_sha1(ref_name, NULL); + strbuf_reset(&ref_name); + strbuf_addf(&ref_name, "tags/%s", t->name); + lock = lock_ref_sha1(ref_name.buf, NULL); if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0) - failure |= error("Unable to update %s", ref_name); + failure |= error("Unable to update %s", ref_name.buf); } + strbuf_release(&ref_name); } static void dump_marks_helper(FILE *f, diff --git a/log-tree.c b/log-tree.c index 0c53dc11ab..95e9b1da25 100644 --- a/log-tree.c +++ b/log-tree.c @@ -649,7 +649,7 @@ void show_log(struct rev_info *opt) graph_show_commit_msg(opt->graph, &msgbuf); else fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); - if (opt->use_terminator) { + if (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) { if (!opt->missing_newline) graph_show_padding(opt->graph); putchar(opt->diffopt.line_termination); @@ -676,7 +676,8 @@ int log_tree_diff_flush(struct rev_info *opt) show_log(opt); if ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) && opt->verbose_header && - opt->commit_format != CMIT_FMT_ONELINE) { + opt->commit_format != CMIT_FMT_ONELINE && + !commit_format_is_empty(opt->commit_format)) { /* * When showing a verbose header (i.e. log message), * and not in --pretty=oneline format, we would want @@ -24,6 +24,11 @@ static size_t commit_formats_len; static size_t commit_formats_alloc; static struct cmt_fmt_map *find_commit_format(const char *sought); +int commit_format_is_empty(enum cmit_fmt fmt) +{ + return fmt == CMIT_FMT_USERFORMAT && !*user_format; +} + static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat) { free(user_format); @@ -146,7 +151,7 @@ void get_commit_format(const char *arg, struct rev_info *rev) struct cmt_fmt_map *commit_format; rev->use_terminator = 0; - if (!arg || !*arg) { + if (!arg) { rev->commit_format = CMIT_FMT_DEFAULT; return; } @@ -155,7 +160,7 @@ void get_commit_format(const char *arg, struct rev_info *rev) return; } - if (strchr(arg, '%')) { + if (!*arg || strchr(arg, '%')) { save_user_format(rev, arg, 1); return; } diff --git a/read-cache.c b/read-cache.c index 5d3c8bd4aa..6f0057fe66 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1064,6 +1064,14 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, return ce; } + if (has_symlink_leading_path(ce->name, ce_namelen(ce))) { + if (ignore_missing) + return ce; + if (err) + *err = ENOENT; + return NULL; + } + if (lstat(ce->name, &st) < 0) { if (ignore_missing && errno == ENOENT) return ce; @@ -2387,7 +2387,8 @@ static void try_remove_empty_parents(char *name) /* make sure nobody touched the ref, and unlink */ static void prune_ref(struct ref_to_prune *r) { - struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1); + struct ref_lock *lock = lock_any_ref_for_update(r->name, r->sha1, + 0, NULL); if (lock) { unlink_or_warn(git_path("%s", r->name)); diff --git a/revision.c b/revision.c index 2571ada6bf..615535c984 100644 --- a/revision.c +++ b/revision.c @@ -1825,7 +1825,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--pretty")) { revs->verbose_header = 1; revs->pretty_given = 1; - get_commit_format(arg+8, revs); + get_commit_format(NULL, revs); } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) { /* * Detached form ("--pretty X" as opposed to "--pretty=X") diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 3f80ff0c14..46f6ae2571 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1010,6 +1010,17 @@ test_expect_success 'git -c "key=value" support' ' test_must_fail git -c name=value config core.name ' +# We just need a type-specifier here that cares about the +# distinction internally between a NULL boolean and a real +# string (because most of git's internal parsers do care). +# Using "--path" works, but we do not otherwise care about +# its semantics. +test_expect_success 'git -c can represent empty string' ' + echo >expect && + git -c foo.empty= config --path foo.empty >actual && + test_cmp expect actual +' + test_expect_success 'key sanity-checking' ' test_must_fail git config foo=bar && test_must_fail git config foo=.bar && diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index 1a2080e3dc..3a017bf437 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -151,4 +151,11 @@ test_expect_success 'delete ref while another dangling packed ref' ' test_cmp /dev/null result ' +test_expect_success 'pack ref directly below refs/' ' + git update-ref refs/top HEAD && + git pack-refs --all --prune && + grep refs/top .git/packed-refs && + test_path_is_missing .git/refs/top +' + test_done diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh index c393be691b..a9a0583811 100755 --- a/t/t4119-apply-config.sh +++ b/t/t4119-apply-config.sh @@ -159,4 +159,21 @@ test_expect_success 'same but with traditional patch input of depth 2' ' check_result sub/file1 ' +test_expect_success 'in subdir with traditional patch input' ' + cd "$D" && + git config apply.whitespace strip && + cat >.gitattributes <<-EOF && + /* whitespace=blank-at-eol + sub/* whitespace=-blank-at-eol + EOF + rm -f sub/file1 && + cp saved sub/file1 && + git update-index --refresh && + + cd sub && + git apply ../gpatch.file && + echo "B " >expect && + test_cmp expect file1 +' + test_done diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh index 5d0c598338..c6474de4c8 100755 --- a/t/t4124-apply-ws-rule.sh +++ b/t/t4124-apply-ws-rule.sh @@ -512,4 +512,15 @@ test_expect_success 'whitespace=fix to expand' ' git -c core.whitespace=tab-in-indent apply --whitespace=fix patch ' +test_expect_success 'whitespace check skipped for excluded paths' ' + git config core.whitespace blank-at-eol && + >used && + >unused && + git add used unused && + echo "used" >used && + echo "unused " >unused && + git diff-files -p used unused >patch && + git apply --include=used --stat --whitespace=error <patch +' + test_done diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh index a45c31692e..348d9b3bc7 100755 --- a/t/t5704-bundle.sh +++ b/t/t5704-bundle.sh @@ -14,7 +14,10 @@ test_expect_success 'setup' ' git tag -d third ' -test_expect_success 'tags can be excluded by rev-list options' ' +test_expect_success 'annotated tags can be excluded by rev-list options' ' + git bundle create bundle --all --since=7.Apr.2005.15:14:00.-0700 && + git ls-remote bundle > output && + grep tag output && git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 && git ls-remote bundle > output && ! grep tag output diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 0c9ec0ad44..eae9e5a937 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -223,6 +223,23 @@ test_expect_success 'checkout --merge --conflict=diff3 <branch>' ' test_cmp two expect ' +test_expect_success 'switch to another branch while carrying a deletion' ' + + git checkout -f master && git reset --hard && git clean -f && + git rm two && + + test_must_fail git checkout simple 2>errs && + test_i18ngrep overwritten errs && + + git checkout --merge simple 2>errs && + test_i18ngrep ! overwritten errs && + git ls-files -u && + test_must_fail git cat-file -t :0:two && + test "$(git cat-file -t :1:two)" = blob && + test "$(git cat-file -t :2:two)" = blob && + test_must_fail git cat-file -t :3:two +' + test_expect_success 'checkout to detach HEAD (with advice declined)' ' git config advice.detachedHead false && diff --git a/t/t7515-status-symlinks.sh b/t/t7515-status-symlinks.sh new file mode 100755 index 0000000000..9f989be01b --- /dev/null +++ b/t/t7515-status-symlinks.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +test_description='git status and symlinks' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo .gitignore >.gitignore && + echo actual >>.gitignore && + echo expect >>.gitignore && + mkdir dir && + echo x >dir/file1 && + echo y >dir/file2 && + git add dir && + git commit -m initial && + git tag initial +' + +test_expect_success SYMLINKS 'symlink to a directory' ' + test_when_finished "rm symlink" && + ln -s dir symlink && + echo "?? symlink" >expect && + git status --porcelain >actual && + test_cmp expect actual +' + +test_expect_success SYMLINKS 'symlink replacing a directory' ' + test_when_finished "rm -rf copy && git reset --hard initial" && + mkdir copy && + cp dir/file1 copy/file1 && + echo "changed in copy" >copy/file2 && + git add copy && + git commit -m second && + rm -rf copy && + ln -s dir copy && + echo " D copy/file1" >expect && + echo " D copy/file2" >>expect && + echo "?? copy" >>expect && + git status --porcelain >actual && + test_cmp expect actual +' + +test_done diff --git a/unpack-trees.c b/unpack-trees.c index c6aa8fb993..629c658c46 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1176,7 +1176,8 @@ return_failed: static int reject_merge(const struct cache_entry *ce, struct unpack_trees_options *o) { - return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name); + return o->gently ? -1 : + add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name); } static int same(const struct cache_entry *a, const struct cache_entry *b) @@ -1631,7 +1632,7 @@ int threeway_merge(const struct cache_entry * const *stages, /* #14, #14ALT, #2ALT */ if (remote && !df_conflict_head && head_match && !remote_match) { if (index && !same(index, remote) && !same(index, head)) - return o->gently ? -1 : reject_merge(index, o); + return reject_merge(index, o); return merged_entry(remote, index, o); } /* @@ -1639,7 +1640,7 @@ int threeway_merge(const struct cache_entry * const *stages, * make sure that it matches head. */ if (index && !same(index, head)) - return o->gently ? -1 : reject_merge(index, o); + return reject_merge(index, o); if (head) { /* #5ALT, #15 */ @@ -1768,9 +1769,8 @@ int twoway_merge(const struct cache_entry * const *src, else return merged_entry(newtree, current, o); } - return o->gently ? -1 : reject_merge(current, o); - } - else if ((!oldtree && !newtree) || /* 4 and 5 */ + return reject_merge(current, o); + } else if ((!oldtree && !newtree) || /* 4 and 5 */ (!oldtree && newtree && same(current, newtree)) || /* 6 and 7 */ (oldtree && newtree && @@ -1779,26 +1779,15 @@ int twoway_merge(const struct cache_entry * const *src, !same(oldtree, newtree) && /* 18 and 19 */ same(current, newtree))) { return keep_entry(current, o); - } - else if (oldtree && !newtree && same(current, oldtree)) { + } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ return deleted_entry(oldtree, current, o); - } - else if (oldtree && newtree && + } else if (oldtree && newtree && same(current, oldtree) && !same(current, newtree)) { /* 20 or 21 */ return merged_entry(newtree, current, o); - } - else { - /* all other failures */ - if (oldtree) - return o->gently ? -1 : reject_merge(oldtree, o); - if (current) - return o->gently ? -1 : reject_merge(current, o); - if (newtree) - return o->gently ? -1 : reject_merge(newtree, o); - return -1; - } + } else + return reject_merge(current, o); } else if (newtree) { if (oldtree && !o->initial_checkout) { diff --git a/upload-pack.c b/upload-pack.c index 01de944a0a..433211a238 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -167,7 +167,9 @@ static void create_pack_file(void) if (!pollsize) break; - ret = poll(pfd, pollsize, 1000 * keepalive); + ret = poll(pfd, pollsize, + keepalive < 0 ? -1 : 1000 * keepalive); + if (ret < 0) { if (errno != EINTR) { error("poll failed, resuming: %s", |