Merge branch 'ss/builtin-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Fri, 10 Jan 2014 18:33:47 +0000 (10:33 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 10 Jan 2014 18:33:48 +0000 (10:33 -0800)
"git help $cmd" unnecessarily enumerated potential command names
from the filesystem, even when $cmd is known to be a built-in.

Ideas for further optimization, primarily by killing the use of
is_in_cmdlist(), were suggested in the discussion, but they can
come as follow-ups on top of this series.

* ss/builtin-cleanup:
builtin/help.c: speed up is_git_command() by checking for builtin commands first
builtin/help.c: call load_command_list() only when it is needed
git.c: consistently use the term "builtin" instead of "internal command"

62 files changed:
Documentation/Makefile
Documentation/diff-config.txt
Documentation/diff-options.txt
Documentation/git-cat-file.txt
Documentation/git-filter-branch.txt
Documentation/git-replace.txt
Documentation/gitmodules.txt
Documentation/glossary-content.txt
Documentation/user-manual.txt
abspath.c
builtin/add.c
builtin/blame.c
builtin/cat-file.c
builtin/fetch.c
builtin/gc.c
builtin/merge-base.c
builtin/pack-objects.c
builtin/prune-packed.c
builtin/prune.c
builtin/replace.c
cache.h
commit.c
commit.h
config.mak.uname
contrib/completion/git-completion.bash
contrib/completion/git-completion.zsh
csum-file.c
csum-file.h
daemon.c
diff.c
diffcore-order.c
dir.c
git-compat-util.h
log-tree.c
pack-write.c
pathspec.c
pathspec.h
po/de.po
prompt.c
replace_object.c
sha1_file.c
streaming.c
t/annotate-tests.sh
t/lib-httpd.sh
t/lib-httpd/passwd
t/t0000-basic.sh
t/t1006-cat-file.sh
t/t3700-add.sh
t/t4056-diff-order.sh [new file with mode: 0755]
t/t4205-log-pretty-formats.sh
t/t5510-fetch.sh
t/t5540-http-push.sh
t/t5541-http-push.sh
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh
t/t6010-merge-base.sh
t/t6050-replace.sh
t/t6101-rev-parse-parents.sh
t/t6132-pathspec-exclude.sh [new file with mode: 0755]
t/test-lib.sh
tree-walk.c
unpack-trees.c
index 91a12c7e51e7869f4b64385aeffc1762c3ddfa38..36c58fc6460fb6e6fd2eefdc0d17384a4a65d851 100644 (file)
@@ -324,7 +324,7 @@ manpage-base-url.xsl: manpage-base-url.xsl.in
 
 user-manual.xml: user-manual.txt user-manual.conf
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
-       $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book -o $@+ $< && \
+       $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d article -o $@+ $< && \
        mv $@+ $@
 
 technical/api-index.txt: technical/api-index-skel.txt \
index 223b9310df0990accdb0d43b096bd52856f56bef..f07b4513ed76978e3bb7ef02ebf31d504f7098ff 100644 (file)
@@ -98,6 +98,11 @@ diff.mnemonicprefix::
 diff.noprefix::
        If set, 'git diff' does not show any source or destination prefix.
 
+diff.orderfile::
+       File indicating how to order files within a diff, using
+       one shell glob pattern per line.
+       Can be overridden by the '-O' option to linkgit:git-diff[1].
+
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
        detection; equivalent to the 'git diff' option '-l'.
index bbed2cd79c6ebbb95e3b66b5f1ccaa3762d9dfce..9b37b2a10b94449b73aeceadae175d52ee081e4e 100644 (file)
@@ -432,6 +432,9 @@ endif::git-format-patch[]
 -O<orderfile>::
        Output the patch in the order specified in the
        <orderfile>, which has one shell glob pattern per line.
+       This overrides the `diff.orderfile` configuration variable
+       (see linkgit:git-config[1]).  To cancel `diff.orderfile`,
+       use `-O/dev/null`.
 
 ifndef::git-format-patch[]
 -R::
index 322f5ed3155887ef69876bd439a12c4641007c9e..f6a16f4300b15669a518faa3d8a63d7586233258 100644 (file)
@@ -109,6 +109,11 @@ newline. The available atoms are:
        The size, in bytes, that the object takes up on disk. See the
        note about on-disk sizes in the `CAVEATS` section below.
 
+`deltabase`::
+       If the object is stored as a delta on-disk, this expands to the
+       40-hex sha1 of the delta base object. Otherwise, expands to the
+       null sha1 (40 zeroes). See `CAVEATS` below.
+
 `rest`::
        If this atom is used in the output string, input lines are split
        at the first whitespace boundary. All characters before that
@@ -152,10 +157,11 @@ should be taken in drawing conclusions about which refs or objects are
 responsible for disk usage. The size of a packed non-delta object may be
 much larger than the size of objects which delta against it, but the
 choice of which object is the base and which is the delta is arbitrary
-and is subject to change during a repack. Note also that multiple copies
-of an object may be present in the object database; in this case, it is
-undefined which copy's size will be reported.
+and is subject to change during a repack.
 
+Note also that multiple copies of an object may be present in the object
+database; in this case, it is undefined which copy's size or delta base
+will be reported.
 
 GIT
 ---
index e4c8e82660ddf9aace717e269a50ffa9adf4405e..2eba6271704adb6179a1c31d9d6974fbe70b66f4 100644 (file)
@@ -393,7 +393,7 @@ git filter-branch --index-filter \
 Checklist for Shrinking a Repository
 ------------------------------------
 
-git-filter-branch is often used to get rid of a subset of files,
+git-filter-branch can be used to get rid of a subset of files,
 usually with some combination of `--index-filter` and
 `--subdirectory-filter`.  People expect the resulting repository to
 be smaller than the original, but you need a few more steps to
@@ -429,6 +429,37 @@ warned.
   (or if your git-gc is not new enough to support arguments to
   `--prune`, use `git repack -ad; git prune` instead).
 
+Notes
+-----
+
+git-filter-branch allows you to make complex shell-scripted rewrites
+of your Git history, but you probably don't need this flexibility if
+you're simply _removing unwanted data_ like large files or passwords.
+For those operations you may want to consider
+link:http://rtyley.github.io/bfg-repo-cleaner/[The BFG Repo-Cleaner],
+a JVM-based alternative to git-filter-branch, typically at least
+10-50x faster for those use-cases, and with quite different
+characteristics:
+
+* Any particular version of a file is cleaned exactly _once_. The BFG,
+  unlike git-filter-branch, does not give you the opportunity to
+  handle a file differently based on where or when it was committed
+  within your history. This constraint gives the core performance
+  benefit of The BFG, and is well-suited to the task of cleansing bad
+  data - you don't care _where_ the bad data is, you just want it
+  _gone_.
+
+* By default The BFG takes full advantage of multi-core machines,
+  cleansing commit file-trees in parallel. git-filter-branch cleans
+  commits sequentially (ie in a single-threaded manner), though it
+  _is_ possible to write filters that include their own parallellism,
+  in the scripts executed against each commit.
+
+* The link:http://rtyley.github.io/bfg-repo-cleaner/#examples[command options]
+  are much more restrictive than git-filter branch, and dedicated just
+  to the tasks of removing unwanted data- e.g:
+  `--strip-blobs-bigger-than 1M`.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index f373ab48d4d575d847408e333b5bfb747d1a75a2..0a02f70657776bc5d862add34f466b4fe98f6e35 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git replace' [-f] <object> <replacement>
 'git replace' -d <object>...
-'git replace' -l [<pattern>]
+'git replace' [--format=<format>] [-l [<pattern>]]
 
 DESCRIPTION
 -----------
@@ -70,6 +70,23 @@ OPTIONS
        Typing "git replace" without arguments, also lists all replace
        refs.
 
+--format=<format>::
+       When listing, use the specified <format>, which can be one of
+       'short', 'medium' and 'long'. When omitted, the format
+       defaults to 'short'.
+
+FORMATS
+-------
+
+The following format are available:
+
+* 'short':
+       <replaced sha1>
+* 'medium':
+       <replaced sha1> -> <replacement sha1>
+* 'long':
+       <replaced sha1> (<replaced type>) -> <replacement sha1> (<replacement type>)
+
 CREATING REPLACEMENT OBJECTS
 ----------------------------
 
index f7be93f6317c7170af642c43d107a6617eb33a18..347a9f76ee809c3691dc6580b0fba3ebde0b0e3c 100644 (file)
@@ -35,6 +35,8 @@ submodule.<name>.url::
        linkgit:git-clone[1] or (if it begins with ./ or ../) a location
        relative to the superproject's origin repository.
 
+In addition, there are a number of optional keys:
+
 submodule.<name>.update::
        Defines what to do when the submodule is updated by the superproject.
        If 'checkout' (the default), the new commit specified in the
index aa1c8880dd491a5840492d610c44fec6e07684ca..378306f58162fdfee900b48b8bb6ad8171cbfca3 100644 (file)
@@ -323,24 +323,26 @@ including Documentation/chapter_1/figure_1.jpg.
 A pathspec that begins with a colon `:` has special meaning.  In the
 short form, the leading colon `:` is followed by zero or more "magic
 signature" letters (which optionally is terminated by another colon `:`),
-and the remainder is the pattern to match against the path. The optional
-colon that terminates the "magic signature" can be omitted if the pattern
-begins with a character that cannot be a "magic signature" and is not a
-colon.
+and the remainder is the pattern to match against the path.
+The "magic signature" consists of ASCII symbols that are neither
+alphanumeric, glob, regex special charaters nor colon.
+The optional colon that terminates the "magic signature" can be
+omitted if the pattern begins with a character that does not belong to
+"magic signature" symbol set and is not a colon.
 +
 In the long form, the leading colon `:` is followed by a open
 parenthesis `(`, a comma-separated list of zero or more "magic words",
 and a close parentheses `)`, and the remainder is the pattern to match
 against the path.
 +
-The "magic signature" consists of an ASCII symbol that is not
-alphanumeric.
+A pathspec with only a colon means "there is no pathspec". This form
+should not be combined with other pathspec.
 +
 --
-top `/`;;
-       The magic word `top` (mnemonic: `/`) makes the pattern match
-       from the root of the working tree, even when you are running
-       the command from inside a subdirectory.
+top;;
+       The magic word `top` (magic signature: `/`) makes the pattern
+       match from the root of the working tree, even when you are
+       running the command from inside a subdirectory.
 
 literal;;
        Wildcards in the pattern such as `*` or `?` are treated
@@ -377,14 +379,12 @@ full pathname may have special meaning:
  - Other consecutive asterisks are considered invalid.
 +
 Glob magic is incompatible with literal magic.
+
+exclude;;
+       After a path matches any non-exclude pathspec, it will be run
+       through all exclude pathspec (magic signature: `!`). If it
+       matches, the path is ignored.
 --
-+
-Currently only the slash `/` is recognized as the "magic signature",
-but it is envisioned that we will support more types of magic in later
-versions of Git.
-+
-A pathspec with only a colon means "there is no pathspec". This form
-should not be combined with other pathspec.
 
 [[def_parent]]parent::
        A <<def_commit_object,commit object>> contains a (possibly empty) list
index cbb01a1ea2e05c7adb1be3d5fde1261ef9ff71e5..248dcabd50fb1fb844eaf27090b2f7f45bb0e661 100644 (file)
@@ -1,5 +1,5 @@
 Git User Manual
-_______________
+===============
 
 Git is a fast distributed revision control system.
 
index 8b3385a77749108b29d0a25042a13a174ddbc2e4..ca33558a91c5259a793fc56b571fa683e16b134f 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -215,23 +215,25 @@ const char *absolute_path(const char *path)
  */
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
-       static char path[PATH_MAX];
+       static struct strbuf path = STRBUF_INIT;
 #ifndef GIT_WINDOWS_NATIVE
        if (!pfx_len || is_absolute_path(arg))
                return arg;
