From: Junio C Hamano Date: Wed, 30 Nov 2005 19:05:48 +0000 (-0800) Subject: Merge branches 'jc/apply', 'lt/ls-tree', 'lt/bisect' and 'lt/merge' X-Git-Tag: v0.99.9l^2~24 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=5401f3040b61e11da79d676e42aacfa9f1131083;hp=830273d10cf1cfe646db39b87d3ccb49bf88b73a;p=git.git Merge branches 'jc/apply', 'lt/ls-tree', 'lt/bisect' and 'lt/merge' --- diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index 3b04bfee..67f51265 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 e2dfb00a..cf7ba76d 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 38599b5b..6c0696c3 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-bisect.sh b/git-bisect.sh index d92993b9..68838f3f 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-diff.sh b/git-diff.sh index b3ec84be..7baf7044 100755 --- a/git-diff.sh +++ b/git-diff.sh @@ -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" diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index c3eca8b3..739a0729 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 01292335..e599b11c 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/git-mv.perl b/git-mv.perl index 53046baf..b6c0b488 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -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 730ffd92..a847ef69 100755 --- 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 { diff --git a/ls-tree.c b/ls-tree.c index d7c7e750..d4b62198 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; +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] [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] [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; } diff --git a/rev-list.c b/rev-list.c index e17f9280..8020d974 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/setup.c b/setup.c index cc44a724..3286a568 100644 --- 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 { diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index dff7d691..22bdacaf 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 c6ce56c8..ae086755 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 -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' diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 54103683..d78deb1e 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/t/t7001-mv.sh b/t/t7001-mv.sh new file mode 100755 index 00000000..43d74c50 --- /dev/null +++ b/t/t7001-mv.sh @@ -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 8b42a07b..043f0321 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 9975e882..768e5e9e 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 */