Merge branch 'jk/oi-delta-base'
authorJunio C Hamano <gitster@pobox.com>
Fri, 10 Jan 2014 18:33:11 +0000 (10:33 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 10 Jan 2014 18:33:11 +0000 (10:33 -0800)
Teach "cat-file --batch" to show delta-base object name for a
packed object that is represented as a delta.

* jk/oi-delta-base:
cat-file: provide %(deltabase) batch format
sha1_object_info_extended: provide delta base sha1s

66 files changed:
Documentation/diff-config.txt
Documentation/diff-options.txt
Documentation/git-filter-branch.txt
Documentation/git-pack-objects.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-replace.txt
Documentation/git.txt
Documentation/gitmodules.txt
Documentation/glossary-content.txt
abspath.c
builtin/add.c
builtin/cat-file.c
builtin/diff.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/pack-objects.c
builtin/prune-packed.c
builtin/prune.c
builtin/push.c
builtin/replace.c
builtin/rev-parse.c
cache.h
contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
contrib/remote-helpers/git-remote-bzr
contrib/remote-helpers/git-remote-hg
contrib/remote-helpers/test-hg.sh
csum-file.c
csum-file.h
daemon.c
diff-no-index.c
diff.c
diff.h
diffcore-order.c
dir.c
git-difftool--helper.sh
git-pull.sh
git-rebase.sh
log-tree.c
pack-write.c
pack.h
pathspec.c
pathspec.h
perl/Git/SVN/Editor.pm
po/de.po
remote.c
remote.h
replace_object.c
sha1_file.c
streaming.c
t/perf/p4001-diff-no-index.sh [new file with mode: 0755]
t/t1006-cat-file.sh
t/t1506-rev-parse-diagnosis.sh
t/t3400-rebase.sh
t/t3700-add.sh
t/t4020-diff-external.sh
t/t4053-diff-no-index.sh
t/t4056-diff-order.sh [new file with mode: 0755]
t/t4205-log-pretty-formats.sh
t/t5302-pack-index.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t6050-replace.sh
t/t6132-pathspec-exclude.sh [new file with mode: 0755]
tree-walk.c
unpack-trees.c
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 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 d94edcd4b4fdd8bd57150bd0ad8f870a45e0249c..cdab9ed503a5adaace14b06863f31347725d06db 100644 (file)
@@ -51,8 +51,7 @@ base-name::
        <base-name> to determine the name of the created file.
        When this option is used, the two files are written in
        <base-name>-<SHA-1>.{pack,idx} files.  <SHA-1> is a hash
-       of the sorted object names to make the resulting filename
-       based on the pack content, and written to the standard
+       based on the pack content and is written to the standard
        output of the command.
 
 --stdout::
index 9eec74091097c0ea5c9fea69a09eb63e3cebd8d9..2b7f4f939f38626dbd796b808cc21285ad5c8c3d 100644 (file)
@@ -56,8 +56,13 @@ it can be any arbitrary "SHA-1 expression", such as `master~4` or
 +
 The <dst> tells which ref on the remote side is updated with this
 push. Arbitrary expressions cannot be used here, an actual ref must
-be named. If `:`<dst> is omitted, the same ref as <src> will be
-updated.
+be named.
+If `git push [<repository>]` without any `<refspec>` argument is set to
+update some ref at the destination with `<src>` with
+`remote.<repository>.push` configuration variable, `:<dst>` part can
+be omitted---such a push will update a ref that `<src>` normally updates
+without any `<refspec>` on the command line.  Otherwise, missing
+`:<dst>` means to update the same ref as the `<src>`.
 +
 The object referenced by <src> is used to update the <dst> reference
 on the remote side.  By default this is only allowed if <dst> is not
index 94e07fdab550dcac70734fb79724b00cf8d23edc..2889be6bdc4a1a243585e43def2e7416a9aa7038 100644 (file)
@@ -324,6 +324,16 @@ fresh commits so it can be remerged successfully without needing to "revert
 the reversion" (see the
 link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details).
 
+--fork-point::
+--no-fork-point::
+       Use 'git merge-base --fork-point' to find a better common ancestor
+       between `upstream` and `branch` when calculating which commits have
+       have been introduced by `branch` (see linkgit:git-merge-base[1]).
++
+If no non-option arguments are given on the command line, then the default is
+`--fork-point @{u}` otherwise the `upstream` argument is interpreted literally
+unless the `--fork-point` option is specified.
+
 --ignore-whitespace::
 --whitespace=<option>::
        These flag are passed to the 'git apply' program
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 cc0e5e2bfe66a0962283fd570c5e440e72b57813..aec372646ffc30f13b924159d47ade373fc44b87 100644 (file)
@@ -807,6 +807,15 @@ temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits.
 +
 For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
 parameter, <path>.
++
+For each path 'GIT_EXTERNAL_DIFF' is called, two environment variables,
+'GIT_DIFF_PATH_COUNTER' and 'GIT_DIFF_PATH_TOTAL' are set.
+
+'GIT_DIFF_PATH_COUNTER'::
+       A 1-based counter incremented by one for every path.
+
+'GIT_DIFF_PATH_TOTAL'::
+       The total number of paths.
 
 other
 ~~~~~
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 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 2e0af2e617e400a5c99465edd82f0dd1f8927680..d5a93e0e911ccbe4560a03ed78436bfb174e0f68 100644 (file)
@@ -199,25 +199,28 @@ static size_t expand_format(struct strbuf *sb, const char *start, void *data)
        return end - start + 1;
 }
 
