Imported Upstream version 2.4.4 upstream/2.4.4
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:15:12 +0000 (15:15 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:15:12 +0000 (15:15 +0900)
28 files changed:
Documentation/RelNotes/2.4.4.txt [new file with mode: 0644]
Documentation/diff-options.txt
Documentation/git-commit.txt
Documentation/git-http-backend.txt
Documentation/git-submodule.txt
Documentation/git.txt
Documentation/glossary-content.txt
Documentation/pretty-formats.txt
GIT-VERSION-GEN
RelNotes
builtin/blame.c
builtin/clean.c
builtin/clone.c
builtin/log.c
builtin/rm.c
dir.c
git-compat-util.h
git-stash.sh
http-backend.c
merge-recursive.c
pack-bitmap.c
po/de.po
sha1_file.c
sha1_name.c
submodule.c
t/t0021-conversion.sh
t/t3903-stash.sh
t/t5551-http-fetch-smart.sh

diff --git a/Documentation/RelNotes/2.4.4.txt b/Documentation/RelNotes/2.4.4.txt
new file mode 100644 (file)
index 0000000..f1ccd00
--- /dev/null
@@ -0,0 +1,35 @@
+Git v2.4.4 Release Notes
+========================
+
+Fixes since v2.4.3
+------------------
+
+ * l10n updates for German.
+
+ * An earlier leakfix to bitmap testing code was incomplete.
+
+ * "git clean pathspec..." tried to lstat(2) and complain even for
+   paths outside the given pathspec.
+
+ * Communication between the HTTP server and http_backend process can
+   lead to a dead-lock when relaying a large ref negotiation request.
+   Diagnose the situation better, and mitigate it by reading such a
+   request first into core (to a reasonable limit).
+
+ * The clean/smudge interface did not work well when filtering an
+   empty contents (failed and then passed the empty input through).
+   It can be argued that a filter that produces anything but empty for
+   an empty input is nonsense, but if the user wants to do strange
+   things, then why not?
+
+ * Make "git stash something --help" error out, so that users can
+   safely say "git stash drop --help".
+
+ * Clarify that "log --raw" and "log --format=raw" are unrelated
+   concepts.
+
+ * Catch a programmer mistake to feed a pointer not an array to
+   ARRAY_SIZE() macro, by using a couple of GCC extensions.
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
index b7c3afe..3ad6404 100644 (file)
@@ -43,10 +43,19 @@ endif::git-format-patch[]
 
 ifndef::git-format-patch[]
 --raw::
-       Generate the raw format.
+ifndef::git-log[]
+       Generate the diff in raw format.
 ifdef::git-diff-core[]
        This is the default.
 endif::git-diff-core[]
+endif::git-log[]
+ifdef::git-log[]
+       For each commit, show a summary of changes using the raw diff
+       format. See the "RAW OUTPUT FORMAT" section of
+       linkgit:git-diff[1]. This is different from showing the log
+       itself in raw format, which you can achieve with
+       `--format=raw`.
+endif::git-log[]
 endif::git-format-patch[]
 
 ifndef::git-format-patch[]
index 617dea0..904dafa 100644 (file)
@@ -94,7 +94,7 @@ OPTIONS
 --reset-author::
        When used with -C/-c/--amend options, or when committing after a
        a conflicting cherry-pick, declare that the authorship of the
-       resulting commit now belongs of the committer. This also renews
+       resulting commit now belongs to the committer. This also renews
        the author timestamp.
 
 --short::
index 3ca18c4..9268fb6 100644 (file)
@@ -255,6 +255,15 @@ The GIT_HTTP_EXPORT_ALL environmental variable may be passed to
 'git-http-backend' to bypass the check for the "git-daemon-export-ok"
 file in each repository before allowing export of that repository.
 
+The `GIT_HTTP_MAX_REQUEST_BUFFER` environment variable (or the
+`http.maxRequestBuffer` config variable) may be set to change the
+largest ref negotiation request that git will handle during a fetch; any
+fetch requiring a larger buffer will not succeed.  This value should not
+normally need to be changed, but may be helpful if you are fetching from
+a repository with an extremely large number of refs.  The value can be
+specified with a unit (e.g., `100M` for 100 megabytes). The default is
+10 megabytes.
+
 The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and
 GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}',
 ensuring that any reflogs created by 'git-receive-pack' contain some
