Merge branch 'ah/doc-gitattributes-empty-index' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 10 Jul 2017 20:58:56 +0000 (13:58 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 10 Jul 2017 20:58:56 +0000 (13:58 -0700)
An example in documentation that does not work in multi worktree
configuration has been corrected.

* ah/doc-gitattributes-empty-index:
doc: do not use `rm .git/index` when normalizing line endings

33 files changed:
.github/CONTRIBUTING.md [new file with mode: 0644]
.github/PULL_REQUEST_TEMPLATE.md [new file with mode: 0644]
Documentation/RelNotes/2.13.2.txt
Documentation/git-reset.txt
Documentation/git-rm.txt
Documentation/pretty-formats.txt
GIT-VERSION-GEN
builtin/cat-file.c
builtin/diff-tree.c
builtin/diff.c
builtin/grep.c
builtin/log.c
builtin/name-rev.c
cache.h
contrib/diff-highlight/.gitignore [new file with mode: 0644]
contrib/diff-highlight/DiffHighlight.pm [new file with mode: 0644]
contrib/diff-highlight/Makefile
contrib/diff-highlight/README
contrib/diff-highlight/diff-highlight [deleted file]
contrib/diff-highlight/diff-highlight.perl [new file with mode: 0644]
git-stash.sh
mergetools/meld
po/de.po
revision.c
revision.h
sha1_name.c
t/t3200-branch.sh
t/t3903-stash.sh
t/t4063-diff-blobs.sh [new file with mode: 0755]
t/t4202-log.sh
t/t5100-mailinfo.sh
t/t5313-pack-bounds-checks.sh
tree-walk.c
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..64e605a
--- /dev/null
@@ -0,0 +1,19 @@
+## Contributing to Git
+
+Thanks for taking the time to contribute to Git! Please be advised that the
+Git community does not use github.com for their contributions. Instead, we use
+a mailing list (git@vger.kernel.org) for code submissions, code
+reviews, and bug reports.
+
+Nevertheless, you can use [submitGit](http://submitgit.herokuapp.com/) to
+conveniently send your Pull Requests commits to our mailing list.
+
+Please read ["A note from the maintainer"](https://git.kernel.org/pub/scm/git/git.git/plain/MaintNotes?h=todo)
+to learn how the Git project is managed, and how you can work with it.
+In addition, we highly recommend you to read [our submission guidelines](../Documentation/SubmittingPatches).
+
+If you prefer video, then [this talk](https://www.youtube.com/watch?v=Q7i_qQW__q4&feature=youtu.be&t=6m4s)
+might be useful to you as the presenter walks you through the contribution
+process by example.
+
+Your friendly Git community!
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644 (file)
index 0000000..adba13e
--- /dev/null
@@ -0,0 +1,7 @@
+Thanks for taking the time to contribute to Git! Please be advised that the
+Git community does not use github.com for their contributions. Instead, we use
+a mailing list (git@vger.kernel.org) for code submissions, code reviews, and
+bug reports. Nevertheless, you can use submitGit to conveniently send your Pull
+Requests commits to our mailing list.
+
+Please read the "guidelines for contributing" linked above!
index c8ba0fa16f4904c421c878b53163c8095abf7f17..8c2b20071ed2bb04789f0957a548db9c2bfad7d7 100644 (file)
@@ -34,4 +34,21 @@ Fixes since v2.13.1
  * "git pull --rebase --autostash" didn't auto-stash when the local history
    fast-forwards to the upstream.
 
+ * "git describe --contains" penalized light-weight tags so much that
+   they were almost never considered.  Instead, give them about the
+   same chance to be considered as an annotated tag that is the same
+   age as the underlying commit would.
+
+ * The result from "git diff" that compares two blobs, e.g. "git diff
+   $commit1:$path $commit2:$path", used to be shown with the full
+   object name as given on the command line, but it is more natural to
+   use the $path in the output and use it to look up .gitattributes.
+
+ * A flaky test has been corrected.
+
+ * Help contributors that visit us at GitHub.
+
+ * "git stash push <pathspec>" did not work from a subdirectory at all.
+   Bugfix for a topic in v2.13
+
 Also contains various documentation updates and code clean-ups.
index 8a21198d65c77c4a23f95d089ed1ce7f003dc0c6..70f3753e1e90f74bb41e8a9ce90dcf7dc4420192 100644 (file)
@@ -115,7 +115,7 @@ $ git pull git://info.example.com/ nitfol  <4>
 in these files are in good order.  You do not want to see them
 when you run "git diff", because you plan to work on other files
 and changes with these files are distracting.
-<2> Somebody asks you to pull, and the changes sounds worthy of merging.
+<2> Somebody asks you to pull, and the changes sound worthy of merging.
 <3> However, you already dirtied the index (i.e. your index does
 not match the HEAD commit).  But you know the pull you are going
 to make does not affect frotz.c or filfre.c, so you revert the
index f1efc116ebb88a3bc31898f6a18837f8b951ad09..8c87e8cdd772a42192f54a6a49789c6005eeb87d 100644 (file)
@@ -140,10 +140,11 @@ Only submodules using a gitfile (which means they were cloned
 with a Git version 1.7.8 or newer) will be removed from the work
 tree, as their repository lives inside the .git directory of the
 superproject. If a submodule (or one of those nested inside it)
-still uses a .git directory, `git rm` will fail - no matter if forced
-or not - to protect the submodule's history. If it exists the
-submodule.<name> section in the linkgit:gitmodules[5] file will also
-be removed and that file will be staged (unless --cached or -n are used).
+still uses a .git directory, `git rm` will move the submodules
+git directory into the superprojects git directory to protect
+the submodule's history. If it exists the submodule.<name> section
+in the linkgit:gitmodules[5] file will also be removed and that file
+will be staged (unless --cached or -n are used).
 
 A submodule is considered up-to-date when the HEAD is the same as
 recorded in the index, no tracked files are modified and no untracked
index 38040e95b5cdb137fd3575076e5f69d393c19280..a48d267e26c1098e003ed6342b33c6e43d57a884 100644 (file)
@@ -213,8 +213,8 @@ If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
 is inserted immediately before the expansion if and only if the
 placeholder expands to a non-empty string.
 
-If you add a `-` (minus sign) after '%' of a placeholder, line-feeds that
-immediately precede the expansion are deleted if and only if the
+If you add a `-` (minus sign) after '%' of a placeholder, all consecutive
+line-feeds immediately preceding the expansion are deleted if and only if the
 placeholder expands to an empty string.
 
 If you add a ` ` (space) after '%' of a placeholder, a space
index 5b6722840e43a4fe331d588bf9c2a39de6cb33ea..c26b01f60bf23a3d9c9bae5d40c0abc0d985ccb0 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.13.1
+DEF_VER=v2.13.2
 
 LF='
 '
index 9af863e7915d21aee7af3cb4d01ebbad2efbcb59..73c81f0cb1ae34169667adc7ee7fc246468e08a4 100644 (file)
@@ -61,7 +61,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
        if (unknown_type)
                flags |= LOOKUP_UNKNOWN_OBJECT;
 
-       if (get_sha1_with_context(obj_name, 0, oid.hash, &obj_context))
+       if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
+                                 oid.hash, &obj_context))
                die("Not a valid object name %s", obj_name);
 
        if (!path)
@@ -166,6 +167,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
 
        write_or_die(1, buf, size);
        free(buf);
+       free(obj_context.path);
        return 0;
 }
 
index 326f88b6576d6c02e9a0bcecb5a0b402bb257372..a570fea55b760809ac276cedbed8de9f7f95e44b 100644 (file)
@@ -128,9 +128,11 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        }
 
        /*
-        * NOTE! We expect "a ^b" to be equal to "a..b", so we
-        * reverse the order of the objects if the second one
-        * is marked UNINTERESTING.
+        * NOTE!  We expect "a..b" to expand to "^a b" but it is
+        * perfectly valid for revision range parser to yield "b ^a",
+        * which means the same thing. If we get the latter, i.e. the
+        * second one is marked UNINTERESTING, we recover the original
+        * order the user gave, i.e. "a..b", by swapping the trees.
         */
        nr_sha1 = opt->pending.nr;
        switch (nr_sha1) {
index d184aafab9e353279125f9f7f07b63c3780bcba1..5e7c6428c91203fa1f3fb0c7790364987a623fc0 100644 (file)
 #define DIFF_NO_INDEX_EXPLICIT 1
 #define DIFF_NO_INDEX_IMPLICIT 2
 
-struct blobinfo {
-       struct object_id oid;
-       const char *name;
-       unsigned mode;
-};
-
 static const char builtin_diff_usage[] =
 "git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
 
+static const char *blob_path(struct object_array_entry *entry)
+{
+       return entry->path ? entry->path : entry->name;
+}
+
 static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
                         const struct object_id *old_oid,
                         const struct object_id *new_oid,
                         int old_oid_valid,
                         int new_oid_valid,
-                        const char *old_name,
-                        const char *new_name)
+                        const char *old_path,
+                        const char *new_path)
 {
        struct diff_filespec *one, *two;
 
@@ -47,16 +46,16 @@ static void stuff_change(struct diff_options *opt,
        if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
                SWAP(old_mode, new_mode);
                SWAP(old_oid, new_oid);
-               SWAP(old_name, new_name);
+               SWAP(old_path, new_path);
        }
 
        if (opt->prefix &&
-           (strncmp(old_name, opt->prefix, opt->prefix_length) ||
-            strncmp(new_name, opt->prefix, opt->prefix_length)))
+           (strncmp(old_path, opt->prefix, opt->prefix_length) ||
+            strncmp(new_path, opt->prefix, opt->prefix_length)))
                return;
 
