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)
18 files changed:
Documentation/git-diff-files.txt
Documentation/tutorial.txt
diff-files.c
git-bisect.sh
git-diff.sh
git-merge-one-file.sh
git-merge-recursive.py
git-mv.perl
gitk
ls-tree.c
rev-list.c
setup.c
t/t0000-basic.sh
t/t3100-ls-tree-restrict.sh
t/t3101-ls-tree-dirname.sh
t/t7001-mv.sh [new file with mode: 0755]
tree.c
tree.h

index 3b04bfe..67f5126 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 e2dfb00..cf7ba76 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 38599b5..6c0696c 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 d92993b..68838f3 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 b3ec84b..7baf704 100755 (executable)
@@ -7,8 +7,6 @@ rev=$(git-rev-parse --revs-only --no-flags --sq "$@") || exit
 flags=$(git-rev-parse --no-revs --flags --sq "$@")
 files=$(git-rev-parse --no-revs --no-flags --sq "$@")
 
-: ${flags:="'-M' '-p'"}
-
 # I often say 'git diff --cached -p' and get scolded by git-diff-files, but
 # obviously I mean 'git diff --cached -p HEAD' in that case.
 case "$rev" in
@@ -20,6 +18,21 @@ case "$rev" in
        esac
 esac
 
+# If we do not have --name-status, --name-only nor -r, default to -p.
+# If we do not have -B nor -C, default to -M.
+case " $flags " in
+*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* )
+       ;;
+*)
+       flags="$flags'-p' " ;;
+esac
+case " $flags " in
+*" '-"[BCM]* | *" '--find-copies-harder' "*)
+       ;; # something like -M50.
+*)
+       flags="$flags'-M' " ;;
+esac
+
 case "$rev" in
 ?*' '?*' '?*)
        echo >&2 "I don't understand"
index c3eca8b..739a072 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 0129233..e599b11 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 53046ba..b6c0b48 100755 (executable)
@@ -19,15 +19,9 @@ EOT
        exit(1);
 }
 
-# Sanity checks:
-my $GIT_DIR = $ENV{'GIT_DIR'} || ".git";
-
-unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" && 
-       -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") {
-    print "Error: git repository not found.";
-    exit(1);
-}
-
+my $GIT_DIR = `git rev-parse --git-dir`;
+exit 1 if $?; # rev-parse would have given "not a git dir" message.
+chomp($GIT_DIR);
 
 our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);
 getopts("hnfkv") || usage;
