diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index 3b04bfeec6..67f51265e6 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -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 diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index e2dfb00ab1..cf7ba76ddf 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -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 ------------------------------------------------ diff --git a/diff-files.c b/diff-files.c index 38599b5b75..6c0696c34f 100644 --- a/diff-files.c +++ b/diff-files.c @@ -7,12 +7,12 @@ #include "diff.h" static const char diff_files_usage[] = -"git-diff-files [-q] " -"[] [...]" +"git-diff-files [-q] [-0/-1/2/3] [] [...]" 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) { diff --git a/git-applymbox.sh b/git-applymbox.sh index 24d4a8cb4e..c686cc8d27 100755 --- a/git-applymbox.sh +++ b/git-applymbox.sh @@ -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 diff --git a/git-applypatch.sh b/git-applypatch.sh index f0549960fb..4c577eb835 100755 --- a/git-applypatch.sh +++ b/git-applypatch.sh @@ -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) && diff --git a/git-bisect.sh b/git-bisect.sh index d92993b94e..68838f3fad 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -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 [] reset bisect state and start bisection. git bisect bad [] mark a known-bad revision. git bisect good [...] mark ... 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" diff --git a/git-format-patch.sh b/git-format-patch.sh index 9b4088045a..1eebe857c0 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -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] [-...] @@ -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 diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index c3eca8b332..739a07292a 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -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" diff --git a/git-merge-recursive.py b/git-merge-recursive.py index 0129233550..e599b11cc5 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -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.") diff --git a/ls-tree.c b/ls-tree.c index d7c7e750fb..d4b62198a0 100644 --- 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; - -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 struct tree_entry_list *find_entry(const char *path, char *pathbuf) -{ - 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 */ - break; - } - elem = elem->next; - } - if (!elem) - return NULL; - - oldelem = elem; - } - path = next; - } - - 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'; - - return 0; -} - -static int show_entry(struct tree_entry_list *e, int level, char *pathbuf) -{ - 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; -} +const char **pathspec; static const char ls_tree_usage[] = "git-ls-tree [-d] [-r] [-z] [path...]"; -int main(int argc, char **argv) +static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { - static char *path0[] = { "", NULL }; - char **path; - unsigned char sha1[20]; + 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; + 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; + } + } + type = "tree"; + } + + 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; +} + +int main(int argc, const char **argv) +{ + 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; } diff --git a/rev-list.c b/rev-list.c index e17f928061..8020d974f2 100644 --- a/rev-list.c +++ b/rev-list.c @@ -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; diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index dff7d69163..22bdacaf78 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -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 diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index c6ce56c86b..ae086755ea 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -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 +EOF + test_output' + +# ... and path2/ shows the children. +test_expect_success \ + 'ls-tree filtered with path2/' \ + 'git-ls-tree $tree path2/ >current && + cat >expected <<\EOF && 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/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' - -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/' \ - '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' diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 5410368348..d78deb1e71 100644 --- a/t/t3101-ls-tree-dirname.sh +++ b/t/t3101-ls-tree-dirname.sh @@ -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 8b42a07b20..043f032151 100644 --- 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 9975e88216..768e5e9eb8 100644 --- 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 */