index 2c25916..f17687e 100644 (file)
@@ -25,22 +25,17 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Submodules allow foreign repositories to be embedded within
-a dedicated subdirectory of the source tree, always pointed
-at a particular commit.
+Inspects, updates and manages submodules.
 
-They are not to be confused with remotes, which are meant mainly
-for branches of the same project; submodules are meant for
-different projects you would like to make part of your source tree,
-while the history of the two projects still stays completely
-independent and you cannot modify the contents of the submodule
-from within the main project.
-If you want to merge the project histories and want to treat the
-aggregated whole as a single project from then on, you may want to
-add a remote for the other project and use the 'subtree' merge strategy,
-instead of treating the other project as a submodule. Directories
-that come from both projects can be cloned and checked out as a whole
-if you choose to go that route.
+A submodule allows you to keep another Git repository in a subdirectory
+of your repository. The other repository has its own history, which does not
+interfere with the history of the current repository. This can be used to
+have external dependencies such as third party libraries for example.
+
+When cloning or pulling a repository containing submodules however,
+these will not be checked out by default; the 'init' and 'update'
+subcommands will maintain submodules checked out and at
+appropriate revision in your working tree.
 
 Submodules are composed from a so-called `gitlink` tree entry
 in the main repository that refers to a particular commit object
@@ -51,19 +46,18 @@ describes the default URL the submodule shall be cloned from.
 The logical name can be used for overriding this URL within your
 local repository configuration (see 'submodule init').
 
-This command will manage the tree entries and contents of the
-gitmodules file for you, as well as inspect the status of your
-submodules and update them.
-When adding a new submodule to the tree, the 'add' subcommand
-is to be used.  However, when pulling a tree containing submodules,
-these will not be checked out by default;
-the 'init' and 'update' subcommands will maintain submodules
-checked out and at appropriate revision in your working tree.
-You can briefly inspect the up-to-date status of your submodules
-using the 'status' subcommand and get a detailed overview of the
-difference between the index and checkouts using the 'summary'
-subcommand.
-
+Submodules are not to be confused with remotes, which are other
+repositories of the same project; submodules are meant for
+different projects you would like to make part of your source tree,
+while the history of the two projects still stays completely
+independent and you cannot modify the contents of the submodule
+from within the main project.
+If you want to merge the project histories and want to treat the
+aggregated whole as a single project from then on, you may want to
+add a remote for the other project and use the 'subtree' merge strategy,
+instead of treating the other project as a submodule. Directories
+that come from both projects can be cloned and checked out as a whole
+if you choose to go that route.
 
 COMMANDS
 --------
index 12d9df1..338ebb7 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.4.3/git.html[documentation for release 2.4.3]
+* link:v2.4.4/git.html[documentation for release 2.4.4]
 
 * release notes for
+  link:RelNotes/2.4.4.txt[2.4.4],
   link:RelNotes/2.4.3.txt[2.4.3],
   link:RelNotes/2.4.2.txt[2.4.2],
   link:RelNotes/2.4.1.txt[2.4.1],
index bf383c2..ab18f4b 100644 (file)
@@ -469,6 +469,11 @@ The most notable example is `HEAD`.
        <<def_push,push>> to describe the mapping between remote
        <<def_ref,ref>> and local ref.
 
+[[def_remote]]remote repository::
+       A <<def_repository,repository>> which is used to track the same
+       project but resides somewhere else. To communicate with remotes,
+       see <<def_fetch,fetch>> or <<def_push,push>>.
+
 [[def_remote_tracking_branch]]remote-tracking branch::
        A <<def_ref,ref>> that is used to follow changes from another
        <<def_repository,repository>>. It typically looks like
@@ -515,6 +520,17 @@ The most notable example is `HEAD`.
        is created by giving the `--depth` option to linkgit:git-clone[1], and
        its history can be later deepened with linkgit:git-fetch[1].
 
+[[def_submodule]]submodule::
+       A <<def_repository,repository>> that holds the history of a
+       separate project inside another repository (the latter of
+       which is called <<def_superproject, superproject>>).
+
+[[def_superproject]]superproject::
+       A <<def_repository,repository>> that references repositories
+       of other projects in its working tree as <<def_submodule,submodules>>.
+       The superproject knows about the names of (but does not hold
+       copies of) commit objects of the contained submodules.
+
 [[def_symref]]symref::
        Symbolic reference: instead of containing the <<def_SHA1,SHA-1>>
        id itself, it is of the format 'ref: refs/some/thing' and when
