Merge branches 'jc/apply', 'lt/ls-tree', 'lt/bisect' and 'lt/merge'
authorJunio C Hamano <junkio@cox.net>
Wed, 30 Nov 2005 19:05:48 +0000 (11:05 -0800)
committerJunio C Hamano <junkio@cox.net>
Wed, 30 Nov 2005 19:05:48 +0000 (11:05 -0800)
16 files changed:
Documentation/git-diff-files.txt
Documentation/tutorial.txt
diff-files.c
git-applymbox.sh
git-applypatch.sh
git-bisect.sh
git-format-patch.sh
git-merge-one-file.sh
git-merge-recursive.py
ls-tree.c
rev-list.c
t/t0000-basic.sh
t/t3100-ls-tree-restrict.sh
t/t3101-ls-tree-dirname.sh
tree.c
tree.h
index 3b04bfeec6ad18edffd9b6d44709a10fdda36b8c..67f51265e6aeadf85db582fe89c2c5752b36745a 100644 (file)
@@ -21,6 +21,15 @@ OPTIONS
 -------
 include::diff-options.txt[]
 
+-1 -2 -3 or --base --ours --theirs, and -0::
+       Diff against the "base" version, "our branch" or "their
+       branch" respectively.  With these options, diffs for
+       merged entries are not shown.
++
+The default is to diff against our branch (-2) and the 
+cleanly resolved paths.  The option -0 can be given to
+omit diff output for unmerged entries and just show "Unmerged".
+
 -q::
        Remain silent even on nonexisting files
 
index e2dfb00ab10de601204715e4b244420a0a2cb7c8..cf7ba76ddfb76b4e92b71474a3386d0573820ac5 100644 (file)
@@ -898,9 +898,8 @@ file, which had no differences in the `mybranch` branch), and say:
        fatal: Merge requires file-level merging
        Nope.
        ...
-       merge: warning: conflicts during merge
-       ERROR: Merge conflict in hello.
-       fatal: merge program failed
+       Auto-merging hello 
+       CONFLICT (content): Merge conflict in hello 
        Automatic merge failed/prevented; fix up by hand
 ----------------
 
@@ -942,10 +941,10 @@ environment, is `git show-branch`.
 
 ------------------------------------------------
 $ git show-branch master mybranch
-* [master] Merged "mybranch" changes.
+* [master] Merge work in mybranch
  ! [mybranch] Some work.
 --
-+  [master] Merged "mybranch" changes.
++  [master] Merge work in mybranch
 ++ [mybranch] Some work.
 ------------------------------------------------
 
@@ -998,10 +997,10 @@ looks like, or run `show-branch`, which tells you this.
 
 ------------------------------------------------
 $ git show-branch master mybranch
-! [master] Merged "mybranch" changes.
- * [mybranch] Merged "mybranch" changes.
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
 --
-++ [master] Merged "mybranch" changes.
+++ [master] Merge work in mybranch
 ------------------------------------------------
 
 
index 38599b5b755df431e3e061df8a304698cc4496fc..6c0696c34f0af95acbfe06f644f7df7fb1e93509 100644 (file)
@@ -7,12 +7,12 @@
 #include "diff.h"
 
 static const char diff_files_usage[] =
-"git-diff-files [-q] "
-"[<common diff options>] [<path>...]"
+"git-diff-files [-q] [-0/-1/2/3] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
 static struct diff_options diff_options;
 static int silent = 0;
+static int diff_unmerged_stage = 2;
 
 static void show_unmerge(const char *path)
 {
@@ -46,7 +46,21 @@ int main(int argc, const char **argv)
                        argc--;
                        break;
                }
-               if (!strcmp(argv[1], "-q"))
+               if (!strcmp(argv[1], "-0"))
+                       diff_unmerged_stage = 0;
+               else if (!strcmp(argv[1], "-1"))
+                       diff_unmerged_stage = 1;
+               else if (!strcmp(argv[1], "-2"))
+                       diff_unmerged_stage = 2;
+               else if (!strcmp(argv[1], "-3"))
+                       diff_unmerged_stage = 3;
+               else if (!strcmp(argv[1], "--base"))
+                       diff_unmerged_stage = 1;
+               else if (!strcmp(argv[1], "--ours"))
+                       diff_unmerged_stage = 2;
+               else if (!strcmp(argv[1], "--theirs"))
+                       diff_unmerged_stage = 3;
+               else if (!strcmp(argv[1], "-q"))
                        silent = 1;
                else if (!strcmp(argv[1], "-r"))
                        ; /* no-op */