-static void print_object_or_die(int fd, const unsigned char *sha1,
-                               enum object_type type, unsigned long size)
+static void print_object_or_die(int fd, struct expand_data *data)
 {
-       if (type == OBJ_BLOB) {
+       const unsigned char *sha1 = data->sha1;
+
+       assert(data->info.typep);
+
+       if (data->type == OBJ_BLOB) {
                if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
                        die("unable to stream %s to stdout", sha1_to_hex(sha1));
        }
        else {
-               enum object_type rtype;
-               unsigned long rsize;
+               enum object_type type;
+               unsigned long size;
                void *contents;
 
-               contents = read_sha1_file(sha1, &rtype, &rsize);
+               contents = read_sha1_file(sha1, &type, &size);
                if (!contents)
                        die("object %s disappeared", sha1_to_hex(sha1));
-               if (rtype != type)
+               if (type != data->type)
                        die("object %s changed type!?", sha1_to_hex(sha1));
-               if (rsize != size)
-                       die("object %s change size!?", sha1_to_hex(sha1));
+               if (data->info.sizep && size != data->size)
+                       die("object %s changed size!?", sha1_to_hex(sha1));
 
                write_or_die(fd, contents, size);
                free(contents);
@@ -244,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;
@@ -256,7 +259,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
        strbuf_release(&buf);
 
        if (opt->print_contents) {
-               print_object_or_die(1, data->sha1, data->type, data->size);
+               print_object_or_die(1, data);
                write_or_die(1, "\n", 1);
        }
        return 0;
@@ -280,6 +283,13 @@ static int batch_objects(struct batch_options *opt)
        strbuf_expand(&buf, opt->format, expand_format, &data);
        data.mark_query = 0;
 
+       /*
+        * If we are printing out the object, then always fill in the type,
+        * since we will want to decide whether or not to stream.
+        */
+       if (opt->print_contents)
+               data.info.typep = &data.type;
+
        /*
         * We are going to call get_sha1 on a potentially very large number of
         * objects. In most large cases, these will be actual object sha1s. The
index fe0cc7f1b5b1451f267b1d9e24a6e40cd6e55325..0f247d24008a4dc7ba5958e716c13f7582a820f1 100644 (file)
@@ -16,6 +16,9 @@
 #include "submodule.h"
 #include "sha1-array.h"
 
+#define DIFF_NO_INDEX_EXPLICIT 1
+#define DIFF_NO_INDEX_IMPLICIT 2
+
 struct blobinfo {
        unsigned char sha1[20];
        const char *name;
@@ -259,7 +262,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        struct object_array ent = OBJECT_ARRAY_INIT;
        int blobs = 0, paths = 0;
        struct blobinfo blob[2];
-       int nongit;
+       int nongit = 0, no_index = 0;
        int result = 0;
 
        /*
@@ -285,14 +288,59 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
         * Other cases are errors.
         */
 
-       prefix = setup_git_directory_gently(&nongit);
-       gitmodules_config();
+       /* Were we asked to do --no-index explicitly? */
+       for (i = 1; i < argc; i++) {
+               if (!strcmp(argv[i], "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(argv[i], "--no-index"))
+                       no_index = DIFF_NO_INDEX_EXPLICIT;
+               if (argv[i][0] != '-')
+                       break;
+       }
+
+       if (!no_index)
+               prefix = setup_git_directory_gently(&nongit);
+
+       /*
+        * Treat git diff with at least one path outside of the
+        * repo the same as if the command would have been executed
+        * outside of a git repository.  In this case it behaves
+        * the same way as "git diff --no-index <a> <b>", which acts
+        * as a colourful "diff" replacement.
+        */
+       if (nongit || ((argc == i + 2) &&
+                      (!path_inside_repo(prefix, argv[i]) ||
+                       !path_inside_repo(prefix, argv[i + 1]))))
+               no_index = DIFF_NO_INDEX_IMPLICIT;
+
+       if (!no_index)
+               gitmodules_config();
        git_config(git_diff_ui_config, NULL);
 
        init_revisions(&rev, prefix);
 
-       /* If this is a no-index diff, just run it and exit there. */
-       diff_no_index(&rev, argc, argv, nongit, prefix);
+       if (no_index && argc != i + 2) {
+               if (no_index == DIFF_NO_INDEX_IMPLICIT) {
+                       /*
+                        * There was no --no-index and there were not two
+                        * paths. It is possible that the user intended
+                        * to do an inside-repository operation.
+                        */
+                       fprintf(stderr, "Not a git repository\n");
+                       fprintf(stderr,
+                               "To compare two paths outside a working tree:\n");
+               }
+               /* Give the usage message for non-repository usage and exit. */
+               usagef("git diff %s <path> <path>",
+                      no_index == DIFF_NO_INDEX_EXPLICIT ?
+                      "--no-index" : "[--no-index]");
+
+       }
+       if (no_index)
+               /* If this is a no-index diff, just run it and exit there. */
+               diff_no_index(&rev, argc, argv, prefix);
 
        /* Otherwise, we are doing the usual "git" diff */
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
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 6551e7b39b6039032852d6ec0d0dd8da0d7031dc..51798b48b54266fccd2f7b4687646d152ab70588 100644 (file)
@@ -92,7 +92,7 @@ static struct {
  */
 static const char **used_atom;
 static cmp_type *used_atom_type;
-static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
+static int used_atom_cnt, need_tagged, need_symref;
 static int need_color_reset_at_eol;
 
 /*
@@ -1105,7 +1105,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 
        if (!sort)
                sort = default_sort();
-       sort_atom_limit = used_atom_cnt;
 
        /* for warn_ambiguous_refs */
        git_config(git_default_config, NULL);
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 a73982a30879f89be939034e462df68ee2c51c0b..0e50ddbb01342128d9118217118726000bdeaff6 100644 (file)
@@ -35,35 +35,75 @@ static void add_refspec(const char *ref)
        refspec[refspec_nr-1] = ref;
 }
 
-static void set_refspecs(const char **refs, int nr)
+static const char *map_refspec(const char *ref,
+                              struct remote *remote, struct ref *local_refs)
 {
+       struct ref *matched = NULL;
+
+       /* Does "ref" uniquely name our ref? */
+       if (count_refspec_match(ref, local_refs, &matched) != 1)
+               return ref;
+
+       if (remote->push) {
+               struct refspec query;
+               memset(&query, 0, sizeof(struct refspec));
+               query.src = matched->name;
+               if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&
+                   query.dst) {
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addf(&buf, "%s%s:%s",
+                                   query.force ? "+" : "",
+                                   query.src, query.dst);
+                       return strbuf_detach(&buf, NULL);
+               }
+       }
+
+       if (push_default == PUSH_DEFAULT_UPSTREAM &&
+           !prefixcmp(matched->name, "refs/heads/")) {
+               struct branch *branch = branch_get(matched->name + 11);
+               if (branch->merge_nr == 1 && branch->merge[0]->src) {
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addf(&buf, "%s:%s",
+                                   ref, branch->merge[0]->src);
+                       return strbuf_detach(&buf, NULL);
+               }
+       }
+
+       return ref;
+}
+
+static void set_refspecs(const char **refs, int nr, const char *repo)
+{
+       struct remote *remote = NULL;
+       struct ref *local_refs = NULL;
        int i;
+
        for (i = 0; i < nr; i++) {
                const char *ref = refs[i];
                if (!strcmp("tag", ref)) {
-                       char *tag;
-                       int len;
+                       struct strbuf tagref = STRBUF_INIT;
                        if (nr <= ++i)
                                die(_("tag shorthand without <tag>"));
-                       len = strlen(refs[i]) + 11;
-                       if (deleterefs) {
-                               tag = xmalloc(len+1);
-                               strcpy(tag, ":refs/tags/");
-                       } else {
-                               tag = xmalloc(len);
-                               strcpy(tag, "refs/tags/");
+                       ref = refs[i];
+                       if (deleterefs)
+                               strbuf_addf(&tagref, ":refs/tags/%s", ref);
+                       else
+                               strbuf_addf(&tagref, "refs/tags/%s", ref);
+                       ref = strbuf_detach(&tagref, NULL);
+               } else if (deleterefs) {
+                       struct strbuf delref = STRBUF_INIT;
+                       if (strchr(ref, ':'))
+                               die(_("--delete only accepts plain target ref names"));
+                       strbuf_addf(&delref, ":%s", ref);
+                       ref = strbuf_detach(&delref, NULL);
+               } else if (!strchr(ref, ':')) {
+                       if (!remote) {
+                               /* lazily grab remote and local_refs */
+                               remote = remote_get(repo);
+                               local_refs = get_local_heads();
                        }
-                       strcat(tag, refs[i]);
-                       ref = tag;
-               } else if (deleterefs && !strchr(ref, ':')) {
-                       char *delref;
-                       int len = strlen(ref)+1;
-                       delref = xmalloc(len+1);
-                       strcpy(delref, ":");
-                       strcat(delref, ref);
-                       ref = delref;
-               } else if (deleterefs)
-                       die(_("--delete only accepts plain target ref names"));
+                       ref = map_refspec(ref, remote, local_refs);
+               }
                add_refspec(ref);
        }
 }
@@ -501,7 +541,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 
        if (argc > 0) {
                repo = argv[0];
-               set_refspecs(argv + 1, argc - 1);
+               set_refspecs(argv + 1, argc - 1, repo);
        }
 
        rc = do_push(repo, flags);
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);
 }
index 6e802fdeb8bce0f7fdce925f47fc239462f6a459..aaeb611a9729938251b1c679e35c446bb737ff4f 100644 (file)
@@ -286,6 +286,7 @@ static int try_difference(const char *arg)
                                exclude = n;
                        }
                }