index dcf7429..dc865cb 100644 (file)
@@ -79,7 +79,10 @@ stored in the commit object.  Notably, the SHA-1s are
 displayed in full, regardless of whether --abbrev or
 --no-abbrev are used, and 'parents' information show the
 true parent commits, without taking grafts or history
-simplification into account.
+simplification into account. Note that this format affects the way
+commits are displayed, but not the way the diff is shown e.g. with
+`git log --raw`. To get full object names in a raw diff format,
+use `--no-abbrev`.
 
 * 'format:<string>'
 +
index f94921c..9765504 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.4.3
+DEF_VER=v2.4.4
 
 LF='
 '
index 9810a70..9294b6f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.4.3.txt
\ No newline at end of file
+Documentation/RelNotes/2.4.4.txt
\ No newline at end of file
index 8d70623..b3e948e 100644 (file)
@@ -26,8 +26,9 @@
 #include "userdiff.h"
 #include "line-range.h"
 #include "line-log.h"
+#include "dir.h"
 
-static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] file");
+static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
 
 static const char *blame_opt_usage[] = {
        blame_usage,
@@ -2151,16 +2152,6 @@ static void sanity_check_refcnt(struct scoreboard *sb)
        }
 }
 
-/*
- * Used for the command line parsing; check if the path exists
- * in the working tree.
- */
-static int has_string_in_work_tree(const char *path)
-{
-       struct stat st;
-       return !lstat(path, &st);
-}
-
 static unsigned parse_score(const char *arg)
 {
        char *end;
@@ -2656,14 +2647,14 @@ parse_done:
                if (argc < 2)
                        usage_with_options(blame_opt_usage, options);
                path = add_prefix(prefix, argv[argc - 1]);
-               if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
+               if (argc == 3 && !file_exists(path)) { /* (2b) */
                        path = add_prefix(prefix, argv[1]);
                        argv[1] = argv[2];
                }
                argv[argc - 1] = "--";
 
                setup_work_tree();
-               if (!has_string_in_work_tree(path))
+               if (!file_exists(path))
                        die_errno("cannot stat path '%s'", path);
        }
 
index 98c103f..6dcb72e 100644 (file)
@@ -314,7 +314,6 @@ static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
 {
        struct string_list menu_list = STRING_LIST_INIT_DUP;
        struct strbuf menu = STRBUF_INIT;
-       struct strbuf buf = STRBUF_INIT;
        struct menu_item *menu_item;
        struct string_list_item *string_list_item;
        int i;
@@ -363,7 +362,6 @@ static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
        pretty_print_menus(&menu_list);
 
        strbuf_release(&menu);
-       strbuf_release(&buf);
        string_list_clear(&menu_list, 0);
 }
 
@@ -941,15 +939,15 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                if (!cache_name_is_other(ent->name, ent->len))
                        continue;
 
-               if (lstat(ent->name, &st))
-                       die_errno("Cannot lstat '%s'", ent->name);
-
                if (pathspec.nr)
                        matches = dir_path_match(ent, &pathspec, 0, NULL);
 
                if (pathspec.nr && !matches)
                        continue;
 
+               if (lstat(ent->name, &st))
+                       die_errno("Cannot lstat '%s'", ent->name);
+
                if (S_ISDIR(st.st_mode) && !remove_directories &&
                    matches != MATCHED_EXACTLY)
                        continue;
index 13030ee..4646922 100644 (file)
@@ -51,15 +51,6 @@ static struct string_list option_config;
 static struct string_list option_reference;
 static int option_dissociate;
 