-       one = alloc_filespec(old_name);
-       two = alloc_filespec(new_name);
+       one = alloc_filespec(old_path);
+       two = alloc_filespec(new_path);
        fill_filespec(one, old_oid->hash, old_oid_valid, old_mode);
        fill_filespec(two, new_oid->hash, new_oid_valid, new_mode);
 
@@ -65,7 +64,7 @@ static void stuff_change(struct diff_options *opt,
 
 static int builtin_diff_b_f(struct rev_info *revs,
                            int argc, const char **argv,
-                           struct blobinfo *blob)
+                           struct object_array_entry **blob)
 {
        /* Blob vs file in the working tree*/
        struct stat st;
@@ -84,14 +83,15 @@ static int builtin_diff_b_f(struct rev_info *revs,
 
        diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
 
-       if (blob[0].mode == S_IFINVALID)
-               blob[0].mode = canon_mode(st.st_mode);
+       if (blob[0]->mode == S_IFINVALID)
+               blob[0]->mode = canon_mode(st.st_mode);
 
        stuff_change(&revs->diffopt,
-                    blob[0].mode, canon_mode(st.st_mode),
-                    &blob[0].oid, &null_oid,
+                    blob[0]->mode, canon_mode(st.st_mode),
+                    &blob[0]->item->oid, &null_oid,
                     1, 0,
-                    path, path);
+                    blob[0]->path ? blob[0]->path : path,
+                    path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
@@ -99,24 +99,24 @@ static int builtin_diff_b_f(struct rev_info *revs,
 
 static int builtin_diff_blobs(struct rev_info *revs,
                              int argc, const char **argv,
-                             struct blobinfo *blob)
+                             struct object_array_entry **blob)
 {
        unsigned mode = canon_mode(S_IFREG | 0644);
 
        if (argc > 1)
                usage(builtin_diff_usage);
 
-       if (blob[0].mode == S_IFINVALID)
-               blob[0].mode = mode;
+       if (blob[0]->mode == S_IFINVALID)
+               blob[0]->mode = mode;
 
-       if (blob[1].mode == S_IFINVALID)
-               blob[1].mode = mode;
+       if (blob[1]->mode == S_IFINVALID)
+               blob[1]->mode = mode;
 
        stuff_change(&revs->diffopt,
-                    blob[0].mode, blob[1].mode,
-                    &blob[0].oid, &blob[1].oid,
+                    blob[0]->mode, blob[1]->mode,
+                    &blob[0]->item->oid, &blob[1]->item->oid,
                     1, 1,
-                    blob[0].name, blob[1].name);
+                    blob_path(blob[0]), blob_path(blob[1]));
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
@@ -259,7 +259,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
        struct object_array ent = OBJECT_ARRAY_INIT;
        int blobs = 0, paths = 0;
-       struct blobinfo blob[2];
+       struct object_array_entry *blob[2];
        int nongit = 0, no_index = 0;
        int result = 0;
 
@@ -408,9 +408,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                } else if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
                                die(_("more than two blobs given: '%s'"), name);
-                       hashcpy(blob[blobs].oid.hash, obj->oid.hash);
-                       blob[blobs].name = name;
-                       blob[blobs].mode = entry->mode;
+                       blob[blobs] = entry;
                        blobs++;
 
                } else {
index 3ffb5b4e8176bbcd1ecf2e4ae2dc84aee968cd22..254c1c7849152884d9abd0b603f1c75f87e085b5 100644 (file)
@@ -1190,7 +1190,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        break;
                }
 
-               if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
+               if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH,
+                                         oid.hash, &oc)) {
                        if (seen_dashdash)
                                die(_("unable to resolve revision: %s"), arg);
                        break;
@@ -1200,6 +1201,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                if (!seen_dashdash)
                        verify_non_filename(prefix, arg);
                add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
+               free(oc.path);
        }
 
        /*
index ec3258368ca96eb9120c64994b8132a6e00b5d24..57ce470f5094c238ea4d5390f358b5cdd9499a56 100644 (file)
@@ -483,16 +483,20 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
            !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
                return stream_blob_to_fd(1, oid, NULL, 0);
 
-       if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context))
+       if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
+                                 oidc.hash, &obj_context))
                die(_("Not a valid object name %s"), obj_name);
-       if (!obj_context.path[0] ||
-           !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size))
+       if (!obj_context.path ||
+           !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
+               free(obj_context.path);
                return stream_blob_to_fd(1, oid, NULL, 0);
+       }
 
        if (!buf)
                die(_("git show %s: bad file"), obj_name);
 
        write_or_die(1, buf, size);
+       free(obj_context.path);
        return 0;
 }
 
index e7a3fe7ee70759c34b9de12177078a55f27bfb9a..1767af750dde7f5184b4df25067f9f616c1918b3 100644 (file)
@@ -13,6 +13,7 @@ typedef struct rev_name {
        unsigned long taggerdate;
        int generation;
        int distance;
+       int from_tag;
 } rev_name;
 
 static long cutoff = LONG_MAX;
@@ -20,9 +21,47 @@ static long cutoff = LONG_MAX;
 /* How many generations are maximally preferred over _one_ merge traversal? */
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
+static int is_better_name(struct rev_name *name,
+                         const char *tip_name,
+                         unsigned long taggerdate,
+                         int generation,
+                         int distance,
+                         int from_tag)
+{
+       /*
+        * When comparing names based on tags, prefer names
+        * based on the older tag, even if it is farther away.
+        */
+       if (from_tag && name->from_tag)
+               return (name->taggerdate > taggerdate ||
+                       (name->taggerdate == taggerdate &&
+                        name->distance > distance));
+
+       /*
+        * We know that at least one of them is a non-tag at this point.
+        * favor a tag over a non-tag.
+        */
+       if (name->from_tag != from_tag)
+               return from_tag;
+
+       /*
+        * We are now looking at two non-tags.  Tiebreak to favor
+        * shorter hops.
+        */
+       if (name->distance != distance)
+               return name->distance > distance;
+
+       /* ... or tiebreak to favor older date */
+       if (name->taggerdate != taggerdate)
+               return name->taggerdate > taggerdate;
+
+       /* keep the current one if we cannot decide */
+       return 0;
+}
+
 static void name_rev(struct commit *commit,
                const char *tip_name, unsigned long taggerdate,
-               int generation, int distance,
+               int generation, int distance, int from_tag,
                int deref)
 {
        struct rev_name *name = (struct rev_name *)commit->util;
@@ -46,14 +85,14 @@ static void name_rev(struct commit *commit,
                name = xmalloc(sizeof(rev_name));
                commit->util = name;
                goto copy_data;
-       } else if (name->taggerdate > taggerdate ||
-                       (name->taggerdate == taggerdate &&
-                        name->distance > distance)) {
+       } else if (is_better_name(name, tip_name, taggerdate,
+                                 generation, distance, from_tag)) {
 copy_data:
                name->tip_name = tip_name;
                name->taggerdate = taggerdate;
                name->generation = generation;
                name->distance = distance;
+               name->from_tag = from_tag;
        } else {
                free(to_free);
                return;
@@ -75,10 +114,12 @@ static void name_rev(struct commit *commit,
                                                   parent_number);
 
                        name_rev(parents->item, new_name, taggerdate, 0,
-                               distance + MERGE_TRAVERSAL_WEIGHT, 0);
+                                distance + MERGE_TRAVERSAL_WEIGHT,
+                                from_tag, 0);
                } else {
                        name_rev(parents->item, tip_name, taggerdate,
-                               generation + 1, distance + 1, 0);
+                                generation + 1, distance + 1,
+                                from_tag, 0);
                }
        }
 }