+               *dotdot = '.';
                return 1;
        }
        *dotdot = '.';
@@ -309,8 +310,10 @@ static int try_parent_shorthands(const char *arg)
                return 0;
 
        *dotdot = 0;
-       if (get_sha1_committish(arg, sha1))
+       if (get_sha1_committish(arg, sha1)) {
+               *dotdot = '^';
                return 0;
+       }
 
        if (!parents_only)
                show_rev(NORMAL, sha1, arg);
@@ -319,6 +322,7 @@ static int try_parent_shorthands(const char *arg)
                show_rev(parents_only ? NORMAL : REVERSED,
                                parents->item->object.sha1, arg);
 
+       *dotdot = '^';
        return 1;
 }
 
@@ -488,6 +492,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       int has_dashdash = 0;
        int output_prefix = 0;
        unsigned char sha1[20];
        const char *name = NULL;
@@ -501,6 +506,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        if (argc > 1 && !strcmp("-h", argv[1]))
                usage(builtin_rev_parse_usage);
 
+       for (i = 1; i < argc; i++) {
+               if (!strcmp(argv[i], "--")) {
+                       has_dashdash = 1;
+                       break;
+               }
+       }
+
        prefix = setup_git_directory();
        git_config(git_default_config, NULL);
        for (i = 1; i < argc; i++) {
@@ -788,6 +800,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                }
                if (verify)
                        die_no_single_rev(quiet);
+               if (has_dashdash)
+                       die("bad revision '%s'", arg);
                as_is = 1;
                if (!show_file(arg, output_prefix))
                        continue;
diff --git a/cache.h b/cache.h
index 67356dbe5434a37fc7418174187154a852249472..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 *);
@@ -1099,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 635c96bc563a21e98df181e3a179e50e0eaeadaa..2a317fca442a51dd6f47bfeeaa19bf32ed6e670f 100644 (file)
@@ -60,7 +60,7 @@
 #define gnome_keyring_memory_free gnome_keyring_free_password
 #define gnome_keyring_memory_strdup g_strdup
 