-       memcpy(path, pfx, pfx_len);
-       strcpy(path + pfx_len, arg);
+       strbuf_reset(&path);
+       strbuf_add(&path, pfx, pfx_len);
+       strbuf_addstr(&path, arg);
 #else
        char *p;
        /* don't add prefix to absolute paths, but still replace '\' by '/' */
+       strbuf_reset(&path);
        if (is_absolute_path(arg))
                pfx_len = 0;
        else if (pfx_len)
-               memcpy(path, pfx, pfx_len);
-       strcpy(path + pfx_len, arg);
-       for (p = path + pfx_len; *p; p++)
+               strbuf_add(&path, pfx, pfx_len);
+       strbuf_addstr(&path, arg);
+       for (p = path.buf + pfx_len; *p; p++)
                if (*p == '\\')
                        *p = '/';
 #endif
-       return path;
+       return path.buf;
 }
index 226f758869358444ad1c39e5e0a05c961fb76859..2a2722fa10aa981695b21984e7c425c35b4f18d0 100644 (file)
@@ -540,11 +540,14 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                               PATHSPEC_FROMTOP |
                               PATHSPEC_LITERAL |
                               PATHSPEC_GLOB |
-                              PATHSPEC_ICASE);
+                              PATHSPEC_ICASE |
+                              PATHSPEC_EXCLUDE);
 
                for (i = 0; i < pathspec.nr; i++) {
                        const char *path = pathspec.items[i].match;
-                       if (!seen[i] &&
+                       if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
+                               continue;
+                       if (!seen[i] && path[0] &&
                            ((pathspec.items[i].magic &
                              (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
                             !file_exists(path))) {
index 4916eb2bd26bed670aa3662e790abad9b336ebe3..e44a6bb30a5a5299c6d57150dd2f23454b25891a 100644 (file)
@@ -1803,17 +1803,17 @@ static int prepare_lines(struct scoreboard *sb)
 static int read_ancestry(const char *graft_file)
 {
        FILE *fp = fopen(graft_file, "r");
-       char buf[1024];
+       struct strbuf buf = STRBUF_INIT;
        if (!fp)
                return -1;
-       while (fgets(buf, sizeof(buf), fp)) {
+       while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               int len = strlen(buf);
-               struct commit_graft *graft = read_graft_line(buf, len);
+               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
                if (graft)
                        register_commit_graft(graft, 0);
        }
        fclose(fp);
+       strbuf_release(&buf);
        return 0;
 }
 
index f8288c830cad215af63dd6d575c9bd4bf7aeaf6c..d5a93e0e911ccbe4560a03ed78436bfb174e0f68 100644 (file)
@@ -118,6 +118,7 @@ struct expand_data {
        unsigned long size;
        unsigned long disk_size;
        const char *rest;
+       unsigned char delta_base_sha1[20];
 
        /*
         * If mark_query is true, we do not expand anything, but rather
@@ -174,6 +175,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
                        data->split_on_whitespace = 1;
                else if (data->rest)
                        strbuf_addstr(sb, data->rest);
+       } else if (is_atom("deltabase", atom, len)) {
+               if (data->mark_query)
+                       data->info.delta_base_sha1 = data->delta_base_sha1;
+               else
+                       strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1));
        } else
                die("unknown format element: %.*s", len, atom);
 }
@@ -241,7 +247,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
                return 0;
        }
 
-       if (sha1_object_info_extended(data->sha1, &data->info) < 0) {
+       if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
                printf("%s missing\n", obj_name);
                fflush(stdout);
                return 0;
index 1e7d617f4671c43f704afe912edac5f5dc6dc316..09825c84d731014e075a6f77791f0681b7c4efb0 100644 (file)
@@ -44,6 +44,7 @@ static struct transport *gtransport;
 static struct transport *gsecondary;
 static const char *submodule_prefix = "";
 static const char *recurse_submodules_default;
+static int shown_url = 0;
 
 static int option_parse_recurse_submodules(const struct option *opt,
                                   const char *arg, int unset)
@@ -535,7 +536,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 {
        FILE *fp;
        struct commit *commit;
-       int url_len, i, shown_url = 0, rc = 0;
+       int url_len, i, rc = 0;
        struct strbuf note = STRBUF_INIT;
        const char *what, *kind;
        struct ref *rm;
@@ -708,17 +709,36 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
        return ret;
 }
 
-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
+               const char *raw_url)
 {
-       int result = 0;
+       int url_len, i, result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
+       char *url;
        const char *dangling_msg = dry_run
                ? _("   (%s will become dangling)")
                : _("   (%s has become dangling)");
 
+       if (raw_url)
+               url = transport_anonymize_url(raw_url);
+       else
+               url = xstrdup("foreign");
+
+       url_len = strlen(url);
+       for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+               ;
+
+       url_len = i + 1;
+       if (4 < i && !strncmp(".git", url + i - 3, 4))
+               url_len = i - 3;
+
        for (ref = stale_refs; ref; ref = ref->next) {
                if (!dry_run)
                        result |= delete_ref(ref->name, NULL, 0);
+               if (verbosity >= 0 && !shown_url) {
+                       fprintf(stderr, _("From %.*s\n"), url_len, url);
+                       shown_url = 1;
+               }
                if (verbosity >= 0) {
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
                                TRANSPORT_SUMMARY(_("[deleted]")),
@@ -726,6 +746,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
+       free(url);
        free_refs(stale_refs);
        return result;
 }
@@ -842,11 +863,6 @@ static int do_fetch(struct transport *transport,
 
        if (tags == TAGS_DEFAULT && autotags)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
-       if (fetch_refs(transport, ref_map)) {
-               free_refs(ref_map);
-               retcode = 1;
-               goto cleanup;
-       }
        if (prune) {
                /*
                 * We only prune based on refspecs specified
@@ -854,13 +870,19 @@ static int do_fetch(struct transport *transport,
                 * don't care whether --tags was specified.
                 */
                if (ref_count) {
-                       prune_refs(refs, ref_count, ref_map);
+                       prune_refs(refs, ref_count, ref_map, transport->url);
                } else {
                        prune_refs(transport->remote->fetch,
                                   transport->remote->fetch_refspec_nr,
-                                  ref_map);
+                                  ref_map,
+                                  transport->url);
                }
        }
+       if (fetch_refs(transport, ref_map)) {
+               free_refs(ref_map);
+               retcode = 1;
+               goto cleanup;
+       }
        free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
index c14190f840b0427820ebf69b209d86f0a21c62b7..25f2237c08f3b8fbc8f3554f3e22bf7e59e74b1c 100644 (file)
@@ -222,7 +222,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
                        time(NULL) - st.st_mtime <= 12 * 3600 &&
                        fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 &&
                        /* be gentle to concurrent "gc" on remote hosts */
-                       (strcmp(locking_host, my_host) || !kill(pid, 0));
+                       (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
                if (fp != NULL)
                        fclose(fp);
                if (should_exit) {
index a2923235e1024e554b0dc937c8222282d1855065..0ecde8da30615996f54563014e4774b67812c531 100644 (file)
@@ -48,19 +48,36 @@ static struct commit *get_commit_reference(const char *arg)
        return r;
 }
 
-static int handle_octopus(int count, const char **args, int reduce, int show_all)
+static int handle_independent(int count, const char **args)
 {
        struct commit_list *revs = NULL;
        struct commit_list *result;
        int i;
 
-       if (reduce)
-               show_all = 1;
+       for (i = count - 1; i >= 0; i--)
+               commit_list_insert(get_commit_reference(args[i]), &revs);
+
+       result = reduce_heads(revs);
+       if (!result)
+               return 1;
+
+       while (result) {
+               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+               result = result->next;
+       }
+       return 0;
+}
+
+static int handle_octopus(int count, const char **args, int show_all)
+{
+       struct commit_list *revs = NULL;
+       struct commit_list *result;
+       int i;
 
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
+       result = reduce_heads(get_octopus_merge_bases(revs));
 
        if (!result)
                return 1;
@@ -220,8 +237,11 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        if (cmdmode == 'r' && show_all)
                die("--independent cannot be used with --all");
 
-       if (cmdmode == 'r' || cmdmode == 'o')
-               return handle_octopus(argc, argv, cmdmode == 'r', show_all);
+       if (cmdmode == 'o')
+               return handle_octopus(argc, argv, show_all);
+
+       if (cmdmode == 'r')
+               return handle_independent(argc, argv);
 
        if (cmdmode == 'f') {
                if (argc < 1 || 2 < argc)
index dfb4d84caa506cee874fd972bc0e9df1a4121362..541667f1026d7ba62be8e029c31138d5661f65c7 100644 (file)
@@ -737,8 +737,6 @@ static void write_pack_file(void)
                        f = create_tmp_packfile(&pack_tmp_name);
 
                offset = write_pack_header(f, nr_remaining);
-               if (!offset)
-                       die_errno("unable to write pack header");
                nr_written = 0;
                for (; i < nr_objects; i++) {
                        struct object_entry *e = write_order[i];
index fa6ce42f44f82d34299f2dc0109be4d831cb3d48..fcf5fb6129c05156c7fd7bbb22ac380dbd0b3245 100644 (file)
@@ -10,58 +10,62 @@ static const char * const prune_packed_usage[] = {
 
 static struct progress *progress;
 
-static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
+static void prune_dir(int i, DIR *dir, struct strbuf *pathname, int opts)
 {
        struct dirent *de;
        char hex[40];
+       int top_len = pathname->len;
 
        sprintf(hex, "%02x", i);
        while ((de = readdir(dir)) != NULL) {
                unsigned char sha1[20];
                if (strlen(de->d_name) != 38)
                        continue;
-               memcpy(hex+2, de->d_name, 38);
+               memcpy(hex + 2, de->d_name, 38);
                if (get_sha1_hex(hex, sha1))
                        continue;
                if (!has_sha1_pack(sha1))
                        continue;
-               memcpy(pathname + len, de->d_name, 38);
+
+               strbuf_add(pathname, de->d_name, 38);
                if (opts & PRUNE_PACKED_DRY_RUN)
-                       printf("rm -f %s\n", pathname);
+                       printf("rm -f %s\n", pathname->buf);
                else
-                       unlink_or_warn(pathname);
+                       unlink_or_warn(pathname->buf);
                display_progress(progress, i + 1);
+               strbuf_setlen(pathname, top_len);
        }
 }
 
 void prune_packed_objects(int opts)
 {
        int i;
-       static char pathname[PATH_MAX];
        const char *dir = get_object_directory();
-       int len = strlen(dir);
+       struct strbuf pathname = STRBUF_INIT;
+       int top_len;
 
+       strbuf_addstr(&pathname, dir);
        if (opts & PRUNE_PACKED_VERBOSE)
                progress = start_progress_delay("Removing duplicate objects",
                        256, 95, 2);
 
-       if (len > PATH_MAX - 42)
-               die("impossible object directory");
-       memcpy(pathname, dir, len);
-       if (len && pathname[len-1] != '/')
-               pathname[len++] = '/';
+       if (pathname.len && pathname.buf[pathname.len - 1] != '/')
+               strbuf_addch(&pathname, '/');
+
+       top_len = pathname.len;
        for (i = 0; i < 256; i++) {
                DIR *d;
 
                display_progress(progress, i + 1);
-               sprintf(pathname + len, "%02x/", i);
-               d = opendir(pathname);
+               strbuf_setlen(&pathname, top_len);
+               strbuf_addf(&pathname, "%02x/", i);
+               d = opendir(pathname.buf);
                if (!d)
                        continue;
-               prune_dir(i, d, pathname, len + 3, opts);
+               prune_dir(i, d, &pathname, opts);
                closedir(d);
-               pathname[len + 2] = '\0';
-               rmdir(pathname);
+               strbuf_setlen(&pathname, top_len + 2);
+               rmdir(pathname.buf);
        }
        stop_progress(&progress);
 }
index a85ef54d5aeb679067c7f3ad428439886a162362..beb0dc942c1263ecf4aa5ab87b10520f481ebe43 100644 (file)
@@ -17,9 +17,8 @@ static int verbose;
 static unsigned long expire;
 static int show_progress = -1;
 
-static int prune_tmp_object(const char *path, const char *filename)
+static int prune_tmp_file(const char *fullpath)
 {
-       const char *fullpath = mkpath("%s/%s", path, filename);
        struct stat st;
        if (lstat(fullpath, &st))
                return error("Could not stat '%s'", fullpath);
@@ -32,9 +31,8 @@ static int prune_tmp_object(const char *path, const char *filename)
        return 0;
 }
 
-static int prune_object(char *path, const char *filename, const unsigned char *sha1)
+static int prune_object(const char *fullpath, const unsigned char *sha1)
 {
-       const char *fullpath = mkpath("%s/%s", path, filename);
        struct stat st;
        if (lstat(fullpath, &st))
                return error("Could not stat '%s'", fullpath);
@@ -50,9 +48,10 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
        return 0;
 }
 
-static int prune_dir(int i, char *path)
+static int prune_dir(int i, struct strbuf *path)
 {
-       DIR *dir = opendir(path);
+       size_t baselen = path->len;
+       DIR *dir = opendir(path->buf);
        struct dirent *de;
 
        if (!dir)
@@ -77,28 +76,39 @@ static int prune_dir(int i, char *path)
                        if (lookup_object(sha1))
                                continue;
 
-                       prune_object(path, de->d_name, sha1);
+                       strbuf_addf(path, "/%s", de->d_name);
+                       prune_object(path->buf, sha1);
+                       strbuf_setlen(path, baselen);
                        continue;
                }
                if (starts_with(de->d_name, "tmp_obj_")) {
-                       prune_tmp_object(path, de->d_name);
+                       strbuf_addf(path, "/%s", de->d_name);
+                       prune_tmp_file(path->buf);
+                       strbuf_setlen(path, baselen);
                        continue;
                }
-               fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+               fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name);
        }
        closedir(dir);
        if (!show_only)
-               rmdir(path);
+               rmdir(path->buf);
        return 0;
 }
 
 static void prune_object_dir(const char *path)
 {
+       struct strbuf buf = STRBUF_INIT;
+       size_t baselen;
        int i;
+
+       strbuf_addstr(&buf, path);
+       strbuf_addch(&buf, '/');
+       baselen = buf.len;
+
        for (i = 0; i < 256; i++) {
-               static char dir[4096];
-               sprintf(dir, "%s/%02x", path, i);
-               prune_dir(i, dir);
+               strbuf_addf(&buf, "%02x", i);
+               prune_dir(i, &buf);
+               strbuf_setlen(&buf, baselen);
        }
 }
 
@@ -120,7 +130,7 @@ static void remove_temporary_files(const char *path)
        }
        while ((de = readdir(dir)) != NULL)
                if (starts_with(de->d_name, "tmp_"))
-                       prune_tmp_object(path, de->d_name);
+                       prune_tmp_file(mkpath("%s/%s", path, de->d_name));
        closedir(dir);
 }
 
index b1bd3ef9946761b146a38d5977a7378175cda047..2336325ce330f5268800a7bc949c066ce028852c 100644 (file)
 static const char * const git_replace_usage[] = {
        N_("git replace [-f] <object> <replacement>"),
        N_("git replace -d <object>..."),
-       N_("git replace -l [<pattern>]"),
+       N_("git replace [--format=<format>] [-l [<pattern>]]"),
        NULL
 };
 
+enum replace_format {
+      REPLACE_FORMAT_SHORT,
+      REPLACE_FORMAT_MEDIUM,
+      REPLACE_FORMAT_LONG
+};
+
+struct show_data {
+       const char *pattern;
+       enum replace_format format;
+};
+
 static int show_reference(const char *refname, const unsigned char *sha1,
                          int flag, void *cb_data)
 {
-       const char *pattern = cb_data;
+       struct show_data *data = cb_data;
 
-       if (!fnmatch(pattern, refname, 0))
-               printf("%s\n", refname);
+       if (!fnmatch(data->pattern, refname, 0)) {
+               if (data->format == REPLACE_FORMAT_SHORT)
+                       printf("%s\n", refname);
+               else if (data->format == REPLACE_FORMAT_MEDIUM)
+                       printf("%s -> %s\n", refname, sha1_to_hex(sha1));
+               else { /* data->format == REPLACE_FORMAT_LONG */
+                       unsigned char object[20];
+                       enum object_type obj_type, repl_type;
+
+                       if (get_sha1(refname, object))
+                               return error("Failed to resolve '%s' as a valid ref.", refname);
+
+                       obj_type = sha1_object_info(object, NULL);
+                       repl_type = sha1_object_info(sha1, NULL);
+
+                       printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
+                              sha1_to_hex(sha1), typename(repl_type));
+               }
+       }
 
        return 0;
 }
 
-static int list_replace_refs(const char *pattern)
+static int list_replace_refs(const char *pattern, const char *format)
 {
+       struct show_data data;
+
        if (pattern == NULL)
                pattern = "*";
+       data.pattern = pattern;
 
-       for_each_replace_ref(show_reference, (void *) pattern);
+       if (format == NULL || *format == '\0' || !strcmp(format, "short"))
+               data.format = REPLACE_FORMAT_SHORT;
+       else if (!strcmp(format, "medium"))
+               data.format = REPLACE_FORMAT_MEDIUM;
+       else if (!strcmp(format, "long"))
+               data.format = REPLACE_FORMAT_LONG;
+       else
+               die("invalid replace format '%s'\n"
+                   "valid formats are 'short', 'medium' and 'long'\n",
+                   format);
+
+       for_each_replace_ref(show_reference, (void *) &data);
 
        return 0;
 }
@@ -127,19 +169,27 @@ static int replace_object(const char *object_ref, const char *replace_ref,
 int cmd_replace(int argc, const char **argv, const char *prefix)
 {
        int list = 0, delete = 0, force = 0;
+       const char *format = NULL;
        struct option options[] = {
                OPT_BOOL('l', "list", &list, N_("list replace refs")),
                OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
                OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+               OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
        };
 
+       read_replace_refs = 0;
+
        argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
 
        if (list && delete)
                usage_msg_opt("-l and -d cannot be used together",
                              git_replace_usage, options);
 
+       if (format && delete)
+               usage_msg_opt("--format and -d cannot be used together",
+                             git_replace_usage, options);
+
        if (force && (list || delete))
                usage_msg_opt("-f cannot be used with -d or -l",
                              git_replace_usage, options);
@@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                if (argc != 2)
                        usage_msg_opt("bad number of arguments",
                                      git_replace_usage, options);
+               if (format)
+                       usage_msg_opt("--format cannot be used when not listing",
+                                     git_replace_usage, options);
                return replace_object(argv[0], argv[1], force);
        }
 
@@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                usage_msg_opt("-f needs some arguments",
                              git_replace_usage, options);
 
-       return list_replace_refs(argv[0]);
+       return list_replace_refs(argv[0], format);
 }
diff --git a/cache.h b/cache.h
index ce377e1354a4d0fd719b50edb32124110956467f..83a27269b89871b166286550ae9cb22c85dc7d10 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -760,11 +760,11 @@ int daemon_avoid_alias(const char *path);
 int offset_1st_component(const char *path);
 
 /* object replacement */
-#define READ_SHA1_FILE_REPLACE 1
+#define LOOKUP_REPLACE_OBJECT 1
 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
 static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 {
-       return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE);
+       return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT);
 }
 extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
 static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
@@ -773,6 +773,12 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh
                return sha1;
        return do_lookup_replace_object(sha1);
 }