@@ -209,9 +250,13 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
        }
        if (o && o->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)o;
+               int from_tag = starts_with(path, "refs/tags/");
 
+               if (taggerdate == ULONG_MAX)
+                       taggerdate = ((struct commit *)o)->date;
                path = name_ref_abbrev(path, can_abbreviate_output);
-               name_rev(commit, xstrdup(path), taggerdate, 0, 0, deref);
+               name_rev(commit, xstrdup(path), taggerdate, 0, 0,
+                        from_tag, deref);
        }
        return 0;
 }
diff --git a/cache.h b/cache.h
index e1f0e182ad011d5a3b6912f48b597dcc6d1070b0..52b91f5b64146e1b6c7c93aa5054a7aca6303b42 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1333,13 +1333,18 @@ static inline int hex2chr(const char *s)
 
 struct object_context {
        unsigned char tree[20];
-       char path[PATH_MAX];
        unsigned mode;
        /*
         * symlink_path is only used by get_tree_entry_follow_symlinks,
         * and only for symlinks that point outside the repository.
         */
        struct strbuf symlink_path;
+       /*
+        * If GET_SHA1_RECORD_PATH is set, this will record path (if any)
+        * found when resolving the name. The caller is responsible for
+        * releasing the memory.
+        */
+       char *path;
 };
 
 #define GET_SHA1_QUIETLY           01
@@ -1349,6 +1354,7 @@ struct object_context {
 #define GET_SHA1_TREEISH          020
 #define GET_SHA1_BLOB             040
 #define GET_SHA1_FOLLOW_SYMLINKS 0100
+#define GET_SHA1_RECORD_PATH     0200
 #define GET_SHA1_ONLY_TO_DIE    04000
 
 #define GET_SHA1_DISAMBIGUATORS \
@@ -1363,7 +1369,7 @@ extern int get_sha1_tree(const char *str, unsigned char *sha1);
 extern int get_sha1_treeish(const char *str, unsigned char *sha1);
 extern int get_sha1_blob(const char *str, unsigned char *sha1);
 extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
-extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
+extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc);
 
 extern int get_oid(const char *str, struct object_id *oid);
 