@@ -95,11 +109,26 @@ int main(int argc, const char **argv)
 
                if (ce_stage(ce)) {
                        show_unmerge(ce->name);
-                       while (i < entries &&
-                              !strcmp(ce->name, active_cache[i]->name))
+                       while (i < entries) {
+                               struct cache_entry *nce = active_cache[i];
+
+                               if (strcmp(ce->name, nce->name))
+                                       break;
+                               /* diff against the proper unmerged stage */
+                               if (ce_stage(nce) == diff_unmerged_stage)
+                                       ce = nce;
                                i++;
-                       i--; /* compensate for loop control increments */
-                       continue;
+                       }
+                       /*
+                        * Compensate for loop update
+                        */
+                       i--;
+                       /*
+                        * Show the diff for the 'ce' if we found the one
+                        * from the desired stage.
+                        */
+                       if (ce_stage(ce) != diff_unmerged_stage)
+                               continue;
                }
 
                if (lstat(ce->name, &st) < 0) {
index 24d4a8cb4eb95a5201c205be879d3ac6a62a8a15..c686cc8d27a64998c141cca2989375910601f90f 100755 (executable)
@@ -33,7 +33,7 @@ do
        -k)     keep_subject=-k ;;
        -q)     query_apply=t ;;
        -c)     continue="$2"; resume=f; shift ;;
-       -m)     fallback_3way=t ;;
+       -m)     fall_back_3way=t ;;
        -*)     usage ;;
        *)      break ;;
        esac
index f0549960fbd706f06fff5b2c602450917505a1ad..4c577eb835b877dd0766edbc5a603ee4e3db049a 100755 (executable)
@@ -120,26 +120,36 @@ git-apply --index "$PATCHFILE" || {
        O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
        rm -fr .patch-merge-*
 
+       if git-apply -z --index-info "$PATCHFILE" \
+               >.patch-merge-index-info 2>/dev/null &&
+               GIT_INDEX_FILE=.patch-merge-tmp-index \
+               git-update-index -z --index-info <.patch-merge-index-info &&
+               GIT_INDEX_FILE=.patch-merge-tmp-index \
+               git-write-tree >.patch-merge-tmp-base &&
+               (
+                       mkdir .patch-merge-tmp-dir &&
+                       cd .patch-merge-tmp-dir &&
+                       GIT_INDEX_FILE="../.patch-merge-tmp-index" \
+                       GIT_OBJECT_DIRECTORY="$O_OBJECT" \
+                       git-apply $binary --index
+               ) <"$PATCHFILE"
+       then
+               echo Using index info to reconstruct a base tree...
+               mv .patch-merge-tmp-base .patch-merge-base
+               mv .patch-merge-tmp-index .patch-merge-index
+       else
        (
                N=10
 
-               # if the patch records the base tree...
-               sed -ne '
-                       /^diff /q
-                       /^applies-to: \([0-9a-f]*\)$/{
-                               s//\1/p
-                               q
-                       }
-               ' "$PATCHFILE"
-
-               # or hoping the patch is against our recent commits...
+               # Otherwise, try nearby trees that can be used to apply the
+               # patch.
                git-rev-list --max-count=$N HEAD
 
                # or hoping the patch is against known tags...
                git-ls-remote --tags .
        ) |
-       while read base junk
-       do
+           while read base junk
+           do
                # Try it if we have it as a tree.
                git-cat-file tree "$base" >/dev/null 2>&1 || continue
 
@@ -155,7 +165,8 @@ git-apply --index "$PATCHFILE" || {
                        mv ../.patch-merge-tmp-index ../.patch-merge-index &&
                        echo "$base" >../.patch-merge-base
                ) <"$PATCHFILE"  2>/dev/null && break
-       done
+           done
+       fi
 
        test -f .patch-merge-index &&
        his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
index d92993b94e4264d9c13547f8bc3a1d9c6c77017a..68838f3fad1d22ab4f14977434e9ce73365fb304 100755 (executable)
@@ -1,9 +1,19 @@
 #!/bin/sh
 . git-sh-setup
 
+sq() {
+       perl -e '
+               for (@ARGV) {
+                       s/'\''/'\'\\\\\'\''/g;
+                       print " '\''$_'\''";
+               }
+               print "\n";
+       ' "$@"
+}
+
 usage() {
     echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize]
-git bisect start               reset bisect state and start bisection.
+git bisect start [<pathspec>]  reset bisect state and start bisection.
 git bisect bad [<rev>]         mark <rev> a known-bad revision.
 git bisect good [<rev>...]     mark <rev>... known-good revisions.
 git bisect next                        find next bisection to test and check it out.
@@ -33,7 +43,6 @@ bisect_autostart() {
 }
 
 bisect_start() {
-        case "$#" in 0) ;; *) usage ;; esac
        #
        # Verify HEAD. If we were bisecting before this, reset to the
        # top-of-line master first!