-static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
-{
-       struct string_list *option_reference = opt->value;
-       if (!arg)
-               return -1;
-       string_list_append(option_reference, arg);
-       return 0;
-}
-
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
        OPT_BOOL(0, "progress", &option_progress,
@@ -83,8 +74,10 @@ static struct option builtin_clone_options[] = {
                    N_("initialize submodules in the clone")),
        OPT_STRING(0, "template", &option_template, N_("template-directory"),
                   N_("directory from which templates will be used")),
-       OPT_CALLBACK(0 , "reference", &option_reference, N_("repo"),
-                    N_("reference repository"), &opt_parse_reference),
+       OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"),
+                       N_("reference repository")),
+       OPT_BOOL(0, "dissociate", &option_dissociate,
+                N_("use --reference only while cloning")),
        OPT_STRING('o', "origin", &option_origin, N_("name"),
                   N_("use <name> instead of 'origin' to track upstream")),
        OPT_STRING('b', "branch", &option_branch, N_("branch"),
@@ -95,8 +88,6 @@ static struct option builtin_clone_options[] = {
                    N_("create a shallow clone of that depth")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
                    N_("clone only one branch, HEAD or --branch")),
-       OPT_BOOL(0, "dissociate", &option_dissociate,
-                N_("use --reference only while cloning")),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
                   N_("separate git dir from working tree")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
index dd8f3fc..4c4e6be 100644 (file)
@@ -38,7 +38,7 @@ static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
 static const char * const builtin_log_usage[] = {
-       N_("git log [<options>] [<revision range>] [[--] <path>...]"),
+       N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
        N_("git show [<options>] <object>..."),
        NULL
 };
index 3304bff..80b972f 100644 (file)
@@ -84,7 +84,6 @@ static int check_submodules_use_gitfiles(void)
                const char *name = list.entry[i].name;
                int pos;
                const struct cache_entry *ce;
-               struct stat st;
 
                pos = cache_name_pos(name, strlen(name));
                if (pos < 0) {
@@ -95,7 +94,7 @@ static int check_submodules_use_gitfiles(void)
                ce = active_cache[pos];
 
                if (!S_ISGITLINK(ce->ce_mode) ||
-                   (lstat(ce->name, &st) < 0) ||
+                   !file_exists(ce->name) ||
                    is_empty_dir(name))
                        continue;
 
diff --git a/dir.c b/dir.c
index 4183acc..56106c9 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -385,7 +385,6 @@ int report_path_error(const char *ps_matched,
        /*
         * Make sure all pathspec matched; otherwise it is an error.
         */
-       struct strbuf sb = STRBUF_INIT;
        int num, errors = 0;
        for (num = 0; num < pathspec->nr; num++) {
                int other, found_dup;
@@ -417,7 +416,6 @@ int report_path_error(const char *ps_matched,
                      pathspec->items[num].original);
                errors++;
        }
-       strbuf_release(&sb);
        return errors;
 }
 
index bc8fc8c..b45c75f 100644 (file)
@@ -3,6 +3,23 @@
 
 #define _FILE_OFFSET_BITS 64
 
+
+/* Derived from Linux "Features Test Macro" header
+ * Convenience macros to test the versions of gcc (or
+ * a compatible compiler).
+ * Use them like this:
+ *  #if GIT_GNUC_PREREQ (2,8)
+ *   ... code requiring gcc 2.8 or later ...
+ *  #endif
+*/
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define GIT_GNUC_PREREQ(maj, min) \
+       ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+ #define GIT_GNUC_PREREQ(maj, min) 0
+#endif
+
+
 #ifndef FLEX_ARRAY
 /*
  * See if our compiler is known to support flexible array members.
 #endif
 #endif
 
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+/*
+ * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler.  This can be used in an expression: its value is "0".
+ *
+ * Example:
+ *     #define foo_to_char(foo)                                        \
+ *              ((char *)(foo)                                         \
+ *               + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
+ */
+#define BUILD_ASSERT_OR_ZERO(cond) \
+       (sizeof(char [1 - 2*!(cond)]) - 1)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+# if GIT_GNUC_PREREQ(3, 1)
+ /* &arr[0] degrades to a pointer: a different type from an array */
+# define BARF_UNLESS_AN_ARRAY(arr)                                             \
+       BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(arr), \
+                                                          __typeof__(&(arr)[0])))
+# else
+#  define BARF_UNLESS_AN_ARRAY(arr) 0
+# endif
+#endif
+/*
+ * ARRAY_SIZE - get the number of elements in a visible array
+ *  <at> x: the array whose size you want.
+ *
+ * This does not work on pointers, or arrays declared as [], or
+ * function parameters.  With correct compiler support, such usage
+ * will cause a build error (see the build_assert_or_zero macro).
+ */
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]) + BARF_UNLESS_AN_ARRAY(x))
+
 #define bitsizeof(x)  (CHAR_BIT * sizeof(x))
 
 #define maximum_signed_value_of_type(a) \