diff --git a/contrib/diff-highlight/.gitignore b/contrib/diff-highlight/.gitignore
new file mode 100644 (file)
index 0000000..c074548
--- /dev/null
@@ -0,0 +1,2 @@
+shebang.perl
+diff-highlight
diff --git a/contrib/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm
new file mode 100644 (file)
index 0000000..663992e
--- /dev/null
@@ -0,0 +1,233 @@
+package DiffHighlight;
+
+use 5.008;
+use warnings FATAL => 'all';
+use strict;
+
+# Highlight by reversing foreground and background. You could do
+# other things like bold or underline if you prefer.
+my @OLD_HIGHLIGHT = (
+       color_config('color.diff-highlight.oldnormal'),
+       color_config('color.diff-highlight.oldhighlight', "\x1b[7m"),
+       color_config('color.diff-highlight.oldreset', "\x1b[27m")
+);
+my @NEW_HIGHLIGHT = (
+       color_config('color.diff-highlight.newnormal', $OLD_HIGHLIGHT[0]),
+       color_config('color.diff-highlight.newhighlight', $OLD_HIGHLIGHT[1]),
+       color_config('color.diff-highlight.newreset', $OLD_HIGHLIGHT[2])
+);
+
+my $RESET = "\x1b[m";
+my $COLOR = qr/\x1b\[[0-9;]*m/;
+my $BORING = qr/$COLOR|\s/;
+
+# The patch portion of git log -p --graph should only ever have preceding | and
+# not / or \ as merge history only shows up on the commit line.
+my $GRAPH = qr/$COLOR?\|$COLOR?\s+/;
+
+my @removed;
+my @added;
+my $in_hunk;
+
+our $line_cb = sub { print @_ };
+our $flush_cb = sub { local $| = 1 };
+
+sub handle_line {
+       local $_ = shift;
+
+       if (!$in_hunk) {
+               $line_cb->($_);
+               $in_hunk = /^$GRAPH*$COLOR*\@\@ /;
+       }
+       elsif (/^$GRAPH*$COLOR*-/) {
+               push @removed, $_;
+       }
+       elsif (/^$GRAPH*$COLOR*\+/) {
+               push @added, $_;
+       }
+       else {
+               show_hunk(\@removed, \@added);
+               @removed = ();
+               @added = ();
+
+               $line_cb->($_);
+               $in_hunk = /^$GRAPH*$COLOR*[\@ ]/;
+       }
+
+       # Most of the time there is enough output to keep things streaming,
+       # but for something like "git log -Sfoo", you can get one early
+       # commit and then many seconds of nothing. We want to show
+       # that one commit as soon as possible.
+       #
+       # Since we can receive arbitrary input, there's no optimal
+       # place to flush. Flushing on a blank line is a heuristic that
+       # happens to match git-log output.
+       if (!length) {
+               $flush_cb->();
+       }
+}
+
+sub flush {
+       # Flush any queued hunk (this can happen when there is no trailing
+       # context in the final diff of the input).
+       show_hunk(\@removed, \@added);
+}
+
+sub highlight_stdin {
+       while (<STDIN>) {
+               handle_line($_);
+       }
+       flush();
+}
+
+# Ideally we would feed the default as a human-readable color to
+# git-config as the fallback value. But diff-highlight does
+# not otherwise depend on git at all, and there are reports
+# of it being used in other settings. Let's handle our own
+# fallback, which means we will work even if git can't be run.
+sub color_config {
+       my ($key, $default) = @_;
+       my $s = `git config --get-color $key 2>/dev/null`;
+       return length($s) ? $s : $default;
+}
+
+sub show_hunk {
+       my ($a, $b) = @_;
+
+       # If one side is empty, then there is nothing to compare or highlight.
+       if (!@$a || !@$b) {
+               $line_cb->(@$a, @$b);
+               return;
+       }
+
+       # If we have mismatched numbers of lines on each side, we could try to
+       # be clever and match up similar lines. But for now we are simple and
+       # stupid, and only handle multi-line hunks that remove and add the same
+       # number of lines.
+       if (@$a != @$b) {
+               $line_cb->(@$a, @$b);
+               return;
+       }
+
+       my @queue;
+       for (my $i = 0; $i < @$a; $i++) {
+               my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]);
+               $line_cb->($rm);
+               push @queue, $add;
+       }
+       $line_cb->(@queue);
+}
+
+sub highlight_pair {
+       my @a = split_line(shift);
+       my @b = split_line(shift);
+
+       # Find common prefix, taking care to skip any ansi
+       # color codes.
+       my $seen_plusminus;
+       my ($pa, $pb) = (0, 0);
+       while ($pa < @a && $pb < @b) {
+               if ($a[$pa] =~ /$COLOR/) {
+                       $pa++;
+               }
+               elsif ($b[$pb] =~ /$COLOR/) {
+                       $pb++;
+               }
+               elsif ($a[$pa] eq $b[$pb]) {
+                       $pa++;
+                       $pb++;
+               }
+               elsif (!$seen_plusminus && $a[$pa] eq '-' && $b[$pb] eq '+') {
+                       $seen_plusminus = 1;
+                       $pa++;
+                       $pb++;
+               }
+               else {
+                       last;
+               }
+       }
+
+       # Find common suffix, ignoring colors.
+       my ($sa, $sb) = ($#a, $#b);
+       while ($sa >= $pa && $sb >= $pb) {
+               if ($a[$sa] =~ /$COLOR/) {
+                       $sa--;
+               }
+               elsif ($b[$sb] =~ /$COLOR/) {
+                       $sb--;
+               }
+               elsif ($a[$sa] eq $b[$sb]) {
+                       $sa--;
+                       $sb--;
+               }
+               else {
+                       last;
+               }
+       }
+
+       if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
+               return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT),
+                      highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT);
+       }
+       else {
+               return join('', @a),
+                      join('', @b);
+       }
+}
+
+# we split either by $COLOR or by character. This has the side effect of
+# leaving in graph cruft. It works because the graph cruft does not contain "-"
+# or "+"
+sub split_line {
+       local $_ = shift;
+       return utf8::decode($_) ?
+               map { utf8::encode($_); $_ }
+                       map { /$COLOR/ ? $_ : (split //) }
+                       split /($COLOR+)/ :
+               map { /$COLOR/ ? $_ : (split //) }
+               split /($COLOR+)/;
+}
+
+sub highlight_line {
+       my ($line, $prefix, $suffix, $theme) = @_;
+
+       my $start = join('', @{$line}[0..($prefix-1)]);
+       my $mid = join('', @{$line}[$prefix..$suffix]);
+       my $end = join('', @{$line}[($suffix+1)..$#$line]);
+
+       # If we have a "normal" color specified, then take over the whole line.
+       # Otherwise, we try to just manipulate the highlighted bits.
+       if (defined $theme->[0]) {
+               s/$COLOR//g for ($start, $mid, $end);
+               chomp $end;
+               return join('',
+                       $theme->[0], $start, $RESET,
+                       $theme->[1], $mid, $RESET,
+                       $theme->[0], $end, $RESET,
+                       "\n"
+               );
+       } else {
+               return join('',
+                       $start,
+                       $theme->[1], $mid, $theme->[2],
+                       $end
+               );
+       }
+}
+
+# Pairs are interesting to highlight only if we are going to end up
+# highlighting a subset (i.e., not the whole line). Otherwise, the highlighting
+# is just useless noise. We can detect this by finding either a matching prefix
+# or suffix (disregarding boring bits like whitespace and colorization).
+sub is_pair_interesting {
+       my ($a, $pa, $sa, $b, $pb, $sb) = @_;
+       my $prefix_a = join('', @$a[0..($pa-1)]);
+       my $prefix_b = join('', @$b[0..($pb-1)]);
+       my $suffix_a = join('', @$a[($sa+1)..$#$a]);
+       my $suffix_b = join('', @$b[($sb+1)..$#$b]);
+
+       return $prefix_a !~ /^$GRAPH*$COLOR*-$BORING*$/ ||
+              $prefix_b !~ /^$GRAPH*$COLOR*\+$BORING*$/ ||
+              $suffix_a !~ /^$BORING*$/ ||
+              $suffix_b !~ /^$BORING*$/;
+}
index 9018724524ab7868bb015696e301396f1f9b5667..fbf5c582496eece56b29a91a2f3085716f4b0e9f 100644 (file)
@@ -1,5 +1,20 @@
-# nothing to build
-all:
+all: diff-highlight
 
-test:
+PERL_PATH = /usr/bin/perl
+-include ../../config.mak
+
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+
+diff-highlight: shebang.perl DiffHighlight.pm diff-highlight.perl
+       cat $^ >$@+
+       chmod +x $@+
+       mv $@+ $@
+
+shebang.perl: FORCE
+       @echo '#!$(PERL_PATH_SQ)' >$@+
+       @cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@
+
+test: all
        $(MAKE) -C t
+
+.PHONY: FORCE
index 836b97a73075a51ef8bc66ba080085a7de4c8f32..d4c23431752063c7b455d429321264a4bd6becf7 100644 (file)
@@ -99,6 +99,36 @@ newHighlight = "black #aaffaa"
 ---------------------------------------------
 
 
+Using diff-highlight as a module
+--------------------------------
+
+If you want to pre- or post- process the highlighted lines as part of
+another perl script, you can use the DiffHighlight module. You can
+either "require" it or just cat the module together with your script (to
+avoid run-time dependencies).
+
+Your script may set up one or more of the following variables:
+
+  - $DiffHighlight::line_cb - this should point to a function which is
+    called whenever DiffHighlight has lines (which may contain
+    highlights) to output. The default function prints each line to
+    stdout. Note that the function may be called with multiple lines.
+
+  - $DiffHighlight::flush_cb - this should point to a function which
+    flushes the output (because DiffHighlight believes it has completed
+    processing a logical chunk of input). The default function flushes
+    stdout.
+
+The script may then feed lines, one at a time, to DiffHighlight::handle_line().
+When lines are done processing, they will be fed to $line_cb. Note that
+DiffHighlight may queue up many input lines (to analyze a whole hunk)
+before calling $line_cb. After providing all lines, call
+DiffHighlight::flush() to flush any unprocessed lines.
+
+If you just want to process stdin, DiffHighlight::highlight_stdin()
+is a convenience helper which will loop and flush for you.
+
+
 Bugs
 ----
 
diff --git a/contrib/diff-highlight/diff-highlight b/contrib/diff-highlight/diff-highlight
deleted file mode 100755 (executable)
index 81bd804..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-#!/usr/bin/perl
-
-use 5.008;
-use warnings FATAL => 'all';
-use strict;
-
-# Highlight by reversing foreground and background. You could do
-# other things like bold or underline if you prefer.
-my @OLD_HIGHLIGHT = (
-       color_config('color.diff-highlight.oldnormal'),
-       color_config('color.diff-highlight.oldhighlight', "\x1b[7m"),
-       color_config('color.diff-highlight.oldreset', "\x1b[27m")
-);
-my @NEW_HIGHLIGHT = (
-       color_config('color.diff-highlight.newnormal', $OLD_HIGHLIGHT[0]),
-       color_config('color.diff-highlight.newhighlight', $OLD_HIGHLIGHT[1]),
-       color_config('color.diff-highlight.newreset', $OLD_HIGHLIGHT[2])
-);
-
-my $RESET = "\x1b[m";
-my $COLOR = qr/\x1b\[[0-9;]*m/;
-my $BORING = qr/$COLOR|\s/;
-
-# The patch portion of git log -p --graph should only ever have preceding | and
-# not / or \ as merge history only shows up on the commit line.
-my $GRAPH = qr/$COLOR?\|$COLOR?\s+/;
-
-my @removed;
-my @added;
-my $in_hunk;
-
-# Some scripts may not realize that SIGPIPE is being ignored when launching the
-# pager--for instance scripts written in Python.
-$SIG{PIPE} = 'DEFAULT';
-
-while (<>) {
-       if (!$in_hunk) {
-               print;
-               $in_hunk = /^$GRAPH*$COLOR*\@\@ /;
-       }
-       elsif (/^$GRAPH*$COLOR*-/) {
-               push @removed, $_;
-       }
-       elsif (/^$GRAPH*$COLOR*\+/) {
-               push @added, $_;
-       }
-       else {
-               show_hunk(\@removed, \@added);
-               @removed = ();
-               @added = ();
-
-               print;
-               $in_hunk = /^$GRAPH*$COLOR*[\@ ]/;
-       }
-
-       # Most of the time there is enough output to keep things streaming,
-       # but for something like "git log -Sfoo", you can get one early
-       # commit and then many seconds of nothing. We want to show
-       # that one commit as soon as possible.
-       #
-       # Since we can receive arbitrary input, there's no optimal
-       # place to flush. Flushing on a blank line is a heuristic that
-       # happens to match git-log output.
-       if (!length) {
-               local $| = 1;
-       }
-}
-
-# Flush any queued hunk (this can happen when there is no trailing context in
-# the final diff of the input).
-show_hunk(\@removed, \@added);
-
-exit 0;
-
-# Ideally we would feed the default as a human-readable color to
-# git-config as the fallback value. But diff-highlight does
-# not otherwise depend on git at all, and there are reports
-# of it being used in other settings. Let's handle our own
-# fallback, which means we will work even if git can't be run.
-sub color_config {
-       my ($key, $default) = @_;
-       my $s = `git config --get-color $key 2>/dev/null`;
-       return length($s) ? $s : $default;
-}
-
-sub show_hunk {
-       my ($a, $b) = @_;
-
-       # If one side is empty, then there is nothing to compare or highlight.
-       if (!@$a || !@$b) {
-               print @$a, @$b;
-               return;
-       }
-
-       # If we have mismatched numbers of lines on each side, we could try to
-       # be clever and match up similar lines. But for now we are simple and
-       # stupid, and only handle multi-line hunks that remove and add the same
-       # number of lines.
-       if (@$a != @$b) {
-               print @$a, @$b;
-               return;
-       }
-
-       my @queue;
-       for (my $i = 0; $i < @$a; $i++) {
-               my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]);
-               print $rm;
-               push @queue, $add;
-       }
-       print @queue;
-}
-
-sub highlight_pair {
-       my @a = split_line(shift);
-       my @b = split_line(shift);
-
-       # Find common prefix, taking care to skip any ansi
-       # color codes.
-       my $seen_plusminus;
-       my ($pa, $pb) = (0, 0);
-       while ($pa < @a && $pb < @b) {
-               if ($a[$pa] =~ /$COLOR/) {
-                       $pa++;
-               }
-               elsif ($b[$pb] =~ /$COLOR/) {
-                       $pb++;
-               }
-               elsif ($a[$pa] eq $b[$pb]) {
-                       $pa++;
-                       $pb++;
-               }
-               elsif (!$seen_plusminus && $a[$pa] eq '-' && $b[$pb] eq '+') {
-                       $seen_plusminus = 1;
-                       $pa++;
-                       $pb++;
-               }
-               else {
-                       last;
-               }
-       }
-
-       # Find common suffix, ignoring colors.
-       my ($sa, $sb) = ($#a, $#b);
-       while ($sa >= $pa && $sb >= $pb) {
-               if ($a[$sa] =~ /$COLOR/) {
-                       $sa--;
-               }
-               elsif ($b[$sb] =~ /$COLOR/) {
-                       $sb--;
-               }
-               elsif ($a[$sa] eq $b[$sb]) {
-                       $sa--;
-                       $sb--;
-               }
-               else {
-                       last;
-               }
-       }
-
-       if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
-               return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT),
-                      highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT);
-       }
-       else {
-               return join('', @a),
-                      join('', @b);
-       }
-}
-
-# we split either by $COLOR or by character. This has the side effect of
-# leaving in graph cruft. It works because the graph cruft does not contain "-"
-# or "+"
-sub split_line {
-       local $_ = shift;
-       return utf8::decode($_) ?
-               map { utf8::encode($_); $_ }
-                       map { /$COLOR/ ? $_ : (split //) }
-                       split /($COLOR+)/ :
-               map { /$COLOR/ ? $_ : (split //) }
-               split /($COLOR+)/;
-}
-
-sub highlight_line {
-       my ($line, $prefix, $suffix, $theme) = @_;
-
-       my $start = join('', @{$line}[0..($prefix-1)]);
-       my $mid = join('', @{$line}[$prefix..$suffix]);
-       my $end = join('', @{$line}[($suffix+1)..$#$line]);
-
-       # If we have a "normal" color specified, then take over the whole line.
-       # Otherwise, we try to just manipulate the highlighted bits.
-       if (defined $theme->[0]) {
-               s/$COLOR//g for ($start, $mid, $end);
-               chomp $end;
-               return join('',
-                       $theme->[0], $start, $RESET,
-                       $theme->[1], $mid, $RESET,
-                       $theme->[0], $end, $RESET,
-                       "\n"
-               );
-       } else {
-               return join('',
-                       $start,
-                       $theme->[1], $mid, $theme->[2],
-                       $end
-               );
-       }
-}
-
-# Pairs are interesting to highlight only if we are going to end up
-# highlighting a subset (i.e., not the whole line). Otherwise, the highlighting
-# is just useless noise. We can detect this by finding either a matching prefix
-# or suffix (disregarding boring bits like whitespace and colorization).
-sub is_pair_interesting {
-       my ($a, $pa, $sa, $b, $pb, $sb) = @_;
-       my $prefix_a = join('', @$a[0..($pa-1)]);
-       my $prefix_b = join('', @$b[0..($pb-1)]);
-       my $suffix_a = join('', @$a[($sa+1)..$#$a]);
-       my $suffix_b = join('', @$b[($sb+1)..$#$b]);
-
-       return $prefix_a !~ /^$GRAPH*$COLOR*-$BORING*$/ ||
-              $prefix_b !~ /^$GRAPH*$COLOR*\+$BORING*$/ ||
-              $suffix_a !~ /^$BORING*$/ ||
-              $suffix_b !~ /^$BORING*$/;
-}
diff --git a/contrib/diff-highlight/diff-highlight.perl b/contrib/diff-highlight/diff-highlight.perl
new file mode 100644 (file)
index 0000000..9b3e9c1
--- /dev/null
@@ -0,0 +1,8 @@
+package main;
+
+# Some scripts may not realize that SIGPIPE is being ignored when launching the
+# pager--for instance scripts written in Python.
+$SIG{PIPE} = 'DEFAULT';
+
+DiffHighlight::highlight_stdin();
+exit 0;
index 2fb651b2b8d9d91a130b1cbd11c3c2b6b1cf961b..e7b85932d6ba62ec9f581c692a321ac24e52dcac 100755 (executable)
@@ -19,6 +19,7 @@ OPTIONS_SPEC=
 START_DIR=$(pwd)
 . git-sh-setup
 require_work_tree
+prefix=$(git rev-parse --show-prefix) || exit 1
 cd_to_toplevel
 
 TMP="$GIT_DIR/.git-stash.$$"
@@ -273,6 +274,8 @@ push_stash () {
                shift
        done
 
+       eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
+
        if test -n "$patch_mode" && test -n "$untracked"
        then
                die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
index bc178e88827a1a0558e9fbee1a6f18f6ede83df1..7a08470f8836e14824a215b29e0c3a5dbeba9c17 100644 (file)
@@ -10,7 +10,7 @@ merge_cmd () {
 
        if test "$meld_has_output_option" = true
        then
-               "$merge_tool_path" --output "$MERGED" \
+               "$merge_tool_path" --output="$MERGED" \
                        "$LOCAL" "$BASE" "$REMOTE"
        else
                "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
index 679f8f4720fdf02ee37a4660632e08d861ff5432..42c4508ed6f57873dcbbb9af134ed2db61e3edaa 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -8564,7 +8564,7 @@ msgstr "\"auto-gc\" Modus aktivieren"
 #: builtin/gc.c:362
 msgid "force running gc even if there may be another gc running"
 msgstr ""
-"Ausführung von \"git gc\" erwzingen, selbst wenn ein anderes\n"
+"Ausführung von \"git gc\" erzwingen, selbst wenn ein anderes\n"
 "\"git gc\" bereits ausgeführt wird"
 
 #: builtin/gc.c:379
index 67ebc6fc4b89358f1d28dc223935d66bfb00135b..5dfb322ccd63276603537b6e43c5c00f9b9e8dd6 100644 (file)
@@ -1429,134 +1429,168 @@ static void prepare_show_merge(struct rev_info *revs)
        revs->limited = 1;
 }
 
+static int dotdot_missing(const char *arg, char *dotdot,
+                         struct rev_info *revs, int symmetric)
+{
+       if (revs->ignore_missing)
+               return 0;
+       /* de-munge so we report the full argument */
+       *dotdot = '.';
+       die(symmetric
+           ? "Invalid symmetric difference expression %s"
+           : "Invalid revision range %s", arg);
+}
+
+static int handle_dotdot_1(const char *arg, char *dotdot,
+                          struct rev_info *revs, int flags,
+                          int cant_be_filename,
+                          struct object_context *a_oc,
+                          struct object_context *b_oc)
+{
+       const char *a_name, *b_name;
+       struct object_id a_oid, b_oid;
+       struct object *a_obj, *b_obj;
+       unsigned int a_flags, b_flags;
+       int symmetric = 0;
+       unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
+       unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH;
+
+       a_name = arg;
+       if (!*a_name)
+               a_name = "HEAD";
+
+       b_name = dotdot + 2;
+       if (*b_name == '.') {
+               symmetric = 1;
+               b_name++;
+       }
+       if (!*b_name)
+               b_name = "HEAD";
+
+       if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) ||
+           get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc))
+               return -1;
+
+       if (!cant_be_filename) {
+               *dotdot = '.';
+               verify_non_filename(revs->prefix, arg);
+               *dotdot = '\0';
+       }
+
+       a_obj = parse_object(a_oid.hash);
+       b_obj = parse_object(b_oid.hash);
+       if (!a_obj || !b_obj)
+               return dotdot_missing(arg, dotdot, revs, symmetric);
+
+       if (!symmetric) {
+               /* just A..B */
+               b_flags = flags;
+               a_flags = flags_exclude;
+       } else {
+               /* A...B -- find merge bases between the two */
+               struct commit *a, *b;
+               struct commit_list *exclude;
+
+               a = lookup_commit_reference(a_obj->oid.hash);
+               b = lookup_commit_reference(b_obj->oid.hash);
+               if (!a || !b)
+                       return dotdot_missing(arg, dotdot, revs, symmetric);
+
+               exclude = get_merge_bases(a, b);
+               add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
+                                    flags_exclude);
+               add_pending_commit_list(revs, exclude, flags_exclude);
+               free_commit_list(exclude);
+
+               b_flags = flags;
+               a_flags = flags | SYMMETRIC_LEFT;
+       }
+
+       a_obj->flags |= a_flags;
+       b_obj->flags |= b_flags;
+       add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags);
+       add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags);
+       add_pending_object_with_path(revs, a_obj, a_name, a_oc->mode, a_oc->path);
+       add_pending_object_with_path(revs, b_obj, b_name, b_oc->mode, b_oc->path);
+       return 0;
+}
+
+static int handle_dotdot(const char *arg,
+                        struct rev_info *revs, int flags,
+                        int cant_be_filename)
+{
+       struct object_context a_oc, b_oc;
+       char *dotdot = strstr(arg, "..");
+       int ret;
+
+       if (!dotdot)
+               return -1;
+
+       memset(&a_oc, 0, sizeof(a_oc));
+       memset(&b_oc, 0, sizeof(b_oc));
+
+       *dotdot = '\0';
+       ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
+                             &a_oc, &b_oc);
+       *dotdot = '.';
+
+       free(a_oc.path);
+       free(b_oc.path);
+
+       return ret;
+}
+
 int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
 {
        struct object_context oc;
-       char *dotdot;
+       char *mark;
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
        const char *arg = arg_;
        int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
-       unsigned get_sha1_flags = 0;
+       unsigned get_sha1_flags = GET_SHA1_RECORD_PATH;
 
        flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
 
-       dotdot = strstr(arg, "..");
-       if (dotdot) {
-               unsigned char from_sha1[20];
-               const char *next = dotdot + 2;
-               const char *this = arg;
-               int symmetric = *next == '.';
-               unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
-               static const char head_by_default[] = "HEAD";
-               unsigned int a_flags;
-
-               *dotdot = 0;
-               next += symmetric;
-
-               if (!*next)
-                       next = head_by_default;
-               if (dotdot == arg)
-                       this = head_by_default;
-               if (this == head_by_default && next == head_by_default &&
-                   !symmetric) {
-                       /*
-                        * Just ".."?  That is not a range but the
-                        * pathspec for the parent directory.
-                        */
-                       if (!cant_be_filename) {
-                               *dotdot = '.';
-                               return -1;
-                       }
-               }
-               if (!get_sha1_committish(this, from_sha1) &&
-                   !get_sha1_committish(next, sha1)) {
-                       struct object *a_obj, *b_obj;
-
-                       if (!cant_be_filename) {
-                               *dotdot = '.';
-                               verify_non_filename(revs->prefix, arg);
-                       }
-
-                       a_obj = parse_object(from_sha1);
-                       b_obj = parse_object(sha1);
-                       if (!a_obj || !b_obj) {
-                       missing:
-                               if (revs->ignore_missing)
-                                       return 0;
-                               die(symmetric
-                                   ? "Invalid symmetric difference expression %s"
-                                   : "Invalid revision range %s", arg);
-                       }
-
-                       if (!symmetric) {
-                               /* just A..B */
-                               a_flags = flags_exclude;
-                       } else {
-                               /* A...B -- find merge bases between the two */
-                               struct commit *a, *b;
-                               struct commit_list *exclude;
-
-                               a = (a_obj->type == OBJ_COMMIT
-                                    ? (struct commit *)a_obj
-                                    : lookup_commit_reference(a_obj->oid.hash));
-                               b = (b_obj->type == OBJ_COMMIT
-                                    ? (struct commit *)b_obj
-                                    : lookup_commit_reference(b_obj->oid.hash));
-                               if (!a || !b)
-                                       goto missing;
-                               exclude = get_merge_bases(a, b);
-                               add_rev_cmdline_list(revs, exclude,
-                                                    REV_CMD_MERGE_BASE,
-                                                    flags_exclude);
-                               add_pending_commit_list(revs, exclude,
-                                                       flags_exclude);
-                               free_commit_list(exclude);
-
-                               a_flags = flags | SYMMETRIC_LEFT;
-                       }
-
-                       a_obj->flags |= a_flags;
-                       b_obj->flags |= flags;
-                       add_rev_cmdline(revs, a_obj, this,
-                                       REV_CMD_LEFT, a_flags);
-                       add_rev_cmdline(revs, b_obj, next,
-                                       REV_CMD_RIGHT, flags);
-                       add_pending_object(revs, a_obj, this);
-                       add_pending_object(revs, b_obj, next);
-                       return 0;
-               }
-               *dotdot = '.';
+       if (!cant_be_filename && !strcmp(arg, "..")) {
+               /*
+                * Just ".."?  That is not a range but the
+                * pathspec for the parent directory.
+                */
+               return -1;
        }
 
-       dotdot = strstr(arg, "^@");
-       if (dotdot && !dotdot[2]) {
-               *dotdot = 0;
+       if (!handle_dotdot(arg, revs, flags, revarg_opt))
+               return 0;
+
+       mark = strstr(arg, "^@");
+       if (mark && !mark[2]) {
+               *mark = 0;
                if (add_parents_only(revs, arg, flags, 0))
                        return 0;
-               *dotdot = '^';
+               *mark = '^';
        }
-       dotdot = strstr(arg, "^!");
-       if (dotdot && !dotdot[2]) {
-               *dotdot = 0;
+       mark = strstr(arg, "^!");
+       if (mark && !mark[2]) {
+               *mark = 0;
                if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
-                       *dotdot = '^';
+                       *mark = '^';
        }
-       dotdot = strstr(arg, "^-");
-       if (dotdot) {
+       mark = strstr(arg, "^-");
+       if (mark) {
                int exclude_parent = 1;
 
-               if (dotdot[2]) {
+               if (mark[2]) {
                        char *end;
-                       exclude_parent = strtoul(dotdot + 2, &end, 10);
+                       exclude_parent = strtoul(mark + 2, &end, 10);
                        if (*end != '\0' || !exclude_parent)
                                return -1;
                }
 
-               *dotdot = 0;
+               *mark = 0;
                if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
-                       *dotdot = '^';
+                       *mark = '^';
        }
 
        local_flags = 0;
@@ -1566,7 +1600,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
        }
 
        if (revarg_opt & REVARG_COMMITTISH)
-               get_sha1_flags = GET_SHA1_COMMITTISH;
+               get_sha1_flags |= GET_SHA1_COMMITTISH;
 
        if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
                return revs->ignore_missing ? 0 : -1;
@@ -1574,7 +1608,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
-       add_pending_object_with_mode(revs, object, arg, oc.mode);
+       add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
+       free(oc.path);
        return 0;
 }
 
@@ -1690,8 +1725,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->max_count = atoi(argv[1]);
                revs->no_walk = 0;
                return 2;
-       } else if (starts_with(arg, "-n")) {
-               revs->max_count = atoi(arg + 2);
+       } else if (skip_prefix(arg, "-n", &optarg)) {
+               revs->max_count = atoi(optarg);
                revs->no_walk = 0;
        } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
                revs->max_age = atoi(optarg);
@@ -1750,16 +1785,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--author-date-order")) {
                revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
                revs->topo_order = 1;
-       } else if (starts_with(arg, "--early-output")) {
-               int count = 100;
-               switch (arg[14]) {
-               case '=':
-                       count = atoi(arg+15);
-                       /* Fallthrough */
-               case 0:
-                       revs->topo_order = 1;
-                      revs->early_output = count;
-               }
+       } else if (!strcmp(arg, "--early-output")) {
+               revs->early_output = 100;
+               revs->topo_order = 1;
+       } else if (skip_prefix(arg, "--early-output=", &optarg)) {
+               if (strtoul_ui(optarg, 10, &revs->early_output) < 0)
+                       die("'%s': not a non-negative integer", optarg);
+               revs->topo_order = 1;
        } else if (!strcmp(arg, "--parents")) {
                revs->rewrite_parents = 1;
                revs->print_parents = 1;
@@ -1775,13 +1807,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
                revs->max_parents = 1;
-       } else if (starts_with(arg, "--min-parents=")) {
-               revs->min_parents = atoi(arg+14);
-       } else if (starts_with(arg, "--no-min-parents")) {
+       } else if (skip_prefix(arg, "--min-parents=", &optarg)) {
+               revs->min_parents = atoi(optarg);
+       } else if (!strcmp(arg, "--no-min-parents")) {
                revs->min_parents = 0;
-       } else if (starts_with(arg, "--max-parents=")) {
-               revs->max_parents = atoi(arg+14);
-       } else if (starts_with(arg, "--no-max-parents")) {
+       } else if (skip_prefix(arg, "--max-parents=", &optarg)) {
+               revs->max_parents = atoi(optarg);
+       } else if (!strcmp(arg, "--no-max-parents")) {
                revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
@@ -1862,14 +1894,15 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(NULL, revs);
-       } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) {
+       } else if (skip_prefix(arg, "--pretty=", &optarg) ||
+                  skip_prefix(arg, "--format=", &optarg)) {
                /*
                 * Detached form ("--pretty X" as opposed to "--pretty=X")
                 * not allowed, since the argument is optional.
                 */
                revs->verbose_header = 1;
                revs->pretty_given = 1;
-               get_commit_format(arg+9, revs);
+               get_commit_format(optarg, revs);
        } else if (!strcmp(arg, "--expand-tabs")) {
                revs->expand_tabs_in_log = 8;
        } else if (!strcmp(arg, "--no-expand-tabs")) {
@@ -1887,26 +1920,23 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->show_signature = 1;
        } else if (!strcmp(arg, "--no-show-signature")) {
                revs->show_signature = 0;
-       } else if (!strcmp(arg, "--show-linear-break") ||
-                  starts_with(arg, "--show-linear-break=")) {
-               if (starts_with(arg, "--show-linear-break="))
-                       revs->break_bar = xstrdup(arg + 20);
-               else
-                       revs->break_bar = "                    ..........";
+       } else if (!strcmp(arg, "--show-linear-break")) {
+               revs->break_bar = "                    ..........";
+               revs->track_linear = 1;
+               revs->track_first_time = 1;
+       } else if (skip_prefix(arg, "--show-linear-break=", &optarg)) {
+               revs->break_bar = xstrdup(optarg);
                revs->track_linear = 1;
                revs->track_first_time = 1;
-       } else if (starts_with(arg, "--show-notes=") ||
-                  starts_with(arg, "--notes=")) {
+       } else if (skip_prefix(arg, "--show-notes=", &optarg) ||
+                  skip_prefix(arg, "--notes=", &optarg)) {
                struct strbuf buf = STRBUF_INIT;
                revs->show_notes = 1;
                revs->show_notes_given = 1;
-               if (starts_with(arg, "--show-notes")) {
-                       if (revs->notes_opt.use_default_notes < 0)
-                               revs->notes_opt.use_default_notes = 1;
-                       strbuf_addstr(&buf, arg+13);
-               }
-               else
-                       strbuf_addstr(&buf, arg+8);
+               if (starts_with(arg, "--show-notes=") &&
+                   revs->notes_opt.use_default_notes < 0)
+                       revs->notes_opt.use_default_notes = 1;
+               strbuf_addstr(&buf, optarg);
                expand_notes_ref(&buf);
                string_list_append(&revs->notes_opt.extra_notes_refs,
                                   strbuf_detach(&buf, NULL));
@@ -1943,8 +1973,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->abbrev = 0;
        } else if (!strcmp(arg, "--abbrev")) {
                revs->abbrev = DEFAULT_ABBREV;
-       } else if (starts_with(arg, "--abbrev=")) {
-               revs->abbrev = strtoul(arg + 9, NULL, 10);
+       } else if (skip_prefix(arg, "--abbrev=", &optarg)) {
+               revs->abbrev = strtoul(optarg, NULL, 10);
                if (revs->abbrev < MINIMUM_ABBREV)
                        revs->abbrev = MINIMUM_ABBREV;
                else if (revs->abbrev > 40)
@@ -2104,20 +2134,20 @@ static int handle_revision_pseudo_opt(const char *submodule,
        } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
                add_ref_exclusion(&revs->ref_excludes, optarg);
                return argcount;
-       } else if (starts_with(arg, "--branches=")) {
+       } else if (skip_prefix(arg, "--branches=", &optarg)) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
-               for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
+               for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
-       } else if (starts_with(arg, "--tags=")) {
+       } else if (skip_prefix(arg, "--tags=", &optarg)) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
-               for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
+               for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
-       } else if (starts_with(arg, "--remotes=")) {
+       } else if (skip_prefix(arg, "--remotes=", &optarg)) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
-               for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
+               for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
                add_reflogs_to_pending(revs, *flags);
@@ -2127,14 +2157,14 @@ static int handle_revision_pseudo_opt(const char *submodule,
                *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
-       } else if (starts_with(arg, "--no-walk=")) {
+       } else if (skip_prefix(arg, "--no-walk=", &optarg)) {
                /*
                 * Detached form ("--no-walk X" as opposed to "--no-walk=X")
                 * not allowed, since the argument is optional.
                 */
-               if (!strcmp(arg + 10, "sorted"))
+               if (!strcmp(optarg, "sorted"))
                        revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
-               else if (!strcmp(arg + 10, "unsorted"))
+               else if (!strcmp(optarg, "unsorted"))
                        revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
                else
                        return error("invalid argument to --no-walk");
index 14886ec92b4f67527a86fc821998214a4d2b321f..3672d39237c7f06783b5f88c184ac418dbb5542b 100644 (file)
@@ -74,8 +74,9 @@ struct rev_info {
        /* topo-sort */
        enum rev_sort_order sort_order;
 
-       unsigned int    early_output:1,
-                       ignore_missing:1,
+       unsigned int early_output;
+
+       unsigned int    ignore_missing:1,
                        ignore_missing_links:1;
 
        /* Traversal flags */
index 8eec9f7c1bb59b8124e098367b7bff552bcafcf4..5e2ec37b6557f59970ed2b2dd69d4e0b151d1fd0 100644 (file)
@@ -1511,6 +1511,7 @@ static int get_sha1_with_context_1(const char *name,
 
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
+       strbuf_init(&oc->symlink_path, 0);
        ret = get_sha1_1(name, namelen, sha1, flags);
        if (!ret)
                return ret;
@@ -1549,7 +1550,8 @@ static int get_sha1_with_context_1(const char *name,
                        namelen = strlen(cp);
                }
 
-               strlcpy(oc->path, cp, sizeof(oc->path));
+               if (flags & GET_SHA1_RECORD_PATH)
+                       oc->path = xstrdup(cp);
 
                if (!active_cache)
                        read_cache();
@@ -1612,7 +1614,8 @@ static int get_sha1_with_context_1(const char *name,
                                }
                        }
                        hashcpy(oc->tree, tree_sha1);
-                       strlcpy(oc->path, filename, sizeof(oc->path));
+                       if (flags & GET_SHA1_RECORD_PATH)
+                               oc->path = xstrdup(filename);
 
                        free(new_filename);
                        return ret;
@@ -1638,9 +1641,9 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
        get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
 }
 
-int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
+int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc)
 {
        if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
                die("BUG: incompatible flags for get_sha1_with_context");
-       return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
+       return get_sha1_with_context_1(str, flags, NULL, sha1, oc);
 }
index 10f8f026ffb4b21e62bdfa5fe1b8a4d6a3b3f13c..48d152b9a9519e00227a0877631d643ecf38e687 100755 (executable)
@@ -100,6 +100,23 @@ test_expect_success 'git branch -m n/n n should work' '
        git reflog exists refs/heads/n
 '
 
+# The topmost entry in reflog for branch bbb is about branch creation.
+# Hence, we compare bbb@{1} (instead of bbb@{0}) with aaa@{0}.
+
+test_expect_success 'git branch -m bbb should rename checked out branch' '
+       test_when_finished git branch -D bbb &&
+       test_when_finished git checkout master &&
+       git checkout -b aaa &&
+       git commit --allow-empty -m "a new commit" &&
+       git rev-parse aaa@{0} >expect &&
+       git branch -m bbb &&
+       git rev-parse bbb@{1} >actual &&
+       test_cmp expect actual &&
+       git symbolic-ref HEAD >actual &&
+       echo refs/heads/bbb >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git branch -m o/o o should fail when o/p exists' '
        git branch o/o &&
        git branch o/p &&
index 3b4bed5c9ace3b9122380287c55b4130135018c7..4046817d70a0ac1dd5b11a49c500896688a4f0b1 100755 (executable)
@@ -812,6 +812,22 @@ test_expect_success 'stash -- <pathspec> stashes and restores the file' '
        test_path_is_file bar
 '
 
+test_expect_success 'stash -- <pathspec> stashes in subdirectory' '
+       mkdir sub &&
+       >foo &&
+       >bar &&
+       git add foo bar &&
+       (
+               cd sub &&
+               git stash push -- ../foo
+       ) &&
+       test_path_is_file bar &&
+       test_path_is_missing foo &&
+       git stash pop &&
+       test_path_is_file foo &&
+       test_path_is_file bar
+'
+
 test_expect_success 'stash with multiple pathspec arguments' '
        >foo &&
        >bar &&
diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh
new file mode 100755 (executable)
index 0000000..bc69e26
--- /dev/null
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='test direct comparison of blobs via git-diff'
+. ./test-lib.sh
+
+run_diff () {
+       # use full-index to make it easy to match the index line
+       git diff --full-index "$@" >diff
+}
+
+check_index () {
+       grep "^index $1\\.\\.$2" diff
+}
+
+check_mode () {
+       grep "^old mode $1" diff &&
+       grep "^new mode $2" diff
+}
+
+check_paths () {
+       grep "^diff --git a/$1 b/$2" diff
+}
+
+test_expect_success 'create some blobs' '
+       echo one >one &&
+       echo two >two &&
+       chmod +x two &&
+       git add . &&
+
+       # cover systems where modes are ignored
+       git update-index --chmod=+x two &&
+
+       git commit -m base &&
+
+       sha1_one=$(git rev-parse HEAD:one) &&
+       sha1_two=$(git rev-parse HEAD:two)
+'
+
+test_expect_success 'diff by sha1' '
+       run_diff $sha1_one $sha1_two
+'
+test_expect_success 'index of sha1 diff' '
+       check_index $sha1_one $sha1_two
+'
+test_expect_success 'sha1 diff uses arguments as paths' '
+       check_paths $sha1_one $sha1_two
+'
+test_expect_success 'sha1 diff has no mode change' '
+       ! grep mode diff
+'
+
+test_expect_success 'diff by tree:path (run)' '
+       run_diff HEAD:one HEAD:two
+'
+test_expect_success 'index of tree:path diff' '
+       check_index $sha1_one $sha1_two
+'
+test_expect_success 'tree:path diff uses filenames as paths' '
+       check_paths one two
+'
+test_expect_success 'tree:path diff shows mode change' '
+       check_mode 100644 100755
+'
+
+test_expect_success 'diff by ranged tree:path' '
+       run_diff HEAD:one..HEAD:two
+'
+test_expect_success 'index of ranged tree:path diff' '
+       check_index $sha1_one $sha1_two
+'
+test_expect_success 'ranged tree:path diff uses filenames as paths' '
+       check_paths one two
+'
+test_expect_success 'ranged tree:path diff shows mode change' '
+       check_mode 100644 100755
+'
+
+test_expect_success 'diff blob against file' '
+       run_diff HEAD:one two
+'
+test_expect_success 'index of blob-file diff' '
+       check_index $sha1_one $sha1_two
+'
+test_expect_success 'blob-file diff uses filename as paths' '
+       check_paths one two
+'
+test_expect_success FILEMODE 'blob-file diff shows mode change' '
+       check_mode 100644 100755
+'
+
+test_expect_success 'blob-file diff prefers filename to sha1' '
+       run_diff $sha1_one two &&
+       check_paths two two
+'
+
+test_done
index 1c7d6729c699dc8c8784d19659551df24530bd05..e4441957e2b406c16121578f6d1344488509cafe 100755 (executable)
@@ -399,7 +399,7 @@ cat > expect <<\EOF
 | |
 | |     Merge branch 'side'
 | |
-| * commit side
+| * commit tags/side-2
 | | Author: A U Thor <author@example.com>
 | |
 | |     side-2
@@ -1392,4 +1392,13 @@ test_expect_success 'log --source paints tag names' '
        test_cmp expect actual
 '
 
+test_expect_success 'log --source paints symmetric ranges' '
+       cat >expect <<-\EOF &&
+       09e12a9 source-b three
+       8e393e1 source-a two
+       EOF
+       git log --oneline --source source-a...source-b >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 7171f67539bfafcb17d2d2fcb2f4ba4856c1131c..9690dcad4fd5ba0148b3f6a709ab321976545db0 100755 (executable)
@@ -171,4 +171,46 @@ test_expect_success 'mailinfo with mailinfo.scissors config' '
 '
 
 
+test_expect_success 'mailinfo no options' '
+       subj="$(echo "Subject: [PATCH] [other] [PATCH] message" |
+               git mailinfo /dev/null /dev/null)" &&
+       test z"$subj" = z"Subject: message"
+'
+
+test_expect_success 'mailinfo -k' '
+       subj="$(echo "Subject: [PATCH] [other] [PATCH] message" |
+               git mailinfo -k /dev/null /dev/null)" &&
+       test z"$subj" = z"Subject: [PATCH] [other] [PATCH] message"
+'
+
+test_expect_success 'mailinfo -b no [PATCH]' '
+       subj="$(echo "Subject: [other] message" |
+               git mailinfo -b /dev/null /dev/null)" &&
+       test z"$subj" = z"Subject: [other] message"
+'
+
+test_expect_success 'mailinfo -b leading [PATCH]' '
+       subj="$(echo "Subject: [PATCH] [other] message" |
+               git mailinfo -b /dev/null /dev/null)" &&
+       test z"$subj" = z"Subject: [other] message"
+'
+
+test_expect_success 'mailinfo -b double [PATCH]' '
+       subj="$(echo "Subject: [PATCH] [PATCH] message" |
+               git mailinfo -b /dev/null /dev/null)" &&
+       test z"$subj" = z"Subject: message"
+'
+
+test_expect_failure 'mailinfo -b trailing [PATCH]' '
+       subj="$(echo "Subject: [other] [PATCH] message" |
+               git mailinfo -b /dev/null /dev/null)" &&
+       test z"$subj" = z"Subject: [other] message"
+'
+
+test_expect_failure 'mailinfo -b separated double [PATCH]' '
+       subj="$(echo "Subject: [PATCH] [other] [PATCH] message" |
+               git mailinfo -b /dev/null /dev/null)" &&
+       test z"$subj" = z"Subject: [other] message"
+'
+
 test_done
index a8a587abc3799f069deab3bc48914cea81bb15cf..9372508c993e72ad99da004ca2400df81c721006 100755 (executable)
@@ -139,7 +139,13 @@ test_expect_success 'bogus offset into v2 extended table' '
 test_expect_success 'bogus offset inside v2 extended table' '
        # We need two objects here, so we can plausibly require
        # an extended table (if the first object were larger than 2^31).
-       do_pack "$object $(git rev-parse HEAD)" --index-version=2 &&
+       #
+       # Note that the value is important here. We want $object as
+       # the second entry in sorted-sha1 order. The sha1 of 1485 starts
+       # with "000", which sorts before that of $object (which starts
+       # with "fff").
+       second=$(echo 1485 | git hash-object -w --stdin) &&
+       do_pack "$object $second" --index-version=2 &&
 
        # We have to make extra room for the table, so we cannot
        # just munge in place as usual.
index f25a08fddfab443b1eff1f13f33939f1b21f4b23..6a42e402b00a3da90a008adf6e4c68dc8c45e516 100644 (file)
@@ -589,7 +589,6 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s
        int i;
 
        init_tree_desc(&t, NULL, 0UL);
-       strbuf_init(result_path, 0);
        strbuf_addstr(&namebuf, name);
        hashcpy(current_tree_sha1, tree_sha1);