-static const chargnome_keyring_result_to_message(GnomeKeyringResult result)
+static const char *gnome_keyring_result_to_message(GnomeKeyringResult result)
 {
        switch (result) {
        case GNOME_KEYRING_RESULT_OK:
@@ -95,9 +95,9 @@ static const char* gnome_keyring_result_to_message(GnomeKeyringResult result)
 
 static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
 {
-       gpointer *data = (gpointer*) user_data;
-       int *done = (int*) data[0];
-       GnomeKeyringResult *r = (GnomeKeyringResult*) data[1];
+       gpointer *data = (gpointer *)user_data;
+       int *done = (int *)data[0];
+       GnomeKeyringResult *r = (GnomeKeyringResult *)data[1];
 
        *r = result;
        *done = 1;
@@ -130,34 +130,30 @@ static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, gu
 /*
  * This credential struct and API is simplified from git's credential.{h,c}
  */
-struct credential
-{
-       char          *protocol;
-       char          *host;
+struct credential {
+       char *protocol;
+       char *host;
        unsigned short port;
-       char          *path;
-       char          *username;
-       char          *password;
+       char *path;
+       char *username;
+       char *password;
 };
 
-#define CREDENTIAL_INIT \
-  { NULL,NULL,0,NULL,NULL,NULL }
+#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
 
-typedef int (*credential_op_cb)(struct credential*);
+typedef int (*credential_op_cb)(struct credential *);
 
-struct credential_operation
-{
-       char             *name;
+struct credential_operation {
+       char *name;
        credential_op_cb op;
 };
 
-#define CREDENTIAL_OP_END \
-  { NULL,NULL }
+#define CREDENTIAL_OP_END { NULL, NULL }
 
 /* ----------------- GNOME Keyring functions ----------------- */
 
 /* create a special keyring option string, if path is given */
-static charkeyring_object(struct credential *c)
+static char *keyring_object(struct credential *c)
 {
        if (!c->path)
                return NULL;
@@ -170,7 +166,7 @@ static char* keyring_object(struct credential *c)
 
 static int keyring_get(struct credential *c)
 {
-       charobject = NULL;
+       char *object = NULL;
        GList *entries;
        GnomeKeyringNetworkPasswordData *password_data;
        GnomeKeyringResult result;
@@ -204,7 +200,7 @@ static int keyring_get(struct credential *c)
        }
 
        /* pick the first one from the list */
-       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
 
        gnome_keyring_memory_free(c->password);
        c->password = gnome_keyring_memory_strdup(password_data->password);
@@ -221,7 +217,7 @@ static int keyring_get(struct credential *c)
 static int keyring_store(struct credential *c)
 {
        guint32 item_id;
-       char  *object = NULL;
+       char *object = NULL;
        GnomeKeyringResult result;
 
        /*
@@ -262,7 +258,7 @@ static int keyring_store(struct credential *c)
 
 static int keyring_erase(struct credential *c)
 {
-       char  *object = NULL;
+       char *object = NULL;
        GList *entries;
        GnomeKeyringNetworkPasswordData *password_data;
        GnomeKeyringResult result;
@@ -298,22 +294,20 @@ static int keyring_erase(struct credential *c)
        if (result == GNOME_KEYRING_RESULT_CANCELLED)
                return EXIT_SUCCESS;
 
-       if (result != GNOME_KEYRING_RESULT_OK)
-       {
+       if (result != GNOME_KEYRING_RESULT_OK) {
                g_critical("%s", gnome_keyring_result_to_message(result));
                return EXIT_FAILURE;
        }
 
        /* pick the first one from the list (delete all matches?) */
-       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
 
        result = gnome_keyring_item_delete_sync(
                password_data->keyring, password_data->item_id);
 
        gnome_keyring_network_password_list_free(entries);
 
-       if (result != GNOME_KEYRING_RESULT_OK)
-       {
+       if (result != GNOME_KEYRING_RESULT_OK) {
                g_critical("%s", gnome_keyring_result_to_message(result));
                return EXIT_FAILURE;
        }
@@ -325,9 +319,8 @@ static int keyring_erase(struct credential *c)
  * Table with helper operation callbacks, used by generic
  * credential helper main function.
  */
-static struct credential_operation const credential_helper_ops[] =
-{
-       { "get",   keyring_get   },
+static struct credential_operation const credential_helper_ops[] = {
+       { "get",   keyring_get },
        { "store", keyring_store },
        { "erase", keyring_erase },
        CREDENTIAL_OP_END
@@ -353,24 +346,23 @@ static void credential_clear(struct credential *c)
 
 static int credential_read(struct credential *c)
 {
-       char    *buf;
+       char *buf;
        size_t line_len;
-       char   *key;
-       char   *value;
+       char *key;
+       char *value;
 
        key = buf = gnome_keyring_memory_alloc(1024);
 
-       while (fgets(buf, 1024, stdin))
-       {
+       while (fgets(buf, 1024, stdin)) {
                line_len = strlen(buf);
 
                if (line_len && buf[line_len-1] == '\n')
-                       buf[--line_len]='\0';
+                       buf[--line_len] = '\0';
 
                if (!line_len)
                        break;
 
-               value = strchr(buf,'=');
+               value = strchr(buf, '=');
                if (!value) {
                        g_warning("invalid credential line: %s", key);
                        gnome_keyring_memory_free(buf);
@@ -384,7 +376,7 @@ static int credential_read(struct credential *c)
                } else if (!strcmp(key, "host")) {
                        g_free(c->host);
                        c->host = g_strdup(value);
-                       value = strrchr(c->host,':');
+                       value = strrchr(c->host, ':');
                        if (value) {
                                *value++ = '\0';
                                c->port = atoi(value);
@@ -398,7 +390,8 @@ static int credential_read(struct credential *c)
                } else if (!strcmp(key, "password")) {
                        gnome_keyring_memory_free(c->password);
                        c->password = gnome_keyring_memory_strdup(value);
-                       while (*value) *value++ = '\0';
+                       while (*value)
+                               *value++ = '\0';
                }
                /*
                 * Ignore other lines; we don't know what they mean, but
@@ -429,16 +422,16 @@ static void credential_write(const struct credential *c)
 static void usage(const char *name)
 {
        struct credential_operation const *try_op = credential_helper_ops;
-       const char *basename = strrchr(name,'/');
+       const char *basename = strrchr(name, '/');
 
        basename = (basename) ? basename + 1 : name;
        fprintf(stderr, "usage: %s <", basename);
        while (try_op->name) {
-               fprintf(stderr,"%s",(try_op++)->name);
+               fprintf(stderr, "%s", (try_op++)->name);
                if (try_op->name)
-                       fprintf(stderr,"%s","|");
+                       fprintf(stderr, "%s", "|");
        }
-       fprintf(stderr,"%s",">\n");
+       fprintf(stderr, "%s", ">\n");
 }
 
 int main(int argc, char *argv[])
@@ -446,7 +439,7 @@ int main(int argc, char *argv[])
        int ret = EXIT_SUCCESS;
 
        struct credential_operation const *try_op = credential_helper_ops;
-       struct credential                  cred   = CREDENTIAL_INIT;
+       struct credential cred = CREDENTIAL_INIT;
 
        if (!argv[1]) {
                usage(argv[0]);
index 7e345320ada69f1cbb909130d406cf08ce9ae130..332aba784b8810120df8e1ddfafeb034a55337a3 100755 (executable)
@@ -883,6 +883,16 @@ def main(args):
     global branches, peers
     global transports
 
+    marks = None
+    is_tmp = False
+    gitdir = os.environ.get('GIT_DIR', None)
+
+    if len(args) < 3:
+        die('Not enough arguments.')
+
+    if not gitdir:
+        die('GIT_DIR not set')
+
     alias = args[1]
     url = args[2]
 
@@ -891,7 +901,6 @@ def main(args):
     blob_marks = {}
     parsed_refs = {}
     files_cache = {}
-    marks = None
     branches = {}
     peers = {}
     transports = []
@@ -899,11 +908,8 @@ def main(args):
     if alias[5:] == url:
         is_tmp = True
         alias = hashlib.sha1(alias).hexdigest()
-    else:
-        is_tmp = False
 
     prefix = 'refs/bzr/%s' % alias
-    gitdir = os.environ['GIT_DIR']
     dirname = os.path.join(gitdir, 'bzr', alias)
 
     if not is_tmp:
index 30402d55323488f7c2bc6599bbc4a471f2120b33..eb89ef67798e70fd77eff341e3dda5385d3cde48 100755 (executable)
@@ -415,6 +415,9 @@ def get_repo(url, alias):
         local_path = os.path.join(dirname, 'clone')
         if not os.path.exists(local_path):
             hg.share(myui, shared_path, local_path, update=False)
+        else:
+            # make sure the shared path is always up-to-date
+            util.writefile(os.path.join(local_path, '.hg', 'sharedpath'), hg_path)
 
         repo = hg.repository(myui, local_path)
         try:
@@ -537,7 +540,7 @@ def export_ref(repo, name, kind, head):
 
         print "commit %s" % ref
         print "mark :%d" % (note_mark)
-        print "committer remote-hg <> %s" % (ptime.strftime('%s %z'))
+        print "committer remote-hg <> %d %s" % (ptime.time(), gittz(ptime.timezone))
         desc = "Notes for %s\n" % (name)
         print "data %d" % (len(desc))
         print desc
@@ -1164,6 +1167,16 @@ def main(args):
     global dry_run
     global notes, alias
 
+    marks = None
+    is_tmp = False
+    gitdir = os.environ.get('GIT_DIR', None)
+
+    if len(args) < 3:
+        die('Not enough arguments.')
+
+    if not gitdir:
+        die('GIT_DIR not set')
+
     alias = args[1]
     url = args[2]
     peer = None
@@ -1184,16 +1197,12 @@ def main(args):
     if alias[4:] == url:
         is_tmp = True
         alias = hashlib.sha1(alias).hexdigest()
-    else:
-        is_tmp = False
 
-    gitdir = os.environ['GIT_DIR']
     dirname = os.path.join(gitdir, 'hg', alias)
     branches = {}
     bmarks = {}
     blob_marks = {}
     parsed_refs = {}
-    marks = None
     parsed_tags = {}
     filenodes = {}
     fake_bmark = None
index 347e812923a20652b964db6b820f8f08d6c0795f..5d128a5da9a1afd3bf441f58e75d4f02a64509d5 100755 (executable)
@@ -337,6 +337,17 @@ test_expect_success 'remote cloning' '
        check gitrepo HEAD zero
 '
 
+test_expect_success 'moving remote clone' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       mv gitrepo gitrepo2 &&
+       cd gitrepo2 &&
+       git fetch
+       )
+'
+
 test_expect_success 'remote update bookmark' '
        test_when_finished "rm -rf gitrepo*" &&
 
@@ -444,6 +455,74 @@ test_expect_success 'remote new bookmark multiple branch head' '
 # cleanup previous stuff
 rm -rf hgrepo
 
+test_expect_success 'fetch special filenames' '
+       test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       echo test >> "æ rø" &&
+       hg add "æ rø" &&
+       echo test >> "ø~?" &&
+       hg add "ø~?" &&
+       hg commit -m add-utf-8 &&
+       echo test >> "æ rø" &&
+       hg commit -m test-utf-8 &&
+       hg rm "ø~?" &&
+       hg mv "æ rø" "ø~?" &&
+       hg commit -m hg-mv-utf-8
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git -c core.quotepath=false ls-files > ../actual
+       ) &&
+       echo "ø~?" > expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'push special filenames' '
+       test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" &&
+
+       mkdir -p tmp && cd tmp &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       echo one >> content &&
+       hg add content &&
+       hg commit -m one
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+
+       echo test >> "æ rø" &&
+       git add "æ rø" &&
+       git commit -m utf-8 &&
+
+       git push
+       ) &&
+
+       (cd hgrepo &&
+       hg update &&
+       hg manifest > ../actual
+       ) &&
+
+       printf "content\næ rø\n" > expected &&
+       test_cmp expected actual
+'
+
 setup_big_push () {
        (
        hg init hgrepo &&
index 53f5375b6ca3368de6647cf5edcd7fb4dec79657..b00b215031174a0f8ee3cd3d51c662d3cacc6b00 100644 (file)
@@ -11,7 +11,7 @@
 #include "progress.h"
 #include "csum-file.h"
 
-static void flush(struct sha1file *f, void *buf, unsigned int count)
+static void flush(struct sha1file *f, const void *buf, unsigned int count)
 {
        if (0 <= f->check_fd && count)  {
                unsigned char check_buffer[8192];
@@ -86,13 +86,13 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
        return fd;
 }
 
-int sha1write(struct sha1file *f, void *buf, unsigned int count)
+void sha1write(struct sha1file *f, const void *buf, unsigned int count)
 {
        while (count) {
                unsigned offset = f->offset;
                unsigned left = sizeof(f->buffer) - offset;
                unsigned nr = count > left ? left : count;
-               void *data;
+               const void *data;
 
                if (f->do_crc)
                        f->crc32 = crc32(f->crc32, buf, nr);
@@ -116,7 +116,6 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count)
                }
                f->offset = offset;
        }
-       return 0;
 }
 
 struct sha1file *sha1fd(int fd, const char *name)
index 3b540bdc21d2edf8d3d6fc2b5e52cc840aa19395..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 *, 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;
                }
index 00a8eefde9aeb96592f5ed6455dbf5d3b16747c4..33e5982a1c61a5726580f40df0a07dadc7e2aaf1 100644 (file)
@@ -183,54 +183,12 @@ static int queue_diff(struct diff_options *o,
 
 void diff_no_index(struct rev_info *revs,
                   int argc, const char **argv,
-                  int nongit, const char *prefix)
+                  const char *prefix)
 {
        int i, prefixlen;
-       int no_index = 0;
        unsigned deprecated_show_diff_q_option_used = 0;
        const char *paths[2];
 
-       /* Were we asked to do --no-index explicitly? */
-       for (i = 1; i < argc; i++) {
-               if (!strcmp(argv[i], "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(argv[i], "--no-index"))
-                       no_index = 1;
-               if (argv[i][0] != '-')
-                       break;
-       }
-
-       if (!no_index && !nongit) {
-               /*
-                * Inside a git repository, without --no-index.  Only
-                * when a path outside the repository is given,
-                * e.g. "git diff /var/tmp/[12]", or "git diff
-                * Makefile /var/tmp/Makefile", allow it to be used as
-                * a colourful "diff" replacement.
-                */
-               if ((argc != i + 2) ||
-                   (path_inside_repo(prefix, argv[i]) &&
-                    path_inside_repo(prefix, argv[i+1])))
-                       return;
-       }
-       if (argc != i + 2) {
-               if (!no_index) {
-                       /*
-                        * There was no --no-index and there were not two
-                        * paths. It is possible that the user intended
-                        * to do an inside-repository operation.
-                        */
-                       fprintf(stderr, "Not a git repository\n");
-                       fprintf(stderr,
-                               "To compare two paths outside a working tree:\n");
-               }
-               /* Give the usage message for non-repository usage and exit. */
-               usagef("git diff %s <path> <path>",
-                      no_index ? "--no-index" : "[--no-index]");
-       }
-
        diff_setup(&revs->diffopt);
        for (i = 1; i < argc - 2; ) {
                int j;
diff --git a/diff.c b/diff.c
index b79432b1ec97909e20eb76fceb5530b2f7be647f..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);
@@ -2899,11 +2902,16 @@ static void run_external_diff(const char *pgm,
                              struct diff_filespec *one,
                              struct diff_filespec *two,
                              const char *xfrm_msg,
-                             int complete_rewrite)
+                             int complete_rewrite,
+                             struct diff_options *o)
 {
        const char *spawn_arg[10];
        int retval;
        const char **arg = &spawn_arg[0];
+       struct diff_queue_struct *q = &diff_queued_diff;
+       const char *env[3] = { NULL };
+       char env_counter[50];
+       char env_total[50];
 
        if (one && two) {
                struct diff_tempfile *temp_one, *temp_two;
@@ -2928,7 +2936,14 @@ static void run_external_diff(const char *pgm,
        }
        *arg = NULL;
        fflush(NULL);
-       retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL);
+
+       env[0] = env_counter;
+       snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d",
+                ++o->diff_path_counter);
+       env[1] = env_total;
+       snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+
+       retval = run_command_v_opt_cd_env(spawn_arg, RUN_USING_SHELL, NULL, env);
        remove_tempfile();
        if (retval) {
                fprintf(stderr, "external diff died, stopping at %s.\n", name);
@@ -3042,7 +3057,7 @@ static void run_diff_cmd(const char *pgm,
 
        if (pgm) {
                run_external_diff(pgm, name, other, one, two, xfrm_msg,
-                                 complete_rewrite);
+                                 complete_rewrite, o);
                return;
        }
        if (one && two)
@@ -3207,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) {
@@ -3317,6 +3334,8 @@ void diff_setup_done(struct diff_options *options)
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
+
+       options->diff_path_counter = 0;
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
diff --git a/diff.h b/diff.h
index 1c05b3a6bec6ce90a97efe366614d23133026b00..ce123fa06fee6fb6e5e76d6a92ea8284a5bbbcf1 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -164,6 +164,8 @@ struct diff_options {
        diff_prefix_fn_t output_prefix;
        int output_prefix_length;
        void *output_prefix_data;
+
+       int diff_path_counter;
 };
 
 enum color_diff {
@@ -330,7 +332,7 @@ extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
 
 extern int diff_result_code(struct diff_options *, int);
 
-extern void diff_no_index(struct rev_info *, int, const char **, int, const char *);
+extern void diff_no_index(struct rev_info *, int, const char **, const char *);
 
 extern int index_differs_from(const char *def, int diff_flags);
 
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 b00ed95dba9c035ee812d3a2d6db2d13a1944a33..7ef36b9482ccc4111f2c888bf5ccbaa2421a5875 100755 (executable)
@@ -40,7 +40,8 @@ launch_merge_tool () {
        # the user with the real $MERGED name before launching $merge_tool.
        if should_prompt
        then
-               printf "\nViewing: '%s'\n" "$MERGED"
+               printf "\nViewing (%s/%s): '%s'\n" "$GIT_DIFF_PATH_COUNTER" \
+                       "$GIT_DIFF_PATH_TOTAL" "$MERGED"
                if use_ext_cmd
                then
                        printf "Launch '%s' [Y/n]: " \
index 97716b885f8d52be465552458374698c4c78a51b..f210d0a14707c57d66baa2d098d0d2efdaaaf72e 100755 (executable)
@@ -229,15 +229,7 @@ test true = "$rebase" && {
        test -n "$curr_branch" &&
        . git-parse-remote &&
        remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
-       oldremoteref="$(git rev-parse -q --verify "$remoteref")" &&
-       for reflog in $(git rev-list -g $remoteref 2>/dev/null)
-       do
-               if test "$reflog" = "$(git merge-base $reflog $curr_branch)"
-               then
-                       oldremoteref="$reflog"
-                       break
-               fi
-       done
+       oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch)
 }
 orig_head=$(git rev-parse -q --verify HEAD)
 git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1
index 226752fbff62f4f27da95f7d711c898503fd7148..7185dc84387d6e20299fdda86800372445b9318e 100755 (executable)
@@ -14,6 +14,7 @@ git-rebase --continue | --abort | --skip | --edit-todo
 v,verbose!         display a diffstat of what changed upstream
 q,quiet!           be quiet. implies --no-stat
 autostash!         automatically stash/stash pop before and after
+fork-point         use 'merge-base --fork-point' to refine upstream
 onto=!             rebase onto given branch instead of upstream
 p,preserve-merges! try to recreate merges instead of ignoring them
 s,strategy=!       use the given merge strategy
@@ -66,6 +67,7 @@ verbose=
 diffstat=
 test "$(git config --bool rebase.stat)" = true && diffstat=t
 autostash="$(git config --bool rebase.autostash || echo false)"
+fork_point=auto
 git_am_opt=
 rebase_root=
 force_rebase=
@@ -260,6 +262,12 @@ do
        --no-autosquash)
                autosquash=
                ;;
+       --fork-point)
+               fork_point=t
+               ;;
+       --no-fork-point)
+               fork_point=
+               ;;
        -M|-m)
                do_merge=t
                ;;
@@ -437,6 +445,8 @@ then
                        error_on_missing_default_upstream "rebase" "rebase" \
                                "against" "git rebase <branch>"
                fi
+
+               test "$fork_point" = auto && fork_point=t
                ;;
        *)      upstream_name="$1"
                shift
@@ -522,6 +532,15 @@ case "$#" in
        ;;
 esac
 
+if test "$fork_point" = t
+then
+       new_upstream=$(git merge-base --fork-point "$upstream_name" "$switch_to")
+       if test -n "$new_upstream"
+       then
+               upstream=$new_upstream
+       fi
+fi
+
 if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
 then
        stash_sha1=$(git stash create "autostash") ||
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 ca9e63be18f9333bf0f603ddb9ddd923588be0aa..605d01b25cbd9d796810f8aa810894a2c198da28 100644 (file)
@@ -44,14 +44,13 @@ static int need_large_offset(off_t offset, const struct pack_idx_option *opts)
  */
 const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects,
                           int nr_objects, const struct pack_idx_option *opts,
-                          unsigned char *sha1)
+                          const unsigned char *sha1)
 {
        struct sha1file *f;
        struct pack_idx_entry **sorted_by_sha, **list, **last;
        off_t last_obj_offset = 0;
        uint32_t array[256];
        int i, fd;
-       git_SHA_CTX ctx;
        uint32_t index_version;
 
        if (nr_objects) {
@@ -114,9 +113,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
        }
        sha1write(f, array, 256 * 4);
 
-       /* compute the SHA1 hash of sorted object names. */
-       git_SHA1_Init(&ctx);
-
        /*
         * Write the actual SHA1 entries..
         */
@@ -128,7 +124,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
                        sha1write(f, &offset, 4);
                }
                sha1write(f, obj->sha1, 20);
-               git_SHA1_Update(&ctx, obj->sha1, 20);
                if ((opts->flags & WRITE_IDX_STRICT) &&
                    (i && !hashcmp(list[-2]->sha1, obj->sha1)))
                        die("The same object %s appears twice in the pack",
@@ -178,7 +173,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
        sha1write(f, sha1, 20);
        sha1close(f, NULL, ((opts->flags & WRITE_IDX_VERIFY)
                            ? CSUM_CLOSE : CSUM_FSYNC));
-       git_SHA1_Final(sha1, &ctx);
        return index_name;
 }
 
@@ -189,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);
 }
 
diff --git a/pack.h b/pack.h
index aa6ee7d606f1d5336bff9a3067b44057c362e788..12d951659c16462cf2da6a43e2c4974b85e39712 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -76,7 +76,7 @@ struct pack_idx_entry {
 struct progress;
 typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*);
 
-extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, unsigned char *sha1);
+extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
 extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 extern int verify_pack_index(struct packed_git *);
 extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uint32_t);
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 b3bcd476daa7d4ab32c30c6fbd2f0a73ec9d0b4f..34e8af966c4a97bd3fafd5d9cfecbfb982f78897 100644 (file)
@@ -304,8 +304,12 @@ sub C {
        my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
        my $pbat = $self->ensure_path($dir, $deletions);
+       # workaround for a bug in svn serf backend (v1.8.5 and below):
+       # store third argument to ->add_file() in a local variable, to make it
+       # have the same lifetime as $fbat
+       my $upa = $self->url_path($m->{file_a});
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-                               $self->url_path($m->{file_a}), $self->{r});
+                               $upa, $self->{r});
        print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
        $self->chg_file($fbat, $m);
        $self->close_file($fbat,undef,$self->{pool});
@@ -323,8 +327,10 @@ sub R {
        my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
        my $pbat = $self->ensure_path($dir, $deletions);
+       # workaround for a bug in svn serf backend, see comment in C() above
+       my $upa = $self->url_path($m->{file_a});
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-                               $self->url_path($m->{file_a}), $self->{r});
+                               $upa, $self->{r});
        print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
        $self->apply_autoprops($file, $fbat);
        $self->chg_file($fbat, $m);
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 709a9cbd26fea972a6781581a0c345c8e95a7f6d..a89efab5b766ba9374c3a09656b2d56593884c8e 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -852,7 +852,7 @@ static int match_name_with_pattern(const char *key, const char *name,
        return ret;
 }
 
-static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
+int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
 {
        int i;
        int find_src = !query->src;
@@ -986,9 +986,9 @@ void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
        *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
 }
 
-static int count_refspec_match(const char *pattern,
-                              struct ref *refs,
-                              struct ref **matched_ref)
+int count_refspec_match(const char *pattern,
+                       struct ref *refs,
+                       struct ref **matched_ref)
 {
        int patlen = strlen(pattern);
        struct ref *matched_weak = NULL;
index c07eb9950a15600f7fde7faa60570ea5a558fb60..00c6a76ef803005698e14d7f31eb4acd22b35649 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -128,6 +128,7 @@ struct ref *alloc_ref(const char *name);
 struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
 void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *));
+extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref);
 int ref_compare_name(const void *, const void *);
 
 int check_ref_type(const struct ref *ref, int flags);
@@ -162,6 +163,7 @@ struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 
 void free_refspec(int nr_refspec, struct refspec *refspec);
 
+extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);
 char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
                     const char *name);
 
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 4e8dd8be6dcfeba1639a3490811636f9c6249c6c..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
 
