diff options
78 files changed, 1158 insertions, 358 deletions
diff --git a/Documentation/RelNotes/1.7.7.1.txt b/Documentation/RelNotes/1.7.7.1.txt new file mode 100644 index 0000000000..ac9b838e25 --- /dev/null +++ b/Documentation/RelNotes/1.7.7.1.txt @@ -0,0 +1,60 @@ +Git v1.7.7.1 Release Notes +========================== + +Fixes since v1.7.7 +------------------ + + * On some BSD systems, adding +s bit on directories is detrimental + (it is not necessary on BSD to begin with). "git init --shared" + has been updated to take this into account without extra makefile + settings on platforms the Makefile knows about. + + * After incorrectly written third-party tools store a tag object in + HEAD, git diagnosed it as a repository corruption and refused to + proceed in order to avoid spreading the damage. We now gracefully + recover from such a situation by pretending as if the commit that + is pointed at by the tag were in HEAD. + + * "git apply --whitespace=error" did not bother to report the exact + line number in the patch that introduced new blank lines at the end + of the file. + + * "git apply --index" did not check corrupted patch. + + * "git checkout $tree $directory/" resurrected paths locally removed or + modified only in the working tree in $directory/ that did not appear + in $directory of the given $tree. They should have been kept intact. + + * "git diff $tree $path" used to apply the pathspec at the output stage, + reading the whole tree, wasting resources. + + * The code to check for updated submodules during a "git fetch" of the + superproject had an unnecessary quadratic loop. + + * "git fetch" from a large bundle did not enable the progress output. + + * When "git fsck --lost-and-found" found that an empty blob object in the + object store is unreachable, it incorrectly reported an error after + writing the lost blob out successfully. + + * "git filter-branch" did not refresh the index before checking that the + working tree was clean. + + * "git grep $tree" when run with multiple threads had an unsafe access to + the object database that should have been protected with mutex. + + * The "--ancestry-path" option to "git log" and friends misbehaved in a + history with complex criss-cross merges and showed an uninteresting + side history as well. + + * Test t1304 assumed LOGNAME is always set, which may not be true on + some systems. + + * Tests with --valgrind failed to find "mergetool" scriptlets. + + * "git patch-id" miscomputed the patch-id in a patch that has a line longer + than 1kB. + + * When an "exec" insn failed after modifying the index and/or the working + tree during "rebase -i", we now check and warn that the changes need to + be cleaned up. diff --git a/Documentation/config.txt b/Documentation/config.txt index 0658ffb889..eae75a39c0 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -147,7 +147,7 @@ advice.*:: core.fileMode:: If false, the executable bit differences between the index and - the working copy are ignored; useful on broken filesystems like FAT. + the working tree are ignored; useful on broken filesystems like FAT. See linkgit:git-update-index[1]. + The default is true, except linkgit:git-clone[1] or linkgit:git-init[1] @@ -179,7 +179,7 @@ is created. core.trustctime:: If false, the ctime differences between the index and the - working copy are ignored; useful when the inode change time + working tree are ignored; useful when the inode change time is regularly modified by something outside Git (file system crawlers and some backup systems). See linkgit:git-update-index[1]. True by default. @@ -292,7 +292,7 @@ core.ignoreStat:: If true, commands which modify both the working tree and the index will mark the updated paths with the "assume unchanged" bit in the index. These marked files are then assumed to stay unchanged in the - working copy, until you mark them otherwise manually - Git will not + working tree, until you mark them otherwise manually - Git will not detect the file changes by lstat() calls. This is useful on systems where those are very slow, such as Microsoft Windows. See linkgit:git-update-index[1]. @@ -1453,7 +1453,8 @@ notes.rewriteRef:: You may also specify this configuration several times. + Does not have a default value; you must configure this variable to -enable note rewriting. +enable note rewriting. Set it to `refs/notes/commits` to enable +rewriting for the default commit notes. + This setting can be overridden with the `GIT_NOTES_REWRITE_REF` environment variable, which must be a colon separated list of refs or diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index b620b3afec..5c53bdba94 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -45,6 +45,10 @@ ifndef::git-format-patch[] Synonym for `-p --raw`. endif::git-format-patch[] +--minimal:: + Spend extra time to make sure the smallest possible + diff is produced. + --patience:: Generate a diff using the "patience diff" algorithm. diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index 3470910109..2a49de7cfe 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -17,9 +17,10 @@ Use `git mergetool` to run one of several merge utilities to resolve merge conflicts. It is typically run after 'git merge'. If one or more <file> parameters are given, the merge tool program will -be run to resolve differences on each file. If no <file> names are -specified, 'git mergetool' will run the merge tool program on every file -with merge conflicts. +be run to resolve differences on each file (skipping those without +conflicts). Specifying a directory will include all unresolved files in +that path. If no <file> names are specified, 'git mergetool' will run +the merge tool program on every file with merge conflicts. OPTIONS ------- diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index c45d53c6e1..5375549820 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -47,7 +47,7 @@ OPTIONS -i:: Usually a merge requires the index file as well as the - files in the working tree are up to date with the + files in the working tree to be up to date with the current head commit, in order not to lose local changes. This flag disables the check with the working tree and is meant to be used when creating a merge of @@ -71,21 +71,21 @@ OPTIONS --aggressive:: Usually a three-way merge by 'git read-tree' resolves the merge for really trivial cases and leaves other - cases unresolved in the index, so that Porcelains can + cases unresolved in the index, so that porcelains can implement different merge policies. This flag makes the - command to resolve a few more cases internally: + command resolve a few more cases internally: + * when one side removes a path and the other side leaves the path unmodified. The resolution is to remove that path. * when both sides remove a path. The resolution is to remove that path. -* when both sides adds a path identically. The resolution +* when both sides add a path identically. The resolution is to add that path. --prefix=<prefix>/:: Keep the current index contents, and read the contents - of named tree-ish under directory at `<prefix>`. The + of the named tree-ish under the directory at `<prefix>`. The original index file cannot have anything at the path - `<prefix>` itself, and have nothing in `<prefix>/` + `<prefix>` itself, nor anything in the `<prefix>/` directory. Note that the `<prefix>/` value must end with a slash. @@ -379,45 +379,45 @@ have finished your work-in-progress), attempt the merge again. Sparse checkout --------------- -"Sparse checkout" allows to sparsely populate working directory. -It uses skip-worktree bit (see linkgit:git-update-index[1]) to tell -Git whether a file on working directory is worth looking at. +"Sparse checkout" allows populating the working directory sparsely. +It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell +Git whether a file in the working directory is worth looking at. -"git read-tree" and other merge-based commands ("git merge", "git -checkout"...) can help maintaining skip-worktree bitmap and working +'git read-tree' and other merge-based commands ('git merge', 'git +checkout'...) can help maintaining the skip-worktree bitmap and working directory update. `$GIT_DIR/info/sparse-checkout` is used to -define the skip-worktree reference bitmap. When "git read-tree" needs -to update working directory, it will reset skip-worktree bit in index +define the skip-worktree reference bitmap. When 'git read-tree' needs +to update the working directory, it resets the skip-worktree bit in the index based on this file, which uses the same syntax as .gitignore files. -If an entry matches a pattern in this file, skip-worktree will be -set on that entry. Otherwise, skip-worktree will be unset. +If an entry matches a pattern in this file, skip-worktree will not be +set on that entry. Otherwise, skip-worktree will be set. Then it compares the new skip-worktree value with the previous one. If -skip-worktree turns from unset to set, it will add the corresponding -file back. If it turns from set to unset, that file will be removed. +skip-worktree turns from set to unset, it will add the corresponding +file back. If it turns from unset to set, that file will be removed. While `$GIT_DIR/info/sparse-checkout` is usually used to specify what -files are in. You can also specify what files are _not_ in, using -negate patterns. For example, to remove file "unwanted": +files are in, you can also specify what files are _not_ in, using +negate patterns. For example, to remove the file `unwanted`: ---------------- -* +/* !unwanted ---------------- -Another tricky thing is fully repopulating working directory when you +Another tricky thing is fully repopulating the working directory when you no longer want sparse checkout. You cannot just disable "sparse -checkout" because skip-worktree are still in the index and you working -directory is still sparsely populated. You should re-populate working +checkout" because skip-worktree bits are still in the index and your working +directory is still sparsely populated. You should re-populate the working directory with the `$GIT_DIR/info/sparse-checkout` file content as follows: ---------------- -* +/* ---------------- -Then you can disable sparse checkout. Sparse checkout support in "git -read-tree" and similar commands is disabled by default. You need to +Then you can disable sparse checkout. Sparse checkout support in 'git +read-tree' and similar commands is disabled by default. You need to turn `core.sparseCheckout` on in order to have sparse checkout support. diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 08cad6d2b6..f977e8780b 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -318,7 +318,7 @@ Any other arguments are passed directly to 'git log' Show what revision and author last modified each line of a file. The output of this mode is format-compatible with the output of `svn blame' by default. Like the SVN blame command, - local uncommitted changes in the working copy are ignored; + local uncommitted changes in the working tree are ignored; the version of the file in the HEAD revision is annotated. Unknown arguments are passed directly to 'git blame'. + diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index d3931294d1..a3081f4e23 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -264,7 +264,9 @@ tree files, you have to explicitly tell git about it by dropping "assume unchanged" bit, either before or after you modify them. In order to set "assume unchanged" bit, use `--assume-unchanged` -option. To unset, use `--no-assume-unchanged`. +option. To unset, use `--no-assume-unchanged`. To see which files +have the "assume unchanged" bit set, use `git ls-files -v` +(see linkgit:git-ls-files[1]). The command looks at `core.ignorestat` configuration variable. When this is true, paths updated with `git update-index paths...` and @@ -363,7 +365,8 @@ ctime for marking files processed) (see linkgit:git-config[1]). SEE ALSO -------- linkgit:git-config[1], -linkgit:git-add[1] +linkgit:git-add[1], +linkgit:git-ls-files[1] GIT --- diff --git a/Documentation/git.txt b/Documentation/git.txt index cbc51d5a94..5e80cfd71a 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -44,9 +44,10 @@ 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.7/git.html[documentation for release 1.7.7] +* link:v1.7.7.1/git.html[documentation for release 1.7.7.1] * release notes for + link:RelNotes/1.7.7.1.txt[1.7.7.1], link:RelNotes/1.7.7.txt[1.7.7]. * link:v1.7.6.4/git.html[documentation for release 1.7.6.4] diff --git a/Documentation/technical/api-argv-array.txt b/Documentation/technical/api-argv-array.txt new file mode 100644 index 0000000000..49b3d52952 --- /dev/null +++ b/Documentation/technical/api-argv-array.txt @@ -0,0 +1,46 @@ +argv-array API +============== + +The argv-array API allows one to dynamically build and store +NULL-terminated lists. An argv-array maintains the invariant that the +`argv` member always points to a non-NULL array, and that the array is +always NULL-terminated at the element pointed to by `argv[argc]`. This +makes the result suitable for passing to functions expecting to receive +argv from main(), or the link:api-run-command.html[run-command API]. + +The link:api-string-list.html[string-list API] is similar, but cannot be +used for these purposes; instead of storing a straight string pointer, +it contains an item structure with a `util` field that is not compatible +with the traditional argv interface. + +Each `argv_array` manages its own memory. Any strings pushed into the +array are duplicated, and all memory is freed by argv_array_clear(). + +Data Structures +--------------- + +`struct argv_array`:: + + A single array. This should be initialized by assignment from + `ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv` + member contains the actual array; the `argc` member contains the + number of elements in the array, not including the terminating + NULL. + +Functions +--------- + +`argv_array_init`:: + Initialize an array. This is no different than assigning from + `ARGV_ARRAY_INIT`. + +`argv_array_push`:: + Push a copy of a string onto the end of the array. + +`argv_array_pushf`:: + Format a string and push it onto the end of the array. This is a + convenience wrapper combining `strbuf_addf` and `argv_array_push`. + +`argv_array_clear`:: + Free all memory associated with the array and return it to the + initial, empty state. diff --git a/Documentation/technical/api-sha1-array.txt b/Documentation/technical/api-sha1-array.txt new file mode 100644 index 0000000000..4a4bae8109 --- /dev/null +++ b/Documentation/technical/api-sha1-array.txt @@ -0,0 +1,79 @@ +sha1-array API +============== + +The sha1-array API provides storage and manipulation of sets of SHA1 +identifiers. The emphasis is on storage and processing efficiency, +making them suitable for large lists. Note that the ordering of items is +not preserved over some operations. + +Data Structures +--------------- + +`struct sha1_array`:: + + A single array of SHA1 hashes. This should be initialized by + assignment from `SHA1_ARRAY_INIT`. The `sha1` member contains + the actual data. The `nr` member contains the number of items in + the set. The `alloc` and `sorted` members are used internally, + and should not be needed by API callers. + +Functions +--------- + +`sha1_array_append`:: + Add an item to the set. The sha1 will be placed at the end of + the array (but note that some operations below may lose this + ordering). + +`sha1_array_sort`:: + Sort the elements in the array. + +`sha1_array_lookup`:: + Perform a binary search of the array for a specific sha1. + If found, returns the offset (in number of elements) of the + sha1. If not found, returns a negative integer. If the array is + not sorted, this function has the side effect of sorting it. + +`sha1_array_clear`:: + Free all memory associated with the array and return it to the + initial, empty state. + +`sha1_array_for_each_unique`:: + Efficiently iterate over each unique element of the list, + executing the callback function for each one. If the array is + not sorted, this function has the side effect of sorting it. + +Examples +-------- + +----------------------------------------- +void print_callback(const unsigned char sha1[20], + void *data) +{ + printf("%s\n", sha1_to_hex(sha1)); +} + +void some_func(void) +{ + struct sha1_array hashes = SHA1_ARRAY_INIT; + unsigned char sha1[20]; + + /* Read objects into our set */ + while (read_object_from_stdin(sha1)) + sha1_array_append(&hashes, sha1); + + /* Check if some objects are in our set */ + while (read_object_from_stdin(sha1)) { + if (sha1_array_lookup(&hashes, sha1) >= 0) + printf("it's in there!\n"); + + /* + * Print the unique set of objects. We could also have + * avoided adding duplicate objects in the first place, + * but we would end up re-sorting the array repeatedly. + * Instead, this will sort once and then skip duplicates + * in linear time. + */ + sha1_array_for_each_unique(&hashes, print_callback, NULL); +} +----------------------------------------- diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index f1dc5faead..dbdb4c137f 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.7.7 +DEF_VER=v1.7.7.1 LF=' ' @@ -505,6 +505,7 @@ VCSSVN_LIB=vcs-svn/lib.a LIB_H += advice.h LIB_H += archive.h +LIB_H += argv-array.h LIB_H += attr.h LIB_H += blob.h LIB_H += builtin.h @@ -586,6 +587,7 @@ LIB_OBJS += alloc.o LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o +LIB_OBJS += argv-array.o LIB_OBJS += attr.o LIB_OBJS += base85.o LIB_OBJS += bisect.o @@ -820,6 +822,7 @@ ifeq ($(uname_S),GNU/kFreeBSD) NO_STRLCPY = YesPlease NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease + DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease endif ifeq ($(uname_S),UnixWare) CC = cc @@ -1 +1 @@ -Documentation/RelNotes/1.7.7.txt
\ No newline at end of file +Documentation/RelNotes/1.7.7.1.txt
\ No newline at end of file diff --git a/argv-array.c b/argv-array.c new file mode 100644 index 0000000000..a4e04201e6 --- /dev/null +++ b/argv-array.c @@ -0,0 +1,51 @@ +#include "cache.h" +#include "argv-array.h" +#include "strbuf.h" + +static const char *empty_argv_storage = NULL; +const char **empty_argv = &empty_argv_storage; + +void argv_array_init(struct argv_array *array) +{ + array->argv = empty_argv; + array->argc = 0; + array->alloc = 0; +} + +static void argv_array_push_nodup(struct argv_array *array, const char *value) +{ + if (array->argv == empty_argv) + array->argv = NULL; + + ALLOC_GROW(array->argv, array->argc + 2, array->alloc); + array->argv[array->argc++] = value; + array->argv[array->argc] = NULL; +} + +void argv_array_push(struct argv_array *array, const char *value) +{ + argv_array_push_nodup(array, xstrdup(value)); +} + +void argv_array_pushf(struct argv_array *array, const char *fmt, ...) +{ + va_list ap; + struct strbuf v = STRBUF_INIT; + + va_start(ap, fmt); + strbuf_vaddf(&v, fmt, ap); + va_end(ap); + + argv_array_push_nodup(array, strbuf_detach(&v, NULL)); +} + +void argv_array_clear(struct argv_array *array) +{ + if (array->argv != empty_argv) { + int i; + for (i = 0; i < array->argc; i++) + free((char **)array->argv[i]); + free(array->argv); + } + argv_array_init(array); +} diff --git a/argv-array.h b/argv-array.h new file mode 100644 index 0000000000..74dd2b1bc0 --- /dev/null +++ b/argv-array.h @@ -0,0 +1,20 @@ +#ifndef ARGV_ARRAY_H +#define ARGV_ARRAY_H + +extern const char **empty_argv; + +struct argv_array { + const char **argv; + int argc; + int alloc; +}; + +#define ARGV_ARRAY_INIT { empty_argv, 0, 0 } + +void argv_array_init(struct argv_array *); +void argv_array_push(struct argv_array *, const char *); +__attribute__((format (printf,2,3))) +void argv_array_pushf(struct argv_array *, const char *fmt, ...); +void argv_array_clear(struct argv_array *); + +#endif /* ARGV_ARRAY_H */ @@ -10,18 +10,13 @@ #include "log-tree.h" #include "bisect.h" #include "sha1-array.h" +#include "argv-array.h" static struct sha1_array good_revs; static struct sha1_array skipped_revs; static const unsigned char *current_bad_sha1; -struct argv_array { - const char **argv; - int argv_nr; - int argv_alloc; -}; - static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; static const char *argv_show_branch[] = {"show-branch", NULL, NULL}; static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL}; @@ -405,21 +400,6 @@ struct commit_list *find_bisection(struct commit_list *list, return best; } -static void argv_array_push(struct argv_array *array, const char *string) -{ - ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc); - array->argv[array->argv_nr++] = string; -} - -static void argv_array_push_sha1(struct argv_array *array, - const unsigned char *sha1, - const char *format) -{ - struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, format, sha1_to_hex(sha1)); - argv_array_push(array, strbuf_detach(&buf, NULL)); -} - static int register_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { @@ -449,16 +429,10 @@ static void read_bisect_paths(struct argv_array *array) die_errno("Could not open file '%s'", filename); while (strbuf_getline(&str, fp, '\n') != EOF) { - char *quoted; - int res; - strbuf_trim(&str); - quoted = strbuf_detach(&str, NULL); - res = sq_dequote_to_argv(quoted, &array->argv, - &array->argv_nr, &array->argv_alloc); - if (res) + if (sq_dequote_to_argv_array(str.buf, array)) die("Badly quoted content in file '%s': %s", - filename, quoted); + filename, str.buf); } strbuf_release(&str); @@ -623,7 +597,7 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix, const char *bad_format, const char *good_format, int read_paths) { - struct argv_array rev_argv = { NULL, 0, 0 }; + struct argv_array rev_argv = ARGV_ARRAY_INIT; int i; init_revisions(revs, prefix); @@ -631,17 +605,17 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix, revs->commit_format = CMIT_FMT_UNSPECIFIED; /* rev_argv.argv[0] will be ignored by setup_revisions */ - argv_array_push(&rev_argv, xstrdup("bisect_rev_setup")); - argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format); + argv_array_push(&rev_argv, "bisect_rev_setup"); + argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1)); for (i = 0; i < good_revs.nr; i++) - argv_array_push_sha1(&rev_argv, good_revs.sha1[i], - good_format); - argv_array_push(&rev_argv, xstrdup("--")); + argv_array_pushf(&rev_argv, good_format, + sha1_to_hex(good_revs.sha1[i])); + argv_array_push(&rev_argv, "--"); if (read_paths) read_bisect_paths(&rev_argv); - argv_array_push(&rev_argv, NULL); - setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL); + setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL); + /* XXX leak rev_argv, as "revs" may still be pointing to it */ } static void bisect_common(struct rev_info *revs) diff --git a/builtin/apply.c b/builtin/apply.c index f2edc52818..311a94e181 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1407,6 +1407,9 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc "%d leading pathname components (line %d)" , p_value, linenr); patch->old_name = patch->new_name = patch->def_name; } + if (!patch->is_delete && !patch->new_name) + die("git diff header lacks filename information " + "(line %d)", linenr); patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; @@ -2447,6 +2450,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, char *old, *oldlines; struct strbuf newlines; int new_blank_lines_at_end = 0; + int found_new_blank_lines_at_end = 0; + int hunk_linenr = frag->linenr; unsigned long leading, trailing; int pos, applied_pos; struct image preimage; @@ -2540,14 +2545,18 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, error("invalid start of line: '%c'", first); return -1; } - if (added_blank_line) + if (added_blank_line) { + if (!new_blank_lines_at_end) + found_new_blank_lines_at_end = hunk_linenr; new_blank_lines_at_end++; + } else if (is_blank_context) ; else new_blank_lines_at_end = 0; patch += len; size -= len; + hunk_linenr++; } if (inaccurate_eof && old > oldlines && old[-1] == '\n' && @@ -2629,7 +2638,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, preimage.nr + applied_pos >= img->nr && (ws_rule & WS_BLANK_AT_EOF) && ws_error_action != nowarn_ws_error) { - record_ws_error(WS_BLANK_AT_EOF, "+", 1, frag->linenr); + record_ws_error(WS_BLANK_AT_EOF, "+", 1, + found_new_blank_lines_at_end); if (ws_error_action == correct_ws_error) { while (new_blank_lines_at_end--) remove_last_line(&postimage); diff --git a/builtin/archive.c b/builtin/archive.c index 883c0092ad..931956def9 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -61,6 +61,8 @@ static int run_remote_archiver(int argc, const char **argv, if (strcmp(buf, "ACK")) { if (len > 5 && !prefixcmp(buf, "NACK ")) die(_("git archive: NACK %s"), buf + 5); + if (len > 4 && !prefixcmp(buf, "ERR ")) + die(_("remote error: %s"), buf + 4); die(_("git archive: protocol error")); } diff --git a/builtin/bundle.c b/builtin/bundle.c index 81046a9cb8..92a8a6026a 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -58,7 +58,7 @@ int cmd_bundle(int argc, const char **argv, const char *prefix) } else if (!strcmp(cmd, "unbundle")) { if (!startup_info->have_repository) die(_("Need a repository to unbundle.")); - return !!unbundle(&header, bundle_fd) || + return !!unbundle(&header, bundle_fd, 0) || list_bundle_refs(&header, argc, argv); } else usage(builtin_bundle_usage); diff --git a/builtin/checkout.c b/builtin/checkout.c index 5e356a6c61..a7a493cecf 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -19,6 +19,7 @@ #include "ll-merge.h" #include "resolve-undo.h" #include "submodule.h" +#include "argv-array.h" static const char * const checkout_usage[] = { "git checkout [options] <branch>", @@ -71,7 +72,7 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, hashcpy(ce->sha1, sha1); memcpy(ce->name, base, baselen); memcpy(ce->name + baselen, pathname, len - baselen); - ce->ce_flags = create_ce_flags(len, 0); + ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE; ce->ce_mode = create_ce_mode(mode); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); return 0; @@ -228,6 +229,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; + if (source_tree && !(ce->ce_flags & CE_UPDATE)) + continue; match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched); } @@ -266,6 +269,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, state.refresh_cache = 1; for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; + if (source_tree && !(ce->ce_flags & CE_UPDATE)) + continue; if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) { if (!ce_stage(ce)) { errs |= checkout_entry(ce, &state, NULL); @@ -588,24 +593,12 @@ static void update_refs_for_switch(struct checkout_opts *opts, report_tracking(new); } -struct rev_list_args { - int argc; - int alloc; - const char **argv; -}; - -static void add_one_rev_list_arg(struct rev_list_args *args, const char *s) -{ - ALLOC_GROW(args->argv, args->argc + 1, args->alloc); - args->argv[args->argc++] = s; -} - static int add_one_ref_to_rev_list_arg(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { - add_one_rev_list_arg(cb_data, refname); + argv_array_push(cb_data, refname); return 0; } @@ -685,15 +678,14 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs) */ static void orphaned_commit_warning(struct commit *commit) { - struct rev_list_args args = { 0, 0, NULL }; + struct argv_array args = ARGV_ARRAY_INIT; struct rev_info revs; - add_one_rev_list_arg(&args, "(internal)"); - add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1)); - add_one_rev_list_arg(&args, "--not"); + argv_array_push(&args, "(internal)"); + argv_array_push(&args, sha1_to_hex(commit->object.sha1)); + argv_array_push(&args, "--not"); for_each_ref(add_one_ref_to_rev_list_arg, &args); - add_one_rev_list_arg(&args, "--"); - add_one_rev_list_arg(&args, NULL); + argv_array_push(&args, "--"); init_revisions(&revs, NULL); if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1) @@ -705,6 +697,7 @@ static void orphaned_commit_warning(struct commit *commit) else describe_detached_head(_("Previous HEAD position was"), commit); + argv_array_clear(&args); clear_commit_marks(commit, -1); for_each_ref(clear_commit_marks_from_one_ref, NULL); } diff --git a/builtin/clone.c b/builtin/clone.c index 488f48e9a5..efe8b6cce5 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -577,9 +577,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (0 <= option_verbosity) { if (option_bare) - printf(_("Cloning into bare repository %s...\n"), dir); + printf(_("Cloning into bare repository '%s'...\n"), dir); else - printf(_("Cloning into %s...\n"), dir); + printf(_("Cloning into '%s'...\n"), dir); } init_db(option_template, INIT_DB_QUIET); write_config(&option_config); diff --git a/builtin/commit.c b/builtin/commit.c index cbc9613ec6..66ffe31736 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1392,7 +1392,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (get_sha1("HEAD", sha1)) current_head = NULL; else { - current_head = lookup_commit(sha1); + current_head = lookup_commit_or_die(sha1, "HEAD"); if (!current_head || parse_commit(current_head)) die(_("could not parse HEAD commit")); } @@ -1424,6 +1424,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) pptr = &commit_list_insert(c->item, pptr)->next; } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; + struct commit *commit; FILE *fp; if (!reflog_msg) @@ -1437,7 +1438,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) unsigned char sha1[20]; if (get_sha1_hex(m.buf, sha1) < 0) die(_("Corrupt MERGE_HEAD file (%s)"), m.buf); - pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next; + commit = lookup_commit_or_die(sha1, "MERGE_HEAD"); + pptr = &commit_list_insert(commit, pptr)->next; } fclose(fp); strbuf_release(&m); diff --git a/builtin/config.c b/builtin/config.c index 0b4ecac855..0315ad76f8 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -99,6 +99,7 @@ static int show_config(const char *key_, const char *value_, void *cb) const char *vptr = value; int must_free_vptr = 0; int dup_error = 0; + int must_print_delim = 0; if (!use_key_regexp && strcmp(key_, key)) return 0; @@ -109,10 +110,8 @@ static int show_config(const char *key_, const char *value_, void *cb) return 0; if (show_keys) { - if (value_) - printf("%s%c", key_, key_delim); - else - printf("%s", key_); + printf("%s", key_); + must_print_delim = 1; } if (seen && !do_all) dup_error = 1; @@ -130,16 +129,23 @@ static int show_config(const char *key_, const char *value_, void *cb) } else if (types == TYPE_PATH) { git_config_pathname(&vptr, key_, value_); must_free_vptr = 1; + } else if (value_) { + vptr = value_; + } else { + /* Just show the key name */ + vptr = ""; + must_print_delim = 0; } - else - vptr = value_?value_:""; seen++; if (dup_error) { error("More than one value for the key %s: %s", key_, vptr); } - else + else { + if (must_print_delim) + printf("%c", key_delim); printf("%s%c", vptr, term); + } if (must_free_vptr) /* If vptr must be freed, it's a pointer to a * dynamically allocated buffer, it's safe to cast to diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 75816329d6..7e2f22589d 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -293,7 +293,7 @@ static int do_fmt_merge_msg(int merge_title, struct strbuf *in, struct commit *head; struct rev_info rev; - head = lookup_commit(head_sha1); + head = lookup_commit_or_die(head_sha1, "HEAD"); init_revisions(&rev, NULL); rev.commit_format = CMIT_FMT_ONELINE; rev.ignore_merges = 1; diff --git a/builtin/fsck.c b/builtin/fsck.c index 5ae0366bc8..df1a88b51a 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -231,12 +231,9 @@ static void check_unreachable_object(struct object *obj) unsigned long size; char *buf = read_sha1_file(obj->sha1, &type, &size); - if (buf) { - if (fwrite(buf, size, 1, f) != 1) - die_errno("Could not write '%s'", - filename); - free(buf); - } + if (buf && fwrite(buf, 1, size, f) != size) + die_errno("Could not write '%s'", filename); + free(buf); } else fprintf(f, "%s\n", sha1_to_hex(obj->sha1)); if (fclose(f)) diff --git a/builtin/grep.c b/builtin/grep.c index 1c359c2671..a286692e46 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -598,8 +598,11 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct strbuf base; int hit, len; + read_sha1_lock(); data = read_object_with_reference(obj->sha1, tree_type, &size, NULL); + read_sha1_unlock(); + if (!data) die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1)); diff --git a/builtin/merge.c b/builtin/merge.c index ab4077f272..581f494aee 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -50,7 +50,6 @@ static int fast_forward_only; static int allow_trivial = 1, have_message; static struct strbuf merge_msg; static struct commit_list *remoteheads; -static unsigned char head[20], stash[20]; static struct strategy **use_strategies; static size_t use_strategies_nr, use_strategies_alloc; static const char **xopts; @@ -217,7 +216,7 @@ static void drop_save(void) unlink(git_path("MERGE_MODE")); } -static void save_state(void) +static int save_state(unsigned char *stash) { int len; struct child_process cp; @@ -236,11 +235,12 @@ static void save_state(void) if (finish_command(&cp) || len < 0) die(_("stash failed")); - else if (!len) - return; + else if (!len) /* no changes */ + return -1; strbuf_setlen(&buffer, buffer.len-1); if (get_sha1(buffer.buf, stash)) die(_("not a valid object: %s"), buffer.buf); + return 0; } static void read_empty(unsigned const char *sha1, int verbose) @@ -278,7 +278,8 @@ static void reset_hard(unsigned const char *sha1, int verbose) die(_("read-tree failed")); } -static void restore_state(void) +static void restore_state(const unsigned char *head, + const unsigned char *stash) { struct strbuf sb = STRBUF_INIT; const char *args[] = { "stash", "apply", NULL, NULL }; @@ -308,10 +309,9 @@ static void finish_up_to_date(const char *msg) drop_save(); } -static void squash_message(void) +static void squash_message(struct commit *commit) { struct rev_info rev; - struct commit *commit; struct strbuf out = STRBUF_INIT; struct commit_list *j; int fd; @@ -326,7 +326,6 @@ static void squash_message(void) rev.ignore_merges = 1; rev.commit_format = CMIT_FMT_MEDIUM; - commit = lookup_commit(head); commit->object.flags |= UNINTERESTING; add_pending_object(&rev, &commit->object, NULL); @@ -355,9 +354,11 @@ static void squash_message(void) strbuf_release(&out); } -static void finish(const unsigned char *new_head, const char *msg) +static void finish(struct commit *head_commit, + const unsigned char *new_head, const char *msg) { struct strbuf reflog_message = STRBUF_INIT; + const unsigned char *head = head_commit->object.sha1; if (!msg) strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); @@ -368,7 +369,7 @@ static void finish(const unsigned char *new_head, const char *msg) getenv("GIT_REFLOG_ACTION"), msg); } if (squash) { - squash_message(); + squash_message(head_commit); } else { if (verbosity >= 0 && !merge_msg.len) printf(_("No merge message -- not updating HEAD\n")); @@ -661,7 +662,7 @@ int try_merge_command(const char *strategy, size_t xopts_nr, } static int try_merge_strategy(const char *strategy, struct commit_list *common, - const char *head_arg) + struct commit *head, const char *head_arg) { int index_fd; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); @@ -707,7 +708,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, commit_list_insert(j->item, &reversed); index_fd = hold_locked_index(lock, 1); - clean = merge_recursive(&o, lookup_commit(head), + clean = merge_recursive(&o, head, remoteheads->item, reversed, &result); if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || @@ -858,25 +859,26 @@ static void run_prepare_commit_msg(void) read_merge_msg(); } -static int merge_trivial(void) +static int merge_trivial(struct commit *head) { unsigned char result_tree[20], result_commit[20]; struct commit_list *parent = xmalloc(sizeof(*parent)); write_tree_trivial(result_tree); printf(_("Wonderful.\n")); - parent->item = lookup_commit(head); + parent->item = head; parent->next = xmalloc(sizeof(*parent->next)); parent->next->item = remoteheads->item; parent->next->next = NULL; run_prepare_commit_msg(); commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL); - finish(result_commit, "In-index merge"); + finish(head, result_commit, "In-index merge"); drop_save(); return 0; } -static int finish_automerge(struct commit_list *common, +static int finish_automerge(struct commit *head, + struct commit_list *common, unsigned char *result_tree, const char *wt_strategy) { @@ -887,12 +889,12 @@ static int finish_automerge(struct commit_list *common, free_commit_list(common); if (allow_fast_forward) { parents = remoteheads; - commit_list_insert(lookup_commit(head), &parents); + commit_list_insert(head, &parents); parents = reduce_heads(parents); } else { struct commit_list **pptr = &parents; - pptr = &commit_list_insert(lookup_commit(head), + pptr = &commit_list_insert(head, pptr)->next; for (j = remoteheads; j; j = j->next) pptr = &commit_list_insert(j->item, pptr)->next; @@ -902,7 +904,7 @@ static int finish_automerge(struct commit_list *common, run_prepare_commit_msg(); commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); - finish(result_commit, buf.buf); + finish(head, result_commit, buf.buf); strbuf_release(&buf); drop_save(); return 0; @@ -936,7 +938,8 @@ static int suggest_conflicts(int renormalizing) return 1; } -static struct commit *is_old_style_invocation(int argc, const char **argv) +static struct commit *is_old_style_invocation(int argc, const char **argv, + const unsigned char *head) { struct commit *second_token = NULL; if (argc > 2) { @@ -1008,9 +1011,12 @@ static int setup_with_upstream(const char ***argv) int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; + unsigned char stash[20]; + unsigned char head_sha1[20]; + struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; - int flag, head_invalid = 0, i; + int flag, i; int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; struct commit_list *common = NULL; const char *best_strategy = NULL, *wt_strategy = NULL; @@ -1023,11 +1029,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ - branch = resolve_ref("HEAD", head, 0, &flag); + branch = resolve_ref("HEAD", head_sha1, 0, &flag); if (branch && !prefixcmp(branch, "refs/heads/")) branch += 11; - if (is_null_sha1(head)) - head_invalid = 1; + if (!branch || is_null_sha1(head_sha1)) + head_commit = NULL; + else + head_commit = lookup_commit_or_die(head_sha1, "HEAD"); git_config(git_merge_config, NULL); @@ -1104,12 +1112,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * additional safety measure to check for it. */ - if (!have_message && is_old_style_invocation(argc, argv)) { + if (!have_message && head_commit && + is_old_style_invocation(argc, argv, head_commit->object.sha1)) { strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; argv += 2; argc -= 2; - } else if (head_invalid) { + } else if (!head_commit) { struct object *remote_head; /* * If the merged head is a valid one there is no reason @@ -1156,7 +1165,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } } - if (head_invalid || !argc) + if (!head_commit || !argc) usage_with_options(builtin_merge_usage, builtin_merge_options); @@ -1197,17 +1206,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } if (!remoteheads->next) - common = get_merge_bases(lookup_commit(head), - remoteheads->item, 1); + common = get_merge_bases(head_commit, remoteheads->item, 1); else { struct commit_list *list = remoteheads; - commit_list_insert(lookup_commit(head), &list); + commit_list_insert(head_commit, &list); common = get_octopus_merge_bases(list); free(list); } - update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0, - DIE_ON_ERR); + update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1, + NULL, 0, DIE_ON_ERR); if (!common) ; /* No common ancestors found. We need a real merge. */ @@ -1221,13 +1229,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix) return 0; } else if (allow_fast_forward && !remoteheads->next && !common->next && - !hashcmp(common->item->object.sha1, head)) { + !hashcmp(common->item->object.sha1, head_commit->object.sha1)) { /* Again the most common case of merging one remote. */ struct strbuf msg = STRBUF_INIT; struct object *o; char hex[41]; - strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV)); + strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV)); if (verbosity >= 0) printf(_("Updating %s..%s\n"), @@ -1243,10 +1251,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!o) return 1; - if (checkout_fast_forward(head, remoteheads->item->object.sha1)) + if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1)) return 1; - finish(o->sha1, msg.buf); + finish(head_commit, o->sha1, msg.buf); drop_save(); return 0; } else if (!remoteheads->next && common->next) @@ -1266,8 +1274,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) git_committer_info(IDENT_ERROR_ON_NO_NAME); printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.sha1, - head, remoteheads->item->object.sha1)) - return merge_trivial(); + head_commit->object.sha1, remoteheads->item->object.sha1)) + return merge_trivial(head_commit); printf(_("Nope.\n")); } } else { @@ -1286,8 +1294,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = get_merge_bases(lookup_commit(head), - j->item, 1); + common_one = get_merge_bases(head_commit, j->item, 1); if (hashcmp(common_one->item->object.sha1, j->item->object.sha1)) { up_to_date = 0; @@ -1314,21 +1321,18 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * sync with the head commit. The strategies are responsible * to ensure this. */ - if (use_strategies_nr != 1) { - /* - * Stash away the local changes so that we can try more - * than one. - */ - save_state(); - } else { - memcpy(stash, null_sha1, 20); - } + if (use_strategies_nr == 1 || + /* + * Stash away the local changes so that we can try more than one. + */ + save_state(stash)) + hashcpy(stash, null_sha1); for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { printf(_("Rewinding the tree to pristine...\n")); - restore_state(); + restore_state(head_commit->object.sha1, stash); } if (use_strategies_nr != 1) printf(_("Trying merge strategy %s...\n"), @@ -1340,7 +1344,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) wt_strategy = use_strategies[i]->name; ret = try_merge_strategy(use_strategies[i]->name, - common, head_arg); + common, head_commit, head_arg); if (!option_commit && !ret) { merge_was_ok = 1; /* @@ -1382,14 +1386,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * auto resolved the merge cleanly. */ if (automerge_was_ok) - return finish_automerge(common, result_tree, wt_strategy); + return finish_automerge(head_commit, common, result_tree, + wt_strategy); /* * Pick the result from the best strategy and have the user fix * it up. */ if (!best_strategy) { - restore_state(); + restore_state(head_commit->object.sha1, stash); if (use_strategies_nr > 1) fprintf(stderr, _("No merge strategy handled the merge.\n")); @@ -1401,14 +1406,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix) ; /* We already have its result in the working tree. */ else { printf(_("Rewinding the tree to pristine...\n")); - restore_state(); + restore_state(head_commit->object.sha1, stash); printf(_("Using the %s to prepare resolving by hand.\n"), best_strategy); - try_merge_strategy(best_strategy, common, head_arg); + try_merge_strategy(best_strategy, common, head_commit, head_arg); } if (squash) - finish(NULL, NULL); + finish(head_commit, NULL, NULL); else { int fd; struct commit_list *j; diff --git a/builtin/patch-id.c b/builtin/patch-id.c index f821eb3f0b..3cfe02d5a5 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -56,13 +56,13 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after) return 1; } -static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx) +static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct strbuf *line_buf) { - static char line[1000]; int patchlen = 0, found_next = 0; int before = -1, after = -1; - while (fgets(line, sizeof(line), stdin) != NULL) { + while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) { + char *line = line_buf->buf; char *p = line; int len; @@ -133,14 +133,16 @@ static void generate_id_list(void) unsigned char sha1[20], n[20]; git_SHA_CTX ctx; int patchlen; + struct strbuf line_buf = STRBUF_INIT; git_SHA1_Init(&ctx); hashclr(sha1); while (!feof(stdin)) { - patchlen = get_one_patchid(n, &ctx); + patchlen = get_one_patchid(n, &ctx, &line_buf); flush_current_id(patchlen, sha1, &ctx); hashcpy(sha1, n); } + strbuf_release(&line_buf); } static const char patch_id_usage[] = "git patch-id < patch"; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index ae164da4d5..af429e1017 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -205,21 +205,15 @@ static int copy_to_sideband(int in, int out, void *arg) return 0; } -static int run_receive_hook(struct command *commands, const char *hook_name) +typedef int (*feed_fn)(void *, const char **, size_t *); +static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state) { - static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4]; - struct command *cmd; struct child_process proc; struct async muxer; const char *argv[2]; - int have_input = 0, code; - - for (cmd = commands; !have_input && cmd; cmd = cmd->next) { - if (!cmd->error_string) - have_input = 1; - } + int code; - if (!have_input || access(hook_name, X_OK) < 0) + if (access(hook_name, X_OK) < 0) return 0; argv[0] = hook_name; @@ -247,15 +241,13 @@ static int run_receive_hook(struct command *commands, const char *hook_name) return code; } - for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) { - size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", - sha1_to_hex(cmd->old_sha1), - sha1_to_hex(cmd->new_sha1), - cmd->ref_name); - if (write_in_full(proc.in, buf, n) != n) - break; - } + while (1) { + const char *buf; + size_t n; + if (feed(feed_state, &buf, &n)) + break; + if (write_in_full(proc.in, buf, n) != n) + break; } close(proc.in); if (use_sideband) @@ -263,6 +255,47 @@ static int run_receive_hook(struct command *commands, const char *hook_name) return finish_command(&proc); } +struct receive_hook_feed_state { + struct command *cmd; + struct strbuf buf; +}; + +static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) +{ + struct receive_hook_feed_state *state = state_; + struct command *cmd = state->cmd; + + while (cmd && cmd->error_string) + cmd = cmd->next; + if (!cmd) + return -1; /* EOF */ + strbuf_reset(&state->buf); + strbuf_addf(&state->buf, "%s %s %s\n", + sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1), + cmd->ref_name); + state->cmd = cmd->next; + if (bufp) { + *bufp = state->buf.buf; + *sizep = state->buf.len; + } + return 0; +} + +static int run_receive_hook(struct command *commands, const char *hook_name) +{ + struct receive_hook_feed_state state; + int status; + + strbuf_init(&state.buf, 0); + state.cmd = commands; + if (feed_receive_hook(&state, NULL, NULL)) + return 0; + state.cmd = commands; + status = run_and_feed_hook(hook_name, feed_receive_hook, &state); + strbuf_release(&state.buf); + return status; +} + static int run_update_hook(struct command *cmd) { static const char update_hook[] = "hooks/update"; diff --git a/builtin/remote.c b/builtin/remote.c index de52367927..e1285becff 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -571,7 +571,7 @@ static int read_remote_branches(const char *refname, unsigned char orig_sha1[20]; const char *symref; - strbuf_addf(&buf, "refs/remotes/%s", rename->old); + strbuf_addf(&buf, "refs/remotes/%s/", rename->old); if (!prefixcmp(refname, buf.buf)) { item = string_list_append(rename->remote_branches, xstrdup(refname)); symref = resolve_ref(refname, orig_sha1, 1, &flag); @@ -622,10 +622,11 @@ static int mv(int argc, const char **argv) OPT_END() }; struct remote *oldremote, *newremote; - struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT, + old_remote_context = STRBUF_INIT; struct string_list remote_branches = STRING_LIST_INIT_NODUP; struct rename_info rename; - int i; + int i, refspec_updated = 0; if (argc != 3) usage_with_options(builtin_remote_rename_usage, options); @@ -660,15 +661,25 @@ static int mv(int argc, const char **argv) strbuf_addf(&buf, "remote.%s.fetch", rename.new); if (git_config_set_multivar(buf.buf, NULL, NULL, 1)) return error("Could not remove config section '%s'", buf.buf); + strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old); for (i = 0; i < oldremote->fetch_refspec_nr; i++) { char *ptr; strbuf_reset(&buf2); strbuf_addstr(&buf2, oldremote->fetch_refspec[i]); - ptr = strstr(buf2.buf, rename.old); - if (ptr) - strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old), - rename.new, strlen(rename.new)); + ptr = strstr(buf2.buf, old_remote_context.buf); + if (ptr) { + refspec_updated = 1; + strbuf_splice(&buf2, + ptr-buf2.buf + strlen(":refs/remotes/"), + strlen(rename.old), rename.new, + strlen(rename.new)); + } else + warning("Not updating non-default fetch respec\n" + "\t%s\n" + "\tPlease update the configuration manually if necessary.", + buf2.buf); + if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0)) return error("Could not append '%s'", buf.buf); } @@ -686,6 +697,9 @@ static int mv(int argc, const char **argv) } } + if (!refspec_updated) + return 0; + /* * First remove symrefs, then rename the rest, finally create * the new symrefs. @@ -380,12 +380,15 @@ int create_bundle(struct bundle_header *header, const char *path, return 0; } -int unbundle(struct bundle_header *header, int bundle_fd) +int unbundle(struct bundle_header *header, int bundle_fd, int flags) { const char *argv_index_pack[] = {"index-pack", - "--fix-thin", "--stdin", NULL}; + "--fix-thin", "--stdin", NULL, NULL}; struct child_process ip; + if (flags & BUNDLE_VERBOSE) + argv_index_pack[3] = "-v"; + if (verify_bundle(header, 0)) return -1; memset(&ip, 0, sizeof(ip)); @@ -18,7 +18,8 @@ int read_bundle_header(const char *path, struct bundle_header *header); int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv); int verify_bundle(struct bundle_header *header, int verbose); -int unbundle(struct bundle_header *header, int bundle_fd); +#define BUNDLE_VERBOSE 1 +int unbundle(struct bundle_header *header, int bundle_fd, int flags); int list_bundle_refs(struct bundle_header *header, int argc, const char **argv); @@ -39,6 +39,18 @@ struct commit *lookup_commit_reference(const unsigned char *sha1) return lookup_commit_reference_gently(sha1, 0); } +struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name) +{ + struct commit *c = lookup_commit_reference(sha1); + if (!c) + die(_("could not parse %s"), ref_name); + if (hashcmp(sha1, c->object.sha1)) { + warning(_("%s %s is not a commit!"), + ref_name, sha1_to_hex(sha1)); + } + return c; +} + struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); @@ -38,6 +38,13 @@ struct commit *lookup_commit_reference_gently(const unsigned char *sha1, int quiet); struct commit *lookup_commit_reference_by_name(const char *name); +/* + * Look up object named by "sha1", dereference tag as necessary, + * get a commit and return it. If "sha1" does not dereference to + * a commit, use ref_name to report an error and die. + */ +struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name); + int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size); int parse_commit(struct commit *item); @@ -552,23 +552,35 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt static int match_tz(const char *date, int *offp) { char *end; - int offset = strtoul(date+1, &end, 10); - int min, hour; - int n = end - date - 1; + int hour = strtoul(date + 1, &end, 10); + int n = end - (date + 1); + int min = 0; - min = offset % 100; - hour = offset / 100; + if (n == 4) { + /* hhmm */ + min = hour % 100; + hour = hour / 100; + } else if (n != 2) { + min = 99; /* random crap */ + } else if (*end == ':') { + /* hh:mm? */ + min = strtoul(end + 1, &end, 10); + if (end - (date + 1) != 5) + min = 99; /* random crap */ + } /* otherwise we parsed "hh" */ /* - * Don't accept any random crap.. At least 3 digits, and - * a valid minute. We might want to check that the minutes - * are divisible by 30 or something too. + * Don't accept any random crap. Even though some places have + * offset larger than 12 hours (e.g. Pacific/Kiritimati is at + * UTC+14), there is something wrong if hour part is much + * larger than that. We might also want to check that the + * minutes are divisible by 15 or something too. (Offset of + * Kathmandu, Nepal is UTC+5:45) */ - if (min < 60 && n > 2) { - offset = hour*60+min; + if (min < 60 && hour < 24) { + int offset = hour * 60 + min; if (*date == '-') offset = -offset; - *offp = offset; } return end - date; diff --git a/diff-lib.c b/diff-lib.c index f8454dd291..62f4cd94cf 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -289,7 +289,7 @@ static void show_new_file(struct rev_info *revs, /* * New file in the index: it might actually be different in - * the working copy. + * the working tree. */ if (get_stat_data(new, &sha1, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) @@ -468,6 +468,7 @@ static int diff_cache(struct rev_info *revs, opts.unpack_data = revs; opts.src_index = &the_index; opts.dst_index = NULL; + opts.pathspec = &revs->diffopt.pathspec; init_tree_desc(&t, tree->buffer, tree->size); return unpack_trees(1, &t, &opts); @@ -3385,6 +3385,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } /* xdiff options */ + else if (!strcmp(arg, "--minimal")) + DIFF_XDL_SET(options, NEED_MINIMAL); + else if (!strcmp(arg, "--no-minimal")) + DIFF_XDL_CLR(options, NEED_MINIMAL); else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) DIFF_XDL_SET(options, IGNORE_WHITESPACE); else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 804a7f4bc9..add2c0247f 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -108,9 +108,7 @@ OPTIONS_SPEC= . git-sh-setup if [ "$(is_bare_repository)" = false ]; then - git diff-files --ignore-submodules --quiet && - git diff-index --cached --quiet HEAD -- || - die "Cannot rewrite branch(es) with a dirty working directory." + require_clean_work_tree 'rewrite branches' fi tempdir=.git-rewrite diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index 7aeb96952f..f612cb847a 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -117,7 +117,7 @@ case "${1:-.}${2:-.}${3:-.}" in # If we do not have enough common material, it is not # worth trying two-file merge using common subsections. - expr "$sz0" \< "$sz1" \* 2 >/dev/null || : >$orig + expr $sz0 \< $sz1 \* 2 >/dev/null || : >$orig ;; *) echo "Auto-merging $4" diff --git a/git-mergetool.sh b/git-mergetool.sh index b6d463f0d0..085e213a12 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -342,64 +342,42 @@ merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo fa last_status=0 rollup_status=0 -rerere=false - -files_to_merge() { - if test "$rerere" = true - then - git rerere remaining - else - git ls-files -u | sed -e 's/^[^ ]* //' | sort -u - fi -} - +files= if test $# -eq 0 ; then cd_to_toplevel if test -e "$GIT_DIR/MERGE_RR" then - rerere=true - fi - - files=$(files_to_merge) - if test -z "$files" ; then - echo "No files need merging" - exit 0 + files=$(git rerere remaining) + else + files=$(git ls-files -u | sed -e 's/^[^ ]* //' | sort -u) fi +else + files=$(git ls-files -u -- "$@" | sed -e 's/^[^ ]* //' | sort -u) +fi - # Save original stdin - exec 3<&0 +if test -z "$files" ; then + echo "No files need merging" + exit 0 +fi - printf "Merging:\n" - printf "$files\n" +printf "Merging:\n" +printf "$files\n" - files_to_merge | - while IFS= read i - do - if test $last_status -ne 0; then - prompt_after_failed_merge <&3 || exit 1 - fi - printf "\n" - merge_file "$i" <&3 - last_status=$? - if test $last_status -ne 0; then - rollup_status=1 - fi - done -else - while test $# -gt 0; do - if test $last_status -ne 0; then - prompt_after_failed_merge || exit 1 - fi - printf "\n" - merge_file "$1" - last_status=$? - if test $last_status -ne 0; then - rollup_status=1 - fi - shift - done -fi +IFS=' +' +for i in $files +do + if test $last_status -ne 0; then + prompt_after_failed_merge || exit 1 + fi + printf "\n" + merge_file "$i" + last_status=$? + if test $last_status -ne 0; then + rollup_status=1 + fi +done exit $rollup_status diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c6ba7c1551..94f36c254c 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -472,18 +472,24 @@ do_next () { git rev-parse --verify HEAD > "$state_dir"/stopped-sha ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution status=$? + # Run in subshell because require_clean_work_tree can die. + dirty=f + (require_clean_work_tree "rebase" 2>/dev/null) || dirty=t if test "$status" -ne 0 then warn "Execution failed: $rest" + test "$dirty" = f || + warn "and made changes to the index and/or the working tree" + warn "You can fix the problem, and then run" warn warn " git rebase --continue" warn exit "$status" - fi - # Run in subshell because require_clean_work_tree can die. - if ! (require_clean_work_tree "rebase") + elif test "$dirty" = t then + warn "Execution succeeded: $rest" + warn "but left changes to the index and/or the working tree" warn "Commit or stash your changes, and then run" warn warn " git rebase --continue" @@ -647,8 +653,24 @@ continue) then : Nothing to commit -- skip this else + if ! test -f "$author_script" + then + die "You have staged changes in your working tree. If these changes are meant to be +squashed into the previous commit, run: + + git commit --amend + +If they are meant to go into a new commit, run: + + git commit + +In both case, once you're done, continue with: + + git rebase --continue +" + fi . "$author_script" || - die "Cannot find the author identity" + die "Error trying to find the author identity to amend commit" current_head= if test -f "$amend" then diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 70a576a626..85d64b244d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1517,6 +1517,17 @@ sub esc_path { return $str; } +# Sanitize for use in XHTML + application/xml+xhtm (valid XML 1.0) +sub sanitize { + my $str = shift; + + return undef unless defined $str; + + $str = to_utf8($str); + $str =~ s|([[:cntrl:]])|($1 =~ /[\t\n\r]/ ? $1 : quot_cec($1))|eg; + return $str; +} + # Make control characters "printable", using character escape codes (CEC) sub quot_cec { my $cntrl = shift; @@ -6484,7 +6495,8 @@ sub git_blob { $nr++; $line = untabify($line); printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!, - $nr, esc_attr(href(-replay => 1)), $nr, $nr, $syntax ? to_utf8($line) : esc_html($line, -nbsp=>1); + $nr, esc_attr(href(-replay => 1)), $nr, $nr, + $syntax ? sanitize($line) : esc_html($line, -nbsp=>1); } } close $fd diff --git a/gitweb/static/js/javascript-detection.js b/gitweb/static/js/javascript-detection.js index 93dd2bdd91..fa2596f77c 100644 --- a/gitweb/static/js/javascript-detection.js +++ b/gitweb/static/js/javascript-detection.js @@ -16,7 +16,7 @@ * and other reasons to not add 'js=1' param at the end of link * @constant */ -var jsExceptionsRe = /[;?]js=[01]$/; +var jsExceptionsRe = /[;?]js=[01](#.*)?$/; /** * Add '?js=1' or ';js=1' to the end of every link in the document @@ -33,9 +33,9 @@ function fixLinks() { var allLinks = document.getElementsByTagName("a") || document.links; for (var i = 0, len = allLinks.length; i < len; i++) { var link = allLinks[i]; - if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/; - link.href += - (link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1'; + if (!jsExceptionsRe.test(link)) { + link.href = link.href.replace(/(#|$)/, + (link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1$1'); } } } diff --git a/http-push.c b/http-push.c index 376331a76f..44f814cda2 100644 --- a/http-push.c +++ b/http-push.c @@ -1606,10 +1606,10 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) strbuf_release(&buffer); } -static int verify_merge_base(unsigned char *head_sha1, unsigned char *branch_sha1) +static int verify_merge_base(unsigned char *head_sha1, struct ref *remote) { - struct commit *head = lookup_commit(head_sha1); - struct commit *branch = lookup_commit(branch_sha1); + struct commit *head = lookup_commit_or_die(head_sha1, "HEAD"); + struct commit *branch = lookup_commit_or_die(remote->old_sha1, remote->name); struct commit_list *merge_bases = get_merge_bases(head, branch, 1); return (merge_bases && !merge_bases->next && merge_bases->item == branch); @@ -1680,7 +1680,7 @@ static int delete_remote_branch(const char *pattern, int force) return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, sha1_to_hex(remote_ref->old_sha1)); /* Remote branch must be an ancestor of remote HEAD */ - if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) { + if (!verify_merge_base(head_sha1, remote_ref)) { return error("The branch '%s' is not an ancestor " "of your current HEAD.\n" "If you are sure you want to delete it," @@ -851,8 +851,13 @@ static int http_request(const char *url, void *result, int target, int options) init_curl_http_auth(slot->curl); ret = HTTP_REAUTH; } - } else + } else { + if (!curl_errorstr[0]) + strlcpy(curl_errorstr, + curl_easy_strerror(results.curl_result), + sizeof(curl_errorstr)); ret = HTTP_ERROR; + } } else { error("Unable to start HTTP request for %s", url); ret = HTTP_START_FAILED; @@ -908,7 +913,7 @@ int http_error(const char *url, int ret) { /* http_request has already handled HTTP_START_FAILED. */ if (ret != HTTP_START_FAILED) - error("%s while accessing %s\n", curl_errorstr, url); + error("%s while accessing %s", curl_errorstr, url); return ret; } diff --git a/merge-recursive.c b/merge-recursive.c index 3efc04e04f..c34a4f148b 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -390,7 +390,7 @@ static void record_df_conflict_files(struct merge_options *o, struct string_list *entries) { /* If there is a D/F conflict and the file for such a conflict - * currently exist in the working copy, we want to allow it to be + * currently exist in the working tree, we want to allow it to be * removed to make room for the corresponding directory if needed. * The files underneath the directories of such D/F conflicts will * be processed before the corresponding file involved in the D/F diff --git a/notes-merge.c b/notes-merge.c index e1aaf43b43..baaf31f4ae 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -680,7 +680,7 @@ int notes_merge_commit(struct notes_merge_options *o, * Finally store the new commit object SHA1 into 'result_sha1'. */ struct dir_struct dir; - const char *path = git_path(NOTES_MERGE_WORKTREE "/"); + char *path = xstrdup(git_path(NOTES_MERGE_WORKTREE "/")); int path_len = strlen(path), i; const char *msg = strstr(partial_commit->buffer, "\n\n"); @@ -720,6 +720,7 @@ int notes_merge_commit(struct notes_merge_options *o, result_sha1); OUTPUT(o, 4, "Finalized notes merge commit: %s", sha1_to_hex(result_sha1)); + free(path); return 0; } @@ -1,5 +1,6 @@ #include "cache.h" #include "quote.h" +#include "argv-array.h" int quote_path_fully = 1; @@ -120,7 +121,9 @@ char *sq_dequote(char *arg) return sq_dequote_step(arg, NULL); } -int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc) +static int sq_dequote_to_argv_internal(char *arg, + const char ***argv, int *nr, int *alloc, + struct argv_array *array) { char *next = arg; @@ -130,13 +133,27 @@ int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc) char *dequoted = sq_dequote_step(next, &next); if (!dequoted) return -1; - ALLOC_GROW(*argv, *nr + 1, *alloc); - (*argv)[(*nr)++] = dequoted; + if (argv) { + ALLOC_GROW(*argv, *nr + 1, *alloc); + (*argv)[(*nr)++] = dequoted; + } + if (array) + argv_array_push(array, dequoted); } while (next); return 0; } +int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc) +{ + return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL); +} + +int sq_dequote_to_argv_array(char *arg, struct argv_array *array) +{ + return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array); +} + /* 1 means: quote as octal * 0 means: quote as octal if (quote_path_fully) * -1 means: never quote @@ -40,12 +40,19 @@ extern char *sq_dequote(char *); /* * Same as the above, but can be used to unwrap many arguments in the - * same string separated by space. "next" is changed to point to the - * next argument that should be passed as first parameter. When there - * is no more argument to be dequoted, "next" is updated to point to NULL. + * same string separated by space. Like sq_quote, it works in place, + * modifying arg and appending pointers into it to argv. */ extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc); +/* + * Same as above, but store the unquoted strings in an argv_array. We will + * still modify arg in place, but unlike sq_dequote_to_argv, the argv_array + * will duplicate and take ownership of the strings. + */ +struct argv_array; +extern int sq_dequote_to_argv_array(char *arg, struct argv_array *); + extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp); extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq); extern void quote_two_c_style(struct strbuf *, const char *, const char *, int); diff --git a/read-cache.c b/read-cache.c index 01a0e25051..5790a91044 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1249,9 +1249,9 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries) { - long per_entry; - - per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry); + size_t fix_size_mem = offsetof(struct cache_entry, name); + size_t fix_size_dsk = offsetof(struct ondisk_cache_entry, name); + long per_entry = (fix_size_mem - fix_size_dsk + 7) & ~7; /* * Alignment can cause differences. This should be "alignof", but diff --git a/remote-curl.c b/remote-curl.c index b8cf45a7dd..0aa4bfed30 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -573,7 +573,14 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads) close(client.in); client.in = -1; - strbuf_read(&rpc->result, client.out, 0); + if (!err) { + strbuf_read(&rpc->result, client.out, 0); + } else { + char buf[4096]; + for (;;) + if (xread(client.out, buf, sizeof(buf)) <= 0) + break; + } close(client.out); client.out = -1; diff --git a/revision.c b/revision.c index c46cfaa3e4..3d2deedaf8 100644 --- a/revision.c +++ b/revision.c @@ -729,12 +729,16 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li * to filter the result of "A..B" further to the ones that can actually * reach A. */ -static struct commit_list *collect_bottom_commits(struct commit_list *list) +static struct commit_list *collect_bottom_commits(struct rev_info *revs) { - struct commit_list *elem, *bottom = NULL; - for (elem = list; elem; elem = elem->next) - if (elem->item->object.flags & UNINTERESTING) - commit_list_insert(elem->item, &bottom); + struct commit_list *bottom = NULL; + int i; + for (i = 0; i < revs->cmdline.nr; i++) { + struct rev_cmdline_entry *elem = &revs->cmdline.rev[i]; + if ((elem->flags & UNINTERESTING) && + elem->item->type == OBJ_COMMIT) + commit_list_insert((struct commit *)elem->item, &bottom); + } return bottom; } @@ -765,7 +769,7 @@ static int limit_list(struct rev_info *revs) struct commit_list *bottom = NULL; if (revs->ancestry_path) { - bottom = collect_bottom_commits(list); + bottom = collect_bottom_commits(revs); if (!bottom) die("--ancestry-path given but there are no bottom commits"); } @@ -822,6 +826,23 @@ static int limit_list(struct rev_info *revs) return 0; } +static void add_rev_cmdline(struct rev_info *revs, + struct object *item, + const char *name, + int whence, + unsigned flags) +{ + struct rev_cmdline_info *info = &revs->cmdline; + int nr = info->nr; + + ALLOC_GROW(info->rev, nr + 1, info->alloc); + info->rev[nr].item = item; + info->rev[nr].name = name; + info->rev[nr].whence = whence; + info->rev[nr].flags = flags; + info->nr++; +} + struct all_refs_cb { int all_flags; int warned_bad_reflog; @@ -834,6 +855,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, struct all_refs_cb *cb = cb_data; struct object *object = get_reference(cb->all_revs, path, sha1, cb->all_flags); + add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags); add_pending_object(cb->all_revs, object, path); return 0; } @@ -860,6 +882,7 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data) struct object *o = parse_object(sha1); if (o) { o->flags |= cb->all_flags; + /* ??? CMDLINEFLAGS ??? */ add_pending_object(cb->all_revs, o, ""); } else if (!cb->warned_bad_reflog) { @@ -896,12 +919,13 @@ static void handle_reflog(struct rev_info *revs, unsigned flags) for_each_reflog(handle_one_reflog, &cb); } -static int add_parents_only(struct rev_info *revs, const char *arg, int flags) +static int add_parents_only(struct rev_info *revs, const char *arg_, int flags) { unsigned char sha1[20]; struct object *it; struct commit *commit; struct commit_list *parents; + const char *arg = arg_; if (*arg == '^') { flags ^= UNINTERESTING; @@ -925,6 +949,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags) for (parents = commit->parents; parents; parents = parents->next) { it = &parents->item->object; it->flags |= flags; + add_rev_cmdline(revs, it, arg_, REV_CMD_PARENTS_ONLY, flags); add_pending_object(revs, it, arg); } return 1; @@ -986,10 +1011,12 @@ static void prepare_show_merge(struct rev_info *revs) const char **prune = NULL; int i, prune_num = 1; /* counting terminating NULL */ - if (get_sha1("HEAD", sha1) || !(head = lookup_commit(sha1))) + if (get_sha1("HEAD", sha1)) die("--merge without HEAD?"); - if (get_sha1("MERGE_HEAD", sha1) || !(other = lookup_commit(sha1))) + head = lookup_commit_or_die(sha1, "HEAD"); + if (get_sha1("MERGE_HEAD", sha1)) die("--merge without MERGE_HEAD?"); + other = lookup_commit_or_die(sha1, "MERGE_HEAD"); add_pending_object(revs, &head->object, "HEAD"); add_pending_object(revs, &other->object, "MERGE_HEAD"); bases = get_merge_bases(head, other, 1); @@ -1018,7 +1045,7 @@ static void prepare_show_merge(struct rev_info *revs) revs->limited = 1; } -int handle_revision_arg(const char *arg, struct rev_info *revs, +int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, int cant_be_filename) { @@ -1027,6 +1054,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, struct object *object; unsigned char sha1[20]; int local_flags; + const char *arg = arg_; dotdot = strstr(arg, ".."); if (dotdot) { @@ -1035,6 +1063,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, const char *this = arg; int symmetric = *next == '.'; unsigned int flags_exclude = flags ^ UNINTERESTING; + unsigned int a_flags; *dotdot = 0; next += symmetric; @@ -1069,10 +1098,15 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, add_pending_commit_list(revs, exclude, flags_exclude); free_commit_list(exclude); - a->object.flags |= flags | SYMMETRIC_LEFT; + a_flags = flags | SYMMETRIC_LEFT; } else - a->object.flags |= flags_exclude; + a_flags = flags_exclude; + a->object.flags |= a_flags; b->object.flags |= flags; + add_rev_cmdline(revs, &a->object, this, + REV_CMD_LEFT, a_flags); + add_rev_cmdline(revs, &b->object, next, + REV_CMD_RIGHT, flags); add_pending_object(revs, &a->object, this); add_pending_object(revs, &b->object, next); return 0; @@ -1103,6 +1137,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, if (!cant_be_filename) verify_non_filename(revs->prefix, arg); object = get_reference(revs, arg, sha1, flags ^ local_flags); + add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags); add_pending_object_with_mode(revs, object, arg, mode); return 0; } diff --git a/revision.h b/revision.h index 3d64adad18..93d31556cb 100644 --- a/revision.h +++ b/revision.h @@ -24,6 +24,23 @@ struct rev_info; struct log_info; struct string_list; +struct rev_cmdline_info { + unsigned int nr; + unsigned int alloc; + struct rev_cmdline_entry { + struct object *item; + const char *name; + enum { + REV_CMD_REF, + REV_CMD_PARENTS_ONLY, + REV_CMD_LEFT, + REV_CMD_RIGHT, + REV_CMD_REV + } whence; + unsigned flags; + } *rev; +}; + struct rev_info { /* Starting list */ struct commit_list *commits; @@ -32,6 +49,9 @@ struct rev_info { /* Parents of shown commits */ struct object_array boundary_commits; + /* The end-points specified by the end user */ + struct rev_cmdline_info cmdline; + /* Basic information */ const char *prefix; const char *def; diff --git a/run-command.c b/run-command.c index a2796c4cae..1c51043884 100644 --- a/run-command.c +++ b/run-command.c @@ -1,6 +1,7 @@ #include "cache.h" #include "run-command.h" #include "exec_cmd.h" +#include "argv-array.h" static inline void close_pair(int fd[2]) { @@ -605,26 +606,23 @@ int finish_async(struct async *async) int run_hook(const char *index_file, const char *name, ...) { struct child_process hook; - const char **argv = NULL, *env[2]; + struct argv_array argv = ARGV_ARRAY_INIT; + const char *p, *env[2]; char index[PATH_MAX]; va_list args; int ret; - size_t i = 0, alloc = 0; if (access(git_path("hooks/%s", name), X_OK) < 0) return 0; va_start(args, name); - ALLOC_GROW(argv, i + 1, alloc); - argv[i++] = git_path("hooks/%s", name); - while (argv[i-1]) { - ALLOC_GROW(argv, i + 1, alloc); - argv[i++] = va_arg(args, const char *); - } + argv_array_push(&argv, git_path("hooks/%s", name)); + while ((p = va_arg(args, const char *))) + argv_array_push(&argv, p); va_end(args); memset(&hook, 0, sizeof(hook)); - hook.argv = argv; + hook.argv = argv.argv; hook.no_stdin = 1; hook.stdout_to_stderr = 1; if (index_file) { @@ -635,6 +633,6 @@ int run_hook(const char *index_file, const char *name, ...) } ret = run_command(&hook); - free(argv); + argv_array_clear(&argv); return ret; } @@ -357,7 +357,6 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) { int ch; - strbuf_grow(sb, 0); if (feof(fp)) return EOF; diff --git a/submodule.c b/submodule.c index ad86534ba1..0b709bc291 100644 --- a/submodule.c +++ b/submodule.c @@ -8,12 +8,18 @@ #include "diffcore.h" #include "refs.h" #include "string-list.h" +#include "sha1-array.h" +#include "argv-array.h" static struct string_list config_name_for_path; static struct string_list config_fetch_recurse_submodules_for_name; static struct string_list config_ignore_for_name; static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; static struct string_list changed_submodule_paths; +static int initialized_fetch_ref_tips; +static struct sha1_array ref_tips_before_fetch; +static struct sha1_array ref_tips_after_fetch; + /* * The following flag is set if the .gitmodules file is unmerged. We then * disable recursion for all submodules where .git/config doesn't have a @@ -474,20 +480,46 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q, } } +static int add_sha1_to_array(const char *ref, const unsigned char *sha1, + int flags, void *data) +{ + sha1_array_append(data, sha1); + return 0; +} + void check_for_new_submodule_commits(unsigned char new_sha1[20]) { + if (!initialized_fetch_ref_tips) { + for_each_ref(add_sha1_to_array, &ref_tips_before_fetch); + initialized_fetch_ref_tips = 1; + } + + sha1_array_append(&ref_tips_after_fetch, new_sha1); +} + +static void add_sha1_to_argv(const unsigned char sha1[20], void *data) +{ + argv_array_push(data, sha1_to_hex(sha1)); +} + +static void calculate_changed_submodule_paths(void) +{ struct rev_info rev; struct commit *commit; - const char *argv[] = {NULL, NULL, "--not", "--all", NULL}; - int argc = ARRAY_SIZE(argv) - 1; + struct argv_array argv = ARGV_ARRAY_INIT; /* No need to check if there are no submodules configured */ if (!config_name_for_path.nr) return; init_revisions(&rev, NULL); - argv[1] = xstrdup(sha1_to_hex(new_sha1)); - setup_revisions(argc, argv, &rev, NULL); + argv_array_push(&argv, "--"); /* argv[0] program name */ + sha1_array_for_each_unique(&ref_tips_after_fetch, + add_sha1_to_argv, &argv); + argv_array_push(&argv, "--not"); + sha1_array_for_each_unique(&ref_tips_before_fetch, + add_sha1_to_argv, &argv); + setup_revisions(argv.argc, argv.argv, &rev, NULL); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); @@ -511,7 +543,11 @@ void check_for_new_submodule_commits(unsigned char new_sha1[20]) parent = parent->next; } } - free((char *)argv[1]); + + argv_array_clear(&argv); + sha1_array_clear(&ref_tips_before_fetch); + sha1_array_clear(&ref_tips_after_fetch); + initialized_fetch_ref_tips = 0; } int fetch_populated_submodules(int num_options, const char **options, @@ -545,6 +581,8 @@ int fetch_populated_submodules(int num_options, const char **options, cp.git_cmd = 1; cp.no_stdin = 1; + calculate_changed_submodule_paths(); + for (i = 0; i < active_nr; i++) { struct strbuf submodule_path = STRBUF_INIT; struct strbuf submodule_git_dir = STRBUF_INIT; diff --git a/t/t0006-date.sh b/t/t0006-date.sh index f87abb5a06..1d29810a7a 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -40,6 +40,12 @@ check_parse 2008-02 bad check_parse 2008-02-14 bad check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000' check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500' +check_parse '2008-02-14 20:30:45 -0015' '2008-02-14 20:30:45 -0015' +check_parse '2008-02-14 20:30:45 -5' '2008-02-14 20:30:45 +0000' +check_parse '2008-02-14 20:30:45 -5:' '2008-02-14 20:30:45 +0000' +check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500' +check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000' +check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500' check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5 check_approxidate() { diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 3e140c18f4..dffccf84f8 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -333,6 +333,12 @@ test_expect_success 'get-regexp variable with no value' \ 'git config --get-regexp novalue > output && cmp output expect' +echo 'novalue.variable true' > expect + +test_expect_success 'get-regexp --bool variable with no value' \ + 'git config --bool --get-regexp novalue > output && + cmp output expect' + echo 'emptyvalue.variable ' > expect test_expect_success 'get-regexp variable with empty value' \ diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh index b5d89a2250..2b962cfda7 100755 --- a/t/t1304-default-acl.sh +++ b/t/t1304-default-acl.sh @@ -25,6 +25,11 @@ else test_set_prereq SETFACL fi +if test -z "$LOGNAME" +then + LOGNAME=$USER +fi + check_perms_and_acl () { test -r "$1" && getfacl "$1" > actual && diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh new file mode 100755 index 0000000000..56090d2eba --- /dev/null +++ b/t/t2022-checkout-paths.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='checkout $tree -- $paths' +. ./test-lib.sh + +test_expect_success setup ' + mkdir dir && + >dir/master && + echo common >dir/common && + git add dir/master dir/common && + test_tick && git commit -m "master has dir/master" && + git checkout -b next && + git mv dir/master dir/next0 && + echo next >dir/next1 && + git add dir && + test_tick && git commit -m "next has dir/next but not dir/master" +' + +test_expect_success 'checking out paths out of a tree does not clobber unrelated paths' ' + git checkout next && + git reset --hard && + rm dir/next0 && + cat dir/common >expect.common && + echo modified >expect.next1 && + cat expect.next1 >dir/next1 && + echo untracked >expect.next2 && + cat expect.next2 >dir/next2 && + + git checkout master dir && + + test_cmp expect.common dir/common && + test_path_is_file dir/master && + git diff --exit-code master dir/master && + + test_path_is_missing dir/next0 && + test_cmp expect.next1 dir/next1 && + test_path_is_file dir/next2 && + test_must_fail git ls-files --error-unmatch dir/next2 && + test_cmp expect.next2 dir/next2 +' + +test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 8538813d1d..b981572d73 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -527,6 +527,20 @@ test_expect_success 'auto-amend only edited commits after "edit"' ' git rebase --abort ' +test_expect_success 'clean error after failed "exec"' ' + test_tick && + test_when_finished "git rebase --abort || :" && + ( + FAKE_LINES="1 exec_false" && + export FAKE_LINES && + test_must_fail git rebase -i HEAD^ + ) && + echo "edited again" > file7 && + git add file7 && + test_must_fail git rebase --continue 2>error && + grep "You have staged changes in your working tree." error +' + test_expect_success 'rebase a detached HEAD' ' grandparent=$(git rev-parse HEAD~2) && git checkout $(git rev-parse HEAD) && diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh new file mode 100755 index 0000000000..b7da95fac5 --- /dev/null +++ b/t/t4254-am-corrupt.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +test_description='git am with corrupt input' +. ./test-lib.sh + +# Note the missing "+++" line: +cat > bad-patch.diff <<'EOF' +From: A U Thor <au.thor@example.com> +diff --git a/f b/f +index 7898192..6178079 100644 +--- a/f +@@ -1 +1 @@ +-a ++b +EOF + +test_expect_success setup ' + test $? = 0 && + echo a > f && + git add f && + test_tick && + git commit -m initial +' + +# This used to fail before, too, but with a different diagnostic. +# fatal: unable to write file '(null)' mode 100644: Bad address +# Also, it had the unwanted side-effect of deleting f. +test_expect_success 'try to apply corrupted patch' ' + git am bad-patch.diff 2> actual + test $? = 1 +' + +cat > expected <<EOF +fatal: git diff header lacks filename information (line 4) +EOF + +test_expect_success 'compare diagnostic; ensure file is still here' ' + test $? = 0 && + test -f f && + test_cmp expected actual +' + +test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 0d0222ea2a..e8af615e6d 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -631,6 +631,37 @@ test_expect_success 'rename a remote' ' ' +test_expect_success 'rename does not update a non-default fetch refspec' ' + + git clone one four.one && + (cd four.one && + git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* && + git remote rename origin upstream && + test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" && + git rev-parse -q origin/master) + +' + +test_expect_success 'rename a remote with name part of fetch spec' ' + + git clone one four.two && + (cd four.two && + git remote rename origin remote && + git remote rename remote upstream && + test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*") + +' + +test_expect_success 'rename a remote with name prefix of other remote' ' + + git clone one four.three && + (cd four.three && + git remote add o git://example.com/repo.git && + git remote rename o upstream && + test "$(git rev-parse origin/master)" = "$(git rev-parse master)") + +' + cat > remotes_origin << EOF URL: $(pwd)/one Push: refs/heads/master:refs/heads/upstream diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh index 76410293b3..39b4cb0ecd 100755 --- a/t/t6019-rev-list-ancestry-path.sh +++ b/t/t6019-rev-list-ancestry-path.sh @@ -70,4 +70,42 @@ test_expect_success 'rev-list --ancestry-patch D..M -- M.t' ' test_cmp expect actual ' +# b---bc +# / \ / +# a X +# \ / \ +# c---cb +# +# All refnames prefixed with 'x' to avoid confusion with the tags +# generated by test_commit on case-insensitive systems. +test_expect_success 'setup criss-cross' ' + mkdir criss-cross && + (cd criss-cross && + git init && + test_commit A && + git checkout -b xb master && + test_commit B && + git checkout -b xc master && + test_commit C && + git checkout -b xbc xb -- && + git merge xc && + git checkout -b xcb xc -- && + git merge xb && + git checkout master) +' + +# no commits in bc descend from cb +test_expect_success 'criss-cross: rev-list --ancestry-path cb..bc' ' + (cd criss-cross && + git rev-list --ancestry-path xcb..xbc > actual && + test -z "$(cat actual)") +' + +# no commits in repository descend from cb +test_expect_success 'criss-cross: rev-list --ancestry-path --all ^cb' ' + (cd criss-cross && + git rev-list --ancestry-path --all ^xcb > actual && + test -z "$(cat actual)") +' + test_done diff --git a/t/t7511-status-index.sh b/t/t7511-status-index.sh new file mode 100755 index 0000000000..bca359dc1e --- /dev/null +++ b/t/t7511-status-index.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='git status with certain file name lengths' + +. ./test-lib.sh + +files="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z" + +check() { + len=$1 + prefix=$2 + + for i in $files + do + : >$prefix$i + done + + test_expect_success "status, filename length $len" " + git add $prefix* && + git status + " + rm $prefix* .git/index +} + +check 1 +check 2 p +check 3 pr +check 4 pre +check 5 pref +check 6 prefi +check 7 prefix +check 8 prefix- +check 9 prefix-p +check 10 prefix-pr +check 11 prefix-pre +check 12 prefix-pref +check 13 prefix-prefi +check 14 prefix-prefix +check 15 prefix-prefix- +check 16 prefix-prefix-p +check 17 prefix-prefix-pr +check 18 prefix-prefix-pre +check 19 prefix-prefix-pref +check 20 prefix-prefix-prefi +check 21 prefix-prefix-prefix +check 22 prefix-prefix-prefix- +check 23 prefix-prefix-prefix-p +check 24 prefix-prefix-prefix-pr + +test_done diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh index 72a8731d5e..aa74184c31 100755 --- a/t/t7607-merge-overwrite.sh +++ b/t/t7607-merge-overwrite.sh @@ -107,6 +107,7 @@ error: The following untracked working tree files would be overwritten by merge: sub sub2 Please move or remove them before you can merge. +Aborting EOF test_expect_success 'will not overwrite untracked file in leading path' ' diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh index c994836c53..0e4a682c64 100755 --- a/t/t7609-merge-co-error-msgs.sh +++ b/t/t7609-merge-co-error-msgs.sh @@ -32,6 +32,7 @@ error: The following untracked working tree files would be overwritten by merge: three two Please move or remove them before you can merge. +Aborting EOF test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' ' @@ -56,6 +57,7 @@ Please, commit your changes or stash them before you can merge. error: The following untracked working tree files would be overwritten by merge: five Please move or remove them before you can merge. +Aborting EOF test_expect_success 'untracked files or local changes ovewritten by merge' ' @@ -71,6 +73,7 @@ error: Your local changes to the following files would be overwritten by checkou rep/one rep/two Please, commit your changes or stash them before you can switch branches. +Aborting EOF test_expect_success 'cannot switch branches because of local changes' ' @@ -92,6 +95,7 @@ error: Your local changes to the following files would be overwritten by checkou rep/one rep/two Please, commit your changes or stash them before you can switch branches. +Aborting EOF test_expect_success 'not uptodate file porcelain checkout error' ' @@ -105,6 +109,7 @@ error: Updating the following directories would lose untracked files in it: rep rep2 +Aborting EOF test_expect_success 'not_uptodate_dir porcelain checkout error' ' diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index cbc08e3276..4aab2a75b8 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -16,6 +16,7 @@ Testing basic merge tool invocation' test_expect_success 'setup' ' git config rerere.enabled true && echo master >file1 && + echo master spaced >"spaced name" && echo master file11 >file11 && echo master file12 >file12 && echo master file13 >file13 && @@ -30,13 +31,14 @@ test_expect_success 'setup' ' git commit -m "Add foo" ) && git submodule add git://example.com/submod submod && - git add file1 file1[1-4] subdir/file3 .gitmodules submod && + git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod && git commit -m "add initial versions" && git checkout -b branch1 master && git submodule update -N && echo branch1 change >file1 && echo branch1 newfile >file2 && + echo branch1 spaced >"spaced name" && echo branch1 change file11 >file11 && echo branch1 change file13 >file13 && echo branch1 sub >subdir/file3 && @@ -47,7 +49,7 @@ test_expect_success 'setup' ' git commit -m "Add bar on branch1" && git checkout -b submod-branch1 ) && - git add file1 file11 file13 file2 subdir/file3 submod && + git add file1 "spaced name" file11 file13 file2 subdir/file3 submod && git rm file12 && git commit -m "branch1 changes" && @@ -55,6 +57,7 @@ test_expect_success 'setup' ' git submodule update -N && echo master updated >file1 && echo master new >file2 && + echo master updated spaced >"spaced name" && echo master updated file12 >file12 && echo master updated file14 >file14 && echo master new sub >subdir/file3 && @@ -65,7 +68,7 @@ test_expect_success 'setup' ' git commit -m "Add bar on master" && git checkout -b submod-master ) && - git add file1 file12 file14 file2 subdir/file3 submod && + git add file1 "spaced name" file12 file14 file2 subdir/file3 submod && git rm file11 && git commit -m "master updates" && @@ -78,8 +81,8 @@ 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 file1 >/dev/null 2>&1 ) && - ( yes "" | git mergetool file2 >/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 ) && ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && @@ -97,6 +100,7 @@ test_expect_success 'mergetool crlf' ' test_must_fail git merge master >/dev/null 2>&1 && ( 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 subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && @@ -126,7 +130,7 @@ test_expect_success 'mergetool on file in parent dir' ' ( cd subdir && ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) && - ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) && + ( yes "" | git mergetool ../file2 ../spaced\ name >/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 ) && @@ -180,6 +184,24 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' ' git reset --hard ' +test_expect_success 'mergetool takes partial path' ' + git config rerere.enabled false && + git checkout -b test12 branch1 && + git submodule update -N && + test_must_fail git merge master && + + #shouldnt need these lines + #( 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 ) && + #( yes "" | git mergetool file1 file2 >/dev/null 2>&1 ) && + + ( yes "" | git mergetool subdir ) && + + test "$(cat subdir/file3)" = "master new sub" && + git reset --hard +' + test_expect_success 'deleted vs modified submodule' ' git checkout -b test6 branch1 && git submodule update -N && @@ -189,7 +211,7 @@ test_expect_success 'deleted vs modified submodule' ' git checkout -b test6.a test6 && test_must_fail git merge master && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/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 && @@ -205,7 +227,7 @@ test_expect_success 'deleted vs modified submodule' ' git submodule update -N && test_must_fail git merge master && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool submod ) && test ! -e submod && @@ -218,7 +240,7 @@ test_expect_success 'deleted vs modified submodule' ' git submodule update -N && test_must_fail git merge test6 && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "r" | git mergetool submod ) && test ! -e submod && @@ -233,7 +255,7 @@ test_expect_success 'deleted vs modified submodule' ' git submodule update -N && test_must_fail git merge test6 && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/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" && @@ -256,7 +278,7 @@ test_expect_success 'file vs modified submodule' ' git checkout -b test7.a branch1 && test_must_fail git merge master && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/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 && @@ -271,7 +293,7 @@ test_expect_success 'file vs modified submodule' ' git checkout -b test7.b test7 && test_must_fail git merge master && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool submod ) && git submodule update -N && @@ -286,7 +308,7 @@ test_expect_success 'file vs modified submodule' ' git submodule update -N && test_must_fail git merge test7 && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && ( yes "r" | git mergetool submod ) && test -d submod.orig && @@ -301,7 +323,7 @@ test_expect_success 'file vs modified submodule' ' git submodule update -N && test_must_fail git merge test7 && test -n "$(git ls-files -u)" && - ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/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" && @@ -388,7 +410,7 @@ test_expect_success 'directory vs modified submodule' ' test "$(cat submod/file16)" = "not a submodule" && rm -rf submod.orig && - git reset --hard && + git reset --hard >/dev/null 2>&1 && test_must_fail git merge master && test -n "$(git ls-files -u)" && test ! -e submod.orig && @@ -400,7 +422,7 @@ test_expect_success 'directory vs modified submodule' ' ( cd submod && git clean -f && git reset --hard ) && git submodule update -N && test "$(cat submod/bar)" = "master submodule" && - git reset --hard && rm -rf submod-movedaside && + git reset --hard >/dev/null 2>&1 && rm -rf submod-movedaside && git checkout -b test11.c master && git submodule update -N && @@ -410,7 +432,7 @@ test_expect_success 'directory vs modified submodule' ' git submodule update -N && test "$(cat submod/bar)" = "master submodule" && - git reset --hard && + git reset --hard >/dev/null 2>&1 && git submodule update -N && test_must_fail git merge test11 && test -n "$(git ls-files -u)" && @@ -418,7 +440,7 @@ test_expect_success 'directory vs modified submodule' ' ( yes "r" | git mergetool submod ) && test "$(cat submod/file16)" = "not a submodule" && - git reset --hard master && + git reset --hard master >/dev/null 2>&1 && ( cd submod && git clean -f && git reset --hard ) && git submodule update -N ' diff --git a/t/t9159-git-svn-no-parent-mergeinfo.sh b/t/t9159-git-svn-no-parent-mergeinfo.sh index 85120b70db..69e4815781 100755 --- a/t/t9159-git-svn-no-parent-mergeinfo.sh +++ b/t/t9159-git-svn-no-parent-mergeinfo.sh @@ -2,6 +2,14 @@ test_description='git svn handling of root commits in merge ranges' . ./lib-git-svn.sh +svn_ver="$(svn --version --quiet)" +case $svn_ver in +0.* | 1.[0-4].*) + skip_all="skipping git-svn test - SVN too old ($svn_ver)" + test_done + ;; +esac + test_expect_success 'test handling of root commits in merge ranges' ' mkdir -p init/trunk init/branches init/tags && echo "r1" > init/trunk/file.txt && diff --git a/t/test-lib.sh b/t/test-lib.sh index d7dfc8b0b1..bdd9513b84 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -950,6 +950,8 @@ then do make_valgrind_symlink $file done + # special-case the mergetools loadables + make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools" OLDIFS=$IFS IFS=: for path in $PATH diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample index b187c4bb1f..18c4829765 100755 --- a/templates/hooks--pre-commit.sample +++ b/templates/hooks--pre-commit.sample @@ -18,6 +18,9 @@ fi # If you want to allow non-ascii filenames set this variable to true. allownonascii=$(git config hooks.allownonascii) +# Redirect output to stderr. +exec 1>&2 + # Cross platform projects tend to avoid non-ascii filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. @@ -25,8 +28,8 @@ if [ "$allownonascii" != "true" ] && # Note that the use of brackets around a tr range is ok here, (it's # even required, for portability to Solaris 10's /usr/bin/tr), since # the square bracket bytes happen to fall in the designated range. - test "$(git diff --cached --name-only --diff-filter=A -z $against | - LC_ALL=C tr -d '[ -~]\0')" + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 then echo "Error: Attempt to add a non-ascii file name." echo @@ -43,4 +46,5 @@ then exit 1 fi +# If there are whitespace errors, print the offending file names and fail. exec git diff-index --check --cached $against -- diff --git a/transport.c b/transport.c index fa279d531f..e1940615e2 100644 --- a/transport.c +++ b/transport.c @@ -432,7 +432,8 @@ static int fetch_refs_from_bundle(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct bundle_transport_data *data = transport->data; - return unbundle(&data->header, data->fd); + return unbundle(&data->header, data->fd, + transport->progress ? BUNDLE_VERBOSE : 0); } static int close_bundle(struct transport *transport) diff --git a/tree-walk.c b/tree-walk.c index 33f749e1e7..808bb55ba3 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -309,6 +309,18 @@ static void free_extended_entry(struct tree_desc_x *t) } } +static inline int prune_traversal(struct name_entry *e, + struct traverse_info *info, + struct strbuf *base, + int still_interesting) +{ + if (!info->pathspec || still_interesting == 2) + return 2; + if (still_interesting < 0) + return still_interesting; + return tree_entry_interesting(e, base, 0, info->pathspec); +} + int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) { int ret = 0; @@ -316,10 +328,18 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) struct name_entry *entry = xmalloc(n*sizeof(*entry)); int i; struct tree_desc_x *tx = xcalloc(n, sizeof(*tx)); + struct strbuf base = STRBUF_INIT; + int interesting = 1; for (i = 0; i < n; i++) tx[i].d = t[i]; + if (info->prev) { + strbuf_grow(&base, info->pathlen); + make_traverse_path(base.buf, info->prev, &info->name); + base.buf[info->pathlen-1] = '/'; + strbuf_setlen(&base, info->pathlen); + } for (;;) { unsigned long mask, dirmask; const char *first = NULL; @@ -376,16 +396,22 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) mask |= 1ul << i; if (S_ISDIR(entry[i].mode)) dirmask |= 1ul << i; + e = &entry[i]; } if (!mask) break; - ret = info->fn(n, mask, dirmask, entry, info); - if (ret < 0) { - error = ret; - if (!info->show_all_errors) - break; + interesting = prune_traversal(e, info, &base, interesting); + if (interesting < 0) + break; + if (interesting) { + ret = info->fn(n, mask, dirmask, entry, info); + if (ret < 0) { + error = ret; + if (!info->show_all_errors) + break; + } + mask &= ret; } - mask &= ret; ret = 0; for (i = 0; i < n; i++) if (mask & (1ul << i)) @@ -395,6 +421,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) for (i = 0; i < n; i++) free_extended_entry(tx + i); free(tx); + strbuf_release(&base); return error; } diff --git a/tree-walk.h b/tree-walk.h index 39524b7dba..0089581e1d 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -44,6 +44,7 @@ struct traverse_info { struct traverse_info *prev; struct name_entry name; int pathlen; + struct pathspec *pathspec; unsigned long conflicts; traverse_callback_t fn; diff --git a/unpack-trees.c b/unpack-trees.c index cc616c3f99..237aed8c76 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -159,7 +159,7 @@ static void display_error_msgs(struct unpack_trees_options *o) string_list_clear(rejects, 0); } if (something_displayed) - printf("Aborting\n"); + fprintf(stderr, "Aborting\n"); } /* @@ -444,6 +444,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, newinfo = *info; newinfo.prev = info; + newinfo.pathspec = info->pathspec; newinfo.name = *p; newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1; newinfo.conflicts |= df_conflicts; @@ -1040,6 +1041,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options info.fn = unpack_callback; info.data = o; info.show_all_errors = o->show_all_errors; + info.pathspec = o->pathspec; if (o->prefix) { /* diff --git a/unpack-trees.h b/unpack-trees.h index 7998948307..5e432f576e 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -52,6 +52,7 @@ struct unpack_trees_options { const char *prefix; int cache_bottom; struct dir_struct *dir; + struct pathspec *pathspec; merge_fn_t fn; const char *msgs[NB_UNPACK_TREES_ERROR_TYPES]; /* diff --git a/wt-status.c b/wt-status.c index 8836a527d0..70fdb76ff2 100644 --- a/wt-status.c +++ b/wt-status.c @@ -396,7 +396,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s) if (s->ignore_submodule_arg) { DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg); - } + } rev.diffopt.format_callback = wt_status_collect_changed_cb; rev.diffopt.format_callback_data = s; init_pathspec(&rev.prune_data, s->pathspec); diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c index 5a33d1a869..e419f4f726 100644 --- a/xdiff/xprepare.c +++ b/xdiff/xprepare.c @@ -383,7 +383,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) { * might be potentially discarded if they happear in a run of discardable. */ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) { - long i, nm, nreff; + long i, nm, nreff, mlim; xrecord_t **recs; xdlclass_t *rcrec; char *dis, *dis1, *dis2; @@ -396,16 +396,20 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd dis1 = dis; dis2 = dis1 + xdf1->nrec + 1; + if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT) + mlim = XDL_MAX_EQLIMIT; for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) { rcrec = cf->rcrecs[(*recs)->ha]; nm = rcrec ? rcrec->len2 : 0; - dis1[i] = (nm == 0) ? 0: 1; + dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1; } + if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT) + mlim = XDL_MAX_EQLIMIT; for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) { rcrec = cf->rcrecs[(*recs)->ha]; nm = rcrec ? rcrec->len1 : 0; - dis2[i] = (nm == 0) ? 0: 1; + dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1; } for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; |