@@ -57,7 +66,11 @@ bisect_start() {
        rm -f "$GIT_DIR/refs/heads/bisect"
        rm -rf "$GIT_DIR/refs/bisect/"
        mkdir "$GIT_DIR/refs/bisect"
-       echo "git-bisect start" >"$GIT_DIR/BISECT_LOG"
+       {
+           echo -n "git-bisect start"
+           sq "$@"
+       } >"$GIT_DIR/BISECT_LOG"
+       sq "$@" >"$GIT_DIR/BISECT_NAMES"
 }
 
 bisect_bad() {
@@ -121,7 +134,7 @@ bisect_next() {
        bad=$(git-rev-parse --verify refs/bisect/bad) &&
        good=$(git-rev-parse --sq --revs-only --not \
                $(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
-       rev=$(eval "git-rev-list --bisect $good $bad") || exit
+       rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
        if [ -z "$rev" ]; then
            echo "$bad was both good and bad"
            exit 1
@@ -131,7 +144,7 @@ bisect_next() {
            git-diff-tree --pretty $rev
            exit 0
        fi
-       nr=$(eval "git-rev-list $rev $good" | wc -l) || exit
+       nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit
        echo "Bisecting: $nr revisions left to test after this"
        echo "$rev" > "$GIT_DIR/refs/heads/new-bisect"
        git checkout new-bisect || exit
@@ -142,7 +155,8 @@ bisect_next() {
 
 bisect_visualize() {
        bisect_next_check fail
-       gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*`
+       not=`cd "$GIT_DIR/refs" && echo bisect/good-*`
+       eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
 bisect_reset() {
@@ -173,7 +187,8 @@ bisect_replay () {
                test "$bisect" = "git-bisect" || continue
                case "$command" in
                start)
-                       bisect_start
+                       cmd="bisect_start $rev"
+                       eval "$cmd"
                        ;;
                good)
                        echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
index 9b4088045a5d53ceb1b893e5f6bda575518d36c1..1eebe857c021495b92d9e39ec1193b85ab4e980e 100755 (executable)
@@ -5,6 +5,10 @@
 
 . git-sh-setup
 
+# Force diff to run in C locale.
+LANG=C LC_ALL=C
+export LANG LC_ALL
+
 usage () {
     echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
     [--check] [--signoff] [-<diff options>...]
@@ -202,7 +206,7 @@ process_one () {
            ;;
        esac
 
-       eval "$(LANG=C LC_ALL=C sed -ne "$whosepatchScript" $commsg)"
+       eval "$(sed -ne "$whosepatchScript" $commsg)"
        test "$author,$au" = ",$me" || {
                mailScript="$mailScript"'
        a\
@@ -238,9 +242,8 @@ Date: '"$ad"
        echo
        git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
        echo
-       git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
        git-diff-tree -p $diff_opts "$commit"
-       echo "---"
+       echo "-- "
        echo "@@GIT_VERSION@@"
 
        case "$mbox" in
index c3eca8b3321df8dbf632fb687003737d8989d6f6..739a07292afad6baf5b0a34ff1405eb2c90f5825 100755 (executable)
@@ -79,11 +79,7 @@ case "${1:-.}${2:-.}${3:-.}" in
                ;;
        esac
 
-       # We reset the index to the first branch, making
-       # git-diff-file useful
-       git-update-index --add --cacheinfo "$6" "$2" "$4"
-               git-checkout-index -u -f -- "$4" &&
-               merge "$4" "$orig" "$src2"
+       merge "$4" "$orig" "$src2"
        ret=$?
        rm -f -- "$orig" "$src2"
 
index 01292335509bfd1460e12e0e1d86c4fcd1f265a4..e599b11cc57ecd0219d39fbfc8141fa5455755bf 100755 (executable)
@@ -828,8 +828,6 @@ def processEntry(entry, branch1Name, branch2Name):
             if cacheOnly:
                 updateFile(False, sha, mode, path)
             else:
-                updateFileExt(aSha, aMode, path,
-                              updateCache=True, updateWd=False)
                 updateFileExt(sha, mode, path, updateCache=False, updateWd=True)
     else:
         die("ERROR: Fatal merge failure, shouldn't happen.")
index d7c7e750fbf08363731e7bafdc6b2b1b058ccc38..d4b62198a0774409fff4acdbc8b35a7e052f498d 100644 (file)
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -12,218 +12,56 @@ static int line_termination = '\n';
 #define LS_RECURSIVE 1
 #define LS_TREE_ONLY 2
 static int ls_options = 0;
+const char **pathspec;
 
-static struct tree_entry_list root_entry;
-
-static void prepare_root(unsigned char *sha1)
-{
-       unsigned char rsha[20];
-       unsigned long size;
-       void *buf;
-       struct tree *root_tree;
-
-       buf = read_object_with_reference(sha1, "tree", &size, rsha);
-       free(buf);
-       if (!buf)
-               die("Could not read %s", sha1_to_hex(sha1));
-
-       root_tree = lookup_tree(rsha);
-       if (!root_tree)
-               die("Could not read %s", sha1_to_hex(sha1));
-
-       /* Prepare a fake entry */
-       root_entry.directory = 1;
-       root_entry.executable = root_entry.symlink = 0;
-       root_entry.mode = S_IFDIR;
-       root_entry.name = "";
-       root_entry.item.tree = root_tree;
-       root_entry.parent = NULL;
-}
-
-static int prepare_children(struct tree_entry_list *elem)
-{
-       if (!elem->directory)
-               return -1;
-       if (!elem->item.tree->object.parsed) {
-               struct tree_entry_list *e;
-               if (parse_tree(elem->item.tree))
-                       return -1;
-               /* Set up the parent link */
-               for (e = elem->item.tree->entries; e; e = e->next)
-                       e->parent = elem;
-       }
-       return 0;
-}
+static const char ls_tree_usage[] =
+       "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
 
-static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
+static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
 {
-       const char *next, *slash;
-       int len;
-       struct tree_entry_list *elem = &root_entry, *oldelem = NULL;
-
-       *(pathbuf) = '\0';
-
-       /* Find tree element, descending from root, that
-        * corresponds to the named path, lazily expanding
-        * the tree if possible.
-        */
-
-       while (path) {
-               /* The fact we still have path means that the caller
-                * wants us to make sure that elem at this point is a
-                * directory, and possibly descend into it.  Even what
-                * is left is just trailing slashes, we loop back to
-                * here, and this call to prepare_children() will
-                * catch elem not being a tree.  Nice.
-                */
-               if (prepare_children(elem))
-                       return NULL;
-
-               slash = strchr(path, '/');
-               if (!slash) {
-                       len = strlen(path);
-                       next = NULL;
-               }
-               else {
-                       next = slash + 1;
-                       len = slash - path;
-               }
-               if (len) {
-                       if (oldelem) {
-                               pathbuf += sprintf(pathbuf, "%s/", oldelem->name);
-                       }
-
-                       /* (len == 0) if the original path was "drivers/char/"
-                        * and we have run already two rounds, having elem
-                        * pointing at the drivers/char directory.
-                        */
-                       elem = elem->item.tree->entries;
-                       while (elem) {
-                               if ((strlen(elem->name) == len) &&
-                                   !strncmp(elem->name, path, len)) {
-                                       /* found */
+       const char *type = "blob";
+
+       if (S_ISDIR(mode)) {
+               const char **s;
+               if (ls_options & LS_RECURSIVE)
+                       return READ_TREE_RECURSIVE;
+               s = pathspec;
+               if (s) {
+                       for (;;) {
+                               const char *spec = *s++;
+                               int len, speclen;
+
+                               if (!spec)
                                        break;
-                               }
-                               elem = elem->next;
+                               if (strncmp(base, spec, baselen))
+                                       continue;
+                               len = strlen(pathname);
+                               spec += baselen;
+                               speclen = strlen(spec);
+                               if (speclen <= len)
+                                       continue;
+                               if (memcmp(pathname, spec, len))
+                                       continue;
+                               return READ_TREE_RECURSIVE;
                        }
-                       if (!elem)
-                               return NULL;
-
-                       oldelem = elem;
                }
-               path = next;
+               type = "tree";
        }
 
-       return elem;
-}
-
-static const char *entry_type(struct tree_entry_list *e)
-{
-       return (e->directory ? "tree" : "blob");
-}
-
-static const char *entry_hex(struct tree_entry_list *e)
-{
-       return sha1_to_hex(e->directory
-                          ? e->item.tree->object.sha1
-                          : e->item.blob->object.sha1);
-}
-
-/* forward declaration for mutually recursive routines */
-static int show_entry(struct tree_entry_list *, int, char *pathbuf);
-
-static int show_children(struct tree_entry_list *e, int level, char *pathbuf)
-{
-       int oldlen = strlen(pathbuf);
-
-       if (e != &root_entry)
-               sprintf(pathbuf + oldlen, "%s/", e->name);
-
-       if (prepare_children(e))
-               die("internal error: ls-tree show_children called with non tree");
-       e = e->item.tree->entries;
-       while (e) {
-               show_entry(e, level, pathbuf);
-               e = e->next;
-       }
-
-       pathbuf[oldlen] = '\0';
-
+       printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1));
+       write_name_quoted(base, baselen, pathname, line_termination, stdout);
+       putchar(line_termination);
        return 0;
 }
 
-static int show_entry(struct tree_entry_list *e, int level, char *pathbuf)
+int main(int argc, const char **argv)
 {
-       int err = 0; 
-
-       if (e != &root_entry) {
-               int pathlen = strlen(pathbuf);
-               printf("%06o %s %s      ",
-                      e->mode, entry_type(e), entry_hex(e));
-               write_name_quoted(pathbuf, pathlen, e->name,
-                                 line_termination, stdout);
-               putchar(line_termination);
-       }
-
-       if (e->directory) {
-               /* If this is a directory, we have the following cases:
-                * (1) This is the top-level request (explicit path from the
-                *     command line, or "root" if there is no command line).
-                *  a. Without any flag.  We show direct children.  We do not 
-                *     recurse into them.
-                *  b. With -r.  We do recurse into children.
-                *  c. With -d.  We do not recurse into children.
-                * (2) We came here because our caller is either (1-a) or
-                *     (1-b).
-                *  a. Without any flag.  We do not show our children (which
-                *     are grandchildren for the original request).
-                *  b. With -r.  We continue to recurse into our children.
-                *  c. With -d.  We should not have come here to begin with.
-                */
-               if (level == 0 && !(ls_options & LS_TREE_ONLY))
-                       /* case (1)-a and (1)-b */
-                       err = err | show_children(e, level+1, pathbuf);
-               else if (level && ls_options & LS_RECURSIVE)
-                       /* case (2)-b */
-                       err = err | show_children(e, level+1, pathbuf);
-       }
-       return err;
-}
-
-static int list_one(const char *path)
-{
-       int err = 0;
-       char pathbuf[MAXPATHLEN + 1];
-       struct tree_entry_list *e = find_entry(path, pathbuf);
-       if (!e) {
-               /* traditionally ls-tree does not complain about
-                * missing path.  We may change this later to match
-                * what "/bin/ls -a" does, which is to complain.
-                */
-               return err;
-       }
-       err = err | show_entry(e, 0, pathbuf);
-       return err;
-}
-
-static int list(char **path)
-{
-       int i;
-       int err = 0;
-       for (i = 0; path[i]; i++)
-               err = err | list_one(path[i]);
-       return err;
-}
-
-static const char ls_tree_usage[] =
-       "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
-
-int main(int argc, char **argv)
-{
-       static char *path0[] = { "", NULL };
-       char **path;
+       const char *prefix;
        unsigned char sha1[20];
+       char *buf;
+       unsigned long size;
 
+       prefix = setup_git_directory();
        while (1 < argc && argv[1][0] == '-') {
                switch (argv[1][1]) {
                case 'z':
@@ -246,9 +84,11 @@ int main(int argc, char **argv)
        if (get_sha1(argv[1], sha1) < 0)
                usage(ls_tree_usage);
 
-       path = (argc == 2) ? path0 : (argv + 2);
-       prepare_root(sha1);
-       if (list(path) < 0)
-               die("list failed");
+       pathspec = get_pathspec(prefix, argv + 2);
+       buf = read_object_with_reference(sha1, "tree", &size, NULL);
+       if (!buf)
+               die("not a tree object");
+       read_tree_recursive(buf, size, "", 0, 0, pathspec, show_tree);
+
        return 0;
 }
index e17f928061250c24465e1b96f89a18acb98a748c..8020d974f2fc84d477920bd10ea450c44fce0a77 100644 (file)
@@ -350,7 +350,8 @@ static int count_distance(struct commit_list *entry)
 
                if (commit->object.flags & (UNINTERESTING | COUNTED))
                        break;
-               nr++;
+               if (!paths || (commit->object.flags & TREECHANGE))
+                       nr++;
                commit->object.flags |= COUNTED;
                p = commit->parents;
                entry = p;
@@ -362,6 +363,7 @@ static int count_distance(struct commit_list *entry)
                        }
                }
        }
+
        return nr;
 }
 
@@ -382,15 +384,20 @@ static struct commit_list *find_bisection(struct commit_list *list)
        nr = 0;
        p = list;
        while (p) {
-               nr++;
+               if (!paths || (p->item->object.flags & TREECHANGE))
+                       nr++;
                p = p->next;
        }
        closest = 0;
        best = list;
 
-       p = list;
-       while (p) {
-               int distance = count_distance(p);
+       for (p = list; p; p = p->next) {
+               int distance;
+
+               if (paths && !(p->item->object.flags & TREECHANGE))
+                       continue;
+
+               distance = count_distance(p);
                clear_distance(list);
                if (nr - distance < distance)
                        distance = nr - distance;
@@ -398,7 +405,6 @@ static struct commit_list *find_bisection(struct commit_list *list)
                        best = p;
                        closest = distance;
                }
-               p = p->next;
        }
        if (best)
                best->next = NULL;
index dff7d6916374d6f1c9302970cef7d0ab7e0c75ad..22bdacaf7861d15bb5fd383e9b17c22f082d72b7 100755 (executable)
@@ -126,19 +126,18 @@ test_expect_success \
     'git-ls-tree output for a known tree.' \
     'diff current expected'
 
+# This changed in ls-tree pathspec change -- recursive does
+# not show tree nodes anymore.
 test_expect_success \
     'showing tree with git-ls-tree -r' \
     'git-ls-tree -r $tree >current'
 cat >expected <<\EOF
 100644 blob f87290f8eb2cbbea7857214459a0739927eab154   path0
 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01   path0sym
-040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe   path2
 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7   path2/file2
 120000 blob d8ce161addc5173867a3c3c730924388daedbc38   path2/file2sym
-040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3   path3
 100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376   path3/file3
 120000 blob 8599103969b43aff7e430efea79ca4636466794f   path3/file3sym
-040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2   path3/subp3
 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f   path3/subp3/file3
 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c   path3/subp3/file3sym
 EOF
index c6ce56c86b95ae778b5e044999f8c5d8c0c70816..ae086755ea7b727b29811c8ce8cd32a7e2bd6e0f 100755 (executable)
@@ -54,8 +54,6 @@ test_expect_success \
      cat >expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
-040000 tree X  path2
-040000 tree X  path2/baz
 100644 blob X  path2/baz/b
 120000 blob X  path2/bazbo
 100644 blob X  path2/foo
@@ -70,12 +68,14 @@ EOF
      test_output'
 
 
+# it used to be path1 and then path0, but with pathspec semantics
+# they are shown in canonical order.
 test_expect_success \
     'ls-tree filtered with path1 path0' \
     'git-ls-tree $tree path1 path0 >current &&
      cat >expected <<\EOF &&
-120000 blob X  path1
 100644 blob X  path0
+120000 blob X  path1
 EOF
      test_output'
 
@@ -86,45 +86,34 @@ test_expect_success \
 EOF
      test_output'
 
+# It used to show path2 and its immediate children but
+# with pathspec semantics it shows only path2
 test_expect_success \
     'ls-tree filtered with path2' \
     'git-ls-tree $tree path2 >current &&
      cat >expected <<\EOF &&
 040000 tree X  path2
-040000 tree X  path2/baz
-120000 blob X  path2/bazbo
-100644 blob X  path2/foo
-EOF
-     test_output'
-
-test_expect_success \
-    'ls-tree filtered with path2/baz' \
-    'git-ls-tree $tree path2/baz >current &&
-     cat >expected <<\EOF &&
-040000 tree X  path2/baz
-100644 blob X  path2/baz/b
 EOF
      test_output'
 
+# ... and path2/ shows the children.
 test_expect_success \
-    'ls-tree filtered with path2' \
-    'git-ls-tree $tree path2 >current &&
+    'ls-tree filtered with path2/' \
+    'git-ls-tree $tree path2/ >current &&
      cat >expected <<\EOF &&
-040000 tree X  path2
 040000 tree X  path2/baz
 120000 blob X  path2/bazbo
 100644 blob X  path2/foo
 EOF
      test_output'
 
+# The same change -- exact match does not show children of
+# path2/baz
 test_expect_success \
-    'ls-tree filtered with path2/' \
-    'git-ls-tree $tree path2/ >current &&
+    'ls-tree filtered with path2/baz' \
+    'git-ls-tree $tree path2/baz >current &&
      cat >expected <<\EOF &&
-040000 tree X  path2
 040000 tree X  path2/baz
-120000 blob X  path2/bazbo
-100644 blob X  path2/foo
 EOF
      test_output'
 
index 54103683480eb5f2a18ceac3f1018769ea31b18f..d78deb1e710056e236ba61f4b8ceb55e11c44cbf 100644 (file)
@@ -59,24 +59,16 @@ test_expect_success \
 EOF
      test_output'
 
+# Recursive does not show tree nodes anymore...
 test_expect_success \
     'ls-tree recursive' \
     'git-ls-tree -r $tree >current &&
      cat >expected <<\EOF &&
 100644 blob X  1.txt
 100644 blob X  2.txt
-040000 tree X  path0
-040000 tree X  path0/a
-040000 tree X  path0/a/b
-040000 tree X  path0/a/b/c
 100644 blob X  path0/a/b/c/1.txt
-040000 tree X  path1
-040000 tree X  path1/b
-040000 tree X  path1/b/c
 100644 blob X  path1/b/c/1.txt
-040000 tree X  path2
 100644 blob X  path2/1.txt
-040000 tree X  path3
 100644 blob X  path3/1.txt
 100644 blob X  path3/2.txt
 EOF
@@ -110,41 +102,27 @@ test_expect_success \
 EOF
      test_output'
 
+# I am not so sure about this one after ls-tree doing pathspec match.
+# Having both path0/a and path0/a/b/c makes path0/a redundant, and
+# it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
 test_expect_success \
     'ls-tree filter directories' \
     'git-ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
      cat >expected <<\EOF &&
-040000 tree X  path3
-100644 blob X  path3/1.txt
-100644 blob X  path3/2.txt
-040000 tree X  path2
-100644 blob X  path2/1.txt
 040000 tree X  path0/a/b/c
-100644 blob X  path0/a/b/c/1.txt
 040000 tree X  path1/b/c
-100644 blob X  path1/b/c/1.txt
-040000 tree X  path0/a
-040000 tree X  path0/a/b
+040000 tree X  path2
+040000 tree X  path3
 EOF
      test_output'
 
+# Again, duplicates are filtered away so this is equivalent to
+# having 1.txt and path3
 test_expect_success \
     'ls-tree filter odd names' \
     'git-ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
      cat >expected <<\EOF &&
 100644 blob X  1.txt
-100644 blob X  1.txt
-100644 blob X  1.txt
-100644 blob X  path3/1.txt
-100644 blob X  path3/1.txt
-100644 blob X  path3/1.txt
-040000 tree X  path3
-100644 blob X  path3/1.txt
-100644 blob X  path3/2.txt
-040000 tree X  path3
-100644 blob X  path3/1.txt
-100644 blob X  path3/2.txt
-040000 tree X  path3
 100644 blob X  path3/1.txt
 100644 blob X  path3/2.txt
 EOF
diff --git a/tree.c b/tree.c
index 8b42a07b208aceeaa4900c034eea8a5b6b7ac323..043f032151cbca16dc144f9128b6327c755bcedd 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -9,9 +9,16 @@ const char *tree_type = "tree";
 
 static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
 {
-       int len = strlen(pathname);
-       unsigned int size = cache_entry_size(baselen + len);
-       struct cache_entry *ce = xmalloc(size);
+       int len;
+       unsigned int size;
+       struct cache_entry *ce;
+
+       if (S_ISDIR(mode))
+               return READ_TREE_RECURSIVE;
+
+       len = strlen(pathname);
+       size = cache_entry_size(baselen + len);
+       ce = xmalloc(size);
 
        memset(ce, 0, size);
 
@@ -67,9 +74,10 @@ static int match_tree_entry(const char *base, int baselen, const char *path, uns
        return 0;
 }
 
-static int read_tree_recursive(void *buffer, unsigned long size,
-                              const char *base, int baselen,
-                              int stage, const char **match)
+int read_tree_recursive(void *buffer, unsigned long size,
+                       const char *base, int baselen,
+                       int stage, const char **match,
+                       read_tree_fn_t fn)
 {
        while (size) {
                int len = strlen(buffer)+1;
@@ -86,6 +94,14 @@ static int read_tree_recursive(void *buffer, unsigned long size,
                if (!match_tree_entry(base, baselen, path, mode, match))
                        continue;
 
+               switch (fn(sha1, base, baselen, path, mode, stage)) {
+               case 0:
+                       continue;
+               case READ_TREE_RECURSIVE:
+                       break;;
+               default:
+                       return -1;
+               }
                if (S_ISDIR(mode)) {
                        int retval;
                        int pathlen = strlen(path);
@@ -106,22 +122,20 @@ static int read_tree_recursive(void *buffer, unsigned long size,
                        retval = read_tree_recursive(eltbuf, eltsize,
                                                     newbase,
                                                     baselen + pathlen + 1,
-                                                    stage, match);
+                                                    stage, match, fn);
                        free(eltbuf);
                        free(newbase);
                        if (retval)
                                return -1;
                        continue;
                }
-               if (read_one_entry(sha1, base, baselen, path, mode, stage) < 0)
-                       return -1;
        }
        return 0;
 }
 
 int read_tree(void *buffer, unsigned long size, int stage, const char **match)
 {
-       return read_tree_recursive(buffer, size, "", 0, stage, match);
+       return read_tree_recursive(buffer, size, "", 0, stage, match, read_one_entry);
 }
 
 struct tree *lookup_tree(const unsigned char *sha1)
diff --git a/tree.h b/tree.h
index 9975e88216dc6924c1d6e9ec3c7ae6d90c2df1dd..768e5e9eb894d37c0dca452bc7d96a1271035b55 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -35,4 +35,13 @@ int parse_tree(struct tree *tree);
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const unsigned char *sha1);
 
+#define READ_TREE_RECURSIVE 1
+typedef int (*read_tree_fn_t)(unsigned char *, const char *, int, const char *, unsigned int, int);
+
+extern int read_tree_recursive(void *buffer, unsigned long size,
+                       const char *base, int baselen,
+                       int stage, const char **match,
+                       read_tree_fn_t fn);
+
+
 #endif /* TREE_H */