index cc28368..0fddd54 100755 (executable)
@@ -219,6 +219,9 @@ save_stash () {
                -a|--all)
                        untracked=all
                        ;;
+               --help)
+                       show_help
+                       ;;
                --)
                        shift
                        break
@@ -301,11 +304,17 @@ list_stash () {
 }
 
 show_stash () {
+       ALLOW_UNKNOWN_FLAGS=t
        assert_stash_like "$@"
 
        git diff ${FLAGS:---stat} $b_commit $w_commit
 }
 
+show_help () {
+       exec git help stash
+       exit 1
+}
+
 #
 # Parses the remaining options looking for flags and
 # at most one revision defaulting to ${ref_stash}@{0}
@@ -332,13 +341,14 @@ show_stash () {
 #
 #   GIT_QUIET is set to t if -q is specified
 #   INDEX_OPTION is set to --index if --index is specified.
-#   FLAGS is set to the remaining flags
+#   FLAGS is set to the remaining flags (if allowed)
 #
 # dies if:
 #   * too many revisions specified
 #   * no revision is specified and there is no stash stack
 #   * a revision is specified which cannot be resolve to a SHA1
 #   * a non-existent stash reference is specified
+#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
 #
 
 parse_flags_and_rev()
@@ -371,7 +381,12 @@ parse_flags_and_rev()
                        --index)
                                INDEX_OPTION=--index
                        ;;
+                       --help)
+                               show_help
+                       ;;
                        -*)
+                               test "$ALLOW_UNKNOWN_FLAGS" = t ||
+                                       die "$(eval_gettext "unknown option: \$opt")"
                                FLAGS="${FLAGS}${FLAGS:+ }$opt"
                        ;;
                esac
index b6c0484..6bf139b 100644 (file)
@@ -13,18 +13,20 @@ static const char content_type[] = "Content-Type";
 static const char content_length[] = "Content-Length";
 static const char last_modified[] = "Last-Modified";
 static int getanyfile = 1;
+static unsigned long max_request_buffer = 10 * 1024 * 1024;
 
 static struct string_list *query_params;
 
 struct rpc_service {
        const char *name;
        const char *config_name;
+       unsigned buffer_input : 1;
        signed enabled : 2;
 };
 
 static struct rpc_service rpc_service[] = {
-       { "upload-pack", "uploadpack", 1 },
-       { "receive-pack", "receivepack", -1 },
+       { "upload-pack", "uploadpack", 1, 1 },
+       { "receive-pack", "receivepack", 0, -1 },
 };
 
 static struct string_list *get_parameters(void)
@@ -225,6 +227,7 @@ static void http_config(void)
        struct strbuf var = STRBUF_INIT;
 
        git_config_get_bool("http.getanyfile", &getanyfile);
+       git_config_get_ulong("http.maxrequestbuffer", &max_request_buffer);
 
        for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
                struct rpc_service *svc = &rpc_service[i];
@@ -266,9 +269,52 @@ static struct rpc_service *select_service(const char *name)
        return svc;
 }
 
