From 0eea345111a9b9fea4dd2841b80bc7d62964e812 Mon Sep 17 00:00:00 2001 From: Charles Bailey Date: Thu, 13 Nov 2008 12:41:13 +0000 Subject: [PATCH 001/231] Fix some tab/space inconsistencies in git-mergetool.sh git-mergetool.sh mostly uses 8 space tabs and 4 spaces per indent. This change corrects this in a part of the file affect by a later commit in this patch series. diff -w considers this change is to be a null change. Signed-off-by: Charles Bailey Signed-off-by: Junio C Hamano --- git-mergetool.sh | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/git-mergetool.sh b/git-mergetool.sh index 94187c306c..e2da5fc546 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -401,25 +401,25 @@ fi if test $# -eq 0 ; then - files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u` - if test -z "$files" ; then - echo "No files need merging" - exit 0 - fi - echo Merging the files: "$files" - git ls-files -u | - sed -e 's/^[^ ]* //' | - sort -u | - while IFS= read i - do - printf "\n" - merge_file "$i" < /dev/tty > /dev/tty - done + files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u` + if test -z "$files" ; then + echo "No files need merging" + exit 0 + fi + echo Merging the files: "$files" + git ls-files -u | + sed -e 's/^[^ ]* //' | + sort -u | + while IFS= read i + do + printf "\n" + merge_file "$i" < /dev/tty > /dev/tty + done else - while test $# -gt 0; do - printf "\n" - merge_file "$1" - shift - done + while test $# -gt 0; do + printf "\n" + merge_file "$1" + shift + done fi exit 0 From 682b451f84c046aa5b082e89e3db62950cb6261a Mon Sep 17 00:00:00 2001 From: Charles Bailey Date: Thu, 13 Nov 2008 12:41:14 +0000 Subject: [PATCH 002/231] Add -y/--no-prompt option to mergetool This option lets git mergetool invoke the conflict resolution program without waiting for a user prompt each time. Also added a mergetool.prompt (default true) configuration variable controlling the same behaviour Signed-off-by: Charles Bailey Signed-off-by: Junio C Hamano --- Documentation/config.txt | 3 +++ Documentation/git-mergetool.txt | 11 ++++++++++- git-mergetool.sh | 16 +++++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 32dcd643d2..bc5642d603 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -976,6 +976,9 @@ mergetool.keepBackup:: is set to `false` then this file is not preserved. Defaults to `true` (i.e. keep the backup files). +mergetool.prompt:: + Prompt before each invocation of the merge resolution program. + pack.window:: The size of the window used by linkgit:git-pack-objects[1] when no window size is given on the command line. Defaults to 10. diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index e0b2703b38..176483a1ae 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -7,7 +7,7 @@ git-mergetool - Run merge conflict resolution tools to resolve merge conflicts SYNOPSIS -------- -'git mergetool' [--tool=] []... +'git mergetool' [--tool=] [-y|--no-prompt|--prompt] []... DESCRIPTION ----------- @@ -60,6 +60,15 @@ variable `mergetool..trustExitCode` can be set to `true`. Otherwise, 'git-mergetool' will prompt the user to indicate the success of the resolution after the custom tool has exited. +-y or --no-prompt:: + Don't prompt before each invocation of the merge resolution + program. + +--prompt:: + Prompt before each invocation of the merge resolution program. + This is the default behaviour; the option is provided to + override any configuration settings. + Author ------ Written by Theodore Y Ts'o diff --git a/git-mergetool.sh b/git-mergetool.sh index e2da5fc546..507028f859 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -8,7 +8,7 @@ # at the discretion of Junio C Hamano. # -USAGE='[--tool=tool] [file to merge] ...' +USAGE='[--tool=tool] [-y|--no-prompt|--prompt] [file to merge] ...' SUBDIRECTORY_OK=Yes OPTIONS_SPEC= . git-sh-setup @@ -176,8 +176,10 @@ merge_file () { echo "Normal merge conflict for '$MERGED':" describe_file "$local_mode" "local" "$LOCAL" describe_file "$remote_mode" "remote" "$REMOTE" - printf "Hit return to start merge resolution tool (%s): " "$merge_tool" - read ans + if "$prompt" = true; then + printf "Hit return to start merge resolution tool (%s): " "$merge_tool" + read ans + fi case "$merge_tool" in kdiff3) @@ -280,6 +282,8 @@ merge_file () { cleanup_temp_files } +prompt=$(git config --bool mergetool.prompt || echo true) + while test $# != 0 do case "$1" in @@ -295,6 +299,12 @@ do shift ;; esac ;; + -y|--no-prompt) + prompt=false + ;; + --prompt) + prompt=true + ;; --) break ;; From 9e8eceab73e9f6a1e9349b1c14d4db0c035ac1ba Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 1 Dec 2008 09:30:31 -0800 Subject: [PATCH 003/231] Add 'merge' mode to 'git reset' We have always had a nice way to reset a working tree to another state while carrying our changes around: "git read-tree -u -m". Yes, it fails if the target tree is different in the paths that are dirty in the working tree, but this is how we used to switch branches in "git checkout", and it worked fine. However, perhaps exactly _because_ we've supported this from very early on, another low-level command, namely "git reset", never did. But as time went on, 'git reset' remains as a very common command, while 'git read-tree' is now a very odd and low-level plumbing thing that nobody sane should ever use, because it only makes sense together with other operations like either switching branches or just rewriting HEAD. Which means that we have effectively lost the ability to do something very common: jump to another point in time without always dropping all our dirty state. So add this kind of mode to "git reset", and since it merges your changes to what you are resetting to, just call it that: "git reset --merge". I've wanted this for a long time, since I very commonly carry a dirty tree while working on things. My main 'Makefile' file quite often has the next version already modified, and sometimes I have local modifications that I don't want to commit, but I still do pulls and patch applications, and occasionally want to do "git reset" to undo them - while still keeping my local modifications. (Maybe we could eventually change it to something like "if we have a working tree, default to --merge, otherwise default to --mixed"). NOTE! This new mode is certainly not perfect. There's a few things to look out for: - if the index has unmerged entries, "--merge" will currently simply refuse to reset ("you need to resolve your current index first"). You'll need to use "--hard" or similar in this case. This is sad, because normally a unmerged index means that the working tree file should have matched the source tree, so the correct action is likely to make --merge reset such a path to the target (like --hard), regardless of dirty state in-tree or in-index. But that's not how read-tree has ever worked, so.. - "git checkout -m" actually knows how to do a three-way merge, rather than refuse to update the working tree. So we do know how to do that, and arguably that would be even nicer behavior. At the same time it's also arguably true that there is a chance of loss of state (ie you cannot get back to the original tree if the three-way merge ends up resolving cleanly to no diff at all), so the "refuse to do it" is in some respects the safer - but less user-friendly - option. In other words, I think 'git reset --merge' could become a bit more friendly, but this is already a big improvement. It allows you to undo a recent commit without having to throw your current work away. Yes, yes, with a dirty tree you could always do git stash git reset --hard git stash apply instead, but isn't "git reset --merge" a nice way to handle one particular simple case? Signed-off-by: Linus Torvalds -- Hmm? Maybe I'm the only one that does a lot of work with a dirty tree, and sure, I can do other things like the "git stash" thing, or using "git checkout" to actually create a new branch, and then playing games with branch renaming etc to make it work like this one. But I suspect others dislike how "git reset" works too. But see the suggested improvements above. builtin-reset.c | 26 ++++++++++++++++++-------- 1 files changed, 18 insertions(+), 8 deletions(-) --- builtin-reset.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index 9514b77f8c..c0cb915c26 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -20,11 +20,14 @@ #include "parse-options.h" static const char * const git_reset_usage[] = { - "git reset [--mixed | --soft | --hard] [-q] []", + "git reset [--mixed | --soft | --hard | --merge] [-q] []", "git reset [--mixed] [--] ...", NULL }; +enum reset_type { MIXED, SOFT, HARD, MERGE, NONE }; +static const char *reset_type_names[] = { "mixed", "soft", "hard", "merge", NULL }; + static char *args_to_str(const char **argv) { char *buf = NULL; @@ -49,7 +52,7 @@ static inline int is_merge(void) return !access(git_path("MERGE_HEAD"), F_OK); } -static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int quiet) +static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet) { int i = 0; const char *args[6]; @@ -57,9 +60,17 @@ static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int qu args[i++] = "read-tree"; if (!quiet) args[i++] = "-v"; - args[i++] = "--reset"; - if (is_hard_reset) + switch (reset_type) { + case MERGE: args[i++] = "-u"; + args[i++] = "-m"; + break; + case HARD: + args[i++] = "-u"; + /* fallthrough */ + default: + args[i++] = "--reset"; + } args[i++] = sha1_to_hex(sha1); args[i] = NULL; @@ -169,9 +180,6 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size) warning("Reflog action message too long: %.*s...", 50, buf); } -enum reset_type { MIXED, SOFT, HARD, NONE }; -static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL }; - int cmd_reset(int argc, const char **argv, const char *prefix) { int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0; @@ -186,6 +194,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT), OPT_SET_INT(0, "hard", &reset_type, "reset HEAD, index and working tree", HARD), + OPT_SET_INT(0, "merge", &reset_type, + "reset HEAD, index and working tree", MERGE), OPT_BOOLEAN('q', NULL, &quiet, "disable showing new HEAD in hard reset and progress message"), OPT_END() @@ -266,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (is_merge() || read_cache() < 0 || unmerged_cache()) die("Cannot do a soft reset in the middle of a merge."); } - else if (reset_index_file(sha1, (reset_type == HARD), quiet)) + else if (reset_index_file(sha1, reset_type, quiet)) die("Could not reset index file to revision '%s'.", rev); /* Any resets update HEAD to the head being switched to, From d937c374ccb987356c1412986c8fb106b8aaef75 Mon Sep 17 00:00:00 2001 From: "David M. Syzdek" Date: Mon, 3 Nov 2008 09:14:28 -0900 Subject: [PATCH 004/231] autoconf: Enable threaded delta search when pthreads are supported Automatically set THREADED_DELTA_SEARCH when autoconf test detects support for pthreads on the platform. This will change the default for some platforms that did not enable threaded delta search previously. Signed-off-by: Junio C Hamano --- config.mak.in | 1 + configure.ac | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config.mak.in b/config.mak.in index 14dfb21fa5..55b25c3d26 100644 --- a/config.mak.in +++ b/config.mak.in @@ -52,4 +52,5 @@ NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@ FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@ SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@ NO_PTHREADS=@NO_PTHREADS@ +THREADED_DELTA_SEARCH=@THREADED_DELTA_SEARCH@ PTHREAD_LIBS=@PTHREAD_LIBS@ diff --git a/configure.ac b/configure.ac index 8821b5080a..cee06d7683 100644 --- a/configure.ac +++ b/configure.ac @@ -492,7 +492,8 @@ AC_SUBST(NO_MKDTEMP) # # Define NO_PTHREADS if we do not have pthreads # -# Define PTHREAD_LIBS to the linker flag used for Pthread support. +# Define PTHREAD_LIBS to the linker flag used for Pthread support and define +# THREADED_DELTA_SEARCH if Pthreads are available. AC_LANG_CONFTEST([AC_LANG_PROGRAM( [[#include ]], [[pthread_mutex_t test_mutex;]] @@ -500,16 +501,19 @@ AC_LANG_CONFTEST([AC_LANG_PROGRAM( ${CC} -pthread conftest.c -o conftest.o > /dev/null 2>&1 if test $? -eq 0;then PTHREAD_LIBS="-pthread" + THREADED_DELTA_SEARCH=YesPlease else ${CC} -lpthread conftest.c -o conftest.o > /dev/null 2>&1 if test $? -eq 0;then PTHREAD_LIBS="-lpthread" + THREADED_DELTA_SEARCH=YesPlease else NO_PTHREADS=UnfortunatelyYes fi fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(NO_PTHREADS) +AC_SUBST(THREADED_DELTA_SEARCH) ## Site configuration (override autodetection) ## --with-PACKAGE[=ARG] and --without-PACKAGE From 3f6aff68896f8490e101eb5c4ad8f0fa5b8b67bb Mon Sep 17 00:00:00 2001 From: William Pursell Date: Thu, 4 Dec 2008 10:00:24 +0000 Subject: [PATCH 005/231] Add subroutine to display one-line summary of hunks This commit implements a rather simple-minded mechanism to display a one-line summary of the hunks in an array ref. The display consists of the line numbers and the first changed line, truncated to 80 characters. 20 lines are displayed at a time, and the index of the first undisplayed line is returned, allowing the caller to display more if desired. (The 20 and 80 should be made configurable.) Signed-off-by: William Pursell Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index b0223c3419..eb11132b23 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -836,6 +836,47 @@ sub patch_update_cmd { } } +# Generate a one line summary of a hunk. +sub summarize_hunk { + my $rhunk = shift; + my $summary = $rhunk->{TEXT}[0]; + + # Keep the line numbers, discard extra context. + $summary =~ s/@@(.*?)@@.*/$1 /s; + $summary .= " " x (20 - length $summary); + + # Add some user context. + for my $line (@{$rhunk->{TEXT}}) { + if ($line =~ m/^[+-].*\w/) { + $summary .= $line; + last; + } + } + + chomp $summary; + return substr($summary, 0, 80) . "\n"; +} + + +# Print a one-line summary of each hunk in the array ref in +# the first argument, starting wih the index in the 2nd. +sub display_hunks { + my ($hunks, $i) = @_; + my $ctr = 0; + $i ||= 0; + for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) { + my $status = " "; + if (defined $hunks->[$i]{USE}) { + $status = $hunks->[$i]{USE} ? "+" : "-"; + } + printf "%s%2d: %s", + $status, + $i + 1, + summarize_hunk($hunks->[$i]); + } + return $i; +} + sub patch_update_file { my ($ix, $num); my $path = shift; From 070434d02b96ae0fac0a3364c202136f5eb5a7a9 Mon Sep 17 00:00:00 2001 From: William Pursell Date: Thu, 4 Dec 2008 10:22:40 +0000 Subject: [PATCH 006/231] Add 'g' command to go to a hunk When a minor change is made while the working directory is in a bit of a mess, it is somewhat difficult to wade through all of the hunks using git add --patch. This allows one to jump to the hunk that needs to be staged without having to respond 'n' to each preceding hunk. Signed-off-by: William Pursell Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index eb11132b23..ca60356d00 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -800,6 +800,7 @@ y - stage this hunk n - do not stage this hunk a - stage this and all the remaining hunks in the file d - do not stage this hunk nor any of the remaining hunks in the file +g - select a hunk to go to j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk @@ -945,6 +946,9 @@ sub patch_update_file { if ($ix < $num - 1) { $other .= '/J'; } + if ($num > 1) { + $other .= '/g'; + } for ($i = 0; $i < $num; $i++) { if (!defined $hunk[$i]{USE}) { $undecided = 1; @@ -978,6 +982,28 @@ sub patch_update_file { } next; } + elsif ($other =~ /g/ && $line =~ /^g(.*)/) { + my $response = $1; + my $no = $ix > 10 ? $ix - 10 : 0; + while ($response eq '') { + my $extra = ""; + $no = display_hunks(\@hunk, $no); + if ($no < $num) { + $extra = " ( to see more)"; + } + print "go to which hunk$extra? "; + $response = ; + chomp $response; + } + if ($response !~ /^\s*\d+\s*$/) { + print STDERR "Invalid number: '$response'\n"; + } elsif (0 < $response && $response <= $num) { + $ix = $response - 1; + } else { + print STDERR "Sorry, only $num hunks available.\n"; + } + next; + } elsif ($line =~ /^d/i) { while ($ix < $num) { if (!defined $hunk[$ix]{USE}) { From 1b5b465fbde9515e04bd1e8524a208c92af3555e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 3 Dec 2008 18:00:12 -0800 Subject: [PATCH 007/231] Document "git-reset --merge" The commit log message for the feature made it sound as if this is a saner version of --mixed, but the use case presented makes it clear that it is a better variant of --hard when your changes and somebody else's changes are mixed together. Perhaps we would want to rewrite the example that shows the use of --hard not to talk about recovering from a failed merge? Signed-off-by: Junio C Hamano --- Documentation/git-reset.txt | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 52aab5e680..c542b0caf7 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,7 +8,7 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git reset' [--mixed | --soft | --hard] [-q] [] +'git reset' [--mixed | --soft | --hard | --merge] [-q] [] 'git reset' [-q] [] [--] ... DESCRIPTION @@ -45,6 +45,11 @@ OPTIONS switched to. Any changes to tracked files in the working tree since are lost. +--merge:: + Resets the index to match the tree recorded by the named commit, + and updates the files that are different between the named commit + and the current commit in the working tree. + -q:: Be quiet, only report errors. @@ -152,6 +157,28 @@ tip of the current branch in ORIG_HEAD, so resetting hard to it brings your index file and the working tree back to that state, and resets the tip of the branch to that commit. +Undo a merge or pull inside a dirty work tree:: ++ +------------ +$ git pull <1> +Auto-merging nitfol +Merge made by recursive. + nitfol | 20 +++++---- + ... +$ git reset --merge ORIG_HEAD <2> +------------ ++ +<1> Even if you may have local modifications in your +working tree, you can safely say "git pull" when you know +that the change in the other branch does not overlap with +them. +<2> After inspecting the result of the merge, you may find +that the change in the other branch is unsatisfactory. Running +"git reset --hard ORIG_HEAD" will let you go back to where you +were, but it will discard your local changes, which you do not +want. "git reset --merge" keeps your local changes. + + Interrupted workflow:: + Suppose you are interrupted by an urgent fix request while you From 4a24bfc22081a6c2771b62d464222f81c470192c Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 9 Dec 2008 23:46:16 +0100 Subject: [PATCH 008/231] gitweb: Move 'lineno' id from link to row element in git_blame Move l ID from link element inside table row (inside cell element for column with line numbers), to encompassing table row element. It was done to make it easier to manipulate result HTML with DOM, and to be able write 'blame_incremental' view with the same, or nearly the same result. Signed-off-by: Jakub Narebski Acked-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 951739210a..e01e1afe95 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4645,7 +4645,7 @@ HTML if ($group_size) { $current_color = ++$current_color % $num_colors; } - print "\n"; + print "\n"; if ($group_size) { print " $parent_commit); print ""; print $cgi->a({ -href => "$blamed#l$orig_lineno", - -id => "l$lineno", -class => "linenr" }, esc_html($lineno)); print ""; From d2ce10d7b7d67ff8b50ae749ce4c5b1a2b8d133c Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 9 Dec 2008 23:48:51 +0100 Subject: [PATCH 009/231] gitweb: A bit of code cleanup in git_blame() Among others, here are the highlights: * move variable declaration closer to the place it is set and used, if possible, * uniquify and simplify coding style a bit, which includes removing unnecessary '()'. * check type only if $hash was defined, as otherwise from the way git_get_hash_by_path() is called (and works), we know that it is a blob, * use modern calling convention for git-blame, * remove unused variable, * don't use implicit variables ($_), * add some comments Signed-off-by: Jakub Narebski Acked-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 67 +++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e01e1afe95..ccbf5d4745 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4575,28 +4575,33 @@ sub git_tag { } sub git_blame { - my $fd; - my $ftype; - + # permissions gitweb_check_feature('blame') - or die_error(403, "Blame view not allowed"); + or die_error(403, "Blame view not allowed"); + # error checking die_error(400, "No file name given") unless $file_name; $hash_base ||= git_get_head_hash($project); - die_error(404, "Couldn't find base commit") unless ($hash_base); + die_error(404, "Couldn't find base commit") unless $hash_base; my %co = parse_commit($hash_base) or die_error(404, "Commit not found"); + my $ftype = "blob"; if (!defined $hash) { $hash = git_get_hash_by_path($hash_base, $file_name, "blob") or die_error(404, "Error looking up file"); + } else { + $ftype = git_get_type($hash); + if ($ftype !~ "blob") { + die_error(400, "Object is not a blob"); + } } - $ftype = git_get_type($hash); - if ($ftype !~ "blob") { - die_error(400, "Object is not a blob"); - } - open ($fd, "-|", git_cmd(), "blame", '-p', '--', - $file_name, $hash_base) + + # run git-blame --porcelain + open my $fd, "-|", git_cmd(), "blame", '-p', + $hash_base, '--', $file_name or die_error(500, "Open git-blame failed"); + + # page header git_header_html(); my $formats_nav = $cgi->a({-href => href(action=>"blob", -replay=>1)}, @@ -4610,40 +4615,44 @@ sub git_blame { git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); git_print_page_path($file_name, $ftype, $hash_base); - my @rev_color = (qw(light2 dark2)); + + # page body + my @rev_color = qw(light2 dark2); my $num_colors = scalar(@rev_color); my $current_color = 0; - my $last_rev; + my %metainfo = (); + print < HTML - my %metainfo = (); - while (1) { - $_ = <$fd>; - last unless defined $_; + LINE: + while (my $line = <$fd>) { + chomp $line; + # the header: [] + # no for subsequent lines in group of lines my ($full_rev, $orig_lineno, $lineno, $group_size) = - /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/; + ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/); if (!exists $metainfo{$full_rev}) { $metainfo{$full_rev} = {}; } my $meta = $metainfo{$full_rev}; - while (<$fd>) { - last if (s/^\t//); - if (/^(\S+) (.*)$/) { + my $data; + while ($data = <$fd>) { + chomp $data; + last if ($data =~ s/^\t//); # contents of line + if ($data =~ /^(\S+) (.*)$/) { $meta->{$1} = $2; } } - my $data = $_; - chomp $data; - my $rev = substr($full_rev, 0, 8); + my $short_rev = substr($full_rev, 0, 8); my $author = $meta->{'author'}; - my %date = parse_date($meta->{'author-time'}, - $meta->{'author-tz'}); + my %date = + parse_date($meta->{'author-time'}, $meta->{'author-tz'}); my $date = $date{'iso-tz'}; if ($group_size) { - $current_color = ++$current_color % $num_colors; + $current_color = ($current_color + 1) % $num_colors; } print "\n"; if ($group_size) { @@ -4654,7 +4663,7 @@ HTML print $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)}, - esc_html($rev)); + esc_html($short_rev)); print "\n"; } open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^") @@ -4677,6 +4686,8 @@ HTML print ""; close $fd or print "Reading blob failed\n"; + + # page footer git_footer_html(); } From a325a1a70b90a0cef39a258f1471e5770aa66eee Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 10 Dec 2008 23:44:50 +0100 Subject: [PATCH 010/231] Add support for a pdf version of the user manual Use dblatex in order to create a pdf version of the git user manual. No existing Makefile targets (including "all") are touched, so you need to explicitly say make pdf sudo make install-pdf to get user-manual.pdf created and installed. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- Documentation/Makefile | 13 +++++++++++++ INSTALL | 3 +++ Makefile | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/Documentation/Makefile b/Documentation/Makefile index c34c1cae20..69f4ade06f 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -32,6 +32,7 @@ DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT)) prefix?=$(HOME) bindir?=$(prefix)/bin htmldir?=$(prefix)/share/doc/git-doc +pdfdir?=$(prefix)/share/doc/git-doc mandir?=$(prefix)/share/man man1dir=$(mandir)/man1 man5dir=$(mandir)/man5 @@ -50,6 +51,7 @@ infodir?=$(prefix)/share/info MAKEINFO=makeinfo INSTALL_INFO=install-info DOCBOOK2X_TEXI=docbook2x-texi +DBLATEX=dblatex ifndef PERL_PATH PERL_PATH = /usr/bin/perl endif @@ -87,6 +89,8 @@ man7: $(DOC_MAN7) info: git.info gitman.info +pdf: user-manual.pdf + install: install-man install-man: man @@ -107,6 +111,10 @@ install-info: info echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \ fi +install-pdf: pdf + $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir) + $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir) + install-html: html sh ./install-webdoc.sh $(DESTDIR)$(htmldir) @@ -190,6 +198,11 @@ user-manual.texi: user-manual.xml $(DOCBOOK2X_TEXI) user-manual.xml --to-stdout | $(PERL_PATH) fix-texi.perl >$@+ mv $@+ $@ +user-manual.pdf: user-manual.xml + $(RM) $@+ $@ + $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< + mv $@+ $@ + gitman.texi: $(MAN_XML) cat-texi.perl $(RM) $@+ $@ ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --to-stdout $(xml);)) | \ diff --git a/INSTALL b/INSTALL index d1deb0b3c7..ae7f7508f8 100644 --- a/INSTALL +++ b/INSTALL @@ -101,6 +101,9 @@ Issues of note: Building and installing the info file additionally requires makeinfo and docbook2X. Version 0.8.3 is known to work. + Building and installing the pdf file additionally requires + dblatex. Version 0.2.7 with asciidoc >= 8.2.7 is known to work. + The documentation is written for AsciiDoc 7, but "make ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8. diff --git a/Makefile b/Makefile index 51581973ab..b1456a2e5b 100644 --- a/Makefile +++ b/Makefile @@ -1306,6 +1306,9 @@ html: info: $(MAKE) -C Documentation info +pdf: + $(MAKE) -C Documentation pdf + TAGS: $(RM) TAGS $(FIND) . -name '*.[hcS]' -print | xargs etags -a @@ -1448,6 +1451,9 @@ install-html: install-info: $(MAKE) -C Documentation install-info +install-pdf: + $(MAKE) -C Documentation install-pdf + quick-install-doc: $(MAKE) -C Documentation quick-install From 39c19ce2755830dd1dfdabf36e2b0166df3546f8 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 11 Dec 2008 01:33:29 +0100 Subject: [PATCH 011/231] gitweb: cache $parent_commit info in git_blame() Luben Tuikov changed 'lineno' link from leading to commit which gave current version of given block of lines, to leading to parent of this commit in 244a70e (Blame "linenr" link jumps to previous state at "orig_lineno"). This made possible data mining using 'blame' view. The current implementation calls rev-parse once per each blamed line to find parent revision of blamed commit, even when the same commit appears more than once, which is inefficient. This patch mitigates this issue by caching $parent_commit info in %metainfo, which makes gitweb call rev-parse only once per each unique commit in the output from "git blame". In the tables below you can see simple benchmark comparing gitweb performance before and after this patch File | L[1] | C[2] || Time0[3] | Before[4] | After[4] ==================================================================== blob.h | 18 | 4 || 0m1.727s | 0m2.545s | 0m2.474s GIT-VERSION-GEN | 42 | 13 || 0m2.165s | 0m2.448s | 0m2.071s README | 46 | 6 || 0m1.593s | 0m2.727s | 0m2.242s revision.c | 1923 | 121 || 0m2.357s | 0m30.365s | 0m7.028s gitweb/gitweb.perl | 6291 | 428 || 0m8.080s | 1m37.244s | 0m20.627s File | L/C | Before/After ========================================= blob.h | 4.5 | 1.03 GIT-VERSION-GEN | 3.2 | 1.18 README | 7.7 | 1.22 revision.c | 15.9 | 4.32 gitweb/gitweb.perl | 14.7 | 4.71 As you can see the greater ratio of lines in file to unique commits in blame output, the greater gain from the new implementation. Legend: [1] Number of lines: $ wc -l [2] Number of unique commits in the blame output: $ git blame -p | grep author-time | wc -l [3] Time for running "git blame -p" (user time, single run): $ time git blame -p >/dev/null [4] Time to run gitweb as Perl script from command line: $ gitweb-run.sh "p=.git;a=blame;f=" > /dev/null 2>&1 The gitweb-run.sh script includes slightly modified (with adjusted pathnames) code from gitweb_run() function from the test script t/t9500-gitweb-standalone-no-errors.sh; gitweb config file gitweb_config.perl contents (again up to adjusting pathnames; in particular $projectroot variable should point to top directory of git repository) can be found in the same place. Discussion ~~~~~~~~~~ A possible future improvement would be to open a bidi pipe to "git cat-file --batch-check", (like in Git::Repo in gitweb caching by Lea Wiemann), feed $long_rev^ to it, and parse its output, which is in the following form: 926b07e694599d86cec668475071b32147c95034 commit 637 This would mean one call to git-cat-file for the whole 'blame' view, instead of one call to git-rev-parse per each unique commit in blame output. Yet another solution would be to change use of validate_refname() to validate_revision() when checking script parameters (CGI query or path_info), with validate_revision being something like the following: sub validate_revision { my $rev = shift; return validate_refname(strip_rev_suffixes($rev)); } so we don't need to calculate $long_rev^, but can pass "$long_rev^" as 'hb' parameter. This solution has the advantage that it can be easily adapted to future incremental blame output. Signed-off-by: Jakub Narebski Acked-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ccbf5d4745..f992de223d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4666,11 +4666,17 @@ HTML esc_html($short_rev)); print "\n"; } - open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^") - or die_error(500, "Open git-rev-parse failed"); - my $parent_commit = <$dd>; - close $dd; - chomp($parent_commit); + my $parent_commit; + if (!exists $meta->{'parent'}) { + open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^") + or die_error(500, "Open git-rev-parse failed"); + $parent_commit = <$dd>; + close $dd; + chomp($parent_commit); + $meta->{'parent'} = $parent_commit; + } else { + $parent_commit = $meta->{'parent'}; + } my $blamed = href(action => 'blame', file_name => $meta->{'filename'}, hash_base => $parent_commit); From 43cc2b4266cc75f1a5a10e1a805d05e419000573 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 11 Dec 2008 15:36:47 -0500 Subject: [PATCH 012/231] autodetect number of CPUs by default when using threads ... and display the actual number of threads used when locally repacking. A remote server still won't tell you how many threads it uses during a fetch though. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index cedef52fd3..619e597d5c 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -78,7 +78,7 @@ static int progress = 1; static int window = 10; static uint32_t pack_size_limit, pack_size_limit_cfg; static int depth = 50; -static int delta_search_threads = 1; +static int delta_search_threads; static int pack_to_stdout; static int num_preferred_base; static struct progress *progress_state; @@ -1612,6 +1612,9 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, find_deltas(list, &list_size, window, depth, processed); return; } + if (progress > pack_to_stdout) + fprintf(stderr, "Delta compression using %d threads.\n", + delta_search_threads); /* Partition the work amongst work threads. */ for (i = 0; i < delta_search_threads; i++) { From b0169d84df4c09ed7dfcc6aea5aecd177ddda75b Mon Sep 17 00:00:00 2001 From: Charles Bailey Date: Fri, 12 Dec 2008 21:48:40 +0000 Subject: [PATCH 013/231] mergetool: Add prompt to continue after failing to merge a file This option stops git mergetool from aborting at the first failed merge. After a failed merge the user will be prompted to indicated whether he wishes to continue with attempting to merge subsequent paths or to abort. This allows some additional use patterns. Merge conflicts can now be previewed one at time and merges can also be skipped so that they can be performed in a later pass. Signed-off-by: Charles Bailey Signed-off-by: Junio C Hamano --- git-mergetool.sh | 52 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/git-mergetool.sh b/git-mergetool.sh index 507028f859..5144971647 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -70,16 +70,16 @@ resolve_symlink_merge () { git checkout-index -f --stage=2 -- "$MERGED" git add -- "$MERGED" cleanup_temp_files --save-backup - return + return 0 ;; [rR]*) git checkout-index -f --stage=3 -- "$MERGED" git add -- "$MERGED" cleanup_temp_files --save-backup - return + return 0 ;; [aA]*) - exit 1 + return 1 ;; esac done @@ -97,15 +97,15 @@ resolve_deleted_merge () { [mMcC]*) git add -- "$MERGED" cleanup_temp_files --save-backup - return + return 0 ;; [dD]*) git rm -- "$MERGED" > /dev/null cleanup_temp_files - return + return 0 ;; [aA]*) - exit 1 + return 1 ;; esac done @@ -137,7 +137,7 @@ merge_file () { else echo "$MERGED: file does not need merging" fi - exit 1 + return 1 fi ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" @@ -269,7 +269,7 @@ merge_file () { if test "$status" -ne 0; then echo "merge of $MERGED failed" 1>&2 mv -- "$BACKUP" "$MERGED" - exit 1 + return 1 fi if test "$merge_keep_backup" = "true"; then @@ -280,6 +280,7 @@ merge_file () { git add -- "$MERGED" cleanup_temp_files + return 0 } prompt=$(git config --bool mergetool.prompt || echo true) @@ -350,6 +351,22 @@ init_merge_tool_path() { fi } +prompt_after_failed_merge() { + while true; do + printf "Continue merging other unresolved paths (y/n) ? " + read ans + case "$ans" in + + [yY]*) + return 0 + ;; + + [nN]*) + return 1 + ;; + esac + done +} if test -z "$merge_tool"; then merge_tool=`git config merge.tool` @@ -409,6 +426,8 @@ else fi fi +last_status=0 +rollup_status=0 if test $# -eq 0 ; then files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u` @@ -422,14 +441,29 @@ if test $# -eq 0 ; then sort -u | while IFS= read i do + if test $last_status -ne 0; then + prompt_after_failed_merge < /dev/tty || exit 1 + fi printf "\n" merge_file "$i" < /dev/tty > /dev/tty + last_status=$? + if test $last_status -ne 0; then + rollup_status=1 + fi done else while test $# -gt 0; do + if test $last_status -ne 0; then + prompt_after_failed_merge || exit 1 + fi printf "\n" merge_file "$1" + last_status=$? + if test $last_status -ne 0; then + rollup_status=1 + fi shift done fi -exit 0 + +exit $rollup_status From 162eba8b43a0e665c8e3439191981412815cc6db Mon Sep 17 00:00:00 2001 From: Charles Bailey Date: Fri, 12 Dec 2008 21:48:41 +0000 Subject: [PATCH 014/231] mergetool: Don't keep temporary merge files unless told to This changes git mergetool to remove the temporary files used to invoke the merge tool even if it returns non-zero. This also adds a configuration option (mergetool.keepTemporaries) to retain the previous behaviour if desired. Signed-off-by: Charles Bailey Signed-off-by: Junio C Hamano --- Documentation/config.txt | 7 +++++++ git-mergetool.sh | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index bc5642d603..3d5a8df0cf 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -976,6 +976,13 @@ mergetool.keepBackup:: is set to `false` then this file is not preserved. Defaults to `true` (i.e. keep the backup files). +mergetool.keepTemporaries:: + When invoking a custom merge tool, git uses a set of temporary + files to pass to the tool. If the tool returns an error and this + variable is set to `true`, then these temporary files will be + preserved, otherwise they will be removed after the tool has + exited. Defaults to `false`. + mergetool.prompt:: Prompt before each invocation of the merge resolution program. diff --git a/git-mergetool.sh b/git-mergetool.sh index 5144971647..f04240daca 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -269,6 +269,11 @@ merge_file () { if test "$status" -ne 0; then echo "merge of $MERGED failed" 1>&2 mv -- "$BACKUP" "$MERGED" + + if test "$merge_keep_temporaries" = "false"; then + cleanup_temp_files + fi + return 1 fi @@ -415,6 +420,7 @@ else init_merge_tool_path "$merge_tool" merge_keep_backup="$(git config --bool merge.keepBackup || echo true)" + merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then echo "The merge tool $merge_tool is not available as '$merge_tool_path'" From bf874896240cb00b22cd4f6cbcc143b17cc7fe0e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 13 Dec 2008 15:06:40 -0500 Subject: [PATCH 015/231] pack-objects: don't use too many threads with few objects If there are few objects to deltify, they might be split amongst threads so that there is simply no other objects left to delta against within the same thread. Let's use the same 2*window treshold as used for the final load balancing to allow extra threads to be created. This fixes the benign t5300 test failure. Signed-off-by: Nicolas Pitre Tested-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 619e597d5c..e8515348be 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1620,6 +1620,10 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, for (i = 0; i < delta_search_threads; i++) { unsigned sub_size = list_size / (delta_search_threads - i); + /* don't use too small segments or no deltas will be found */ + if (sub_size < 2*window && i+1 < delta_search_threads) + sub_size = 0; + p[i].window = window; p[i].depth = depth; p[i].processed = processed; From 7bb1fcc6fcc5a2d0164f243fd10f346eb0822ddf Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Wed, 10 Dec 2008 21:12:59 +0100 Subject: [PATCH 016/231] modify/delete conflict resolution overwrites untracked file If a file was removed in HEAD, but modified in MERGE_HEAD, recursive merge will result in a "CONFLICT (delete/modify)". If the (now untracked) file already exists and was not added to the index, it is overwritten with the conflict resolution contents. In similar situations (cf. test 2), the merge would abort with "error: Untracked working tree 'file' would be overwritten by merge." The same should happen in this case. Signed-off-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- t/t7607-merge-overwrite.sh | 87 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 t/t7607-merge-overwrite.sh diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh new file mode 100755 index 0000000000..513097c1a1 --- /dev/null +++ b/t/t7607-merge-overwrite.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +test_description='git-merge + +Do not overwrite changes.' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo c0 > c0.c && + git add c0.c && + git commit -m c0 && + git tag c0 && + echo c1 > c1.c && + git add c1.c && + git commit -m c1 && + git tag c1 && + git reset --hard c0 && + echo c2 > c2.c && + git add c2.c && + git commit -m c2 && + git tag c2 && + git reset --hard c1 && + echo "c1 a" > c1.c && + git add c1.c && + git commit -m "c1 a" && + git tag c1a && + echo "VERY IMPORTANT CHANGES" > important +' + +test_expect_success 'will not overwrite untracked file' ' + git reset --hard c1 && + cat important > c2.c && + ! git merge c2 && + test_cmp important c2.c +' + +test_expect_success 'will not overwrite new file' ' + git reset --hard c1 && + cat important > c2.c && + git add c2.c && + ! git merge c2 && + test_cmp important c2.c +' + +test_expect_success 'will not overwrite staged changes' ' + git reset --hard c1 && + cat important > c2.c && + git add c2.c && + rm c2.c && + ! git merge c2 && + git checkout c2.c && + test_cmp important c2.c +' + +test_expect_failure 'will not overwrite removed file' ' + git reset --hard c1 && + git rm c1.c && + git commit -m "rm c1.c" && + cat important > c1.c && + ! git merge c1a && + test_cmp important c1.c +' + +test_expect_success 'will not overwrite re-added file' ' + git reset --hard c1 && + git rm c1.c && + git commit -m "rm c1.c" && + cat important > c1.c && + git add c1.c && + ! git merge c1a && + test_cmp important c1.c +' + +test_expect_success 'will not overwrite removed file with staged changes' ' + git reset --hard c1 && + git rm c1.c && + git commit -m "rm c1.c" && + cat important > c1.c && + git add c1.c && + rm c1.c && + ! git merge c1a && + git checkout c1.c && + test_cmp important c1.c +' + +test_done From c5ab03f26c992e30f355fba129f70db0f290fcd7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 14 Dec 2008 19:40:09 -0800 Subject: [PATCH 017/231] merge-recursive: do not clobber untracked working tree garbage When merge-recursive wanted to create a new file in the work tree (either as the final result, or a hint for reference purposes while delete/modify conflicts), it unconditionally overwrote an untracked file in the working tree. Be careful not to lose whatever the user has that is not tracked. Signed-off-by: Junio C Hamano --- builtin-merge-recursive.c | 32 ++++++++++++++++++++++++++++++++ t/t7607-merge-overwrite.sh | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index b9738655ad..ab9f6e0f14 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -471,6 +471,30 @@ static void flush_buffer(int fd, const char *buf, unsigned long size) } } +static int would_lose_untracked(const char *path) +{ + int pos = cache_name_pos(path, strlen(path)); + + if (pos < 0) + pos = -1 - pos; + while (pos < active_nr && + !strcmp(path, active_cache[pos]->name)) { + /* + * If stage #0, it is definitely tracked. + * If it has stage #2 then it was tracked + * before this merge started. All other + * cases the path was not tracked. + */ + switch (ce_stage(active_cache[pos])) { + case 0: + case 2: + return 0; + } + pos++; + } + return file_exists(path); +} + static int make_room_for_path(const char *path) { int status; @@ -486,6 +510,14 @@ static int make_room_for_path(const char *path) die(msg, path, ""); } + /* + * Do not unlink a file in the work tree if we are not + * tracking it. + */ + if (would_lose_untracked(path)) + return error("refusing to lose untracked file at '%s'", + path); + /* Successful unlink is good.. */ if (!unlink(path)) return 0; diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh index 513097c1a1..49f4e1599a 100755 --- a/t/t7607-merge-overwrite.sh +++ b/t/t7607-merge-overwrite.sh @@ -53,7 +53,7 @@ test_expect_success 'will not overwrite staged changes' ' test_cmp important c2.c ' -test_expect_failure 'will not overwrite removed file' ' +test_expect_success 'will not overwrite removed file' ' git reset --hard c1 && git rm c1.c && git commit -m "rm c1.c" && From 1415be8f0f86c6d2aa2cb014c3cc32dd92e9c43a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 15 Dec 2008 20:44:30 +0100 Subject: [PATCH 018/231] Force t5302 to use a single thread If the packs are made using multiple threads, they are no longer identical on the 4-core Xeon I tested on. Signed-off-by: Johannes Schindelin Acked-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- t/t5302-pack-index.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 884e24253a..e6f70d474f 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -10,6 +10,7 @@ test_expect_success \ 'setup' \ 'rm -rf .git git init && + git config pack.threads 1 && i=1 && while test $i -le 100 do From cdad8170b223a73dc37197d5ba78a3ac7b824406 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Mon, 15 Dec 2008 22:16:19 -0800 Subject: [PATCH 019/231] gitweb: unify boolean feature subroutines The boolean feature subroutines behaved identically except for the name of the configuration option, so make that a parameter and unify them. Signed-off-by: Matt Kraai Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 41 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6eb370d8de..827e5c5137 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -203,7 +203,7 @@ our %feature = ( # $feature{'blame'}{'override'} = 1; # and in project config gitweb.blame = 0|1; 'blame' => { - 'sub' => \&feature_blame, + 'sub' => sub { feature_bool('blame', @_) }, 'override' => 0, 'default' => [0]}, @@ -241,7 +241,7 @@ our %feature = ( # $feature{'grep'}{'override'} = 1; # and in project config gitweb.grep = 0|1; 'grep' => { - 'sub' => \&feature_grep, + 'sub' => sub { feature_bool('grep', @_) }, 'override' => 0, 'default' => [1]}, @@ -255,7 +255,7 @@ our %feature = ( # $feature{'pickaxe'}{'override'} = 1; # and in project config gitweb.pickaxe = 0|1; 'pickaxe' => { - 'sub' => \&feature_pickaxe, + 'sub' => sub { feature_bool('pickaxe', @_) }, 'override' => 0, 'default' => [1]}, @@ -363,16 +363,17 @@ sub gitweb_check_feature { } -sub feature_blame { - my ($val) = git_get_project_config('blame', '--bool'); +sub feature_bool { + my $key = shift; + my ($val) = git_get_project_config($key, '--bool'); if ($val eq 'true') { - return 1; + return (1); } elsif ($val eq 'false') { - return 0; + return (0); } - return $_[0]; + return ($_[0]); } sub feature_snapshot { @@ -387,30 +388,6 @@ sub feature_snapshot { return @fmts; } -sub feature_grep { - my ($val) = git_get_project_config('grep', '--bool'); - - if ($val eq 'true') { - return (1); - } elsif ($val eq 'false') { - return (0); - } - - return ($_[0]); -} - -sub feature_pickaxe { - my ($val) = git_get_project_config('pickaxe', '--bool'); - - if ($val eq 'true') { - return (1); - } elsif ($val eq 'false') { - return (0); - } - - return ($_[0]); -} - # checking HEAD file with -e is fragile if the repository was # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed # and then pruned. From 9872cd6f6c40803db9e784b6287db72e0cb6b5c3 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 18 Dec 2008 08:13:16 +0100 Subject: [PATCH 020/231] gitweb: add patch view The output of commitdiff_plain is not intended for git-am: * when given a range of commits, commitdiff_plain publishes a single patch with the message from the first commit, instead of a patchset * the hand-built email format replicates the commit summary both as email subject and as first line of the email itself, resulting in a duplication if the output is used with git-am. We thus create a new view that can be fed to git-am directly, allowing patch exchange via gitweb. The new view exposes the output of git format-patch directly, limiting it to a single patch in the case of a single commit. A configurable upper limit defaulting to 16 is imposed on the number of commits which will be included in a patchset, to prevent DoS attacks on the server. Setting the limit to 0 will disable the patch view, setting it to a negative number will remove the limit. Signed-off-by: Giuseppe Bilotta Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 69 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 95988fba4a..9a11be3d94 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -329,6 +329,21 @@ our %feature = ( 'ctags' => { 'override' => 0, 'default' => [0]}, + + # The maximum number of patches in a patchset generated in patch + # view. Set this to 0 or undef to disable patch view, or to a + # negative number to remove any limit. + + # To disable system wide have in $GITWEB_CONFIG + # $feature{'patches'}{'default'} = [0]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'patches'}{'override'} = 1; + # and in project config gitweb.patches = 0|n; + # where n is the maximum number of patches allowed in a patchset. + 'patches' => { + 'sub' => \&feature_patches, + 'override' => 0, + 'default' => [16]}, ); sub gitweb_get_feature { @@ -410,6 +425,16 @@ sub feature_pickaxe { return ($_[0]); } +sub feature_patches { + my @val = (git_get_project_config('patches', '--int')); + + if (@val) { + return @val; + } + + return ($_[0]); +} + # checking HEAD file with -e is fragile if the repository was # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed # and then pruned. @@ -503,6 +528,7 @@ our %actions = ( "heads" => \&git_heads, "history" => \&git_history, "log" => \&git_log, + "patch" => \&git_patch, "rss" => \&git_rss, "atom" => \&git_atom, "search" => \&git_search, @@ -5386,6 +5412,13 @@ sub git_blobdiff_plain { sub git_commitdiff { my $format = shift || 'html'; + + my $patch_max; + if ($format eq 'patch') { + ($patch_max) = gitweb_get_feature('patches'); + die_error(403, "Patch view not allowed") unless $patch_max; + } + $hash ||= $hash_base || "HEAD"; my %co = parse_commit($hash) or die_error(404, "Unknown commit object"); @@ -5483,7 +5516,23 @@ sub git_commitdiff { open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, '-p', $hash_parent_param, $hash, "--" or die_error(500, "Open git-diff-tree failed"); - + } elsif ($format eq 'patch') { + # For commit ranges, we limit the output to the number of + # patches specified in the 'patches' feature. + # For single commits, we limit the output to a single patch, + # diverging from the git-format-patch default. + my @commit_spec = (); + if ($hash_parent) { + if ($patch_max > 0) { + push @commit_spec, "-$patch_max"; + } + push @commit_spec, '-n', "$hash_parent..$hash"; + } else { + push @commit_spec, '-1', '--root', $hash; + } + open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8', + '--stdout', @commit_spec + or die_error(500, "Open git-format-patch failed"); } else { die_error(400, "Unknown commitdiff format"); } @@ -5532,6 +5581,14 @@ sub git_commitdiff { print to_utf8($line) . "\n"; } print "---\n\n"; + } elsif ($format eq 'patch') { + my $filename = basename($project) . "-$hash.patch"; + + print $cgi->header( + -type => 'text/plain', + -charset => 'utf-8', + -expires => $expires, + -content_disposition => 'inline; filename="' . "$filename" . '"'); } # write patch @@ -5553,6 +5610,11 @@ sub git_commitdiff { print <$fd>; close $fd or print "Reading git-diff-tree failed\n"; + } elsif ($format eq 'patch') { + local $/ = undef; + print <$fd>; + close $fd + or print "Reading git-format-patch failed\n"; } } @@ -5560,6 +5622,11 @@ sub git_commitdiff_plain { git_commitdiff('plain'); } +# format-patch-style patches +sub git_patch { + git_commitdiff('patch'); +} + sub git_history { if (!defined $hash_base) { $hash_base = git_get_head_hash($project); From 2020985464ba0135f717cd14309ac63a8dfda341 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 18 Dec 2008 08:13:17 +0100 Subject: [PATCH 021/231] gitweb: change call pattern for git_commitdiff Since we are going to introduce an additional parameter for git_commitdiff to tune patch view, we switch to named/hash-based parameter passing for clarity and robustness. Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 9a11be3d94..63e93a2736 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5411,7 +5411,8 @@ sub git_blobdiff_plain { } sub git_commitdiff { - my $format = shift || 'html'; + my %params = @_; + my $format = $params{-format} || 'html'; my $patch_max; if ($format eq 'patch') { @@ -5619,12 +5620,12 @@ sub git_commitdiff { } sub git_commitdiff_plain { - git_commitdiff('plain'); + git_commitdiff(-format => 'plain'); } # format-patch-style patches sub git_patch { - git_commitdiff('patch'); + git_commitdiff(-format => 'patch'); } sub git_history { From a3411f8a2d3acc311991cf2221efa9de81cd03f7 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 18 Dec 2008 08:13:18 +0100 Subject: [PATCH 022/231] gitweb: add patches view The only difference between patch and patches view is in the treatement of single commits: the former only displays a single patch, whereas the latter displays a patchset leading to the specified commit. Signed-off-by: Giuseppe Bilotta Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 63e93a2736..4b28136ba7 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -529,6 +529,7 @@ our %actions = ( "history" => \&git_history, "log" => \&git_log, "patch" => \&git_patch, + "patches" => \&git_patches, "rss" => \&git_rss, "atom" => \&git_atom, "search" => \&git_search, @@ -5529,7 +5530,15 @@ sub git_commitdiff { } push @commit_spec, '-n', "$hash_parent..$hash"; } else { - push @commit_spec, '-1', '--root', $hash; + if ($params{-single}) { + push @commit_spec, '-1'; + } else { + if ($patch_max > 0) { + push @commit_spec, "-$patch_max"; + } + push @commit_spec, "-n"; + } + push @commit_spec, '--root', $hash; } open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8', '--stdout', @commit_spec @@ -5625,6 +5634,10 @@ sub git_commitdiff_plain { # format-patch-style patches sub git_patch { + git_commitdiff(-format => 'patch', -single=> 1); +} + +sub git_patches { git_commitdiff(-format => 'patch'); } From 75bf2cb2983865760464ecec3fa1cd6f9d3719d5 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Thu, 18 Dec 2008 08:13:19 +0100 Subject: [PATCH 023/231] gitweb: link to patch(es) view in commit(diff) and (short)log view We link to patch view in commit and commitdiff view, and to patches view in log and shortlog view. In (short)log view, the link is only offered when the number of commits shown is no more than the allowed maximum number of patches. Signed-off-by: Giuseppe Bilotta Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 4b28136ba7..8a8a32ac15 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5023,6 +5023,15 @@ sub git_log { my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100); + my ($patch_max) = gitweb_get_feature('patches'); + if ($patch_max) { + if ($patch_max < 0 || @commitlist <= $patch_max) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => href(action=>"patches", -replay=>1)}, + "patches"); + } + } + git_header_html(); git_print_page_nav('log','', $hash,undef,undef, $paging_nav); @@ -5102,6 +5111,11 @@ sub git_commit { } @$parents ) . ')'; } + if (gitweb_check_feature('patches')) { + $formats_nav .= " | " . + $cgi->a({-href => href(action=>"patch", -replay=>1)}, + "patch"); + } if (!defined $parent) { $parent = "--root"; @@ -5415,9 +5429,8 @@ sub git_commitdiff { my %params = @_; my $format = $params{-format} || 'html'; - my $patch_max; + my ($patch_max) = gitweb_get_feature('patches'); if ($format eq 'patch') { - ($patch_max) = gitweb_get_feature('patches'); die_error(403, "Patch view not allowed") unless $patch_max; } @@ -5435,6 +5448,11 @@ sub git_commitdiff { $formats_nav = $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)}, "raw"); + if ($patch_max) { + $formats_nav .= " | " . + $cgi->a({-href => href(action=>"patch", -replay=>1)}, + "patch"); + } if (defined $hash_parent && $hash_parent ne '-c' && $hash_parent ne '--cc') { @@ -5991,6 +6009,14 @@ sub git_shortlog { $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } + my $patch_max = gitweb_check_feature('patches'); + if ($patch_max) { + if ($patch_max < 0 || @commitlist <= $patch_max) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => href(action=>"patches", -replay=>1)}, + "patches"); + } + } git_header_html(); git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav); From e1f33efe07b9a520505fccd71bea1292fc9448dd Mon Sep 17 00:00:00 2001 From: "Kirill A. Korinskiy" Date: Tue, 23 Dec 2008 11:31:15 +0300 Subject: [PATCH 024/231] http-push: support full URI in handle_remote_ls_ctx() The program calls remote_ls() to get list of files from the server over HTTP; handle_remote_ls_ctx() is used to parse its response to populate "struct remote_ls_ctx" that is returned from remote_ls(). The handle_remote_ls_ctx() function assumed that the server returns a local path in href field, but RFC 4918 (14.7) demand of support full URI (e.g. "http://localhost:8080/repo.git"). This resulted in push failure (e.g. git-http-push issues a PROPFIND request to "/repo.git/alhost:8080/repo.git/refs/" to the server). Signed-off-by: Kirill A. Korinskiy Signed-off-by: Junio C Hamano --- http-push.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/http-push.c b/http-push.c index 7c6460919b..a4b7d08663 100644 --- a/http-push.c +++ b/http-push.c @@ -87,6 +87,7 @@ static struct object_list *objects; struct repo { char *url; + char *path; int path_len; int has_info_refs; int can_update_info_refs; @@ -1424,9 +1425,19 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed) ls->userFunc(ls); } } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) { - ls->dentry_name = xmalloc(strlen(ctx->cdata) - + char *path = ctx->cdata; + if (*ctx->cdata == 'h') { + path = strstr(path, "//"); + if (path) { + path = strchr(path+2, '/'); + } + } + if (path) { + path += remote->path_len; + } + ls->dentry_name = xmalloc(strlen(path) - remote->path_len + 1); - strcpy(ls->dentry_name, ctx->cdata + remote->path_len); + strcpy(ls->dentry_name, path + remote->path_len); } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) { ls->dentry_flags |= IS_DIR; } @@ -2206,10 +2217,11 @@ int main(int argc, char **argv) if (!remote->url) { char *path = strstr(arg, "//"); remote->url = arg; + remote->path_len = strlen(arg); if (path) { - path = strchr(path+2, '/'); - if (path) - remote->path_len = strlen(path); + remote->path = strchr(path+2, '/'); + if (remote->path) + remote->path_len = strlen(remote->path); } continue; } @@ -2238,8 +2250,9 @@ int main(int argc, char **argv) rewritten_url = xmalloc(strlen(remote->url)+2); strcpy(rewritten_url, remote->url); strcat(rewritten_url, "/"); + remote->path = rewritten_url + (remote->path - remote->url); + remote->path_len++; remote->url = rewritten_url; - ++remote->path_len; } /* Verify DAV compliance/lock support */ From 36e3b5eafe967cb721f5e2bbaa396f979b8ebd7c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Dec 2008 23:10:20 +0100 Subject: [PATCH 025/231] merge-recursive: mark rename/delete conflict as unmerged When a file was renamed in one branch, but deleted in the other, one should expect the index to contain an unmerged entry, namely the target of the rename. Make it so. Noticed by Constantine Plotnikov. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 5 +++++ t/t6024-recursive-merge.sh | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/merge-recursive.c b/merge-recursive.c index a0c804c817..69e7152204 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -902,6 +902,11 @@ static int process_renames(struct merge_options *o, ren1_src, ren1_dst, branch1, branch2); update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); + update_stages(ren1_dst, NULL, + branch1 == o->branch1 ? + ren1->pair->two : NULL, + branch1 == o->branch1 ? + NULL : ren1->pair->two, 1); } else if (!sha_eq(dst_other.sha1, null_sha1)) { const char *new_path; clean_merge = 0; diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh index 802d0d06eb..129fa3000c 100755 --- a/t/t6024-recursive-merge.sh +++ b/t/t6024-recursive-merge.sh @@ -97,4 +97,27 @@ test_expect_success 'refuse to merge binary files' ' merge.err ' +test_expect_success 'mark rename/delete as unmerged' ' + + git reset --hard && + git checkout -b delete && + git rm a1 && + test_tick && + git commit -m delete && + git checkout -b rename HEAD^ && + git mv a1 a2 + test_tick && + git commit -m rename && + test_must_fail git merge delete && + test 1 = $(git ls-files --unmerged | wc -l) && + git rev-parse --verify :2:a2 && + test_must_fail git rev-parse --verify :3:a2 && + git checkout -f delete && + test_must_fail git merge rename && + test 1 = $(git ls-files --unmerged | wc -l) && + test_must_fail git rev-parse --verify :2:a2 && + git rev-parse --verify :3:a2 + +' + test_done From d4789c60aae4bbc97aaef5cf2e0a9aa0a4df05d2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 25 Dec 2008 16:54:23 -0800 Subject: [PATCH 026/231] ls-tree: add --full-tree option The established behaviour of "git ls-tree $tree_ish" run from a subdirectory "sub/dir" in a work tree is to limit the output to the paths in the subdirectory, and strip off the leading "sub/dir" from the output, since 3c5e846 (ls-tree: major rewrite to do pathspec, 2005-11-26). This was a "usability" feature made back in the days when the line between Porcelain and plumbing was blurry, and in retrospect, it probably was misguided. The behaviour may be what the end user would expect when the command is run interactively from a subdirectory, but it also means that a scripted Porcelain that wants to use the command to list the full contents of a tree object has to do cd_to_toplevel (and save the output from "rev-parse --show-prefix" before doing so, so that it can be used as a pathspec if it wants to limit its operation to the original subdirectory in other commands). This new option makes the command operate on the full tree object, regardless of where in the work tree it is run from. It also implies the behaviour that is triggered by the existing --full-name option. Signed-off-by: Junio C Hamano --- builtin-ls-tree.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index cb61717685..c386aa5754 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -156,6 +156,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) chomp_prefix = 0; break; } + if (!strcmp(argv[1]+2, "full-tree")) { + ls_tree_prefix = prefix = NULL; + chomp_prefix = 0; + break; + } if (!prefixcmp(argv[1]+2, "abbrev=")) { abbrev = strtoul(argv[1]+9, NULL, 10); if (abbrev && abbrev < MINIMUM_ABBREV) From 81dc223deba84341be9681d7a1a4a183e02e41d6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Dec 2008 14:02:01 -0800 Subject: [PATCH 027/231] describe: Avoid unnecessary warning when using --all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 212945d4 ("Teach git-describe to verify annotated tag names before output") git-describe learned how to output a warning if an annotated tag object was matched but its internal name doesn't match the local ref name. However, "git describe --all" causes the local ref name to be prefixed with "tags/", so we need to skip over this prefix before comparing the local ref name with the name recorded inside of the tag object. Patch-by: René Scharfe Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-describe.c | 2 +- t/t6120-describe.sh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin-describe.c b/builtin-describe.c index 3da99c1d06..22b989fd74 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -160,7 +160,7 @@ static void display_name(struct commit_name *n) n->tag = lookup_tag(n->sha1); if (!n->tag || parse_tag(n->tag) || !n->tag->tag) die("annotated tag %s not available", n->path); - if (strcmp(n->tag->tag, n->path)) + if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) warning("tag '%s' is really '%s' here", n->tag->tag, n->path); } diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index c6bfef5f47..113a085cb9 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -100,6 +100,12 @@ check_describe B --tags HEAD^^2^ check_describe B-0-* --long HEAD^^2^ check_describe A-3-* --long HEAD^^2 +: >err.expect +check_describe A --all A^0 +test_expect_success 'no warning was displayed for A' ' + test_cmp err.expect err.actual +' + test_expect_success 'rename tag A to Q locally' ' mv .git/refs/tags/A .git/refs/tags/Q ' From c569b1fee151e467854ca268997f4f3baaf73564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 26 Dec 2008 10:46:25 +0100 Subject: [PATCH 028/231] daemon: handle freopen() failure Die if stderr couldn't be sent to /dev/null when operating in inetd mode and report the error message from the OS. This fixes a compiler warning about the return value of freopen() being ignored on Ubuntu 8.10. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- daemon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daemon.c b/daemon.c index 1cef3098d2..8c317bed4a 100644 --- a/daemon.c +++ b/daemon.c @@ -1118,7 +1118,9 @@ int main(int argc, char **argv) struct sockaddr *peer = (struct sockaddr *)&ss; socklen_t slen = sizeof(ss); - freopen("/dev/null", "w", stderr); + if (!freopen("/dev/null", "w", stderr)) + die("failed to redirect stderr to /dev/null: %s", + strerror(errno)); if (getpeername(0, peer, &slen)) peer = NULL; From a583971f15e520c8cb5f9967383903e13c353c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 26 Dec 2008 11:01:57 +0100 Subject: [PATCH 029/231] daemon: cleanup: replace loop with if Replace a loop around an enter_repo() call, which was used to retry a single time with a different parameter in case the first call fails, with two calls and an if. This is shorter and cleaner. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- daemon.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/daemon.c b/daemon.c index 8c317bed4a..4468fb9d35 100644 --- a/daemon.c +++ b/daemon.c @@ -150,7 +150,6 @@ static char *path_ok(char *directory) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; - int retried_path = 0; char *path; char *dir; @@ -219,22 +218,15 @@ static char *path_ok(char *directory) dir = rpath; } - do { - path = enter_repo(dir, strict_paths); - if (path) - break; - + path = enter_repo(dir, strict_paths); + if (!path && base_path && base_path_relaxed) { /* * if we fail and base_path_relaxed is enabled, try without * prefixing the base path */ - if (base_path && base_path_relaxed && !retried_path) { - dir = directory; - retried_path = 1; - continue; - } - break; - } while (1); + dir = directory; + path = enter_repo(dir, strict_paths); + } if (!path) { logerror("'%s': unable to chdir or not a git archive", dir); From 6720e95b305737fddc776f2904c339a0701e6ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 26 Dec 2008 11:12:15 +0100 Subject: [PATCH 030/231] daemon: cleanup: factor out xstrdup_tolower() Add xstrdup_tolower(), a helper to get a lower case copy of a string, and use it in two cases. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- daemon.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/daemon.c b/daemon.c index 4468fb9d35..60bf6c743c 100644 --- a/daemon.c +++ b/daemon.c @@ -397,6 +397,14 @@ static void make_service_overridable(const char *name, int ena) die("No such service %s", name); } +static char *xstrdup_tolower(const char *str) +{ + char *p, *dup = xstrdup(str); + for (p = dup; *p; p++) + *p = tolower(*p); + return dup; +} + /* * Separate the "extra args" information as supplied by the client connection. */ @@ -405,7 +413,6 @@ static void parse_extra_args(char *extra_args, int buflen) char *val; int vallen; char *end = extra_args + buflen; - char *hp; while (extra_args < end && *extra_args) { saw_extended_args = 1; @@ -423,7 +430,7 @@ static void parse_extra_args(char *extra_args, int buflen) tcp_port = xstrdup(port); } free(hostname); - hostname = xstrdup(host); + hostname = xstrdup_tolower(host); } /* On to the next one */ @@ -431,20 +438,11 @@ static void parse_extra_args(char *extra_args, int buflen) } } - /* - * Replace literal host with lowercase-ized hostname. - */ - hp = hostname; - if (!hp) - return; - for ( ; *hp; hp++) - *hp = tolower(*hp); - /* * Locate canonical hostname and its IP address. */ + if (hostname) { #ifndef NO_IPV6 - { struct addrinfo hints; struct addrinfo *ai, *ai0; int gai; @@ -468,9 +466,7 @@ static void parse_extra_args(char *extra_args, int buflen) } freeaddrinfo(ai0); } - } #else - { struct hostent *hent; struct sockaddr_in sa; char **ap; @@ -491,8 +487,8 @@ static void parse_extra_args(char *extra_args, int buflen) canon_hostname = xstrdup(hent->h_name); free(ip_address); ip_address = xstrdup(addrbuf); - } #endif + } } @@ -945,12 +941,8 @@ int main(int argc, char **argv) char *arg = argv[i]; if (!prefixcmp(arg, "--listen=")) { - char *p = arg + 9; - char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1); - while (*p) - *ph++ = tolower(*p++); - *ph = 0; - continue; + listen_addr = xstrdup_tolower(arg + 9); + continue; } if (!prefixcmp(arg, "--port=")) { char *end; From 4deba8b7798aac52e33aa8e1c49a8cdc0940ac36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 26 Dec 2008 11:17:04 +0100 Subject: [PATCH 031/231] merge-file: handle freopen() failure Report the error if redirection of stderr to /dev/null failed. This silences a compiler warning about ignoring the return value of freopen() on Ubuntu 8.10. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin-merge-file.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin-merge-file.c b/builtin-merge-file.c index 9d4e874809..96edb97a83 100644 --- a/builtin-merge-file.c +++ b/builtin-merge-file.c @@ -51,8 +51,11 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, options, merge_file_usage, 0); if (argc != 3) usage_with_options(merge_file_usage, options); - if (quiet) - freopen("/dev/null", "w", stderr); + if (quiet) { + if (!freopen("/dev/null", "w", stderr)) + return error("failed to redirect stderr to /dev/null: " + "%s\n", strerror(errno)); + } for (i = 0; i < 3; i++) { if (!names[i]) From a010966844221ea14f1de26fdab8fe37332ff928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 27 Dec 2008 01:32:49 +0100 Subject: [PATCH 032/231] pretty: factor out skip_empty_lines() The patch after the next one will use it. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pretty.c b/pretty.c index f6ff31264b..c43497bb24 100644 --- a/pretty.c +++ b/pretty.c @@ -181,6 +181,20 @@ static int is_empty_line(const char *line, int *len_p) return !len; } +static const char *skip_empty_lines(const char *msg) +{ + for (;;) { + int linelen = get_one_line(msg); + int ll = linelen; + if (!linelen) + break; + if (!is_empty_line(msg, &ll)) + break; + msg += linelen; + } + return msg; +} + static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, const struct commit *commit, int abbrev) { @@ -850,15 +864,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, } /* Skip excess blank lines at the beginning of body, if any... */ - for (;;) { - int linelen = get_one_line(msg); - int ll = linelen; - if (!linelen) - break; - if (!is_empty_line(msg, &ll)) - break; - msg += linelen; - } + msg = skip_empty_lines(msg); /* These formats treat the title line specially. */ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) From 88c44735ab24aea3e669501c4491f7c9ca745ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 27 Dec 2008 01:39:35 +0100 Subject: [PATCH 033/231] pretty: factor out format_subject() The next patch will use it. In the version that was factored out, we can't rely on the len of the struct strbuf to find out if a line separator needs to be added, as it might already contain something. Add a guard variable ("first") instead. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/pretty.c b/pretty.c index c43497bb24..632abc5231 100644 --- a/pretty.c +++ b/pretty.c @@ -495,6 +495,28 @@ static void parse_commit_header(struct format_commit_context *context) context->commit_header_parsed = 1; } +static const char *format_subject(struct strbuf *sb, const char *msg, + const char *line_separator) +{ + int first = 1; + + for (;;) { + const char *line = msg; + int linelen = get_one_line(line); + + msg += linelen; + if (!linelen || is_empty_line(line, &linelen)) + break; + + strbuf_grow(sb, linelen + 2); + if (!first) + strbuf_addstr(sb, line_separator); + strbuf_add(sb, line, linelen); + first = 0; + } + return msg; +} + static void format_decoration(struct strbuf *sb, const struct commit *commit) { struct name_decoration *d; @@ -718,27 +740,11 @@ void pp_title_line(enum cmit_fmt fmt, const char *encoding, int need_8bit_cte) { + const char *line_separator = (fmt == CMIT_FMT_EMAIL) ? "\n " : " "; struct strbuf title; strbuf_init(&title, 80); - - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(line); - - *msg_p += linelen; - if (!linelen || is_empty_line(line, &linelen)) - break; - - strbuf_grow(&title, linelen + 2); - if (title.len) { - if (fmt == CMIT_FMT_EMAIL) { - strbuf_addch(&title, '\n'); - } - strbuf_addch(&title, ' '); - } - strbuf_add(&title, line, linelen); - } + *msg_p = format_subject(&title, *msg_p, line_separator); strbuf_grow(sb, title.len + 1024); if (subject) { From f53bd743ff713f6507f28f8e730ac3e27858ab43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 27 Dec 2008 01:49:21 +0100 Subject: [PATCH 034/231] pretty: support multiline subjects with format: git log --pretty=format:%s (and tformat:) used to display the first line of the subject, unlike the other --pretty options, which would construct a subject line from all lines of the first paragraph of the commit message. For consistency and increased code reuse, change format: to do the same as the other options. Before: $ git log --pretty=oneline v1.6.1 | md5sum 7c0896d2a94fc3315a0372b9b3373a8f - $ git log --pretty=tformat:"%H %s" v1.6.1 | md5sum 298903b1c065002e15daa5329213c51f - After: $ git log --pretty=tformat:"%H %s" v1.6.1 | md5sum 7c0896d2a94fc3315a0372b9b3373a8f - $ git log --pretty=oneline v1.6.1 | md5sum 7c0896d2a94fc3315a0372b9b3373a8f - Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 53 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/pretty.c b/pretty.c index 632abc5231..343dca556c 100644 --- a/pretty.c +++ b/pretty.c @@ -424,13 +424,15 @@ struct chunk { struct format_commit_context { const struct commit *commit; enum date_mode dmode; + unsigned commit_header_parsed:1; + unsigned commit_message_parsed:1; /* These offsets are relative to the start of the commit message. */ - int commit_header_parsed; - struct chunk subject; struct chunk author; struct chunk committer; struct chunk encoding; + size_t message_off; + size_t subject_off; size_t body_off; /* The following ones are relative to the result struct strbuf. */ @@ -460,23 +462,14 @@ static void parse_commit_header(struct format_commit_context *context) { const char *msg = context->commit->buffer; int i; - enum { HEADER, SUBJECT, BODY } state; - for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { + for (i = 0; msg[i]; i++) { int eol; for (eol = i; msg[eol] && msg[eol] != '\n'; eol++) ; /* do nothing */ - if (state == SUBJECT) { - context->subject.off = i; - context->subject.len = eol - i; - i = eol; - } if (i == eol) { - state++; - /* strip empty lines */ - while (msg[eol] == '\n' && msg[eol + 1] == '\n') - eol++; + break; } else if (!prefixcmp(msg + i, "author ")) { context->author.off = i + 7; context->author.len = eol - i - 7; @@ -488,10 +481,8 @@ static void parse_commit_header(struct format_commit_context *context) context->encoding.len = eol - i - 9; } i = eol; - if (!msg[i]) - break; } - context->body_off = i; + context->message_off = i; context->commit_header_parsed = 1; } @@ -508,6 +499,8 @@ static const char *format_subject(struct strbuf *sb, const char *msg, if (!linelen || is_empty_line(line, &linelen)) break; + if (!sb) + continue; strbuf_grow(sb, linelen + 2); if (!first) strbuf_addstr(sb, line_separator); @@ -517,6 +510,21 @@ static const char *format_subject(struct strbuf *sb, const char *msg, return msg; } +static void parse_commit_message(struct format_commit_context *c) +{ + const char *msg = c->commit->buffer + c->message_off; + const char *start = c->commit->buffer; + + msg = skip_empty_lines(msg); + c->subject_off = msg - start; + + msg = format_subject(NULL, msg, NULL); + msg = skip_empty_lines(msg); + c->body_off = msg - start; + + c->commit_message_parsed = 1; +} + static void format_decoration(struct strbuf *sb, const struct commit *commit) { struct name_decoration *d; @@ -636,9 +644,6 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, parse_commit_header(c); switch (placeholder[0]) { - case 's': /* subject */ - strbuf_add(sb, msg + c->subject.off, c->subject.len); - return 1; case 'a': /* author ... */ return format_person_part(sb, placeholder[1], msg + c->author.off, c->author.len, @@ -650,6 +655,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, case 'e': /* encoding */ strbuf_add(sb, msg + c->encoding.off, c->encoding.len); return 1; + } + + /* Now we need to parse the commit message. */ + if (!c->commit_message_parsed) + parse_commit_message(c); + + switch (placeholder[0]) { + case 's': /* subject */ + format_subject(sb, msg + c->subject_off, " "); + return 1; case 'b': /* body */ strbuf_addstr(sb, msg + c->body_off); return 1; From 3a882d9696ba2ba46adc107b92b4721e59bb60b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adeodato=20Sim=C3=B3?= Date: Sat, 27 Dec 2008 19:23:30 +0100 Subject: [PATCH 035/231] git-shortlog.txt: improve documentation about .mailmap files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The description on .mailmap made it seem like they are only useful for commits with a wrong address for an author, but they are about fixing the real name. Explain this better in the text, and replace the existing example with a new one that hopefully makes things clearer. Signed-off-by: Adeodato Simó Signed-off-by: Junio C Hamano --- Documentation/git-shortlog.txt | 40 ++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 7ccf31ccc4..8f7c0e226d 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -48,15 +48,41 @@ OPTIONS FILES ----- -If the file `.mailmap` exists, it will be used for mapping author -email addresses to a real author name. One mapping per line, first -the author name followed by the email address enclosed by -'<' and '>'. Use hash '#' for comments. Example: +If a file `.mailmap` exists at the toplevel of the repository, +it is used to map an author email address to a canonical real name. This +can be used to coalesce together commits by the same person where their +name was spelled differently (whether with the same email address or +not). + +Each line in the file consists, in this order, of the canonical real name +of an author, whitespace, and an email address (enclosed by '<' and '>') +to map to the name. Use hash '#' for comments, either on their own line, +or after the email address. + +A canonical name may appear in more than one line, associated with +different email addresses, but it doesn't make sense for a given address +to appear more than once (if that happens, a later line overrides the +earlier ones). + +So, for example, if your history contains commits by two authors, Jane +and Joe, whose names appear in the repository under several forms: ------------ -# Keep alphabetized -Adam Morrow -Eve Jones +Joe Developer +Joe R. Developer +Jane Doe +Jane Doe +Jane D. +------------ + +Then, supposing Joe wants his middle name initial used, and Jane prefers +her family name fully spelled out, a proper `.mailmap` file would look like: + +------------ +# Note how we don't need an entry for , because the +# real name of that author is correct already, and coalesced directly. +Jane Doe +Joe R. Developer ------------ Author From f83b9ba20973472f64699e1e2707b74da211a89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adeodato=20Sim=C3=B3?= Date: Sat, 27 Dec 2008 09:50:30 +0100 Subject: [PATCH 036/231] git-send-email.txt: move --format-patch paragraph to a proper location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When introducing --format-patch, its documentation was accidentally inserted in the middle of documentation for --validate. Signed-off-by: Adeodato Simó Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 12788667d4..b69846e522 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -197,12 +197,6 @@ Administering --[no-]validate:: Perform sanity checks on patches. Currently, validation means the following: - ---[no-]format-patch:: - When an argument may be understood either as a reference or as a file name, - choose to understand it as a format-patch argument ('--format-patch') - or as a file name ('--no-format-patch'). By default, when such a conflict - occurs, git send-email will fail. + -- * Warn of patches that contain lines longer than 998 characters; this @@ -212,6 +206,12 @@ Administering Default is the value of 'sendemail.validate'; if this is not set, default to '--validate'. +--[no-]format-patch:: + When an argument may be understood either as a reference or as a file name, + choose to understand it as a format-patch argument ('--format-patch') + or as a file name ('--no-format-patch'). By default, when such a conflict + occurs, git send-email will fail. + CONFIGURATION ------------- From 78f8fbc9d641096eef495dd877de3438d8a000e0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 27 Dec 2008 14:25:10 -0800 Subject: [PATCH 037/231] Start 1.6.2 cycle Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.2.txt | 28 ++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.6.2.txt diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes-1.6.2.txt new file mode 100644 index 0000000000..1a80626781 --- /dev/null +++ b/Documentation/RelNotes-1.6.2.txt @@ -0,0 +1,28 @@ +GIT v1.6.2 Release Notes +======================== + +Updates since v1.6.1 +-------------------- + +(subsystems) + +(portability) + +(performance) + +(usability, bells and whistles) + +(internal) + + +Fixes since v1.6.1 +------------------ + +All of the fixes in v1.6.1.X maintenance series are included in this +release, unless otherwise noted. + +-- +exec >/var/tmp/1 +O=v1.6.1 +echo O=$(git describe master) +git shortlog --no-merges $O..master ^maint diff --git a/RelNotes b/RelNotes index 3d420845b1..ecced08731 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.6.1.txt \ No newline at end of file +Documentation/RelNotes-1.6.2.txt \ No newline at end of file From e70b9a8bd2e25ced75cff2a2c26aed3f983425f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 27 Dec 2008 15:21:04 +0700 Subject: [PATCH 038/231] grep: support --no-ext-grep to test builtin grep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin-grep.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin-grep.c b/builtin-grep.c index 624f86e287..3c97c2c4d5 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -20,6 +20,8 @@ #endif #endif +static int builtin_grep; + /* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. @@ -389,7 +391,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) * we grep through the checked-out files. It tends to * be a lot more optimized */ - if (!cached) { + if (!cached && !builtin_grep) { hit = external_grep(opt, paths, cached); if (hit >= 0) return hit; @@ -545,6 +547,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix) cached = 1; continue; } + if (!strcmp("--no-ext-grep", arg)) { + builtin_grep = 1; + continue; + } if (!strcmp("-a", arg) || !strcmp("--text", arg)) { opt.binary = GREP_BINARY_TEXT; From 57d43466fbb4ffb4106b97593a96d714137dde2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 27 Dec 2008 15:21:03 +0700 Subject: [PATCH 039/231] grep: grep cache entries if they are "assume unchanged" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Assume unchanged" bit means "please pretend that I have never touched this file", so if user removes the file, we should not care. This patch teaches "git grep" to use cache version in such situations. External grep case has not been fixed yet. But given that on the platform that CE_VALID bit may be used like Windows, external grep is not available anyway, I would wait for people to raise their hands before touching it. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin-grep.c | 7 ++++++- t/t7002-grep.sh | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/builtin-grep.c b/builtin-grep.c index 3c97c2c4d5..bebf15cd6f 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -404,7 +404,12 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) continue; if (!pathspec_matches(paths, ce->name)) continue; - if (cached) { + /* + * If CE_VALID is on, we assume worktree file and its cache entry + * are identical, even if worktree file has been modified, so use + * cache version instead + */ + if (cached || (ce->ce_flags & CE_VALID)) { if (ce_stage(ce)) continue; hit |= grep_sha1(opt, ce->sha1, ce->name, 0); diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index 18fe6f2d57..c4938544d4 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -161,7 +161,14 @@ test_expect_success 'log grep (6)' ' git log --author=-0700 --pretty=tformat:%s >actual && >expect && test_cmp expect actual +' +test_expect_success 'grep with CE_VALID file' ' + git update-index --assume-unchanged t/t && + rm t/t && + test "$(git grep --no-ext-grep t)" = "t/t:test" && + git update-index --no-assume-unchanged t/t && + git checkout t/t ' test_done From fb3bb3d1325f1d0a8cdc402a596c9a520b0ccbe6 Mon Sep 17 00:00:00 2001 From: Devin Doucette Date: Sat, 27 Dec 2008 02:39:31 -0700 Subject: [PATCH 040/231] gitweb: Fix export check in git_get_projects_list When $filter was empty, the path passed to check_export_ok would contain an extra '/', which some implementations of export_auth_hook are sensitive to. It makes more sense to fix this here than to handle the special case in each implementation of export_auth_hook. Signed-off-by: Devin Doucette Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 8f574c7b36..99f71b47c2 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2147,8 +2147,9 @@ sub git_get_projects_list { my $subdir = substr($File::Find::name, $pfxlen + 1); # we check related file in $projectroot - if (check_export_ok("$projectroot/$filter/$subdir")) { - push @list, { path => ($filter ? "$filter/" : '') . $subdir }; + my $path = ($filter ? "$filter/" : '') . $subdir; + if (check_export_ok("$projectroot/$path")) { + push @list, { path => $path }; $File::Find::prune = 1; } }, From 8b30ad01b421213113c64a1d3e46dcd8791d88df Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Mon, 29 Dec 2008 10:03:08 +0200 Subject: [PATCH 041/231] Fix the building of user-manual.texi and gitman.texi documents Previously "docbook2x-texi" failed to generate user-manual.texi and gitman.texi files from .xml input files because "iconv" stopped at "illegal input sequence" error. This was due to some UTF-8 octets in the input .xml files. This patch adds option --encoding=UTF-8 for "docbook2x-texi" to allow the building of .texi files complete. Signed-off-by: Teemu Likonen Signed-off-by: Junio C Hamano --- Documentation/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index c34c1cae20..c41a7b483c 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -187,13 +187,14 @@ git.info: user-manual.texi user-manual.texi: user-manual.xml $(RM) $@+ $@ - $(DOCBOOK2X_TEXI) user-manual.xml --to-stdout | $(PERL_PATH) fix-texi.perl >$@+ + $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout | \ + $(PERL_PATH) fix-texi.perl >$@+ mv $@+ $@ gitman.texi: $(MAN_XML) cat-texi.perl $(RM) $@+ $@ - ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --to-stdout $(xml);)) | \ - $(PERL_PATH) cat-texi.perl $@ >$@+ + ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \ + --to-stdout $(xml);)) | $(PERL_PATH) cat-texi.perl $@ >$@+ mv $@+ $@ gitman.info: gitman.texi From 9c6c304d6a01b72d7ce20722b80ca62e01fe8356 Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Mon, 29 Dec 2008 10:03:09 +0200 Subject: [PATCH 042/231] Fix the building of gitman.info document "makeinfo" failed to generate gitman.info from gitman.texi input file because the combined manual page file contains several nodes with the same name (DESCRIPTION, OPTIONS, SEE ALSO etc.). An Info document should contain unique node names. This patch creates a simple (read: ugly) work-around by suppressing the validation of the final Info file. Jumping to nodes in the Info document still works but they are not very useful. Common man-page headings like DESCRIPTION and OPTIONS appear in the Info node list and they point to the man page where they appear first (that is git-add currently). Also, this patch adds directory-entry information for Info document to make the document appear in the top-level Info directory. Signed-off-by: Teemu Likonen Signed-off-by: Junio C Hamano --- Documentation/Makefile | 2 +- Documentation/cat-texi.perl | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index c41a7b483c..5cd8b63ac5 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -198,7 +198,7 @@ gitman.texi: $(MAN_XML) cat-texi.perl mv $@+ $@ gitman.info: gitman.texi - $(MAKEINFO) --no-split $*.texi + $(MAKEINFO) --no-split --no-validate $*.texi $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml $(RM) $@+ $@ diff --git a/Documentation/cat-texi.perl b/Documentation/cat-texi.perl index dbc133cd3c..828ec62554 100755 --- a/Documentation/cat-texi.perl +++ b/Documentation/cat-texi.perl @@ -18,8 +18,12 @@ close TMP; printf '\input texinfo @setfilename gitman.info -@documentencoding us-ascii -@node Top,,%s +@documentencoding UTF-8 +@dircategory Development +@direntry +* Git Man Pages: (gitman). Manual pages for Git revision control system +@end direntry +@node Top,,, (dir) @top Git Manual Pages @documentlanguage en @menu From 6d0e674a575421347abe5749e645ca6dc78c8207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 28 Dec 2008 19:45:32 +0100 Subject: [PATCH 043/231] diff: add option to show context between close hunks Merge two hunks if there is only the specified number of otherwise unshown context between them. For --inter-hunk-context=1, the resulting patch has the same number of lines but shows uninterrupted context instead of a context header line in between. Patches generated with this option are easier to read but are also more likely to conflict if the file to be patched contains other changes. This patch keeps the default for this option at 0. It is intended to just make the feature available in order to see its advantages and downsides. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/diff-options.txt | 4 ++ contrib/completion/git-completion.bash | 2 + diff.c | 4 ++ diff.h | 1 + t/t4032-diff-inter-hunk-context.sh | 92 ++++++++++++++++++++++++++ xdiff/xdiff.h | 1 + xdiff/xemit.c | 3 +- 7 files changed, 106 insertions(+), 1 deletion(-) create mode 100755 t/t4032-diff-inter-hunk-context.sh diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index c62b45cdba..c7fcc8007a 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -215,6 +215,10 @@ endif::git-format-patch[] -w:: Shorthand for "--ignore-all-space". +--inter-hunk-context=:: + Show the context between diff hunks, up to the specified number + of lines, thereby fusing hunks that are close to each other. + --exit-code:: Make the program exit with codes similar to diff(1). That is, it exits with 1 if there were differences and diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..a046441974 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -776,6 +776,7 @@ _git_diff () --no-ext-diff --no-prefix --src-prefix= --dst-prefix= --base --ours --theirs + --inter-hunk-context= " return ;; @@ -967,6 +968,7 @@ _git_log () --color-words --walk-reflogs --parents --children --full-history --merge + --inter-hunk-context= " return ;; diff --git a/diff.c b/diff.c index 0484601f42..56b80f9609 100644 --- a/diff.c +++ b/diff.c @@ -1469,6 +1469,7 @@ static void builtin_diff(const char *name_a, ecbdata.file = o->file; xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; + xecfg.interhunkctxlen = o->interhunkcontext; xecfg.flags = XDL_EMIT_FUNCNAMES; if (pe) xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); @@ -2538,6 +2539,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->b_prefix = arg + 13; else if (!strcmp(arg, "--no-prefix")) options->a_prefix = options->b_prefix = ""; + else if (opt_arg(arg, '\0', "inter-hunk-context", + &options->interhunkcontext)) + ; else if (!prefixcmp(arg, "--output=")) { options->file = fopen(arg + strlen("--output="), "w"); options->close_file = 1; diff --git a/diff.h b/diff.h index 42582edee6..4d5a32781d 100644 --- a/diff.h +++ b/diff.h @@ -78,6 +78,7 @@ struct diff_options { const char *a_prefix, *b_prefix; unsigned flags; int context; + int interhunkcontext; int break_opt; int detect_rename; int skip_stat_unmatch; diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh new file mode 100755 index 0000000000..e4e3e28fc7 --- /dev/null +++ b/t/t4032-diff-inter-hunk-context.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +test_description='diff hunk fusing' + +. ./test-lib.sh + +f() { + echo $1 + i=1 + while test $i -le $2 + do + echo $i + i=$(expr $i + 1) + done + echo $3 +} + +t() { + case $# in + 4) hunks=$4; cmd="diff -U$3";; + 5) hunks=$5; cmd="diff -U$3 --inter-hunk-context=$4";; + esac + label="$cmd, $1 common $2" + file=f$1 + expected=expected.$file.$3.$hunks + + if ! test -f $file + then + f A $1 B >$file + git add $file + git commit -q -m. $file + f X $1 Y >$file + fi + + test_expect_success "$label: count hunks ($hunks)" " + test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks + " + + test -f $expected && + test_expect_success "$label: check output" " + git $cmd $file | grep -v '^index ' >actual && + test_cmp $expected actual + " +} + +cat <expected.f1.0.1 || exit 1 +diff --git a/f1 b/f1 +--- a/f1 ++++ b/f1 +@@ -1,3 +1,3 @@ +-A ++X + 1 +-B ++Y +EOF + +cat <expected.f1.0.2 || exit 1 +diff --git a/f1 b/f1 +--- a/f1 ++++ b/f1 +@@ -1 +1 @@ +-A ++X +@@ -3 +3 @@ A +-B ++Y +EOF + +# common lines ctx intrctx hunks +t 1 line 0 2 +t 1 line 0 0 2 +t 1 line 0 1 1 +t 1 line 0 2 1 +t 1 line 1 1 + +t 2 lines 0 2 +t 2 lines 0 0 2 +t 2 lines 0 1 2 +t 2 lines 0 2 1 +t 2 lines 1 1 + +t 3 lines 1 2 +t 3 lines 1 0 2 +t 3 lines 1 1 1 +t 3 lines 1 2 1 + +t 9 lines 3 2 +t 9 lines 3 2 2 +t 9 lines 3 3 1 + +test_done diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h index 84fff583e2..361f802319 100644 --- a/xdiff/xdiff.h +++ b/xdiff/xdiff.h @@ -84,6 +84,7 @@ typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long typedef struct s_xdemitconf { long ctxlen; + long interhunkctxlen; unsigned long flags; find_func_t find_func; void *find_func_priv; diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 4625c1b421..05bfa41f10 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -59,9 +59,10 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t * */ xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) { xdchange_t *xch, *xchp; + long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen; for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next) - if (xch->i1 - (xchp->i1 + xchp->chg1) > 2 * xecfg->ctxlen) + if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common) break; return xchp; From bd7c6e7fc53691e2c459eaad73bc12006939333e Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Mon, 29 Dec 2008 00:37:25 +0100 Subject: [PATCH 044/231] doc/git-send-email: mention sendemail.cc config variable This variable was added in 5f8b9fc (git-send-email: add a new sendemail.cc configuration variable, 2008-04-27), but is not yet refered to by the documentation. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 12788667d4..2b6d68c498 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -34,6 +34,7 @@ The --bcc option must be repeated for each user you want on the bcc list. --cc:: Specify a starting "Cc:" value for each email. + Default is the value of 'sendemail.cc'. + The --cc option must be repeated for each user you want on the cc list. From a9e67c8ccc6958d09c575a3d4837a49d8a2ec7d5 Mon Sep 17 00:00:00 2001 From: "jidanni@jidanni.org" Date: Mon, 29 Dec 2008 15:03:17 +0800 Subject: [PATCH 045/231] Documentation/diff-options.txt: unify options Instead of listing short option (e.g. "-U") as a shorthand for its longer counterpart (e.g. "--unified="), list the synonyms together. It saves one indirection to find what the reader wants. Signed-off-by: jidanni Signed-off-by: Junio C Hamano --- Documentation/diff-options.txt | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index c62b45cdba..b432d2518a 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -19,16 +19,12 @@ endif::git-format-patch[] ifndef::git-format-patch[] -p:: +-u:: Generate patch (see section on generating patches). {git-diff? This is the default.} endif::git-format-patch[] --u:: - Synonym for "-p". - -U:: - Shorthand for "--unified=". - --unified=:: Generate diffs with lines of context instead of the usual three. Implies "-p". @@ -190,31 +186,25 @@ endif::git-format-patch[] can name which subdirectory to make the output relative to by giving a as an argument. +-a:: --text:: Treat all files as text. --a:: - Shorthand for "--text". - --ignore-space-at-eol:: Ignore changes in whitespace at EOL. +-b:: --ignore-space-change:: Ignore changes in amount of whitespace. This ignores whitespace at line end, and considers all other sequences of one or more whitespace characters to be equivalent. --b:: - Shorthand for "--ignore-space-change". - +-w:: --ignore-all-space:: Ignore whitespace when comparing lines. This ignores differences even if one line has whitespace where the other line has none. --w:: - Shorthand for "--ignore-all-space". - --exit-code:: Make the program exit with codes similar to diff(1). That is, it exits with 1 if there were differences and From 936b7057e8401665a2d106e8de018561db6333fe Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 29 Dec 2008 01:17:34 -0800 Subject: [PATCH 046/231] Prepare for v1.6.1.1 maintenance release Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.1.1.txt | 23 +++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.6.1.1.txt diff --git a/Documentation/RelNotes-1.6.1.1.txt b/Documentation/RelNotes-1.6.1.1.txt new file mode 100644 index 0000000000..10b38e6ec1 --- /dev/null +++ b/Documentation/RelNotes-1.6.1.1.txt @@ -0,0 +1,23 @@ +GIT v1.6.1.1 Release Notes +========================== + +Fixes since v1.6.1 +------------------ + +* "git describe --all" complained when a commit is described with a tag, + which was nonsense. + +* "git log --pretty=format:%s" did not handle a multi-line subject the + same way as built-in log listers (i.e. shortlog, --pretty=oneline, etc.) + +* "git daemon", and "git merge-file" are more careful when freopen fails + and barf, instead of going on and writing to unopened filehandle. + +Other documentation fixes. + +--- +exec >/var/tmp/1 +O=v1.6.1-15-ga9e67c8 +echo O=$(git describe maint) +git shortlog --no-merges $O..maint + diff --git a/RelNotes b/RelNotes index 3d420845b1..2ebf251ddf 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.6.1.txt \ No newline at end of file +Documentation/RelNotes-1.6.1.1.txt \ No newline at end of file From f26c4940c4c7942a95058d9aaef7b01967085a60 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 28 Dec 2008 00:03:57 +0100 Subject: [PATCH 047/231] parse-opt: migrate builtin-apply. The only incompatible change is that the user how have to use '--' before a patch file if it is named "--build-fake-ancestor=something". Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- Documentation/git-apply.txt | 4 +- builtin-apply.c | 281 +++++++++++++++++++----------------- 2 files changed, 154 insertions(+), 131 deletions(-) diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index e726510ab1..9400f6a5d0 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git apply' [--stat] [--numstat] [--summary] [--check] [--index] - [--apply] [--no-add] [--build-fake-ancestor ] [-R | --reverse] + [--apply] [--no-add] [--build-fake-ancestor=] [-R | --reverse] [--allow-binary-replacement | --binary] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached] [--whitespace=] @@ -64,7 +64,7 @@ OPTIONS cached data, apply the patch, and store the result in the index, without using the working tree. This implies '--index'. ---build-fake-ancestor :: +--build-fake-ancestor=:: Newer 'git-diff' output has embedded 'index information' for each blob to help identify the original version that the patch applies to. When this flag is given, and if diff --git a/builtin-apply.c b/builtin-apply.c index 07244b0736..cb988a3526 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -14,6 +14,7 @@ #include "builtin.h" #include "string-list.h" #include "dir.h" +#include "parse-options.h" /* * --check turns on checking that the working tree matches the @@ -45,9 +46,11 @@ static int apply_verbosely; static int no_add; static const char *fake_ancestor; static int line_termination = '\n'; -static unsigned long p_context = ULONG_MAX; -static const char apply_usage[] = -"git apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; +static unsigned int p_context = UINT_MAX; +static const char * const apply_usage[] = { + "git apply [options] [...]", + NULL +}; static enum ws_error_action { nowarn_ws_error, @@ -61,6 +64,8 @@ static int applied_after_fixing_ws; static const char *patch_input_file; static const char *root; static int root_len; +static int read_stdin = 1; +static int options; static void parse_whitespace_option(const char *option) { @@ -3135,150 +3140,168 @@ static int git_apply_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } +static int option_parse_stdin(const struct option *opt, + const char *arg, int unset) +{ + int *errs = opt->value; + + *errs |= apply_patch(0, "", options); + read_stdin = 0; + return 0; +} + +static int option_parse_exclude(const struct option *opt, + const char *arg, int unset) +{ + add_name_limit(arg, 1); + return 0; +} + +static int option_parse_include(const struct option *opt, + const char *arg, int unset) +{ + add_name_limit(arg, 0); + has_include = 1; + return 0; +} + +static int option_parse_p(const struct option *opt, + const char *arg, int unset) +{ + p_value = atoi(arg); + p_value_known = 1; + return 0; +} + +static int option_parse_z(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + line_termination = '\n'; + else + line_termination = 0; + return 0; +} + +static int option_parse_whitespace(const struct option *opt, + const char *arg, int unset) +{ + const char **whitespace_option = opt->value; + + *whitespace_option = arg; + parse_whitespace_option(arg); + return 0; +} + +static int option_parse_directory(const struct option *opt, + const char *arg, int unset) +{ + root_len = strlen(arg); + if (root_len && arg[root_len - 1] != '/') { + char *new_root; + root = new_root = xmalloc(root_len + 2); + strcpy(new_root, arg); + strcpy(new_root + root_len++, "/"); + } else + root = arg; + return 0; +} int cmd_apply(int argc, const char **argv, const char *unused_prefix) { int i; - int read_stdin = 1; - int options = 0; int errs = 0; int is_not_gitdir; + int binary; + int force_apply = 0; const char *whitespace_option = NULL; + struct option builtin_apply_options[] = { + { OPTION_CALLBACK, '-', NULL, &errs, NULL, + "read the patch from the standard input", + PARSE_OPT_NOARG, option_parse_stdin }, + { OPTION_CALLBACK, 0, "exclude", NULL, "path", + "don´t apply changes matching the given path", + 0, option_parse_exclude }, + { OPTION_CALLBACK, 0, "include", NULL, "path", + "apply changes matching the given path", + 0, option_parse_include }, + { OPTION_CALLBACK, 'p', NULL, NULL, "num", + "remove leading slashes from traditional diff paths", + 0, option_parse_p }, + OPT_BOOLEAN(0, "no-add", &no_add, + "ignore additions made by the patch"), + OPT_BOOLEAN(0, "stat", &diffstat, + "instead of applying the patch, output diffstat for the input"), + OPT_BOOLEAN(0, "allow-binary-replacement", &binary, + "now no-op"), + OPT_BOOLEAN(0, "binary", &binary, + "now no-op"), + OPT_BOOLEAN(0, "numstat", &numstat, + "shows number of added and deleted lines in decimal notation"), + OPT_BOOLEAN(0, "summary", &summary, + "instead of applying the patch, output a summary for the input"), + OPT_BOOLEAN(0, "check", &check, + "instead of applying the patch, see if the patch is applicable"), + OPT_BOOLEAN(0, "index", &check_index, + "make sure the patch is applicable to the current index"), + OPT_BOOLEAN(0, "cached", &cached, + "apply a patch without touching the working tree"), + OPT_BOOLEAN(0, "apply", &force_apply, + "also apply the patch (use with --stat/--summary/--check)"), + OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file", + "build a temporary index based on embedded index information"), + { OPTION_CALLBACK, 'z', NULL, NULL, NULL, + "paths are separated with NUL character", + PARSE_OPT_NOARG, option_parse_z }, + OPT_INTEGER('C', NULL, &p_context, + "ensure at least lines of context match"), + { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action", + "detect new or modified lines that have whitespace errors", + 0, option_parse_whitespace }, + OPT_BOOLEAN('R', "reverse", &apply_in_reverse, + "apply the patch in reverse"), + OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero, + "don't expect at least one line of context"), + OPT_BOOLEAN(0, "reject", &apply_with_reject, + "leave the rejected hunks in corresponding *.rej files"), + OPT__VERBOSE(&apply_verbosely), + OPT_BIT(0, "inaccurate-eof", &options, + "tolerate incorrectly detected missing new-line at the end of file", + INACCURATE_EOF), + OPT_BIT(0, "recount", &options, + "do not trust the line counts in the hunk headers", + RECOUNT), + { OPTION_CALLBACK, 0, "directory", NULL, "root", + "prepend to all filenames", + 0, option_parse_directory }, + OPT_END() + }; + prefix = setup_git_directory_gently(&is_not_gitdir); prefix_length = prefix ? strlen(prefix) : 0; git_config(git_apply_config, NULL); if (apply_default_whitespace) parse_whitespace_option(apply_default_whitespace); - for (i = 1; i < argc; i++) { + argc = parse_options(argc, argv, builtin_apply_options, + apply_usage, 0); + if (apply_with_reject) + apply = apply_verbosely = 1; + if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor)) + apply = 0; + if (check_index && is_not_gitdir) + die("--index outside a repository"); + if (cached) { + if (is_not_gitdir) + die("--cached outside a repository"); + check_index = 1; + } + for (i = 0; i < argc; i++) { const char *arg = argv[i]; - char *end; int fd; - if (!strcmp(arg, "-")) { - errs |= apply_patch(0, "", options); - read_stdin = 0; - continue; - } - if (!prefixcmp(arg, "--exclude=")) { - add_name_limit(arg + 10, 1); - continue; - } - if (!prefixcmp(arg, "--include=")) { - add_name_limit(arg + 10, 0); - has_include = 1; - continue; - } - if (!prefixcmp(arg, "-p")) { - p_value = atoi(arg + 2); - p_value_known = 1; - continue; - } - if (!strcmp(arg, "--no-add")) { - no_add = 1; - continue; - } - if (!strcmp(arg, "--stat")) { - apply = 0; - diffstat = 1; - continue; - } - if (!strcmp(arg, "--allow-binary-replacement") || - !strcmp(arg, "--binary")) { - continue; /* now no-op */ - } - if (!strcmp(arg, "--numstat")) { - apply = 0; - numstat = 1; - continue; - } - if (!strcmp(arg, "--summary")) { - apply = 0; - summary = 1; - continue; - } - if (!strcmp(arg, "--check")) { - apply = 0; - check = 1; - continue; - } - if (!strcmp(arg, "--index")) { - if (is_not_gitdir) - die("--index outside a repository"); - check_index = 1; - continue; - } - if (!strcmp(arg, "--cached")) { - if (is_not_gitdir) - die("--cached outside a repository"); - check_index = 1; - cached = 1; - continue; - } - if (!strcmp(arg, "--apply")) { - apply = 1; - continue; - } - if (!strcmp(arg, "--build-fake-ancestor")) { - apply = 0; - if (++i >= argc) - die ("need a filename"); - fake_ancestor = argv[i]; - continue; - } - if (!strcmp(arg, "-z")) { - line_termination = 0; - continue; - } - if (!prefixcmp(arg, "-C")) { - p_context = strtoul(arg + 2, &end, 0); - if (*end != '\0') - die("unrecognized context count '%s'", arg + 2); - continue; - } - if (!prefixcmp(arg, "--whitespace=")) { - whitespace_option = arg + 13; - parse_whitespace_option(arg + 13); - continue; - } - if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) { - apply_in_reverse = 1; - continue; - } - if (!strcmp(arg, "--unidiff-zero")) { - unidiff_zero = 1; - continue; - } - if (!strcmp(arg, "--reject")) { - apply = apply_with_reject = apply_verbosely = 1; - continue; - } - if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) { - apply_verbosely = 1; - continue; - } - if (!strcmp(arg, "--inaccurate-eof")) { - options |= INACCURATE_EOF; - continue; - } - if (!strcmp(arg, "--recount")) { - options |= RECOUNT; - continue; - } - if (!prefixcmp(arg, "--directory=")) { - arg += strlen("--directory="); - root_len = strlen(arg); - if (root_len && arg[root_len - 1] != '/') { - char *new_root; - root = new_root = xmalloc(root_len + 2); - strcpy(new_root, arg); - strcpy(new_root + root_len++, "/"); - } else - root = arg; - continue; - } if (0 < prefix_length) arg = prefix_filename(prefix, prefix_length, arg); From fe73fc1abe785a423fcc51b753d57ffa1485edab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adeodato=20Sim=C3=B3?= Date: Tue, 30 Dec 2008 22:01:44 +0100 Subject: [PATCH 048/231] builtin-shortlog.c: use string_list_append(), and don't strdup unnecessarily MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make insert_one_record() use string_list_append(), instead of duplicating its code. Because of this, do not free the "util" member when clearing the "onelines" string lists: with the new code path it is not initialized to any value (was being initialized to NULL previously). Also, avoid unnecessary strdup() calls when inserting names in log->list. This list always has "strdup_strings" activated, hence strdup'ing namebuf is unnecessary. This change also removes a latent memory leak in the old code. NB: The duplicated code mentioned above predated the appearance of string_list_append(). Signed-off-by: Adeodato Simó Signed-off-by: Junio C Hamano --- builtin-shortlog.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/builtin-shortlog.c b/builtin-shortlog.c index d03f14fdad..90e76ae420 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -36,7 +36,6 @@ static void insert_one_record(struct shortlog *log, const char *dot3 = log->common_repo_prefix; char *buffer, *p; struct string_list_item *item; - struct string_list *onelines; char namebuf[1024]; size_t len; const char *eol; @@ -68,12 +67,9 @@ static void insert_one_record(struct shortlog *log, snprintf(namebuf + len, room, " %.*s", maillen, boemail); } - buffer = xstrdup(namebuf); - item = string_list_insert(buffer, &log->list); + item = string_list_insert(namebuf, &log->list); if (item->util == NULL) item->util = xcalloc(1, sizeof(struct string_list)); - else - free(buffer); /* Skip any leading whitespace, including any blank lines. */ while (*oneline && isspace(*oneline)) @@ -104,16 +100,7 @@ static void insert_one_record(struct shortlog *log, } } - onelines = item->util; - if (onelines->nr >= onelines->alloc) { - onelines->alloc = alloc_nr(onelines->nr); - onelines->items = xrealloc(onelines->items, - onelines->alloc - * sizeof(struct string_list_item)); - } - - onelines->items[onelines->nr].util = NULL; - onelines->items[onelines->nr++].string = buffer; + string_list_append(buffer, item->util); } static void read_from_stdin(struct shortlog *log) @@ -323,7 +310,7 @@ void shortlog_output(struct shortlog *log) } onelines->strdup_strings = 1; - string_list_clear(onelines, 1); + string_list_clear(onelines, 0); free(onelines); log->list.items[i].util = NULL; } From f29680221124d05581dd2db52a4e7f0fa15fa929 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Mon, 29 Dec 2008 18:45:20 +0100 Subject: [PATCH 049/231] git-cherry: make parameter optional The upstream branch now defaults to the first tracked remote branch, which is set by the configuration variables branch..remote and branch..merge of the current branch. Without such a remote branch, the command "git cherry [-v]" fails with usage output as before and an additional message. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- Documentation/git-cherry.txt | 3 ++- builtin-log.c | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt index 74d14c4e7f..556ea23cd9 100644 --- a/Documentation/git-cherry.txt +++ b/Documentation/git-cherry.txt @@ -7,7 +7,7 @@ git-cherry - Find commits not merged upstream SYNOPSIS -------- -'git cherry' [-v] [] [] +'git cherry' [-v] [] [] [] DESCRIPTION ----------- @@ -51,6 +51,7 @@ OPTIONS :: Upstream branch to compare against. + Defaults to the first tracked remote branch, if available. :: Working branch; defaults to HEAD. diff --git a/builtin-log.c b/builtin-log.c index 99d1137b08..243f8573e4 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -16,6 +16,7 @@ #include "patch-ids.h" #include "run-command.h" #include "shortlog.h" +#include "remote.h" /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@ -1070,13 +1071,14 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) } static const char cherry_usage[] = -"git cherry [-v] [] []"; +"git cherry [-v] [] [] []"; int cmd_cherry(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct patch_ids ids; struct commit *commit; struct commit_list *list = NULL; + struct branch *current_branch; const char *upstream; const char *head = "HEAD"; const char *limit = NULL; @@ -1099,7 +1101,17 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) upstream = argv[1]; break; default: - usage(cherry_usage); + current_branch = branch_get(NULL); + if (!current_branch || !current_branch->merge + || !current_branch->merge[0] + || !current_branch->merge[0]->dst) { + fprintf(stderr, "Could not find a tracked" + " remote branch, please" + " specify manually.\n"); + usage(cherry_usage); + } + + upstream = current_branch->merge[0]->dst; } init_revisions(&revs, prefix); From e1a59774077e21fc9862a968440dd23003345b4a Mon Sep 17 00:00:00 2001 From: Nanako Shiraishi Date: Wed, 31 Dec 2008 19:00:50 +0900 Subject: [PATCH 050/231] Document git-ls-tree --full-tree Signed-off-by: Nanako Shiraishi Signed-off-by: Junio C Hamano --- Documentation/git-ls-tree.txt | 8 +++++++- builtin-ls-tree.c | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index 4c7262f1cd..db6ebccd6d 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git ls-tree' [-d] [-r] [-t] [-l] [-z] - [--name-only] [--name-status] [--full-name] [--abbrev=[]] + [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev=[]] [paths...] DESCRIPTION @@ -30,6 +30,8 @@ in the current working directory. Note that: 'sub/dir' in 'HEAD'). You don't want to give a tree that is not at the root level (e.g. 'git ls-tree -r HEAD:sub dir') in this case, as that would result in asking for 'sub/sub/dir' in the 'HEAD' commit. + However, the current working directory can be ignored by passing + --full-tree option. OPTIONS ------- @@ -66,6 +68,10 @@ OPTIONS Instead of showing the path names relative to the current working directory, show the full path names. +--full-tree:: + Do not limit the listing to the current working directory. + Implies --full-name. + paths:: When paths are given, show them (note that this isn't really raw pathnames, but rather a list of patterns to match). Otherwise diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index c386aa5754..5b63e6eada 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -23,7 +23,7 @@ static int chomp_prefix; static const char *ls_tree_prefix; static const char ls_tree_usage[] = - "git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=]] [path...]"; + "git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=]] [path...]"; static int show_recursive(const char *base, int baselen, const char *pathname) { From 8e8daf33632d14f01d342b4f89d8afceecdd606b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Dec 2008 14:45:11 -0500 Subject: [PATCH 051/231] objects to be pruned immediately don't have to be loosened When there is no grace period before pruning unreferenced objects, it is pointless to push those objects in their loose form just to delete them right away. Also be more explicit about the possibility of using "now" in the gc.pruneexpire config variable (needed for the above behavior to happen). Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 +++- builtin-gc.c | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 52786c7df5..41dd0f3e9f 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -702,7 +702,9 @@ gc.packrefs:: gc.pruneexpire:: When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'. - Override the grace period with this config variable. + Override the grace period with this config variable. The value + "now" may be used to disable this grace period and always prune + unreachable objects immediately. gc.reflogexpire:: 'git-reflog expire' removes reflog entries older than diff --git a/builtin-gc.c b/builtin-gc.c index 781df601c5..f8eae4adb4 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -188,7 +188,9 @@ static int need_to_gc(void) * there is no need. */ if (too_many_packs()) - append_option(argv_repack, "-A", MAX_ADD); + append_option(argv_repack, + !strcmp(prune_expire, "now") ? "-a" : "-A", + MAX_ADD); else if (!too_many_loose_objects()) return 0; @@ -243,7 +245,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix) "run \"git gc\" manually. See " "\"git help gc\" for more information.\n"); } else - append_option(argv_repack, "-A", MAX_ADD); + append_option(argv_repack, + !strcmp(prune_expire, "now") ? "-a" : "-A", + MAX_ADD); if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) return error(FAILED_RUN, argv_pack_refs[0]); From d99bf51add655b749c5c7f94cecb337c7ef8aadb Mon Sep 17 00:00:00 2001 From: "jidanni@jidanni.org" Date: Sun, 28 Dec 2008 03:49:03 +0800 Subject: [PATCH 052/231] Documentation/git-tag.txt: minor typo and grammar fix Signed-off-by: jidanni Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 046ab3542b..e44f543025 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -70,7 +70,7 @@ OPTIONS -m :: Use the given tag message (instead of prompting). - If multiple `-m` options are given, there values are + If multiple `-m` options are given, their values are concatenated as separate paragraphs. Implies `-a` if none of `-a`, `-s`, or `-u ` is given. @@ -207,7 +207,7 @@ determines who are interested in whose tags. A one-shot pull is a sign that a commit history is now crossing the boundary between one circle of people (e.g. "people who are -primarily interested in networking part of the kernel") who may +primarily interested in the networking part of the kernel") who may have their own set of tags (e.g. "this is the third release candidate from the networking group to be proposed for general consumption with 2.6.21 release") to another circle of people From 11b8a41c4569e99a57137cb2db4b642b57171797 Mon Sep 17 00:00:00 2001 From: "Philippe Bruhat (BooK)" Date: Mon, 29 Dec 2008 01:25:00 +0100 Subject: [PATCH 053/231] Git.pm: correctly handle directory name that evaluates to "false" The repository constructor mistakenly rewrote a Directory parameter that Perl happens to evaluate to false (e.g. "0") to ".". Signed-off-by: Junio C Hamano --- perl/Git.pm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/perl/Git.pm b/perl/Git.pm index 8392a68333..ad0f530445 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -166,11 +166,12 @@ sub repository { } } - if (not defined $opts{Repository} and not defined $opts{WorkingCopy}) { - $opts{Directory} ||= '.'; + if (not defined $opts{Repository} and not defined $opts{WorkingCopy} + and not defined $opts{Directory}) { + $opts{Directory} = '.'; } - if ($opts{Directory}) { + if (defined $opts{Directory}) { -d $opts{Directory} or throw Error::Simple("Directory not found: $!"); my $search = Git->repository(WorkingCopy => $opts{Directory}); From cca1704897e7fdb182f68d4c48a437c5d7bc5203 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 2 Jan 2009 19:07:52 +0100 Subject: [PATCH 054/231] git wrapper: Make while loop more reader-friendly It is not a good practice to prefer performance over readability in something as performance uncritical as finding the trailing slash of argv[0]. So avoid head-scratching by making the loop user-readable, and not hyper-performance-optimized. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/git.c b/git.c index 940a498962..e0d9071358 100644 --- a/git.c +++ b/git.c @@ -428,9 +428,8 @@ int main(int argc, const char **argv) * name, and the dirname as the default exec_path * if we don't have anything better. */ - do - --slash; - while (cmd <= slash && !is_dir_sep(*slash)); + while (cmd <= slash && !is_dir_sep(*slash)) + slash--; if (cmd <= slash) { *slash++ = 0; git_set_argv0_path(cmd); From 1f7903a371f90f025cfc3b4aa3a0b4a8f8b05b94 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Jan 2009 02:55:37 -0800 Subject: [PATCH 055/231] builtin-apply: prevent non-explicit permission changes A git patch that does not change the executable bit records the mode bits on its "index" line. "git apply" used to interpret this mode exactly the same way as it interprets the mode recorded on "new mode" line, as the wish by the patch submitter to set the mode to the one recorded on the line. The reason the mode does not agree between the submitter and the receiver in the first place is because there is _another_ commit that only appears on one side but not the other since their histories diverged, and that commit changes the mode. The patch has "index" line but not "new mode" line because its change is about updating the contents without affecting the mode. The application of such a patch is an explicit wish by the submitter to only cherry-pick the commit that updates the contents without cherry-picking the commit that modifies the mode. Viewed this way, the current behaviour is problematic, even though the command does warn when the mode of the path being patched does not match this mode, and a careful user could detect this inconsistencies between the patch submitter and the patch receiver. This changes the semantics of the mode recorded on the "index" line; instead of interpreting it as the submitter's wish to set the mode to the recorded value, it merely informs what the mode submitter happened to have, and the presense of the "index" line is taken as submitter's wish to keep whatever the mode is on the receiving end. This is based on the patch originally done by Alexander Potashev with a minor fix; the tests are mine. Signed-off-by: Junio C Hamano --- builtin-apply.c | 4 ++- t/t4129-apply-samemode.sh | 62 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100755 t/t4129-apply-samemode.sh diff --git a/builtin-apply.c b/builtin-apply.c index c497889312..66437ed751 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -609,7 +609,7 @@ static int gitdiff_index(const char *line, struct patch *patch) memcpy(patch->new_sha1_prefix, line, len); patch->new_sha1_prefix[len] = 0; if (*ptr == ' ') - patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8); + patch->old_mode = strtoul(ptr+1, NULL, 8); return 0; } @@ -2316,6 +2316,8 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s if (st_mode != patch->old_mode) fprintf(stderr, "warning: %s has type %o, expected %o\n", old_name, st_mode, patch->old_mode); + if (!patch->new_mode) + patch->new_mode = st_mode; return 0; is_new: diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh new file mode 100755 index 0000000000..adfcbb5a3b --- /dev/null +++ b/t/t4129-apply-samemode.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +test_description='applying patch with mode bits' + +. ./test-lib.sh + +test_expect_success setup ' + echo original >file && + git add file && + test_tick && + git commit -m initial && + git tag initial && + echo modified >file && + git diff --stat -p >patch-0.txt && + chmod +x file && + git diff --stat -p >patch-1.txt +' + +test_expect_success 'same mode (no index)' ' + git reset --hard && + chmod +x file && + git apply patch-0.txt && + test -x file +' + +test_expect_success 'same mode (with index)' ' + git reset --hard && + chmod +x file && + git add file && + git apply --index patch-0.txt && + test -x file && + git diff --exit-code +' + +test_expect_success 'same mode (index only)' ' + git reset --hard && + chmod +x file && + git add file && + git apply --cached patch-0.txt && + git ls-files -s file | grep "^100755" +' + +test_expect_success 'mode update (no index)' ' + git reset --hard && + git apply patch-1.txt && + test -x file +' + +test_expect_success 'mode update (with index)' ' + git reset --hard && + git apply --index patch-1.txt && + test -x file && + git diff --exit-code +' + +test_expect_success 'mode update (index only)' ' + git reset --hard && + git apply --cached patch-1.txt && + git ls-files -s file | grep "^100755" +' + +test_done From 3442ea4a75a3c76f65efac71b414584f765e5a99 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 3 Jan 2009 04:07:32 -0800 Subject: [PATCH 056/231] git checkout: do not allow switching to a tree-ish that is not a commit "git checkout -b newbranch $commit^{tree}" mistakenly created a new branch rooted at the current HEAD, because in that case, the two structure fields used to see if the command was invoked without any argument (hence it needs to default to checking out the HEAD) were populated incorrectly. Upon seeing a command line argument that we took as a rev, we should store that string in new.name, even if that does not name a commit. This will correctly trigger the existing safety logic. Signed-off-by: Junio C Hamano Acked-by: Daniel Barkalow --- builtin-checkout.c | 2 +- t/t2011-checkout-invalid-head.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index c107fd643a..544aa5add1 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -515,8 +515,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argv++; argc--; + new.name = arg; if ((new.commit = lookup_commit_reference_gently(rev, 1))) { - new.name = arg; setup_branch_path(&new); if (resolve_ref(new.path, rev, 1, NULL)) new.commit = lookup_commit_reference(rev); diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh index 764bb0a6bc..15ebdc26eb 100755 --- a/t/t2011-checkout-invalid-head.sh +++ b/t/t2011-checkout-invalid-head.sh @@ -10,6 +10,10 @@ test_expect_success 'setup' ' git commit -m initial ' +test_expect_success 'checkout should not start branch from a tree' ' + test_must_fail git checkout -b newbranch master^{tree} +' + test_expect_success 'checkout master from invalid HEAD' ' echo 0000000000000000000000000000000000000000 >.git/HEAD && git checkout master -- From 280514e1dff3284549175cb0e8edb9b796e8891e Mon Sep 17 00:00:00 2001 From: Fabian Emmes Date: Fri, 2 Jan 2009 16:40:13 +0100 Subject: [PATCH 057/231] cvsserver: add option to configure commit message cvsserver annotates each commit message by "via git-CVS emulator". This is made configurable via gitcvs.commitmsgannotation. Signed-off-by: Fabian Emmes Signed-off-by: Lars Noschinski Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++++ git-cvsserver.perl | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 52786c7df5..b4e0eea9f7 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -723,6 +723,10 @@ gc.rerereunresolved:: kept for this many days when 'git-rerere gc' is run. The default is 15 days. See linkgit:git-rerere[1]. +gitcvs.commitmsgannotation:: + Append this string to each commit message. Set to empty string + to disable this feature. Defaults to "via git-CVS emulator". + gitcvs.enabled:: Whether the CVS server interface is enabled for this repository. See linkgit:git-cvsserver[1]. diff --git a/git-cvsserver.perl b/git-cvsserver.perl index b0a805c688..cbcaeb44e4 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1358,7 +1358,13 @@ sub req_ci # write our commit message out if we have one ... my ( $msg_fh, $msg_filename ) = tempfile( DIR => $TEMP_DIR ); print $msg_fh $state->{opt}{m};# if ( exists ( $state->{opt}{m} ) ); - print $msg_fh "\n\nvia git-CVS emulator\n"; + if ( defined ( $cfg->{gitcvs}{commitmsgannotation} ) ) { + if ($cfg->{gitcvs}{commitmsgannotation} !~ /^\s*$/ ) { + print $msg_fh "\n\n".$cfg->{gitcvs}{commitmsgannotation}."\n" + } + } else { + print $msg_fh "\n\nvia git-CVS emulator\n"; + } close $msg_fh; my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`; From d8fab0234d6db440a71f38f8538dbdefbbdfd077 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Fri, 2 Jan 2009 23:28:26 +0100 Subject: [PATCH 058/231] rebase -i: execute hook only after argument checking Previously, the pre-rebase-hook would be launched before we knew if the [] arguments were supplied. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c8b0861c08..2c668cd81a 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -554,7 +554,6 @@ first and then run 'git rebase --continue' again." ;; --) shift - run_pre_rebase_hook ${1+"$@"} test $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" @@ -562,11 +561,13 @@ first and then run 'git rebase --continue' again." git var GIT_COMMITTER_IDENT >/dev/null || die "You need to set your committer info first" + UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" + run_pre_rebase_hook ${1+"$@"} + comment_for_reflog start require_clean_work_tree - UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" test -z "$ONTO" && ONTO=$UPSTREAM if test ! -z "$2" From dcfdbdf08bbe102bd4806f3c92fb0e43f53a623a Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sat, 3 Jan 2009 04:59:12 +0100 Subject: [PATCH 059/231] fast-export: print usage when no options specified Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- builtin-fast-export.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin-fast-export.c b/builtin-fast-export.c index 838633808c..e9ee2c79ac 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -497,6 +497,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) OPT_END() }; + if (argc == 1) + usage_with_options (fast_export_usage, options); + /* we handle encodings */ git_config(git_default_config, NULL); From 22b3ddd508b022c76bf36bf390691a6f62c996d5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 2 Jan 2009 19:08:00 +0100 Subject: [PATCH 060/231] bisect view: call gitk if Cygwin's SESSIONNAME variable is set It seems that Cygwin sets the variable SESSIONNAME when an interactive desktop session is running, and does not set it when you log in via ssh. So we can use this variable to determine whether to run gitk or git log in git bisect view. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-bisect.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-bisect.sh b/git-bisect.sh index 17a35f6adc..85db4ba400 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -508,7 +508,7 @@ bisect_visualize() { if test $# = 0 then - case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in + case "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in '') set git log ;; set*) set gitk ;; esac From df63fbbf46c5ec855132f4c631c32b45f67b42e4 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Fri, 2 Jan 2009 13:15:28 +0100 Subject: [PATCH 061/231] gitweb: use href() when generating URLs in OPML Since the OPML project list view was hand-coding the RSS and HTML URLs, it didn't respect global options such as use_pathinfo. Make it use href() to ensure consistency with the rest of the gitweb setup. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 99f71b47c2..7999bb37d3 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -6146,8 +6146,8 @@ XML } my $path = esc_html(chop_str($proj{'path'}, 25, 5)); - my $rss = "$my_url?p=$proj{'path'};a=rss"; - my $html = "$my_url?p=$proj{'path'};a=summary"; + my $rss = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1); + my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1); print "\n"; } print < Date: Fri, 2 Jan 2009 19:08:46 +0100 Subject: [PATCH 062/231] bundle: allow rev-list options to exclude annotated tags With options such as "--all --since=2.weeks.ago", annotated tags used to be included, when they should have been excluded. The reason is that we heavily abuse the revision walker to determine what needs to be included or excluded. And the revision walker does not show tags at all (and therefore never marks tags as uninteresting). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- bundle.c | 32 ++++++++++++++++++++++++++++++++ t/t5704-bundle.sh | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100755 t/t5704-bundle.sh diff --git a/bundle.c b/bundle.c index daecd8e1ca..4977962eb5 100644 --- a/bundle.c +++ b/bundle.c @@ -167,6 +167,32 @@ int list_bundle_refs(struct bundle_header *header, int argc, const char **argv) return list_refs(&header->references, argc, argv); } +static int is_tag_in_date_range(struct object *tag, struct rev_info *revs) +{ + unsigned long size; + enum object_type type; + char *buf, *line, *lineend; + unsigned long date; + + if (revs->max_age == -1 && revs->min_age == -1) + return 1; + + buf = read_sha1_file(tag->sha1, &type, &size); + if (!buf) + return 1; + line = memmem(buf, size, "\ntagger ", 8); + if (!line++) + return 1; + lineend = memchr(line, buf + size - line, '\n'); + line = memchr(line, lineend ? lineend - line : buf + size - line, '>'); + if (!line++) + return 1; + date = strtoul(line, NULL, 10); + free(buf); + return (revs->max_age == -1 || revs->max_age < date) && + (revs->min_age == -1 || revs->min_age > date); +} + int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { @@ -255,6 +281,12 @@ int create_bundle(struct bundle_header *header, const char *path, flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; + if (e->item->type == OBJ_TAG && + !is_tag_in_date_range(e->item, &revs)) { + e->item->flags |= UNINTERESTING; + continue; + } + /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh new file mode 100755 index 0000000000..a8f4419e61 --- /dev/null +++ b/t/t5704-bundle.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +test_description='some bundle related tests' +. ./test-lib.sh + +test_expect_success 'setup' ' + + : > file && + git add file && + test_tick && + git commit -m initial && + test_tick && + git tag -m tag tag && + : > file2 && + git add file2 && + : > file3 && + test_tick && + git commit -m second && + git add file3 && + test_tick && + git commit -m third + +' + +test_expect_success 'tags can be excluded by rev-list options' ' + + git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 && + git ls-remote bundle > output && + ! grep tag output + +' + +test_done From 2ce53f9b77eb36c6d1286f55fba65f8dc66b2564 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 2 Jan 2009 19:08:40 +0100 Subject: [PATCH 063/231] git add: do not add files from a submodule It comes quite as a surprise to an unsuspecting Git user that calling "git add submodule/file" (which is a mistake, alright) _removes_ the submodule in the index, and adds the file. Instead, complain loudly. While at it, be nice when the user said "git add submodule/" which is most likely the consequence of tab-completion, and stage the submodule, instead of trying to add the contents of that directory. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-add.c | 28 ++++++++++++++++++++++++++++ t/t7400-submodule-basic.sh | 25 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/builtin-add.c b/builtin-add.c index 719de8b0f2..ac98c8354d 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -68,6 +68,33 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p free(seen); } +static void treat_gitlinks(const char **pathspec) +{ + int i; + + if (!pathspec || !*pathspec) + return; + + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (S_ISGITLINK(ce->ce_mode)) { + int len = ce_namelen(ce), j; + for (j = 0; pathspec[j]; j++) { + int len2 = strlen(pathspec[j]); + if (len2 <= len || pathspec[j][len] != '/' || + memcmp(ce->name, pathspec[j], len)) + continue; + if (len2 == len + 1) + /* strip trailing slash */ + pathspec[j] = xstrndup(ce->name, len); + else + die ("Path '%s' is in submodule '%.*s'", + pathspec[j], len, ce->name); + } + } + } +} + static void fill_directory(struct dir_struct *dir, const char **pathspec, int ignored_too) { @@ -261,6 +288,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); + treat_gitlinks(pathspec); if (add_new_files) /* This picks up the paths that are not tracked */ diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index be73f7b60a..2ec7ac6a51 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -209,4 +209,29 @@ test_expect_success 'update --init' ' ' +test_expect_success 'do not add files from a submodule' ' + + git reset --hard && + test_must_fail git add init/a + +' + +test_expect_success 'gracefully add submodule with a trailing slash' ' + + git reset --hard && + git commit -m "commit subproject" init && + (cd init && + echo b > a) && + git add init/ && + git diff --exit-code --cached init && + commit=$(cd init && + git commit -m update a >/dev/null && + git rev-parse HEAD) && + git add init/ && + test_must_fail git diff --exit-code --cached init && + test $commit = $(git ls-files --stage | + sed -n "s/^160000 \([^ ]*\).*/\1/p") + +' + test_done From ea718e65fab17297a8a9dbebf63ae99096e9537a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 2 Jan 2009 19:08:43 +0100 Subject: [PATCH 064/231] show : reuse pp_user_info() instead of duplicating code We used to extract the tagger information "by hand" in "git show ", but the function pp_user_info() already does that. Even better: it respects the commit_format and date_format specified by the user. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index 99d1137b08..bc4e1e9654 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -249,22 +249,13 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) static void show_tagger(char *buf, int len, struct rev_info *rev) { - char *email_end, *p; - unsigned long date; - int tz; + struct strbuf out = STRBUF_INIT; - email_end = memchr(buf, '>', len); - if (!email_end) - return; - p = ++email_end; - while (isspace(*p)) - p++; - date = strtoul(p, &p, 10); - while (isspace(*p)) - p++; - tz = (int)strtol(p, NULL, 10); - printf("Tagger: %.*s\nDate: %s\n", (int)(email_end - buf), buf, - show_date(date, tz, rev->date_mode)); + pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode, + git_log_output_encoding ? + git_log_output_encoding: git_commit_encoding); + printf("%s\n", out.buf); + strbuf_release(&out); } static int show_object(const unsigned char *sha1, int show_tag_object, From e89e2ed7c225cf16cffbd9648895528e471e2fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 29 Dec 2008 16:05:46 +0100 Subject: [PATCH 065/231] bash: add '--merge' to 'git reset' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..3b25d48098 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1403,7 +1403,7 @@ _git_reset () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--mixed --hard --soft" + __gitcomp "--merge --mixed --hard --soft" return ;; esac From d500a1ee8fe4424beb7a98e4fa6159677e7569d0 Mon Sep 17 00:00:00 2001 From: Fabian Emmes Date: Fri, 2 Jan 2009 16:40:14 +0100 Subject: [PATCH 066/231] cvsserver: change generation of CVS author names CVS username is generated from local part email address. We take the whole local part but restrict the character set to the Portable Filename Character Set, which is used for Unix login names according to Single Unix Specification v3. This will obviously report different usernames from existing repositories for commits with the local part of the author e-mail address that contains characters outside the PFCS. Hopefully this won't break an old CVS checkout from an earlier version of git-cvsserver, because the names are always shown afresh to the CVS clients and not kept on the client side. Signed-off-by: Fabian Emmes Signed-off-by: Lars Noschinski Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index cbcaeb44e4..fef7faf339 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2533,12 +2533,18 @@ sub open_blob_or_die return $fh; } -# Generate a CVS author name from Git author information, by taking -# the first eight characters of the user part of the email address. +# Generate a CVS author name from Git author information, by taking the local +# part of the email address and replacing characters not in the Portable +# Filename Character Set (see IEEE Std 1003.1-2001, 3.276) by underscores. CVS +# Login names are Unix login names, which should be restricted to this +# character set. sub cvs_author { my $author_line = shift; - (my $author) = $author_line =~ /<([^>@]{1,8})/; + (my $author) = $author_line =~ /<([^@>]*)/; + + $author =~ s/[^-a-zA-Z0-9_.]/_/g; + $author =~ s/^-/_/; $author; } From 3bc52d7a95676bcdeeb61c372b44358daf817baa Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Thu, 1 Jan 2009 22:56:29 +0100 Subject: [PATCH 067/231] Documentation: clarify which parameters are optional to git-cherry An earlier parameter is only optional when all of the later parameters are omitted. Signed-off-by: Junio C Hamano --- Documentation/git-cherry.txt | 2 +- builtin-log.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt index 556ea23cd9..7deefdae8f 100644 --- a/Documentation/git-cherry.txt +++ b/Documentation/git-cherry.txt @@ -7,7 +7,7 @@ git-cherry - Find commits not merged upstream SYNOPSIS -------- -'git cherry' [-v] [] [] [] +'git cherry' [-v] [ [ []]] DESCRIPTION ----------- diff --git a/builtin-log.c b/builtin-log.c index 243f8573e4..7e9616e5e7 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -1071,7 +1071,7 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) } static const char cherry_usage[] = -"git cherry [-v] [] [] []"; +"git cherry [-v] [ [ []]]"; int cmd_cherry(int argc, const char **argv, const char *prefix) { struct rev_info revs; From dd6c1360b22ee89cb179e2a1fface98ecbeb7b3e Mon Sep 17 00:00:00 2001 From: "Marcel M. Cary" Date: Tue, 30 Dec 2008 07:10:24 -0800 Subject: [PATCH 068/231] git-sh-setup: Fix scripts whose PWD is a symlink to a work-dir on OS X On Mac OS X and possibly BSDs, /bin/pwd reads PWD from the environment if available and shows the logical path by default rather than the physical one. Unset PWD before running /bin/pwd in both cd_to_toplevel and its test. Still use the external /bin/pwd because in my Bash on Linux, the builtin pwd prints the same result whether or not PWD is set. Signed-off-by: Marcel M. Cary Tested-by: Wincent Colaiuta (on Mac OS X 10.5.5) Tested-by: Marcel Koeppen (on Mac OS X 10.5.6) Signed-off-by: Junio C Hamano --- git-sh-setup.sh | 2 +- t/t2300-cd-to-toplevel.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index f07d96b9b5..2142308bcc 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -96,7 +96,7 @@ cd_to_toplevel () { ..|../*|*/..|*/../*) # Interpret $cdup relative to the physical, not logical, cwd. # Probably /bin/pwd is more portable than passing -P to cd or pwd. - phys="$(/bin/pwd)/$cdup" + phys="$(unset PWD; /bin/pwd)/$cdup" ;; *) # There's no "..", so no need to make things absolute. diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh index beddb4e9f2..e42cbfe6c6 100755 --- a/t/t2300-cd-to-toplevel.sh +++ b/t/t2300-cd-to-toplevel.sh @@ -10,12 +10,12 @@ test_cd_to_toplevel () { cd '"'$1'"' && . git-sh-setup && cd_to_toplevel && - [ "$(/bin/pwd)" = "$TOPLEVEL" ] + [ "$(unset PWD; /bin/pwd)" = "$TOPLEVEL" ] ) ' } -TOPLEVEL="$(/bin/pwd)/repo" +TOPLEVEL="$(unset PWD; /bin/pwd)/repo" mkdir -p repo/sub/dir mv .git repo/ SUBDIRECTORY_OK=1 From c7719fbe46deae03578c4e19516b9567e98b9f04 Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Mon, 5 Jan 2009 16:25:36 +0100 Subject: [PATCH 069/231] Use capitalized names where appropriate The Linux kernel and Emacs are both spelled capitalized Signed-off-by: Henrik Austad Signed-off-by: Junio C Hamano --- Documentation/gittutorial.txt | 4 ++-- Documentation/user-manual.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt index 7892244ef1..458fafdb2c 100644 --- a/Documentation/gittutorial.txt +++ b/Documentation/gittutorial.txt @@ -590,7 +590,7 @@ list. When the history has lines of development that diverged and then merged back together, the order in which 'git-log' presents those commits is meaningless. -Most projects with multiple contributors (such as the linux kernel, +Most projects with multiple contributors (such as the Linux kernel, or git itself) have frequent merges, and 'gitk' does a better job of visualizing their history. For example, @@ -642,7 +642,7 @@ digressions that may be interesting at this point are: * linkgit:git-format-patch[1], linkgit:git-am[1]: These convert series of git commits into emailed patches, and vice versa, - useful for projects such as the linux kernel which rely heavily + useful for projects such as the Linux kernel which rely heavily on emailed patches. * linkgit:git-bisect[1]: When there is a regression in your diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index d4b1e90f94..5242a7e97b 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -59,7 +59,7 @@ project in mind, here are some interesting examples: ------------------------------------------------ # git itself (approx. 10MB download): $ git clone git://git.kernel.org/pub/scm/git/git.git - # the linux kernel (approx. 150MB download): + # the Linux kernel (approx. 150MB download): $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git ------------------------------------------------ @@ -1340,7 +1340,7 @@ These will display all commits which exist only on HEAD or on MERGE_HEAD, and which touch an unmerged file. You may also use linkgit:git-mergetool[1], which lets you merge the -unmerged files using external tools such as emacs or kdiff3. +unmerged files using external tools such as Emacs or kdiff3. Each time you resolve the conflicts in a file and update the index: From 0ddd93b2717d4da074485d42a08e4d3824580afe Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Mon, 5 Jan 2009 16:25:37 +0100 Subject: [PATCH 070/231] Be consistent in switch usage for tar tar handles switches with and witout preceding '-', but the documentation should be consistent nonetheless. Signed-off-by: Henrik Austad Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 5242a7e97b..19f571ae3b 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1009,7 +1009,7 @@ $ git init If you have some initial content (say, a tarball): ------------------------------------------------- -$ tar -xzvf project.tar.gz +$ tar xzvf project.tar.gz $ cd project $ git init $ git add . # include everything below ./ in the first commit: From 6b9315d5a14b4d1389cff347fcfcb86624bbf645 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Thu, 1 Jan 2009 21:54:31 +0100 Subject: [PATCH 071/231] unpack-trees: handle failure in verify_absent Commit 203a2fe1 (Allow callers of unpack_trees() to handle failure) changed the "die on error" behavior to "return failure code". verify_absent did not handle errors returned by verify_clean_subdirectory, however. Signed-off-by: Junio C Hamano --- t/t1001-read-tree-m-2way.sh | 24 ++++++++++++++++++++++++ unpack-trees.c | 8 +++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index 4b44e131b2..7f6ab31c36 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -341,4 +341,28 @@ test_expect_success \ check_cache_at DF/DF dirty && :' +test_expect_success \ + 'a/b (untracked) vs a case setup.' \ + 'rm -f .git/index && + : >a && + git update-index --add a && + treeM=`git write-tree` && + echo treeM $treeM && + git ls-tree $treeM && + git ls-files --stage >treeM.out && + + rm -f a && + git update-index --remove a && + mkdir a && + : >a/b && + treeH=`git write-tree` && + echo treeH $treeH && + git ls-tree $treeH' + +test_expect_success \ + 'a/b (untracked) vs a, plus c/d case test.' \ + '! git read-tree -u -m "$treeH" "$treeM" && + git ls-files --stage && + test -f a/b' + test_done diff --git a/unpack-trees.c b/unpack-trees.c index cba0aca062..3f42c2997f 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -587,7 +587,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, return 0; if (!lstat(ce->name, &st)) { - int cnt; + int ret; int dtype = ce_to_dtype(ce); struct cache_entry *result; @@ -615,7 +615,9 @@ static int verify_absent(struct cache_entry *ce, const char *action, * files that are in "foo/" we would lose * it. */ - cnt = verify_clean_subdirectory(ce, action, o); + ret = verify_clean_subdirectory(ce, action, o); + if (ret < 0) + return ret; /* * If this removed entries from the index, @@ -634,7 +636,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, * We need to increment it by the number of * deleted entries here. */ - o->pos += cnt; + o->pos += ret; return 0; } From 837e5fe95d9d6e597b748d338e45cca231f558fa Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Thu, 1 Jan 2009 21:54:32 +0100 Subject: [PATCH 072/231] unpack-trees: fix path search bug in verify_absent Commit 0cf73755 (unpack-trees.c: assume submodules are clean during check-out) changed an argument to verify_absent from 'path' to 'ce', which is however shadowed by a local variable of the same name. The bug triggers if verify_absent is used on a tree entry, for which the index contains one or more subsequent directories of the same length. The affected subdirectories are removed from the index. The testcase included in this commit bisects to 55218834 (checkout: do not lose staged removal), which reveals the bug in this case, but is otherwise unrelated. Signed-off-by: Junio C Hamano --- t/t1001-read-tree-m-2way.sh | 27 +++++++++++++++++++++++++++ unpack-trees.c | 20 ++++++++++---------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index 7f6ab31c36..271bc4e17f 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -365,4 +365,31 @@ test_expect_success \ git ls-files --stage && test -f a/b' +test_expect_success \ + 'a/b vs a, plus c/d case setup.' \ + 'rm -f .git/index && + rm -fr a && + : >a && + mkdir c && + : >c/d && + git update-index --add a c/d && + treeM=`git write-tree` && + echo treeM $treeM && + git ls-tree $treeM && + git ls-files --stage >treeM.out && + + rm -f a && + mkdir a + : >a/b && + git update-index --add --remove a a/b && + treeH=`git write-tree` && + echo treeH $treeH && + git ls-tree $treeH' + +test_expect_success \ + 'a/b vs a, plus c/d case test.' \ + 'git read-tree -u -m "$treeH" "$treeM" && + git ls-files --stage | tee >treeMcheck.out && + test_cmp treeM.out treeMcheck.out' + test_done diff --git a/unpack-trees.c b/unpack-trees.c index 3f42c2997f..48e538ab7e 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -516,22 +516,22 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, namelen = strlen(ce->name); pos = index_name_pos(o->src_index, ce->name, namelen); if (0 <= pos) - return cnt; /* we have it as nondirectory */ + return 0; /* we have it as nondirectory */ pos = -pos - 1; for (i = pos; i < o->src_index->cache_nr; i++) { - struct cache_entry *ce = o->src_index->cache[i]; - int len = ce_namelen(ce); + struct cache_entry *ce2 = o->src_index->cache[i]; + int len = ce_namelen(ce2); if (len < namelen || - strncmp(ce->name, ce->name, namelen) || - ce->name[namelen] != '/') + strncmp(ce->name, ce2->name, namelen) || + ce2->name[namelen] != '/') break; /* - * ce->name is an entry in the subdirectory. + * ce2->name is an entry in the subdirectory. */ - if (!ce_stage(ce)) { - if (verify_uptodate(ce, o)) + if (!ce_stage(ce2)) { + if (verify_uptodate(ce2, o)) return -1; - add_entry(o, ce, CE_REMOVE, 0); + add_entry(o, ce2, CE_REMOVE, 0); } cnt++; } @@ -623,7 +623,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, * If this removed entries from the index, * what that means is: * - * (1) the caller unpack_trees_rec() saw path/foo + * (1) the caller unpack_callback() saw path/foo * in the index, and it has not removed it because * it thinks it is handling 'path' as blob with * D/F conflict; From 7b9e3ce0255002075809917e0384cba3a0d18589 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Thu, 1 Jan 2009 21:54:33 +0100 Subject: [PATCH 073/231] unpack-trees: remove redundant path search in verify_absent Since the only caller, verify_absent, relies on the fact that o->pos points to the next index entry anyways, there is no need to recompute its position. Furthermore, if a nondirectory entry were found, this would return too early, because there could still be an untracked directory in the way. This is currently not a problem, because verify_absent is only called if the index does not have this entry. Signed-off-by: Junio C Hamano --- unpack-trees.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index 48e538ab7e..556813eca8 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -493,7 +493,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, * anything in the existing directory there. */ int namelen; - int pos, i; + int i; struct dir_struct d; char *pathbuf; int cnt = 0; @@ -514,11 +514,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, * in that directory. */ namelen = strlen(ce->name); - pos = index_name_pos(o->src_index, ce->name, namelen); - if (0 <= pos) - return 0; /* we have it as nondirectory */ - pos = -pos - 1; - for (i = pos; i < o->src_index->cache_nr; i++) { + for (i = o->pos; i < o->src_index->cache_nr; i++) { struct cache_entry *ce2 = o->src_index->cache[i]; int len = ce_namelen(ce2); if (len < namelen || From a907e1b6ec3038306df68dc1275edad7457bba4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adeodato=20Sim=C3=B3?= Date: Sun, 4 Jan 2009 18:16:01 +0100 Subject: [PATCH 074/231] git.c: make autocorrected aliases work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit help_unknown_cmd() is able to autocorrect a command to an alias, and not only to internal or external commands. However, main() was not passing the autocorrected command through handle_alias(), hence it failed if it was an alias. This commit makes the autocorrected command go through handle_alias(), once handle_internal_command() and execv_dashed_external() have been tried. Since this is done twice in main() now, moved that logic to a new run_argv() function. Also, print the same "Expansion of alias 'x' failed" message when the alias was autocorrected, rather than a generic "Failed to run command 'x'". Signed-off-by: Adeodato Simó Signed-off-by: Junio C Hamano --- git.c | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/git.c b/git.c index e0d9071358..ee331aabc6 100644 --- a/git.c +++ b/git.c @@ -416,12 +416,34 @@ static void execv_dashed_external(const char **argv) strbuf_release(&cmd); } +static int run_argv(int *argcp, const char ***argv) +{ + int done_alias = 0; + + while (1) { + /* See if it's an internal command */ + handle_internal_command(*argcp, *argv); + + /* .. then try the external ones */ + execv_dashed_external(*argv); + + /* It could be an alias -- this works around the insanity + * of overriding "git log" with "git show" by having + * alias.log = show + */ + if (done_alias || !handle_alias(argcp, argv)) + break; + done_alias = 1; + } + + return done_alias; +} + int main(int argc, const char **argv) { const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help"; char *slash = (char *)cmd + strlen(cmd); - int done_alias = 0; /* * Take the basename of argv[0] as the command @@ -479,31 +501,22 @@ int main(int argc, const char **argv) setup_path(); while (1) { - /* See if it's an internal command */ - handle_internal_command(argc, argv); - - /* .. then try the external ones */ - execv_dashed_external(argv); - - /* It could be an alias -- this works around the insanity - * of overriding "git log" with "git show" by having - * alias.log = show - */ - if (done_alias || !handle_alias(&argc, &argv)) + static int done_help = 0; + static int was_alias = 0; + was_alias = run_argv(&argc, &argv); + if (errno != ENOENT) break; - done_alias = 1; - } - - if (errno == ENOENT) { - if (done_alias) { + if (was_alias) { fprintf(stderr, "Expansion of alias '%s' failed; " "'%s' is not a git-command\n", cmd, argv[0]); exit(1); } - argv[0] = help_unknown_cmd(cmd); - handle_internal_command(argc, argv); - execv_dashed_external(argv); + if (!done_help) { + cmd = argv[0] = help_unknown_cmd(cmd); + done_help = 1; + } else + break; } fprintf(stderr, "Failed to run command '%s': %s\n", From d75307084da5f89329de190bb9b4a3196cec1d0e Mon Sep 17 00:00:00 2001 From: Alexander Potashev Date: Sun, 4 Jan 2009 21:38:41 +0300 Subject: [PATCH 075/231] remove trailing LF in die() messages LF at the end of format strings given to die() is redundant because die already adds one on its own. Signed-off-by: Alexander Potashev Signed-off-by: Junio C Hamano --- builtin-cat-file.c | 2 +- builtin-clone.c | 14 +++++++------- builtin-fetch.c | 2 +- builtin-init-db.c | 2 +- builtin-log.c | 2 +- builtin-mailinfo.c | 2 +- builtin-merge-recursive.c | 2 +- builtin-update-index.c | 2 +- connect.c | 2 +- daemon.c | 2 +- diff.c | 2 +- git.c | 2 +- grep.c | 2 +- imap-send.c | 6 +++--- index-pack.c | 2 +- pack-redundant.c | 8 ++++---- 16 files changed, 27 insertions(+), 27 deletions(-) diff --git a/builtin-cat-file.c b/builtin-cat-file.c index 30d00a6664..8fad19daed 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -137,7 +137,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) break; default: - die("git cat-file: unknown option: %s\n", exp_type); + die("git cat-file: unknown option: %s", exp_type); } if (!buf) diff --git a/builtin-clone.c b/builtin-clone.c index 2feac9c5cb..f1a1a0c365 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -192,15 +192,15 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) dir = opendir(src->buf); if (!dir) - die("failed to open %s\n", src->buf); + die("failed to open %s", src->buf); if (mkdir(dest->buf, 0777)) { if (errno != EEXIST) - die("failed to create directory %s\n", dest->buf); + die("failed to create directory %s", dest->buf); else if (stat(dest->buf, &buf)) - die("failed to stat %s\n", dest->buf); + die("failed to stat %s", dest->buf); else if (!S_ISDIR(buf.st_mode)) - die("%s exists and is not a directory\n", dest->buf); + die("%s exists and is not a directory", dest->buf); } strbuf_addch(src, '/'); @@ -224,16 +224,16 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) } if (unlink(dest->buf) && errno != ENOENT) - die("failed to unlink %s\n", dest->buf); + die("failed to unlink %s", dest->buf); if (!option_no_hardlinks) { if (!link(src->buf, dest->buf)) continue; if (option_local) - die("failed to create link %s\n", dest->buf); + die("failed to create link %s", dest->buf); option_no_hardlinks = 1; } if (copy_file(dest->buf, src->buf, 0666)) - die("failed to copy file to %s\n", dest->buf); + die("failed to copy file to %s", dest->buf); } closedir(dir); } diff --git a/builtin-fetch.c b/builtin-fetch.c index 7568163af2..de6f3074b1 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -607,7 +607,7 @@ static void set_option(const char *name, const char *value) { int r = transport_set_option(transport, name, value); if (r < 0) - die("Option \"%s\" value \"%s\" is not valid for %s\n", + die("Option \"%s\" value \"%s\" is not valid for %s", name, value, transport->url); if (r > 0) warning("Option \"%s\" is ignored for %s\n", diff --git a/builtin-init-db.c b/builtin-init-db.c index d30c3fe2ca..ee3911f8ee 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -29,7 +29,7 @@ static void safe_create_dir(const char *dir, int share) } } else if (share && adjust_shared_perm(dir)) - die("Could not make %s writable by group\n", dir); + die("Could not make %s writable by group", dir); } static void copy_templates_1(char *path, int baselen, diff --git a/builtin-log.c b/builtin-log.c index bc4e1e9654..4a02ee987a 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -815,7 +815,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) - die("bogus committer info %s\n", committer); + die("bogus committer info %s", committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } else if (!strcmp(argv[i], "--attach")) { diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index e890f7a6d1..f7c8c08b32 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -494,7 +494,7 @@ static void convert_to_utf8(struct strbuf *line, const char *charset) return; out = reencode_string(line->buf, metainfo_charset, charset); if (!out) - die("cannot convert from %s to %s\n", + die("cannot convert from %s to %s", charset, metainfo_charset); strbuf_attach(line, out, strlen(out), strlen(out)); } diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 6b534c1a66..703045bfc8 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -33,7 +33,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) } if (argc < 4) - die("Usage: %s ... -- ...\n", argv[0]); + die("Usage: %s ... -- ...", argv[0]); for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--")) diff --git a/builtin-update-index.c b/builtin-update-index.c index 65d5775107..5604977505 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -486,7 +486,7 @@ static int unresolve_one(const char *path) static void read_head_pointers(void) { if (read_ref("HEAD", head_sha1)) - die("No HEAD -- no initial commit yet?\n"); + die("No HEAD -- no initial commit yet?"); if (read_ref("MERGE_HEAD", merge_head_sha1)) { fprintf(stderr, "Not in the middle of a merge.\n"); exit(0); diff --git a/connect.c b/connect.c index 2f55ad2c25..2f23ab3b87 100644 --- a/connect.c +++ b/connect.c @@ -315,7 +315,7 @@ static int git_tcp_connect_sock(char *host, int flags) /* Not numeric */ struct servent *se = getservbyname(port,"tcp"); if ( !se ) - die("Unknown port %s\n", port); + die("Unknown port %s", port); nport = se->s_port; } diff --git a/daemon.c b/daemon.c index 60bf6c743c..540700ee84 100644 --- a/daemon.c +++ b/daemon.c @@ -716,7 +716,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p) gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0); if (gai) - die("getaddrinfo() failed: %s\n", gai_strerror(gai)); + die("getaddrinfo() failed: %s", gai_strerror(gai)); for (ai = ai0; ai; ai = ai->ai_next) { int sockfd; diff --git a/diff.c b/diff.c index 0484601f42..c159a5fc65 100644 --- a/diff.c +++ b/diff.c @@ -2039,7 +2039,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one) if (lstat(one->path, &st) < 0) die("stat %s", one->path); if (index_path(one->sha1, one->path, &st, 0)) - die("cannot hash %s\n", one->path); + die("cannot hash %s", one->path); } } else diff --git a/git.c b/git.c index e0d9071358..a53e24feae 100644 --- a/git.c +++ b/git.c @@ -158,7 +158,7 @@ static int handle_alias(int *argcp, const char ***argv) if (ret >= 0 && WIFEXITED(ret) && WEXITSTATUS(ret) != 127) exit(WEXITSTATUS(ret)); - die("Failed to run '%s' when expanding alias '%s'\n", + die("Failed to run '%s' when expanding alias '%s'", alias_string + 1, alias_command); } count = split_cmdline(alias_string, &new_argv); diff --git a/grep.c b/grep.c index 600f69f2fe..49e9319965 100644 --- a/grep.c +++ b/grep.c @@ -395,7 +395,7 @@ static int match_expr_eval(struct grep_opt *o, h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1); break; default: - die("Unexpected node type (internal error) %d\n", x->node); + die("Unexpected node type (internal error) %d", x->node); } if (collect_hits) x->hit |= h; diff --git a/imap-send.c b/imap-send.c index 3703dbd1af..c3fa0df855 100644 --- a/imap-send.c +++ b/imap-send.c @@ -115,9 +115,9 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap) len = vsnprintf(tmp, sizeof(tmp), fmt, ap); if (len < 0) - die("Fatal: Out of memory\n"); + die("Fatal: Out of memory"); if (len >= sizeof(tmp)) - die("imap command overflow !\n"); + die("imap command overflow!"); *strp = xmemdupz(tmp, len); return len; } @@ -482,7 +482,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...) va_start(va, fmt); if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen) - die("Fatal: buffer too small. Please report a bug.\n"); + die("Fatal: buffer too small. Please report a bug."); va_end(va); return ret; } diff --git a/index-pack.c b/index-pack.c index 60ed41a993..2931511e8c 100644 --- a/index-pack.c +++ b/index-pack.c @@ -178,7 +178,7 @@ static char *open_pack_file(char *pack_name) } else output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600); if (output_fd < 0) - die("unable to create %s: %s\n", pack_name, strerror(errno)); + die("unable to create %s: %s", pack_name, strerror(errno)); pack_fd = output_fd; } else { input_fd = open(pack_name, O_RDONLY); diff --git a/pack-redundant.c b/pack-redundant.c index 25b81a445c..e93eb966e2 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -463,7 +463,7 @@ static void minimize(struct pack_list **min) pll_free(perm_all); } if (perm_ok == NULL) - die("Internal error: No complete sets found!\n"); + die("Internal error: No complete sets found!"); /* find the permutation with the smallest size */ perm = perm_ok; @@ -573,14 +573,14 @@ static struct pack_list * add_pack_file(char *filename) struct packed_git *p = packed_git; if (strlen(filename) < 40) - die("Bad pack filename: %s\n", filename); + die("Bad pack filename: %s", filename); while (p) { if (strstr(p->pack_name, filename)) return add_pack(p); p = p->next; } - die("Filename %s not found in packed_git\n", filename); + die("Filename %s not found in packed_git", filename); } static void load_all(void) @@ -636,7 +636,7 @@ int main(int argc, char **argv) add_pack_file(*(argv + i++)); if (local_packs == NULL) - die("Zero packs found!\n"); + die("Zero packs found!"); load_all_objects(); From 50a4b35245bbdb0cfca5d62bc4295a2b2e4f1e87 Mon Sep 17 00:00:00 2001 From: Paul Jarc Date: Sun, 4 Jan 2009 21:27:41 -0500 Subject: [PATCH 076/231] configure clobbers LDFLAGS In a couple of tests, configure clobbers the LDFLAGS value set by the caller. This patch fixes it. Signed-off-by: Junio C Hamano --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8821b5080a..0a5fc8c6f6 100644 --- a/configure.ac +++ b/configure.ac @@ -127,7 +127,7 @@ else SAVE_LDFLAGS="${LDFLAGS}" LDFLAGS="${SAVE_LDFLAGS} -Wl,-rpath,/" AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_wl_rpath=yes], [ld_wl_rpath=no]) - LDFLAGS="${SAVE_LD_FLAGS}" + LDFLAGS="${SAVE_LDFLAGS}" ]) if test "$ld_wl_rpath" = "yes"; then AC_SUBST(CC_LD_DYNPATH, [-Wl,-rpath,]) @@ -136,7 +136,7 @@ else SAVE_LDFLAGS="${LDFLAGS}" LDFLAGS="${SAVE_LDFLAGS} -rpath /" AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_rpath=yes], [ld_rpath=no]) - LDFLAGS="${SAVE_LD_FLAGS}" + LDFLAGS="${SAVE_LDFLAGS}" ]) if test "$ld_rpath" = "yes"; then AC_SUBST(CC_LD_DYNPATH, [-rpath]) From fb098a942b925296e0e86b6935a4fdda872e2f99 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Fri, 2 Jan 2009 12:34:40 +0100 Subject: [PATCH 077/231] gitweb: don't use pathinfo for global actions With PATH_INFO urls, actions for the projects list (e.g. opml, project_index) were being put in the URL right after the base. The resulting URL is not properly parsed by gitweb itself, since it expects a project name as first component of the URL. Accepting global actions in use_pathinfo is not a very robust solution due to possible present and future conflicts between project names and global actions, therefore we just refuse to create PATH_INFO URLs when the project is not defined. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7999bb37d3..b16400193d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -830,7 +830,7 @@ sub href (%) { } my $use_pathinfo = gitweb_check_feature('pathinfo'); - if ($use_pathinfo) { + if ($use_pathinfo and defined $params{'project'}) { # try to put as many parameters as possible in PATH_INFO: # - project name # - action @@ -845,7 +845,7 @@ sub href (%) { $href =~ s,/$,,; # Then add the project name, if present - $href .= "/".esc_url($params{'project'}) if defined $params{'project'}; + $href .= "/".esc_url($params{'project'}); delete $params{'project'}; # since we destructively absorb parameters, we keep this From cec08717cca468c66eb454c7201fd35f54b48a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 6 Jan 2009 21:41:06 +0100 Subject: [PATCH 078/231] shortlog: handle multi-line subjects like log --pretty=oneline et. al. do The commit message parser of git shortlog used to treat only the first non-empty line of the commit message as the subject. Other log commands (e.g. --pretty=oneline) show the whole first paragraph instead (unwrapped into a single line). For consistency, this patch borrows format_subject() from pretty.c to make shortlog do the same. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin-shortlog.c | 9 ++++++--- pretty.c | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/builtin-shortlog.c b/builtin-shortlog.c index d03f14fdad..e49290687f 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -29,6 +29,9 @@ static int compare_by_number(const void *a1, const void *a2) return -1; } +const char *format_subject(struct strbuf *sb, const char *msg, + const char *line_separator); + static void insert_one_record(struct shortlog *log, const char *author, const char *oneline) @@ -41,6 +44,7 @@ static void insert_one_record(struct shortlog *log, size_t len; const char *eol; const char *boemail, *eoemail; + struct strbuf subject = STRBUF_INIT; boemail = strchr(author, '<'); if (!boemail) @@ -89,9 +93,8 @@ static void insert_one_record(struct shortlog *log, while (*oneline && isspace(*oneline) && *oneline != '\n') oneline++; len = eol - oneline; - while (len && isspace(oneline[len-1])) - len--; - buffer = xmemdupz(oneline, len); + format_subject(&subject, oneline, " "); + buffer = strbuf_detach(&subject, NULL); if (dot3) { int dot3len = strlen(dot3); diff --git a/pretty.c b/pretty.c index 343dca556c..421d9c5bca 100644 --- a/pretty.c +++ b/pretty.c @@ -486,8 +486,8 @@ static void parse_commit_header(struct format_commit_context *context) context->commit_header_parsed = 1; } -static const char *format_subject(struct strbuf *sb, const char *msg, - const char *line_separator) +const char *format_subject(struct strbuf *sb, const char *msg, + const char *line_separator) { int first = 1; From 8a124b82a03240b10c83085559e5988bc92ea7e2 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 6 Jan 2009 23:23:37 -0500 Subject: [PATCH 079/231] README: tutorial.txt is now called gittutorial.txt Signed-off-by: Joey Hess Signed-off-by: Junio C Hamano --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 548142c327..5fa41b7a18 100644 --- a/README +++ b/README @@ -24,7 +24,7 @@ It was originally written by Linus Torvalds with help of a group of hackers around the net. It is currently maintained by Junio C Hamano. Please read the file INSTALL for installation instructions. -See Documentation/tutorial.txt to get started, then see +See Documentation/gittutorial.txt to get started, then see Documentation/everyday.txt for a useful minimum set of commands, and "man git-commandname" for documentation of each command. CVS users may also want to read Documentation/cvs-migration.txt. From 2fc647004ac7016128372a85db8245581e493812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 6 Jan 2009 21:41:14 +0100 Subject: [PATCH 080/231] strbuf: instate cleanup rule in case of non-memory errors Make all strbuf functions that can fail free() their memory on error if they have allocated it. They don't shrink buffers that have been grown, though. This allows for easier error handling, as callers only need to call strbuf_release() if A) the command succeeded or B) if they would have had to do so anyway because they added something to the strbuf themselves. Bonus hunk: document strbuf_readlink. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/technical/api-strbuf.txt | 11 +++++++++-- strbuf.c | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt index a8ee2fe6a1..9a4e3ea92c 100644 --- a/Documentation/technical/api-strbuf.txt +++ b/Documentation/technical/api-strbuf.txt @@ -133,8 +133,10 @@ Functions * Adding data to the buffer -NOTE: All of these functions in this section will grow the buffer as - necessary. +NOTE: All of the functions in this section will grow the buffer as necessary. +If they fail for some reason other than memory shortage and the buffer hadn't +been allocated before (i.e. the `struct strbuf` was set to `STRBUF_INIT`), +then they will free() it. `strbuf_addch`:: @@ -235,6 +237,11 @@ same behaviour as well. Read the contents of a file, specified by its path. The third argument can be used to give a hint about the file size, to avoid reallocs. +`strbuf_readlink`:: + + Read the target of a symbolic link, specified by its path. The third + argument can be used to give a hint about the size, to avoid reallocs. + `strbuf_getline`:: Read a line from a FILE* pointer. The second argument specifies the line diff --git a/strbuf.c b/strbuf.c index bdf49544d4..6ed06840b8 100644 --- a/strbuf.c +++ b/strbuf.c @@ -256,18 +256,21 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { size_t res; + size_t oldalloc = sb->alloc; strbuf_grow(sb, size); res = fread(sb->buf + sb->len, 1, size, f); - if (res > 0) { + if (res > 0) strbuf_setlen(sb, sb->len + res); - } + else if (res < 0 && oldalloc == 0) + strbuf_release(sb); return res; } ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) { size_t oldlen = sb->len; + size_t oldalloc = sb->alloc; strbuf_grow(sb, hint ? hint : 8192); for (;;) { @@ -275,7 +278,10 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); if (cnt < 0) { - strbuf_setlen(sb, oldlen); + if (oldalloc == 0) + strbuf_release(sb); + else + strbuf_setlen(sb, oldlen); return -1; } if (!cnt) @@ -292,6 +298,8 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) { + size_t oldalloc = sb->alloc; + if (hint < 32) hint = 32; @@ -311,7 +319,8 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) /* .. the buffer was too small - try again */ hint *= 2; } - strbuf_release(sb); + if (oldalloc == 0) + strbuf_release(sb); return -1; } From e423ffd8a6f46a14ecbb637d909290bd3579cebd Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Tue, 6 Jan 2009 19:53:32 +0100 Subject: [PATCH 081/231] diff: accept -- when using --no-index Accept -- as an "end of options" marker even when using --no-index. Previously, the -- triggered a "normal" index/tree diff and subsequently failed because of the unrecognized (in that mode) --no-index. Note that the second loop can treat '--' as a normal option, because the preceding checks ensure it is the third-to-last argument. While at it, fix the parsing of "-q" option in --no-index mode as well. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- diff-no-index.c | 10 +++++++--- t/t4013-diff-various.sh | 1 + t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir | 3 +++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir diff --git a/diff-no-index.c b/diff-no-index.c index b60d3455da..39868e2a8f 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -173,8 +173,10 @@ void diff_no_index(struct rev_info *revs, /* Were we asked to do --no-index explicitly? */ for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--")) - return; + if (!strcmp(argv[i], "--")) { + i++; + break; + } if (!strcmp(argv[i], "--no-index")) no_index = 1; if (argv[i][0] != '-') @@ -212,8 +214,10 @@ void diff_no_index(struct rev_info *revs, int j; if (!strcmp(argv[i], "--no-index")) i++; - else if (!strcmp(argv[1], "-q")) + else if (!strcmp(argv[i], "-q")) options |= DIFF_SILENT_ON_REMOVED; + else if (!strcmp(argv[i], "--")) + i++; else { j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); if (!j) diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index aeb5405cfe..aba53202f8 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -261,6 +261,7 @@ diff --patch-with-stat -r initial..side diff --patch-with-raw -r initial..side diff --name-status dir2 dir diff --no-index --name-status dir2 dir +diff --no-index --name-status -- dir2 dir diff master master^ side EOF diff --git a/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir b/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir new file mode 100644 index 0000000000..6756f8de67 --- /dev/null +++ b/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir @@ -0,0 +1,3 @@ +$ git diff --no-index --name-status -- dir2 dir +A dir/sub +$ From c6dbca08ca39d9cd511a36cdd18847b8720f1653 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 7 Jan 2009 00:56:03 +0100 Subject: [PATCH 082/231] diff --no-index: test for pager after option parsing We need to parse options before we can see if --exit-code was provided. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- diff-no-index.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/diff-no-index.c b/diff-no-index.c index 39868e2a8f..12ff1f1eef 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -200,13 +200,6 @@ void diff_no_index(struct rev_info *revs, die("git diff %s takes two paths", no_index ? "--no-index" : "[--no-index]"); - /* - * If the user asked for our exit code then don't start a - * pager or we would end up reporting its exit code instead. - */ - if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS)) - setup_pager(); - diff_setup(&revs->diffopt); if (!revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; @@ -226,6 +219,13 @@ void diff_no_index(struct rev_info *revs, } } + /* + * If the user asked for our exit code then don't start a + * pager or we would end up reporting its exit code instead. + */ + if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS)) + setup_pager(); + if (prefix) { int len = strlen(prefix); From a324fc45e4d04e3b879cd2d9a603d73ca696b775 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 7 Jan 2009 12:15:30 +0100 Subject: [PATCH 083/231] diff --no-index -q: fix endless loop We forgot to move to the next argument when parsing -q, getting stuck in an endless loop. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- diff-no-index.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/diff-no-index.c b/diff-no-index.c index 12ff1f1eef..60ed17470a 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -207,8 +207,10 @@ void diff_no_index(struct rev_info *revs, int j; if (!strcmp(argv[i], "--no-index")) i++; - else if (!strcmp(argv[i], "-q")) + else if (!strcmp(argv[i], "-q")) { options |= DIFF_SILENT_ON_REMOVED; + i++; + } else if (!strcmp(argv[i], "--")) i++; else { From 92b7de93fb7801570ddc3195f03f30b9c201a3bd Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 7 Jan 2009 18:04:09 +0100 Subject: [PATCH 084/231] Implement the patience diff algorithm The patience diff algorithm produces slightly more intuitive output than the classic Myers algorithm, as it does not try to minimize the number of +/- lines first, but tries to preserve the lines that are unique. To this end, it first determines lines that are unique in both files, then the maximal sequence which preserves the order (relative to both files) is extracted. Starting from this initial set of common lines, the rest of the lines is handled recursively, with Myers' algorithm as a fallback when the patience algorithm fails (due to no common unique lines). This patch includes memory leak fixes by Pierre Habouzit. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- xdiff/xdiff.h | 1 + xdiff/xdiffi.c | 3 + xdiff/xdiffi.h | 2 + xdiff/xpatience.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++ xdiff/xprepare.c | 3 +- 5 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 xdiff/xpatience.c diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h index 84fff583e2..d238c8263e 100644 --- a/xdiff/xdiff.h +++ b/xdiff/xdiff.h @@ -32,6 +32,7 @@ extern "C" { #define XDF_IGNORE_WHITESPACE (1 << 2) #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3) #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4) +#define XDF_PATIENCE_DIFF (1 << 5) #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL) #define XDL_PATCH_NORMAL '-' diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 9d0324a38c..3e97462bdd 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -329,6 +329,9 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdalgoenv_t xenv; diffdata_t dd1, dd2; + if (xpp->flags & XDF_PATIENCE_DIFF) + return xdl_do_patience_diff(mf1, mf2, xpp, xe); + if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) { return -1; diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h index 3e099dc445..ad033a8e6a 100644 --- a/xdiff/xdiffi.h +++ b/xdiff/xdiffi.h @@ -55,5 +55,7 @@ int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr); void xdl_free_script(xdchange_t *xscr); int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg); +int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *env); #endif /* #if !defined(XDIFFI_H) */ diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c new file mode 100644 index 0000000000..e42c16a807 --- /dev/null +++ b/xdiff/xpatience.c @@ -0,0 +1,381 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Davide Libenzi + * + */ +#include "xinclude.h" +#include "xtypes.h" +#include "xdiff.h" + +/* + * The basic idea of patience diff is to find lines that are unique in + * both files. These are intuitively the ones that we want to see as + * common lines. + * + * The maximal ordered sequence of such line pairs (where ordered means + * that the order in the sequence agrees with the order of the lines in + * both files) naturally defines an initial set of common lines. + * + * Now, the algorithm tries to extend the set of common lines by growing + * the line ranges where the files have identical lines. + * + * Between those common lines, the patience diff algorithm is applied + * recursively, until no unique line pairs can be found; these line ranges + * are handled by the well-known Myers algorithm. + */ + +#define NON_UNIQUE ULONG_MAX + +/* + * This is a hash mapping from line hash to line numbers in the first and + * second file. + */ +struct hashmap { + int nr, alloc; + struct entry { + unsigned long hash; + /* + * 0 = unused entry, 1 = first line, 2 = second, etc. + * line2 is NON_UNIQUE if the line is not unique + * in either the first or the second file. + */ + unsigned long line1, line2; + /* + * "next" & "previous" are used for the longest common + * sequence; + * initially, "next" reflects only the order in file1. + */ + struct entry *next, *previous; + } *entries, *first, *last; + /* were common records found? */ + unsigned long has_matches; + mmfile_t *file1, *file2; + xdfenv_t *env; + xpparam_t const *xpp; +}; + +/* The argument "pass" is 1 for the first file, 2 for the second. */ +static void insert_record(int line, struct hashmap *map, int pass) +{ + xrecord_t **records = pass == 1 ? + map->env->xdf1.recs : map->env->xdf2.recs; + xrecord_t *record = records[line - 1], *other; + /* + * After xdl_prepare_env() (or more precisely, due to + * xdl_classify_record()), the "ha" member of the records (AKA lines) + * is _not_ the hash anymore, but a linearized version of it. In + * other words, the "ha" member is guaranteed to start with 0 and + * the second record's ha can only be 0 or 1, etc. + * + * So we multiply ha by 2 in the hope that the hashing was + * "unique enough". + */ + int index = (int)((record->ha << 1) % map->alloc); + + while (map->entries[index].line1) { + other = map->env->xdf1.recs[map->entries[index].line1 - 1]; + if (map->entries[index].hash != record->ha || + !xdl_recmatch(record->ptr, record->size, + other->ptr, other->size, + map->xpp->flags)) { + if (++index >= map->alloc) + index = 0; + continue; + } + if (pass == 2) + map->has_matches = 1; + if (pass == 1 || map->entries[index].line2) + map->entries[index].line2 = NON_UNIQUE; + else + map->entries[index].line2 = line; + return; + } + if (pass == 2) + return; + map->entries[index].line1 = line; + map->entries[index].hash = record->ha; + if (!map->first) + map->first = map->entries + index; + if (map->last) { + map->last->next = map->entries + index; + map->entries[index].previous = map->last; + } + map->last = map->entries + index; + map->nr++; +} + +/* + * This function has to be called for each recursion into the inter-hunk + * parts, as previously non-unique lines can become unique when being + * restricted to a smaller part of the files. + * + * It is assumed that env has been prepared using xdl_prepare(). + */ +static int fill_hashmap(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env, + struct hashmap *result, + int line1, int count1, int line2, int count2) +{ + result->file1 = file1; + result->file2 = file2; + result->xpp = xpp; + result->env = env; + + /* We know exactly how large we want the hash map */ + result->alloc = count1 * 2; + result->entries = (struct entry *) + xdl_malloc(result->alloc * sizeof(struct entry)); + if (!result->entries) + return -1; + memset(result->entries, 0, result->alloc * sizeof(struct entry)); + + /* First, fill with entries from the first file */ + while (count1--) + insert_record(line1++, result, 1); + + /* Then search for matches in the second file */ + while (count2--) + insert_record(line2++, result, 2); + + return 0; +} + +/* + * Find the longest sequence with a smaller last element (meaning a smaller + * line2, as we construct the sequence with entries ordered by line1). + */ +static int binary_search(struct entry **sequence, int longest, + struct entry *entry) +{ + int left = -1, right = longest; + + while (left + 1 < right) { + int middle = (left + right) / 2; + /* by construction, no two entries can be equal */ + if (sequence[middle]->line2 > entry->line2) + right = middle; + else + left = middle; + } + /* return the index in "sequence", _not_ the sequence length */ + return left; +} + +/* + * The idea is to start with the list of common unique lines sorted by + * the order in file1. For each of these pairs, the longest (partial) + * sequence whose last element's line2 is smaller is determined. + * + * For efficiency, the sequences are kept in a list containing exactly one + * item per sequence length: the sequence with the smallest last + * element (in terms of line2). + */ +static struct entry *find_longest_common_sequence(struct hashmap *map) +{ + struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *)); + int longest = 0, i; + struct entry *entry; + + for (entry = map->first; entry; entry = entry->next) { + if (!entry->line2 || entry->line2 == NON_UNIQUE) + continue; + i = binary_search(sequence, longest, entry); + entry->previous = i < 0 ? NULL : sequence[i]; + sequence[++i] = entry; + if (i == longest) + longest++; + } + + /* No common unique lines were found */ + if (!longest) { + xdl_free(sequence); + return NULL; + } + + /* Iterate starting at the last element, adjusting the "next" members */ + entry = sequence[longest - 1]; + entry->next = NULL; + while (entry->previous) { + entry->previous->next = entry; + entry = entry->previous; + } + xdl_free(sequence); + return entry; +} + +static int match(struct hashmap *map, int line1, int line2) +{ + xrecord_t *record1 = map->env->xdf1.recs[line1 - 1]; + xrecord_t *record2 = map->env->xdf2.recs[line2 - 1]; + return xdl_recmatch(record1->ptr, record1->size, + record2->ptr, record2->size, map->xpp->flags); +} + +static int patience_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2); + +static int walk_common_sequence(struct hashmap *map, struct entry *first, + int line1, int count1, int line2, int count2) +{ + int end1 = line1 + count1, end2 = line2 + count2; + int next1, next2; + + for (;;) { + /* Try to grow the line ranges of common lines */ + if (first) { + next1 = first->line1; + next2 = first->line2; + while (next1 > line1 && next2 > line2 && + match(map, next1 - 1, next2 - 1)) { + next1--; + next2--; + } + } else { + next1 = end1; + next2 = end2; + } + while (line1 < next1 && line2 < next2 && + match(map, line1, line2)) { + line1++; + line2++; + } + + /* Recurse */ + if (next1 > line1 || next2 > line2) { + struct hashmap submap; + + memset(&submap, 0, sizeof(submap)); + if (patience_diff(map->file1, map->file2, + map->xpp, map->env, + line1, next1 - line1, + line2, next2 - line2)) + return -1; + } + + if (!first) + return 0; + + while (first->next && + first->next->line1 == first->line1 + 1 && + first->next->line2 == first->line2 + 1) + first = first->next; + + line1 = first->line1 + 1; + line2 = first->line2 + 1; + + first = first->next; + } +} + +static int fall_back_to_classic_diff(struct hashmap *map, + int line1, int count1, int line2, int count2) +{ + /* + * This probably does not work outside Git, since + * we have a very simple mmfile structure. + * + * Note: ideally, we would reuse the prepared environment, but + * the libxdiff interface does not (yet) allow for diffing only + * ranges of lines instead of the whole files. + */ + mmfile_t subfile1, subfile2; + xpparam_t xpp; + xdfenv_t env; + + subfile1.ptr = (char *)map->env->xdf1.recs[line1 - 1]->ptr; + subfile1.size = map->env->xdf1.recs[line1 + count1 - 2]->ptr + + map->env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr; + subfile2.ptr = (char *)map->env->xdf2.recs[line2 - 1]->ptr; + subfile2.size = map->env->xdf2.recs[line2 + count2 - 2]->ptr + + map->env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr; + xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF; + if (xdl_do_diff(&subfile1, &subfile2, &xpp, &env) < 0) + return -1; + + memcpy(map->env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1); + memcpy(map->env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2); + + xdl_free_env(&env); + + return 0; +} + +/* + * Recursively find the longest common sequence of unique lines, + * and if none was found, ask xdl_do_diff() to do the job. + * + * This function assumes that env was prepared with xdl_prepare_env(). + */ +static int patience_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2) +{ + struct hashmap map; + struct entry *first; + int result = 0; + + /* trivial case: one side is empty */ + if (!count1) { + while(count2--) + env->xdf2.rchg[line2++ - 1] = 1; + return 0; + } else if (!count2) { + while(count1--) + env->xdf1.rchg[line1++ - 1] = 1; + return 0; + } + + memset(&map, 0, sizeof(map)); + if (fill_hashmap(file1, file2, xpp, env, &map, + line1, count1, line2, count2)) + return -1; + + /* are there any matching lines at all? */ + if (!map.has_matches) { + while(count1--) + env->xdf1.rchg[line1++ - 1] = 1; + while(count2--) + env->xdf2.rchg[line2++ - 1] = 1; + xdl_free(map.entries); + return 0; + } + + first = find_longest_common_sequence(&map); + if (first) + result = walk_common_sequence(&map, first, + line1, count1, line2, count2); + else + result = fall_back_to_classic_diff(&map, + line1, count1, line2, count2); + + xdl_free(map.entries); + return result; +} + +int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env) +{ + if (xdl_prepare_env(file1, file2, xpp, env) < 0) + return -1; + + /* environment is cleaned up in xdl_diff() */ + return patience_diff(file1, file2, xpp, env, + 1, env->xdf1.nrec, 1, env->xdf2.nrec); +} diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c index a43aa72cd0..1689085235 100644 --- a/xdiff/xprepare.c +++ b/xdiff/xprepare.c @@ -290,7 +290,8 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdl_free_classifier(&cf); - if (xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) { + if (!(xpp->flags & XDF_PATIENCE_DIFF) && + xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) { xdl_free_ctx(&xe->xdf2); xdl_free_ctx(&xe->xdf1); From 34292bddb861f3cb52a524fdce67234430a744fe Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 1 Jan 2009 17:39:17 +0100 Subject: [PATCH 085/231] Introduce the diff option '--patience' This commit teaches Git to produce diff output using the patience diff algorithm with the diff option '--patience'. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/diff-options.txt | 3 + Makefile | 2 +- diff.c | 2 + t/t4033-diff-patience.sh | 168 +++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100755 t/t4033-diff-patience.sh diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index c62b45cdba..808bf87277 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -40,6 +40,9 @@ endif::git-format-patch[] --patch-with-raw:: Synonym for "-p --raw". +--patience: + Generate a diff using the "patience diff" algorithm. + --stat[=width[,name-width]]:: Generate a diffstat. You can override the default output width for 80-column terminal by "--stat=width". diff --git a/Makefile b/Makefile index aabf0130b9..33e6fa403e 100644 --- a/Makefile +++ b/Makefile @@ -1287,7 +1287,7 @@ $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \ - xdiff/xmerge.o + xdiff/xmerge.o xdiff/xpatience.o $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \ xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h diff --git a/diff.c b/diff.c index 0484601f42..4aeab7746c 100644 --- a/diff.c +++ b/diff.c @@ -2471,6 +2471,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; else if (!strcmp(arg, "--ignore-space-at-eol")) options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + else if (!strcmp(arg, "--patience")) + options->xdl_opts |= XDF_PATIENCE_DIFF; /* flags options */ else if (!strcmp(arg, "--binary")) { diff --git a/t/t4033-diff-patience.sh b/t/t4033-diff-patience.sh new file mode 100755 index 0000000000..1eb14989df --- /dev/null +++ b/t/t4033-diff-patience.sh @@ -0,0 +1,168 @@ +#!/bin/sh + +test_description='patience diff algorithm' + +. ./test-lib.sh + +cat >file1 <<\EOF +#include + +// Frobs foo heartily +int frobnitz(int foo) +{ + int i; + for(i = 0; i < 10; i++) + { + printf("Your answer is: "); + printf("%d\n", foo); + } +} + +int fact(int n) +{ + if(n > 1) + { + return fact(n-1) * n; + } + return 1; +} + +int main(int argc, char **argv) +{ + frobnitz(fact(10)); +} +EOF + +cat >file2 <<\EOF +#include + +int fib(int n) +{ + if(n > 2) + { + return fib(n-1) + fib(n-2); + } + return 1; +} + +// Frobs foo heartily +int frobnitz(int foo) +{ + int i; + for(i = 0; i < 10; i++) + { + printf("%d\n", foo); + } +} + +int main(int argc, char **argv) +{ + frobnitz(fib(10)); +} +EOF + +cat >expect <<\EOF +diff --git a/file1 b/file2 +index 6faa5a3..e3af329 100644 +--- a/file1 ++++ b/file2 +@@ -1,26 +1,25 @@ + #include + ++int fib(int n) ++{ ++ if(n > 2) ++ { ++ return fib(n-1) + fib(n-2); ++ } ++ return 1; ++} ++ + // Frobs foo heartily + int frobnitz(int foo) + { + int i; + for(i = 0; i < 10; i++) + { +- printf("Your answer is: "); + printf("%d\n", foo); + } + } + +-int fact(int n) +-{ +- if(n > 1) +- { +- return fact(n-1) * n; +- } +- return 1; +-} +- + int main(int argc, char **argv) + { +- frobnitz(fact(10)); ++ frobnitz(fib(10)); + } +EOF + +test_expect_success 'patience diff' ' + + test_must_fail git diff --no-index --patience file1 file2 > output && + test_cmp expect output + +' + +test_expect_success 'patience diff output is valid' ' + + mv file2 expect && + git apply < output && + test_cmp expect file2 + +' + +cat >uniq1 <<\EOF +1 +2 +3 +4 +5 +6 +EOF + +cat >uniq2 <<\EOF +a +b +c +d +e +f +EOF + +cat >expect <<\EOF +diff --git a/uniq1 b/uniq2 +index b414108..0fdf397 100644 +--- a/uniq1 ++++ b/uniq2 +@@ -1,6 +1,6 @@ +-1 +-2 +-3 +-4 +-5 +-6 ++a ++b ++c ++d ++e ++f +EOF + +test_expect_success 'completely different files' ' + + test_must_fail git diff --no-index --patience uniq1 uniq2 > output && + test_cmp expect output + +' + +test_done From cc545709253fe8440db2648cb5c771e5b126bdb5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 1 Jan 2009 17:39:37 +0100 Subject: [PATCH 086/231] bash completions: Add the --patience option Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..b98d765deb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -776,6 +776,7 @@ _git_diff () --no-ext-diff --no-prefix --src-prefix= --dst-prefix= --base --ours --theirs + --patience " return ;; @@ -967,6 +968,7 @@ _git_log () --color-words --walk-reflogs --parents --children --full-history --merge + --patience " return ;; From 3f4b609f5f3146a34eb72e82ab55714328e0bd1a Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Thu, 8 Jan 2009 19:53:01 +0100 Subject: [PATCH 087/231] git-commit: color status output when color.ui is set When using "git commit" and there was nothing to commit (the editor wasn't launched), the status output wasn't colored, even though color.ui was set. Only when setting color.status it worked. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- builtin-commit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin-commit.c b/builtin-commit.c index e88b78f811..2d90f7430d 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -945,6 +945,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix) git_config(git_commit_config, NULL); + if (wt_status_use_color == -1) + wt_status_use_color = git_use_color_default; + argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix); index_file = prepare_index(argc, argv, prefix); From 38920dd6d377a82f4c8c51c3451271d6a93c92a8 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Thu, 8 Jan 2009 19:53:05 +0100 Subject: [PATCH 088/231] git-status -v: color diff output when color.ui is set When using "git status -v", the diff output wasn't colored, even though color.ui was set. Only when setting color.diff it worked. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- builtin-commit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin-commit.c b/builtin-commit.c index 2d90f7430d..7cf227a1b1 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -866,6 +866,9 @@ int cmd_status(int argc, const char **argv, const char *prefix) if (wt_status_use_color == -1) wt_status_use_color = git_use_color_default; + if (diff_use_color_default == -1) + diff_use_color_default = git_use_color_default; + argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix); index_file = prepare_index(argc, argv, prefix); From fb62eb7fab97cea880ea7fe4f341a4dfad14ab48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 10 Jan 2009 00:08:40 +0100 Subject: [PATCH 089/231] grep -w: forward to next possible position after rejected match grep -w accepts matches between non-word characters, only. If a match from regexec() doesn't meet this criteria, grep continues its search after the first character of that match. We can be a bit smarter here and skip all positions that follow a word character first, as they can't match our criteria. This way we can consume characters quite cheaply and don't need to special-case the handling of the beginning of a line. Here's a contrived example command on msysgit (best of five runs): $ time git grep -w ...... v1.6.1 >/dev/null real 0m1.611s user 0m0.000s sys 0m0.015s With the patch it's quite a bit faster: $ time git grep -w ...... v1.6.1 >/dev/null real 0m1.179s user 0m0.000s sys 0m0.015s More common search patterns will gain a lot less, but it's a nice clean up anyway. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/grep.c b/grep.c index 49e9319965..22a56b5d54 100644 --- a/grep.c +++ b/grep.c @@ -294,7 +294,6 @@ static struct { static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) { int hit = 0; - int at_true_bol = 1; int saved_ch = 0; regmatch_t pmatch[10]; @@ -337,7 +336,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol * either end of the line, or at word boundary * (i.e. the next char must not be a word char). */ - if ( ((pmatch[0].rm_so == 0 && at_true_bol) || + if ( ((pmatch[0].rm_so == 0) || !word_char(bol[pmatch[0].rm_so-1])) && ((pmatch[0].rm_eo == (eol-bol)) || !word_char(bol[pmatch[0].rm_eo])) ) @@ -349,10 +348,14 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol /* There could be more than one match on the * line, and the first match might not be * strict word match. But later ones could be! + * Forward to the next possible start, i.e. the + * next position following a non-word char. */ bol = pmatch[0].rm_so + bol + 1; - at_true_bol = 0; - goto again; + while (word_char(bol[-1]) && bol < eol) + bol++; + if (bol < eol) + goto again; } } if (p->token == GREP_PATTERN_HEAD && saved_ch) From c822255cfc1ac83daeeeee1647e3c775450c830c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 10 Jan 2009 00:18:34 +0100 Subject: [PATCH 090/231] grep: don't call regexec() for fixed strings Add the new flag "fixed" to struct grep_pat and set it if the pattern is doesn't contain any regex control characters in addition to if the flag -F/--fixed-strings was specified. This gives a nice speed up on msysgit, where regexec() seems to be extra slow. Before (best of five runs): $ time git grep grep v1.6.1 >/dev/null real 0m0.552s user 0m0.000s sys 0m0.000s $ time git grep -F grep v1.6.1 >/dev/null real 0m0.170s user 0m0.000s sys 0m0.015s With the patch: $ time git grep grep v1.6.1 >/dev/null real 0m0.173s user 0m0.000s sys 0m0.000s The difference is much smaller on Linux, but still measurable. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- grep.c | 29 +++++++++++++++++++++++++---- grep.h | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/grep.c b/grep.c index 22a56b5d54..6485760ff3 100644 --- a/grep.c +++ b/grep.c @@ -28,9 +28,31 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat, p->next = NULL; } +static int isregexspecial(int c) +{ + return isspecial(c) || c == '$' || c == '(' || c == ')' || c == '+' || + c == '.' || c == '^' || c == '{' || c == '|'; +} + +static int is_fixed(const char *s) +{ + while (!isregexspecial(*s)) + s++; + return !*s; +} + static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) { - int err = regcomp(&p->regexp, p->pattern, opt->regflags); + int err; + + if (opt->fixed || is_fixed(p->pattern)) + p->fixed = 1; + if (opt->regflags & REG_ICASE) + p->fixed = 0; + if (p->fixed) + return; + + err = regcomp(&p->regexp, p->pattern, opt->regflags); if (err) { char errbuf[1024]; char where[1024]; @@ -159,8 +181,7 @@ void compile_grep_patterns(struct grep_opt *opt) case GREP_PATTERN: /* atom */ case GREP_PATTERN_HEAD: case GREP_PATTERN_BODY: - if (!opt->fixed) - compile_regexp(p, opt); + compile_regexp(p, opt); break; default: opt->extended = 1; @@ -314,7 +335,7 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol } again: - if (!opt->fixed) { + if (!p->fixed) { regex_t *exp = &p->regexp; hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), pmatch, 0); diff --git a/grep.h b/grep.h index 45a222d904..5102ce335d 100644 --- a/grep.h +++ b/grep.h @@ -30,6 +30,7 @@ struct grep_pat { const char *pattern; enum grep_header_field field; regex_t regexp; + unsigned fixed:1; }; enum grep_expr_node { From 64912a67a43dec151906b93f96b07f16e17f71e9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 9 Jan 2009 22:21:36 -0800 Subject: [PATCH 091/231] Resurrect "git apply --flags -" to read from the standard input The previous "parse-opt"ification broke git-apply reading from the standard input. "git apply A - C --- builtin-apply.c | 19 +++++-------------- t/t4106-apply-stdin.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) create mode 100755 t/t4106-apply-stdin.sh diff --git a/builtin-apply.c b/builtin-apply.c index cb988a3526..2811c0fe4d 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -3140,16 +3140,6 @@ static int git_apply_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static int option_parse_stdin(const struct option *opt, - const char *arg, int unset) -{ - int *errs = opt->value; - - *errs |= apply_patch(0, "", options); - read_stdin = 0; - return 0; -} - static int option_parse_exclude(const struct option *opt, const char *arg, int unset) { @@ -3218,9 +3208,6 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) const char *whitespace_option = NULL; struct option builtin_apply_options[] = { - { OPTION_CALLBACK, '-', NULL, &errs, NULL, - "read the patch from the standard input", - PARSE_OPT_NOARG, option_parse_stdin }, { OPTION_CALLBACK, 0, "exclude", NULL, "path", "don´t apply changes matching the given path", 0, option_parse_exclude }, @@ -3302,7 +3289,11 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) const char *arg = argv[i]; int fd; - if (0 < prefix_length) + if (!strcmp(arg, "-")) { + errs |= apply_patch(0, "", options); + read_stdin = 0; + continue; + } else if (0 < prefix_length) arg = prefix_filename(prefix, prefix_length, arg); fd = open(arg, O_RDONLY); diff --git a/t/t4106-apply-stdin.sh b/t/t4106-apply-stdin.sh new file mode 100755 index 0000000000..72467a1e8e --- /dev/null +++ b/t/t4106-apply-stdin.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +test_description='git apply --numstat - text && + git add text && + echo goodbye >text && + git diff >patch +' + +test_expect_success 'git apply --numstat - < patch' ' + echo "1 1 text" >expect && + git apply --numstat - actual && + test_cmp expect actual +' + +test_expect_success 'git apply --numstat - < patch patch' ' + for i in 1 2; do echo "1 1 text"; done >expect && + git apply --numstat - < patch patch >actual && + test_cmp expect actual +' + +test_done From d3240d935c4b844db71e4570eeedfac4cff73f78 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Fri, 31 Oct 2008 10:12:21 +0100 Subject: [PATCH 092/231] filter-branch: add git_commit_non_empty_tree and --prune-empty. git_commit_non_empty_tree is added to the functions that can be run from commit filters. Its effect is to commit only commits actually touching the tree and that are not merge points either. The option --prune-empty is added. It defaults the commit-filter to 'git_commit_non_empty_tree "$@"', and can be used with any other combination of filters, except --commit-hook that must used 'git_commit_non_empty_tree "$@"' where one puts 'git commit-tree "$@"' usually to achieve the same result. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 14 ++++++++++++++ git-filter-branch.sh | 29 ++++++++++++++++++++++++++++- t/t7003-filter-branch.sh | 8 ++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index fed6de6a7f..451950bab6 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -122,6 +122,10 @@ You can use the 'map' convenience function in this filter, and other convenience functions, too. For example, calling 'skip_commit "$@"' will leave out the current commit (but not its changes! If you want that, use 'git-rebase' instead). ++ +You can also use the 'git_commit_non_empty_tree "$@"' instead of +'git commit-tree "$@"' if you don't wish to keep commits with a single parent +and that makes no change to the tree. --tag-name-filter :: This is the filter for rewriting tag names. When passed, @@ -151,6 +155,16 @@ to other tags will be rewritten to point to the underlying commit. The result will contain that directory (and only that) as its project root. +--prune-empty:: + Some kind of filters will generate empty commits, that left the tree + untouched. This switch allow git-filter-branch to ignore such + commits. Though, this switch only applies for commits that have one + and only one parent, it will hence keep merges points. Also, this + option is not compatible with the use of '--commit-filter'. Though you + just need to use the function 'git_commit_non_empty_tree "$@"' instead + of the 'git commit-tree "$@"' idiom in your commit filter to make that + happen. + --original :: Use this option to set the namespace where the original commits will be stored. The default value is 'refs/original'. diff --git a/git-filter-branch.sh b/git-filter-branch.sh index c106f45af7..eb62f719b0 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -40,6 +40,16 @@ skip_commit() done; } +# if you run 'git_commit_non_empty_tree "$@"' in a commit filter, +# it will skip commits that leave the tree untouched, commit the other. +git_commit_non_empty_tree() +{ + if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then + map "$3" + else + git commit-tree "$@" + fi +} # override die(): this version puts in an extra line break, so that # the progress is still visible @@ -109,11 +119,12 @@ filter_tree= filter_index= filter_parent= filter_msg=cat -filter_commit='git commit-tree "$@"' +filter_commit= filter_tag_name= filter_subdir= orig_namespace=refs/original/ force= +prune_empty= while : do case "$1" in @@ -126,6 +137,11 @@ do force=t continue ;; + --prune-empty) + shift + prune_empty=t + continue + ;; -*) ;; *) @@ -176,6 +192,17 @@ do esac done +case "$prune_empty,$filter_commit" in +,) + filter_commit='git commit-tree "$@"';; +t,) + filter_commit="$functions;"' git_commit_non_empty_tree "$@"';; +,*) + ;; +*) + die "Cannot set --prune-empty and --filter-commit at the same time" +esac + case "$force" in t) rm -rf "$tempdir" diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index b0a9d7d536..8537bf9160 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -262,4 +262,12 @@ test_expect_success 'Tag name filtering allows slashes in tag names' ' test_cmp expect actual ' +test_expect_success 'Prune empty commits' ' + git rev-list HEAD > expect && + make_commit to_remove && + git filter-branch -f --index-filter "git update-index --remove to_remove" --prune-empty HEAD && + git rev-list HEAD > actual && + test_cmp expect actual +' + test_done From c9a114b591e42be3ed8e5e4812dfd1031df79a78 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 10 Dec 2008 12:39:17 -0700 Subject: [PATCH 093/231] bash completion: Add '--intent-to-add' long option for 'git add' Signed-off-by: Lee Marlow Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8ec782dc54..e986e783e2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -563,7 +563,7 @@ _git_add () --*) __gitcomp " --interactive --refresh --patch --update --dry-run - --ignore-errors + --ignore-errors --intent-to-add " return esac From df3987717f1546719a1bf1828fb3714cd5ca9faa Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 10 Dec 2008 12:39:18 -0700 Subject: [PATCH 094/231] bash completion: Use 'git add' completions for 'git stage' Signed-off-by: Lee Marlow Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e986e783e2..7b074d7985 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1738,6 +1738,7 @@ _git () show) _git_show ;; show-branch) _git_show_branch ;; stash) _git_stash ;; + stage) _git_add ;; submodule) _git_submodule ;; svn) _git_svn ;; tag) _git_tag ;; From 68c2ec7f43c4dc16833ff58af76ab20e9cdfad17 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 10 Jan 2009 12:41:33 -0800 Subject: [PATCH 095/231] format-patch: show patch text for the root commit Even without --root specified, if the range given on the command line happens to include a root commit, we should include its patch text in the output. This fix deliberately ignores log.showroot configuration variable because "format-patch" and "log -p" can and should behave differently in this case, as the former is about exporting a part of your history in a form that is replayable elsewhere and just giving the commit log message without the patch text does not make any sense for that purpose. Noticed and fix originally attempted by Nathan W. Panike; credit goes to Alexander Potashev for injecting sanity to my initial (broken) fix that used the value from log.showroot configuration, which was misguided. Signed-off-by: Junio C Hamano --- builtin-log.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/builtin-log.c b/builtin-log.c index db71e0da74..5e302906fb 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -962,6 +962,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * get_revision() to do the usual traversal. */ } + + /* + * We cannot move this anywhere earlier because we do want to + * know if --root was given explicitly from the comand line. + */ + rev.show_root_diff = 1; + if (cover_letter) { /* remember the range */ int i; From ae35785e3a205cea04d228b00461f4906548fcc3 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Fri, 2 Jan 2009 13:49:30 +0100 Subject: [PATCH 096/231] gitweb: suggest name for OPML view Suggest opml.xml as name for OPML view by providing the appropriate header, consistently with similar usage in project_index view. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b16400193d..995bc1a6a9 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -6122,7 +6122,11 @@ sub git_atom { sub git_opml { my @list = git_get_projects_list(); - print $cgi->header(-type => 'text/xml', -charset => 'utf-8'); + print $cgi->header( + -type => 'text/xml', + -charset => 'utf-8', + -content_disposition => 'inline; filename="opml.xml"'); + print < From 7eb5bbdb645e04d746bc0edea102744f50a5e529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adeodato=20Sim=C3=B3?= Date: Fri, 9 Jan 2009 18:30:05 +0100 Subject: [PATCH 097/231] t7501-commit.sh: explicitly check that -F prevents invoking the editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "--signoff" test case in t7500-commit.sh was setting VISUAL while using -F -, which indeed tested that the editor is not spawned with -F. However, having it there was confusing, since there was no obvious reason to the casual reader for it to be there. This commits removes the setting of VISUAL from the --signoff test, and adds in t7501-commit.sh a dedicated test case, where the rest of tests for -F are. Signed-off-by: Adeodato Simó Okay-then-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t7500-commit.sh | 5 +---- t/t7501-commit.sh | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 6e18a96319..5998baf27b 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -149,10 +149,7 @@ EOF test_expect_success '--signoff' ' echo "yet another content *narf*" >> foo && - echo "zort" | ( - test_set_editor "$TEST_DIRECTORY"/t7500/add-content && - git commit -s -F - foo - ) && + echo "zort" | git commit -s -F - foo && git cat-file commit HEAD | sed "1,/^$/d" > output && test_cmp expect output ' diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 63bfc6d8b3..b4e2b4db84 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -127,6 +127,26 @@ test_expect_success \ "showing committed revisions" \ "git rev-list HEAD >current" +cat >editor <<\EOF +#!/bin/sh +sed -e "s/good/bad/g" < "$1" > "$1-" +mv "$1-" "$1" +EOF +chmod 755 editor + +cat >msg <file && + VISUAL=./editor git commit -a -F msg && + git show -s --pretty=format:"%s" | grep -q good && + echo "quack" >file && + echo "Another good message." | VISUAL=./editor git commit -a -F - && + git show -s --pretty=format:"%s" | grep -q good + ' # We could just check the head sha1, but checking each commit makes it # easier to isolate bugs. From 39c68542fc8d8477f2080c99efedb9dce975abc6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 7 Jan 2009 19:54:47 -0800 Subject: [PATCH 098/231] Wrap inflate and other zlib routines for better error reporting R. Tyler Ballance reported a mysterious transient repository corruption; after much digging, it turns out that we were not catching and reporting memory allocation errors from some calls we make to zlib. This one _just_ wraps things; it doesn't do the "retry on low memory error" part, at least not yet. It is an independent issue from the reporting. Some of the errors are expected and passed back to the caller, but we die when zlib reports it failed to allocate memory for now. Signed-off-by: Junio C Hamano --- builtin-apply.c | 5 ++-- builtin-pack-objects.c | 6 ++-- builtin-unpack-objects.c | 6 ++-- cache.h | 4 +++ http-push.c | 8 +++--- http-walker.c | 8 +++--- index-pack.c | 12 ++++---- sha1_file.c | 24 ++++++++-------- wrapper.c | 60 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 99 insertions(+), 34 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 50b623e54c..eface9793a 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1258,8 +1258,9 @@ static char *inflate_it(const void *data, unsigned long size, stream.avail_in = size; stream.next_out = out = xmalloc(inflated_size); stream.avail_out = inflated_size; - inflateInit(&stream); - st = inflate(&stream, Z_FINISH); + git_inflate_init(&stream); + st = git_inflate(&stream, Z_FINISH); + git_inflate_end(&stream); if ((st != Z_STREAM_END) || stream.total_out != inflated_size) { free(out); return NULL; diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index fb5e14d56e..85af80f229 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -195,16 +195,16 @@ static int check_pack_inflate(struct packed_git *p, int st; memset(&stream, 0, sizeof(stream)); - inflateInit(&stream); + git_inflate_init(&stream); do { in = use_pack(p, w_curs, offset, &stream.avail_in); stream.next_in = in; stream.next_out = fakebuf; stream.avail_out = sizeof(fakebuf); - st = inflate(&stream, Z_FINISH); + st = git_inflate(&stream, Z_FINISH); offset += stream.next_in - in; } while (st == Z_OK || st == Z_BUF_ERROR); - inflateEnd(&stream); + git_inflate_end(&stream); return (st == Z_STREAM_END && stream.total_out == expect && stream.total_in == len) ? 0 : -1; diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index 40b20f26e8..41d00d36cd 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -99,10 +99,10 @@ static void *get_data(unsigned long size) stream.avail_out = size; stream.next_in = fill(1); stream.avail_in = len; - inflateInit(&stream); + git_inflate_init(&stream); for (;;) { - int ret = inflate(&stream, 0); + int ret = git_inflate(&stream, 0); use(len - stream.avail_in); if (stream.total_out == size && ret == Z_STREAM_END) break; @@ -118,7 +118,7 @@ static void *get_data(unsigned long size) stream.next_in = fill(1); stream.avail_in = len; } - inflateEnd(&stream); + git_inflate_end(&stream); return buf; } diff --git a/cache.h b/cache.h index 42f2f2754b..17ce4441b6 100644 --- a/cache.h +++ b/cache.h @@ -12,6 +12,10 @@ #define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11) #endif +void git_inflate_init(z_streamp strm); +void git_inflate_end(z_streamp strm); +int git_inflate(z_streamp strm, int flush); + #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT) #define DTYPE(de) ((de)->d_type) #else diff --git a/http-push.c b/http-push.c index 6805288857..6d977ea6e1 100644 --- a/http-push.c +++ b/http-push.c @@ -208,7 +208,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, do { request->stream.next_out = expn; request->stream.avail_out = sizeof(expn); - request->zret = inflate(&request->stream, Z_SYNC_FLUSH); + request->zret = git_inflate(&request->stream, Z_SYNC_FLUSH); SHA1_Update(&request->c, expn, sizeof(expn) - request->stream.avail_out); } while (request->stream.avail_in && request->zret == Z_OK); @@ -268,7 +268,7 @@ static void start_fetch_loose(struct transfer_request *request) memset(&request->stream, 0, sizeof(request->stream)); - inflateInit(&request->stream); + git_inflate_init(&request->stream); SHA1_Init(&request->c); @@ -309,7 +309,7 @@ static void start_fetch_loose(struct transfer_request *request) file; also rewind to the beginning of the local file. */ if (prev_read == -1) { memset(&request->stream, 0, sizeof(request->stream)); - inflateInit(&request->stream); + git_inflate_init(&request->stream); SHA1_Init(&request->c); if (prev_posn>0) { prev_posn = 0; @@ -741,7 +741,7 @@ static void finish_request(struct transfer_request *request) if (request->http_code == 416) fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n"); - inflateEnd(&request->stream); + git_inflate_end(&request->stream); SHA1_Final(request->real_sha1, &request->c); if (request->zret != Z_STREAM_END) { unlink(request->tmpfile); diff --git a/http-walker.c b/http-walker.c index 9dc6b27b45..747d3adef8 100644 --- a/http-walker.c +++ b/http-walker.c @@ -82,7 +82,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, do { obj_req->stream.next_out = expn; obj_req->stream.avail_out = sizeof(expn); - obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH); + obj_req->zret = git_inflate(&obj_req->stream, Z_SYNC_FLUSH); SHA1_Update(&obj_req->c, expn, sizeof(expn) - obj_req->stream.avail_out); } while (obj_req->stream.avail_in && obj_req->zret == Z_OK); @@ -142,7 +142,7 @@ static void start_object_request(struct walker *walker, memset(&obj_req->stream, 0, sizeof(obj_req->stream)); - inflateInit(&obj_req->stream); + git_inflate_init(&obj_req->stream); SHA1_Init(&obj_req->c); @@ -183,7 +183,7 @@ static void start_object_request(struct walker *walker, file; also rewind to the beginning of the local file. */ if (prev_read == -1) { memset(&obj_req->stream, 0, sizeof(obj_req->stream)); - inflateInit(&obj_req->stream); + git_inflate_init(&obj_req->stream); SHA1_Init(&obj_req->c); if (prev_posn>0) { prev_posn = 0; @@ -243,7 +243,7 @@ static void finish_object_request(struct object_request *obj_req) return; } - inflateEnd(&obj_req->stream); + git_inflate_end(&obj_req->stream); SHA1_Final(obj_req->real_sha1, &obj_req->c); if (obj_req->zret != Z_STREAM_END) { unlink(obj_req->tmpfile); diff --git a/index-pack.c b/index-pack.c index c99a1a152c..c2d17bf613 100644 --- a/index-pack.c +++ b/index-pack.c @@ -271,10 +271,10 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size) stream.avail_out = size; stream.next_in = fill(1); stream.avail_in = input_len; - inflateInit(&stream); + git_inflate_init(&stream); for (;;) { - int ret = inflate(&stream, 0); + int ret = git_inflate(&stream, 0); use(input_len - stream.avail_in); if (stream.total_out == size && ret == Z_STREAM_END) break; @@ -283,7 +283,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size) stream.next_in = fill(1); stream.avail_in = input_len; } - inflateEnd(&stream); + git_inflate_end(&stream); return buf; } @@ -378,9 +378,9 @@ static void *get_data_from_pack(struct object_entry *obj) stream.avail_out = obj->size; stream.next_in = src; stream.avail_in = len; - inflateInit(&stream); - while ((st = inflate(&stream, Z_FINISH)) == Z_OK); - inflateEnd(&stream); + git_inflate_init(&stream); + while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK); + git_inflate_end(&stream); if (st != Z_STREAM_END || stream.total_out != obj->size) die("serious inflate inconsistency"); free(src); diff --git a/sha1_file.c b/sha1_file.c index 88035a0cd1..71c2282c9c 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1182,8 +1182,8 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon stream->avail_out = bufsiz; if (legacy_loose_object(map)) { - inflateInit(stream); - return inflate(stream, 0); + git_inflate_init(stream); + return git_inflate(stream, 0); } @@ -1203,7 +1203,7 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon /* Set up the stream for the rest.. */ stream->next_in = map; stream->avail_in = mapsize; - inflateInit(stream); + git_inflate_init(stream); /* And generate the fake traditional header */ stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", @@ -1240,11 +1240,11 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size stream->next_out = buf + bytes; stream->avail_out = size - bytes; while (status == Z_OK) - status = inflate(stream, Z_FINISH); + status = git_inflate(stream, Z_FINISH); } buf[size] = 0; if (status == Z_STREAM_END && !stream->avail_in) { - inflateEnd(stream); + git_inflate_end(stream); return buf; } @@ -1334,15 +1334,15 @@ unsigned long get_size_from_delta(struct packed_git *p, stream.next_out = delta_head; stream.avail_out = sizeof(delta_head); - inflateInit(&stream); + git_inflate_init(&stream); do { in = use_pack(p, w_curs, curpos, &stream.avail_in); stream.next_in = in; - st = inflate(&stream, Z_FINISH); + st = git_inflate(&stream, Z_FINISH); curpos += stream.next_in - in; } while ((st == Z_OK || st == Z_BUF_ERROR) && stream.total_out < sizeof(delta_head)); - inflateEnd(&stream); + git_inflate_end(&stream); if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) die("delta data unpack-initial failed"); @@ -1550,14 +1550,14 @@ static void *unpack_compressed_entry(struct packed_git *p, stream.next_out = buffer; stream.avail_out = size; - inflateInit(&stream); + git_inflate_init(&stream); do { in = use_pack(p, w_curs, curpos, &stream.avail_in); stream.next_in = in; - st = inflate(&stream, Z_FINISH); + st = git_inflate(&stream, Z_FINISH); curpos += stream.next_in - in; } while (st == Z_OK || st == Z_BUF_ERROR); - inflateEnd(&stream); + git_inflate_end(&stream); if ((st != Z_STREAM_END) || stream.total_out != size) { free(buffer); return NULL; @@ -1965,7 +1965,7 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size status = error("unable to parse %s header", sha1_to_hex(sha1)); else if (sizep) *sizep = size; - inflateEnd(&stream); + git_inflate_end(&stream); munmap(map, mapsize); return status; } diff --git a/wrapper.c b/wrapper.c index 93562f03ee..c85ca52ec6 100644 --- a/wrapper.c +++ b/wrapper.c @@ -196,3 +196,63 @@ int xmkstemp(char *template) die("Unable to create temporary file: %s", strerror(errno)); return fd; } + +/* + * zlib wrappers to make sure we don't silently miss errors + * at init time. + */ +void git_inflate_init(z_streamp strm) +{ + const char *err; + + switch (inflateInit(strm)) { + case Z_OK: + return; + + case Z_MEM_ERROR: + err = "out of memory"; + break; + case Z_VERSION_ERROR: + err = "wrong version"; + break; + default: + err = "error"; + } + die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message"); +} + +void git_inflate_end(z_streamp strm) +{ + if (inflateEnd(strm) != Z_OK) + error("inflateEnd: %s", strm->msg ? strm->msg : "failed"); +} + +int git_inflate(z_streamp strm, int flush) +{ + int ret = inflate(strm, flush); + const char *err; + + switch (ret) { + /* Out of memory is fatal. */ + case Z_MEM_ERROR: + die("inflate: out of memory"); + + /* Data corruption errors: we may want to recover from them (fsck) */ + case Z_NEED_DICT: + err = "needs dictionary"; break; + case Z_DATA_ERROR: + err = "data stream error"; break; + case Z_STREAM_ERROR: + err = "stream consistency error"; break; + default: + err = "unknown error"; break; + + /* Z_BUF_ERROR: normal, needs more space in the output buffer */ + case Z_BUF_ERROR: + case Z_OK: + case Z_STREAM_END: + return ret; + } + error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message"); + return ret; +} From 9002ec3ae6299dc7872033ba7f6c3cebf2f2c3c1 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 11 Jan 2009 06:25:06 -0500 Subject: [PATCH 099/231] Makefile: clean up TEST_PROGRAMS definition We try to keep lines under 80 characters, not to mention that sticking a bunch of stuff on one line makes diffs messier. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dee97c1b01..2b873fa99f 100644 --- a/Makefile +++ b/Makefile @@ -1356,7 +1356,14 @@ endif ### Testing rules -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X +TEST_PROGRAMS += test-chmtime$X +TEST_PROGRAMS += test-date$X +TEST_PROGRAMS += test-delta$X +TEST_PROGRAMS += test-genrandom$X +TEST_PROGRAMS += test-match-trees$X +TEST_PROGRAMS += test-parse-options$X +TEST_PROGRAMS += test-path-utils$X +TEST_PROGRAMS += test-sha1$X all:: $(TEST_PROGRAMS) From 8ca12c0d62c0be4a4987c4a936467ea2a92e915a Mon Sep 17 00:00:00 2001 From: Alexander Potashev Date: Sat, 10 Jan 2009 15:07:50 +0300 Subject: [PATCH 100/231] add is_dot_or_dotdot inline function A new inline function is_dot_or_dotdot is used to check if the directory name is either "." or "..". It returns a non-zero value if the given string is "." or "..". It's applicable to a lot of Git source code. Signed-off-by: Alexander Potashev Signed-off-by: Junio C Hamano --- builtin-count-objects.c | 5 ++--- builtin-fsck.c | 14 ++++---------- builtin-prune.c | 14 ++++---------- builtin-rerere.c | 11 +++++------ dir.c | 12 ++++-------- dir.h | 7 +++++++ entry.c | 5 ++--- remote.c | 6 ++---- transport.c | 4 +--- 9 files changed, 31 insertions(+), 47 deletions(-) diff --git a/builtin-count-objects.c b/builtin-count-objects.c index ab35b65b07..62fd1f0961 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -5,6 +5,7 @@ */ #include "cache.h" +#include "dir.h" #include "builtin.h" #include "parse-options.h" @@ -21,9 +22,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose, const char *cp; int bad = 0; - if ((ent->d_name[0] == '.') && - (ent->d_name[1] == 0 || - ((ent->d_name[1] == '.') && (ent->d_name[2] == 0)))) + if (is_dot_or_dotdot(ent->d_name)) continue; for (cp = ent->d_name; *cp; cp++) { int ch = *cp; diff --git a/builtin-fsck.c b/builtin-fsck.c index 297b2c41c6..79b87edaf9 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -10,6 +10,7 @@ #include "tree-walk.h" #include "fsck.h" #include "parse-options.h" +#include "dir.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -395,19 +396,12 @@ static void fsck_dir(int i, char *path) while ((de = readdir(dir)) != NULL) { char name[100]; unsigned char sha1[20]; - int len = strlen(de->d_name); - switch (len) { - case 2: - if (de->d_name[1] != '.') - break; - case 1: - if (de->d_name[0] != '.') - break; + if (is_dot_or_dotdot(de->d_name)) continue; - case 38: + if (strlen(de->d_name) == 38) { sprintf(name, "%02x", i); - memcpy(name+2, de->d_name, len+1); + memcpy(name+2, de->d_name, 39); if (get_sha1_hex(name, sha1) < 0) break; add_sha1_list(sha1, DIRENT_SORT_HINT(de)); diff --git a/builtin-prune.c b/builtin-prune.c index 7b4ec80e62..545e9c1f94 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -5,6 +5,7 @@ #include "builtin.h" #include "reachable.h" #include "parse-options.h" +#include "dir.h" static const char * const prune_usage[] = { "git prune [-n] [-v] [--expire
CommitLineData