-       return lim.rlim_cur;
-#elif defined(_SC_OPEN_MAX)
-       return sysconf(_SC_OPEN_MAX);
-#elif defined(OPEN_MAX)
+#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
+
+#ifdef OPEN_MAX
        return OPEN_MAX;
 #else
        return 1; /* see the caller ;-) */
@@ -2494,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;
@@ -2514,23 +2538,23 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
                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 {
@@ -2552,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;
 }
@@ -2644,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;
 
diff --git a/t/perf/p4001-diff-no-index.sh b/t/perf/p4001-diff-no-index.sh
new file mode 100755 (executable)
index 0000000..683be69
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description="Test diff --no-index performance"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+file1=$(git ls-files | tail -n 2 | head -1)
+file2=$(git ls-files | tail -n 1 | head -1)
+
+test_expect_success "empty files, so they take no time to diff" "
+       echo >$file1 &&
+       echo >$file2
+"
+
+test_perf "diff --no-index" "
+       git diff --no-index $file1 $file2 >/dev/null
+"
+
+test_done
index 633dc825ec49eb98be10c8f75545a6978c290231..a72e700ae434b07b08b48a68d4c0ebaa8173002b 100755 (executable)
@@ -85,6 +85,28 @@ $content"
                git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
        test_cmp expect actual
     '