diff --git a/gitk b/gitk
index 730ffd9..a847ef6 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -297,13 +297,16 @@ proc makewindow {} {
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
-    global rowctxmenu gaudydiff mergemax
+    global rowctxmenu mergemax
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
     menu .bar.file
     .bar.file add command -label "Reread references" -command rereadrefs
     .bar.file add command -label "Quit" -command doquit
+    menu .bar.edit
+    .bar add cascade -label "Edit" -menu .bar.edit
+    .bar.edit add command -label "Preferences" -command doprefs
     menu .bar.help
     .bar add cascade -label "Help" -menu .bar.help
     .bar.help add command -label "About gitk" -command about
@@ -414,25 +417,19 @@ proc makewindow {} {
     .ctop.cdet add .ctop.cdet.left
 
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
-    if {$gaudydiff} {
-       $ctext tag conf hunksep -back blue -fore white
-       $ctext tag conf d0 -back "#ff8080"
-       $ctext tag conf d1 -back green
-    } else {
-       $ctext tag conf hunksep -fore blue
-       $ctext tag conf d0 -fore red
-       $ctext tag conf d1 -fore "#00a000"
-       $ctext tag conf m0 -fore red
-       $ctext tag conf m1 -fore blue
-       $ctext tag conf m2 -fore green
-       $ctext tag conf m3 -fore purple
-       $ctext tag conf m4 -fore brown
-       $ctext tag conf mmax -fore darkgrey
-       set mergemax 5
-       $ctext tag conf mresult -font [concat $textfont bold]
-       $ctext tag conf msep -font [concat $textfont bold]
-       $ctext tag conf found -back yellow
-    }
+    $ctext tag conf hunksep -fore blue
+    $ctext tag conf d0 -fore red
+    $ctext tag conf d1 -fore "#00a000"
+    $ctext tag conf m0 -fore red
+    $ctext tag conf m1 -fore blue
+    $ctext tag conf m2 -fore green
+    $ctext tag conf m3 -fore purple
+    $ctext tag conf m4 -fore brown
+    $ctext tag conf mmax -fore darkgrey
+    set mergemax 5
+    $ctext tag conf mresult -font [concat $textfont bold]
+    $ctext tag conf msep -font [concat $textfont bold]
+    $ctext tag conf found -back yellow
 
     frame .ctop.cdet.right
     set cflist .ctop.cdet.right.cfiles
@@ -533,7 +530,7 @@ proc click {w} {
 
 proc savestuff {w} {
     global canv canv2 canv3 ctext cflist mainfont textfont
-    global stuffsaved findmergefiles gaudydiff maxgraphpct
+    global stuffsaved findmergefiles maxgraphpct
     global maxwidth
 
     if {$stuffsaved} return
@@ -543,7 +540,6 @@ proc savestuff {w} {
        puts $f [list set mainfont $mainfont]
        puts $f [list set textfont $textfont]
        puts $f [list set findmergefiles $findmergefiles]
-       puts $f [list set gaudydiff $gaudydiff]
        puts $f [list set maxgraphpct $maxgraphpct]
        puts $f [list set maxwidth $maxwidth]
        puts $f "set geometry(width) [winfo width .ctop]"
@@ -2841,7 +2837,6 @@ proc getblobdiffline {bdf ids} {
     global diffids blobdifffd ctext curdifftag curtagstart
     global diffnexthead diffnextnote difffilestart
     global nextupdate diffinhdr treediffs
-    global gaudydiff
 
     set n [gets $bdf line]
     if {$n < 0} {
@@ -2890,26 +2885,14 @@ proc getblobdiffline {bdf ids} {
        set diffinhdr 0
     } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
                   $line match f1l f1c f2l f2c rest]} {
-       if {$gaudydiff} {
-           $ctext insert end "\t" hunksep
-           $ctext insert end "    $f1l    " d0 "    $f2l    " d1
-           $ctext insert end "    $rest \n" hunksep
-       } else {
-           $ctext insert end "$line\n" hunksep
-       }
+       $ctext insert end "$line\n" hunksep
        set diffinhdr 0
     } else {
        set x [string range $line 0 0]
        if {$x == "-" || $x == "+"} {
            set tag [expr {$x == "+"}]
-           if {$gaudydiff} {
-               set line [string range $line 1 end]
-           }
            $ctext insert end "$line\n" d$tag
        } elseif {$x == " "} {
-           if {$gaudydiff} {
-               set line [string range $line 1 end]
-           }
            $ctext insert end "$line\n"
        } elseif {$diffinhdr || $x == "\\"} {
            # e.g. "\ No newline at end of file"
@@ -3634,26 +3617,80 @@ proc doquit {} {
     destroy .
 }
 
-proc formatdate {d} {
-    global hours nhours tfd fastdate
+proc doprefs {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
 
-    if {!$fastdate} {
-       return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+    set top .gitkprefs
+    set prefstop $top
+    if {[winfo exists $top]} {
+       raise $top
+       return
     }
-    set hr [expr {$d / 3600}]
-    set ms [expr {$d % 3600}]
-    if {![info exists hours($hr)]} {
-       set hours($hr) [clock format $d -format "%Y-%m-%d %H"]
-       set nhours($hr) 0
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+       set oldprefs($v) [set $v]
     }
-    incr nhours($hr)
-    set minsec [format "%.2d:%.2d" [expr {$ms/60}] [expr {$ms%60}]]
-    return "$hours($hr):$minsec"
+    toplevel $top
+    wm title $top "Gitk preferences"
+    label $top.ldisp -text "Commit list display options"
+    grid $top.ldisp - -sticky w -pady 10
+    label $top.spacer -text " "
+    label $top.maxwidthl -text "Maximum graph width (lines)" \
+       -font optionfont
+    spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
+    grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
+    label $top.maxpctl -text "Maximum graph width (% of pane)" \
+       -font optionfont
+    spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
+    grid x $top.maxpctl $top.maxpct -sticky w
+    checkbutton $top.findm -variable findmergefiles
+    label $top.findml -text "Include merges for \"Find\" in \"Files\"" \
+       -font optionfont
+    grid $top.findm $top.findml - -sticky w
+    label $top.ddisp -text "Diff display options"
+    grid $top.ddisp - -sticky w -pady 10
+    label $top.diffoptl -text "Options for diff program" \
+       -font optionfont
+    entry $top.diffopt -width 20 -textvariable diffopts
+    grid x $top.diffoptl $top.diffopt -sticky w
+    frame $top.buts
+    button $top.buts.ok -text "OK" -command prefsok
+    button $top.buts.can -text "Cancel" -command prefscan
+    grid $top.buts.ok $top.buts.can
+    grid columnconfigure $top.buts 0 -weight 1 -uniform a
+    grid columnconfigure $top.buts 1 -weight 1 -uniform a
+    grid $top.buts - - -pady 10 -sticky ew
+}
+
+proc prefscan {} {
+    global maxwidth maxgraphpct diffopts findmergefiles
+    global oldprefs prefstop
+
+    foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
+       set $v $oldprefs($v)
+    }
+    catch {destroy $prefstop}
+    unset prefstop
+}
+
+proc prefsok {} {
+    global maxwidth maxgraphpct
+    global oldprefs prefstop
+
+    catch {destroy $prefstop}
+    unset prefstop
+    if {$maxwidth != $oldprefs(maxwidth)
+       || $maxgraphpct != $oldprefs(maxgraphpct)} {
+       redisplay
+    }
+}
+
+proc formatdate {d} {
+    return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
 }
 
 # defaults...
 set datemode 0
-set boldnames 0
 set diffopts "-U 5 -p"
 set wrcomcmd "git-diff-tree --stdin -p --pretty"
 
@@ -3668,7 +3705,6 @@ if {$gitencoding == ""} {
 set mainfont {Helvetica 9}
 set textfont {Courier 9}
 set findmergefiles 0
-set gaudydiff 0
 set maxgraphpct 50
 set maxwidth 16
 set revlistorder 0
@@ -3679,15 +3715,13 @@ set colors {green red blue magenta darkgrey brown orange}
 catch {source ~/.gitk}
 
 set namefont $mainfont
-if {$boldnames} {
-    lappend namefont bold
-}
+
+font create optionfont -family sans-serif -size -12
 
 set revtreeargs {}
 foreach arg $argv {
     switch -regexp -- $arg {
        "^$" { }
-       "^-b" { set boldnames 1 }
        "^-d" { set datemode 1 }
        "^-r" { set revlistorder 1 }
        default {
index d7c7e75..d4b6219 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 e17f928..8020d97 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;
diff --git a/setup.c b/setup.c
index cc44a72..3286a56 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -116,7 +116,7 @@ static const char *setup_git_directory_1(void)
                if (validate_symref(path))
                        goto bad_dir_environ;
                if (getenv(DB_ENVIRONMENT)) {
-                       if (access(DB_ENVIRONMENT, X_OK))
+                       if (access(getenv(DB_ENVIRONMENT), X_OK))
                                goto bad_dir_environ;
                }
                else {
index dff7d69..22bdaca 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 c6ce56c..ae08675 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 5410368..d78deb1 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/t/t7001-mv.sh b/t/t7001-mv.sh
new file mode 100755 (executable)
index 0000000..43d74c5
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='git-mv in subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+    'prepare reference tree' \
+    'mkdir path0 path1 &&
+     cp ../../COPYING path0/COPYING &&
+     git-add path0/COPYING &&
+     git-commit -m add -a'
+
+test_expect_success \
+    'moving the file' \
+    'cd path0 && git-mv COPYING ../path1/COPYING'
+
+# in path0 currently
+test_expect_success \
+    'commiting the change' \
+    'cd .. && git-commit -m move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+    grep -E "^R100.+path0/COPYING.+path1/COPYING"'
+
+test_done
diff --git a/tree.c b/tree.c
index 8b42a07..043f032 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 9975e88..768e5e9 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 */