+static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag)
+{
+       if (!(flag & LOOKUP_REPLACE_OBJECT))
+               return sha1;
+       return lookup_replace_object(sha1);
+}
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
@@ -1074,6 +1080,7 @@ struct object_info {
        enum object_type *typep;
        unsigned long *sizep;
        unsigned long *disk_sizep;
+       unsigned char *delta_base_sha1;
 
        /* Response */
        enum {
@@ -1098,7 +1105,7 @@ struct object_info {
                } packed;
        } u;
 };
-extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
+extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 
 /* Dumb servers support */
 extern int update_server_info(int);
index 5df1df73512ebb90f558ce6f2f7e899e75f03ff8..6bf4fe00d4b0365eca75fd9988852650660b6884 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -196,19 +196,19 @@ struct commit_graft *read_graft_line(char *buf, int len)
 static int read_graft_file(const char *graft_file)
 {
        FILE *fp = fopen(graft_file, "r");
-       char buf[1024];
+       struct strbuf buf = STRBUF_INIT;
        if (!fp)
                return -1;
-       while (fgets(buf, sizeof(buf), fp)) {
+       while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               int len = strlen(buf);
-               struct commit_graft *graft = read_graft_line(buf, len);
+               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
                if (!graft)
                        continue;
                if (register_commit_graft(graft, 1))
-                       error("duplicate graft data: %s", buf);
+                       error("duplicate graft data: %s", buf.buf);
        }
        fclose(fp);
+       strbuf_release(&buf);
        return 0;
 }
 
@@ -841,26 +841,26 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
 struct commit_list *get_octopus_merge_bases(struct commit_list *in)
 {
        struct commit_list *i, *j, *k, *ret = NULL;
-       struct commit_list **pptr = &ret;
 
-       for (i = in; i; i = i->next) {
-               if (!ret)
-                       pptr = &commit_list_insert(i->item, pptr)->next;
-               else {
-                       struct commit_list *new = NULL, *end = NULL;
-
-                       for (j = ret; j; j = j->next) {
-                               struct commit_list *bases;
-                               bases = get_merge_bases(i->item, j->item, 1);
-                               if (!new)
-                                       new = bases;
-                               else
-                                       end->next = bases;
-                               for (k = bases; k; k = k->next)
-                                       end = k;
-                       }
-                       ret = new;
+       if (!in)
+               return ret;
+
+       commit_list_insert(in->item, &ret);
+
+       for (i = in->next; i; i = i->next) {
+               struct commit_list *new = NULL, *end = NULL;
+
+               for (j = ret; j; j = j->next) {
+                       struct commit_list *bases;
+                       bases = get_merge_bases(i->item, j->item, 1);
+                       if (!new)
+                               new = bases;
+                       else
+                               end->next = bases;
+                       for (k = bases; k; k = k->next)
+                               end = k;
                }
+               ret = new;
        }
        return ret;
 }
@@ -1356,7 +1356,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
        }
 }
 