+
+    test -z "$content" ||
+    test_expect_success "--batch without type ($type)" '
+       {
+               echo "$size" &&
+               maybe_remove_timestamp "$content" $no_ts
+       } >expect &&
+       echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full &&
+       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       test_cmp expect actual
+    '
+
+    test -z "$content" ||
+    test_expect_success "--batch without size ($type)" '
+       {
+               echo "$type" &&
+               maybe_remove_timestamp "$content" $no_ts
+       } >expect &&
+       echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full &&
+       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       test_cmp expect actual
+    '
 }
 
 hello_content="Hello World"
index f950c101287c2f05bbd70c1faffbcf2cda1edbee..613d9bfe1bbb153d1c2e647aee26a60e07a6af86 100755 (executable)
@@ -196,4 +196,28 @@ test_expect_success 'dotdot is not an empty set' '
        test_cmp expect actual
 '
 
+test_expect_success 'arg before dashdash must be a revision (missing)' '
+       test_must_fail git rev-parse foobar -- 2>stderr &&
+       test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (file)' '
+       >foobar &&
+       test_must_fail git rev-parse foobar -- 2>stderr &&
+       test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
+       >foobar &&
+       git update-ref refs/heads/foobar HEAD &&
+       {
+               # we do not want to use rev-parse here, because
+               # we are testing it
+               cat .git/refs/heads/foobar &&
+               printf "%s\n" --
+       } >expect &&
+       git rev-parse foobar -- >actual &&
+       test_cmp expect actual
+'
+
 test_done