-static void inflate_request(const char *prog_name, int out)
+/*
+ * This is basically strbuf_read(), except that if we
+ * hit max_request_buffer we die (we'd rather reject a
+ * maliciously large request than chew up infinite memory).
+ */
+static ssize_t read_request(int fd, unsigned char **out)
+{
+       size_t len = 0, alloc = 8192;
+       unsigned char *buf = xmalloc(alloc);
+
+       if (max_request_buffer < alloc)
+               max_request_buffer = alloc;
+
+       while (1) {
+               ssize_t cnt;
+
+               cnt = read_in_full(fd, buf + len, alloc - len);
+               if (cnt < 0) {
+                       free(buf);
+                       return -1;
+               }
+
+               /* partial read from read_in_full means we hit EOF */
+               len += cnt;
+               if (len < alloc) {
+                       *out = buf;
+                       return len;
+               }
+
+               /* otherwise, grow and try again (if we can) */
+               if (alloc == max_request_buffer)
+                       die("request was larger than our maximum size (%lu);"
+                           " try setting GIT_HTTP_MAX_REQUEST_BUFFER",
+                           max_request_buffer);
+
+               alloc = alloc_nr(alloc);
+               if (alloc > max_request_buffer)
+                       alloc = max_request_buffer;
+               REALLOC_ARRAY(buf, alloc);
+       }
+}
+
+static void inflate_request(const char *prog_name, int out, int buffer_input)
 {
        git_zstream stream;
+       unsigned char *full_request = NULL;
        unsigned char in_buf[8192];
        unsigned char out_buf[8192];
        unsigned long cnt = 0;
@@ -277,11 +323,21 @@ static void inflate_request(const char *prog_name, int out)
        git_inflate_init_gzip_only(&stream);
 
        while (1) {
-               ssize_t n = xread(0, in_buf, sizeof(in_buf));
+               ssize_t n;
+
+               if (buffer_input) {
+                       if (full_request)
+                               n = 0; /* nothing left to read */
+                       else
+                               n = read_request(0, &full_request);
+                       stream.next_in = full_request;
+               } else {
+                       n = xread(0, in_buf, sizeof(in_buf));
+                       stream.next_in = in_buf;
+               }
+
                if (n <= 0)
                        die("request ended in the middle of the gzip stream");
-
-               stream.next_in = in_buf;
                stream.avail_in = n;
 
                while (0 < stream.avail_in) {
@@ -307,9 +363,22 @@ static void inflate_request(const char *prog_name, int out)
 done:
        git_inflate_end(&stream);
        close(out);
+       free(full_request);
+}
+
+static void copy_request(const char *prog_name, int out)
+{
+       unsigned char *buf;
+       ssize_t n = read_request(0, &buf);
+       if (n < 0)
+               die_errno("error reading request body");
+       if (write_in_full(out, buf, n) != n)
+               die("%s aborted reading request", prog_name);
+       close(out);
+       free(buf);
 }
 
-static void run_service(const char **argv)
+static void run_service(const char **argv, int buffer_input)
 {
        const char *encoding = getenv("HTTP_CONTENT_ENCODING");
        const char *user = getenv("REMOTE_USER");
@@ -334,7 +403,7 @@ static void run_service(const char **argv)
                                 "GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
 
        cld.argv = argv;
-       if (gzipped_request)
+       if (buffer_input || gzipped_request)
                cld.in = -1;
        cld.git_cmd = 1;
        if (start_command(&cld))
@@ -342,7 +411,9 @@ static void run_service(const char **argv)
 
        close(1);
        if (gzipped_request)
-               inflate_request(argv[0], cld.in);
+               inflate_request(argv[0], cld.in, buffer_input);
+       else if (buffer_input)
+               copy_request(argv[0], cld.in);
        else
                close(0);
 
@@ -392,7 +463,7 @@ static void get_info_refs(char *arg)
                packet_flush(1);
 
                argv[0] = svc->name;
-               run_service(argv);
+               run_service(argv, 0);
 
        } else {
                select_getanyfile();
@@ -496,25 +567,28 @@ static void service_rpc(char *service_name)
        end_headers();
 
        argv[0] = svc->name;
-       run_service(argv);
+       run_service(argv, svc->buffer_input);
        strbuf_release(&buf);
 }
 
+static int dead;
 static NORETURN void die_webcgi(const char *err, va_list params)
 {
-       static int dead;
+       if (dead <= 1) {
+               vreportf("fatal: ", err, params);
 
-       if (!dead) {
-               dead = 1;
                http_status(500, "Internal Server Error");
                hdr_nocache();
                end_headers();
-
-               vreportf("fatal: ", err, params);
        }
        exit(0); /* we successfully reported a failure ;-) */
 }
 
+static int die_webcgi_recursing(void)
+{
+       return dead++ > 1;
+}
+
 static char* getdir(void)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -569,6 +643,7 @@ int main(int argc, char **argv)
 
        git_extract_argv0_path(argv[0]);
        set_die_routine(die_webcgi);
+       set_die_is_recursing_routine(die_webcgi_recursing);
 
        if (!method)
                die("No REQUEST_METHOD from server");
@@ -619,6 +694,9 @@ int main(int argc, char **argv)
                not_found("Repository not exported: '%s'", dir);
 
        http_config();
+       max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER",
+                                          max_request_buffer);
+
        cmd->imp(cmd_arg);
        return 0;
 }
index 1c9c30d..44d85be 100644 (file)
@@ -611,7 +611,6 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
 {
        struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
-       struct stat st;
        size_t base_len;
 
        strbuf_addf(&newpath, "%s~", path);
@@ -620,7 +619,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
        base_len = newpath.len;
        while (string_list_has_string(&o->current_file_set, newpath.buf) ||
               string_list_has_string(&o->current_directory_set, newpath.buf) ||
-              lstat(newpath.buf, &st) == 0) {
+              file_exists(newpath.buf)) {
                strbuf_setlen(&newpath, base_len);
                strbuf_addf(&newpath, "_%d", suffix++);
        }
index e5abb8a..2b3ff23 100644 (file)
@@ -985,7 +985,7 @@ void test_bitmap_walk(struct rev_info *revs)
        else
                fprintf(stderr, "Mismatch!\n");
 
-       free(result);
+       bitmap_free(result);
 }
 
 static int rebuild_bitmap(uint32_t *reposition,
index 2feaec1..7d603c2 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -2183,7 +2183,7 @@ msgstr "Nichts spezifiziert, nichts hinzugefügt.\n"
 #: builtin/add.c:358
 #, c-format
 msgid "Maybe you wanted to say 'git add .'?\n"
-msgstr "Wollten Sie vielleicht 'git add .' sagen?\n"
+msgstr "Meinten Sie vielleicht 'git add .'?\n"
 
 #: builtin/add.c:363 builtin/check-ignore.c:172 builtin/clean.c:920
 #: builtin/commit.c:335 builtin/mv.c:130 builtin/reset.c:235 builtin/rm.c:299
@@ -10478,8 +10478,8 @@ msgstr ""
 #: git-am.sh:142
 msgid "Using index info to reconstruct a base tree..."
 msgstr ""
-"Verwende Informationen aus der Staging-Area, um einen Basisverzeichnis "
-"nachzustellen"
+"Verwende Informationen aus der Staging-Area, um ein Basisverzeichnis "
+"nachzustellen ..."
 
 #: git-am.sh:157
 msgid ""
@@ -10491,11 +10491,11 @@ msgstr ""
 
 #: git-am.sh:166
 msgid "Falling back to patching base and 3-way merge..."
-msgstr "Falle zurück zum Patchen der Basis und des 3-Wege-Merges ..."
+msgstr "Falle zurück zum Patchen der Basis und zum 3-Wege-Merge ..."
 
 #: git-am.sh:182
 msgid "Failed to merge in the changes."
-msgstr "Merge der Änderungen fehlgeschlagen"
+msgstr "Merge der Änderungen fehlgeschlagen."
 
 #: git-am.sh:277
 msgid "Only one StGIT patch series can be applied at once"
index 001537c..ac0ca1a 100644 (file)
@@ -3202,7 +3202,7 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
        int ret;
 
        if (!size) {
-               ret = index_mem(sha1, NULL, size, type, path, flags);
+               ret = index_mem(sha1, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
index 6d10f05..6de8c87 100644 (file)
@@ -6,6 +6,7 @@
 #include "tree-walk.h"
 #include "refs.h"
 #include "remote.h"
+#include "dir.h"
 
 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 
@@ -1237,14 +1238,13 @@ static void diagnose_invalid_sha1_path(const char *prefix,
                                       const char *object_name,
                                       int object_name_len)
 {
-       struct stat st;
        unsigned char sha1[20];
        unsigned mode;
 
        if (!prefix)
                prefix = "";
 
-       if (!lstat(filename, &st))
+       if (file_exists(filename))
                die("Path '%s' exists on disk, but not in '%.*s'.",
                    filename, object_name_len, object_name);
        if (errno == ENOENT || errno == ENOTDIR) {
@@ -1271,7 +1271,6 @@ static void diagnose_invalid_index_path(int stage,
                                        const char *prefix,
                                        const char *filename)
 {
-       struct stat st;
        const struct cache_entry *ce;
        int pos;
        unsigned namelen = strlen(filename);
@@ -1314,7 +1313,7 @@ static void diagnose_invalid_index_path(int stage,
                            ce_stage(ce), filename);
        }
 
-       if (!lstat(filename, &st))
+       if (file_exists(filename))
                die("Path '%s' exists on disk, but not in the index.", filename);
        if (errno == ENOENT || errno == ENOTDIR)
                die("Path '%s' does not exist (neither on disk nor in the index).",
index c0e6c81..5a563ad 100644 (file)
@@ -891,7 +891,6 @@ int submodule_uses_gitfile(const char *path)
 
 int ok_to_remove_submodule(const char *path)
 {
-       struct stat st;
        ssize_t len;
        struct child_process cp = CHILD_PROCESS_INIT;
        const char *argv[] = {
@@ -904,7 +903,7 @@ int ok_to_remove_submodule(const char *path)
        struct strbuf buf = STRBUF_INIT;
        int ok_to_remove = 1;
 
-       if ((lstat(path, &st) < 0) || is_empty_dir(path))
+       if (!file_exists(path) || is_empty_dir(path))
                return 1;
 
        if (!submodule_uses_gitfile(path))
index ca7d2a6..e9f1626 100755 (executable)
@@ -216,4 +216,30 @@ test_expect_success EXPENSIVE 'filter large file' '
        ! test -s err
 '
 
+test_expect_success "filter: clean empty file" '
+       git config filter.in-repo-header.clean  "echo cleaned && cat" &&
+       git config filter.in-repo-header.smudge "sed 1d" &&
+
+       echo "empty-in-worktree    filter=in-repo-header" >>.gitattributes &&
+       >empty-in-worktree &&
+
+       echo cleaned >expected &&
+       git add empty-in-worktree &&
+       git show :empty-in-worktree >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success "filter: smudge empty file" '
+       git config filter.empty-in-repo.clean "cat >/dev/null" &&
+       git config filter.empty-in-repo.smudge "echo smudged && cat" &&
+
+       echo "empty-in-repo filter=empty-in-repo" >>.gitattributes &&
+       echo dead data walking >empty-in-repo &&
+       git add empty-in-repo &&
+
+       echo smudged >expected &&
+       git checkout-index --prefix=filtered- empty-in-repo &&
+       test_cmp expected filtered-empty-in-repo
+'
+
 test_done
index 0746eee..7396ca9 100755 (executable)
@@ -100,6 +100,10 @@ test_expect_success 'unstashing in a subdirectory' '
        )
 '
 
+test_expect_success 'stash drop complains of extra options' '
+       test_must_fail git stash drop --foo
+'
+
 test_expect_success 'drop top stash' '
        git reset --hard &&
        git stash list > stashlist1 &&
index 2b29311..58207d8 100755 (executable)
@@ -218,27 +218,35 @@ test_expect_success 'transfer.hiderefs works over smart-http' '
        git -C hidden.git rev-parse --verify b
 '
 
-test_expect_success 'create 2,000 tags in the repo' '
-       (
-       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       for i in $(test_seq 2000)
+# create an arbitrary number of tags, numbered from tag-$1 to tag-$2
+create_tags () {
+       rm -f marks &&
+       for i in $(test_seq "$1" "$2")
        do
-               echo "commit refs/heads/too-many-refs"
-               echo "mark :$i"
-               echo "committer git <git@example.com> $i +0000"
-               echo "data 0"
-               echo "M 644 inline bla.txt"
-               echo "data 4"
-               echo "bla"
+               # don't use here-doc, because it requires a process
+               # per loop iteration
+               echo "commit refs/heads/too-many-refs-$1" &&
+               echo "mark :$i" &&
+               echo "committer git <git@example.com> $i +0000" &&
+               echo "data 0" &&
+               echo "M 644 inline bla.txt" &&
+               echo "data 4" &&
+               echo "bla" &&
                # make every commit dangling by always
                # rewinding the branch after each commit
-               echo "reset refs/heads/too-many-refs"
-               echo "from :1"
+               echo "reset refs/heads/too-many-refs-$1" &&
+               echo "from :$1"
        done | git fast-import --export-marks=marks &&
 
        # now assign tags to all the dangling commits we created above
        tag=$(perl -e "print \"bla\" x 30") &&
        sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+}
+
+test_expect_success 'create 2,000 tags in the repo' '
+       (
+               cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+               create_tags 1 2000
        )
 '
 
@@ -259,5 +267,20 @@ test_expect_success 'large fetch-pack requests can be split across POSTs' '
        test_line_count = 2 posts
 '
 
+test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
+       (
+               cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+               create_tags 2001 50000
+       ) &&
+       git -C too-many-refs fetch -q --tags &&
+       (
+               cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+               create_tags 50001 100000
+       ) &&
+       git -C too-many-refs fetch -q --tags &&
+       git -C too-many-refs for-each-ref refs/tags >tags &&
+       test_line_count = 100000 tags
+'
+
 stop_httpd
 test_done