-int commit_tree(const struct strbuf *msg, unsigned char *tree,
+int commit_tree(const struct strbuf *msg, const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
 {
@@ -1485,7 +1485,7 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
index 934af889f7d8f1aa9fba0e9ff900b1f1132a44a7..f8a451d86498cc1f6a9c60256f68a885262ecdd9 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -232,11 +232,11 @@ struct commit_extra_header {
 extern void append_merge_tag_headers(struct commit_list *parents,
                                     struct commit_extra_header ***tail);
 
-extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
                       struct commit_list *parents, unsigned char *ret,
                       const char *author, const char *sign_commit);
 
-extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
                                struct commit_list *parents, unsigned char *ret,
                                const char *author, const char *sign_commit,
                                struct commit_extra_header *);
index 82d549e48ba796469a27ef41ad0c9ad2a8d96cbc..7d31fad241761e41de655de6f42ddfb8c87ea4f2 100644 (file)
@@ -198,6 +198,13 @@ ifeq ($(uname_S),OpenBSD)
        BASIC_LDFLAGS += -L/usr/local/lib
        HAVE_PATHS_H = YesPlease
 endif
+ifeq ($(uname_S),MirBSD)
+       NO_STRCASESTR = YesPlease
+       NO_MEMMEM = YesPlease
+       USE_ST_TIMESPEC = YesPlease
+       NEEDS_LIBICONV = YesPlease
+       HAVE_PATHS_H = YesPlease
+endif
 ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
                NEEDS_LIBICONV = YesPlease
index 51c2dd4dec3b65dc6ab8f051580fc03ce3027d5b..4fe5ce31bd99e5c5307761c947d4661241ec3c24 100644 (file)
@@ -178,9 +178,9 @@ _get_comp_words_by_ref ()
 }
 fi
 
-__gitcompadd ()
+__gitcompappend ()
 {
-       local i=0
+       local i=${#COMPREPLY[@]}
        for x in $1; do
                if [[ "$x" == "$3"* ]]; then
                        COMPREPLY[i++]="$2$x$4"
@@ -188,6 +188,12 @@ __gitcompadd ()
        done
 }
 
+__gitcompadd ()
+{
+       COMPREPLY=()
+       __gitcompappend "$@"
+}
+
 # Generates completion reply, appending a space to possible completion words,
 # if necessary.
 # It accepts 1 to 4 arguments:
@@ -218,6 +224,14 @@ __gitcomp ()
        esac
 }
 
+# Variation of __gitcomp_nl () that appends to the existing list of
+# completion candidates, COMPREPLY.
+__gitcomp_nl_append ()
+{
+       local IFS=$'\n'
+       __gitcompappend "$1" "${2-}" "${3-$cur}" "${4- }"
+}
+
 # Generates completion reply from newline-separated possible completion words
 # by appending a space to all of them.
 # It accepts 1 to 4 arguments:
@@ -229,8 +243,8 @@ __gitcomp ()
 #    appended.
 __gitcomp_nl ()
 {
-       local IFS=$'\n'
-       __gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }"
+       COMPREPLY=()
+       __gitcomp_nl_append "$@"
 }
 
 # Generates completion reply with compgen from newline-separated possible
@@ -1827,6 +1841,7 @@ _git_config ()
        branch.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+               __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
                return
                ;;
        guitool.*.*)
@@ -1869,6 +1884,7 @@ _git_config ()
        remote.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
+               __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
                return
                ;;
        url.*.*)
index fac5e711ebbbd074366d89848165e0f9dbc36c36..6b779685722916f8032dce9b3b46220eb6a19414 100644 (file)
@@ -30,10 +30,10 @@ if [ -z "$script" ]; then
        local -a locations
        local e
        locations=(
+               $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
                '/etc/bash_completion.d/git' # fedora, old debian
                '/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian
                '/usr/share/bash-completion/git' # gentoo
-               $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
                )
        for e in $locations; do
                test -f $e && script="$e" && break
@@ -76,6 +76,14 @@ __gitcomp_nl ()
        compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
 }
 
+__gitcomp_nl_append ()
+{
+       emulate -L zsh
+
+       local IFS=$'\n'
+       compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
+}
+
 __gitcomp_file ()
 {
        emulate -L zsh
index 465971c7f303e29bf9501e6968da1ea71b5af76a..b00b215031174a0f8ee3cd3d51c662d3cacc6b00 100644 (file)
@@ -86,7 +86,7 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
        return fd;
 }
 
-int sha1write(struct sha1file *f, const void *buf, unsigned int count)
+void sha1write(struct sha1file *f, const void *buf, unsigned int count)
 {
        while (count) {
                unsigned offset = f->offset;
@@ -116,7 +116,6 @@ int sha1write(struct sha1file *f, const void *buf, unsigned int count)
                }
                f->offset = offset;
        }
-       return 0;
 }
 
 struct sha1file *sha1fd(int fd, const char *name)
index 9dedb038ea9c77a8fadac370e039f1672e948325..bb543d52f108cf1e976ee644e337f5ccec983790 100644 (file)
@@ -34,7 +34,7 @@ extern struct sha1file *sha1fd(int fd, const char *name);
 extern struct sha1file *sha1fd_check(const char *name);
 extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);
 extern int sha1close(struct sha1file *, unsigned char *, unsigned int);
-extern int sha1write(struct sha1file *, const void *, unsigned int);
+extern void sha1write(struct sha1file *, const void *, unsigned int);
 extern void sha1flush(struct sha1file *f);
 extern void crc32_begin(struct sha1file *);
 extern uint32_t crc32_end(struct sha1file *);
index 7bee953de8e67bcb02acf178f8f1451633976b90..503e03906c5c7f921e4d67a53835f771ea695d0c 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1278,11 +1278,11 @@ int main(int argc, char **argv)
                        make_service_overridable(arg + 18, 0);
                        continue;
                }