index ebf93b0695dfac37fc276e1f07377d858eb3e1da..998503db12eb115e8e160937d456dfd9cb46311f 100755 (executable)
@@ -134,12 +134,14 @@ test_expect_success 'fail when upstream arg is missing and not configured' '
        test_must_fail git rebase
 '
 
-test_expect_success 'default to @{upstream} when upstream arg is missing' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
        git checkout -b default topic &&
        git config branch.default.remote . &&
        git config branch.default.merge refs/heads/master &&
        git rebase &&
-       test "$(git rev-parse default~1)" = "$(git rev-parse master)"
+       git rev-parse --verify master >expect &&
+       git rev-parse default~1 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'rebase -q is quiet' '
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 &&
index 8a309795c9c9654446d82c2e474e1ca08486e081..bcae35ac1c93aaf79032db5ee09fd2d3400101f5 100755 (executable)
@@ -193,6 +193,19 @@ test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
        GIT_EXTERNAL_DIFF=echo git diff
 '
 
+test_expect_success 'GIT_EXTERNAL_DIFF path counter/total' '
+       write_script external-diff.sh <<-\EOF &&
+       echo $GIT_DIFF_PATH_COUNTER of $GIT_DIFF_PATH_TOTAL >>counter.txt
+       EOF
+       >counter.txt &&
+       cat >expect <<-\EOF &&
+       1 of 2
+       2 of 2
+       EOF
+       GIT_EXTERNAL_DIFF=./external-diff.sh git diff &&
+       test_cmp expect counter.txt
+'
+
 test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
        touch file.ext &&
        git add file.ext &&
