From 2ed49d542470e265cbebbe1eb8083f61a512c680 Mon Sep 17 00:00:00 2001 From: Jeff Hobbs Date: Tue, 22 Nov 2005 17:39:53 -0800 Subject: [PATCH] gitk: put braces around exprs This braces all exprs. It just seemed to be a few that were missed. Signed-off-by: Paul Mackerras --- gitk | 83 ++++++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/gitk b/gitk index 3dd97e291..7b733e9c5 100755 --- a/gitk +++ b/gitk @@ -30,7 +30,7 @@ proc getcommits {rargs} { set commits {} set phase getcommits set startmsecs [clock clicks -milliseconds] - set nextupdate [expr $startmsecs + 100] + set nextupdate [expr {$startmsecs + 100}] set ncmupdate 1 if [catch { set parse_args [concat --default HEAD $rargs] @@ -74,9 +74,9 @@ proc getcommitlines {commfd} { } if {[string range $err 0 4] == "usage"} { set err \ -{Gitk: error reading commits: bad arguments to git-rev-list. -(Note: arguments to gitk are passed to git-rev-list -to allow selection of commits to be displayed.)} + "Gitk: error reading commits: bad arguments to git-rev-list.\ + (Note: arguments to gitk are passed to git-rev-list\ + to allow selection of commits to be displayed.)" } else { set err "Error reading commits: $err" } @@ -310,10 +310,10 @@ proc makewindow {} { . configure -menu .bar if {![info exists geometry(canv1)]} { - set geometry(canv1) [expr 45 * $charspc] - set geometry(canv2) [expr 30 * $charspc] - set geometry(canv3) [expr 15 * $charspc] - set geometry(canvh) [expr 25 * $linespc + 4] + set geometry(canv1) [expr {45 * $charspc}] + set geometry(canv2) [expr {30 * $charspc}] + set geometry(canv3) [expr {15 * $charspc}] + set geometry(canvh) [expr {25 * $linespc + 4}] set geometry(ctextw) 80 set geometry(ctexth) 30 set geometry(cflistw) 30 @@ -548,10 +548,10 @@ proc savestuff {w} { puts $f [list set maxwidth $maxwidth] puts $f "set geometry(width) [winfo width .ctop]" puts $f "set geometry(height) [winfo height .ctop]" - puts $f "set geometry(canv1) [expr [winfo width $canv]-2]" - puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]" - puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]" - puts $f "set geometry(canvh) [expr [winfo height $canv]-2]" + puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]" + puts $f "set geometry(canv2) [expr {[winfo width $canv2]-2}]" + puts $f "set geometry(canv3) [expr {[winfo width $canv3]-2}]" + puts $f "set geometry(canvh) [expr {[winfo height $canv]-2}]" set wid [expr {([winfo width $ctext] - 8) \ / [font measure $textfont "0"]}] puts $f "set geometry(ctextw) $wid" @@ -580,12 +580,12 @@ proc resizeclistpanes {win w} { set sash0 30 } if {$sash1 < $sash0 + 20} { - set sash1 [expr $sash0 + 20] + set sash1 [expr {$sash0 + 20}] } if {$sash1 > $w - 10} { - set sash1 [expr $w - 10] + set sash1 [expr {$w - 10}] if {$sash0 > $sash1 - 20} { - set sash0 [expr $sash1 - 20] + set sash0 [expr {$sash1 - 20}] } } } @@ -608,7 +608,7 @@ proc resizecdetpanes {win w} { set sash0 45 } if {$sash0 > $w - 15} { - set sash0 [expr $w - 15] + set sash0 [expr {$w - 15}] } } $win sash place 0 $sash0 [lindex $s0 1] @@ -819,9 +819,9 @@ proc drawcommitline {level} { } set x [xcoord $level $level $lineno] set y1 $canvy - set canvy [expr $canvy + $linespc] + set canvy [expr {$canvy + $linespc}] allcanvs conf -scrollregion \ - [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]] + [list 0 0 0 [expr {$y1 + 0.5 * $linespc + 2}]] if {[info exists mainline($id)]} { lappend mainline($id) $x $y1 if {$mainlinearrow($id) ne "none"} { @@ -830,8 +830,8 @@ proc drawcommitline {level} { } drawlines $id 0 0 set orad [expr {$linespc / 3}] - set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \ - [expr $x + $orad - 1] [expr $y1 + $orad - 1] \ + set t [$canv create oval [expr {$x - $orad}] [expr {$y1 - $orad}] \ + [expr {$x + $orad - 1}] [expr {$y1 + $orad - 1}] \ -fill $ofill -outline black -width 1] $canv raise $t $canv bind $t <1> {selcanvline {} %x %y} @@ -886,8 +886,8 @@ proc drawtags {id x xt y1} { } set delta [expr {int(0.5 * ($linespc - $lthickness))}] - set yt [expr $y1 - 0.5 * $linespc] - set yb [expr $yt + $linespc - 1] + set yt [expr {$y1 - 0.5 * $linespc}] + set yb [expr {$yt + $linespc - 1}] set xvals {} set wvals {} foreach tag $marks { @@ -900,12 +900,12 @@ proc drawtags {id x xt y1} { -width $lthickness -fill black -tags tag.$id] $canv lower $t foreach tag $marks x $xvals wid $wvals { - set xl [expr $x + $delta] - set xr [expr $x + $delta + $wid + $lthickness] + set xl [expr {$x + $delta}] + set xr [expr {$x + $delta + $wid + $lthickness}] if {[incr ntags -1] >= 0} { # draw a tag - set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \ - $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \ + set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \ + $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \ -width 1 -outline black -fill yellow -tags tag.$id] $canv bind $t <1> [list showtag $tag 1] set rowtextx($idline($id)) [expr {$xr + $linespc}] @@ -916,7 +916,7 @@ proc drawtags {id x xt y1} { } else { set col "#ddddff" } - set xl [expr $xl - $delta/2] + set xl [expr {$xl - $delta/2}] $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \ -width 1 -outline black -fill $col -tags tag.$id } @@ -1491,7 +1491,7 @@ proc drawgraph {} { if {$displayorder == {}} return set startmsecs [clock clicks -milliseconds] - set nextupdate [expr $startmsecs + 100] + set nextupdate [expr {$startmsecs + 100}] set ncmupdate 1 initgraph foreach id $displayorder { @@ -1520,7 +1520,7 @@ proc drawrest {} { } drawmore 0 set phase {} - set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs] + set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}] #puts "overall $drawmsecs ms for $numcommits commits" if {$redisplaying} { if {$stopped == 0 && [info exists selectedline]} { @@ -1548,8 +1548,8 @@ proc findmatches {f} { set matches {} set i 0 while {[set j [string first $foundstring $str $i]] >= 0} { - lappend matches [list $j [expr $j+$foundstrlen-1]] - set i [expr $j + $foundstrlen] + lappend matches [list $j [expr {$j+$foundstrlen-1}]] + set i [expr {$j + $foundstrlen}] } } return $matches @@ -1630,7 +1630,7 @@ proc findselectline {l} { set matches [findmatches $f] foreach match $matches { set start [lindex $match 0] - set end [expr [lindex $match 1] + 1] + set end [expr {[lindex $match 1] + 1}] $ctext tag add found "1.0 + $start c" "1.0 + $end c" } } @@ -1984,9 +1984,10 @@ proc markmatches {canv l str tag matches font} { set start [lindex $match 0] set end [lindex $match 1] if {$start > $end} continue - set xoff [font measure $font [string range $str 0 [expr $start-1]]] - set xlen [font measure $font [string range $str 0 [expr $end]]] - set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \ + set xoff [font measure $font [string range $str 0 [expr {$start-1}]]] + set xlen [font measure $font [string range $str 0 [expr {$end}]]] + set t [$canv create rect [expr {$x0+$xoff}] $y0 \ + [expr {$x0+$xlen+2}] $y1 \ -outline {} -tags matches -fill yellow] $canv lower $t } @@ -2078,8 +2079,8 @@ proc selectline {l isnew} { set ytop [expr {$y - $linespc - 1}] set ybot [expr {$y + $linespc + 1}] set wnow [$canv yview] - set wtop [expr [lindex $wnow 0] * $ymax] - set wbot [expr [lindex $wnow 1] * $ymax] + set wtop [expr {[lindex $wnow 0] * $ymax}] + set wbot [expr {[lindex $wnow 1] * $ymax}] set wh [expr {$wbot - $wtop}] set newtop $wtop if {$ytop < $wtop} { @@ -2105,7 +2106,7 @@ proc selectline {l isnew} { if {$newtop < 0} { set newtop 0 } - allcanvs yview moveto [expr $newtop * 1.0 / $ymax] + allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}] } if {$isnew} { @@ -2173,7 +2174,7 @@ proc selectline {l isnew} { proc selnextline {dir} { global selectedline if {![info exists selectedline]} return - set l [expr $selectedline + $dir] + set l [expr {$selectedline + $dir}] unmarkmatches selectline $l 1 } @@ -2966,8 +2967,8 @@ proc setcoords {} { set linespc [font metrics $mainfont -linespace] set charspc [font measure $mainfont "m"] - set canvy0 [expr 3 + 0.5 * $linespc] - set canvx0 [expr 3 + 0.5 * $linespc] + set canvy0 [expr {3 + 0.5 * $linespc}] + set canvx0 [expr {3 + 0.5 * $linespc}] set lthickness [expr {int($linespc / 9) + 1}] set xspc1(0) $linespc set xspc2 $linespc -- cgit v1.2.1 From 495473c08a957464e7e326ae7bafb477db90fcbe Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Tue, 22 Nov 2005 23:15:01 -0500 Subject: [PATCH] gitk: UTF-8 support Add gitencoding variable and set it to "utf-8". Use it for converting git-rev-list output. Signed-off-by: Pavel Roskin Signed-off-by: Paul Mackerras --- gitk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index 7b733e9c5..ecc1688c7 100755 --- a/gitk +++ b/gitk @@ -19,7 +19,7 @@ proc gitdir {} { proc getcommits {rargs} { global commits commfd phase canv mainfont env global startmsecs nextupdate ncmupdate - global ctext maincursor textcursor leftover + global ctext maincursor textcursor leftover gitencoding # check that we can find a .git directory somewhere... set gitdir [gitdir] @@ -49,7 +49,7 @@ proc getcommits {rargs} { exit 1 } set leftover {} - fconfigure $commfd -blocking 0 -translation lf + fconfigure $commfd -blocking 0 -translation lf -encoding $gitencoding fileevent $commfd readable [list getcommitlines $commfd] $canv delete all $canv create text 3 3 -anchor nw -text "Reading commits..." \ @@ -3658,6 +3658,7 @@ set datemode 0 set boldnames 0 set diffopts "-U 5 -p" set wrcomcmd "git-diff-tree --stdin -p --pretty" +set gitencoding "utf-8" set mainfont {Helvetica 9} set textfont {Courier 9} -- cgit v1.2.1 From ab5f86275c070331c308a133afa3a648b347c062 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 20:57:02 -0800 Subject: shell.c: complain on insufficient arguments. Originally noticed by Tommi Virtanen, but done slightly differently. Signed-off-by: Junio C Hamano --- shell.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell.c b/shell.c index 2c4789e94..cd316185e 100644 --- a/shell.c +++ b/shell.c @@ -5,8 +5,7 @@ static int do_generic_cmd(const char *me, char *arg) { const char *my_argv[4]; - arg = sq_dequote(arg); - if (!arg) + if (!arg || !(arg = sq_dequote(arg))) die("bad argument"); my_argv[0] = me; -- cgit v1.2.1 From f359ae42ac102ef98d5708f1dc8b06e6af2701c1 Mon Sep 17 00:00:00 2001 From: Alexander Litvinov Date: Wed, 23 Nov 2005 16:19:41 +0600 Subject: git-mv is not able to handle big directories Use update-index --stdin to handle large number of files without breaking exec() argument storage limit. [jc: with minor cleanup from the version posted on the list] Signed-off-by: Junio C Hamano --- git-mv.perl | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/git-mv.perl b/git-mv.perl index bf54c3841..b2eace5b2 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -193,14 +193,27 @@ if ($opt_n) { exit(1); } -my $rc; -if (scalar @changedfiles >0) { - $rc = system("git-update-index","--",@changedfiles); - die "git-update-index failed to update changed files with code $?\n" if $rc; +if (@changedfiles) { + open(H, "| git-update-index -z --stdin") + or die "git-update-index failed to update changed files with code $!\n"; + foreach my $fileName (@changedfiles) { + print H "$fileName\0"; + } + close(H); +} +if (@addedfiles) { + open(H, "| git-update-index --add -z --stdin") + or die "git-update-index failed to add new names with code $!\n"; + foreach my $fileName (@addedfiles) { + print H "$fileName\0"; + } + close(H); } -if (scalar @addedfiles >0) { - $rc = system("git-update-index","--add","--",@addedfiles); - die "git-update-index failed to add new names with code $?\n" if $rc; +if (@deletedfiles) { + open(H, "| git-update-index --remove -z --stdin") + or die "git-update-index failed to remove old names with code $!\n"; + foreach my $fileName (@deletedfiles) { + print H "$fileName\0"; + } + close(H); } -$rc = system("git-update-index","--remove","--",@deletedfiles); -die "git-update-index failed to remove old names with code $?\n" if $rc; -- cgit v1.2.1 From 2d76d0d151239538c43b61572283405b10df43ac Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 23:36:58 -0800 Subject: name-rev: fix off-by-one error in --stdin. It dropped the last hexdigit in the object name. [jc: Noticed and patch supplied by ALASCM, reworked to apply at the right place by me] Signed-off-by: Junio C Hamano --- name-rev.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/name-rev.c b/name-rev.c index 817e36b79..7d89401a4 100644 --- a/name-rev.c +++ b/name-rev.c @@ -217,10 +217,9 @@ int main(int argc, char **argv) if (!strcmp(name, "undefined")) continue; - fwrite(p_start, p - p_start, 1, stdout); - fputc('(', stdout); - fputs(name, stdout); - fputc(')', stdout); + fwrite(p_start, p - p_start + 1, 1, + stdout); + printf(" (%s)", name); p_start = p + 1; } } -- cgit v1.2.1 From 51b3c00e9d95371a9ad202204f01c5981f241b20 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Nov 2005 12:09:07 -0800 Subject: format-patch: output filename reported to stdout verbatim. Prepending asterisk to the output was just adding noise, and making scripts like proposed git-send-mail by Andreas Ericsson do unnecessary work. Signed-off-by: Junio C Hamano --- git-format-patch.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-format-patch.sh b/git-format-patch.sh index bc5687653..9b4088045 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -268,7 +268,7 @@ do file=`printf '%04d-%stxt' $i "$title"` if test '' = "$stdout" then - echo "* $file" + echo "$file" process_one >"$outdir$file" if test t = "$check" then @@ -279,7 +279,7 @@ do : fi else - echo >&2 "* $file" + echo >&2 "$file" process_one fi i=`expr "$i" + 1` -- cgit v1.2.1 From ab9cb76f661c1794800361c27bc5a515245aaaef Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 15:59:09 -0800 Subject: Repository format version check. This adds the repository format version code, first done by Martin Atukunda. Signed-off-by: Junio C Hamano --- cache.h | 5 +++++ environment.c | 1 + setup.c | 16 ++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/cache.h b/cache.h index 6ac94c5a1..de53f4128 100644 --- a/cache.h +++ b/cache.h @@ -182,6 +182,10 @@ extern int trust_executable_bit; extern int only_use_symrefs; extern int diff_rename_limit_default; +#define GIT_REPO_VERSION 0 +extern int repository_format_version; +extern int check_repository_format(void); + #define MTIME_CHANGED 0x0001 #define CTIME_CHANGED 0x0002 #define OWNER_CHANGED 0x0004 @@ -388,6 +392,7 @@ extern int git_config_int(const char *, const char *); extern int git_config_bool(const char *, const char *); extern int git_config_set(const char *, const char *); extern int git_config_set_multivar(const char *, const char *, const char *, int); +extern int check_repository_format_version(const char *var, const char *value); #define MAX_GITNAME (1000) extern char git_default_email[MAX_GITNAME]; diff --git a/environment.c b/environment.c index b5026f126..3f194732e 100644 --- a/environment.c +++ b/environment.c @@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; int trust_executable_bit = 1; int only_use_symrefs = 0; +int repository_format_version = 0; static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; diff --git a/setup.c b/setup.c index ab3c778e8..0e2e3c8c3 100644 --- a/setup.c +++ b/setup.c @@ -154,6 +154,22 @@ static const char *setup_git_directory_1(void) return cwd + offset; } +int check_repository_format_version(const char *var, const char *value) +{ + if (strcmp(var, "core.repositoryformatversion") == 0) + repository_format_version = git_config_int(var, value); + return 0; +} + +int check_repository_format(void) +{ + git_config(check_repository_format_version); + if (GIT_REPO_VERSION < repository_format_version) + die ("Expected git repo version <= %d, found %d", + GIT_REPO_VERSION, repository_format_version); + return 0; +} + const char *setup_git_directory(void) { const char *retval = setup_git_directory_1(); -- cgit v1.2.1 From 1644162ad53da1d0107c5c45c866e75ef95660bb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 10:48:26 -0800 Subject: Check repository format version in enter_repo(). After daemon, upload-pack and receive-pack find out where the git directory is and chdir() there, make sure that repository is in a format we understand, after putenv("GIT_DIR=.") so that it knows to pick up the configuration file from there. Signed-off-by: Junio C Hamano --- path.c | 1 + 1 file changed, 1 insertion(+) diff --git a/path.c b/path.c index 4d889473a..2c077c0c8 100644 --- a/path.c +++ b/path.c @@ -199,6 +199,7 @@ char *enter_repo(char *path, int strict) if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && validate_symref("HEAD") == 0) { putenv("GIT_DIR=."); + check_repository_format(); return current_dir(); } -- cgit v1.2.1 From 4f629539cd99fb9fc68dbdc56812f291565d0f87 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 16:03:56 -0800 Subject: init-db: check template and repository format. This makes init-db repository version aware. It checks if an existing config file says the repository being reinitialized is of a wrong version and aborts before doing further harm. When copying the templates, it makes sure the they are of the right repository format version. Otherwise the templates are ignored with an warning message. It copies the templates before creating the HEAD, and if the config file is copied from the template directory, reads it, primarily to pick up the value of core.symrefsonly. It changes the way the result of the filemode reliability test is written to the configuration file using git_config_set(). The test is done even if the config file was copied from the templates. And finally, our own repository format version is written to the config file. Signed-off-by: Junio C Hamano --- cache.h | 1 + config.c | 16 +++++++++--- init-db.c | 89 ++++++++++++++++++++++++++++++++++++--------------------------- 3 files changed, 64 insertions(+), 42 deletions(-) diff --git a/cache.h b/cache.h index de53f4128..61bf884f8 100644 --- a/cache.h +++ b/cache.h @@ -387,6 +387,7 @@ extern int gitfakemunmap(void *start, size_t length); typedef int (*config_fn_t)(const char *, const char *); extern int git_default_config(const char *, const char *); +extern int git_config_from_file(config_fn_t fn, const char *); extern int git_config(config_fn_t fn); extern int git_config_int(const char *, const char *); extern int git_config_bool(const char *, const char *); diff --git a/config.c b/config.c index 5cc853508..0c43d7615 100644 --- a/config.c +++ b/config.c @@ -11,6 +11,7 @@ #define MAXNAME (256) static FILE *config_file; +static char *config_file_name; static int config_linenr; static int get_next_char(void) { @@ -186,7 +187,7 @@ static int git_parse_file(config_fn_t fn) if (get_value(fn, var, baselen+1) < 0) break; } - die("bad config file line %d", config_linenr); + die("bad config file line %d in %s", config_linenr, config_file_name); } int git_config_int(const char *name, const char *value) @@ -197,7 +198,7 @@ int git_config_int(const char *name, const char *value) if (!*end) return val; } - die("bad config value for '%s'", name); + die("bad config value for '%s' in %s", name, config_file_name); } int git_config_bool(const char *name, const char *value) @@ -240,21 +241,28 @@ int git_default_config(const char *var, const char *value) return 0; } -int git_config(config_fn_t fn) +int git_config_from_file(config_fn_t fn, const char *filename) { int ret; - FILE *f = fopen(git_path("config"), "r"); + FILE *f = fopen(filename, "r"); ret = -1; if (f) { config_file = f; + config_file_name = filename; config_linenr = 1; ret = git_parse_file(fn); fclose(f); + config_file_name = NULL; } return ret; } +int git_config(config_fn_t fn) +{ + return git_config_from_file(fn, git_path("config")); +} + /* * Find all the stuff for git_config_set() below. */ diff --git a/init-db.c b/init-db.c index bd88291b0..8195b6842 100644 --- a/init-db.c +++ b/init-db.c @@ -132,6 +132,23 @@ static void copy_templates(const char *git_dir, int len, char *template_dir) return; } + /* Make sure that template is from the correct vintage */ + strcpy(template_path + template_len, "config"); + repository_format_version = 0; + git_config_from_file(check_repository_format_version, + template_path); + template_path[template_len] = 0; + + if (repository_format_version && + repository_format_version != GIT_REPO_VERSION) { + fprintf(stderr, "warning: not copying templates of " + "a wrong format version %d from '%s'\n", + repository_format_version, + template_dir); + closedir(dir); + return; + } + memcpy(path, git_dir, len); path[len] = 0; copy_templates_1(path, len, @@ -140,12 +157,13 @@ static void copy_templates(const char *git_dir, int len, char *template_dir) closedir(dir); } -static void create_default_files(const char *git_dir, - char *template_path) +static void create_default_files(const char *git_dir, char *template_path) { unsigned len = strlen(git_dir); static char path[PATH_MAX]; unsigned char sha1[20]; + struct stat st1; + char repo_version_string[10]; if (len > sizeof(path)-50) die("insane git directory %s", git_dir); @@ -164,6 +182,15 @@ static void create_default_files(const char *git_dir, strcpy(path + len, "refs/tags"); safe_create_dir(path); + /* First copy the templates -- we might have the default + * config file there, in which case we would want to read + * from it after installing. + */ + path[len] = 0; + copy_templates(path, len, template_path); + + git_config(git_default_config); + /* * Create the default symlink from ".git/HEAD" to the "master" * branch, if it does not exist yet. @@ -173,44 +200,22 @@ static void create_default_files(const char *git_dir, if (create_symref(path, "refs/heads/master") < 0) exit(1); } - path[len] = 0; - copy_templates(path, len, template_path); - /* - * Find out if we can trust the executable bit. - */ - safe_create_dir(path); + /* This forces creation of new config file */ + sprintf(repo_version_string, "%d", GIT_REPO_VERSION); + git_config_set("core.repositoryformatversion", repo_version_string); + + path[len] = 0; strcpy(path + len, "config"); - if (access(path, R_OK) < 0) { - static const char contents[] = - "#\n" - "# This is the config file\n" - "#\n" - "\n" - "; core variables\n" - "[core]\n" - " ; Don't trust file modes\n" - " filemode = false\n" - "\n"; - FILE *config = fopen(path, "w"); - struct stat st; - - if (!config) - die("Can not write to %s?", path); - - fwrite(contents, sizeof(contents)-1, 1, config); - - fclose(config); - - if (!lstat(path, &st)) { - struct stat st2; - if (!chmod(path, st.st_mode ^ S_IXUSR) && - !lstat(path, &st2) && - st.st_mode != st2.st_mode) - unlink(path); - else - fprintf(stderr, "Ignoring file modes\n"); - } + + /* Check filemode trustability */ + if (!lstat(path, &st1)) { + struct stat st2; + int filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) && + !lstat(path, &st2) && + st1.st_mode != st2.st_mode); + git_config_set("core.filemode", + filemode ? "true" : "false"); } } @@ -249,6 +254,14 @@ int main(int argc, char **argv) fprintf(stderr, "defaulting to local storage area\n"); } safe_create_dir(git_dir); + + /* Check to see if the repository version is right. + * Note that a newly created repository does not have + * config file, so this will not fail. What we are catching + * is an attempt to reinitialize new repository with an old tool. + */ + check_repository_format(); + create_default_files(git_dir, template_dir); /* -- cgit v1.2.1 From 22752e4c43e1bc864eaf1c2c015031a2b5e98a3b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 16:08:48 -0800 Subject: setup_git_directory(): check repository format version. After figuring out the GIT_DIR location, make sure the repository is of the right vintage, by calling check_repository_format(). . Signed-off-by: Junio C Hamano --- setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.c b/setup.c index 0e2e3c8c3..cc44a724b 100644 --- a/setup.c +++ b/setup.c @@ -173,5 +173,6 @@ int check_repository_format(void) const char *setup_git_directory(void) { const char *retval = setup_git_directory_1(); + check_repository_format(); return retval; } -- cgit v1.2.1 From 3ae64dff6894adc995c913aaf7fe2d65c78c3529 Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Sun, 27 Nov 2005 21:58:52 +0100 Subject: git-mv: shrink usage, no usage on error Small fixes to be consistent with other git scripts: - usage message is only about options and arguments - on error, exit(1) without the usage message Additionally, "beautifies" output with -n a little bit Signed-off-by: Josef Weidendorfer Signed-off-by: Junio C Hamano --- git-mv.perl | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/git-mv.perl b/git-mv.perl index b2eace5b2..990bec503 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -13,22 +13,8 @@ use Getopt::Std; sub usage() { print < -$0 [-f] [-k] [-n] ... - -In the first form, source must exist and be either a file, -symlink or directory, dest must not exist. It renames source to dest. -In the second form, the last argument has to be an existing -directory; the given sources will be moved into this directory. - -Updates the git cache to reflect the change. -Use "git commit" to make the change permanently. - -Options: - -f Force renaming/moving, even if target exists - -k Continue on error by skipping - not-existing or not revision-controlled source - -n Do nothing; show what would happen +$0 [-f] [-n] +$0 [-f] [-n] [-k] ... EOT exit(1); } @@ -38,8 +24,8 @@ my $GIT_DIR = $ENV{'GIT_DIR'} || ".git"; unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" && -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") { - print "Git repository not found."; - usage(); + print "Error: git repository not found."; + exit(1); } @@ -70,7 +56,7 @@ else { print "Error: moving to directory '" . $ARGV[$argCount-1] . "' not possible; not exisiting\n"; - usage; + exit(1); } @srcArgs = ($ARGV[0]); @dstArgs = ($ARGV[1]); @@ -148,7 +134,7 @@ while(scalar @srcArgs > 0) { next; } print "Error: $bad\n"; - usage(); + exit(1); } push @srcs, $src; push @dsts, $dst; @@ -187,33 +173,39 @@ while(scalar @srcs > 0) { } if ($opt_n) { + if (@changedfiles) { print "Changed : ". join(", ", @changedfiles) ."\n"; + } + if (@addedfiles) { print "Adding : ". join(", ", @addedfiles) ."\n"; + } + if (@deletedfiles) { print "Deleting : ". join(", ", @deletedfiles) ."\n"; - exit(1); + } } - -if (@changedfiles) { +else { + if (@changedfiles) { open(H, "| git-update-index -z --stdin") or die "git-update-index failed to update changed files with code $!\n"; foreach my $fileName (@changedfiles) { print H "$fileName\0"; } close(H); -} -if (@addedfiles) { + } + if (@addedfiles) { open(H, "| git-update-index --add -z --stdin") or die "git-update-index failed to add new names with code $!\n"; foreach my $fileName (@addedfiles) { print H "$fileName\0"; } close(H); -} -if (@deletedfiles) { + } + if (@deletedfiles) { open(H, "| git-update-index --remove -z --stdin") or die "git-update-index failed to remove old names with code $!\n"; foreach my $fileName (@deletedfiles) { print H "$fileName\0"; } close(H); + } } -- cgit v1.2.1 From f6bc189a457b2575587f26e27f1eabdd615b2d78 Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Sun, 27 Nov 2005 22:04:14 +0100 Subject: git-mv: keep git index consistent with file system on failed rename When doing multiple renames, and a rename in the middle fails, git-mv did not store the successful renames in the git index; this is fixed by delaying the error message on a failed rename to after the git updating. Signed-off-by: Josef Weidendorfer Signed-off-by: Junio C Hamano --- git-mv.perl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/git-mv.perl b/git-mv.perl index 990bec503..ac19876fe 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -142,14 +142,17 @@ while(scalar @srcArgs > 0) { # Final pass: rename/move my (@deletedfiles,@addedfiles,@changedfiles); +$bad = ""; while(scalar @srcs > 0) { $src = shift @srcs; $dst = shift @dsts; if ($opt_n || $opt_v) { print "Renaming $src to $dst\n"; } if (!$opt_n) { - rename($src,$dst) - or die "rename failed: $!"; + if (!rename($src,$dst)) { + $bad = "renaming '$src' failed: $!"; + last; + } } $safesrc = quotemeta($src); @@ -209,3 +212,8 @@ else { close(H); } } + +if ($bad ne "") { + print "Error: $bad\n"; + exit(1); +} -- cgit v1.2.1 From ca203ee7db708baa278950501f8d01f29c5190be Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Sun, 27 Nov 2005 22:06:42 +0100 Subject: git-mv: fully detect 'directory moved into itself' This gives a better error message when trying to move a directory into some subdirectory of itself; ie. no real bug fix: renaming already failed before, but with a strange "invalid argument". Signed-off-by: Josef Weidendorfer Signed-off-by: Junio C Hamano --- git-mv.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-mv.perl b/git-mv.perl index ac19876fe..8d294d652 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -108,7 +108,7 @@ while(scalar @srcArgs > 0) { } } - if (($bad eq "") && ($src eq $dstDir)) { + if (($bad eq "") && ($dst =~ /^$src\//)) { $bad = "can not move directory '$src' into itself"; } -- cgit v1.2.1 From b933e818e951c290e8355ae544567bba949f392e Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Sun, 27 Nov 2005 22:08:33 +0100 Subject: Small fixes in Documentation/git-mv.txt The two synopsis lines have to be prefixed with a space so that asciidoc inserts a line break inbetween for the manual page. Signed-off-by: Josef Weidendorfer Signed-off-by: Junio C Hamano --- Documentation/git-mv.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index f2d5882f4..3013b8d0c 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -8,14 +8,14 @@ git-mv - Script used to move or rename a file, directory or symlink. SYNOPSIS -------- -'git-mv' [-f] [-n] -'git-mv' [-f] [-k] [-n] ... + 'git-mv' [-f] [-n] + 'git-mv' [-f] [-n] [-k] ... DESCRIPTION ----------- This script is used to move or rename a file, directory or symlink. In the first form, it renames , which must exist and be either -a file, symlink or directory, to , which must not exist. +a file, symlink or directory, to . In the second form, the last argument has to be an existing directory; the given sources will be moved into this directory. @@ -25,7 +25,7 @@ committed. OPTIONS ------- -f:: - Force renaming or moving even targets exist + Force renaming or moving of a file even if the target exists -k:: Skip move or rename actions which would lead to an error condition. An error happens when a source is neither existing nor -- cgit v1.2.1 From 26169747b811b8ecd5693adfce4f5c7e322d2487 Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Sun, 27 Nov 2005 22:11:33 +0100 Subject: git-mv: follow -k request even on failing renames -k requests to keep running on an error condition. Previously, git-mv stopped on failing renames even with -k. There are some error conditions which are not checked in the first phase of git-mv, eg. 'permission denied'. Still, option -k should work. Signed-off-by: Josef Weidendorfer Signed-off-by: Junio C Hamano --- git-mv.perl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git-mv.perl b/git-mv.perl index 8d294d652..65b1dcfdf 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -151,6 +151,11 @@ while(scalar @srcs > 0) { if (!$opt_n) { if (!rename($src,$dst)) { $bad = "renaming '$src' failed: $!"; + if ($opt_k) { + print "Warning: skipped: $bad\n"; + $bad = ""; + next; + } last; } } -- cgit v1.2.1 From 55d1eb047d4dffc633502ea2a80dd092194fc900 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Sun, 27 Nov 2005 23:29:30 +0100 Subject: Fix gitk this->selected diffs The change made in 8b7e5d76e836396a097bb6f61cf930ea872a7bd3 to accomodate dense revlists in single-commit diffs has broken computing of diffs between arbitrary trees, which does need to consider two commit ids. This patch changes the two git-diff-tree calls to get the necessary two ids in this case. It does so by propagating a "singlecommit" flag through all functions involved via an additional argument. Signed-off-by: Yann Dirson Signed-off-by: Junio C Hamano --- gitk | 53 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/gitk b/gitk index ecc1688c7..b53a5c56c 100755 --- a/gitk +++ b/gitk @@ -2165,9 +2165,9 @@ proc selectline {l isnew} { $cflist delete 0 end $cflist insert end "Comments" if {$nparents($id) == 1} { - startdiff [concat $id $parents($id)] + startdiff [concat $id $parents($id)] 1 } elseif {$nparents($id) > 1} { - mergediff $id + mergediff $id 1 } } @@ -2236,7 +2236,7 @@ proc goforw {} { } } -proc mergediff {id} { +proc mergediff {id singlecommit} { global parents diffmergeid diffmergegca mergefilelist diffpindex set diffmergeid $id @@ -2247,7 +2247,7 @@ proc mergediff {id} { showmergediff } } else { - contmergediff {} + contmergediff {} $singlecommit } } @@ -2267,7 +2267,7 @@ proc findgca {ids} { return $gca } -proc contmergediff {ids} { +proc contmergediff {ids singlecommit} { global diffmergeid diffpindex parents nparents diffmergegca global treediffs mergefilelist diffids treepending @@ -2284,7 +2284,7 @@ proc contmergediff {ids} { if {![info exists treediffs($ids)]} { set diffids $ids if {![info exists treepending]} { - gettreediffs $ids + gettreediffs $ids $singlecommit } return } @@ -2762,39 +2762,45 @@ proc similarity {pnum l nlc f events} { return [expr {200 * $same / (2 * $same + $diff)}] } -proc startdiff {ids} { +proc startdiff {ids singlecommit} { global treediffs diffids treepending diffmergeid set diffids $ids catch {unset diffmergeid} if {![info exists treediffs($ids)]} { if {![info exists treepending]} { - gettreediffs $ids + gettreediffs $ids $singlecommit } } else { - addtocflist $ids + addtocflist $ids $singlecommit } } -proc addtocflist {ids} { +proc addtocflist {ids singlecommit} { global treediffs cflist foreach f $treediffs($ids) { $cflist insert end $f } - getblobdiffs $ids + getblobdiffs $ids $singlecommit } -proc gettreediffs {ids} { +proc gettreediffs {ids singlecommit} { global treediff parents treepending set treepending $ids set treediff {} set id [lindex $ids 0] - if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $id" r]}] return + if {$singlecommit == 1} { + set range "$id" + } else { + set p [lindex $ids 1] + set range "$p $id" + } + if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $range" r]}] return fconfigure $gdtf -blocking 0 - fileevent $gdtf readable [list gettreediffline $gdtf $ids] + fileevent $gdtf readable [list gettreediffline $gdtf $ids $singlecommit] } -proc gettreediffline {gdtf ids} { +proc gettreediffline {gdtf ids singlecommit} { global treediff treediffs treepending diffids diffmergeid set n [gets $gdtf line] @@ -2804,12 +2810,12 @@ proc gettreediffline {gdtf ids} { set treediffs($ids) $treediff unset treepending if {$ids != $diffids} { - gettreediffs $diffids + gettreediffs $diffids $singlecommit } else { if {[info exists diffmergeid]} { - contmergediff $ids + contmergediff $ids $singlecommit } else { - addtocflist $ids + addtocflist $ids $singlecommit } } return @@ -2818,13 +2824,18 @@ proc gettreediffline {gdtf ids} { lappend treediff $file } -proc getblobdiffs {ids} { +proc getblobdiffs {ids singlecommit} { global diffopts blobdifffd diffids env curdifftag curtagstart global difffilestart nextupdate diffinhdr treediffs set id [lindex $ids 0] set env(GIT_DIFF_OPTS) $diffopts - set cmd [list | git-diff-tree --no-commit-id -r -p -C $id] + if {$singlecommit == 1} { + set cmd [list | git-diff-tree --no-commit-id -r -p -C $id] + } else { + set p [lindex $ids 1] + set cmd [list | git-diff-tree --no-commit-id -r -p -C $p $id] + } if {[catch {set bdf [open $cmd r]} err]} { puts "error getting diffs: $err" return @@ -3341,7 +3352,7 @@ proc doseldiff {oldid newid} { $ctext conf -state disabled $ctext tag delete Comments $ctext tag remove found 1.0 end - startdiff [list $newid $oldid] + startdiff [list $newid $oldid] 0 } proc mkpatch {} { -- cgit v1.2.1 From 4e72dcec89c7cda7022d4ec2dd686e77deb5376e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 16:09:40 -0800 Subject: Introduce i18n.commitencoding. This is to hold what the project-local rule as to the charset/encoding for the commit log message is. Lack of it defaults to utf-8. Signed-off-by: Junio C Hamano --- cache.h | 3 +++ config.c | 5 +++++ environment.c | 1 + 3 files changed, 9 insertions(+) diff --git a/cache.h b/cache.h index 61bf884f8..634b5aa69 100644 --- a/cache.h +++ b/cache.h @@ -399,6 +399,9 @@ extern int check_repository_format_version(const char *var, const char *value); extern char git_default_email[MAX_GITNAME]; extern char git_default_name[MAX_GITNAME]; +#define MAX_ENCODING_LENGTH 64 +extern char git_commit_encoding[MAX_ENCODING_LENGTH]; + /* Sane ctype - no locale, and works with signed chars */ #undef isspace #undef isdigit diff --git a/config.c b/config.c index 0c43d7615..152fa282f 100644 --- a/config.c +++ b/config.c @@ -237,6 +237,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "i18n.commitencoding")) { + strncpy(git_commit_encoding, value, sizeof(git_commit_encoding)); + return 0; + } + /* Add other config variables here.. */ return 0; } diff --git a/environment.c b/environment.c index 3f194732e..0886ad38f 100644 --- a/environment.c +++ b/environment.c @@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME]; int trust_executable_bit = 1; int only_use_symrefs = 0; int repository_format_version = 0; +char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; -- cgit v1.2.1 From 650e4be59b9f385f56e5829d97d09e8440f174b8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 16:22:16 -0800 Subject: mailinfo: allow -u to fall back on latin1 to utf8 conversion. When the message body does not identify what encoding it is in, -u assumes it is in latin-1 and converts it to utf8, which is the recommended encoding for git commit log messages. With -u=, the conversion is made into the specified one, instead of utf8, to allow project-local policies. Signed-off-by: Junio C Hamano --- mailinfo.c | 59 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/mailinfo.c b/mailinfo.c index cb853df99..6d8c93360 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -16,7 +16,7 @@ extern char *gitstrcasestr(const char *haystack, const char *needle); static FILE *cmitmsg, *patchfile; static int keep_subject = 0; -static int metainfo_utf8 = 0; +static char *metainfo_charset = NULL; static char line[1000]; static char date[1000]; static char name[1000]; @@ -441,29 +441,38 @@ static int decode_b_segment(char *in, char *ot, char *ep) static void convert_to_utf8(char *line, char *charset) { - if (*charset) { - char *in, *out; - size_t insize, outsize, nrc; - char outbuf[4096]; /* cheat */ - iconv_t conv = iconv_open("utf-8", charset); - - if (conv == (iconv_t) -1) { - fprintf(stderr, "cannot convert from %s to utf-8\n", - charset); + char *in, *out; + size_t insize, outsize, nrc; + char outbuf[4096]; /* cheat */ + static char latin_one[] = "latin-1"; + char *input_charset = *charset ? charset : latin_one; + iconv_t conv = iconv_open(metainfo_charset, input_charset); + + if (conv == (iconv_t) -1) { + static int warned_latin1_once = 0; + if (input_charset != latin_one) { + fprintf(stderr, "cannot convert from %s to %s\n", + input_charset, metainfo_charset); *charset = 0; - return; } - in = line; - insize = strlen(in); - out = outbuf; - outsize = sizeof(outbuf); - nrc = iconv(conv, &in, &insize, &out, &outsize); - iconv_close(conv); - if (nrc == (size_t) -1) - return; - *out = 0; - strcpy(line, outbuf); + else if (!warned_latin1_once) { + warned_latin1_once = 1; + fprintf(stderr, "tried to convert from %s to %s, " + "but your iconv does not work with it.\n", + input_charset, metainfo_charset); + } + return; } + in = line; + insize = strlen(in); + out = outbuf; + outsize = sizeof(outbuf); + nrc = iconv(conv, &in, &insize, &out, &outsize); + iconv_close(conv); + if (nrc == (size_t) -1) + return; + *out = 0; + strcpy(line, outbuf); } static void decode_header_bq(char *it) @@ -511,7 +520,7 @@ static void decode_header_bq(char *it) } if (sz < 0) return; - if (metainfo_utf8) + if (metainfo_charset) convert_to_utf8(piecebuf, charset_q); strcpy(out, piecebuf); out += strlen(out); @@ -590,7 +599,7 @@ static int handle_commit_msg(void) * normalize the log message to UTF-8. */ decode_transfer_encoding(line); - if (metainfo_utf8) + if (metainfo_charset) convert_to_utf8(line, charset); fputs(line, cmitmsg); } while (fgets(line, sizeof(line), stdin) != NULL); @@ -720,7 +729,9 @@ int main(int argc, char **argv) if (!strcmp(argv[1], "-k")) keep_subject = 1; else if (!strcmp(argv[1], "-u")) - metainfo_utf8 = 1; + metainfo_charset = "utf-8"; + else if (!strncmp(argv[1], "-u=", 3)) + metainfo_charset = argv[1] + 3; else usage(); argc--; argv++; -- cgit v1.2.1 From f1f909e3185b5ee366e198042447afe749bfc813 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 16:29:38 -0800 Subject: mailinfo: Use i18n.commitencoding This uses i18n.commitencoding configuration item to pick up the default commit encoding for the repository when converting form e-mail encoding to commit encoding (the default is utf8). Signed-off-by: Junio C Hamano --- mailinfo.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mailinfo.c b/mailinfo.c index 6d8c93360..de105acaa 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -8,6 +8,7 @@ #include #include #include +#include "cache.h" #ifdef NO_STRCASESTR extern char *gitstrcasestr(const char *haystack, const char *needle); @@ -718,27 +719,27 @@ static void handle_body(void) static const char mailinfo_usage[] = "git-mailinfo [-k] [-u] msg patch info"; -static void usage(void) { - fprintf(stderr, "%s\n", mailinfo_usage); - exit(1); -} - int main(int argc, char **argv) { + /* NEEDSWORK: might want to do the optional .git/ directory + * discovery + */ + git_config(git_default_config); + while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "-k")) keep_subject = 1; else if (!strcmp(argv[1], "-u")) - metainfo_charset = "utf-8"; + metainfo_charset = git_commit_encoding; else if (!strncmp(argv[1], "-u=", 3)) metainfo_charset = argv[1] + 3; else - usage(); + usage(mailinfo_usage); argc--; argv++; } if (argc != 3) - usage(); + usage(mailinfo_usage); cmitmsg = fopen(argv[1], "w"); if (!cmitmsg) { perror(argv[1]); -- cgit v1.2.1 From 9f63892b3803701c97259d51143e199fe9603d3f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 01:29:52 -0800 Subject: mailinfo: Do not use -u=; say --encoding= Specifying the value for a single letter, single dash option parameter with equal sign looked funny, and more importantly calling the flag to override encoding from utf-8 to something else "-u" (obviously abbreviated from "utf-8") did not make any sense. So spell it out. Signed-off-by: Junio C Hamano --- Documentation/git-mailinfo.txt | 11 +++++++++-- mailinfo.c | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt index dc7d725ea..889075474 100644 --- a/Documentation/git-mailinfo.txt +++ b/Documentation/git-mailinfo.txt @@ -8,7 +8,7 @@ git-mailinfo - Extracts patch from a single e-mail message. SYNOPSIS -------- -'git-mailinfo' [-k] [-u] +'git-mailinfo' [-k] [-u | --encoding=] DESCRIPTION @@ -37,10 +37,17 @@ OPTIONS author email are taken from the e-mail without any charset conversion, after minimally decoding MIME transfer encoding. This flag causes the resulting - commit to be encoded in utf-8 by transliterating them. + commit to be encoded in the encoding specified by + i18n.commitencoding configuration (defaults to utf-8) by + transliterating them. Note that the patch is always used as is without charset conversion, even with this flag. +--encoding=:: + Similar to -u but if the local convention is different + from what is specified by i18n.commitencoding, this flag + can be used to override it. + :: The commit log message extracted from e-mail, usually except the title line which comes from e-mail Subject. diff --git a/mailinfo.c b/mailinfo.c index de105acaa..890e3487a 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -717,7 +717,7 @@ static void handle_body(void) } static const char mailinfo_usage[] = - "git-mailinfo [-k] [-u] msg patch info"; + "git-mailinfo [-k] [-u | --encoding=] msg patch info"; int main(int argc, char **argv) { @@ -731,8 +731,8 @@ int main(int argc, char **argv) keep_subject = 1; else if (!strcmp(argv[1], "-u")) metainfo_charset = git_commit_encoding; - else if (!strncmp(argv[1], "-u=", 3)) - metainfo_charset = argv[1] + 3; + else if (!strncmp(argv[1], "--encoding=", 11)) + metainfo_charset = argv[1] + 11; else usage(mailinfo_usage); argc--; argv++; -- cgit v1.2.1 From d327244a8435539b62d73ab151bd6c46324cbeb6 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 28 Nov 2005 20:41:56 +1100 Subject: gitk: Fix diff this->selected and selected->this functions The change in 8b7e5d76e836396a097bb6f61cf930ea872a7bd3, which makes a couple of git-diff-tree calls supply only one id rather than two, fixes the display when showing what a single commit did with dense revlists, but broke the diff this->selected and diff selected->this right-click menu functions. Yann Dirson pointed this out and had a patch that fixed the diff menu functions by passing a "singlecommit" flag around. This fixes it a bit differently, by making the ids and diffids variables be either a single id, in the case of showing what a commit did, or {oldid newid}, in the case of the diff menu functions. That way we can just pass $ids to git-diff-tree as is. Most of the changes in fact are just reversing the order of ids in $ids and $diffids, because they used to be {child parent}, but git-diff-tree requires old id before new id. Signed-off-by: Paul Mackerras --- gitk | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/gitk b/gitk index ecc1688c7..e1c395475 100755 --- a/gitk +++ b/gitk @@ -2165,7 +2165,7 @@ proc selectline {l isnew} { $cflist delete 0 end $cflist insert end "Comments" if {$nparents($id) == 1} { - startdiff [concat $id $parents($id)] + startdiff $id } elseif {$nparents($id) > 1} { mergediff $id } @@ -2274,12 +2274,12 @@ proc contmergediff {ids} { # diff the child against each of the parents, and diff # each of the parents against the GCA. while 1 { - if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} { - set ids [list [lindex $ids 1] $diffmergegca] + if {[lindex $ids 1] == $diffmergeid && $diffmergegca ne {}} { + set ids [list $diffmergegca [lindex $ids 0]] } else { if {[incr diffpindex] >= $nparents($diffmergeid)} break set p [lindex $parents($diffmergeid) $diffpindex] - set ids [list $diffmergeid $p] + set ids [list $p $diffmergeid] } if {![info exists treediffs($ids)]} { set diffids $ids @@ -2297,8 +2297,8 @@ proc contmergediff {ids} { if {$diffmergegca ne {}} { set files {} foreach p $parents($diffmergeid) { - set gcadiffs $treediffs([list $p $diffmergegca]) - foreach f $treediffs([list $diffmergeid $p]) { + set gcadiffs $treediffs([list $diffmergegca $p]) + foreach f $treediffs([list $p $diffmergeid]) { if {[lsearch -exact $files $f] < 0 && [lsearch -exact $gcadiffs $f] >= 0} { lappend files $f @@ -2311,7 +2311,7 @@ proc contmergediff {ids} { set files $treediffs([list $diffmergeid $p]) for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} { set p [lindex $parents($diffmergeid) $i] - set df $treediffs([list $diffmergeid $p]) + set df $treediffs([list $p $diffmergeid]) set nf {} foreach f $files { if {[lsearch -exact $df $f] >= 0} { @@ -2788,8 +2788,7 @@ proc gettreediffs {ids} { global treediff parents treepending set treepending $ids set treediff {} - set id [lindex $ids 0] - if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $id" r]}] return + if [catch {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]}] return fconfigure $gdtf -blocking 0 fileevent $gdtf readable [list gettreediffline $gdtf $ids] } @@ -2822,9 +2821,8 @@ proc getblobdiffs {ids} { global diffopts blobdifffd diffids env curdifftag curtagstart global difffilestart nextupdate diffinhdr treediffs - set id [lindex $ids 0] set env(GIT_DIFF_OPTS) $diffopts - set cmd [list | git-diff-tree --no-commit-id -r -p -C $id] + set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids] if {[catch {set bdf [open $cmd r]} err]} { puts "error getting diffs: $err" return @@ -3341,7 +3339,7 @@ proc doseldiff {oldid newid} { $ctext conf -state disabled $ctext tag delete Comments $ctext tag remove found 1.0 end - startdiff [list $newid $oldid] + startdiff [list $oldid $newid] } proc mkpatch {} { -- cgit v1.2.1 From 0dccc7dceef03e4231b24c9e126110b338e1c1e1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 01:46:15 -0800 Subject: config.c: constness tightening to avoid compilation warning. Signed-off-by: Junio C Hamano --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index 152fa282f..34584f62b 100644 --- a/config.c +++ b/config.c @@ -11,7 +11,7 @@ #define MAXNAME (256) static FILE *config_file; -static char *config_file_name; +static const char *config_file_name; static int config_linenr; static int get_next_char(void) { -- cgit v1.2.1 From 671bc1538a88d82096a8e59d9ccef4f4a5c40a32 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 16:12:51 -0800 Subject: [PATCH] gitk: Use i18n.commitencoding configuration item. Hardcoding "utf-8" in the script breaks projects that use local encoding, so allow setting i18n.commitEncoding. Signed-off-by: Junio C Hamano Signed-off-by: Paul Mackerras --- gitk | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index e1c395475..730ffd920 100755 --- a/gitk +++ b/gitk @@ -3656,7 +3656,14 @@ set datemode 0 set boldnames 0 set diffopts "-U 5 -p" set wrcomcmd "git-diff-tree --stdin -p --pretty" -set gitencoding "utf-8" + +set gitencoding "" +catch { + set gitencoding [exec git-repo-config --get i18n.commitencoding] +} +if {$gitencoding == ""} { + set gitencoding "utf-8" +} set mainfont {Helvetica 9} set textfont {Courier 9} -- cgit v1.2.1 From 90109b320d45520cf5721de08d761ad06c0445ab Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 02:54:05 -0800 Subject: git-mv: quote $src in regexp properly. Noticed and fixed by Matthias Urlichs and Josef Weidendorfer. Signed-off-by: Junio C Hamano --- git-mv.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-mv.perl b/git-mv.perl index 65b1dcfdf..53046bafd 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -108,7 +108,7 @@ while(scalar @srcArgs > 0) { } } - if (($bad eq "") && ($dst =~ /^$src\//)) { + if (($bad eq "") && ($dst =~ /^$safesrc\//)) { $bad = "can not move directory '$src' into itself"; } -- cgit v1.2.1 From 7f4bd5d831ea838668d1de5f5af022f763230eee Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 13:00:31 -0800 Subject: rebase: one safety net, one bugfix and one optimization. When a .dotest from a previously failed rebase or patch application exists, rebase got confused and tried to apply mixture of what was already there and what is being rebased. Check the existence of the directory and barf. It failed with an mysterious "fatal: cannot read mbox" message if the branch being rebased is fully in sync with the base. Also if the branch is a proper descendant of the base, there is no need to run rebase logic. Prevent these from happening by checking where the merge-base is. Signed-off-by: Junio C Hamano --- git-rebase.sh | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/git-rebase.sh b/git-rebase.sh index 2bc3a1299..638ff0dbc 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -5,9 +5,25 @@ . git-sh-setup -# The other head is given +# Make sure we do not have .dotest +if mkdir .dotest +then + rmdir .dotest +else + echo >&2 ' +It seems that I cannot create a .dotest directory, and I wonder if you +are in the middle of patch application or another rebase. If that is not +the case, please rm -fr .dotest and run me again. I am stopping in case +you still have something valuable there.' + exit 1 +fi + +# The other head is given. Make sure it is valid. other=$(git-rev-parse --verify "$1^0") || exit +# Make sure we have HEAD that is valid. +head=$(git-rev-parse --verify "HEAD^0") || exit + # The tree must be really really clean. git-update-index --refresh || exit diff=$(git-diff-index --cached --name-status -r HEAD) @@ -23,6 +39,16 @@ case "$#" in git-checkout "$2" || exit esac +# If the HEAD is a proper descendant of $other, we do not even need +# to rebase. Make sure we do not do needless rebase. In such a +# case, merge-base should be the same as "$other". +mb=$(git-merge-base "$other" "$head") +if test "$mb" = "$other" +then + echo >&2 "Current branch `git-symbolic-ref HEAD` is up to date." + exit 0 +fi + # Rewind the head to "$other" git-reset --hard "$other" git-format-patch -k --stdout --full-index "$other" ORIG_HEAD | -- cgit v1.2.1 From f2e6f1c9763f65918211ed28caf77a40effa4e7e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 20:51:44 -0800 Subject: name-rev: fix parent counting. Noticed by linux@horizon.com. The first merge parent (typically "our branch") is ^1, not ^0, and the first other branch is ^2. Signed-off-by: Junio C Hamano --- name-rev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/name-rev.c b/name-rev.c index 7d89401a4..65333d416 100644 --- a/name-rev.c +++ b/name-rev.c @@ -21,7 +21,7 @@ static void name_rev(struct commit *commit, { struct rev_name *name = (struct rev_name *)commit->object.util; struct commit_list *parents; - int parent_number = 0; + int parent_number = 1; if (!commit->object.parsed) parse_commit(commit); @@ -56,7 +56,7 @@ copy_data: for (parents = commit->parents; parents; parents = parents->next, parent_number++) { - if (parent_number > 0) { + if (parent_number > 1) { char *new_name = xmalloc(strlen(tip_name)+8); if (generation > 0) -- cgit v1.2.1 From 3e2f62bebfe8e9aeea63849c37c65f25ea001e65 Mon Sep 17 00:00:00 2001 From: Jan Andres Date: Tue, 29 Nov 2005 01:51:54 +0100 Subject: Fix typo in http-push.c Typo resulted in accessing past the beginning of a string causing segfaults. [jc: signoffs?] --- http-push.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-push.c b/http-push.c index 76c788673..ad789829c 100644 --- a/http-push.c +++ b/http-push.c @@ -784,7 +784,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) strtol(ctx->cdata + 7, NULL, 10); } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) { if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) { - lock->token = xmalloc(strlen(ctx->cdata - 15)); + lock->token = xmalloc(strlen(ctx->cdata) - 15); strcpy(lock->token, ctx->cdata + 16); } } -- cgit v1.2.1 From ffb1a4bed55534e276a0e4fc7dc1c94d17be8579 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 22:54:30 -0800 Subject: Documentation: Describe merge operation a bit better. In git-merge documentation, add a section to describe what happens to the index and working tree during merge, and what their cleanliness requirements are before the merge. Signed-off-by: Junio C Hamano --- Documentation/git-merge.txt | 95 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 904e2fc4c..c1174041e 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -37,6 +37,101 @@ include::merge-options.txt[] include::merge-strategies.txt[] +HOW MERGE WORKS +--------------- + +A merge is always between the current `HEAD` and one or more +remote branch heads, and the index file must exactly match the +tree of `HEAD` commit (i.e. the contents of the last commit) when +it happens. In other words, `git-diff --cached HEAD` must +report no changes. + +[NOTE] +This is a bit of lie. In certain special cases, your index are +allowed to be different from the tree of `HEAD` commit. The most +notable case is when your `HEAD` commit is already ahead of what +is being merged, in which case your index can have arbitrary +difference from your `HEAD` commit. Otherwise, your index entries +are allowed have differences from your `HEAD` commit that match +the result of trivial merge (e.g. you received the same patch +from external source to produce the same result as what you are +merging). For example, if a path did not exist in the common +ancestor and your head commit but exists in the tree you are +merging into your repository, and if you already happen to have +that path exactly in your index, the merge does not have to +fail. + +Otherwise, merge will refuse to do any harm to your repository +(that is, it may fetch the objects from remote, and it may even +update the local branch used to keep track of the remote branch +with `git pull remote rbranch:lbranch`, but your working tree, +`.git/HEAD` pointer and index file are left intact). + +You may have local modifications in the working tree files. In +other words, `git-diff` is allowed to report changes. +However, the merge uses your working tree as the working area, +and in order to prevent the merge operation from losing such +changes, it makes sure that they do not interfere with the +merge. Those complex tables in read-tree documentation define +what it means for a path to "interfere with the merge". And if +your local modifications interfere with the merge, again, it +stops before touching anything. + +So in the above two "failed merge" case, you do not have to +worry about lossage of data --- you simply were not ready to do +a merge, so no merge happened at all. You may want to finish +whatever you were in the middle of doing, and retry the same +pull after you are done and ready. + +When things cleanly merge, these things happen: + +1. the results are updated both in the index file and in your + working tree, +2. index file is written out as a tree, +3. the tree gets committed, and +4. the `HEAD` pointer gets advanced. + +Because of 2., we require that the original state of the index +file to match exactly the current `HEAD` commit; otherwise we +will write out your local changes already registered in your +index file along with the merge result, which is not good. +Because 1. involves only the paths different between your +branch and the remote branch you are pulling from during the +merge (which is typically a fraction of the whole tree), you can +have local modifications in your working tree as long as they do +not overlap with what the merge updates. + +When there are conflicts, these things happen: + +1. `HEAD` stays the same. + +2. Cleanly merged paths are updated both in the index file and + in your working tree. + +3. For conflicting paths, the index file records the version + from `HEAD`. The working tree files have the result of + "merge" program; i.e. 3-way merge result with familiar + conflict markers `<<< === >>>`. + +4. No other changes are done. In particular, the local + modifications you had before you started merge will stay the + same and the index entries for them stay as they were, + i.e. matching `HEAD`. + +After seeing a conflict, you can do two things: + + * Decide not to merge. The only clean-up you need are to reset + the index file to the `HEAD` commit to reverse 2. and to clean + up working tree changes made by 2. and 3.; `git-reset` can + be used for this. + + * Resolve the conflicts. `git-diff` would report only the + conflicting paths because of the above 2. and 3.. Edit the + working tree files into a desirable shape, `git-update-index` + them, to make the index file contain what the merge result + should be, and run `git-commit` to commit the result. + + SEE ALSO -------- gitlink:git-fmt-merge-msg[1], gitlink:git-pull[1] -- cgit v1.2.1 From 9ef2b3cbf62d15aa0312bde349347873d7c0f399 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 22:55:25 -0800 Subject: write_name_quoted(): make one of the path a counted string. This is to prepare for ls-tree updates. Signed-off-by: Junio C Hamano --- ls-files.c | 8 +++++--- ls-tree.c | 4 +++- quote.c | 29 +++++++++++++++++++---------- quote.h | 4 ++-- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/ls-files.c b/ls-files.c index db2288aee..f3f1a6a66 100644 --- a/ls-files.c +++ b/ls-files.c @@ -344,7 +344,7 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent) return; fputs(tag, stdout); - write_name_quoted("", ent->name + offset, line_terminator, stdout); + write_name_quoted("", 0, ent->name + offset, line_terminator, stdout); putchar(line_terminator); } @@ -433,7 +433,8 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) if (!show_stage) { fputs(tag, stdout); - write_name_quoted("", ce->name + offset, line_terminator, stdout); + write_name_quoted("", 0, ce->name + offset, + line_terminator, stdout); putchar(line_terminator); } else { @@ -442,7 +443,8 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) ntohl(ce->ce_mode), sha1_to_hex(ce->sha1), ce_stage(ce)); - write_name_quoted("", ce->name + offset, line_terminator, stdout); + write_name_quoted("", 0, ce->name + offset, + line_terminator, stdout); putchar(line_terminator); } } diff --git a/ls-tree.c b/ls-tree.c index d9f15e349..d7c7e750f 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -157,9 +157,11 @@ 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, e->name, line_termination, stdout); + write_name_quoted(pathbuf, pathlen, e->name, + line_termination, stdout); putchar(line_termination); } diff --git a/quote.c b/quote.c index e662a7da7..76eb14426 100644 --- a/quote.c +++ b/quote.c @@ -112,7 +112,8 @@ char *sq_dequote(char *arg) * but not enclosed in double-quote pair. Return value is undefined. */ -int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) +static int quote_c_style_counted(const char *name, int namelen, + char *outbuf, FILE *outfp, int no_dq) { #undef EMIT #define EMIT(c) \ @@ -125,7 +126,7 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) if (!no_dq) EMIT('"'); - for (sp = name; (ch = *sp++); ) { + for (sp = name; (ch = *sp++) && (sp - name) <= namelen; ) { if ((ch < ' ') || (ch == '"') || (ch == '\\') || (ch == 0177)) { @@ -162,6 +163,12 @@ int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) return needquote ? count : 0; } +int quote_c_style(const char *name, char *outbuf, FILE *outfp, int no_dq) +{ + int cnt = strlen(name); + return quote_c_style_counted(name, cnt, outbuf, outfp, no_dq); +} + /* * C-style name unquoting. * @@ -227,28 +234,30 @@ char *unquote_c_style(const char *quoted, const char **endp) } } -void write_name_quoted(const char *prefix, const char *name, - int quote, FILE *out) +void write_name_quoted(const char *prefix, int prefix_len, + const char *name, int quote, FILE *out) { int needquote; if (!quote) { no_quote: - if (prefix && prefix[0]) - fputs(prefix, out); + if (prefix_len) + fprintf(out, "%.*s", prefix_len, prefix); fputs(name, out); return; } needquote = 0; - if (prefix && prefix[0]) - needquote = quote_c_style(prefix, NULL, NULL, 0); + if (prefix_len) + needquote = quote_c_style_counted(prefix, prefix_len, + NULL, NULL, 0); if (!needquote) needquote = quote_c_style(name, NULL, NULL, 0); if (needquote) { fputc('"', out); - if (prefix && prefix[0]) - quote_c_style(prefix, NULL, out, 1); + if (prefix_len) + quote_c_style_counted(prefix, prefix_len, + NULL, out, 1); quote_c_style(name, NULL, out, 1); fputc('"', out); } diff --git a/quote.h b/quote.h index 2486e6e68..c1ab3788e 100644 --- a/quote.h +++ b/quote.h @@ -41,7 +41,7 @@ extern int quote_c_style(const char *name, char *outbuf, FILE *outfp, int nodq); extern char *unquote_c_style(const char *quoted, const char **endp); -extern void write_name_quoted(const char *prefix, const char *name, - int quote, FILE *out); +extern void write_name_quoted(const char *prefix, int prefix_len, + const char *name, int quote, FILE *out); #endif -- cgit v1.2.1 From 3c5e8468a93cdb427b4c4a129339e80e1813e5c0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 26 Nov 2005 09:38:20 -0800 Subject: ls-tree: major rewrite to do pathspec git-ls-tree should be rewritten to use a pathspec the same way everybody else does. Right now it's the odd man out: if you do git-ls-tree HEAD divers/char drivers/ it will show the same files _twice_, which is not how pathspecs in general work. How about this patch? It breaks some of the git-ls-tree tests, but it makes git-ls-tree work a lot more like other git pathspec commands, and it removes more than 150 lines by re-using the recursive tree traversal (but the "-d" flag is gone for good, so I'm not pushing this too hard). Linus --- ls-tree.c | 227 ++++++-------------------------------------------------------- tree.c | 34 +++++++--- tree.h | 9 +++ 3 files changed, 55 insertions(+), 215 deletions(-) diff --git a/ls-tree.c b/ls-tree.c index d7c7e750f..598b72949 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -13,217 +13,32 @@ static int line_termination = '\n'; #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 const char ls_tree_usage[] = + "git-ls-tree [-d] [-r] [-z] [path...]"; -static int show_children(struct tree_entry_list *e, int level, char *pathbuf) +static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { - int oldlen = strlen(pathbuf); + const char *type = "blob"; + int retval = 0; - 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; + if (S_ISDIR(mode)) { + type = "tree"; + if (ls_options & LS_RECURSIVE) + retval = READ_TREE_RECURSIVE; } - pathbuf[oldlen] = '\0'; - - return 0; + printf("%06o %s %s\t%.*s%s%c", mode, type, sha1_to_hex(sha1), baselen, base, pathname, line_termination); + return retval; } -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 **path, *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 +61,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"); + path = 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, path, show_tree); + return 0; } diff --git a/tree.c b/tree.c index 8b42a07b2..043f03215 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 9975e8821..768e5e9eb 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 */ -- cgit v1.2.1 From b45c569b6f257d8905acd8313224dc086266f602 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 27 Nov 2005 11:00:09 -0800 Subject: ls-tree: further cleanup to parallel ls-files. To get more a "git-ls-files" approach, this trivial patch (on top of my previous one) enables recursion, and doesn't show partial trees. [jc: after further discussion, this version enables recursion by default, and you can disable it with "-d" flag. git-ls-tree -d HEAD Documentation/no/such/directory shows Documentation tree (without -d it shows nothing). git-ls-tree HEAD shows everything from the tree. Only to get the single level from the top git-ls-tree -d HEAD is needed. But there is no way to get the single level with pathspec. You need to extract the object name of Documentation tree from the parent tree and run git-ls-tree -d $tree_id_of_Documentation_tree to get something similar to what you can get from the current git-ls-tree HEAD Documentation ] --- ls-tree.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ls-tree.c b/ls-tree.c index 598b72949..cf0dbbc7e 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -11,7 +11,7 @@ static int line_termination = '\n'; #define LS_RECURSIVE 1 #define LS_TREE_ONLY 2 -static int ls_options = 0; +static int ls_options = LS_RECURSIVE; static const char ls_tree_usage[] = "git-ls-tree [-d] [-r] [-z] [path...]"; @@ -19,16 +19,15 @@ static const char ls_tree_usage[] = static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { const char *type = "blob"; - int retval = 0; if (S_ISDIR(mode)) { - type = "tree"; if (ls_options & LS_RECURSIVE) - retval = READ_TREE_RECURSIVE; + return READ_TREE_RECURSIVE; + type = "tree"; } printf("%06o %s %s\t%.*s%s%c", mode, type, sha1_to_hex(sha1), baselen, base, pathname, line_termination); - return retval; + return 0; } int main(int argc, const char **argv) -- cgit v1.2.1 From e2466376ec9a8508b4c8ba2b0c29945c5bfc6cc2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 27 Nov 2005 22:48:08 -0800 Subject: ls-tree: further tweaks of the rewrite It modifies the selection a bit, so that a pathspec that is a superset of a particular tree path will always cause it to recurse into that tree. As an example, let's say that we do git-ls-tree HEAD drivers/char _without_ the "-r". What will happen is that it will start out doing all the base tree, and for "drivers" it will notice that it's a proper subset of "drivers/char", so it will always recurse into _that_ tree (but not into other trees). Then, it will not match anything else than "char" in that subdirectory, and because that's not a proper superset (it's an exact match), it will _not_ recurse into it, so you get: [torvalds@g5 linux]$ ~/git/git-ls-tree HEAD drivers/char 040000 tree 9568cda453aae205bb58983747fa73b9696d9d51 drivers/char which is what you got with the old git-ls-tree too. But interestingly, if you add the slash, it will become a proper superset and it will recurse into _that_ subdirectory (but no deeper: so if you want all subdirectories _below_ drivers/char/, you still need to give "-r"): [torvalds@g5 linux]$ ~/git/git-ls-tree HEAD drivers/char/ 100644 blob 2b6b1d772ed776fff87927fc34adc2e40500218e drivers/char/.gitignore 100644 blob 56b8a2e76ab10a5c21787cb7068a846075cbaffd drivers/char/ChangeLog 100644 blob 970f70d498f4c814e1cf3362e33d7e23ac53c299 drivers/char/Kconfig ... See? This is on top of the previous two diffs, holler if you want a whole new "everything combined" version.. It hasn't gotten lots of testing, but it should work. Linus --- ls-tree.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/ls-tree.c b/ls-tree.c index cf0dbbc7e..4df583012 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -11,7 +11,8 @@ static int line_termination = '\n'; #define LS_RECURSIVE 1 #define LS_TREE_ONLY 2 -static int ls_options = LS_RECURSIVE; +static int ls_options = 0; +const char **pathspec; static const char ls_tree_usage[] = "git-ls-tree [-d] [-r] [-z] [path...]"; @@ -21,8 +22,29 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c 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"; } @@ -32,7 +54,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c int main(int argc, const char **argv) { - const char **path, *prefix; + const char *prefix; unsigned char sha1[20]; char *buf; unsigned long size; @@ -60,11 +82,11 @@ int main(int argc, const char **argv) if (get_sha1(argv[1], sha1) < 0) usage(ls_tree_usage); - path = get_pathspec(prefix, argv + 2); + 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, path, show_tree); + read_tree_recursive(buf, size, "", 0, 0, pathspec, show_tree); return 0; } -- cgit v1.2.1 From 32b5904b5f94829aa7ae11b585ca9c639d57cc74 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 02:30:04 -0800 Subject: ls-tree: Resurrect funny name quoting lost during rewrite. The rewrite to match ls-files/diff-tree behaviour accidentally lost the name quoting. I am not proud about this code, but this would get the test going. Signed-off-by: Junio C Hamano --- ls-tree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ls-tree.c b/ls-tree.c index 4df583012..d4b62198a 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -48,7 +48,9 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c type = "tree"; } - printf("%06o %s %s\t%.*s%s%c", mode, type, sha1_to_hex(sha1), baselen, base, pathname, line_termination); + 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; } -- cgit v1.2.1 From 246cc52f388cae8ca99e5a12b8458c9bfa467765 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 02:32:42 -0800 Subject: ls-tree: match the test to the new semantics. The diff for this commit is a good illustration of what changed in ls-tree behaviour. - With -r, tree nodes themselves are not shown anymore, but blobs in subtrees are shown. - The order of paths parameters do not matter, since they are not like arguments to /bin/ls, but are filter patterns. - When filter patterns overlap, unintuitive things happen. Signed-off-by: Junio C Hamano --- t/t0000-basic.sh | 5 ++--- t/t3100-ls-tree-restrict.sh | 35 ++++++++++++----------------------- t/t3101-ls-tree-dirname.sh | 38 ++++++++------------------------------ 3 files changed, 22 insertions(+), 56 deletions(-) diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index dff7d6916..22bdacaf7 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 c6ce56c86..ae086755e 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 541036834..d78deb1e7 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 -- cgit v1.2.1 From b3cfd939c330211c9812f814094aa1dbe9531bdc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 27 Nov 2005 11:32:03 -0800 Subject: bisect: limit the searchspace by pathspecs It was surprisingly easy to do. git bisect start followed by all the normal "git bisect good/bad" stuff. Almost totally untested, and I guarantee that if your pathnames have spaces in them (or your GIT_DIR has spaces in it) this won't work. I don't know how to fix that, my shell programming isn't good enough. This involves small changes to make "git-rev-list --bisect" work in the presense of a pathspec limiter, and then truly trivial (and that's the broken part) changes to make "git bisect" save away and use the pathspec. I tried one bisection, and a "git bisect visualize", and it all looked correct. But hey, don't be surprised if it has problems. Linus Signed-off-by: Junio C Hamano --- git-bisect.sh | 10 +++++----- rev-list.c | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index d92993b94..2455f00ee 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -33,7 +33,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 +56,8 @@ 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 "git-bisect start $@" >"$GIT_DIR/BISECT_LOG" + echo "$@" > "$GIT_DIR/BISECT_NAMES" } bisect_bad() { @@ -121,7 +121,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 +131,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 +142,7 @@ bisect_next() { bisect_visualize() { bisect_next_check fail - gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*` + gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*` -- $(cat $GIT_DIR/BISECT_NAMES) } bisect_reset() { diff --git a/rev-list.c b/rev-list.c index e17f92806..8020d974f 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; -- cgit v1.2.1 From e9a45d75b5eccfc288c4533b5e34af1f77dd32c9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 17:42:05 -0800 Subject: bisect: quote pathnames for eval safety. ... and make sure they are on the same line. Signed-off-by: Junio C Hamano --- git-bisect.sh | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 2455f00ee..68838f3fa 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. @@ -56,8 +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 "$@" > "$GIT_DIR/BISECT_NAMES" + { + echo -n "git-bisect start" + sq "$@" + } >"$GIT_DIR/BISECT_LOG" + sq "$@" >"$GIT_DIR/BISECT_NAMES" } bisect_bad() { @@ -142,7 +155,8 @@ bisect_next() { bisect_visualize() { bisect_next_check fail - gitk bisect/bad --not `cd "$GIT_DIR/refs" && echo bisect/good-*` -- $(cat $GIT_DIR/BISECT_NAMES) + 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" -- cgit v1.2.1 From 4ca0660816671f65546626b6e2c2038b6a347a8b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 23:14:15 -0800 Subject: working from subdirectory: preparation - prefix_filename() is like prefix_path() but can be used to name any file on the filesystem, not the files that might go into the index file. - setup_git_directory_gently() tries to find the GIT_DIR, but does not die() if called outside a git repository. Signed-off-by: Junio C Hamano --- cache.h | 2 ++ setup.c | 28 +++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/cache.h b/cache.h index 634b5aa69..f9b367f31 100644 --- a/cache.h +++ b/cache.h @@ -147,8 +147,10 @@ extern char *get_graft_file(void); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" extern const char **get_pathspec(const char *prefix, const char **pathspec); +extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern const char *prefix_path(const char *prefix, int len, const char *path); +extern const char *prefix_filename(const char *prefix, int len, const char *path); #define alloc_nr(x) (((x)+16)*3/2) diff --git a/setup.c b/setup.c index cc44a724b..bde590f7c 100644 --- a/setup.c +++ b/setup.c @@ -47,6 +47,21 @@ const char *prefix_path(const char *prefix, int len, const char *path) return path; } +/* + * Unlike prefix_path, this should be used if the named file does + * not have to interact with index entry; i.e. name of a random file + * on the filesystem. + */ +const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) +{ + static char path[PATH_MAX]; + if (!pfx || !*pfx || arg[0] == '/') + return arg; + memcpy(path, pfx, pfx_len); + strcpy(path + pfx_len, arg); + return path; +} + const char **get_pathspec(const char *prefix, const char **pathspec) { const char *entry = *pathspec; @@ -92,7 +107,7 @@ static int is_toplevel_directory(void) return 1; } -static const char *setup_git_directory_1(void) +const char *setup_git_directory_gently(int *nongit_ok) { static char cwd[PATH_MAX+1]; int len, offset; @@ -139,8 +154,15 @@ static const char *setup_git_directory_1(void) break; chdir(".."); do { - if (!offset) + if (!offset) { + if (nongit_ok) { + if (chdir(cwd)) + die("Cannot come back to cwd"); + *nongit_ok = 1; + return NULL; + } die("Not a git repository"); + } } while (cwd[--offset] != '/'); } @@ -172,7 +194,7 @@ int check_repository_format(void) const char *setup_git_directory(void) { - const char *retval = setup_git_directory_1(); + const char *retval = setup_git_directory_gently(NULL); check_repository_format(); return retval; } -- cgit v1.2.1 From edf2e37002eeb30a2ccad5db3b3e1fe41cdc7eb0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 23:14:15 -0800 Subject: git-apply: work from subdirectory. When applying a patch to index file, we need to know where GIT_DIR is; use setup_git_directory() to find it out. This also allows us to work from a subdirectory if we wanted to. When git-apply is run from a subdirectory, it applies the given patch only to the files under the current directory and below. Signed-off-by: Junio C Hamano --- apply.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apply.c b/apply.c index 50be8f3e2..1742ab28e 100644 --- a/apply.c +++ b/apply.c @@ -16,6 +16,9 @@ // --numstat does numeric diffstat, and doesn't actually apply // --index-info shows the old and new index info for paths if available. // +static const char *prefix; +static int prefix_length = -1; + static int allow_binary_replacement = 0; static int check_index = 0; static int write_index = 0; @@ -1706,6 +1709,12 @@ static int use_patch(struct patch *p) return 0; x = x->next; } + if (0 < prefix_length) { + int pathlen = strlen(pathname); + if (pathlen <= prefix_length || + memcmp(prefix, pathname, prefix_length)) + return 0; + } return 1; } @@ -1845,6 +1854,15 @@ int main(int argc, char **argv) line_termination = 0; continue; } + + if (check_index && prefix_length < 0) { + prefix = setup_git_directory(); + prefix_length = prefix ? strlen(prefix) : 0; + git_config(git_default_config); + } + if (0 < prefix_length) + arg = prefix_filename(prefix, prefix_length, arg); + fd = open(arg, O_RDONLY); if (fd < 0) usage(apply_usage); -- cgit v1.2.1 From e44eb3e4c74c5f6c1fca1cf92ddb454ad248c24c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 23:50:21 -0800 Subject: peek-remote: honor proxy config even from subdirectory. Use setup_git_directory_gently() at the beginning of peek-remote so that git:// proxy can be picked up from the configuration file. Signed-off-by: Junio C Hamano --- peek-remote.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/peek-remote.c b/peek-remote.c index ee49bf3b7..a90cf2206 100644 --- a/peek-remote.c +++ b/peek-remote.c @@ -27,6 +27,9 @@ int main(int argc, char **argv) char *dest = NULL; int fd[2]; pid_t pid; + int nongit = 0; + + setup_git_directory_gently(&nongit); for (i = 1; i < argc; i++) { char *arg = argv[i]; -- cgit v1.2.1 From 61e2b01529d4cb4138c00a653006d16f7a9179ce Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 23:52:04 -0800 Subject: fsck-objects: work from subdirectory. Not much point making it work from subdirectory, but for a consistency make it so. Signed-off-by: Junio C Hamano --- fsck-objects.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fsck-objects.c b/fsck-objects.c index 0433a1d0d..90e638e57 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -431,6 +431,8 @@ int main(int argc, char **argv) { int i, heads; + setup_git_directory(); + for (i = 1; i < argc; i++) { const char *arg = argv[i]; -- cgit v1.2.1 From c3e9a6534c88767e2ad9126ed45598157c28d1f4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Nov 2005 00:22:48 -0800 Subject: checkout-index: work from subdirectory. With this, git-checkout-index from a subdirectory works as expected. Note that "git-checkout-index -a" checks out files only in the current directory and under. Signed-off-by: Junio C Hamano --- checkout-index.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/checkout-index.c b/checkout-index.c index dab3778a9..f1e716d41 100644 --- a/checkout-index.c +++ b/checkout-index.c @@ -34,6 +34,9 @@ */ #include "cache.h" +static const char *prefix; +static int prefix_length; + static struct checkout state = { .base_dir = "", .base_dir_len = 0, @@ -69,6 +72,10 @@ static int checkout_all(void) struct cache_entry *ce = active_cache[i]; if (ce_stage(ce)) continue; + if (prefix && *prefix && + ( ce_namelen(ce) <= prefix_length || + memcmp(prefix, ce->name, prefix_length) )) + continue; if (checkout_entry(ce, &state) < 0) errs++; } @@ -91,6 +98,9 @@ int main(int argc, char **argv) int newfd = -1; int all = 0; + prefix = setup_git_directory(); + prefix_length = prefix ? strlen(prefix) : 0; + if (read_cache() < 0) { die("invalid cache"); } @@ -155,7 +165,7 @@ int main(int argc, char **argv) if (all) die("git-checkout-index: don't mix '--all' and explicit filenames"); - checkout_file(arg); + checkout_file(prefix_path(prefix, prefix_length, arg)); } if (all) -- cgit v1.2.1 From 706fe6ae03e2c1452d59892944701c56237b903f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Nov 2005 00:30:07 -0800 Subject: hash-object: work within subdirectory. When -w is given, it needs to find out where the .git directory is, so run the setup_git_directory() when we see a -w. Signed-off-by: Junio C Hamano --- hash-object.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hash-object.c b/hash-object.c index c8c9adb3a..c3d05a853 100644 --- a/hash-object.c +++ b/hash-object.c @@ -29,6 +29,8 @@ int main(int argc, char **argv) int i; const char *type = "blob"; int write_object = 0; + const char *prefix; + int prefix_length = -1; for (i = 1 ; i < argc; i++) { if (!strcmp(argv[i], "-t")) { @@ -36,10 +38,20 @@ int main(int argc, char **argv) die(hash_object_usage); type = argv[i]; } - else if (!strcmp(argv[i], "-w")) + else if (!strcmp(argv[i], "-w")) { + if (prefix_length < 0) { + prefix = setup_git_directory(); + prefix_length = prefix ? strlen(prefix) : 0; + } write_object = 1; - else - hash_object(argv[i], type, write_object); + } + else { + char *arg = argv[i]; + if (0 <= prefix_length) + arg = prefix_filename(prefix, prefix_length, + arg); + hash_object(arg, type, write_object); + } } return 0; } -- cgit v1.2.1 From b191fa72ea501c0789fb1bd7a80fcec9da38804d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Nov 2005 00:40:50 -0800 Subject: ls-tree: work from subdirectory. This makes ls-tree to work from subdirectory. It defaults to show the paths under the current subdirectory, and interprets user-supplied paths as relative to the current subdirectory. Signed-off-by: Junio C Hamano --- ls-tree.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ls-tree.c b/ls-tree.c index d7c7e750f..cf4223f4b 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -206,7 +206,7 @@ static int list_one(const char *path) return err; } -static int list(char **path) +static int list(const char **path) { int i; int err = 0; @@ -218,11 +218,16 @@ static int list(char **path) static const char ls_tree_usage[] = "git-ls-tree [-d] [-r] [-z] [path...]"; -int main(int argc, char **argv) +int main(int argc, const char **argv) { - static char *path0[] = { "", NULL }; - char **path; + static const char *path0[] = { "", NULL }; + const char **path; unsigned char sha1[20]; + int nongit = 0; + const char *prefix = setup_git_directory_gently(&nongit); + + if (prefix) + path0[0] = prefix; while (1 < argc && argv[1][0] == '-') { switch (argv[1][1]) { @@ -246,7 +251,11 @@ int main(int argc, char **argv) if (get_sha1(argv[1], sha1) < 0) usage(ls_tree_usage); - path = (argc == 2) ? path0 : (argv + 2); + if (argc == 2) + path = path0; + else + path = get_pathspec(prefix, argv + 2); + prepare_root(sha1); if (list(path) < 0) die("list failed"); -- cgit v1.2.1 From 5a3277133d200151fe526e56e036c933d343958a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Nov 2005 00:47:59 -0800 Subject: Make networking commands to work from a subdirectory. These are whole-tree operations and there is not much point making them operable from within a subdirectory, but it is easy to do so, and using setup_git_directory() upfront helps git:// proxy specification picked up from the correct place. Signed-off-by: Junio C Hamano --- clone-pack.c | 2 ++ fetch-pack.c | 2 ++ http-fetch.c | 2 ++ http-push.c | 1 + local-fetch.c | 2 ++ send-pack.c | 1 + ssh-fetch.c | 2 ++ ssh-upload.c | 3 +++ 8 files changed, 15 insertions(+) diff --git a/clone-pack.c b/clone-pack.c index 960921903..a99a95c5f 100644 --- a/clone-pack.c +++ b/clone-pack.c @@ -271,6 +271,8 @@ int main(int argc, char **argv) int fd[2]; pid_t pid; + setup_git_directory(); + nr_heads = 0; heads = NULL; for (i = 1; i < argc; i++) { diff --git a/fetch-pack.c b/fetch-pack.c index 656598266..58ba2094d 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -424,6 +424,8 @@ int main(int argc, char **argv) int fd[2]; pid_t pid; + setup_git_directory(); + nr_heads = 0; heads = NULL; for (i = 1; i < argc; i++) { diff --git a/http-fetch.c b/http-fetch.c index 435317342..ad59f1cce 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -922,6 +922,8 @@ int main(int argc, char **argv) int arg = 1; int rc = 0; + setup_git_directory(); + while (arg < argc && argv[arg][0] == '-') { if (argv[arg][1] == 't') { get_tree = 1; diff --git a/http-push.c b/http-push.c index ad789829c..c6e782cbe 100644 --- a/http-push.c +++ b/http-push.c @@ -1239,6 +1239,7 @@ int main(int argc, char **argv) int rc = 0; int i; + setup_git_directory(); setup_ident(); remote = xmalloc(sizeof(*remote)); diff --git a/local-fetch.c b/local-fetch.c index 093110914..fa9e697fd 100644 --- a/local-fetch.c +++ b/local-fetch.c @@ -207,6 +207,8 @@ int main(int argc, char **argv) char *commit_id; int arg = 1; + setup_git_directory(); + while (arg < argc && argv[arg][0] == '-') { if (argv[arg][1] == 't') get_tree = 1; diff --git a/send-pack.c b/send-pack.c index 3eeb18f7c..2a14b0084 100644 --- a/send-pack.c +++ b/send-pack.c @@ -273,6 +273,7 @@ int main(int argc, char **argv) int fd[2], ret; pid_t pid; + setup_git_directory(); argv++; for (i = 1; i < argc; i++, argv++) { char *arg = *argv; diff --git a/ssh-fetch.c b/ssh-fetch.c index bf01fbc00..4eb9e0482 100644 --- a/ssh-fetch.c +++ b/ssh-fetch.c @@ -131,6 +131,8 @@ int main(int argc, char **argv) prog = getenv("GIT_SSH_PUSH"); if (!prog) prog = "git-ssh-upload"; + setup_git_directory(); + while (arg < argc && argv[arg][0] == '-') { if (argv[arg][1] == 't') { get_tree = 1; diff --git a/ssh-upload.c b/ssh-upload.c index 603abcc8c..b675a0b1f 100644 --- a/ssh-upload.c +++ b/ssh-upload.c @@ -121,6 +121,9 @@ int main(int argc, char **argv) prog = getenv(COUNTERPART_ENV_NAME); if (!prog) prog = COUNTERPART_PROGRAM_NAME; + + setup_git_directory(); + while (arg < argc && argv[arg][0] == '-') { if (argv[arg][1] == 'w') arg++; -- cgit v1.2.1 From 53228a5fb8e80f87803e4a3ba8ed25b70fb4871d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 26 Nov 2005 00:50:02 -0800 Subject: Make the rest of commands work from a subdirectory. These commands are converted to run from a subdirectory. commit-tree convert-objects merge-base merge-index mktag pack-objects pack-redundant prune-packed read-tree tar-tree unpack-file unpack-objects update-server-info write-tree Signed-off-by: Junio C Hamano --- commit-tree.c | 2 ++ convert-objects.c | 2 ++ merge-base.c | 2 ++ merge-index.c | 1 + mktag.c | 2 ++ pack-objects.c | 2 ++ pack-redundant.c | 2 ++ prune-packed.c | 2 ++ read-tree.c | 2 ++ tar-tree.c | 2 ++ unpack-file.c | 2 ++ unpack-objects.c | 2 ++ update-server-info.c | 2 ++ write-tree.c | 5 ++++- 14 files changed, 29 insertions(+), 1 deletion(-) diff --git a/commit-tree.c b/commit-tree.c index b60299fed..4634b50e6 100644 --- a/commit-tree.c +++ b/commit-tree.c @@ -91,6 +91,8 @@ int main(int argc, char **argv) if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0) usage(commit_tree_usage); + setup_git_directory(); + check_valid(tree_sha1, "tree"); for (i = 2; i < argc; i += 2) { char *a, *b; diff --git a/convert-objects.c b/convert-objects.c index a892013f0..d78a8b4ae 100644 --- a/convert-objects.c +++ b/convert-objects.c @@ -316,6 +316,8 @@ int main(int argc, char **argv) unsigned char sha1[20]; struct entry *entry; + setup_git_directory(); + if (argc != 2 || get_sha1(argv[1], sha1)) usage("git-convert-objects "); diff --git a/merge-base.c b/merge-base.c index 751c3c281..e73fca745 100644 --- a/merge-base.c +++ b/merge-base.c @@ -236,6 +236,8 @@ int main(int argc, char **argv) struct commit *rev1, *rev2; unsigned char rev1key[20], rev2key[20]; + setup_git_directory(); + while (1 < argc && argv[1][0] == '-') { char *arg = argv[1]; if (!strcmp(arg, "-a") || !strcmp(arg, "--all")) diff --git a/merge-index.c b/merge-index.c index 727527fd5..024196e7a 100644 --- a/merge-index.c +++ b/merge-index.c @@ -102,6 +102,7 @@ int main(int argc, char **argv) if (argc < 3) usage("git-merge-index [-o] [-q] (-a | *)"); + setup_git_directory(); read_cache(); i = 1; diff --git a/mktag.c b/mktag.c index 585677eb8..97e270a57 100644 --- a/mktag.c +++ b/mktag.c @@ -111,6 +111,8 @@ int main(int argc, char **argv) if (argc != 1) usage("cat | git-mktag"); + setup_git_directory(); + // Read the signature size = 0; for (;;) { diff --git a/pack-objects.c b/pack-objects.c index 8864a31cc..a62c9f8d1 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -473,6 +473,8 @@ int main(int argc, char **argv) struct object_entry **list; int i; + setup_git_directory(); + for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/pack-redundant.c b/pack-redundant.c index 793fa0809..0a4327892 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -600,6 +600,8 @@ int main(int argc, char **argv) unsigned char *sha1; char buf[42]; /* 40 byte sha1 + \n + \0 */ + setup_git_directory(); + for (i = 1; i < argc; i++) { const char *arg = argv[i]; if(!strcmp(arg, "--")) { diff --git a/prune-packed.c b/prune-packed.c index 26123f7f6..d24b09711 100644 --- a/prune-packed.c +++ b/prune-packed.c @@ -58,6 +58,8 @@ int main(int argc, char **argv) { int i; + setup_git_directory(); + for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/read-tree.c b/read-tree.c index df156ea0d..e3b9c0d9f 100644 --- a/read-tree.c +++ b/read-tree.c @@ -629,6 +629,8 @@ int main(int argc, char **argv) unsigned char sha1[20]; merge_fn_t fn = NULL; + setup_git_directory(); + newfd = hold_index_file_for_update(&cache_file, get_index_file()); if (newfd < 0) die("unable to create new cachefile"); diff --git a/tar-tree.c b/tar-tree.c index 970c4bb54..bacb23ae6 100644 --- a/tar-tree.c +++ b/tar-tree.c @@ -407,6 +407,8 @@ int main(int argc, char **argv) void *buffer; unsigned long size; + setup_git_directory(); + switch (argc) { case 3: basedir = argv[2]; diff --git a/unpack-file.c b/unpack-file.c index d4ac3a546..07303f8bb 100644 --- a/unpack-file.c +++ b/unpack-file.c @@ -29,6 +29,8 @@ int main(int argc, char **argv) if (argc != 2 || get_sha1(argv[1], sha1)) usage("git-unpack-file "); + setup_git_directory(); + puts(create_temp_file(sha1)); return 0; } diff --git a/unpack-objects.c b/unpack-objects.c index 8490895cf..cfd61ae6b 100644 --- a/unpack-objects.c +++ b/unpack-objects.c @@ -269,6 +269,8 @@ int main(int argc, char **argv) int i; unsigned char sha1[20]; + setup_git_directory(); + for (i = 1 ; i < argc; i++) { const char *arg = argv[i]; diff --git a/update-server-info.c b/update-server-info.c index e824f62ea..0b6c3835b 100644 --- a/update-server-info.c +++ b/update-server-info.c @@ -19,5 +19,7 @@ int main(int ac, char **av) if (i != ac) usage(update_server_info_usage); + setup_git_directory(); + return !!update_server_info(force); } diff --git a/write-tree.c b/write-tree.c index 2b2c6b77a..0aac32f22 100644 --- a/write-tree.c +++ b/write-tree.c @@ -86,9 +86,12 @@ static int write_tree(struct cache_entry **cachep, int maxentries, const char *b int main(int argc, char **argv) { int i, funny; - int entries = read_cache(); + int entries; unsigned char sha1[20]; + setup_git_directory(); + + entries = read_cache(); if (argc == 2) { if (!strcmp(argv[1], "--missing-ok")) missing_ok = 1; -- cgit v1.2.1 From 710b7098e28513355cbdbedbb8d9c35ce5b4a488 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 22:53:20 -0800 Subject: count-objects: make it operable from a subdirectory. Signed-off-by: Junio C Hamano --- git-count-objects.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-count-objects.sh b/git-count-objects.sh index d6e9a3221..40c58efe0 100755 --- a/git-count-objects.sh +++ b/git-count-objects.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -. git-sh-setup +GIT_DIR=`git-rev-parse --git-dir` || exit $? dc /dev/null || { # This is not a real DC at all -- it just knows how -- cgit v1.2.1 From 1abacf3b5b53f6cde7148862234d451cd88d0de3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 23:15:02 -0800 Subject: ls-remote: define die() now we do not use git-sh-setup Another interesting "property" is that from inside a git managed tree, "git-ls-remote ." names the current repository no matter how deep a subdirectory you are in. Signed-off-by: Junio C Hamano --- git-ls-remote.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git-ls-remote.sh b/git-ls-remote.sh index dc6a775a9..f69926862 100755 --- a/git-ls-remote.sh +++ b/git-ls-remote.sh @@ -6,6 +6,11 @@ usage () { exit 1; } +die () { + echo >&2 "$*" + exit 1 +} + while case "$#" in 0) break;; esac do case "$1" in -- cgit v1.2.1 From 9cc2527cd2943c82cf448dccec564869b0a762e6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 23:16:15 -0800 Subject: branch: make it operable from a subdirectory. Signed-off-by: Junio C Hamano --- git-branch.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/git-branch.sh b/git-branch.sh index 4cd5da16f..b48c32988 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -1,6 +1,6 @@ #!/bin/sh -. git-sh-setup +GIT_DIR=`git-rev-parse --git-dir` || exit $? usage () { echo >&2 "usage: $(basename $0)"' [-d ] | [[-f] [start-point]] @@ -12,8 +12,7 @@ If two arguments, create a new branch based off of . exit 1 } -headref=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD | - sed -e 's|^refs/heads/||') +headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||') delete_branch () { option="$1" @@ -114,4 +113,3 @@ then fi fi git update-ref "refs/heads/$branchname" $rev - -- cgit v1.2.1 From eefaa4fca79f5240eaefd0046a338dbdac8b4204 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 23:18:04 -0800 Subject: lost-found: make it operable from a subdirectory. Signed-off-by: Junio C Hamano --- git-lost-found.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/git-lost-found.sh b/git-lost-found.sh index 9dd743001..2beec2aa6 100755 --- a/git-lost-found.sh +++ b/git-lost-found.sh @@ -1,7 +1,6 @@ #!/bin/sh -. git-sh-setup - +GIT_DIR=`git-rev-parse --git-dir` || exit $? laf="$GIT_DIR/lost-found" rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit -- cgit v1.2.1 From 7ea2fc47d258657d673ce3b9403ed98cc50e2600 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 23:19:06 -0800 Subject: tag: make it operable from a subdirectory. Signed-off-by: Junio C Hamano --- git-tag.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-tag.sh b/git-tag.sh index 16efc5b70..e71028695 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -1,7 +1,7 @@ #!/bin/sh # Copyright (c) 2005 Linus Torvalds -. git-sh-setup +GIT_DIR=`git-rev-parse --git-dir` || exit $? usage () { echo >&2 "Usage: git-tag [-a | -s | -u ] [-f | -d] [-m ] []" -- cgit v1.2.1 From d6ea70af7708af5f29db09b2d782ab3b9ce50f79 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 23:19:58 -0800 Subject: verify-tag: make it operable from a subdirectory. Signed-off-by: Junio C Hamano --- git-verify-tag.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/git-verify-tag.sh b/git-verify-tag.sh index 3c65f4a6b..d6e0eb87c 100755 --- a/git-verify-tag.sh +++ b/git-verify-tag.sh @@ -1,5 +1,6 @@ #!/bin/sh -. git-sh-setup + +GIT_DIR=`git-rev-parse --git-dir` || exit $? type="$(git-cat-file -t "$1" 2>/dev/null)" || die "$1: no such object." @@ -7,6 +8,9 @@ type="$(git-cat-file -t "$1" 2>/dev/null)" || test "$type" = tag || die "$1: cannot verify a non-tag object of type $type." -git-cat-file tag "$1" > .tmp-vtag || exit 1 -cat .tmp-vtag | sed '/-----BEGIN PGP/Q' | gpg --verify .tmp-vtag - || exit 1 -rm -f .tmp-vtag +git-cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1 +cat "$GIT_DIR/.tmp-vtag" | +sed '/-----BEGIN PGP/Q' | +gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1 +rm -f "$GIT_DIR/.tmp-vtag" + -- cgit v1.2.1 From d165fa14f0a111dfc85d964ecc037d0b280cd54f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 27 Nov 2005 23:33:54 -0800 Subject: define die() for scripts that use it. As a fallout from not using git-sh-setup in scripts that can operate from a subdirectory, we lost definition of die() from them. It might make sense to do some cleanup to consolidate them back again, but this should suffice for now. Signed-off-by: Junio C Hamano --- git-add.sh | 5 +++++ git-branch.sh | 5 +++++ git-diff.sh | 5 +++++ git-merge-octopus.sh | 5 +++++ git-tag.sh | 5 +++++ git-verify-tag.sh | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/git-add.sh b/git-add.sh index b5fe46aa2..fdec86d1a 100755 --- a/git-add.sh +++ b/git-add.sh @@ -1,5 +1,10 @@ #!/bin/sh +die () { + echo >&2 "$*" + exit 1 +} + usage() { die "usage: git add [-n] [-v] ..." } diff --git a/git-branch.sh b/git-branch.sh index b48c32988..5306b2719 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -2,6 +2,11 @@ GIT_DIR=`git-rev-parse --git-dir` || exit $? +die () { + echo >&2 "$*" + exit 1 +} + usage () { echo >&2 "usage: $(basename $0)"' [-d ] | [[-f] [start-point]] diff --git a/git-diff.sh b/git-diff.sh index b3ec84be6..e45f50ec2 100755 --- a/git-diff.sh +++ b/git-diff.sh @@ -9,6 +9,11 @@ files=$(git-rev-parse --no-revs --no-flags --sq "$@") : ${flags:="'-M' '-p'"} +die () { + echo >&2 "$*" + exit 1 +} + # 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 diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh index bb58e22a1..7adffdc79 100755 --- a/git-merge-octopus.sh +++ b/git-merge-octopus.sh @@ -8,6 +8,11 @@ LF=' ' +die () { + echo >&2 "$*" + exit 1 +} + # The first parameters up to -- are merge bases; the rest are heads. bases= head= remotes= sep_seen= for arg diff --git a/git-tag.sh b/git-tag.sh index e71028695..2435a75f7 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -8,6 +8,11 @@ usage () { exit 1 } +die () { + echo >&2 "$*" + exit 1 +} + annotate= signed= force= diff --git a/git-verify-tag.sh b/git-verify-tag.sh index d6e0eb87c..1f44da534 100755 --- a/git-verify-tag.sh +++ b/git-verify-tag.sh @@ -2,6 +2,11 @@ GIT_DIR=`git-rev-parse --git-dir` || exit $? +die () { + echo >&2 "$*" + exit 1 +} + type="$(git-cat-file -t "$1" 2>/dev/null)" || die "$1: no such object." -- cgit v1.2.1 From 99e01692063cc48adee19e1f738472a579c14ca2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 28 Nov 2005 03:19:03 -0800 Subject: hash-object.c: type-fix to squelch compiler warnings. Signed-off-by: Junio C Hamano --- hash-object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hash-object.c b/hash-object.c index c3d05a853..ccba11cb3 100644 --- a/hash-object.c +++ b/hash-object.c @@ -29,7 +29,7 @@ int main(int argc, char **argv) int i; const char *type = "blob"; int write_object = 0; - const char *prefix; + const char *prefix = NULL; int prefix_length = -1; for (i = 1 ; i < argc; i++) { @@ -46,7 +46,7 @@ int main(int argc, char **argv) write_object = 1; } else { - char *arg = argv[i]; + const char *arg = argv[i]; if (0 <= prefix_length) arg = prefix_filename(prefix, prefix_length, arg); -- cgit v1.2.1 From 2c4ed386e8861e730037abe4f4d9e032c5c46242 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Tue, 29 Nov 2005 01:20:49 -0500 Subject: git-clone --shared should imply --local The "--shared" option to git-clone is silently ignored if "--local" is not specified. The manual doesn't mention such dependency. Make "--shared" imply "--local". Signed-off-by: Pavel Roskin Signed-off-by: Junio C Hamano --- git-clone.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone.sh b/git-clone.sh index c09979a7a..699205eb6 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -73,7 +73,7 @@ while *,-n) no_checkout=yes ;; *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;; *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) - local_shared=yes ;; + local_shared=yes; use_local=yes ;; *,-q|*,--quiet) quiet=-q ;; 1,-u|1,--upload-pack) usage ;; *,-u|*,--upload-pack) -- cgit v1.2.1 From 3e9fabc85ef44fa0f275dd89738a2dacb7b6f5db Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Tue, 29 Nov 2005 09:33:36 -0800 Subject: http-push cleanup The malloc patch from Jan Andres fixed the problem that was causing a segfault when freeing the lock token, and Johannes Schindelin found and fixed a problem when no URL is specified on the command line. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano --- http-push.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/http-push.c b/http-push.c index ad789829c..fc013ec13 100644 --- a/http-push.c +++ b/http-push.c @@ -1008,9 +1008,7 @@ static int unlock_remote(struct active_lock *lock) if (lock->owner != NULL) free(lock->owner); free(lock->url); -/* Freeing the token causes a segfault... free(lock->token); -*/ free(lock); return rc; @@ -1273,6 +1271,9 @@ int main(int argc, char **argv) break; } + if (!remote->url) + usage(http_push_usage); + memset(remote_dir_exists, 0, 256); http_init(); -- cgit v1.2.1 From b020dcd54cc3f2b783339f83d09e46bb7ff08a3e Mon Sep 17 00:00:00 2001 From: "jdl@freescale.com" Date: Tue, 29 Nov 2005 08:59:56 -0600 Subject: Fix typos and minor format issues. Signed-off-by: Jon Loeliger Signed-off-by: Junio C Hamano --- Documentation/pull-fetch-param.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt index 6413d525c..b5b979242 100644 --- a/Documentation/pull-fetch-param.txt +++ b/Documentation/pull-fetch-param.txt @@ -15,10 +15,10 @@ - ssh://host.xz/~/path/to/repo.git =============================================================== + - SSH Is the default transport protocol and also supports an - scp-like syntax. Both syntaxes support username expansion, - as does the native git protocol. The following three are - identical to the last three above, respectively: +SSH Is the default transport protocol and also supports an +scp-like syntax. Both syntaxes support username expansion, +as does the native git protocol. The following three are +identical to the last three above, respectively: + =============================================================== - host.xz:/path/to/repo.git/ @@ -26,8 +26,8 @@ - host.xz:path/to/repo.git =============================================================== + - To sync with a local directory, use: - +To sync with a local directory, use: ++ =============================================================== - /path/to/repo.git/ =============================================================== @@ -113,7 +113,7 @@ on the remote branch, merge it into your development branch with `git pull . remote-B`, while you are on `my-B` branch. The common `Pull: master:origin` mapping of a remote `master` branch to a local `origin` branch, which is then merged to a -ocal development branch, again typically named `master`, is made +local development branch, again typically named `master`, is made when you run `git clone` for you to follow this pattern. + [NOTE] -- cgit v1.2.1 From 034908047d8c46fb4e1323285b3b066567c7fadc Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Tue, 29 Nov 2005 08:13:04 +0100 Subject: SVN import: Use one log call One "svn log" (or its equivalent) per revision adds delay and server load. Instead, open two SVN connections -- one for the log, and one for the files. Positive side effect: Only those log entries which actually contain data are committed => no more empty commits. Also, change the "-l" option to set the maximum revision to be pulled, not the number of revisions. Signed-off-by: Junio C Hamano --- Documentation/git-svnimport.txt | 13 +++++-------- git-svnimport.perl | 37 ++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt index fcc79fa93..f8dbee709 100644 --- a/Documentation/git-svnimport.txt +++ b/Documentation/git-svnimport.txt @@ -10,7 +10,7 @@ git-svnimport - Import a SVN repository into git SYNOPSIS -------- 'git-svnimport' [ -o ] [ -h ] [ -v ] [ -d | -D ] - [ -C ] [ -i ] [ -u ] [-l limit_nr_changes] + [ -C ] [ -i ] [ -u ] [-l limit_rev] [ -b branch_subdir ] [ -t trunk_subdir ] [ -T tag_subdir ] [ -s start_chg ] [ -m ] [ -M regex ] [ ] @@ -71,14 +71,11 @@ When importing incementally, you might need to edit the .git/svn2git file. regex. It can be used with -m to also see the default regexes. You must escape forward slashes. --l :: - Limit the number of SVN changesets we pull before quitting. - This option is necessary because the SVN library has serious memory - leaks; the recommended value for nontrivial imports is 100. +-l :: + Specify a maximum revision number to pull. - git-svnimport will still exit with a zero exit code. You can check - the size of the file ".git/svn2git" to determine whether to call - the importer again. + Formerly, this option controlled how many revisions to pull, due to + SVN memory leaks. (These have been worked around.) -v:: Verbosity: let 'svnimport' report what it is doing. diff --git a/git-svnimport.perl b/git-svnimport.perl index 45d77c5ba..65868a91e 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -35,7 +35,7 @@ our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,$opt_b sub usage() { print STDERR <new($svn); $svn = SVNconn->new($svn); my $lwp_ua; @@ -198,7 +199,7 @@ $ENV{GIT_INDEX_FILE} = $git_index; my $maxnum = 0; my $last_rev = ""; my $last_branch; -my $current_rev = $opt_s-1; +my $current_rev = $opt_s || 1; unless(-d $git_dir) { system("git-init-db"); die "Cannot init the GIT db at $git_tree: $?\n" if $?; @@ -254,7 +255,7 @@ EOM my($num,$branch,$ref) = split; $branches{$branch}{$num} = $ref; $branches{$branch}{"LAST"} = $ref; - $current_rev = $num if $current_rev < $num; + $current_rev = $num+1 if $current_rev <= $num; } close($B); } @@ -708,17 +709,17 @@ sub commit { print "DONE: $revision $dest $cid\n" if $opt_v; } -my ($changed_paths, $revision, $author, $date, $message, $pool) = @_; -sub _commit_all { - ($changed_paths, $revision, $author, $date, $message, $pool) = @_; +sub commit_all { + # Recursive use of the SVN connection does not work + local $svn = $svn2; + + my ($changed_paths, $revision, $author, $date, $message, $pool) = @_; my %p; while(my($path,$action) = each %$changed_paths) { $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ]; } $changed_paths = \%p; -} -sub commit_all { my %done; my @col; my $pref; @@ -734,18 +735,12 @@ sub commit_all { } } -while(++$current_rev <= $svn->{'maxrev'}) { - if (defined $opt_l) { - $opt_l--; - if ($opt_l < 0) { - last; - } - } - my $pool=SVN::Pool->new; - $svn->{'svn'}->get_log("/",$current_rev,$current_rev,1,1,1,\&_commit_all,$pool); - $pool->clear; - commit_all(); -} +$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'}; +print "Fetching from $current_rev to $opt_l ...\n" if $opt_v; + +my $pool=SVN::Pool->new; +$svn->{'svn'}->get_log("/",$current_rev,$opt_l,0,1,1,\&commit_all,$pool); +$pool->clear; unlink($git_index); -- cgit v1.2.1 From e1355547fd30b21fbdc3fe4e576759683777c4ce Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 14:14:42 -0800 Subject: applymbox: typofix to enable -m option. The -m option to fall back on 3-way merge was not honoured at all because of a typo. Signed-off-by: Junio C Hamano --- git-applymbox.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-applymbox.sh b/git-applymbox.sh index 24d4a8cb4..c686cc8d2 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 -- cgit v1.2.1 From bf3e274873e56d7df25d60800c8d59a309e0d8c6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:53:30 -0800 Subject: applypatch: use "index" lines not "applies-to". This matches the 3-way fallback used by applypatch to use per-blob "index" lines, not "applies-to" tree object name, to match what git-am does. Signed-off-by: Junio C Hamano --- git-applypatch.sh | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/git-applypatch.sh b/git-applypatch.sh index f0549960f..4c577eb83 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) && -- cgit v1.2.1 From 99e368b996d6fcc2383a4b385130a0cb70c164e2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:51:27 -0800 Subject: format-patch: remove applies-to. The attempt to help 3-way fallback by recording the tree object id for the entire pre-image was unnecessary, and we already have an better alternative in the form of per-blob "index" lines. Signed-off-by: Junio C Hamano --- git-format-patch.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/git-format-patch.sh b/git-format-patch.sh index 9b4088045..a26d46dba 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -238,7 +238,6 @@ 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 "@@GIT_VERSION@@" -- cgit v1.2.1 From a004d3f70f1c074f2d9bd55e7a925ff5916ebbeb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:51:27 -0800 Subject: format-patch: do not abuse 3-dash marker line. Before GIT version at the end of output we used a 3-dash marker; but 3-dash marker is special and should not be overused. Instead, use "-- " which is a standard practice in e-mails to signal the beginning of trailing garbage. Signed-off-by: Junio C Hamano --- git-format-patch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-format-patch.sh b/git-format-patch.sh index a26d46dba..4cd38f34e 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -239,7 +239,7 @@ Date: '"$ad" git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary echo git-diff-tree -p $diff_opts "$commit" - echo "---" + echo "-- " echo "@@GIT_VERSION@@" case "$mbox" in -- cgit v1.2.1 From 830273d10cf1cfe646db39b87d3ccb49bf88b73a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 13:51:27 -0800 Subject: format-patch: run diff in C locale Otherwise it would show incomplete line and binary markers in a locale dependent way. Signed-off-by: Junio C Hamano --- git-format-patch.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git-format-patch.sh b/git-format-patch.sh index 4cd38f34e..1eebe857c 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\ -- cgit v1.2.1 From 712fcc08c7a2d338f9480033597ad62223c657ec Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 30 Nov 2005 09:28:16 +1100 Subject: gitk: Add a preferences dialog with some basic stuff There is a lot more that could be put in, such as a selector for the font family etc., but this is a start. Signed-off-by: Paul Mackerras --- gitk | 142 +++++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 88 insertions(+), 54 deletions(-) diff --git a/gitk b/gitk index 730ffd920..a847ef69c 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 { -- cgit v1.2.1 From 4518bb88392fcd44bacae640754e7326a8fdf477 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 25 Nov 2005 18:45:52 -0800 Subject: [PATCH] Make git-mv work in subdirectories, too Turns out, all git programs git-mv uses are capable of operating in a subdirectory just fine. So don't complain about it. [jc: I think that sounds sane. You need to grab the exit status from `git-rev-parse --git-dir`, which I added. Alex Riesen says this worked fine.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-mv.perl | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/git-mv.perl b/git-mv.perl index 53046bafd..b6c0b4881 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; -- cgit v1.2.1 From 8bf2c69c2dd7430944898fa23edb8562c75c57e0 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 29 Nov 2005 23:10:24 +0100 Subject: [PATCH] Add tests for git-mv in subdirectories Junio C Hamano, Sat, Nov 26, 2005 03:45:52 +0100: > I haven't seriously used git-mv myself, so > somebody needs to test it, and if it actually works and Ack on > it, please. It actually works in subdirs. Signed-off-by: Junio C Hamano --- t/t7001-mv.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 t/t7001-mv.sh diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh new file mode 100755 index 000000000..43d74c502 --- /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 -- cgit v1.2.1 From 562051809589574576971c53c23aad93f8c395d9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 29 Nov 2005 21:59:04 -0800 Subject: git-diff: do not turn off -p/-M with any diff options. When the user gives a diff option (e.g. --cached) to "git diff", we turned off the built-in default option -p, which is usually not what user wants to see. This commit makes lack of --name-status, --name-only nor -r to add -p, and lack of -B*, -C* nor -M* to add -M to the flags given to the underlying diff. Signed-off-by: Junio C Hamano --- git-diff.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/git-diff.sh b/git-diff.sh index b3ec84be6..7baf7044e 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" -- cgit v1.2.1 From 10637b84d91cf8870d1db8609a10dc5e58722378 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 29 Nov 2005 21:06:10 -0800 Subject: diff-files: -1/-2/-3 to diff against unmerged stage. While resolving conflicted merge, it was not easy to compare the working tree file with unmerged index entries. This commit introduces new options -1/-2/-3 (with synonyms --base, --ours, and --theirs) to compare working tree files with specified stages. When none of these options are given, the command defaults to -2 if the index file is unmerged, otherwise it acts as before. [jc: majorly butchered from the version Linus originally posted.] Signed-off-by: Junio C Hamano --- Documentation/git-diff-files.txt | 10 +++++++ diff-files.c | 62 ++++++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index 3b04bfeec..b45d1e69d 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -21,6 +21,16 @@ 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) if there +is an unmerged path, and show diff for unmerged entries +otherwise. The option -0 can be given to force diff for +unmerged entries even when the index is unmerged. + -q:: Remain silent even on nonexisting files diff --git a/diff-files.c b/diff-files.c index 38599b5b7..bbeeea798 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 = -1; 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 */ @@ -73,6 +87,20 @@ int main(int argc, const char **argv) pathspec = get_pathspec(prefix, argv + 1); entries = read_cache(); + if (diff_unmerged_stage < 0) { + /* default to "ours" if unmerged index, otherwise 0 */ + for (i = 0; i < entries; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce)) { + diff_unmerged_stage = 2; + break; + } + } + if (diff_unmerged_stage < 0) + diff_unmerged_stage = 0; + } + + if (diff_setup_done(&diff_options) < 0) usage(diff_files_usage); @@ -94,13 +122,31 @@ int main(int argc, const char **argv) continue; if (ce_stage(ce)) { - show_unmerge(ce->name); - while (i < entries && - !strcmp(ce->name, active_cache[i]->name)) + if (!diff_unmerged_stage) + show_unmerge(ce->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; } + else if (diff_unmerged_stage) + continue; if (lstat(ce->name, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) { -- cgit v1.2.1 From 354b9b59b0a02ce7e32a7b0c10caaa1e3532f22b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 29 Nov 2005 21:06:10 -0800 Subject: merge-one-file: leave unmerged index entries upon automerge failure. When automerge fails, we used to collapse the path to stage0 from "our" branch, to help "diff-files" users to view the half-merged state against the current HEAD. Now diff-files has been taught how to compare with unmerged stage2,leaving them unmerged is a better thing to do, especially this prevents the unresolved conflicts to be committed by mistake. Signed-off-by: Junio C Hamano --- git-merge-one-file.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index c3eca8b33..739a07292 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" -- cgit v1.2.1 From 15bf57a18aaf10c038e2026dfe54281edbb6080a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Nov 2005 02:16:36 -0800 Subject: diff-files: show diffs with stage0 and unmerged stage at the same time. After thinking about it more, I realized that much of the change I did on top of Linus' version does not make much sense. This commit reverts it so that it by default shows diffs with stage0 paths or stage2 paths with working tree; the unmerged stage to use can be overridden with -1/-2/-3 option (-2 is the default so essentially is a no-op). When the index file is unmerged, we are by definition in the middle of a conflicting merge, and we should show the diff with stage 2 by default. More importantly, paths without conflicts are updated in the working tree and collapsed to stage0 in the index, so showing diff with stage0 at the same time does not hurt. In normal cases, stage0 entries should be in sync with the working tree files and does not clutter the output. It even helps the user to realize that the working tree has local changes unrelated to the merge and remember to be careful not to do a "git-commit -a" after resolving the conflicts. When there is no unmerged entries, giving diff_unmerged_stage a default value of 2 does not cause any harm, because it would not be used anyway. So in all, always showing diff between stage0 paths and unmerged entries from a stage (defaulting to 2) is the right thing to do, as Linus originally did. Signed-off-by: Junio C Hamano --- Documentation/git-diff-files.txt | 7 +++---- diff-files.c | 21 ++------------------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index b45d1e69d..67f51265e 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -26,10 +26,9 @@ include::diff-options.txt[] branch" respectively. With these options, diffs for merged entries are not shown. + -The default is to diff against our branch (-2) if there -is an unmerged path, and show diff for unmerged entries -otherwise. The option -0 can be given to force diff for -unmerged entries even when the index is unmerged. +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/diff-files.c b/diff-files.c index bbeeea798..6c0696c34 100644 --- a/diff-files.c +++ b/diff-files.c @@ -12,7 +12,7 @@ COMMON_DIFF_OPTIONS_HELP; static struct diff_options diff_options; static int silent = 0; -static int diff_unmerged_stage = -1; +static int diff_unmerged_stage = 2; static void show_unmerge(const char *path) { @@ -87,20 +87,6 @@ int main(int argc, const char **argv) pathspec = get_pathspec(prefix, argv + 1); entries = read_cache(); - if (diff_unmerged_stage < 0) { - /* default to "ours" if unmerged index, otherwise 0 */ - for (i = 0; i < entries; i++) { - struct cache_entry *ce = active_cache[i]; - if (ce_stage(ce)) { - diff_unmerged_stage = 2; - break; - } - } - if (diff_unmerged_stage < 0) - diff_unmerged_stage = 0; - } - - if (diff_setup_done(&diff_options) < 0) usage(diff_files_usage); @@ -122,8 +108,7 @@ int main(int argc, const char **argv) continue; if (ce_stage(ce)) { - if (!diff_unmerged_stage) - show_unmerge(ce->name); + show_unmerge(ce->name); while (i < entries) { struct cache_entry *nce = active_cache[i]; @@ -145,8 +130,6 @@ int main(int argc, const char **argv) if (ce_stage(ce) != diff_unmerged_stage) continue; } - else if (diff_unmerged_stage) - continue; if (lstat(ce->name, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) { -- cgit v1.2.1 From 58cce8a85da745e20c9527c569a0e0935ff3ab30 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Nov 2005 02:37:06 -0800 Subject: merge-recursive: match the unmerged index entry behaviour with merge-resolve This minimally changes merge-recursive to match what happens when O->A, O->B, A!=B 3-way filelevel merge leaves conflicts to the new merge-resolve behaviour. Signed-off-by: Junio C Hamano --- git-merge-recursive.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/git-merge-recursive.py b/git-merge-recursive.py index 012923355..e599b11cc 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.") -- cgit v1.2.1 From 0501c2409d7661d2b13f094136d246b9b5b8acf9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 30 Nov 2005 02:38:24 -0800 Subject: Tutorial: adjust merge example to recursive strategy. Current default, merge-recursive, gives slightly different message while working from merge-resolve which was used to prepare the illustration in the tutorial. Signed-off-by: Junio C Hamano --- Documentation/tutorial.txt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index e2dfb00ab..cf7ba76dd 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 ------------------------------------------------ -- cgit v1.2.1 From 0738fc2192109f946c033b3b003cb10aace9554e Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Wed, 30 Nov 2005 17:37:10 +0200 Subject: Do not attempt to access literal dirname "GIT_OBJECT_DIRECTORY". Dereference the environment variable before using it. Signed-off-by: Tommi Virtanen Signed-off-by: Junio C Hamano --- setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.c b/setup.c index cc44a724b..3286a568e 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 { -- cgit v1.2.1 From b34403aa97047f90c0cdd5177e63a8e7530e3388 Mon Sep 17 00:00:00 2001 From: Timo Hirvonen Date: Thu, 1 Dec 2005 03:32:01 +0200 Subject: Move couple of ifdefs after "include config.mk" This makes it possible to define WITH_SEND_EMAIL etc. in config.mak. Also remove GIT_LIST_TWEAK because it isn't used anymore. Signed-off-by: Junio C Hamano --- Makefile | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 984d167de..00521fe51 100644 --- a/Makefile +++ b/Makefile @@ -138,8 +138,6 @@ ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X # Backward compatibility -- to be removed after 1.0 PROGRAMS += git-ssh-pull$X git-ssh-push$X -GIT_LIST_TWEAK = - # Set paths to tools early so that they can be used for version tests. ifndef SHELL_PATH SHELL_PATH = /bin/sh @@ -154,20 +152,6 @@ endif PYMODULES = \ gitMergeCommon.py -ifdef WITH_OWN_SUBPROCESS_PY - PYMODULES += compat/subprocess.py -else - ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK) - PYMODULES += compat/subprocess.py - endif -endif - -ifdef WITH_SEND_EMAIL - SCRIPT_PERL += git-send-email.perl -else - GIT_LIST_TWEAK += -e '/^send-email$$/d' -endif - LIB_FILE=libgit.a LIB_H = \ @@ -256,6 +240,18 @@ endif -include config.mak +ifdef WITH_OWN_SUBPROCESS_PY + PYMODULES += compat/subprocess.py +else + ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK) + PYMODULES += compat/subprocess.py + endif +endif + +ifdef WITH_SEND_EMAIL + SCRIPT_PERL += git-send-email.perl +endif + ifndef NO_CURL ifdef CURLDIR # This is still problematic -- gcc does not always want -R. -- cgit v1.2.1 From a3e3dc46901e3ba20a9f1d3ae503ac2faf5a8ea9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Dec 2005 12:24:51 -0800 Subject: Documentation: describe '-f' option to git-fetch. The option description header was there without body text, confusingly getting rendered as if the description for --tags applied to the option. Noticed by Carl Baldwin. Signed-off-by: Junio C Hamano --- Documentation/fetch-options.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index a25d04a4f..200c9b240 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -4,6 +4,11 @@ option old data in `.git/FETCH_HEAD` will be overwritten. -f, \--force:: + When `git-fetch` is used with `:` + refspec, it refuses to update the local branch + `` unless the remote branch `` it + fetches is a descendant of ``. This option + overrides that check. -t, \--tags:: By default, the git core utilities will not fetch and store -- cgit v1.2.1 From 2731d04883317f5de03a942bb0ea8c1eba6babad Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Dec 2005 12:26:41 -0800 Subject: Makefile: say the default target upfront. Alex Riesen wants to keep extra makefile targets in config.mak, but the file is included before any of our real targets. Having this at the beginning allows you to do so. Signed-off-by: Junio C Hamano --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 00521fe51..45db357cc 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +# The default target of this Makefile is... +all: + # Define MOZILLA_SHA1 environment variable when running make to make use of # a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast # on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default -- cgit v1.2.1 From 0f8f45cb4a7e664b396f73c25891da46b953b8b8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 1 Dec 2005 10:35:51 -0800 Subject: git-ls-tree: add "-t" option to always show the tree entries The old (new) behaviour was that it only shows trees if the object is specified exactly, and recursive is not set. That makes sense, because there is obviously nothing else it can show for that case. However, with the new "-t" option, it will show the tree even with "-r", as it traverses down into it. NOTE! This also means that it will show all trees leading up to that tree. For example, if you do a git-ls-tree -t HEAD -- drivers/char/this/file/does/not/exist it will show the trees that lead up to the files that do not exist: [torvalds@g5 linux]$ git-ls-tree -t HEAD -- drivers/char/this/file/does/not/exist 040000 tree 9cb687b77dcd64bf82e9a73214db467c964c1266 drivers 040000 tree 298e2fadf0ff3867d1ef49936fd2c7bf6ce1eb66 drivers/char [torvalds@g5 linux]$ and note how this is true even though I didn't specify "-r": the fact that I supplied a pathspec automatically implies "enough recursion" for that particular pathspec. I think the code is cleaner and easier to understand too: the patch looks bigger, but it's really just splitting up the "should we recurse into this tree" into a function of its own. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- ls-tree.c | 65 ++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/ls-tree.c b/ls-tree.c index d4b62198a..a2a5eb051 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -11,39 +11,53 @@ static int line_termination = '\n'; #define LS_RECURSIVE 1 #define LS_TREE_ONLY 2 +#define LS_SHOW_TREES 4 static int ls_options = 0; const char **pathspec; static const char ls_tree_usage[] = - "git-ls-tree [-d] [-r] [-z] [path...]"; + "git-ls-tree [-d] [-r] [-t] [-z] [path...]"; + +static int show_recursive(const char *base, int baselen, const char *pathname) +{ + const char **s; + + if (ls_options & LS_RECURSIVE) + return 1; + + s = pathspec; + if (!s) + return 0; + + for (;;) { + const char *spec = *s++; + int len, speclen; + + if (!spec) + return 0; + 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 1; + } +} static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage) { + int retval = 0; 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; - } + if (show_recursive(base, baselen, pathname)) { + retval = READ_TREE_RECURSIVE; + if (!(ls_options & LS_SHOW_TREES)) + return retval; } type = "tree"; } @@ -51,7 +65,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c 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; + return retval; } int main(int argc, const char **argv) @@ -73,6 +87,9 @@ int main(int argc, const char **argv) case 'd': ls_options |= LS_TREE_ONLY; break; + case 't': + ls_options |= LS_SHOW_TREES; + break; default: usage(ls_tree_usage); } -- cgit v1.2.1 From f59846718ed98e153c0423eaa5f7ff0526d2a6f8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Dec 2005 13:15:20 -0800 Subject: ls-tree: resurrect '-d' to mean 'show trees only' With this: git-ls-tree -d HEAD -- drivers/net/ shows only immediate subtrees of drivers/net. git-ls-tree -d -t HEAD -- drivers/net/ shows drivers, drivers/net and immediate subtrees of drivers/net. git-ls-tree -d -r HEAD -- drivers/net/ shows drivers, drivers/net and all subtrees of drivers/net (but not blobs). Signed-off-by: Junio C Hamano --- ls-tree.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ls-tree.c b/ls-tree.c index a2a5eb051..07db863bb 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -61,6 +61,8 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c } type = "tree"; } + else if (ls_options & LS_TREE_ONLY) + return 0; printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1)); write_name_quoted(base, baselen, pathname, line_termination, stdout); @@ -95,6 +97,10 @@ int main(int argc, const char **argv) } argc--; argv++; } + /* -d -r should imply -t, but -d by itself should not have to. */ + if ( (LS_TREE_ONLY|LS_RECURSIVE) == + ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options)) + ls_options |= LS_SHOW_TREES; if (argc < 2) usage(ls_tree_usage); -- cgit v1.2.1 From c639a5548a5d8414b55202592885449f66ee2f33 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Dec 2005 14:54:00 -0800 Subject: ls-tree: --name-only Fingers of some "git diff" users are trained to do --name-only which git-ls-tree unfortunately does not take. With this, cd sub/directory && git-ls-tree -r --name-only .. would show only the names not object names nor modes. I threw in another synonym --name-status only for usability, but obviously ls-tree does not do any comparison so what it does is the same as --name-only. Signed-off-by: Junio C Hamano --- ls-tree.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ls-tree.c b/ls-tree.c index 07db863bb..dae377d99 100644 --- a/ls-tree.c +++ b/ls-tree.c @@ -12,11 +12,12 @@ static int line_termination = '\n'; #define LS_RECURSIVE 1 #define LS_TREE_ONLY 2 #define LS_SHOW_TREES 4 +#define LS_NAME_ONLY 8 static int ls_options = 0; const char **pathspec; static const char ls_tree_usage[] = - "git-ls-tree [-d] [-r] [-t] [-z] [path...]"; + "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [path...]"; static int show_recursive(const char *base, int baselen, const char *pathname) { @@ -64,7 +65,8 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen, const c else if (ls_options & LS_TREE_ONLY) return 0; - printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1)); + if (!(ls_options & LS_NAME_ONLY)) + printf("%06o %s %s\t", mode, type, sha1_to_hex(sha1)); write_name_quoted(base, baselen, pathname, line_termination, stdout); putchar(line_termination); return retval; @@ -92,6 +94,13 @@ int main(int argc, const char **argv) case 't': ls_options |= LS_SHOW_TREES; break; + case '-': + if (!strcmp(argv[1]+2, "name-only") || + !strcmp(argv[1]+2, "name-status")) { + ls_options |= LS_NAME_ONLY; + break; + } + /* otherwise fallthru */ default: usage(ls_tree_usage); } -- cgit v1.2.1 From ce3ca275452cf069eb6451d6f5b0f424a6f046aa Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Dec 2005 17:02:04 -0800 Subject: git-merge-one-file: do not worry about 'rmdir -p' not removing directory. 9ae2172aed289f2706a0e88288909fa47eddd7e7 used "rmdir -p" carelessly, causing the more important "git-update-index --remove" to be skipped. Signed-off-by: Junio C Hamano --- git-merge-one-file.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index 739a07292..9a049f426 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -26,7 +26,7 @@ case "${1:-.}${2:-.}${3:-.}" in fi if test -f "$4"; then rm -f -- "$4" && - rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null + rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null || : fi && exec git-update-index --remove -- "$4" ;; -- cgit v1.2.1 From 10b15b86f545e081aebba8783ad9e9acf6bf0d98 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 1 Dec 2005 13:48:35 +0100 Subject: git wrapper: more careful argument stuffing - Use stderr for error output - Build git_command more careful - ENOENT is good enough for check of failed exec to show usage, no access() check needed [jc: Originally from Alex Riesen with inputs from Sven Verdoolaege mixed in.] Signed-off-by: Junio C Hamano --- git.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/git.c b/git.c index 0b10b6e78..878c35970 100644 --- a/git.c +++ b/git.c @@ -283,16 +283,21 @@ int main(int argc, char **argv, char **envp) len = strlen(git_command); prepend_to_path(git_command, len); - strncat(&git_command[len], "/git-", sizeof(git_command) - len); - len += 5; - strncat(&git_command[len], argv[i], sizeof(git_command) - len); - - if (access(git_command, X_OK)) - usage(exec_path, "'%s' is not a git-command", argv[i]); + len += snprintf(git_command + len, sizeof(git_command) - len, + "/git-%s", argv[i]); + if (sizeof(git_command) <= len) { + fprintf(stderr, "git: command name given is too long (%d)\n", len); + exit(1); + } /* execve() can only ever return if it fails */ execve(git_command, &argv[i], envp); - printf("Failed to run command '%s': %s\n", git_command, strerror(errno)); + + if (errno == ENOENT) + usage(exec_path, "'%s' is not a git-command", argv[i]); + + fprintf(stderr, "Failed to run command '%s': %s\n", + git_command, strerror(errno)); return 1; } -- cgit v1.2.1 From a6b51f11ab7f7f838a9b17f81059eebfc36e4c84 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Dec 2005 21:39:41 -0800 Subject: merge-recursive: adjust git-ls-tree use for the latest. You need to pass -t flag if you want to see tree objects in "git-ls-tree -r" output these days. This change broke the tree structure reading code in git-merge-recursive used to detect D/F conflicts. Signed-off-by: Junio C Hamano --- git-merge-recursive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-merge-recursive.py b/git-merge-recursive.py index e599b11cc..b7fb0961e 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -98,7 +98,7 @@ getFilesRE = re.compile(r'^([0-7]+) (\S+) ([0-9a-f]{40})\t(.*)$', re.S) def getFilesAndDirs(tree): files = Set() dirs = Set() - out = runProgram(['git-ls-tree', '-r', '-z', tree]) + out = runProgram(['git-ls-tree', '-r', '-z', '-t', tree]) for l in out.split('\0'): m = getFilesRE.match(l) if m: -- cgit v1.2.1 From 57ae0d09ed3c4e409bed78b77322fa5e9bad3f3f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Dec 2005 22:49:52 -0800 Subject: t3100: add ls-tree -t and -d tests. Signed-off-by: Junio C Hamano --- t/t3100-ls-tree-restrict.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index ae086755e..2ec06d3d3 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -60,6 +60,29 @@ test_expect_success \ EOF test_output' +test_expect_success \ + 'ls-tree recursive with -t' \ + 'git-ls-tree -r -t $tree >current && + 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 +EOF + test_output' + +test_expect_success \ + 'ls-tree recursive with -d' \ + 'git-ls-tree -r -d $tree >current && + cat >expected <<\EOF && +040000 tree X path2 +040000 tree X path2/baz +EOF + test_output' + test_expect_success \ 'ls-tree filtered with path' \ 'git-ls-tree $tree path >current && @@ -117,4 +140,19 @@ test_expect_success \ EOF test_output' +test_expect_success \ + 'ls-tree filtered with path2/bak' \ + 'git-ls-tree $tree path2/bak >current && + cat >expected <<\EOF && +EOF + test_output' + +test_expect_success \ + 'ls-tree -t filtered with path2/bak' \ + 'git-ls-tree -t $tree path2/bak >current && + cat >expected <<\EOF && +040000 tree X path2 +EOF + test_output' + test_done -- cgit v1.2.1 From 1c2c10b6e6d86066d68635a2a968c7162498ea41 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Dec 2005 00:50:59 -0800 Subject: merge-one-file: make sure we create the merged file. The "update-index followed by checkout-index" chain served two purposes -- to collapse the index to "our" version, and make sure that file exists in the working tree. In the recent update to leave the index unmerged on conflicting path, we wanted to stop doing the former, but we still need to do the latter (we allow merging to work in an un-checked-out working tree). Signed-off-by: Junio C Hamano --- git-merge-one-file.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index 9a049f426..906098dda 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -79,7 +79,13 @@ case "${1:-.}${2:-.}${3:-.}" in ;; esac - merge "$4" "$orig" "$src2" + # Create the working tree file, with the correct permission bits. + # we can not rely on the fact that our tree has the path, because + # we allow the merge to be done in an unchecked-out working tree. + rm -f "$4" && + git-cat-file blob "$2" >"$4" && + case "$6" in *7??) chmod +x "$4" ;; esac && + merge "$4" "$orig" "$src2" ret=$? rm -f -- "$orig" "$src2" -- cgit v1.2.1 From 54dd99a127caf4d20e1b91a43949655763d188ed Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Dec 2005 00:54:50 -0800 Subject: merge-one-file: make sure we do not mismerge symbolic links. We ran "merge" command on O->A, O->B, A!=B case without verifying the path involved is not a symlink. Signed-off-by: Junio C Hamano --- git-merge-one-file.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index 906098dda..eafef770d 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -58,6 +58,14 @@ case "${1:-.}${2:-.}${3:-.}" in # Modified in both, but differently. # "$1$2$3" | ".$2$3") + + case ",$6,$7," in + *,120000,*) + echo "ERROR: $4: Not merging symbolic link changes." + exit 1 + ;; + esac + src2=`git-unpack-file $3` case "$1" in '') -- cgit v1.2.1 From 3ace1fe34bbf24a62215d06614f9cf29a80def4a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Dec 2005 01:05:35 -0800 Subject: git-merge documentation: conflicting merge leaves higher stages in index This hopefully concludes the latest updates that changes the behaviour of the merge on an unsuccessful automerge. Instead of collapsing the conflicted path in the index to show HEAD, we leave it unmerged, now that diff-files can compare working tree files with higher stages. Signed-off-by: Junio C Hamano --- Documentation/git-merge.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index c1174041e..0cac563d4 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -108,10 +108,12 @@ When there are conflicts, these things happen: 2. Cleanly merged paths are updated both in the index file and in your working tree. -3. For conflicting paths, the index file records the version - from `HEAD`. The working tree files have the result of - "merge" program; i.e. 3-way merge result with familiar - conflict markers `<<< === >>>`. +3. For conflicting paths, the index file records up to three + versions; stage1 stores the version from the common ancestor, + stage2 from `HEAD`, and stage3 from the remote branch (you + can inspect the stages with `git-ls-files -u`). The working + tree files have the result of "merge" program; i.e. 3-way + merge result with familiar conflict markers `<<< === >>>`. 4. No other changes are done. In particular, the local modifications you had before you started merge will stay the -- cgit v1.2.1 From 3f41f5a9fefafabb561db4ce9c9b4e0944ec1f85 Mon Sep 17 00:00:00 2001 From: No name Date: Fri, 2 Dec 2005 06:37:13 -0500 Subject: documentation: clarify read-tree --reset [jc: light edit applied on top of the original] Signed-off-by: Junio C Hamano --- Documentation/git-read-tree.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 8b9184785..6e92e4aa6 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -28,11 +28,14 @@ will be in unmerged state when "git-read-tree" returns. OPTIONS ------- -m:: - Perform a merge, not just a read. + Perform a merge, not just a read. The command will + refuse to run if your index file has unmerged entries, + indicating that you have not finished previous merge you + started. --reset:: - - Same as -m except that unmerged entries will be silently ignored. + Same as -m, except that unmerged entries are discarded + instead of failing. -u:: After a successful merge, update the files in the work @@ -47,7 +50,6 @@ OPTIONS trees that are not directly related to the current working tree status into a temporary index file. - :: The id of the tree object(s) to be read/merged. -- cgit v1.2.1 From d2abdd2647c6241ec7cced9df9cdd4b98d3d6898 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Dec 2005 12:15:23 -0800 Subject: documentation: git-bisect (help HTML break man) Use the same trick Josef used to introduce line breaks for git-mv documentation for now, to help HTML rendering. This breaks manpages and we need to come up with a better solution. Noticed by linux@horizon.com (No Name). Signed-off-by: Junio C Hamano --- Documentation/git-bisect.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 39fa665d9..8a399703d 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -8,13 +8,13 @@ git-bisect - Find the change that introduced a bug SYNOPSIS -------- -'git bisect' start -'git bisect' bad -'git bisect' good -'git bisect' reset [] -'git bisect' visualize -'git bisect' replay -'git bisect' log + 'git bisect' start + 'git bisect' bad + 'git bisect' good + 'git bisect' reset [] + 'git bisect' visualize + 'git bisect' replay + 'git bisect' log DESCRIPTION ----------- -- cgit v1.2.1 From 56b5e946f2ba4fd57bbe14f9e3ec2e0ae314d5e6 Mon Sep 17 00:00:00 2001 From: No name Date: Fri, 2 Dec 2005 06:37:13 -0500 Subject: documentation: git-tag [jc: light edit applied on top of the original] Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 95de436c1..841c9dcf9 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -10,6 +10,26 @@ SYNOPSIS -------- 'git-tag' [-a | -s | -u ] [-f | -d] [-m ] [] +OPTIONS +------- +-a:: + Make an unsigned, annotated tag object + +-s:: + Make a GPG-signed tag, using the default e-mail address's key + +-u :: + Make a GPG-signed tag, using the given key + +-f:: + Replace an existing tag with the given name (instead of failing) + +-d:: + Delete an existing tag with the given name + +-m :: + Use the given tag message (instead of prompting) + DESCRIPTION ----------- Adds a 'tag' reference in .git/refs/tags/ @@ -23,7 +43,7 @@ creates a 'tag' object, and requires the tag message. Unless in the tag message. Otherwise just the SHA1 object name of the commit object is -written (i.e. an lightweight tag). +written (i.e. a lightweight tag). A GnuPG signed tag object will be created when `-s` or `-u ` is used. When `-u ` is not used, the -- cgit v1.2.1 From 4275df517022604f5c33ba05ae45a885b84e3472 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Sat, 3 Dec 2005 11:40:21 +0100 Subject: git-merge: Exit with code 2 if no strategy was able to handle the merge. This way it is possible to test in scripts if the merge was non-clean or if the strategy had other problems with the merge. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano --- git-merge.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-merge.sh b/git-merge.sh index d352a3cf6..a221daa7f 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -273,7 +273,8 @@ fi case "$best_strategy" in '') restorestate - die "No merge strategy handled the merge." + echo >&2 "No merge strategy handled the merge." + exit 2 ;; "$wt_strategy") # We already have its result in the working tree. -- cgit v1.2.1 From d3bfdb755e06c04492ad3977e7df3d780b6e02f2 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Sat, 3 Dec 2005 11:40:39 +0100 Subject: test-lib.sh: Add new function, test_expect_code The test is considered OK if it exits with code $1 Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano --- t/test-lib.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/t/test-lib.sh b/t/test-lib.sh index e654155a2..f2eccd791 100755 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -133,6 +133,19 @@ test_expect_success () { fi } +test_expect_code () { + test "$#" = 3 || + error "bug in the test script: not 3 parameters to test-expect-code" + say >&3 "expecting exit code $1: $3" + test_run_ "$3" + if [ "$?" = 0 -a "$eval_ret" = "$1" ] + then + test_ok_ "$2" + else + test_failure_ "$@" + fi +} + test_done () { trap - exit case "$test_failure" in -- cgit v1.2.1 From 72d1216a04232f5cc85c6b41109db964b55f8289 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Sat, 3 Dec 2005 11:41:20 +0100 Subject: New test case: merge with directory/file conflicts Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano --- t/t6020-merge-df.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 t/t6020-merge-df.sh diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh new file mode 100755 index 000000000..a19d49de2 --- /dev/null +++ b/t/t6020-merge-df.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2005 Fredrik Kuivinen +# + +test_description='Test merge with directory/file conflicts' +. ./test-lib.sh + +test_expect_success 'prepare repository' \ +'echo "Hello" > init && +git add init && +git commit -m "Initial commit" && +git branch B && +mkdir dir && +echo "foo" > dir/foo && +git add dir/foo && +git commit -m "File: dir/foo" && +git checkout B && +echo "file dir" > dir && +git add dir && +git commit -m "File: dir"' + +test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master' + +test_done -- cgit v1.2.1 From 7057463463ad01266db264acf7e7b5e95e7b4ecf Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Sat, 3 Dec 2005 11:41:54 +0100 Subject: New test case: Criss-cross merge Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano --- t/t6021-merge-criss-cross.sh | 92 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100755 t/t6021-merge-criss-cross.sh diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh new file mode 100755 index 000000000..e8606c751 --- /dev/null +++ b/t/t6021-merge-criss-cross.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# +# Copyright (c) 2005 Fredrik Kuivinen +# + +# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a +# nice decription of what this is about. + + +test_description='Test criss-cross merge' +. ./test-lib.sh + +test_expect_success 'prepare repository' \ +'echo "1 +2 +3 +4 +5 +6 +7 +8 +9" > file && +git add file && +git commit -m "Initial commit" file && +git branch A && +git branch B && +git checkout A && +echo "1 +2 +3 +4 +5 +6 +7 +8 changed in B8, branch A +9" > file && +git commit -m "B8" file && +git checkout B && +echo "1 +2 +3 changed in C3, branch B +4 +5 +6 +7 +8 +9 +" > file && +git commit -m "C3" file && +git branch C3 && +git merge "pre E3 merge" B A && +echo "1 +2 +3 changed in E3, branch B. New file size +4 +5 +6 +7 +8 changed in B8, branch A +9 +" > file && +git commit -m "E3" file && +git checkout A && +git merge "pre D8 merge" A C3 && +echo "1 +2 +3 changed in C3, branch B +4 +5 +6 +7 +8 changed in D8, branch A. New file size 2 +9" > file && +git commit -m D8 file' + +test_expect_success 'Criss-cross merge' 'git merge "final merge" A B' + +cat > file-expect < Date: Fri, 2 Dec 2005 15:08:28 -0800 Subject: Add compat/setenv.c, use in git.c. There is no setenv() in Solaris 5.8. The trivial calls to setenv() were replaced by putenv() in a much earlier patch, but setenv() was used again in git.c. This patch just adds a compat/setenv.c. The rule for building git$(X) also needs to include compat. objects and compiler flags. Those are now in makefile vars COMPAT_OBJS and COMPAT_CFLAGS. Signed-off-by: E. Jason Riedy Signed-off-by: Junio C Hamano --- Makefile | 27 ++++++++++++++++++--------- compat/setenv.c | 31 +++++++++++++++++++++++++++++++ git.c | 4 ++++ 3 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 compat/setenv.c diff --git a/Makefile b/Makefile index 45db357cc..df3c6eb81 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,8 @@ all: # # Define NO_STRCASESTR if you don't have strcasestr. # +# Define NO_SETENV if you don't have setenv in the C library. +# # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. # @@ -194,6 +196,7 @@ shellquote = '$(call shq,$(1))' uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') +uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') ifeq ($(uname_S),Darwin) NEEDS_SSL_WITH_CRYPTO = YesPlease @@ -211,6 +214,9 @@ ifeq ($(uname_S),SunOS) NEEDS_LIBICONV = YesPlease SHELL_PATH = /bin/bash NO_STRCASESTR = YesPlease + ifeq ($(uname_R),5.8) + NO_SETENV = YesPlease + endif INSTALL = ginstall TAR = gtar ALL_CFLAGS += -D__EXTENSIONS__ @@ -314,12 +320,16 @@ ifdef NEEDS_NSL SIMPLE_LIB += -lnsl endif ifdef NO_STRCASESTR - ALL_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1 - LIB_OBJS += compat/strcasestr.o + COMPAT_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1 + COMPAT_OBJS += compat/strcasestr.o +endif +ifdef NO_SETENV + COMPAT_CFLAGS += -Dsetenv=gitsetenv -DNO_SETENV=1 + COMPAT_OBJS += compat/setenv.o endif ifdef NO_MMAP - ALL_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP - LIB_OBJS += compat/mmap.o + COMPAT_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP + COMPAT_OBJS += compat/mmap.o endif ifdef NO_IPV6 ALL_CFLAGS += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in @@ -343,8 +353,8 @@ endif endif endif -ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) - +ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) $(COMPAT_CFLAGS) +LIB_OBJS += $(COMPAT_OBJS) export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir ### Build rules @@ -353,10 +363,9 @@ all: $(ALL_PROGRAMS) all: $(MAKE) -C templates -# Only use $(CFLAGS). We don't need anything else. -git$(X): git.c Makefile +git$(X): git.c $(COMPAT_OBJS) Makefile $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \ - $(CFLAGS) $< -o $@ + $(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(filter %.o,$^) $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh rm -f $@ diff --git a/compat/setenv.c b/compat/setenv.c new file mode 100644 index 000000000..94acd2da9 --- /dev/null +++ b/compat/setenv.c @@ -0,0 +1,31 @@ +#include +#include + +int gitsetenv(const char *name, const char *value, int replace) +{ + int out; + size_t namelen, valuelen; + char *envstr; + + if (!name || !value) return -1; + if (!replace) { + char *oldval = NULL; + oldval = getenv(name); + if (oldval) return 0; + } + + namelen = strlen(name); + valuelen = strlen(value); + envstr = malloc((namelen + valuelen + 2) * sizeof(char)); + if (!envstr) return -1; + + memcpy(envstr, name, namelen); + envstr[namelen] = '='; + memcpy(envstr + namelen + 1, value, valuelen); + envstr[namelen + valuelen + 1] = 0; + + out = putenv(envstr); + + free(envstr); + return out; +} diff --git a/git.c b/git.c index 878c35970..619f25acf 100644 --- a/git.c +++ b/git.c @@ -13,6 +13,10 @@ # define PATH_MAX 4096 #endif +#ifdef NO_SETENV +extern int gitsetenv(char *name, char *value, int overwrite); +#endif + static const char git_usage[] = "Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]"; -- cgit v1.2.1 From 7950571ad75c1c97e5e53626d8342b01b167c790 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 3 Dec 2005 17:57:48 -0800 Subject: A few more options for git-cat-file This adds '-e' option to git-cat-file, to test for the existence of the object. This also cleans up the option-parsing in git-cat-file slightly. [jc: HPA version had -n option which did rev-parse --verify; the real value of this patch is the option parsing cleanup.] Signed-off-by: H. Peter Anvin Signed-off-by: Junio C Hamano --- Documentation/git-cat-file.txt | 13 +++++++++--- cat-file.c | 47 ++++++++++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index ab4dcae21..9a7700fa7 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -8,7 +8,7 @@ git-cat-file - Provide content or type information for repository objects SYNOPSIS -------- -'git-cat-file' (-t | -s | ) +'git-cat-file' (-t | -s | -e | ) DESCRIPTION ----------- @@ -29,6 +29,10 @@ OPTIONS Instead of the content, show the object size identified by . +-e:: + Suppress all output; instead exit with zero status if + exists and is a valid object. + :: Typically this matches the real type of but asking for a type that can trivially be dereferenced from the given @@ -39,8 +43,11 @@ OPTIONS OUTPUT ------ -If '-t' is specified, one of the . If '-s' is specified, -the size of the in bytes. +If '-t' is specified, one of the . + +If '-s' is specified, the size of the in bytes. + +If '-e' is specified, no output. Otherwise the raw (though uncompressed) contents of the will be returned. diff --git a/cat-file.c b/cat-file.c index d775a1545..7594108c6 100644 --- a/cat-file.c +++ b/cat-file.c @@ -11,27 +11,44 @@ int main(int argc, char **argv) char type[20]; void *buf; unsigned long size; + int opt; setup_git_directory(); if (argc != 3 || get_sha1(argv[2], sha1)) - usage("git-cat-file [-t | -s | ] "); - - if (!strcmp("-t", argv[1]) || !strcmp("-s", argv[1])) { - if (!sha1_object_info(sha1, type, - argv[1][1] == 's' ? &size : NULL)) { - switch (argv[1][1]) { - case 't': - printf("%s\n", type); - break; - case 's': - printf("%lu\n", size); - break; - } + usage("git-cat-file [-t|-s|-e|] "); + + opt = 0; + if ( argv[1][0] == '-' ) { + opt = argv[1][1]; + if ( !opt || argv[1][2] ) + opt = -1; /* Not a single character option */ + } + + buf = NULL; + switch (opt) { + case 't': + if (!sha1_object_info(sha1, type, NULL)) { + printf("%s\n", type); return 0; } - buf = NULL; - } else { + break; + + case 's': + if (!sha1_object_info(sha1, type, &size)) { + printf("%lu\n", size); + return 0; + } + break; + + case 'e': + return !has_sha1_file(sha1); + + case 0: buf = read_object_with_reference(sha1, argv[1], &size, NULL); + break; + + default: + die("git-cat-file: unknown option: %s\n", argv[1]); } if (!buf) -- cgit v1.2.1 From d79374c7b58d3814ffdc277de608243f8e665e3a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 3 Dec 2005 01:45:57 -0800 Subject: [PATCH] daemon.c and path.enter_repo(): revamp path validation. The whitelist of git-daemon is checked against return value from enter_repo(), and enter_repo() used to return the value obtained from getcwd() to avoid directory aliasing issues as discussed earier (mid October 2005). Unfortunately, it did not go well as we hoped. For example, /pub on a kernel.org public machine is a symlink to its real mountpoint, and it is understandable that the administrator does not want to adjust the whitelist every time /pub needs to point at a different partition for storage allcation or whatever reasons. Being able to keep using /pub/scm as the whitelist is a desirable property. So this version of enter_repo() reports what it used to chdir() and validate, but does not use getcwd() to canonicalize the directory name. When it sees a user relative path ~user/path, it internally resolves it to try chdir() there, but it still reports ~user/path (possibly after appending .git if allowed to do so, in which case it would report ~user/path.git). What this means is that if a whitelist wants to allow a user relative path, it needs to say "~" (for all users) or list user home directories like "~alice" "~bob". And no, you cannot say /home if the advertised way to access user home directories are ~alice,~bob, etc. The whole point of this is to avoid unnecessary aliasing issues. Anyway, because of this, daemon needs to do a bit more work to guard itself. Namely, it needs to make sure that the accessor does not try to exploit its leading path match rule by inserting /../ in the middle or hanging /.. at the end. I resurrected the belts and suspender paranoia code HPA did for this purpose. This check cannot be done in the enter_repo() unconditionally, because there are valid callers of enter_repo() that want to honor /../; authorized users coming over ssh to run send-pack and fetch-pack should be allowed to do so. Signed-off-by: Junio C Hamano --- daemon.c | 64 ++++++++++++++++++++++++-- path.c | 153 +++++++++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 159 insertions(+), 58 deletions(-) diff --git a/daemon.c b/daemon.c index 91b96569c..539f6e87a 100644 --- a/daemon.c +++ b/daemon.c @@ -82,9 +82,63 @@ static void loginfo(const char *err, ...) va_end(params); } +static int avoid_alias(char *p) +{ + int sl, ndot; + + /* + * This resurrects the belts and suspenders paranoia check by HPA + * done in <435560F7.4080006@zytor.com> thread, now enter_repo() + * does not do getcwd() based path canonicalizations. + * + * sl becomes true immediately after seeing '/' and continues to + * be true as long as dots continue after that without intervening + * non-dot character. + */ + if (!p || (*p != '/' && *p != '~')) + return -1; + sl = 1; ndot = 0; + p++; + + while (1) { + char ch = *p++; + if (sl) { + if (ch == '.') + ndot++; + else if (ch == '/') { + if (ndot < 3) + /* reject //, /./ and /../ */ + return -1; + ndot = 0; + } + else if (ch == 0) { + if (0 < ndot && ndot < 3) + /* reject /.$ and /..$ */ + return -1; + return 0; + } + else + sl = ndot = 0; + } + else if (ch == 0) + return 0; + else if (ch == '/') { + sl = 1; + ndot = 0; + } + } +} + static char *path_ok(char *dir) { - char *path = enter_repo(dir, strict_paths); + char *path; + + if (avoid_alias(dir)) { + logerror("'%s': aliased", dir); + return NULL; + } + + path = enter_repo(dir, strict_paths); if (!path) { logerror("'%s': unable to chdir or not a git archive", dir); @@ -96,9 +150,11 @@ static char *path_ok(char *dir) int pathlen = strlen(path); /* The validation is done on the paths after enter_repo - * canonicalization, so whitelist should be written in - * terms of real pathnames (i.e. after ~user is expanded - * and symlinks resolved). + * appends optional {.git,.git/.git} and friends, but + * it does not use getcwd(). So if your /pub is + * a symlink to /mnt/pub, you can whitelist /pub and + * do not have to say /mnt/pub. + * Do not say /pub/. */ for ( pp = ok_paths ; *pp ; pp++ ) { int len = strlen(*pp); diff --git a/path.c b/path.c index 2c077c0c8..334b2bd19 100644 --- a/path.c +++ b/path.c @@ -131,76 +131,121 @@ int validate_symref(const char *path) return -1; } -static char *current_dir(void) +static char *user_path(char *buf, char *path, int sz) { - return getcwd(pathname, sizeof(pathname)); -} - -static int user_chdir(char *path) -{ - char *dir = path; + struct passwd *pw; + char *slash; + int len, baselen; - if(*dir == '~') { /* user-relative path */ - struct passwd *pw; - char *slash = strchr(dir, '/'); - - dir++; - /* '~/' and '~' (no slash) means users own home-dir */ - if(!*dir || *dir == '/') - pw = getpwuid(getuid()); - else { - if (slash) { - *slash = '\0'; - pw = getpwnam(dir); - *slash = '/'; - } - else - pw = getpwnam(dir); + if (!path || path[0] != '~') + return NULL; + path++; + slash = strchr(path, '/'); + if (path[0] == '/' || !path[0]) { + pw = getpwuid(getuid()); + } + else { + if (slash) { + *slash = 0; + pw = getpwnam(path); + *slash = '/'; } - - /* make sure we got something back that we can chdir() to */ - if(!pw || chdir(pw->pw_dir) < 0) - return -1; - - if(!slash || !slash[1]) /* no path following username */ - return 0; - - dir = slash + 1; + else + pw = getpwnam(path); } - - /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */ - if(chdir(dir) < 0) - return -1; - - return 0; + if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir)) + return NULL; + baselen = strlen(pw->pw_dir); + memcpy(buf, pw->pw_dir, baselen); + while ((1 < baselen) && (buf[baselen-1] == '/')) { + buf[baselen-1] = 0; + baselen--; + } + if (slash && slash[1]) { + len = strlen(slash); + if (sz <= baselen + len) + return NULL; + memcpy(buf + baselen, slash, len + 1); + } + return buf; } +/* + * First, one directory to try is determined by the following algorithm. + * + * (0) If "strict" is given, the path is used as given and no DWIM is + * done. Otherwise: + * (1) "~/path" to mean path under the running user's home directory; + * (2) "~user/path" to mean path under named user's home directory; + * (3) "relative/path" to mean cwd relative directory; or + * (4) "/absolute/path" to mean absolute directory. + * + * Unless "strict" is given, we try access() for existence of "%s.git/.git", + * "%s/.git", "%s.git", "%s" in this order. The first one that exists is + * what we try. + * + * Second, we try chdir() to that. Upon failure, we return NULL. + * + * Then, we try if the current directory is a valid git repository. + * Upon failure, we return NULL. + * + * If all goes well, we return the directory we used to chdir() (but + * before ~user is expanded), avoiding getcwd() resolving symbolic + * links. User relative paths are also returned as they are given, + * except DWIM suffixing. + */ char *enter_repo(char *path, int strict) { - if(!path) + static char used_path[PATH_MAX]; + static char validated_path[PATH_MAX]; + + if (!path) return NULL; - if (strict) { - if (chdir(path) < 0) + if (!strict) { + static const char *suffix[] = { + ".git/.git", "/.git", ".git", "", NULL, + }; + int len = strlen(path); + int i; + while ((1 < len) && (path[len-1] == '/')) { + path[len-1] = 0; + len--; + } + if (PATH_MAX <= len) return NULL; - } - else { - if (!*path) - ; /* happy -- no chdir */ - else if (!user_chdir(path)) - ; /* happy -- as given */ - else if (!user_chdir(mkpath("%s.git", path))) - ; /* happy -- uemacs --> uemacs.git */ - else + if (path[0] == '~') { + if (!user_path(used_path, path, PATH_MAX)) + return NULL; + strcpy(validated_path, path); + path = used_path; + } + else if (PATH_MAX - 10 < len) + return NULL; + else { + path = strcpy(used_path, path); + strcpy(validated_path, path); + } + len = strlen(path); + for (i = 0; suffix[i]; i++) { + strcpy(path + len, suffix[i]); + if (!access(path, F_OK)) { + strcat(validated_path, suffix[i]); + break; + } + } + if (!suffix[i] || chdir(path)) return NULL; - (void)chdir(".git"); + path = validated_path; } + else if (chdir(path)) + return NULL; - if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && - validate_symref("HEAD") == 0) { + if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && + validate_symref("HEAD") == 0) { putenv("GIT_DIR=."); check_repository_format(); - return current_dir(); + return path; } return NULL; -- cgit v1.2.1