-               if (starts_with(arg, "--informative-errors")) {
+               if (!strcmp(arg, "--informative-errors")) {
                        informative_errors = 1;
                        continue;
                }
-               if (starts_with(arg, "--no-informative-errors")) {
+               if (!strcmp(arg, "--no-informative-errors")) {
                        informative_errors = 0;
                        continue;
                }
diff --git a/diff.c b/diff.c
index f99bc83b733acc7992c24c26f4465a669f7a4467..6b4cd0eb36b92167233a82691fbdba5f29555c1c 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -30,6 +30,7 @@ static int diff_use_color_default = -1;
 static int diff_context_default = 3;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
+static const char *diff_order_file_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
@@ -201,6 +202,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return git_config_string(&external_diff_cmd_cfg, var, value);
        if (!strcmp(var, "diff.wordregex"))
                return git_config_string(&diff_word_regex_cfg, var, value);
+       if (!strcmp(var, "diff.orderfile"))
+               return git_config_pathname(&diff_order_file_cfg, var, value);
 
        if (!strcmp(var, "diff.ignoresubmodules"))
                handle_ignore_submodules_arg(&default_diff_options, value);
@@ -3219,6 +3222,8 @@ void diff_setup(struct diff_options *options)
        options->detect_rename = diff_detect_rename_default;
        options->xdl_opts |= diff_algorithm;
 
+       options->orderfile = diff_order_file_cfg;
+
        if (diff_no_prefix) {
                options->a_prefix = options->b_prefix = "";
        } else if (!diff_mnemonic_prefix) {
index 23e93852d8c701760d56e7e728dba7c08367fbe8..fe7f1f4647a16a5882cc4c1e63738c7faa4bc677 100644 (file)
@@ -10,28 +10,21 @@ static int order_cnt;
 
 static void prepare_order(const char *orderfile)
 {
-       int fd, cnt, pass;
+       int cnt, pass;
+       struct strbuf sb = STRBUF_INIT;
        void *map;
        char *cp, *endp;
-       struct stat st;
-       size_t sz;
+       ssize_t sz;
 
        if (order)
                return;
 
-       fd = open(orderfile, O_RDONLY);
-       if (fd < 0)
-               return;
-       if (fstat(fd, &st)) {
-               close(fd);
-               return;
-       }
-       sz = xsize_t(st.st_size);
-       map = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
-       close(fd);
-       if (map == MAP_FAILED)
-               return;
+       sz = strbuf_read_file(&sb, orderfile, 0);
+       if (sz < 0)
+               die_errno(_("failed to read orderfile '%s'"), orderfile);
+       map = strbuf_detach(&sb, NULL);
        endp = (char *) map + sz;
+
        for (pass = 0; pass < 2; pass++) {
                cnt = 0;
                cp = map;
@@ -73,15 +66,16 @@ struct pair_order {
 static int match_order(const char *path)
 {
        int i;
-       char p[PATH_MAX];
+       static struct strbuf p = STRBUF_INIT;
 
        for (i = 0; i < order_cnt; i++) {
-               strcpy(p, path);
-               while (p[0]) {
+               strbuf_reset(&p);
+               strbuf_addstr(&p, path);
+               while (p.buf[0]) {
                        char *cp;
-                       if (!fnmatch(order[i], p, 0))
+                       if (!fnmatch(order[i], p.buf, 0))
                                return i;
-                       cp = strrchr(p, '/');
+                       cp = strrchr(p.buf, '/');
                        if (!cp)
                                break;
                        *cp = 0;
diff --git a/dir.c b/dir.c
index 23b6de47036839ca4297a9b93d1a9794547ccff1..d10a63f731020aab99f9b25a2b96a029d772410d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -126,10 +126,13 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
                       PATHSPEC_MAXDEPTH |
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
-                      PATHSPEC_ICASE);
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
 
        for (n = 0; n < pathspec->nr; n++) {
                size_t i = 0, len = 0, item_len;
+               if (pathspec->items[n].magic & PATHSPEC_EXCLUDE)
+                       continue;
                if (pathspec->items[n].magic & PATHSPEC_ICASE)
                        item_len = pathspec->items[n].prefix;
                else
@@ -279,9 +282,10 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
  * pathspec did not match any names, which could indicate that the
  * user mistyped the nth pathspec.
  */
-int match_pathspec_depth(const struct pathspec *ps,
-                        const char *name, int namelen,
-                        int prefix, char *seen)
+static int match_pathspec_depth_1(const struct pathspec *ps,
+                                 const char *name, int namelen,
+                                 int prefix, char *seen,
+                                 int exclude)
 {
        int i, retval = 0;
 
@@ -290,7 +294,8 @@ int match_pathspec_depth(const struct pathspec *ps,
                       PATHSPEC_MAXDEPTH |
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
-                      PATHSPEC_ICASE);
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -309,8 +314,19 @@ int match_pathspec_depth(const struct pathspec *ps,
 
        for (i = ps->nr - 1; i >= 0; i--) {
                int how;
+
+               if ((!exclude &&   ps->items[i].magic & PATHSPEC_EXCLUDE) ||
+                   ( exclude && !(ps->items[i].magic & PATHSPEC_EXCLUDE)))
+                       continue;
+
                if (seen && seen[i] == MATCHED_EXACTLY)
                        continue;
+               /*
+                * Make exclude patterns optional and never report
+                * "pathspec ':(exclude)foo' matches no files"
+                */
+               if (seen && ps->items[i].magic & PATHSPEC_EXCLUDE)
+                       seen[i] = MATCHED_FNMATCH;
                how = match_pathspec_item(ps->items+i, prefix, name, namelen);
                if (ps->recursive &&
                    (ps->magic & PATHSPEC_MAXDEPTH) &&
@@ -334,6 +350,18 @@ int match_pathspec_depth(const struct pathspec *ps,
        return retval;
 }
 
+int match_pathspec_depth(const struct pathspec *ps,
+                        const char *name, int namelen,
+                        int prefix, char *seen)
+{
+       int positive, negative;
+       positive = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 0);
+       if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
+               return positive;
+       negative = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 1);
+       return negative ? 0 : positive;
+}
+
 /*
  * Return the length of the "simple" part of a path match limiter.
  */
@@ -1375,11 +1403,18 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
                               PATHSPEC_MAXDEPTH |
                               PATHSPEC_LITERAL |
                               PATHSPEC_GLOB |
-                              PATHSPEC_ICASE);
+                              PATHSPEC_ICASE |
+                              PATHSPEC_EXCLUDE);
 
        if (has_symlink_leading_path(path, len))
                return dir->nr;
 
+       /*
+        * exclude patterns are treated like positive ones in
+        * create_simplify. Usually exclude patterns should be a
+        * subset of positive ones, which has no impacts on
+        * create_simplify().
+        */
        simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
        if (!len || treat_leading_path(dir, path, len, simplify))
                read_directory_recursive(dir, path, len, 0, simplify);
index b73916bbc64b3d2b926c4f5aa50311c7a867c711..cbd86c37f51c00679498107d1a05131c19afa36e 100644 (file)
@@ -75,7 +75,7 @@
 # endif
 #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
       !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
-      !defined(__TANDEM) && !defined(__QNX__)
+      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif
index 642faffdb7d149f162c8716eb13dd878d16baa4b..08970bf46e49f6447ada24e81e5792ea9f92f419 100644 (file)
@@ -134,6 +134,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
                obj = ((struct tag *)obj)->tagged;
                if (!obj)
                        break;
+               if (!obj->parsed)
+                       parse_object(obj->sha1);
                add_name_decoration(DECORATION_REF_TAG, refname, obj);
        }
        return 0;
index ddc174e1adb9e1545b2815d8a1990e43f6fa2bfb..605d01b25cbd9d796810f8aa810894a2c198da28 100644 (file)
@@ -183,8 +183,7 @@ off_t write_pack_header(struct sha1file *f, uint32_t nr_entries)
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
        hdr.hdr_version = htonl(PACK_VERSION);
        hdr.hdr_entries = htonl(nr_entries);
-       if (sha1write(f, &hdr, sizeof(hdr)))
-               return 0;
+       sha1write(f, &hdr, sizeof(hdr));
        return sizeof(hdr);
 }
 
index 52d38a4b4fb6cbb4928bf3084b773651f14d7e72..6cb9fd3014a424d11647e48de3b841012ccc2ffe 100644 (file)
@@ -71,8 +71,23 @@ static struct pathspec_magic {
        { PATHSPEC_LITERAL,   0, "literal" },
        { PATHSPEC_GLOB,   '\0', "glob" },
        { PATHSPEC_ICASE,  '\0', "icase" },
+       { PATHSPEC_EXCLUDE, '!', "exclude" },
 };
 
+static void prefix_short_magic(struct strbuf *sb, int prefixlen,
+                              unsigned short_magic)
+{
+       int i;
+       strbuf_addstr(sb, ":(");
+       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+               if (short_magic & pathspec_magic[i].bit) {
+                       if (sb->buf[sb->len - 1] != '(')
+                               strbuf_addch(sb, ',');
+                       strbuf_addstr(sb, pathspec_magic[i].name);
+               }
+       strbuf_addf(sb, ",prefix:%d)", prefixlen);
+}
+
 /*
  * Take an element of a pathspec and check for magic signatures.
  * Append the result to the prefix. Return the magic bitmap.
@@ -232,22 +247,16 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
         */
        if (flags & PATHSPEC_PREFIX_ORIGIN) {
                struct strbuf sb = STRBUF_INIT;
-               const char *start = elt;
                if (prefixlen && !literal_global) {
                        /* Preserve the actual prefix length of each pattern */
                        if (short_magic)
-                               die("BUG: prefixing on short magic is not supported");
+                               prefix_short_magic(&sb, prefixlen, short_magic);
                        else if (long_magic_end) {
-                               strbuf_add(&sb, start, long_magic_end - start);
-                               strbuf_addf(&sb, ",prefix:%d", prefixlen);
-                               start = long_magic_end;
-                       } else {
-                               if (*start == ':')
-                                       start++;
+                               strbuf_add(&sb, elt, long_magic_end - elt);
+                               strbuf_addf(&sb, ",prefix:%d)", prefixlen);
+                       } else
                                strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
-                       }
                }
-               strbuf_add(&sb, start, copyfrom - start);
                strbuf_addstr(&sb, match);
                item->original = strbuf_detach(&sb, NULL);
        } else
@@ -355,7 +364,7 @@ void parse_pathspec(struct pathspec *pathspec,
 {
        struct pathspec_item *item;
        const char *entry = argv ? *argv : NULL;
-       int i, n, prefixlen;
+       int i, n, prefixlen, nr_exclude = 0;
 
        memset(pathspec, 0, sizeof(*pathspec));
 
@@ -412,6 +421,8 @@ void parse_pathspec(struct pathspec *pathspec,
                if ((flags & PATHSPEC_LITERAL_PATH) &&
                    !(magic_mask & PATHSPEC_LITERAL))
                        item[i].magic |= PATHSPEC_LITERAL;
+               if (item[i].magic & PATHSPEC_EXCLUDE)
+                       nr_exclude++;
                if (item[i].magic & magic_mask)
                        unsupported_magic(entry,
                                          item[i].magic & magic_mask,
@@ -427,6 +438,10 @@ void parse_pathspec(struct pathspec *pathspec,
                pathspec->magic |= item[i].magic;
        }
 
+       if (nr_exclude == n)
+               die(_("There is nothing to exclude from by :(exclude) patterns.\n"
+                     "Perhaps you forgot to add either ':/' or '.' ?"));
+
 
        if (pathspec->magic & PATHSPEC_MAXDEPTH) {
                if (flags & PATHSPEC_KEEP_ORDER)
index a75e9242d1bea7e9318426d6786bd5eae9dc6215..0c1126264a394e62cfbed47b2feae85e9b5d1410 100644 (file)
@@ -7,12 +7,14 @@
 #define PATHSPEC_LITERAL       (1<<2)
 #define PATHSPEC_GLOB          (1<<3)
 #define PATHSPEC_ICASE         (1<<4)
+#define PATHSPEC_EXCLUDE       (1<<5)
 #define PATHSPEC_ALL_MAGIC       \
        (PATHSPEC_FROMTOP       | \
         PATHSPEC_MAXDEPTH      | \
         PATHSPEC_LITERAL       | \
         PATHSPEC_GLOB          | \
-        PATHSPEC_ICASE)
+        PATHSPEC_ICASE         | \
+        PATHSPEC_EXCLUDE)
 
 #define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
 
index 5e2d709e152d930fbc01633511241b404d2ce252..c0bbb65bf6aa3066da667aa5c69058f0e6b5ded2 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -72,7 +72,7 @@ msgstr "Archivformat"
 
 #: archive.c:328 builtin/log.c:1193
 msgid "prefix"
-msgstr "Prefix"
+msgstr "Präfix"
 
 #: archive.c:329
 msgid "prepend prefix to each pathname in the archive"
@@ -3716,7 +3716,7 @@ msgid ""
 msgstr ""
 "Eingabehilfe:\n"
 "1          - nummeriertes Element auswählen\n"
-"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"foo        - Element anhand eines eindeutigen Präfix auswählen\n"
 "           - (leer) nichts auswählen"
 
 #: builtin/clean.c:298
@@ -3734,7 +3734,7 @@ msgstr ""
 "1          - einzelnes Element auswählen\n"
 "3-5        - Bereich von Elementen auswählen\n"
 "2-3,6-9    - mehrere Bereiche auswählen\n"
-"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"foo        - Element anhand eines eindeutigen Präfix auswählen\n"
 "-...       - angegebenes Element abwählen\n"
 "*          - alle Elemente auswählen\n"
 "           - (leer) Auswahl beenden"
@@ -6452,7 +6452,7 @@ msgstr "kennzeichnet die Serie als n-te Fassung"
 
 #: builtin/log.c:1194
 msgid "Use [<prefix>] instead of [PATCH]"
-msgstr "verwendet [<Prefix>] anstatt [PATCH]"
+msgstr "verwendet [<Präfix>] anstatt [PATCH]"
 
 #: builtin/log.c:1197
 msgid "store resulting files in <dir>"
@@ -8182,7 +8182,7 @@ msgid ""
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Prefix>] "
+"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Präfix>] "
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<Datei>] (--empty | <Commit-Referenz1> [<Commit-Referenz2> "
 "[<Commit-Referenz3>]])"
@@ -9782,15 +9782,15 @@ msgstr "gibt Tag-Inhalte aus"
 
 #: builtin/write-tree.c:13
 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
-msgstr "git write-tree [--missing-ok] [--prefix=<Prefix>/]"
+msgstr "git write-tree [--missing-ok] [--prefix=<Präfix>/]"
 
 #: builtin/write-tree.c:26
 msgid "<prefix>/"
-msgstr "<Prefix>/"
+msgstr "<Präfix>/"
 
 #: builtin/write-tree.c:27
 msgid "write tree object for a subdirectory <prefix>"
-msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Prefix>"
+msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Präfix>"
 
 #: builtin/write-tree.c:30
 msgid "only useful for debugging"
index d851807feb9849813635471d7099e67ed743a2d7..d7bb17cb663c2f47ad59d34ecea17c6cc977e815 100644 (file)
--- a/prompt.c
+++ b/prompt.c
@@ -22,6 +22,7 @@ static char *do_askpass(const char *cmd, const char *prompt)
        if (start_command(&pass))
                return NULL;
 
+       strbuf_reset(&buffer);
        if (strbuf_read(&buffer, pass.out, 20) < 0)
                err = 1;
 
@@ -38,7 +39,7 @@ static char *do_askpass(const char *cmd, const char *prompt)
 
        strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
 
-       return strbuf_detach(&buffer, NULL);
+       return buffer.buf;
 }
 
 char *git_prompt(const char *prompt, int flags)
index d0b1548726e9d2362d27c6947eb61a0647d0eac2..cdcaf8cbe2f4e6c7a5b334fc2c43da6855df4654 100644 (file)
@@ -97,9 +97,6 @@ const unsigned char *do_lookup_replace_object(const unsigned char *sha1)
        int pos, depth = MAXREPLACEDEPTH;
        const unsigned char *cur = sha1;
 
-       if (!read_replace_refs)
-               return sha1;
-
        prepare_replace_object();
 
        /* Try to recursively replace the object */
index daacc0cfb0132e98a98ba9645991f3d3f228d1cd..e13bd2c3ee7edb4d157e499fbcea80535ec5752f 100644 (file)
@@ -807,15 +807,38 @@ void free_pack_by_name(const char *pack_name)
 static unsigned int get_max_fd_limit(void)
 {
 #ifdef RLIMIT_NOFILE
-       struct rlimit lim;
+       {
+               struct rlimit lim;
 
-       if (getrlimit(RLIMIT_NOFILE, &lim))
-               die_errno("cannot get RLIMIT_NOFILE");
+               if (!getrlimit(RLIMIT_NOFILE, &lim))
+                       return lim.rlim_cur;
+       }
+#endif
+
+#ifdef _SC_OPEN_MAX
+       {
+               long open_max = sysconf(_SC_OPEN_MAX);
+               if (0 < open_max)
+                       return open_max;
+               /*
+                * Otherwise, we got -1 for one of the two
+                * reasons:
+                *
+                * (1) sysconf() did not understand _SC_OPEN_MAX
+                *     and signaled an error with -1; or
+                * (2) sysconf() said there is no limit.
+                *
+                * We _could_ clear errno before calling sysconf() to
+                * tell these two cases apart and return a huge number
+                * in the latter case to let the caller cap it to a
+                * value that is not so selfish, but letting the
+                * fallback OPEN_MAX codepath take care of these cases
+                * is a lot simpler.
+                */
+       }
+#endif
 
-       return lim.rlim_cur;
-#elif defined(_SC_OPEN_MAX)
-       return sysconf(_SC_OPEN_MAX);
-#elif defined(OPEN_MAX)
+#ifdef OPEN_MAX
        return OPEN_MAX;
 #else
        return 1; /* see the caller ;-) */
@@ -1667,6 +1690,38 @@ static off_t get_delta_base(struct packed_git *p,
        return base_offset;
 }
 
+/*
+ * Like get_delta_base above, but we return the sha1 instead of the pack
+ * offset. This means it is cheaper for REF deltas (we do not have to do
+ * the final object lookup), but more expensive for OFS deltas (we
+ * have to load the revidx to convert the offset back into a sha1).
+ */
+static const unsigned char *get_delta_base_sha1(struct packed_git *p,
+                                               struct pack_window **w_curs,
+                                               off_t curpos,
+                                               enum object_type type,
+                                               off_t delta_obj_offset)
+{
+       if (type == OBJ_REF_DELTA) {
+               unsigned char *base = use_pack(p, w_curs, curpos, NULL);
+               return base;
+       } else if (type == OBJ_OFS_DELTA) {
+               struct revindex_entry *revidx;
+               off_t base_offset = get_delta_base(p, w_curs, &curpos,
+                                                  type, delta_obj_offset);
+
+               if (!base_offset)
+                       return NULL;
+
+               revidx = find_pack_revindex(p, base_offset);
+               if (!revidx)
+                       return NULL;
+
+               return nth_packed_object_sha1(p, revidx->nr);
+       } else
+               return NULL;
+}
+
 int unpack_object_header(struct packed_git *p,
                         struct pack_window **w_curs,
                         off_t *curpos,
@@ -1824,6 +1879,22 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
                }
        }
 
+       if (oi->delta_base_sha1) {
+               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+                       const unsigned char *base;
+
+                       base = get_delta_base_sha1(p, &w_curs, curpos,
+                                                  type, obj_offset);
+                       if (!base) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+
+                       hashcpy(oi->delta_base_sha1, base);
+               } else
+                       hashclr(oi->delta_base_sha1);
+       }
+
 out:
        unuse_pack(&w_curs);
        return type;
@@ -2407,6 +2478,9 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        git_zstream stream;
        char hdr[32];
 
+       if (oi->delta_base_sha1)
+               hashclr(oi->delta_base_sha1);
+
        /*
         * If we don't care about type or size, then we don't
         * need to look inside the object at all. Note that we
@@ -2443,13 +2517,14 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        return 0;
 }
 
-int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
+int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
 {
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
+       const unsigned char *real = lookup_replace_object_extended(sha1, flags);
 
-       co = find_cached_object(sha1);
+       co = find_cached_object(real);
        if (co) {
                if (oi->typep)
                        *(oi->typep) = co->type;
@@ -2457,27 +2532,29 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
                        *(oi->sizep) = co->size;
                if (oi->disk_sizep)
                        *(oi->disk_sizep) = 0;
+               if (oi->delta_base_sha1)
+                       hashclr(oi->delta_base_sha1);
                oi->whence = OI_CACHED;
                return 0;
        }
 
-       if (!find_pack_entry(sha1, &e)) {
+       if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
-               if (!sha1_loose_object_info(sha1, oi)) {
+               if (!sha1_loose_object_info(real, oi)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
 
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
-               if (!find_pack_entry(sha1, &e))
+               if (!find_pack_entry(real, &e))
                        return -1;
        }
 
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
-               mark_bad_packed_object(e.p, sha1);
-               return sha1_object_info_extended(sha1, oi);
+               mark_bad_packed_object(e.p, real);
+               return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
        } else {
@@ -2499,7 +2576,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 
        oi.typep = &type;
        oi.sizep = sizep;
-       if (sha1_object_info_extended(sha1, &oi) < 0)
+       if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0)
                return -1;
        return type;
 }
@@ -2591,8 +2668,7 @@ void *read_sha1_file_extended(const unsigned char *sha1,
        void *data;
        char *path;
        const struct packed_git *p;
-       const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE)
-               ? lookup_replace_object(sha1) : sha1;
+       const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
 
        errno = 0;
        data = read_object(repl, type, size);
index debe904523252ae4fd2e18784641575c3f828dd2..9659f18be263a3412a2f3e5d13eb4f4bcd25cee3 100644 (file)
@@ -113,7 +113,7 @@ static enum input_source istream_source(const unsigned char *sha1,
 
        oi->typep = type;
        oi->sizep = &size;
-       status = sha1_object_info_extended(sha1, oi);
+       status = sha1_object_info_extended(sha1, oi, 0);
        if (status < 0)
                return stream_error;
 
index c9d105d70725b9a97d5a9518e83c615821e91c23..304c7b7d8714f9f985cddcc91ba3006f668de127 100644 (file)
@@ -116,6 +116,27 @@ test_expect_success 'blame evil merge' '
        check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1
 '
 
+test_expect_success 'blame huge graft' '
+       test_when_finished "git checkout branch2" &&
+       test_when_finished "rm -f .git/info/grafts" &&
+       graft= &&
+       for i in 0 1 2
+       do
+               for j in 0 1 2 3 4 5 6 7 8 9
+               do
+                       git checkout --orphan "$i$j" &&
+                       printf "%s\n" "$i" "$j" >file &&
+                       test_tick &&
+                       GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \
+                       git commit -a -m "$i$j" &&
+                       commit=$(git rev-parse --verify HEAD) &&
+                       graft="$graft$commit "
+               done
+       done &&
+       printf "%s " $graft >.git/info/grafts &&
+       check_count -h 00 01 1 10 1
+'
+
 test_expect_success 'setup incomplete line' '
        echo "incomplete" | tr -d "\\012" >>file &&
        GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \
index c4707843c4ca5d91d34c86185b3b16bd37d27e36..bfdff2a8c93091c8e2db8e03a74eae5c5a53991c 100644 (file)
@@ -129,7 +129,7 @@ prepare_httpd() {
        HTTPD_DEST=127.0.0.1:$LIB_HTTPD_PORT
        HTTPD_URL=$HTTPD_PROTO://$HTTPD_DEST
        HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST
-       HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:user%40host@$HTTPD_DEST
+       HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:pass%40host@$HTTPD_DEST
 
        if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
        then
@@ -217,7 +217,15 @@ setup_askpass_helper() {
        test_expect_success 'setup askpass helper' '
                write_script "$TRASH_DIRECTORY/askpass" <<-\EOF &&
                echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" &&
-               cat "$TRASH_DIRECTORY/askpass-response"
+               case "$*" in
+               *Username*)
+                       what=user
+                       ;;
+               *Password*)
+                       what=pass
+                       ;;
+               esac &&
+               cat "$TRASH_DIRECTORY/askpass-$what"
                EOF
                GIT_ASKPASS="$TRASH_DIRECTORY/askpass" &&
                export GIT_ASKPASS &&
@@ -227,7 +235,8 @@ setup_askpass_helper() {
 
 set_askpass() {
        >"$TRASH_DIRECTORY/askpass-query" &&
-       echo "$*" >"$TRASH_DIRECTORY/askpass-response"
+       echo "$1" >"$TRASH_DIRECTORY/askpass-user" &&
+       echo "$2" >"$TRASH_DIRECTORY/askpass-pass"
 }
 
 expect_askpass() {
index f2fbcad33e28adcbd30a539371e5d659251ba3a6..99a34d648742f669e7b501298a92fab401bc1971 100644 (file)
@@ -1 +1 @@
-user@host:nKpa8pZUHx/ic
+user@host:xb4E8pqD81KQs
index 10be52beedb4058318555e096d51517f1c30ad9f..a2bb63ce8e5e5586691affb51f9cff1f4d1e327a 100755 (executable)
@@ -41,20 +41,17 @@ test_expect_success '.git/objects should have 3 subdirectories' '
 test_expect_success 'success is reported like this' '
        :
 '
-test_expect_failure 'pretend we have a known breakage' '
-       false
-'
 
 run_sub_test_lib_test () {
        name="$1" descr="$2" # stdin is the body of the test code
        shift 2
        mkdir "$name" &&
        (
-               # Pretend we're a test harness.  This prevents
-               # test-lib from writing the counts to a file that will
-               # later be summarized, showing spurious "failed" tests
-               HARNESS_ACTIVE=t &&
-               export HARNESS_ACTIVE &&
+               # Pretend we're not running under a test harness, whether we
+               # are or not. The test-lib output depends on the setting of
+               # this variable, so we need a stable setting under which to run
+               # the sub-test.
+               sane_unset HARNESS_ACTIVE &&
                cd "$name" &&
                cat >"$name.sh" <<-EOF &&
                #!$SHELL_PATH
@@ -71,6 +68,8 @@ run_sub_test_lib_test () {
                cat >>"$name.sh" &&
                chmod +x "$name.sh" &&
                export TEST_DIRECTORY &&
+               TEST_OUTPUT_DIRECTORY=$(pwd) &&
+               export TEST_OUTPUT_DIRECTORY &&
                ./"$name.sh" "$@" >out 2>err
        )
 }
@@ -233,16 +232,13 @@ test_expect_success 'test --verbose' '
        grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
        check_sub_test_lib_test test-verbose <<-\EOF
        > expecting success: true
-       > Z
        > ok 1 - passing test
        > Z
        > expecting success: echo foo
        > foo
-       > Z
        > ok 2 - test with output
        > Z
        > expecting success: false
-       > Z
        > not ok 3 - failing test
        > #     false
        > Z
@@ -265,7 +261,6 @@ test_expect_success 'test --verbose-only' '
        > Z
        > expecting success: echo foo
        > foo
-       > Z
        > ok 2 - test with output
        > Z
        > not ok 3 - failing test
index 1687098355b710850a04769512320930d9b8b27b..a72e700ae434b07b08b48a68d4c0ebaa8173002b 100755 (executable)
@@ -262,4 +262,38 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" '
     "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
 '
 
+test_expect_success 'setup blobs which are likely to delta' '
+       test-genrandom foo 10240 >foo &&
+       { cat foo; echo plus; } >foo-plus &&
+       git add foo foo-plus &&
+       git commit -m foo &&
+       cat >blobs <<-\EOF
+       HEAD:foo
+       HEAD:foo-plus
+       EOF
+'
+
+test_expect_success 'confirm that neither loose blob is a delta' '
+       cat >expect <<-EOF
+       $_z40
+       $_z40
+       EOF
+       git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+       test_cmp expect actual
+'
+
+# To avoid relying too much on the current delta heuristics,
+# we will check only that one of the two objects is a delta
+# against the other, but not the order. We can do so by just
+# asking for the base of both, and checking whether either
+# sha1 appears in the output.
+test_expect_success '%(deltabase) reports packed delta bases' '
+       git repack -ad &&
+       git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+       {
+               grep "$(git rev-parse HEAD:foo)" actual ||
+               grep "$(git rev-parse HEAD:foo-plus)" actual
+       }
+'
+
 test_done
index aab86e838b806f9bb2289f536f4ba9324d75da0a..fe274e2fb14869314dfb21d563fd2c3f367365c8 100755 (executable)
@@ -272,6 +272,25 @@ test_expect_success '"add non-existent" should fail' '
        ! (git ls-files | grep "non-existent")
 '
 
+test_expect_success 'git add -A on empty repo does not error out' '
+       rm -fr empty &&
+       git init empty &&
+       (
+               cd empty &&
+               git add -A . &&
+               git add -A
+       )
+'
+
+test_expect_success '"git add ." in empty repo' '
+       rm -fr empty &&
+       git init empty &&
+       (
+               cd empty &&
+               git add .
+       )
+'
+
 test_expect_success 'git add --dry-run of existing changed file' "
        echo new >>track-this &&
        git add --dry-run track-this >actual 2>&1 &&
diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh
new file mode 100755 (executable)
index 0000000..1ddd226
--- /dev/null
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+test_description='diff order'
+
+. ./test-lib.sh
+
+create_files () {
+       echo "$1" >a.h &&
+       echo "$1" >b.c &&
+       echo "$1" >c/Makefile &&
+       echo "$1" >d.txt &&
+       git add a.h b.c c/Makefile d.txt &&
+       git commit -m"$1"
+}
+
+test_expect_success 'setup' '
+       mkdir c &&
+       create_files 1 &&
+       create_files 2 &&
+
+       cat >order_file_1 <<-\EOF &&
+       *Makefile
+       *.txt
+       *.h
+       EOF
+
+       cat >order_file_2 <<-\EOF &&
+       *Makefile
+       *.h
+       *.c
+       EOF
+
+       cat >expect_none <<-\EOF &&
+       a.h
+       b.c
+       c/Makefile
+       d.txt
+       EOF
+
+       cat >expect_1 <<-\EOF &&
+       c/Makefile
+       d.txt
+       a.h
+       b.c
+       EOF
+
+       cat >expect_2 <<-\EOF
+       c/Makefile
+       a.h
+       b.c
+       d.txt
+       EOF
+'
+
+test_expect_success "no order (=tree object order)" '
+       git diff --name-only HEAD^..HEAD >actual &&
+       test_cmp expect_none actual
+'
+
+test_expect_success 'missing orderfile' '
+       rm -f bogus_file &&
+       test_must_fail git diff -Obogus_file --name-only HEAD^..HEAD
+'
+
+test_expect_success POSIXPERM,SANITY 'unreadable orderfile' '
+       >unreadable_file &&
+       chmod -r unreadable_file &&
+       test_must_fail git diff -Ounreadable_file --name-only HEAD^..HEAD
+'
+
+test_expect_success 'orderfile is a directory' '
+       test_must_fail git diff -O/ --name-only HEAD^..HEAD
+'
+
+for i in 1 2
+do
+       test_expect_success "orderfile using option ($i)" '
+               git diff -Oorder_file_$i --name-only HEAD^..HEAD >actual &&
+               test_cmp expect_$i actual
+       '
+
+       test_expect_success PIPE "orderfile is fifo ($i)" '
+               rm -f order_fifo &&
+               mkfifo order_fifo &&
+               {
+                       cat order_file_$i >order_fifo &
+               } &&
+               git diff -O order_fifo --name-only HEAD^..HEAD >actual &&
+               wait &&
+               test_cmp expect_$i actual
+       '
+
+       test_expect_success "orderfile using config ($i)" '
+               git -c diff.orderfile=order_file_$i diff --name-only HEAD^..HEAD >actual &&
+               test_cmp expect_$i actual
+       '
+
+       test_expect_success "cancelling configured orderfile ($i)" '
+               git -c diff.orderfile=order_file_$i diff -O/dev/null --name-only HEAD^..HEAD >actual &&
+               test_cmp expect_none actual
+       '
+done
+
+test_done
index fb000411395d50639334c3c0395b2b975aa4bc12..2a6278bb333d2843ee617ff69227feae115aefea 100755 (executable)
@@ -310,4 +310,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'log decoration properly follows tag chain' '
+       git tag -a tag1 -m tag1 &&
+       git tag -a tag2 -m tag2 tag1 &&
+       git tag -d tag1 &&
+       git commit --amend -m shorter &&
+       git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
+       cat <<EOF >expected &&
+6a908c10688b2503073c39c9ba26322c73902bb5  (tag: refs/tags/tag2)
+9f716384d92283fb915a4eee5073f030638e05f9  (tag: refs/tags/message-one)
+b87e4cccdb77336ea79d89224737be7ea8e95367  (tag: refs/tags/message-two)
+EOF
+       sort actual >actual1 &&
+       test_cmp expected actual1
+'
+
 test_done
index 5d4581dac84f0688389ca96ba139518336b78dba..12674ac0980fdafd60037bdff8009bb02a6da14a 100755 (executable)
@@ -614,4 +614,30 @@ test_expect_success 'all boundary commits are excluded' '
        test_bundle_object_count .git/objects/pack/pack-${pack##pack    }.pack 3
 '
 
+test_expect_success 'fetch --prune prints the remotes url' '
+       git branch goodbye &&
+       git clone . only-prunes &&
+       git branch -D goodbye &&
+       (
+               cd only-prunes &&
+               git fetch --prune origin 2>&1 | head -n1 >../actual
+       ) &&
+       echo "From ${D}/." >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'branchname D/F conflict resolved by --prune' '
+       git branch dir/file &&
+       git clone . prune-df-conflict &&
+       git branch -D dir/file &&
+       git branch dir &&
+       (
+               cd prune-df-conflict &&
+               git fetch --prune &&
+               git rev-parse origin/dir >../actual
+       ) &&
+       git rev-parse dir >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 01d0d95b4d6476f691f650cc413fa7156caee88b..5b0198cbc88ad54ea4e6f102429697c2da2ed2ba 100755 (executable)
@@ -154,7 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 
 test_expect_success 'push to password-protected repository (user in URL)' '
        test_commit pw-user &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
        git rev-parse --verify HEAD >expect &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
@@ -168,7 +168,7 @@ test_expect_failure 'user was prompted only once for password' '
 
 test_expect_failure 'push to password-protected repository (no user in URL)' '
        test_commit pw-nouser &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
        expect_askpass both user@host
        git rev-parse --verify HEAD >expect &&
index 470ac54295e8640feb4d23a81c5dc8ce362857d0..bfd241ea8ae4e51f69194f4a08685e9cd9d78b61 100755 (executable)
@@ -274,7 +274,7 @@ test_expect_success 'push over smart http with auth' '
        cd "$ROOT_PATH/test_repo_clone" &&
        echo push-auth-test >expect &&
        test_commit push-auth-test &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL"/auth/smart/test_repo.git &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
                log -1 --format=%s >actual &&
@@ -286,7 +286,7 @@ test_expect_success 'push to auth-only-for-push repo' '
        cd "$ROOT_PATH/test_repo_clone" &&
        echo push-half-auth >expect &&
        test_commit push-half-auth &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
                log -1 --format=%s >actual &&
@@ -316,7 +316,7 @@ test_expect_success 'push into half-auth-complete requires password' '
        cd "$ROOT_PATH/half-auth-clone" &&
        echo two >expect &&
        test_commit two &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
                log -1 --format=%s >actual &&
index f7d0f146f0f69775dd3fa3ea06895e2bb1a74d55..83926247142458323298c844ec99a74c369107e7 100755 (executable)
@@ -62,13 +62,13 @@ test_expect_success 'http auth can use user/pass in URL' '
 '
 
 test_expect_success 'http auth can use just user in URL' '
-       set_askpass user@host &&
+       set_askpass wrong pass@host &&
        git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'http auth can request both user and pass' '
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
        expect_askpass both user@host
 '
@@ -77,7 +77,7 @@ test_expect_success 'http auth respects credential helper config' '
        test_config_global credential.helper "!f() {
                cat >/dev/null
                echo username=user@host
-               echo password=user@host
+               echo password=pass@host
        }; f" &&
        set_askpass wrong &&
        git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
@@ -86,14 +86,14 @@ test_expect_success 'http auth respects credential helper config' '
 
 test_expect_success 'http auth can get username from config' '
        test_config_global "credential.$HTTPD_URL.username" user@host &&
-       set_askpass user@host &&
+       set_askpass wrong pass@host &&
        git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'configured username does not override URL' '
        test_config_global "credential.$HTTPD_URL.username" wrong &&
-       set_askpass user@host &&
+       set_askpass wrong pass@host &&
        git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
        expect_askpass pass user@host
 '
index afb439e09c2db4853c6eba88a1e630f319337cf8..a124efe11459905a11d91bfb32f8388bf4876c1d 100755 (executable)
@@ -119,7 +119,7 @@ test_expect_success 'redirects re-root further requests' '
 
 test_expect_success 'clone from password-protected repository' '
        echo two >expect &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
        expect_askpass both user@host &&
        git --git-dir=smart-auth log -1 --format=%s >actual &&
@@ -137,7 +137,7 @@ test_expect_success 'clone from auth-only-for-push repository' '
 
 test_expect_success 'clone from auth-only-for-objects repository' '
        echo two >expect &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
        expect_askpass both user@host &&
        git --git-dir=half-auth log -1 --format=%s >actual &&
@@ -151,7 +151,7 @@ test_expect_success 'no-op half-auth fetch does not require a password' '
 '
 
 test_expect_success 'redirects send auth to new location' '
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git -c credential.useHttpPath=true \
          clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
        expect_askpass both user@host auth/smart/repo.git
index 30a68335b3a69a8189648213606ddeb32b97d99e..39b3238da211957eeec36a9c7b911cd5942de6dd 100755 (executable)
@@ -258,4 +258,43 @@ test_expect_success 'using reflog to find the fork point' '
        test_cmp expect3 actual
 '
 
+test_expect_success 'merge-base --octopus --all for complex tree' '
+       # Best common ancestor for JE, JAA and JDD is JC
+       #             JE
+       #            / |
+       #           /  |
+       #          /   |
+       #  JAA    /    |
+       #   |\   /     |
+       #   | \  | JDD |
+       #   |  \ |/ |  |
+       #   |   JC JD  |
+       #   |    | /|  |
+       #   |    |/ |  |
+       #  JA    |  |  |
+       #   |\  /|  |  |
+       #   X JB |  X  X
+       #   \  \ | /   /
+       #    \__\|/___/
+       #        J
+       test_commit J &&
+       test_commit JB &&
+       git reset --hard J &&
+       test_commit JC &&
+       git reset --hard J &&
+       test_commit JTEMP1 &&
+       test_merge JA JB &&
+       test_merge JAA JC &&
+       git reset --hard J &&
+       test_commit JTEMP2 &&
+       test_merge JD JB &&
+       test_merge JDD JC &&
+       git reset --hard J &&
+       test_commit JTEMP3 &&
+       test_merge JE JC &&
+       git rev-parse JC >expected &&
+       git merge-base --all --octopus JAA JDD JE >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 7d479843522e3b183fdb4d72d86909b53e0edbda..719a11673bfc93463870d7d601eb3a0634b59b48 100755 (executable)
@@ -276,6 +276,48 @@ test_expect_success '-f option bypasses the type check' '
        git replace -f HEAD^ $BLOB
 '
 
+test_expect_success 'git cat-file --batch works on replace objects' '
+       git replace | grep $PARA3 &&
+       echo $PARA3 | git cat-file --batch
+'
+
+test_expect_success 'test --format bogus' '
+       test_must_fail git replace --format bogus >/dev/null 2>&1
+'
+
+test_expect_success 'test --format short' '
+       git replace --format=short >actual &&
+       git replace >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'test --format medium' '
+       H1=$(git --no-replace-objects rev-parse HEAD~1) &&
+       HT=$(git --no-replace-objects rev-parse HEAD^{tree}) &&
+       MYTAG=$(git --no-replace-objects rev-parse mytag) &&
+       {
+               echo "$H1 -> $BLOB" &&
+               echo "$BLOB -> $REPLACED" &&
+               echo "$HT -> $H1" &&
+               echo "$PARA3 -> $S" &&
+               echo "$MYTAG -> $HASH1"
+       } | sort >expected &&
+       git replace -l --format medium | sort > actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'test --format long' '
+       {
+               echo "$H1 (commit) -> $BLOB (blob)" &&
+               echo "$BLOB (blob) -> $REPLACED (blob)" &&
+               echo "$HT (tree) -> $H1 (commit)" &&
+               echo "$PARA3 (commit) -> $S (commit)" &&
+               echo "$MYTAG (tag) -> $HASH1 (commit)"
+       } | sort >expected &&
+       git replace --format=long | sort > actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'replace ref cleanup' '
        test -n "$(git replace)" &&
        git replace -d $(git replace) &&
index 7ea14ced313d3f0dd20d4afd2fc2a0d3309b938f..10b1452766b613932f66421f6c9bfafecc093177 100755 (executable)
@@ -20,7 +20,17 @@ test_expect_success 'setup' '
        test_commit start2 &&
        git checkout master &&
        git merge -m next start2 &&
-       test_commit final
+       test_commit final &&
+
+       test_seq 40 |
+       while read i
+       do
+               git checkout --orphan "b$i" &&
+               test_tick &&
+               git commit --allow-empty -m "$i" &&
+               commit=$(git rev-parse --verify HEAD) &&
+               printf "$commit " >>.git/info/grafts
+       done
 '
 
 test_expect_success 'start is valid' '
@@ -79,6 +89,10 @@ test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' '
        test_cmp expect actual
 '
 
+test_expect_success 'large graft octopus' '
+       test_cmp_rev_output b31 "git rev-parse --verify b1^30"
+'
+
 test_expect_success 'repack for next test' '
        git repack -a -d
 '
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
new file mode 100755 (executable)
index 0000000..62049be
--- /dev/null
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+test_description='test case exclude pathspec'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       for p in file sub/file sub/sub/file sub/file2 sub/sub/sub/file sub2/file; do
+               if echo $p | grep /; then
+                       mkdir -p `dirname $p`
+               fi &&
+               : >$p &&
+               git add $p &&
+               git commit -m $p
+       done &&
+       git log --oneline --format=%s >actual &&
+       cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/sub/file
+sub/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'exclude only should error out' '
+       test_must_fail git log --oneline --format=%s -- ":(exclude)sub"
+'
+
+test_expect_success 't_e_i() exclude sub' '
+       git log --oneline --format=%s -- . ":(exclude)sub" >actual
+       cat <<EOF >expect &&
+sub2/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub/sub/file' '
+       git log --oneline --format=%s -- . ":(exclude)sub/sub/file" >actual
+       cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub using mnemonic' '
+       git log --oneline --format=%s -- . ":!sub" >actual
+       cat <<EOF >expect &&
+sub2/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude :(icase)SUB' '
+       git log --oneline --format=%s -- . ":(exclude,icase)SUB" >actual
+       cat <<EOF >expect &&
+sub2/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude sub2 from sub' '
+       (
+       cd sub &&
+       git log --oneline --format=%s -- :/ ":/!sub2" >actual
+       cat <<EOF >expect &&
+sub/sub/sub/file
+sub/file2
+sub/sub/file
+sub/file
+file
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 't_e_i() exclude sub/*file' '
+       git log --oneline --format=%s -- . ":(exclude)sub/*file" >actual
+       cat <<EOF >expect &&
+sub2/file
+sub/file2
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 't_e_i() exclude :(glob)sub/*/file' '
+       git log --oneline --format=%s -- . ":(exclude,glob)sub/*/file" >actual
+       cat <<EOF >expect &&
+sub2/file
+sub/sub/sub/file
+sub/file2
+sub/file
+file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub' '
+       git ls-files -- . ":(exclude)sub" >actual
+       cat <<EOF >expect &&
+file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub/sub/file' '
+       git ls-files -- . ":(exclude)sub/sub/file" >actual
+       cat <<EOF >expect &&
+file
+sub/file
+sub/file2
+sub/sub/sub/file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub using mnemonic' '
+       git ls-files -- . ":!sub" >actual
+       cat <<EOF >expect &&
+file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude :(icase)SUB' '
+       git ls-files -- . ":(exclude,icase)SUB" >actual
+       cat <<EOF >expect &&
+file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude sub2 from sub' '
+       (
+       cd sub &&
+       git ls-files -- :/ ":/!sub2" >actual
+       cat <<EOF >expect &&
+../file
+file
+file2
+sub/file
+sub/sub/file
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'm_p_d() exclude sub/*file' '
+       git ls-files -- . ":(exclude)sub/*file" >actual
+       cat <<EOF >expect &&
+file
+sub/file2
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'm_p_d() exclude :(glob)sub/*/file' '
+       git ls-files -- . ":(exclude,glob)sub/*/file" >actual
+       cat <<EOF >expect &&
+file
+sub/file
+sub/file2
+sub/sub/sub/file
+sub2/file
+EOF
+       test_cmp expect actual
+'
+
+test_done
index 1cf78d5b113e0ddac8cdda9b8bc9b49db7b43451..1531c241c08f0fad1a8e0789dbed2675b0fe5e56 100644 (file)
@@ -481,8 +481,6 @@ test_at_end_hook_ () {
 test_done () {
        GIT_EXIT_OK=t
 
-       # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
-       # output file
        if test -z "$HARNESS_ACTIVE"
        then
                test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
index 5ece8c3477b11f3bf0c54196924ac24568309225..680afda060e28fd15c0064bd51cb829dc2469b1c 100644 (file)
@@ -662,9 +662,10 @@ static int match_wildcard_base(const struct pathspec_item *item,
  * Pre-condition: either baselen == base_offset (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
-enum interesting tree_entry_interesting(const struct name_entry *entry,
-                                       struct strbuf *base, int base_offset,
-                                       const struct pathspec *ps)
+static enum interesting do_match(const struct name_entry *entry,
+                                struct strbuf *base, int base_offset,
+                                const struct pathspec *ps,
+                                int exclude)
 {
        int i;
        int pathlen, baselen = base->len - base_offset;
@@ -676,7 +677,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
                       PATHSPEC_MAXDEPTH |
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
-                      PATHSPEC_ICASE);
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -697,6 +699,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
                const char *base_str = base->buf + base_offset;
                int matchlen = item->len, matched = 0;
 
+               if ((!exclude &&   item->magic & PATHSPEC_EXCLUDE) ||
+                   ( exclude && !(item->magic & PATHSPEC_EXCLUDE)))
+                       continue;
+
                if (baselen >= matchlen) {
                        /* If it doesn't match, move along... */
                        if (!match_dir_prefix(item, base_str, match, matchlen))
@@ -782,3 +788,72 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
        }
        return never_interesting; /* No matches */
 }
+
+/*
+ * Is a tree entry interesting given the pathspec we have?
+ *
+ * Pre-condition: either baselen == base_offset (i.e. empty path)
+ * or base[baselen-1] == '/' (i.e. with trailing slash).
+ */
+enum interesting tree_entry_interesting(const struct name_entry *entry,
+                                       struct strbuf *base, int base_offset,
+                                       const struct pathspec *ps)
+{
+       enum interesting positive, negative;
+       positive = do_match(entry, base, base_offset, ps, 0);
+
+       /*
+        * case | entry | positive | negative | result
+        * -----+-------+----------+----------+-------
+        *   1  |  file |   -1     |  -1..2   |  -1
+        *   2  |  file |    0     |  -1..2   |   0
+        *   3  |  file |    1     |   -1     |   1
+        *   4  |  file |    1     |    0     |   1
+        *   5  |  file |    1     |    1     |   0
+        *   6  |  file |    1     |    2     |   0
+        *   7  |  file |    2     |   -1     |   2
+        *   8  |  file |    2     |    0     |   2
+        *   9  |  file |    2     |    1     |   0
+        *  10  |  file |    2     |    2     |  -1
+        * -----+-------+----------+----------+-------
+        *  11  |  dir  |   -1     |  -1..2   |  -1
+        *  12  |  dir  |    0     |  -1..2   |   0
+        *  13  |  dir  |    1     |   -1     |   1
+        *  14  |  dir  |    1     |    0     |   1
+        *  15  |  dir  |    1     |    1     |   1 (*)
+        *  16  |  dir  |    1     |    2     |   0
+        *  17  |  dir  |    2     |   -1     |   2
+        *  18  |  dir  |    2     |    0     |   2
+        *  19  |  dir  |    2     |    1     |   1 (*)
+        *  20  |  dir  |    2     |    2     |  -1
+        *
+        * (*) An exclude pattern interested in a directory does not
+        * necessarily mean it will exclude all of the directory. In
+        * wildcard case, it can't decide until looking at individual
+        * files inside. So don't write such directories off yet.
+        */
+
+       if (!(ps->magic & PATHSPEC_EXCLUDE) ||
+           positive <= entry_not_interesting) /* #1, #2, #11, #12 */
+               return positive;
+
+       negative = do_match(entry, base, base_offset, ps, 1);
+
+       /* #3, #4, #7, #8, #13, #14, #17, #18 */
+       if (negative <= entry_not_interesting)
+               return positive;
+
+       /* #15, #19 */
+       if (S_ISDIR(entry->mode) &&
+           positive >= entry_interesting &&
+           negative == entry_interesting)
+               return entry_interesting;
+
+       if ((positive == entry_interesting &&
+            negative >= entry_interesting) || /* #5, #6, #16 */
+           (positive == all_entries_interesting &&
+            negative == entry_interesting)) /* #9 */
+               return entry_not_interesting;
+
+       return all_entries_not_interesting; /* #10, #20 */
+}
index ad3e9a04fe8ccb9286b94480f4484b0569f70a57..164354dad7cbbaa7100f73256807680a75188021 100644 (file)
@@ -830,23 +830,24 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
 }
 
 static int clear_ce_flags_1(struct cache_entry **cache, int nr,
-                           char *prefix, int prefix_len,
+                           struct strbuf *prefix,
                            int select_mask, int clear_mask,
                            struct exclude_list *el, int defval);
 
 /* Whole directory matching */
 static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
-                             char *prefix, int prefix_len,
+                             struct strbuf *prefix,
                              char *basename,
                              int select_mask, int clear_mask,
                              struct exclude_list *el, int defval)
 {
        struct cache_entry **cache_end;
        int dtype = DT_DIR;
-       int ret = is_excluded_from_list(prefix, prefix_len,
+       int ret = is_excluded_from_list(prefix->buf, prefix->len,
                                        basename, &dtype, el);
+       int rc;
 
-       prefix[prefix_len++] = '/';
+       strbuf_addch(prefix, '/');
 
        /* If undecided, use matching result of parent dir in defval */
        if (ret < 0)
@@ -854,7 +855,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
 
        for (cache_end = cache; cache_end != cache + nr; cache_end++) {
                struct cache_entry *ce = *cache_end;
-               if (strncmp(ce->name, prefix, prefix_len))
+               if (strncmp(ce->name, prefix->buf, prefix->len))
                        break;
        }
 
@@ -865,10 +866,12 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
         * calling clear_ce_flags_1(). That function will call
         * the expensive is_excluded_from_list() on every entry.
         */
-       return clear_ce_flags_1(cache, cache_end - cache,
-                               prefix, prefix_len,
-                               select_mask, clear_mask,
-                               el, ret);
+       rc = clear_ce_flags_1(cache, cache_end - cache,
+                             prefix,
+                             select_mask, clear_mask,
+                             el, ret);
+       strbuf_setlen(prefix, prefix->len - 1);
+       return rc;
 }
 
 /*
@@ -887,7 +890,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
  * Top level path has prefix_len zero.
  */
 static int clear_ce_flags_1(struct cache_entry **cache, int nr,
-                           char *prefix, int prefix_len,
+                           struct strbuf *prefix,
                            int select_mask, int clear_mask,
                            struct exclude_list *el, int defval)
 {
@@ -907,10 +910,10 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                        continue;
                }
 
-               if (prefix_len && strncmp(ce->name, prefix, prefix_len))
+               if (prefix->len && strncmp(ce->name, prefix->buf, prefix->len))
                        break;
 
-               name = ce->name + prefix_len;
+               name = ce->name + prefix->len;
                slash = strchr(name, '/');
 
                /* If it's a directory, try whole directory match first */
@@ -918,29 +921,26 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                        int processed;
 
                        len = slash - name;
-                       memcpy(prefix + prefix_len, name, len);
+                       strbuf_add(prefix, name, len);
 
-                       /*
-                        * terminate the string (no trailing slash),
-                        * clear_c_f_dir needs it
-                        */
-                       prefix[prefix_len + len] = '\0';
                        processed = clear_ce_flags_dir(cache, cache_end - cache,
-                                                      prefix, prefix_len + len,
-                                                      prefix + prefix_len,
+                                                      prefix,
+                                                      prefix->buf + prefix->len - len,
                                                       select_mask, clear_mask,
                                                       el, defval);
 
                        /* clear_c_f_dir eats a whole dir already? */
                        if (processed) {
                                cache += processed;
+                               strbuf_setlen(prefix, prefix->len - len);
                                continue;
                        }
 
-                       prefix[prefix_len + len++] = '/';
+                       strbuf_addch(prefix, '/');
                        cache += clear_ce_flags_1(cache, cache_end - cache,
-                                                 prefix, prefix_len + len,
+                                                 prefix,
                                                  select_mask, clear_mask, el, defval);
+                       strbuf_setlen(prefix, prefix->len - len - 1);
                        continue;
                }
 
@@ -961,9 +961,12 @@ static int clear_ce_flags(struct cache_entry **cache, int nr,
                            int select_mask, int clear_mask,
                            struct exclude_list *el)
 {
-       char prefix[PATH_MAX];
+       static struct strbuf prefix = STRBUF_INIT;
+
+       strbuf_reset(&prefix);
+
        return clear_ce_flags_1(cache, nr,
-                               prefix, 0,
+                               &prefix,
                                select_mask, clear_mask,
                                el, 0);
 }