index 979e98398bebc21fe664ca4a19770e5b251bfc97..2ab3c487340569d7f2f8164776907ef30df2d415 100755 (executable)
@@ -29,4 +29,30 @@ test_expect_success 'git diff --no-index relative path outside repo' '
        )
 '
 
+test_expect_success 'git diff --no-index with broken index' '
+       (
+               cd repo &&
+               echo broken >.git/index &&
+               git diff --no-index a ../non/git/a
+       )
+'
+
+test_expect_success 'git diff outside repo with broken index' '
+       (
+               cd repo &&
+               git diff ../non/git/a ../non/git/b
+       )
+'
+
+test_expect_success 'git diff --no-index executed outside repo gives correct error message' '
+       (
+               GIT_CEILING_DIRECTORIES=$TRASH_DIRECTORY/non &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_must_fail git diff --no-index a 2>actual.err &&
+               echo "usage: git diff --no-index <path> <path>" >expect.err &&
+               test_cmp expect.err actual.err
+       )
+'
+
 test_done
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 fe82025d4a0dbae0cee3d0c3943617ede5b5c7a5..4bbb718751738c3e913c25160d0fff52d8350bba 100755 (executable)
@@ -174,11 +174,11 @@ test_expect_success \
 test_expect_success \
     '[index v1] 5) pack-objects happily reuses corrupted data' \
     'pack4=$(git pack-objects test-4 <obj-list) &&
-     test -f "test-4-${pack1}.pack"'
+     test -f "test-4-${pack4}.pack"'
 
 test_expect_success \
     '[index v1] 6) newly created pack is BAD !' \
-    'test_must_fail git verify-pack -v "test-4-${pack1}.pack"'
+    'test_must_fail git verify-pack -v "test-4-${pack4}.pack"'
 
 test_expect_success \
     '[index v2] 1) stream pack to repository' \
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 99c32d75391bb02fae779ad831879903abca5d25..926e7f6b979d4d1c69ecc9d280cd6de365627012 100755 (executable)
@@ -1126,6 +1126,81 @@ test_expect_success 'fetch follows tags by default' '
        test_cmp expect actual
 '
 
+test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" &&
+               git push dst master &&
+               git show-ref refs/heads/master |
+               sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/next &&
+               test_must_fail git show-ref refs/heads/master &&
+               git show-ref refs/remotes/src/master >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, it is not used as refmap' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config push.default matching &&
+               git push dst master &&
+               git show-ref refs/heads/master >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/next &&
+               git show-ref refs/heads/master >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, upstream mapping is used' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" &&
+               git config push.default upstream &&
+
+               git config branch.master.merge refs/heads/trunk &&
+               git config branch.master.remote dst &&
+
+               git push dst master &&
+               git show-ref refs/heads/master |
+               sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/master &&
+               test_must_fail git show-ref refs/heads/next &&
+               git show-ref refs/heads/trunk >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
 test_expect_success 'push does not follow tags by default' '
        mk_test testrepo heads/master &&
        rm -fr src dst &&
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) &&
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 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);
 }