diff options
38 files changed, 459 insertions, 103 deletions
diff --git a/Documentation/RelNotes/1.7.7.6.txt b/Documentation/RelNotes/1.7.7.6.txt index b8b86ebc61..8df606d452 100644 --- a/Documentation/RelNotes/1.7.7.6.txt +++ b/Documentation/RelNotes/1.7.7.6.txt @@ -8,6 +8,10 @@ Fixes since v1.7.7.5 directory when two paths in question are in adjacent directories and the name of the one directory is a prefix of the other. + * A wildcard that matches deeper hierarchy given to the "diff-index" command, + e.g. "git diff-index HEAD -- '*.txt'", incorrectly reported additions of + matching files even when there is no change. + * When producing a "thin pack" (primarily used in bundles and smart HTTP transfers) out of a fully packed repository, we unnecessarily avoided sending recent objects as a delta against objects we know diff --git a/Documentation/RelNotes/1.7.8.4.txt b/Documentation/RelNotes/1.7.8.4.txt index c21fc99bdb..9bebdbf13d 100644 --- a/Documentation/RelNotes/1.7.8.4.txt +++ b/Documentation/RelNotes/1.7.8.4.txt @@ -8,6 +8,10 @@ Fixes since v1.7.8.3 directory when two paths in question are in adjacent directories and the name of the one directory is a prefix of the other. + * A wildcard that matches deeper hierarchy given to the "diff-index" command, + e.g. "git diff-index HEAD -- '*.txt'", incorrectly reported additions of + matching files even when there is no change. + * When producing a "thin pack" (primarily used in bundles and smart HTTP transfers) out of a fully packed repository, we unnecessarily avoided sending recent objects as a delta against objects we know diff --git a/Documentation/RelNotes/1.7.9.1.txt b/Documentation/RelNotes/1.7.9.1.txt new file mode 100644 index 0000000000..d8a0961149 --- /dev/null +++ b/Documentation/RelNotes/1.7.9.1.txt @@ -0,0 +1,19 @@ +Git v1.7.9.1 Release Notes +========================== + +Fixes since v1.7.9 +------------------ + + * Subprocesses spawned from various git programs were often left running + to completion even when the top-level process was killed. + + * "git log --first-parent $pathspec" did not stay on the first parent + chain and veered into side branch from which the whole change to the + specified paths came. + + * "git push -q" was not sufficiently quiet. + + * When "git push" fails to update any refs, the client side did not + report an error correctly to the end user. + +Also contains minor fixes and documentation updates. diff --git a/Documentation/RelNotes/1.7.9.txt b/Documentation/RelNotes/1.7.9.txt index 5eb88b899a..95320aad5d 100644 --- a/Documentation/RelNotes/1.7.9.txt +++ b/Documentation/RelNotes/1.7.9.txt @@ -1,4 +1,4 @@ -Git v1.7.9 Release Notes (draft) +Git v1.7.9 Release Notes ======================== Updates since v1.7.8 @@ -12,19 +12,20 @@ Updates since v1.7.8 * Git uses gettext to translate its most common interface messages into the user's language if translations are available and the - locale is appropriately set. Distributors can drop in new PO files + locale is appropriately set. Distributors can drop new PO files in po/ to add new translations. - * The code to handle username/password for HTTP transaction used in + * The code to handle username/password for HTTP transactions used in "git push" & "git fetch" learned to talk "credential API" to external programs to cache or store them, to allow integration with platform native keychain mechanisms. - * The prompted input in the terminal use our own getpass() replacement - when possible. HTTP transactions used to ask username without echoing - back what was typed, but with this change you will see it as you type. + * The input prompts in the terminal use our own getpass() replacement + when possible. HTTP transactions used to ask for the username without + echoing back what was typed, but with this change you will see it as + you type. - * The internal of "revert/cherry-pick" has been tweaked to prepare + * The internals of "revert/cherry-pick" have been tweaked to prepare building more generic "sequencer" on top of the implementation that drives them. @@ -63,7 +64,7 @@ Updates since v1.7.8 knows MATLAB. * "git log --format='<format>'" learned new %g[nNeE] specifiers to - show information from the reflog entries when warlking the reflog + show information from the reflog entries when walking the reflog (i.e. with "-g"). * "git pull" can be used to fetch and merge an annotated/signed tag, @@ -109,11 +110,3 @@ Fixes since v1.7.8 Unless otherwise noted, all the fixes since v1.7.8 in the maintenance releases are contained in this release (see release notes to them for details). - --- -exec >/var/tmp/1 -O=v1.7.9-rc0-44-g478c446 -echo O=$(git describe master) -git log --first-parent --oneline --reverse ^$O master -echo -git shortlog --no-merges ^$O ^maint master diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 15d6711d46..6a8b1e3a7d 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -79,6 +79,9 @@ OPTIONS --max-depth <depth>:: For each <pathspec> given on command line, descend at most <depth> levels of directories. A negative value means no limit. + This option is ignored if <pathspec> contains active wildcards. + In other words if "a*" matches a directory named "a*", + "*" is matched literally so --max-depth is still effective. -w:: --word-regexp:: diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index 3c45895299..fcee0008a9 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -44,7 +44,7 @@ OPTIONS -d:: --dereference:: - Dereference tags into object IDs as well. They will be shown with "^{}" + Dereference tags into object IDs as well. They will be shown with "{caret}{}" appended. -s:: @@ -73,9 +73,9 @@ OPTIONS --exclude-existing[=<pattern>]:: Make 'git show-ref' act as a filter that reads refs from stdin of the - form "^(?:<anything>\s)?<refname>(?:{backslash}{caret}\{\})?$" + form "`{caret}(?:<anything>\s)?<refname>(?:{backslash}{caret}{})?$`" and performs the following actions on each: - (1) strip "^{}" at the end of line if any; + (1) strip "{caret}{}" at the end of line if any; (2) ignore if pattern is provided and does not head-match refname; (3) warn if refname is not a well-formed refname and skip; (4) ignore if refname is a ref that exists in the local repository; diff --git a/Documentation/git.txt b/Documentation/git.txt index 8a77fa47ad..f7e201fae3 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -44,17 +44,24 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.7.8.3/git.html[documentation for release 1.7.8.3] +* link:v1.7.9/git.html[documentation for release 1.7.9] * release notes for + link:RelNotes/1.7.9.txt[1.7.9]. + +* link:v1.7.8.4/git.html[documentation for release 1.7.8.4] + +* release notes for + link:RelNotes/1.7.8.4.txt[1.7.8.4], link:RelNotes/1.7.8.3.txt[1.7.8.3], link:RelNotes/1.7.8.2.txt[1.7.8.2], link:RelNotes/1.7.8.1.txt[1.7.8.1], link:RelNotes/1.7.8.txt[1.7.8]. -* link:v1.7.7.5/git.html[documentation for release 1.7.7.5] +* link:v1.7.7.6/git.html[documentation for release 1.7.7.6] * release notes for + link:RelNotes/1.7.7.6.txt[1.7.7.6], link:RelNotes/1.7.7.5.txt[1.7.7.5], link:RelNotes/1.7.7.4.txt[1.7.7.4], link:RelNotes/1.7.7.3.txt[1.7.7.3], @@ -62,9 +69,10 @@ Documentation for older releases are available here: link:RelNotes/1.7.7.1.txt[1.7.7.1], link:RelNotes/1.7.7.txt[1.7.7]. -* link:v1.7.6.5/git.html[documentation for release 1.7.6.5] +* link:v1.7.6.6/git.html[documentation for release 1.7.6.6] * release notes for + link:RelNotes/1.7.6.6.txt[1.7.6.6], link:RelNotes/1.7.6.5.txt[1.7.6.5], link:RelNotes/1.7.6.4.txt[1.7.6.4], link:RelNotes/1.7.6.3.txt[1.7.6.3], diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 15d06cfaab..70204f87f9 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.7.9-rc1 +DEF_VER=v1.7.9 LF=' ' @@ -83,7 +83,11 @@ Issues of note: - "Perl" version 5.8 or later is needed to use some of the features (e.g. preparing a partial commit using "git add -i/-p", interacting with svn repositories with "git svn"). If you can - live without these, use NO_PERL. + live without these, use NO_PERL. Note that recent releases of + Redhat/Fedora are reported to ship Perl binary package with some + core modules stripped away (see http://lwn.net/Articles/477234/), + so you might need to install additional packages other than Perl + itself, e.g. Time::HiRes. - "openssl" library is used by git-imap-send to use IMAP over SSL. If you don't need it, use NO_OPENSSL. @@ -1 +1 @@ -Documentation/RelNotes/1.7.9.txt
\ No newline at end of file +Documentation/RelNotes/1.7.9.1.txt
\ No newline at end of file diff --git a/builtin/grep.c b/builtin/grep.c index 9ce064ac11..5c2ae94e55 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1034,8 +1034,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) #ifndef NO_PTHREADS if (use_threads) { - if (opt.pre_context || opt.post_context || opt.file_break || - opt.funcbody) + if (!(opt.name_only || opt.unmatch_name_only || opt.count) + && (opt.pre_context || opt.post_context || + opt.file_break || opt.funcbody)) skip_first_line = 1; start_threads(&opt); } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 8c9e91e78c..fa7448be5a 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -33,6 +33,7 @@ static int transfer_unpack_limit = -1; static int unpack_limit = 100; static int report_status; static int use_sideband; +static int quiet; static int prefer_ofs_delta = 1; static int auto_update_server_info; static int auto_gc = 1; @@ -122,7 +123,7 @@ static void show_ref(const char *path, const unsigned char *sha1) else packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), path, 0, - " report-status delete-refs side-band-64k", + " report-status delete-refs side-band-64k quiet", prefer_ofs_delta ? " ofs-delta" : ""); sent_capabilities = 1; } @@ -748,10 +749,13 @@ static struct command *read_head_info(void) refname = line + 82; reflen = strlen(refname); if (reflen + 82 < len) { - if (strstr(refname + reflen + 1, "report-status")) + const char *feature_list = refname + reflen + 1; + if (parse_feature_request(feature_list, "report-status")) report_status = 1; - if (strstr(refname + reflen + 1, "side-band-64k")) + if (parse_feature_request(feature_list, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; + if (parse_feature_request(feature_list, "quiet")) + quiet = 1; } cmd = xcalloc(1, sizeof(struct command) + len - 80); hashcpy(cmd->old_sha1, old_sha1); @@ -805,8 +809,10 @@ static const char *unpack(void) if (ntohl(hdr.hdr_entries) < unpack_limit) { int code, i = 0; - const char *unpacker[4]; + const char *unpacker[5]; unpacker[i++] = "unpack-objects"; + if (quiet) + unpacker[i++] = "-q"; if (fsck_objects) unpacker[i++] = "--strict"; unpacker[i++] = hdr_arg; @@ -901,6 +907,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) const char *arg = *argv++; if (*arg == '-') { + if (!strcmp(arg, "--quiet")) { + quiet = 1; + continue; + } + if (!strcmp(arg, "--advertise-refs")) { advertise_refs = 1; continue; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index cd1115ffc6..71f258ef6e 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -263,6 +263,8 @@ int send_pack(struct send_pack_args *args, args->use_ofs_delta = 1; if (server_supports("side-band-64k")) use_sideband = 1; + if (!server_supports("quiet")) + args->quiet = 0; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" @@ -301,11 +303,12 @@ int send_pack(struct send_pack_args *args, char *old_hex = sha1_to_hex(ref->old_sha1); char *new_hex = sha1_to_hex(ref->new_sha1); - if (!cmds_sent && (status_report || use_sideband)) { - packet_buf_write(&req_buf, "%s %s %s%c%s%s", + if (!cmds_sent && (status_report || use_sideband || args->quiet)) { + packet_buf_write(&req_buf, "%s %s %s%c%s%s%s", old_hex, new_hex, ref->name, 0, status_report ? " report-status" : "", - use_sideband ? " side-band-64k" : ""); + use_sideband ? " side-band-64k" : "", + args->quiet ? " quiet" : ""); } else packet_buf_write(&req_buf, "%s %s %s", @@ -439,6 +442,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.force_update = 1; continue; } + if (!strcmp(arg, "--quiet")) { + args.quiet = 1; + continue; + } if (!strcmp(arg, "--verbose")) { args.verbose = 1; continue; @@ -1037,6 +1037,7 @@ struct extra_have_objects { }; extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *); extern int server_supports(const char *feature); +extern const char *parse_feature_request(const char *features, const char *feature); extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path); @@ -101,8 +101,27 @@ struct ref **get_remote_heads(int in, struct ref **list, int server_supports(const char *feature) { - return server_capabilities && - strstr(server_capabilities, feature) != NULL; + return !!parse_feature_request(server_capabilities, feature); +} + +const char *parse_feature_request(const char *feature_list, const char *feature) +{ + int len; + + if (!feature_list) + return NULL; + + len = strlen(feature); + while (*feature_list) { + const char *found = strstr(feature_list, feature); + if (!found) + return NULL; + if ((feature_list == found || isspace(found[-1])) && + (!found[len] || isspace(found[len]) || found[len] == '=')) + return found; + feature_list = found + 1; + } + return NULL; } enum protocol { diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b0062bac22..78be195838 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1622,7 +1622,7 @@ _git_log () __git_merge_options=" --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff --ff-only + --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit " _git_merge () @@ -2631,6 +2631,10 @@ _git () # workaround zsh's bug that leaves 'words' as a special # variable in versions < 4.3.12 typeset -h words + + # workaround zsh's bug that quotes spaces in the COMPREPLY + # array if IFS doesn't contain spaces. + typeset -h IFS fi local cur words cword prev @@ -2687,6 +2691,10 @@ _gitk () # workaround zsh's bug that leaves 'words' as a special # variable in versions < 4.3.12 typeset -h words + + # workaround zsh's bug that quotes spaces in the COMPREPLY + # array if IFS doesn't contain spaces. + typeset -h IFS fi local cur words cword prev diff --git a/credential-cache.c b/credential-cache.c index b15a9a7449..9a03792c7d 100644 --- a/credential-cache.c +++ b/credential-cache.c @@ -71,10 +71,14 @@ static void do_cache(const char *socket, const char *action, int timeout, die_errno("unable to relay credential"); } - if (send_request(socket, &buf) < 0 && (flags & FLAG_SPAWN)) { - spawn_daemon(socket); - if (send_request(socket, &buf) < 0) + if (send_request(socket, &buf) < 0) { + if (errno != ENOENT && errno != ECONNREFUSED) die_errno("unable to connect to cache daemon"); + if (flags & FLAG_SPAWN) { + spawn_daemon(socket); + if (send_request(socket, &buf) < 0) + die_errno("unable to connect to cache daemon"); + } } strbuf_release(&buf); } diff --git a/diff-lib.c b/diff-lib.c index 62f4cd94cf..fc0dff31b5 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -469,6 +469,8 @@ static int diff_cache(struct rev_info *revs, opts.src_index = &the_index; opts.dst_index = NULL; opts.pathspec = &revs->diffopt.pathspec; + opts.pathspec->recursive = 1; + opts.pathspec->max_depth = -1; init_tree_desc(&t, tree->buffer, tree->size); return unpack_trees(1, &t, &opts); @@ -1113,6 +1113,15 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) diff_words_append(line, len, &ecbdata->diff_words->plus); return; + } else if (!prefixcmp(line, "\\ ")) { + /* + * Eat the "no newline at eof" marker as if we + * saw a "+" or "-" line with nothing on it, + * and return without diff_words_flush() to + * defer processing. If this is the end of + * preimage, more "+" lines may come after it. + */ + return; } diff_words_flush(ecbdata); if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) { diff --git a/git-mergetool.sh b/git-mergetool.sh index 085e213a12..a9f23f7fcd 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -181,10 +181,14 @@ stage_submodule () { } checkout_staged_file () { - tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^ ]*\) ') + tmpfile=$(expr \ + "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \ + : '\([^ ]*\) ') if test $? -eq 0 -a -n "$tmpfile" ; then mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3" + else + >"$3" fi } @@ -224,9 +228,9 @@ merge_file () { mv -- "$MERGED" "$BACKUP" cp -- "$BACKUP" "$MERGED" - base_present && checkout_staged_file 1 "$MERGED" "$BASE" - local_present && checkout_staged_file 2 "$MERGED" "$LOCAL" - remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE" + checkout_staged_file 1 "$MERGED" "$BASE" + checkout_staged_file 2 "$MERGED" "$LOCAL" + checkout_staged_file 3 "$MERGED" "$REMOTE" if test -z "$local_mode" -o -z "$remote_mode"; then echo "Deleted merge conflict for '$MERGED':" diff --git a/git-submodule.sh b/git-submodule.sh index 3adab93635..9bb2e13e92 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -131,6 +131,7 @@ module_clone() gitdir= gitdir_base= name=$(module_name "$path" 2>/dev/null) + test -n "$name" || name="$path" base_path=$(dirname "$path") gitdir=$(git rev-parse --git-dir) @@ -495,7 +495,7 @@ static void execv_dashed_external(const char **argv) * if we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code. */ - status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE); + status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT); if (status >= 0 || errno != ENOENT) exit(status); diff --git a/imap-send.c b/imap-send.c index e40125a22b..972ad62cd9 100644 --- a/imap-send.c +++ b/imap-send.c @@ -42,28 +42,6 @@ struct store_conf { unsigned trash_remote_new:1, trash_only_new:1; }; -struct string_list { - struct string_list *next; - char string[1]; -}; - -struct channel_conf { - struct channel_conf *next; - char *name; - struct store_conf *master, *slave; - char *master_name, *slave_name; - char *sync_state; - struct string_list *patterns; - int mops, sops; - unsigned max_messages; /* for slave only */ -}; - -struct group_conf { - struct group_conf *next; - char *name; - struct string_list *channels; -}; - /* For message->status */ #define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */ #define M_DEAD (1<<1) /* expunged */ @@ -71,7 +49,6 @@ struct group_conf { struct message { struct message *next; - /* struct string_list *keywords; */ size_t size; /* zero implies "not fetched" */ int uid; unsigned char flags, status; diff --git a/remote-curl.c b/remote-curl.c index 48c20b86f3..d159fe7f34 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -770,7 +770,9 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs) argv[argc++] = "--thin"; if (options.dry_run) argv[argc++] = "--dry-run"; - if (options.verbosity > 1) + if (options.verbosity == 0) + argv[argc++] = "--quiet"; + else if (options.verbosity > 1) argv[argc++] = "--verbose"; argv[argc++] = url; for (i = 0; i < nr_spec; i++) @@ -805,7 +807,7 @@ static int push(int nr_spec, char **specs) static void parse_push(struct strbuf *buf) { char **specs = NULL; - int alloc_spec = 0, nr_spec = 0, i; + int alloc_spec = 0, nr_spec = 0, i, ret; do { if (!prefixcmp(buf->buf, "push ")) { @@ -822,12 +824,13 @@ static void parse_push(struct strbuf *buf) break; } while (1); - if (push(nr_spec, specs)) - exit(128); /* error already reported */ - + ret = push(nr_spec, specs); printf("\n"); fflush(stdout); + if (ret) + exit(128); /* error already reported */ + free_specs: for (i = 0; i < nr_spec; i++) free(specs[i]); diff --git a/revision.c b/revision.c index 064e351084..eb0be4206d 100644 --- a/revision.c +++ b/revision.c @@ -416,7 +416,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit) static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) { struct commit_list **pp, *parent; - int tree_changed = 0, tree_same = 0; + int tree_changed = 0, tree_same = 0, nth_parent = 0; /* * If we don't do pruning, everything is interesting @@ -444,6 +444,14 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) while ((parent = *pp) != NULL) { struct commit *p = parent->item; + /* + * Do not compare with later parents when we care only about + * the first parent chain, in order to avoid derailing the + * traversal to follow a side branch that brought everything + * in the path we are limited to by the pathspec. + */ + if (revs->first_parent_only && nth_parent++) + break; if (parse_commit(p) < 0) die("cannot simplify commit %s (because of %s)", sha1_to_hex(commit->object.sha1), diff --git a/run-command.c b/run-command.c index 1c51043884..1db8abf984 100644 --- a/run-command.c +++ b/run-command.c @@ -1,8 +1,66 @@ #include "cache.h" #include "run-command.h" #include "exec_cmd.h" +#include "sigchain.h" #include "argv-array.h" +struct child_to_clean { + pid_t pid; + struct child_to_clean *next; +}; +static struct child_to_clean *children_to_clean; +static int installed_child_cleanup_handler; + +static void cleanup_children(int sig) +{ + while (children_to_clean) { + struct child_to_clean *p = children_to_clean; + children_to_clean = p->next; + kill(p->pid, sig); + free(p); + } +} + +static void cleanup_children_on_signal(int sig) +{ + cleanup_children(sig); + sigchain_pop(sig); + raise(sig); +} + +static void cleanup_children_on_exit(void) +{ + cleanup_children(SIGTERM); +} + +static void mark_child_for_cleanup(pid_t pid) +{ + struct child_to_clean *p = xmalloc(sizeof(*p)); + p->pid = pid; + p->next = children_to_clean; + children_to_clean = p; + + if (!installed_child_cleanup_handler) { + atexit(cleanup_children_on_exit); + sigchain_push_common(cleanup_children_on_signal); + installed_child_cleanup_handler = 1; + } +} + +static void clear_child_for_cleanup(pid_t pid) +{ + struct child_to_clean **last, *p; + + last = &children_to_clean; + for (p = children_to_clean; p; p = p->next) { + if (p->pid == pid) { + *last = p->next; + free(p); + return; + } + } +} + static inline void close_pair(int fd[2]) { close(fd[0]); @@ -130,6 +188,9 @@ static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure) } else { error("waitpid is confused (%s)", argv0); } + + clear_child_for_cleanup(pid); + errno = failed_errno; return code; } @@ -292,6 +353,8 @@ fail_pipe: if (cmd->pid < 0) error("cannot fork() for %s: %s", cmd->argv[0], strerror(failed_errno = errno)); + else if (cmd->clean_on_exit) + mark_child_for_cleanup(cmd->pid); /* * Wait for child's execvp. If the execvp succeeds (or if fork() @@ -312,6 +375,7 @@ fail_pipe: cmd->pid = -1; } close(notify_pipe[0]); + } #else { @@ -356,6 +420,8 @@ fail_pipe: failed_errno = errno; if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT)) error("cannot spawn %s: %s", cmd->argv[0], strerror(errno)); + if (cmd->clean_on_exit && cmd->pid >= 0) + mark_child_for_cleanup(cmd->pid); if (cmd->env) free_environ(env); @@ -431,6 +497,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd, cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0; cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0; + cmd->clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0; } int run_command_v_opt(const char **argv, int opt) @@ -540,6 +607,8 @@ int start_async(struct async *async) exit(!!async->proc(proc_in, proc_out, async->data)); } + mark_child_for_cleanup(async->pid); + if (need_in) close(fdin[0]); else if (async->in) diff --git a/run-command.h b/run-command.h index 56491b9f23..44f7d2bd42 100644 --- a/run-command.h +++ b/run-command.h @@ -38,6 +38,7 @@ struct child_process { unsigned silent_exec_failure:1; unsigned stdout_to_stderr:1; unsigned use_shell:1; + unsigned clean_on_exit:1; void (*preexec_cb)(void); }; @@ -52,6 +53,7 @@ extern int run_hook(const char *index_file, const char *name, ...); #define RUN_COMMAND_STDOUT_TO_STDERR 4 #define RUN_SILENT_EXEC_FAILURE 8 #define RUN_USING_SHELL 16 +#define RUN_CLEAN_ON_EXIT 32 int run_command_v_opt(const char **argv, int opt); /* diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh index fbc8cd8f05..af5134b70c 100755 --- a/t/t4010-diff-pathspec.sh +++ b/t/t4010-diff-pathspec.sh @@ -48,6 +48,14 @@ test_expect_success \ compare_diff_raw current expected' cat >expected <<\EOF +:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1 +EOF +test_expect_success \ + '"*file1" should show path1/file1' \ + 'git diff-index --cached $tree -- "*file1" >current && + compare_diff_raw current expected' + +cat >expected <<\EOF :100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M file0 EOF test_expect_success \ diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index 6f1e5a2a15..5c2012111c 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -334,4 +334,18 @@ test_expect_success 'word-diff with diff.sbe' ' word_diff --word-diff=plain ' +test_expect_success 'word-diff with no newline at EOF' ' + cat >expect <<-\EOF && + diff --git a/pre b/post + index 7bf316e..3dd0303 100644 + --- a/pre + +++ b/post + @@ -1 +1 @@ + a a [-a-]{+ab+} a a + EOF + printf "%s" "a a a a a" >pre && + printf "%s" "a a ab a a" >post && + word_diff --word-diff=plain +' + test_done diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh index c229fe68f1..9ee52cfc45 100755 --- a/t/t5523-push-upstream.sh +++ b/t/t5523-push-upstream.sh @@ -108,4 +108,11 @@ test_expect_failure TTY 'push --no-progress suppresses progress' ' ! grep "Writing objects" err ' +test_expect_success TTY 'quiet push' ' + ensure_fresh_upstream && + + test_terminal git push --quiet --no-progress upstream master 2>&1 | tee output && + test_cmp /dev/null output +' + test_done diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh index 9b85d420c3..d66ed24508 100755 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@ -14,6 +14,7 @@ fi ROOT_PATH="$PWD" LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5541'} . "$TEST_DIRECTORY"/lib-httpd.sh +. "$TEST_DIRECTORY"/lib-terminal.sh start_httpd test_expect_success 'setup remote repository' ' @@ -95,6 +96,32 @@ test_expect_success 'create and delete remote branch' ' test_must_fail git show-ref --verify refs/remotes/origin/dev ' +cat >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF +#!/bin/sh +exit 1 +EOF +chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" + +cat >exp <<EOF +remote: error: hook declined to update refs/heads/dev2 +To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git + ! [remote rejected] dev2 -> dev2 (hook declined) +error: failed to push some refs to 'http://127.0.0.1:5541/smart/test_repo.git' +EOF + +test_expect_success 'rejected update prints status' ' + cd "$ROOT_PATH"/test_repo_clone && + git checkout -b dev2 && + : >path4 && + git add path4 && + test_tick && + git commit -m dev2 && + test_must_fail git push origin dev2 2>act && + sed -e "/^remote: /s/ *$//" <act >cmp && + test_cmp exp cmp +' +rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" + cat >exp <<EOF GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200 @@ -106,6 +133,8 @@ GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200 POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200 GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200 POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200 +GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200 +POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200 EOF test_expect_success 'used receive-pack service' ' sed -e " @@ -186,5 +215,12 @@ test_expect_success 'push --mirror to repo with alternates' ' git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git ' +test_expect_success TTY 'quiet push' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit quiet && + test_terminal git push --quiet --no-progress 2>&1 | tee output && + test_cmp /dev/null output +' + stop_httpd test_done diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh index af34a1e817..839ad97b79 100755 --- a/t/t6012-rev-list-simplify.sh +++ b/t/t6012-rev-list-simplify.sh @@ -86,5 +86,6 @@ check_result 'I H E C B A' --full-history --date-order -- file check_result 'I E C B A' --simplify-merges -- file check_result 'I B A' -- file check_result 'I B A' --topo-order -- file +check_result 'H' --first-parent -- another-file test_done diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 33b292b8a8..5b97222c48 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -611,4 +611,12 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re ) ' +test_expect_success 'submodule add properly re-creates deeper level submodules' ' + (cd super && + git reset --hard master && + rm -rf deeper/ && + git submodule add ../submodule deeper/submodule + ) +' + test_done diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index 4aab2a75b8..f5e16fc7db 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -39,6 +39,7 @@ test_expect_success 'setup' ' echo branch1 change >file1 && echo branch1 newfile >file2 && echo branch1 spaced >"spaced name" && + echo branch1 both added >both && echo branch1 change file11 >file11 && echo branch1 change file13 >file13 && echo branch1 sub >subdir/file3 && @@ -50,6 +51,7 @@ test_expect_success 'setup' ' git checkout -b submod-branch1 ) && git add file1 "spaced name" file11 file13 file2 subdir/file3 submod && + git add both && git rm file12 && git commit -m "branch1 changes" && @@ -58,6 +60,7 @@ test_expect_success 'setup' ' echo master updated >file1 && echo master new >file2 && echo master updated spaced >"spaced name" && + echo master both added >both && echo master updated file12 >file12 && echo master updated file14 >file14 && echo master new sub >subdir/file3 && @@ -69,18 +72,22 @@ test_expect_success 'setup' ' git checkout -b submod-master ) && git add file1 "spaced name" file12 file14 file2 subdir/file3 submod && + git add both && git rm file11 && git commit -m "master updates" && git config merge.tool mytool && git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" && - git config mergetool.mytool.trustExitCode true + git config mergetool.mytool.trustExitCode true && + git config mergetool.mybase.cmd "cat \"\$BASE\" >\"\$MERGED\"" && + git config mergetool.mybase.trustExitCode true ' test_expect_success 'custom mergetool' ' git checkout -b test1 branch1 && git submodule update -N && test_must_fail git merge master >/dev/null 2>&1 && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "" | git mergetool file1 file1 ) && ( yes "" | git mergetool file2 "spaced name" >/dev/null 2>&1 ) && ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) && @@ -101,6 +108,7 @@ test_expect_success 'mergetool crlf' ' ( yes "" | git mergetool file1 >/dev/null 2>&1 ) && ( yes "" | git mergetool file2 >/dev/null 2>&1 ) && ( yes "" | git mergetool "spaced name" >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && @@ -131,6 +139,7 @@ test_expect_success 'mergetool on file in parent dir' ' cd subdir && ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) && ( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) && + ( yes "" | git mergetool ../both >/dev/null 2>&1 ) && ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) && @@ -212,6 +221,7 @@ test_expect_success 'deleted vs modified submodule' ' test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "r" | git mergetool submod ) && rmdir submod && mv submod-movedaside submod && @@ -228,6 +238,7 @@ test_expect_success 'deleted vs modified submodule' ' test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool submod ) && test ! -e submod && @@ -241,6 +252,7 @@ test_expect_success 'deleted vs modified submodule' ' test_must_fail git merge test6 && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "r" | git mergetool submod ) && test ! -e submod && @@ -256,6 +268,7 @@ test_expect_success 'deleted vs modified submodule' ' test_must_fail git merge test6 && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool submod ) && test "$(cat submod/bar)" = "master submodule" && @@ -279,6 +292,7 @@ test_expect_success 'file vs modified submodule' ' test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "r" | git mergetool submod ) && rmdir submod && mv submod-movedaside submod && @@ -294,6 +308,7 @@ test_expect_success 'file vs modified submodule' ' test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool submod ) && git submodule update -N && @@ -309,6 +324,7 @@ test_expect_success 'file vs modified submodule' ' test_must_fail git merge test7 && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "r" | git mergetool submod ) && test -d submod.orig && @@ -324,6 +340,7 @@ test_expect_success 'file vs modified submodule' ' test_must_fail git merge test7 && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool both>/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool submod ) && test "$(cat submod/bar)" = "master submodule" && @@ -445,4 +462,13 @@ test_expect_success 'directory vs modified submodule' ' git submodule update -N ' +test_expect_success 'file with no base' ' + git checkout -b test13 branch1 && + test_must_fail git merge master && + git mergetool --no-prompt --tool mybase -- both && + >expected && + test_cmp both expected && + git reset --hard master >/dev/null 2>&1 +' + test_done diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 7ba5b16f99..75f4716d8c 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -246,6 +246,28 @@ do done cat >expected <<EOF +file +EOF +test_expect_success 'grep -l -C' ' + git grep -l -C1 foo >actual && + test_cmp expected actual +' + +cat >expected <<EOF +file:5 +EOF +test_expect_success 'grep -l -C' ' + git grep -c -C1 foo >actual && + test_cmp expected actual +' + +test_expect_success 'grep -L -C' ' + git ls-files >expected && + git grep -L -C1 nonexistent_string >actual && + test_cmp expected actual +' + +cat >expected <<EOF file:foo mmap bar_mmap EOF diff --git a/tree-walk.c b/tree-walk.c index f82dba6a1f..492c7cd744 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -661,6 +661,9 @@ match_wildcards: /* * Match all directories. We'll try to match files * later on. + * max_depth is ignored but we may consider support it + * in future, see + * http://thread.gmane.org/gmane.comp.version-control.git/163757/focus=163840 */ if (ps->recursive && S_ISDIR(entry->mode)) return entry_interesting; diff --git a/unix-socket.c b/unix-socket.c index 84b15099f2..01f119f970 100644 --- a/unix-socket.c +++ b/unix-socket.c @@ -9,48 +9,114 @@ static int unix_stream_socket(void) return fd; } -static void unix_sockaddr_init(struct sockaddr_un *sa, const char *path) +static int chdir_len(const char *orig, int len) +{ + char *path = xmemdupz(orig, len); + int r = chdir(path); + free(path); + return r; +} + +struct unix_sockaddr_context { + char orig_dir[PATH_MAX]; +}; + +static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx) +{ + if (!ctx->orig_dir[0]) + return; + /* + * If we fail, we can't just return an error, since we have + * moved the cwd of the whole process, which could confuse calling + * code. We are better off to just die. + */ + if (chdir(ctx->orig_dir) < 0) + die("unable to restore original working directory"); +} + +static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path, + struct unix_sockaddr_context *ctx) { int size = strlen(path) + 1; - if (size > sizeof(sa->sun_path)) - die("socket path is too long to fit in sockaddr"); + + ctx->orig_dir[0] = '\0'; + if (size > sizeof(sa->sun_path)) { + const char *slash = find_last_dir_sep(path); + const char *dir; + + if (!slash) { + errno = ENAMETOOLONG; + return -1; + } + + dir = path; + path = slash + 1; + size = strlen(path) + 1; + if (size > sizeof(sa->sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + + if (!getcwd(ctx->orig_dir, sizeof(ctx->orig_dir))) { + errno = ENAMETOOLONG; + return -1; + } + if (chdir_len(dir, slash - dir) < 0) + return -1; + } + memset(sa, 0, sizeof(*sa)); sa->sun_family = AF_UNIX; memcpy(sa->sun_path, path, size); + return 0; } int unix_stream_connect(const char *path) { - int fd; + int fd, saved_errno; struct sockaddr_un sa; + struct unix_sockaddr_context ctx; - unix_sockaddr_init(&sa, path); - fd = unix_stream_socket(); - if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { - close(fd); + if (unix_sockaddr_init(&sa, path, &ctx) < 0) return -1; - } + fd = unix_stream_socket(); + if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) + goto fail; + unix_sockaddr_cleanup(&ctx); return fd; + +fail: + saved_errno = errno; + unix_sockaddr_cleanup(&ctx); + close(fd); + errno = saved_errno; + return -1; } int unix_stream_listen(const char *path) { - int fd; + int fd, saved_errno; struct sockaddr_un sa; + struct unix_sockaddr_context ctx; - unix_sockaddr_init(&sa, path); + if (unix_sockaddr_init(&sa, path, &ctx) < 0) + return -1; fd = unix_stream_socket(); unlink(path); - if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { - close(fd); - return -1; - } + if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) + goto fail; - if (listen(fd, 5) < 0) { - close(fd); - return -1; - } + if (listen(fd, 5) < 0) + goto fail; + unix_sockaddr_cleanup(&ctx); return fd; + +fail: + saved_errno = errno; + unix_sockaddr_cleanup(&ctx); + close(fd); + errno = saved_errno; + return -1; } diff --git a/upload-pack.c b/upload-pack.c index 6f36f6255c..0d11e21a68 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -585,6 +585,7 @@ static void receive_needs(void) write_str_in_full(debug_fd, "#S\n"); for (;;) { struct object *o; + const char *features; unsigned char sha1_buf[20]; len = packet_read_line(0, line, sizeof(line)); reset_timeout(); @@ -616,23 +617,26 @@ static void receive_needs(void) get_sha1_hex(line+5, sha1_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); - if (strstr(line+45, "multi_ack_detailed")) + + features = line + 45; + + if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; - else if (strstr(line+45, "multi_ack")) + else if (parse_feature_request(features, "multi_ack")) multi_ack = 1; - if (strstr(line+45, "no-done")) + if (parse_feature_request(features, "no-done")) no_done = 1; - if (strstr(line+45, "thin-pack")) + if (parse_feature_request(features, "thin-pack")) use_thin_pack = 1; - if (strstr(line+45, "ofs-delta")) + if (parse_feature_request(features, "ofs-delta")) use_ofs_delta = 1; - if (strstr(line+45, "side-band-64k")) + if (parse_feature_request(features, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; - else if (strstr(line+45, "side-band")) + else if (parse_feature_request(features, "side-band")) use_sideband = DEFAULT_PACKET_MAX; - if (strstr(line+45, "no-progress")) + if (parse_feature_request(features, "no-progress")) no_progress = 1; - if (strstr(line+45, "include-tag")) + if (parse_feature_request(features, "include-tag")) use_include_tag = 1; o = lookup_object(sha1_buf); |