From bc9e7399af3790918140c30a5b2c85bf9a8f1ad3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Jul 2007 01:38:32 -0700 Subject: [PATCH 01/97] stash: implement "stash create" This subcommand creates a stash from the current state and writes out the resulting commit object ID to the standard output, without updating the stash ref nor resetting the tree. It is intended to be used by scripts to temporarily rewind the working tree to a clean state. Signed-off-by: Junio C Hamano --- git-stash.sh | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/git-stash.sh b/git-stash.sh index 7ba61625ba..04af892531 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -25,19 +25,13 @@ clear_stash () { fi } -save_stash () { +create_stash () { stash_msg="$1" if no_changes then - echo >&2 'No local changes to save' exit 0 fi - test -f "$GIT_DIR/logs/$ref_stash" || - clear_stash || die "Cannot initialize stash" - - # Make sure the reflog for stash is kept. - : >>"$GIT_DIR/logs/$ref_stash" # state of the base commit if b_commit=$(git rev-parse --verify HEAD) @@ -84,7 +78,20 @@ save_stash () { w_commit=$(printf '%s\n' "$stash_msg" | git commit-tree $w_tree -p $b_commit -p $i_commit) || die "Cannot record working tree state" +} +save_stash () { + stash_msg="$1" + + if no_changes + then + echo >&2 'No local changes to save' + exit 0 + fi + test -f "$GIT_DIR/logs/$ref_stash" || + clear_stash || die "Cannot initialize stash" + + create_stash "$stash_msg" git update-ref -m "$stash_msg" $ref_stash $w_commit || die "Cannot save the current status" printf >&2 'Saved "%s"\n' "$stash_msg" @@ -202,6 +209,13 @@ apply) clear) clear_stash ;; +create) + if test $# -gt 0 && test "$1" = create + then + shift + fi + create_stash "$*" && echo "$w_commit" + ;; help | usage) usage ;; From 6c9ad166dbbf9e5a0c09450b892151dbec49b8dc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Jul 2007 00:51:23 -0700 Subject: [PATCH 02/97] rebase: allow starting from a dirty tree. This uses the new "git stash create" interface to stash away the dirty state you have in your working tree before starting a rebase, and then replaying it when you are done with stashing. Signed-off-by: Junio C Hamano --- git-rebase.sh | 72 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index c9942f2400..e342974dc0 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -116,10 +116,30 @@ call_merge () { } finish_rb_merge () { + if test -f "$dotest/stash" + then + stash=$(cat "$dotest/stash") + git stash apply --index "$stash" + fi rm -r "$dotest" echo "All done." } +read_stash () { + if test -f "$1" + then + cat "$1" + fi +} +unstash_and_exit () { + err=$? + if test -f "$1" && test $err = 0 + then + git stash apply --index "$1" + fi + exit $err +} + is_interactive () { test -f "$dotest"/interactive || while case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac @@ -154,8 +174,9 @@ do finish_rb_merge exit fi + stash=$(read_stash ".dotest/stash") git am --resolved --3way --resolvemsg="$RESOLVEMSG" - exit + unstash_and_exit "$stash" ;; --skip) if test -d "$dotest" @@ -174,21 +195,31 @@ do finish_rb_merge exit fi + stash=$(read_stash ".dotest/stash") git am -3 --skip --resolvemsg="$RESOLVEMSG" - exit + unstash_and_exit "$stash" ;; --abort) git rerere clear if test -d "$dotest" then + if test -f "$dotest/stash" + then + stash=$(cat "$dotest/stash") + fi rm -r "$dotest" elif test -d .dotest then + if test -f ".dotest/stash" + then + stash=$(cat ".dotest/stash") + fi rm -r .dotest else die "No rebase in progress?" fi git reset --hard ORIG_HEAD + test -z "$stash" || git stash apply --index "$stash" exit ;; --onto) @@ -254,16 +285,6 @@ else fi fi -# The tree must be really really clean. -git update-index --refresh || exit -diff=$(git diff-index --cached --name-status -r HEAD) -case "$diff" in -?*) echo "cannot rebase: your index is not up-to-date" - echo "$diff" - exit 1 - ;; -esac - # The upstream head must be given. Make sure it is valid. upstream_name="$1" upstream=`git rev-parse --verify "${upstream_name}^0"` || @@ -273,11 +294,19 @@ upstream=`git rev-parse --verify "${upstream_name}^0"` || onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit +# The tree must be clean enough for us to create a stash +stash=$(git stash create) || exit +if test -n "$stash" +then + echo >&2 "Stashed away your working tree changes" +fi + # If a hook exists, give it a chance to interrupt if test -x "$GIT_DIR/hooks/pre-rebase" then "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { echo >&2 "The pre-rebase hook refused to rebase." + test -z "$stash" || git stash apply --index "$stash" exit 1 } fi @@ -286,7 +315,10 @@ fi case "$#" in 2) branch_name="$2" - git-checkout "$2" || usage + git-checkout "$2" || { + test -z "$stash" || git stash apply --index "$stash" + usage + } ;; *) if branch_name=`git symbolic-ref -q HEAD` @@ -309,6 +341,7 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" && ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null then echo >&2 "Current branch $branch_name is up to date." + test -z "$stash" || git stash apply --index "$stash" exit 0 fi @@ -328,6 +361,7 @@ git-reset --hard "$onto" if test "$mb" = "$branch" then echo >&2 "Fast-forwarded $branch_name to $onto_name." + test -z "$stash" || git stash apply --index "$stash" exit 0 fi @@ -335,7 +369,16 @@ if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" - exit $? + err=$? + + if test $err = 0 + then + test -z "$stash" || git stash apply --index "$stash" + exit + else + test -z "$stash" || echo "$stash" >.dotest/stash + exit $err + fi fi # start doing a rebase with git-merge @@ -346,6 +389,7 @@ echo "$onto" > "$dotest/onto" echo "$onto_name" > "$dotest/onto_name" prev_head=`git rev-parse HEAD^0` echo "$prev_head" > "$dotest/prev_head" +test -z "$stash" || echo "$stash" >"$dotest/stash" msgnum=0 for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` From 0f49327c9755b6575b447f79b540749d231cb26d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Nov 2007 13:46:20 -0700 Subject: [PATCH 03/97] Revert "rebase: allow starting from a dirty tree." This reverts commit 6c9ad166dbbf9e5a0c09450b892151dbec49b8dc. Allowing rebase to start in a dirty tree might have been a worthy goal, but it is not necessarily always wanted (some people prefer to be reminded that the state is dirty, and think about the next action that may not be to stash and proceed). Furthermore, depending on the nature of local changes, unstashing the dirty state on top of the rebased result is not always desirable. Signed-off-by: Junio C Hamano --- git-rebase.sh | 72 ++++++++++----------------------------------------- 1 file changed, 14 insertions(+), 58 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index e342974dc0..c9942f2400 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -116,30 +116,10 @@ call_merge () { } finish_rb_merge () { - if test -f "$dotest/stash" - then - stash=$(cat "$dotest/stash") - git stash apply --index "$stash" - fi rm -r "$dotest" echo "All done." } -read_stash () { - if test -f "$1" - then - cat "$1" - fi -} -unstash_and_exit () { - err=$? - if test -f "$1" && test $err = 0 - then - git stash apply --index "$1" - fi - exit $err -} - is_interactive () { test -f "$dotest"/interactive || while case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac @@ -174,9 +154,8 @@ do finish_rb_merge exit fi - stash=$(read_stash ".dotest/stash") git am --resolved --3way --resolvemsg="$RESOLVEMSG" - unstash_and_exit "$stash" + exit ;; --skip) if test -d "$dotest" @@ -195,31 +174,21 @@ do finish_rb_merge exit fi - stash=$(read_stash ".dotest/stash") git am -3 --skip --resolvemsg="$RESOLVEMSG" - unstash_and_exit "$stash" + exit ;; --abort) git rerere clear if test -d "$dotest" then - if test -f "$dotest/stash" - then - stash=$(cat "$dotest/stash") - fi rm -r "$dotest" elif test -d .dotest then - if test -f ".dotest/stash" - then - stash=$(cat ".dotest/stash") - fi rm -r .dotest else die "No rebase in progress?" fi git reset --hard ORIG_HEAD - test -z "$stash" || git stash apply --index "$stash" exit ;; --onto) @@ -285,6 +254,16 @@ else fi fi +# The tree must be really really clean. +git update-index --refresh || exit +diff=$(git diff-index --cached --name-status -r HEAD) +case "$diff" in +?*) echo "cannot rebase: your index is not up-to-date" + echo "$diff" + exit 1 + ;; +esac + # The upstream head must be given. Make sure it is valid. upstream_name="$1" upstream=`git rev-parse --verify "${upstream_name}^0"` || @@ -294,19 +273,11 @@ upstream=`git rev-parse --verify "${upstream_name}^0"` || onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit -# The tree must be clean enough for us to create a stash -stash=$(git stash create) || exit -if test -n "$stash" -then - echo >&2 "Stashed away your working tree changes" -fi - # If a hook exists, give it a chance to interrupt if test -x "$GIT_DIR/hooks/pre-rebase" then "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { echo >&2 "The pre-rebase hook refused to rebase." - test -z "$stash" || git stash apply --index "$stash" exit 1 } fi @@ -315,10 +286,7 @@ fi case "$#" in 2) branch_name="$2" - git-checkout "$2" || { - test -z "$stash" || git stash apply --index "$stash" - usage - } + git-checkout "$2" || usage ;; *) if branch_name=`git symbolic-ref -q HEAD` @@ -341,7 +309,6 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" && ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null then echo >&2 "Current branch $branch_name is up to date." - test -z "$stash" || git stash apply --index "$stash" exit 0 fi @@ -361,7 +328,6 @@ git-reset --hard "$onto" if test "$mb" = "$branch" then echo >&2 "Fast-forwarded $branch_name to $onto_name." - test -z "$stash" || git stash apply --index "$stash" exit 0 fi @@ -369,16 +335,7 @@ if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" - err=$? - - if test $err = 0 - then - test -z "$stash" || git stash apply --index "$stash" - exit - else - test -z "$stash" || echo "$stash" >.dotest/stash - exit $err - fi + exit $? fi # start doing a rebase with git-merge @@ -389,7 +346,6 @@ echo "$onto" > "$dotest/onto" echo "$onto_name" > "$dotest/onto_name" prev_head=`git rev-parse HEAD^0` echo "$prev_head" > "$dotest/prev_head" -test -z "$stash" || echo "$stash" >"$dotest/stash" msgnum=0 for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` From a64d7784e830b3140e7d0f2b45cb3d8fafb84cca Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Nov 2007 14:30:30 -0700 Subject: [PATCH 04/97] git-merge: no reason to use cpio anymore Now we have "git stash create", we can use it to safely stash away the dirty state in the tree. Signed-off-by: Junio C Hamano --- git-merge.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/git-merge.sh b/git-merge.sh index 3a01db0d75..976117ac90 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -28,20 +28,19 @@ allow_trivial_merge=t dropsave() { rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ - "$GIT_DIR/MERGE_SAVE" || exit 1 + "$GIT_DIR/MERGE_STASH" || exit 1 } savestate() { # Stash away any local modifications. - git diff-index -z --name-only $head | - cpio -0 -o >"$GIT_DIR/MERGE_SAVE" + git stash create >"$GIT_DIR/MERGE_STASH" } restorestate() { - if test -f "$GIT_DIR/MERGE_SAVE" + if test -f "$GIT_DIR/MERGE_STASH" then git reset --hard $head >/dev/null - cpio -iuv <"$GIT_DIR/MERGE_SAVE" + git stash apply $(cat "$GIT_DIR/MERGE_STASH") git update-index --refresh >/dev/null fi } @@ -386,7 +385,7 @@ case "$use_strategies" in single_strategy=no ;; *) - rm -f "$GIT_DIR/MERGE_SAVE" + rm -f "$GIT_DIR/MERGE_STASH" single_strategy=yes ;; esac From 648ee5500920b49a5ee5822076104b9304ec97c8 Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Wed, 31 Oct 2007 23:12:20 +0100 Subject: [PATCH 05/97] cvsexportcommit: Add switch to specify CVS workdir Signed-off-by: Robin Rosenberg Signed-off-by: Junio C Hamano --- Documentation/git-cvsexportcommit.txt | 22 +++++++++++---- git-cvsexportcommit.perl | 39 ++++++++++++++++++--------- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index c3922f9238..3f9d2295d3 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout SYNOPSIS -------- -'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID +'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID DESCRIPTION @@ -16,8 +16,9 @@ DESCRIPTION Exports a commit from GIT to a CVS checkout, making it easier to merge patches from a git repository into a CVS repository. -Execute it from the root of the CVS working copy. GIT_DIR must be defined. -See examples below. +Specify the name of a CVS checkout using the -w switch or execute it +from the root of the CVS working copy. In the latter case GIT_DIR must +be defined. See examples below. It does its best to do the safe thing, it will check that the files are unchanged and up to date in the CVS checkout, and it will not autocommit @@ -61,6 +62,11 @@ OPTIONS -u:: Update affected files from CVS repository before attempting export. +-w:: + Specify the location of the CVS checkout to use for the export. This + option does not require GIT_DIR to be set before execution if the + current directory is within a git repository. + -v:: Verbose. @@ -76,6 +82,12 @@ $ git-cvsexportcommit -v $ cvs commit -F .msg ------------ +Merge one patch into CVS (-c and -w options). The working directory is within the Git Repo:: ++ +------------ + $ git-cvsexportcommit -v -c -w ~/project_cvs_checkout +------------ + Merge pending patches into CVS automatically -- only if you really know what you are doing:: + ------------ @@ -86,11 +98,11 @@ $ git-cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git-cvsexportcommit Author ------ -Written by Martin Langhoff +Written by Martin Langhoff and others. Documentation -------------- -Documentation by Martin Langhoff +Documentation by Martin Langhoff and others. GIT --- diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 26844af439..92e41620fd 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -1,28 +1,42 @@ #!/usr/bin/perl -w -# Known limitations: -# - does not propagate permissions -# - error handling has not been extensively tested -# - use strict; use Getopt::Std; use File::Temp qw(tempdir); use Data::Dumper; use File::Basename qw(basename dirname); -unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){ - die "GIT_DIR is not defined or is unreadable"; -} +our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w); -our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u); - -getopts('uhPpvcfam:d:'); +getopts('uhPpvcfam:d:w:'); $opt_h && usage(); die "Need at least one commit identifier!" unless @ARGV; +if ($opt_w) { + unless ($ENV{GIT_DIR}) { + # Remember where our GIT_DIR is before changing to CVS checkout + my $gd =`git-rev-parse --git-dir`; + chomp($gd); + if ($gd eq '.git') { + my $wd = `pwd`; + chomp($wd); + $gd = $wd."/.git" ; + } + $ENV{GIT_DIR} = $gd; + } + + if (! -d $opt_w."/CVS" ) { + die "$opt_w is not a CVS checkout"; + } + chdir $opt_w or die "Cannot change to CVS checkout at $opt_w"; +} +unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){ + die "GIT_DIR is not defined or is unreadable"; +} + + my @cvs; if ($opt_d) { @cvs = ('cvs', '-d', $opt_d); @@ -274,6 +288,7 @@ if ($dirtypatch) { print "You'll need to apply the patch in .cvsexportcommit.diff manually\n"; print "using a patch program. After applying the patch and resolving the\n"; print "problems you may commit using:"; + print "\n cd \"$opt_w\"" if $opt_w; print "\n $cmd\n\n"; exit(1); } @@ -301,7 +316,7 @@ sleep(1); sub usage { print STDERR < Date: Sun, 4 Nov 2007 00:22:42 -0400 Subject: [PATCH 06/97] fix display overlap between remote and local progress It is possible for the remote summary line to be displayed over the local progress display line, and therefore that local progress gets bumped to the next line. However, if the progress line is long enough, it might not be entirely overwritten by the remote summary line. This creates a messed up display such as: remote: Total 310 (delta 160), reused 178 (delta 112)iB/s Receiving objects: 100% (310/310), 379.98 KiB | 136 KiB/s, done. So we have to clear the screen line before displaying the remote message to make sure the local progress is not visible anymore on the first line. Yet some Git versions on the remote side might be sending updates to the same line and terminate it with \r, and a separate packet with a single \n might be sent later when the progress display is done. This means the screen line must *not* be cleared in that case. Since the sideband code already has to figure out line breaks in the received packet to properly prepend the "remote:" prefix, we can easily determine if the remote line about to be displayed is empty. Only when it is not then a proper suffix is inserted before the \r or \n to clear the end of the screen line. Also some magic constants related to the prefix length have been replaced with a variable, making it similar to the suffix length handling. Since gcc is smart enough to detect that the variable is constant there is no impact on the generated code. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- sideband.c | 51 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/sideband.c b/sideband.c index ab8a1e990d..58edea68ee 100644 --- a/sideband.c +++ b/sideband.c @@ -11,13 +11,19 @@ * stream, aka "verbose"). A message over band #3 is a signal that * the remote died unexpectedly. A flush() concludes the stream. */ + +#define PREFIX "remote:" +#define SUFFIX "\e[K" /* change to " " if ANSI sequences don't work */ + int recv_sideband(const char *me, int in_stream, int out, int err) { - char buf[7 + LARGE_PACKET_MAX + 1]; - strcpy(buf, "remote:"); + unsigned pf = strlen(PREFIX); + unsigned sf = strlen(SUFFIX); + char buf[pf + LARGE_PACKET_MAX + sf + 1]; + memcpy(buf, PREFIX, pf); while (1) { int band, len; - len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); + len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { @@ -25,35 +31,52 @@ int recv_sideband(const char *me, int in_stream, int out, int err) safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } - band = buf[7] & 0xff; + band = buf[pf] & 0xff; len--; switch (band) { case 3: - buf[7] = ' '; - buf[8+len] = '\n'; - safe_write(err, buf, 8+len+1); + buf[pf] = ' '; + buf[pf+1+len] = '\n'; + safe_write(err, buf, pf+1+len+1); return SIDEBAND_REMOTE_ERROR; case 2: - buf[7] = ' '; - len += 8; + buf[pf] = ' '; + len += pf+1; while (1) { - int brk = 8; + int brk = pf+1; + + /* Break the buffer into separate lines. */ while (brk < len) { brk++; if (buf[brk-1] == '\n' || buf[brk-1] == '\r') break; } - safe_write(err, buf, brk); + + /* + * Let's insert a suffix to clear the end + * of the screen line, but only if current + * line data actually contains something. + */ + if (brk > pf+1 + 1) { + char save[sf]; + memcpy(save, buf + brk, sf); + buf[brk + sf - 1] = buf[brk - 1]; + memcpy(buf + brk - 1, SUFFIX, sf); + safe_write(err, buf, brk + sf); + memcpy(buf + brk, save, sf); + } else + safe_write(err, buf, brk); + if (brk < len) { - memmove(buf + 8, buf + brk, len - brk); - len = len - brk + 8; + memmove(buf + pf+1, buf + brk, len - brk); + len = len - brk + pf+1; } else break; } continue; case 1: - safe_write(out, buf+8, len); + safe_write(out, buf + pf+1, len); continue; default: len = sprintf(buf, From 49604a4d62d9055dbfc06472e7ef20d8a6114123 Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Sat, 3 Nov 2007 23:38:24 -0400 Subject: [PATCH 07/97] format-patch: Add configuration and off switch for --numbered format.numbered is a tri-state variable. Boolean values enable or disable numbering by default and "auto" enables number when outputting more than one patch. --no-numbered (short: -N) will disable numbering. Signed-off-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- Documentation/config.txt | 6 ++++++ Documentation/git-format-patch.txt | 12 ++++++++---- builtin-log.c | 19 ++++++++++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 0df004ea26..268839da73 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -432,6 +432,12 @@ fetch.unpackLimit:: pack from a push can make the push operation complete faster, especially on slow filesystems. +format.numbered:: + A boolean which can enable sequence numbers in patch subjects. + Seting this option to "auto" will enable it only if there is + more than one patch. See --numbered option in + gitlink:git-format-patch[1]. + format.headers:: Additional email headers to include in a patch to be submitted by mail. See gitlink:git-format-patch[1]. diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index f0617efa0a..92c0ab63b2 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -9,7 +9,7 @@ git-format-patch - Prepare patches for e-mail submission SYNOPSIS -------- [verse] -'git-format-patch' [-n | -k] [-o | --stdout] [--thread] +'git-format-patch' [-n | -N | -k] [-o | --stdout] [--thread] [--attach[=] | --inline[=]] [-s | --signoff] [] [--start-number ] [--numbered-files] @@ -77,6 +77,9 @@ include::diff-options.txt[] -n|--numbered:: Name output in '[PATCH n/m]' format. +-N|--no-numbered:: + Name output in '[PATCH]' format. + --start-number :: Start numbering the patches at instead of 1. @@ -142,15 +145,16 @@ not add any suffix. CONFIGURATION ------------- -You can specify extra mail header lines to be added to each -message in the repository configuration. You can also specify -new defaults for the subject prefix and file suffix. +You can specify extra mail header lines to be added to each message +in the repository configuration, new defaults for the subject prefix +and file suffix, and number patches when outputting more than one. ------------ [format] headers = "Organization: git-foo\n" subjectprefix = CHANGE suffix = .txt + numbered = auto ------------ diff --git a/builtin-log.c b/builtin-log.c index 8b2bf632c5..c5230106a0 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -273,6 +273,8 @@ static int istitlechar(char c) static char *extra_headers = NULL; static int extra_headers_size = 0; static const char *fmt_patch_suffix = ".patch"; +static int numbered = 0; +static int auto_number = 0; static int git_format_config(const char *var, const char *value) { @@ -297,6 +299,15 @@ static int git_format_config(const char *var, const char *value) if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { return 0; } + if (!strcmp(var, "format.numbered")) { + if (!strcasecmp(value, "auto")) { + auto_number = 1; + return 0; + } + + numbered = git_config_bool(var, value); + return 0; + } return git_log_config(var, value); } @@ -466,7 +477,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct rev_info rev; int nr = 0, total, i, j; int use_stdout = 0; - int numbered = 0; int start_number = -1; int keep_subject = 0; int numbered_files = 0; /* _just_ numbers */ @@ -503,6 +513,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; + else if (!strcmp(argv[i], "-N") || + !strcmp(argv[i], "--no-numbered")) { + numbered = 0; + auto_number = 0; + } else if (!prefixcmp(argv[i], "--start-number=")) start_number = strtol(argv[i] + 15, NULL, 10); else if (!strcmp(argv[i], "--numbered-files")) @@ -642,6 +657,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) list[nr - 1] = commit; } total = nr; + if (!keep_subject && auto_number && total > 1) + numbered = 1; if (numbered) rev.total = total + start_number - 1; rev.add_signoff = add_signoff; From e90ecb68178b41357f40b058aa760c2338974c13 Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Sun, 4 Nov 2007 01:50:23 -0400 Subject: [PATCH 08/97] format-patch: Test --[no-]numbered and format.numbered Just because there wasn't a test for --numbered isn't a good reason not to test format.numbered. So now we test both. Signed-off-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- t/t4021-format-patch-numbered.sh | 106 +++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 t/t4021-format-patch-numbered.sh diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh new file mode 100755 index 0000000000..43d64bbd82 --- /dev/null +++ b/t/t4021-format-patch-numbered.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) 2006 Brian C Gernhardt +# + +test_description='Format-patch numbering options' + +. ./test-lib.sh + +test_expect_success setup ' + + echo A > file && + git add file && + git commit -m First && + + echo B >> file && + git commit -a -m Second && + + echo C >> file && + git commit -a -m Third + +' + +# Each of these gets used multiple times. + +test_num_no_numbered() { + cnt=$(grep "^Subject: \[PATCH\]" $1 | wc -l) && + test $cnt = $2 +} + +test_single_no_numbered() { + test_num_no_numbered $1 1 +} + +test_no_numbered() { + test_num_no_numbered $1 2 +} + +test_single_numbered() { + grep "^Subject: \[PATCH 1/1\]" $1 +} + +test_numbered() { + grep "^Subject: \[PATCH 1/2\]" $1 && + grep "^Subject: \[PATCH 2/2\]" $1 +} + +test_expect_success 'Default: no numbered' ' + + git format-patch --stdout HEAD~2 >patch0 && + test_no_numbered patch0 + +' + +test_expect_success 'Use --numbered' ' + + git format-patch --numbered --stdout HEAD~2 >patch1 && + test_numbered patch1 + +' + +test_expect_success 'format.numbered = true' ' + + git config format.numbered true && + git format-patch --stdout HEAD~2 >patch2 && + test_numbered patch2 + +' + +test_expect_success 'format.numbered && single patch' ' + + git format-patch --stdout HEAD^ > patch3 && + test_single_numbered patch3 + +' + +test_expect_success 'format.numbered && --no-numbered' ' + + git format-patch --no-numbered --stdout HEAD~2 >patch4 && + test_no_numbered patch4 + +' + +test_expect_success 'format.numbered = auto' ' + + git config format.numbered auto + git format-patch --stdout HEAD~2 > patch5 && + test_numbered patch5 + +' + +test_expect_success 'format.numbered = auto && single patch' ' + + git format-patch --stdout HEAD^ > patch6 && + test_single_no_numbered patch6 + +' + +test_expect_success 'format.numbered = auto && --no-numbered' ' + + git format-patch --no-numbered --stdout HEAD~2 > patch7 && + test_no_numbered patch7 + +' + +test_done From 0d8aafd25271c8d1cf185019437e21362edc1bc7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sun, 4 Nov 2007 20:07:29 -0500 Subject: [PATCH 09/97] sideband.c: ESC is spelled '\033' not '\e' for portability. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- sideband.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sideband.c b/sideband.c index 58edea68ee..756bbc28d7 100644 --- a/sideband.c +++ b/sideband.c @@ -13,7 +13,7 @@ */ #define PREFIX "remote:" -#define SUFFIX "\e[K" /* change to " " if ANSI sequences don't work */ +#define SUFFIX "\033[K" /* change to " " if ANSI sequences don't work */ int recv_sideband(const char *me, int in_stream, int out, int err) { From 218558af599c01e5dec17a7399d9188a76c50203 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sun, 4 Nov 2007 22:15:41 -0500 Subject: [PATCH 10/97] make display of total transferred more accurate The throughput display needs a delay period before accounting and displaying anything. Yet it might be called after some amount of data has already been transferred. The display of total data is therefore accounted late and therefore smaller than the reality. Let's call display_throughput() with an absolute amount of transferred data instead of a relative number, and let the throughput code find the relative amount of data by itself as needed. This way the displayed total is always exact. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- csum-file.c | 4 +++- csum-file.h | 1 + index-pack.c | 4 ++-- progress.c | 46 ++++++++++++++++++++++------------------------ progress.h | 2 +- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/csum-file.c b/csum-file.c index 3729e73e19..b445e6a2e5 100644 --- a/csum-file.c +++ b/csum-file.c @@ -18,7 +18,8 @@ static void sha1flush(struct sha1file *f, unsigned int count) for (;;) { int ret = xwrite(f->fd, buf, count); if (ret > 0) { - display_throughput(f->tp, ret); + f->total += ret; + display_throughput(f->tp, f->total); buf = (char *) buf + ret; count -= ret; if (count) @@ -101,6 +102,7 @@ struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp f->fd = fd; f->error = 0; f->offset = 0; + f->total = 0; f->tp = tp; f->do_crc = 0; SHA1_Init(&f->ctx); diff --git a/csum-file.h b/csum-file.h index 4d1b231292..a38cc3a2d7 100644 --- a/csum-file.h +++ b/csum-file.h @@ -8,6 +8,7 @@ struct sha1file { int fd, error; unsigned int offset, namelen; SHA_CTX ctx; + off_t total; struct progress *tp; char name[PATH_MAX]; int do_crc; diff --git a/index-pack.c b/index-pack.c index 715a5bb7a6..581a7f5650 100644 --- a/index-pack.c +++ b/index-pack.c @@ -87,9 +87,9 @@ static void *fill(int min) die("early EOF"); die("read error on input: %s", strerror(errno)); } - if (from_stdin) - display_throughput(progress, ret); input_len += ret; + if (from_stdin) + display_throughput(progress, consumed_bytes + input_len); } while (input_len < min); return input_buffer; } diff --git a/progress.c b/progress.c index 3f6a602a53..a963bd8bfc 100644 --- a/progress.c +++ b/progress.c @@ -14,11 +14,10 @@ #define TP_IDX_MAX 8 struct throughput { + off_t prev_total; struct timeval prev_tv; - off_t total; - unsigned long count; - unsigned long avg_bytes; - unsigned long last_bytes[TP_IDX_MAX]; + unsigned int avg_bytes; + unsigned int last_bytes[TP_IDX_MAX]; unsigned int avg_misecs; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; @@ -110,7 +109,7 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } -void display_throughput(struct progress *progress, unsigned long n) +void display_throughput(struct progress *progress, off_t total) { struct throughput *tp; struct timeval tv; @@ -124,14 +123,13 @@ void display_throughput(struct progress *progress, unsigned long n) if (!tp) { progress->throughput = tp = calloc(1, sizeof(*tp)); - if (tp) + if (tp) { + tp->prev_total = total; tp->prev_tv = tv; + } return; } - tp->total += n; - tp->count += n; - /* * We have x = bytes and y = microsecs. We want z = KiB/s: * @@ -152,37 +150,37 @@ void display_throughput(struct progress *progress, unsigned long n) if (misecs > 512) { int l = sizeof(tp->display); + unsigned int count = total - tp->prev_total; + tp->prev_total = total; tp->prev_tv = tv; - tp->avg_bytes += tp->count; + tp->avg_bytes += count; tp->avg_misecs += misecs; - if (tp->total > 1 << 30) { + if (total > 1 << 30) { l -= snprintf(tp->display, l, ", %u.%2.2u GiB", - (int)(tp->total >> 30), - (int)(tp->total & ((1 << 30) - 1)) / 10737419); - } else if (tp->total > 1 << 20) { + (int)(total >> 30), + (int)(total & ((1 << 30) - 1)) / 10737419); + } else if (total > 1 << 20) { l -= snprintf(tp->display, l, ", %u.%2.2u MiB", - (int)(tp->total >> 20), - ((int)(tp->total & ((1 << 20) - 1)) + (int)(total >> 20), + ((int)(total & ((1 << 20) - 1)) * 100) >> 20); - } else if (tp->total > 1 << 10) { + } else if (total > 1 << 10) { l -= snprintf(tp->display, l, ", %u.%2.2u KiB", - (int)(tp->total >> 10), - ((int)(tp->total & ((1 << 10) - 1)) + (int)(total >> 10), + ((int)(total & ((1 << 10) - 1)) * 100) >> 10); } else { - l -= snprintf(tp->display, l, ", %u bytes", - (int)tp->total); + l -= snprintf(tp->display, l, ", %u bytes", (int)total); } snprintf(tp->display + sizeof(tp->display) - l, l, - " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + " | %u KiB/s", tp->avg_bytes / tp->avg_misecs); tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; - tp->last_bytes[tp->idx] = tp->count; + tp->last_bytes[tp->idx] = count; tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; - tp->count = 0; if (progress->last_value != -1 && progress_update) display(progress, progress->last_value, 0); diff --git a/progress.h b/progress.h index 61cb68dfa5..3912969e60 100644 --- a/progress.h +++ b/progress.h @@ -3,7 +3,7 @@ struct progress; -void display_throughput(struct progress *progress, unsigned long n); +void display_throughput(struct progress *progress, off_t total); int display_progress(struct progress *progress, unsigned n); struct progress *start_progress(const char *title, unsigned total); struct progress *start_progress_delay(const char *title, unsigned total, From 9ef4272bea94b022aa84372c06e211bccd5f8a54 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 4 Nov 2007 15:05:45 -0800 Subject: [PATCH 11/97] git-fetch: be even quieter. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-fetch.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 5f5b59bfdb..e65690f25c 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -152,6 +152,7 @@ static int s_update_ref(const char *action, } #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) +#define REFCOL_WIDTH 10 static int update_local_ref(struct ref *ref, const char *remote, @@ -181,8 +182,9 @@ static int update_local_ref(struct ref *ref, if (!hashcmp(ref->old_sha1, ref->new_sha1)) { if (verbose) - sprintf(display, "= %-*s %s -> %s", SUMMARY_WIDTH, - "[up to date]", remote, pretty_ref); + sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH, + "[up to date]", REFCOL_WIDTH, remote, + pretty_ref); return 0; } @@ -194,15 +196,17 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - sprintf(display, "! %-*s %s -> %s (can't fetch in current branch)", - SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); + sprintf(display, "! %-*s %-*s -> %s (can't fetch in current branch)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - sprintf(display, "- %-*s %s -> %s", - SUMMARY_WIDTH, "[tag update]", remote, pretty_ref); + sprintf(display, "- %-*s %-*s -> %s", + SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote, + pretty_ref); return s_update_ref("updating tag", ref, 0); } @@ -220,8 +224,8 @@ static int update_local_ref(struct ref *ref, what = "[new branch]"; } - sprintf(display, "* %-*s %s -> %s", - SUMMARY_WIDTH, what, remote, pretty_ref); + sprintf(display, "* %-*s %-*s -> %s", SUMMARY_WIDTH, what, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref(msg, ref, 0); } @@ -230,20 +234,21 @@ static int update_local_ref(struct ref *ref, strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, ".."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - sprintf(display, " %-*s %s -> %s (fast forward)", - SUMMARY_WIDTH, quickref, remote, pretty_ref); + sprintf(display, " %-*s %-*s -> %s", SUMMARY_WIDTH, quickref, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref("fast forward", ref, 1); } else if (force || ref->force) { char quickref[84]; strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, "..."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - sprintf(display, "+ %-*s %s -> %s (forced update)", - SUMMARY_WIDTH, quickref, remote, pretty_ref); + sprintf(display, "+ %-*s %-*s -> %s (forced update)", + SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref); return s_update_ref("forced-update", ref, 1); } else { - sprintf(display, "! %-*s %s -> %s (non fast forward)", - SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); + sprintf(display, "! %-*s %-*s -> %s (non fast forward)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } } From ec640ed1cf1d62730555705ec18b785c43e81f62 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sun, 4 Nov 2007 22:54:50 -0500 Subject: [PATCH 12/97] remove dead code from the csum-file interface The provided name argument is always constant and valid in every caller's context, so no need to have an array of PATH_MAX chars to copy it into when a simple pointer will do. Unfortunately that means getting rid of wascally wabbits too. The 'error' field is also unused. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- csum-file.c | 14 ++------------ csum-file.h | 6 +++--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/csum-file.c b/csum-file.c index b445e6a2e5..9728a99541 100644 --- a/csum-file.c +++ b/csum-file.c @@ -88,22 +88,12 @@ struct sha1file *sha1fd(int fd, const char *name) struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp) { - struct sha1file *f; - unsigned len; - - f = xmalloc(sizeof(*f)); - - len = strlen(name); - if (len >= PATH_MAX) - die("you wascally wabbit, you"); - f->namelen = len; - memcpy(f->name, name, len+1); - + struct sha1file *f = xmalloc(sizeof(*f)); f->fd = fd; - f->error = 0; f->offset = 0; f->total = 0; f->tp = tp; + f->name = name; f->do_crc = 0; SHA1_Init(&f->ctx); return f; diff --git a/csum-file.h b/csum-file.h index a38cc3a2d7..1af76562f3 100644 --- a/csum-file.h +++ b/csum-file.h @@ -5,12 +5,12 @@ struct progress; /* A SHA1-protected file */ struct sha1file { - int fd, error; - unsigned int offset, namelen; + int fd; + unsigned int offset; SHA_CTX ctx; off_t total; struct progress *tp; - char name[PATH_MAX]; + const char *name; int do_crc; uint32_t crc32; unsigned char buffer[8192]; From 6eea60f85e92901cfe6ad7ab75e4a070215aedf7 Mon Sep 17 00:00:00 2001 From: David Symonds Date: Tue, 6 Nov 2007 10:04:24 +1100 Subject: [PATCH 13/97] Rearrange git-format-patch synopsis to improve clarity. Signed-off-by: David Symonds Signed-off-by: Junio C Hamano --- Documentation/git-format-patch.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 92c0ab63b2..9d4bae2f60 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -9,9 +9,10 @@ git-format-patch - Prepare patches for e-mail submission SYNOPSIS -------- [verse] -'git-format-patch' [-n | -N | -k] [-o | --stdout] [--thread] +'git-format-patch' [-k] [-o | --stdout] [--thread] [--attach[=] | --inline[=]] [-s | --signoff] [] + [-n | --numbered | -N | --no-numbered] [--start-number ] [--numbered-files] [--in-reply-to=Message-Id] [--suffix=.] [--ignore-if-in-upstream] From 34cb704a9b8b45ff3e1342678c5892e1ee6e27f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Mon, 5 Nov 2007 20:36:33 +0100 Subject: [PATCH 14/97] git-commit.sh: Fix usage checks regarding paths given when they do not make sense MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checks that looked for paths given to git-commit in addition to --all or --interactive expected only 3 values, while the case statement actually provides 4, so the check was never triggered. The bug was introduced in 6cbf07efc5702351897dee4742525c9b9f7828ac when the case statement was extended to handle --interactive. Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- git-commit.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-commit.sh b/git-commit.sh index ab43217be4..5e3908f2cb 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -322,9 +322,9 @@ unset only case "$all,$interactive,$also,$#" in *t,*t,*) die "Cannot use -a, --interactive or -i at the same time." ;; -t,,[1-9]*) +t,,,[1-9]*) die "Paths with -a does not make sense." ;; -,t,[1-9]*) +,t,,[1-9]*) die "Paths with --interactive does not make sense." ;; ,,t,0) die "No paths with -i does not make sense." ;; From 243e0614e0f8783599b20106b50eee56d0a17332 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 5 Nov 2007 13:15:21 +0000 Subject: [PATCH 15/97] parse-options: abbreviation engine fix. When an option could be an ambiguous abbreviation of two options, the code used to error out. Even if an exact match would have occured later. Test and original patch by Pierre Habouzit. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- parse-options.c | 33 +++++++++++++++++++++------------ t/t0040-parse-options.sh | 13 +++++++++++++ test-parse-options.c | 1 + 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/parse-options.c b/parse-options.c index cc09c98ec3..15b32f741b 100644 --- a/parse-options.c +++ b/parse-options.c @@ -119,8 +119,8 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, const struct option *options) { const char *arg_end = strchr(arg, '='); - const struct option *abbrev_option = NULL; - int abbrev_flags = 0; + const struct option *abbrev_option = NULL, *ambiguous_option = NULL; + int abbrev_flags = 0, ambiguous_flags = 0; if (!arg_end) arg_end = arg + strlen(arg); @@ -137,16 +137,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, /* abbreviated? */ if (!strncmp(options->long_name, arg, arg_end - arg)) { is_abbreviated: - if (abbrev_option) - return error("Ambiguous option: %s " - "(could be --%s%s or --%s%s)", - arg, - (flags & OPT_UNSET) ? - "no-" : "", - options->long_name, - (abbrev_flags & OPT_UNSET) ? - "no-" : "", - abbrev_option->long_name); + if (abbrev_option) { + /* + * If this is abbreviated, it is + * ambiguous. So when there is no + * exact match later, we need to + * error out. + */ + ambiguous_option = abbrev_option; + ambiguous_flags = abbrev_flags; + } if (!(flags & OPT_UNSET) && *arg_end) p->opt = arg_end + 1; abbrev_option = options; @@ -176,6 +176,15 @@ is_abbreviated: } return get_value(p, options, flags); } + + if (ambiguous_option) + return error("Ambiguous option: %s " + "(could be --%s%s or --%s%s)", + arg, + (ambiguous_flags & OPT_UNSET) ? "no-" : "", + ambiguous_option->long_name, + (abbrev_flags & OPT_UNSET) ? "no-" : "", + abbrev_option->long_name); if (abbrev_option) return get_value(p, abbrev_option, abbrev_flags); return error("unknown option `%s'", arg); diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index ae49424aa0..462fdf262f 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -18,6 +18,7 @@ string options -s, --string get a string --string2 get another string + --st get another string (pervert ordering) EOF @@ -90,4 +91,16 @@ test_expect_failure 'ambiguously abbreviated option' ' test $? != 129 ' +cat > expect << EOF +boolean: 0 +integer: 0 +string: 123 +EOF + +test_expect_success 'non ambiguous option (after two options it abbreviates)' ' + test-parse-options --st 123 > output 2> output.err && + test ! -s output.err && + git diff expect output +' + test_done diff --git a/test-parse-options.c b/test-parse-options.c index 277cfe4d6d..4d3e2ec39e 100644 --- a/test-parse-options.c +++ b/test-parse-options.c @@ -18,6 +18,7 @@ int main(int argc, const char **argv) OPT_GROUP("string options"), OPT_STRING('s', "string", &string, "string", "get a string"), OPT_STRING(0, "string2", &string, "str", "get another string"), + OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_END(), }; int i; From cdf4a751fa728273030651a769f9f45212ff50c9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 3 Nov 2007 14:33:01 +0000 Subject: [PATCH 16/97] builtin-reset: do not call "ls-files --unmerged" Since reset is a builtin now, it can use the full power of libgit.a and check for unmerged entries itself. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-reset.c | 28 ++++++++-------------------- t/t7102-reset.sh | 9 +++++++++ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index 5467e36c73..79792eebf2 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -46,26 +46,14 @@ static inline int is_merge(void) static int unmerged_files(void) { - char b; - ssize_t len; - struct child_process cmd; - const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL}; - - memset(&cmd, 0, sizeof(cmd)); - cmd.argv = argv_ls_files; - cmd.git_cmd = 1; - cmd.out = -1; - - if (start_command(&cmd)) - die("Could not run sub-command: git ls-files"); - - len = xread(cmd.out, &b, 1); - if (len < 0) - die("Could not read output from git ls-files: %s", - strerror(errno)); - finish_command(&cmd); - - return len; + int i; + read_cache(); + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce)) + return 1; + } + return 0; } static int reset_index_file(const unsigned char *sha1, int is_hard_reset) diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index cea9afb764..506767d2d7 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -59,6 +59,15 @@ test_expect_success 'giving a non existing revision should fail' ' check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc ' +test_expect_success 'reset --soft with unmerged index should fail' ' + touch .git/MERGE_HEAD && + echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" | + git update-index --index-info && + ! git reset --soft HEAD && + rm .git/MERGE_HEAD && + git rm --cached -- un +' + test_expect_success \ 'giving paths with options different than --mixed should fail' ' ! git reset --soft -- first && From 620a6cd42ed5ea94684b22714181bf03871733dd Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 3 Nov 2007 15:21:21 +0000 Subject: [PATCH 17/97] builtin-reset: avoid forking "update-index --refresh" Instead of forking update-index, call refresh_cache() directly. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-reset.c | 53 ++++++++++++++++++++++++++---------------------- t/t7102-reset.sh | 10 +++++++++ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index 79792eebf2..44582f23a2 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -95,26 +95,34 @@ static void print_new_head_line(struct commit *commit) printf("\n"); } -static int update_index_refresh(void) +static int update_index_refresh(int fd, struct lock_file *index_lock) { - const char *argv_update_index[] = {"update-index", "--refresh", NULL}; - return run_command_v_opt(argv_update_index, RUN_GIT_CMD); -} + int result; -struct update_cb_data { - int index_fd; - struct lock_file *lock; - int exit_code; -}; + if (!index_lock) { + index_lock = xcalloc(1, sizeof(struct lock_file)); + fd = hold_locked_index(index_lock, 1); + } + + if (read_cache() < 0) + return error("Could not read index"); + result = refresh_cache(0) ? 1 : 0; + if (write_cache(fd, active_cache, active_nr) || + close(fd) || + commit_locked_index(index_lock)) + return error ("Could not refresh index"); + return result; +} static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; - struct update_cb_data *cb = data; + int *discard_flag = data; /* do_diff_cache() mangled the index */ discard_cache(); + *discard_flag = 1; read_cache(); for (i = 0; i < q->nr; i++) { @@ -128,34 +136,33 @@ static void update_index_from_diff(struct diff_queue_struct *q, } else remove_file_from_cache(one->path); } - - cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) || - close(cb->index_fd) || - commit_locked_index(cb->lock); } static int read_from_tree(const char *prefix, const char **argv, unsigned char *tree_sha1) { + struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); + int index_fd, index_was_discarded = 0; struct diff_options opt; - struct update_cb_data cb; memset(&opt, 0, sizeof(opt)); diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; - opt.format_callback_data = &cb; + opt.format_callback_data = &index_was_discarded; - cb.lock = xcalloc(1, sizeof(struct lock_file)); - cb.index_fd = hold_locked_index(cb.lock, 1); - cb.exit_code = 0; + index_fd = hold_locked_index(lock, 1); + index_was_discarded = 0; read_cache(); if (do_diff_cache(tree_sha1, &opt)) return 1; diffcore_std(&opt); diff_flush(&opt); - return cb.exit_code; + if (!index_was_discarded) + /* The index is still clobbered from do_diff_cache() */ + discard_cache(); + return update_index_refresh(index_fd, lock); } static void prepend_reflog_action(const char *action, char *buf, size_t size) @@ -225,9 +232,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); - if (read_from_tree(prefix, argv + i, sha1)) - return 1; - return update_index_refresh() ? 1 : 0; + return read_from_tree(prefix, argv + i, sha1); } if (reset_type == NONE) reset_type = MIXED; /* by default */ @@ -264,7 +269,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) case SOFT: /* Nothing else to do. */ break; case MIXED: /* Report what has not been updated. */ - update_index_refresh(); + update_index_refresh(0, NULL); break; } diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 506767d2d7..e5c9f30c73 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -418,4 +418,14 @@ test_expect_success 'resetting an unmodified path is a no-op' ' git diff-index --cached --exit-code HEAD ' +cat > expect << EOF +file2: needs update +EOF + +test_expect_success '--mixed refreshes the index' ' + echo 123 >> file2 && + git reset --mixed HEAD > output && + git diff --exit-code expect output +' + test_done From 4c324c00501c2da41987498f8c966b022306b244 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 4 Nov 2007 20:46:48 +0100 Subject: [PATCH 18/97] upload-pack: Use finish_{command,async}() instead of waitpid(). upload-pack spawns two processes, rev-list and pack-objects, and carefully monitors their status so that it can report failure to the remote end. This change removes the complicated procedures on the grounds of the following observations: - If everything is OK, rev-list closes its output pipe end, upon which pack-objects (which reads from the pipe) sees EOF and terminates itself, closing its output (and error) pipes. upload-pack reads from both until it sees EOF in both. It collects the exit codes of the child processes (which indicate success) and terminates successfully. - If rev-list sees an error, it closes its output and terminates with failure. pack-objects sees EOF in its input and terminates successfully. Again upload-pack reads its inputs until EOF. When it now collects the exit codes of its child processes, it notices the failure of rev-list and signals failure to the remote end. - If pack-objects sees an error, it terminates with failure. Since this breaks the pipe to rev-list, rev-list is killed with SIGPIPE. upload-pack reads its input until EOF, then collects the exit codes of the child processes, notices their failures, and signals failure to the remote end. - If upload-pack itself dies unexpectedly, pack-objects is killed with SIGPIPE, and subsequently also rev-list. The upshot of this is that precise monitoring of child processes is not required because both terminate if either one of them dies unexpectedly. This allows us to use finish_command() and finish_async() instead of an explicit waitpid(2) call. The change is smaller than it looks because most of it only reduces the indentation of a large part of the inner loop. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t5530-upload-pack-error.sh | 75 ++++++++++++++ upload-pack.c | 196 ++++++++++++++--------------------- 2 files changed, 154 insertions(+), 117 deletions(-) create mode 100755 t/t5530-upload-pack-error.sh diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh new file mode 100755 index 0000000000..cc8949e3ef --- /dev/null +++ b/t/t5530-upload-pack-error.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +test_description='errors in upload-pack' + +. ./test-lib.sh + +D=`pwd` + +corrupt_repo () { + object_sha1=$(git rev-parse "$1") && + ob=$(expr "$object_sha1" : "\(..\)") && + ject=$(expr "$object_sha1" : "..\(..*\)") && + rm -f ".git/objects/$ob/$ject" +} + +test_expect_success 'setup and corrupt repository' ' + + echo file >file && + git add file && + git rev-parse :file && + git commit -a -m original && + test_tick && + echo changed >file && + git commit -a -m changed && + corrupt_repo HEAD:file + +' + +test_expect_failure 'fsck fails' ' + + git fsck +' + +test_expect_success 'upload-pack fails due to error in pack-objects' ' + + ! echo "0032want $(git rev-parse HEAD) +00000009done +0000" | git-upload-pack . > /dev/null 2> output.err && + grep "pack-objects died" output.err +' + +test_expect_success 'corrupt repo differently' ' + + git hash-object -w file && + corrupt_repo HEAD^^{tree} + +' + +test_expect_failure 'fsck fails' ' + + git fsck +' +test_expect_success 'upload-pack fails due to error in rev-list' ' + + ! echo "0032want $(git rev-parse HEAD) +00000009done +0000" | git-upload-pack . > /dev/null 2> output.err && + grep "waitpid (async) failed" output.err +' + +test_expect_success 'create empty repository' ' + + mkdir foo && + cd foo && + git init + +' + +test_expect_failure 'fetch fails' ' + + git fetch .. master + +' + +test_done diff --git a/upload-pack.c b/upload-pack.c index 67994680f2..7e04311027 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -144,6 +144,7 @@ static void create_pack_file(void) char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; int buffered = -1; + ssize_t sz; const char *argv[10]; int arg = 0; @@ -168,22 +169,15 @@ static void create_pack_file(void) pack_objects.git_cmd = 1; pack_objects.argv = argv; - if (start_command(&pack_objects)) { - /* daemon sets things up to ignore TERM */ - kill(rev_list.pid, SIGKILL); + if (start_command(&pack_objects)) die("git-upload-pack: unable to fork git-pack-objects"); - } /* We read from pack_objects.err to capture stderr output for * progress bar, and pack_objects.out to capture the pack data. */ while (1) { - const char *who; struct pollfd pfd[2]; - pid_t pid; - int status; - ssize_t sz; int pe, pu, pollsize; reset_timeout(); @@ -204,123 +198,91 @@ static void create_pack_file(void) pollsize++; } - if (pollsize) { - if (poll(pfd, pollsize, -1) < 0) { - if (errno != EINTR) { - error("poll failed, resuming: %s", - strerror(errno)); - sleep(1); - } - continue; - } - if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { - /* Data ready; we keep the last byte - * to ourselves in case we detect - * broken rev-list, so that we can - * leave the stream corrupted. This - * is unfortunate -- unpack-objects - * would happily accept a valid pack - * data with trailing garbage, so - * appending garbage after we pass all - * the pack data is not good enough to - * signal breakage to downstream. - */ - char *cp = data; - ssize_t outsz = 0; - if (0 <= buffered) { - *cp++ = buffered; - outsz++; - } - sz = xread(pack_objects.out, cp, - sizeof(data) - outsz); - if (0 < sz) - ; - else if (sz == 0) { - close(pack_objects.out); - pack_objects.out = -1; - } - else - goto fail; - sz += outsz; - if (1 < sz) { - buffered = data[sz-1] & 0xFF; - sz--; - } - else - buffered = -1; - sz = send_client_data(1, data, sz); - if (sz < 0) - goto fail; - } - if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { - /* Status ready; we ship that in the side-band - * or dump to the standard error. - */ - sz = xread(pack_objects.err, progress, - sizeof(progress)); - if (0 < sz) - send_client_data(2, progress, sz); - else if (sz == 0) { - close(pack_objects.err); - pack_objects.err = -1; - } - else - goto fail; - } - } + if (!pollsize) + break; - /* See if the children are still there */ - if (rev_list.pid || pack_objects.pid) { - pid = waitpid(-1, &status, WNOHANG); - if (!pid) - continue; - who = ((pid == rev_list.pid) ? "git-rev-list" : - (pid == pack_objects.pid) ? "git-pack-objects" : - NULL); - if (!who) { - if (pid < 0) { - error("git-upload-pack: %s", - strerror(errno)); - goto fail; - } - error("git-upload-pack: we weren't " - "waiting for %d", pid); - continue; + if (poll(pfd, pollsize, -1) < 0) { + if (errno != EINTR) { + error("poll failed, resuming: %s", + strerror(errno)); + sleep(1); } - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) { - error("git-upload-pack: %s died with error.", - who); - goto fail; - } - if (pid == rev_list.pid) - rev_list.pid = 0; - if (pid == pack_objects.pid) - pack_objects.pid = 0; - if (rev_list.pid || pack_objects.pid) - continue; - } - - /* both died happily */ - if (pollsize) continue; - - /* flush the data */ - if (0 <= buffered) { - data[0] = buffered; - sz = send_client_data(1, data, 1); + } + if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { + /* Data ready; we keep the last byte to ourselves + * in case we detect broken rev-list, so that we + * can leave the stream corrupted. This is + * unfortunate -- unpack-objects would happily + * accept a valid packdata with trailing garbage, + * so appending garbage after we pass all the + * pack data is not good enough to signal + * breakage to downstream. + */ + char *cp = data; + ssize_t outsz = 0; + if (0 <= buffered) { + *cp++ = buffered; + outsz++; + } + sz = xread(pack_objects.out, cp, + sizeof(data) - outsz); + if (0 < sz) + ; + else if (sz == 0) { + close(pack_objects.out); + pack_objects.out = -1; + } + else + goto fail; + sz += outsz; + if (1 < sz) { + buffered = data[sz-1] & 0xFF; + sz--; + } + else + buffered = -1; + sz = send_client_data(1, data, sz); if (sz < 0) goto fail; - fprintf(stderr, "flushed.\n"); } - if (use_sideband) - packet_flush(1); - return; + if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { + /* Status ready; we ship that in the side-band + * or dump to the standard error. + */ + sz = xread(pack_objects.err, progress, + sizeof(progress)); + if (0 < sz) + send_client_data(2, progress, sz); + else if (sz == 0) { + close(pack_objects.err); + pack_objects.err = -1; + } + else + goto fail; + } } + + if (finish_command(&pack_objects)) { + error("git-upload-pack: git-pack-objects died with error."); + goto fail; + } + if (finish_async(&rev_list)) + goto fail; /* error was already reported */ + + /* flush the data */ + if (0 <= buffered) { + data[0] = buffered; + sz = send_client_data(1, data, 1); + if (sz < 0) + goto fail; + fprintf(stderr, "flushed.\n"); + } + if (use_sideband) + packet_flush(1); + return; + fail: - if (pack_objects.pid) - kill(pack_objects.pid, SIGKILL); - if (rev_list.pid) - kill(rev_list.pid, SIGKILL); send_client_data(3, abort_msg, sizeof(abort_msg)); die("git-upload-pack: %s", abort_msg); } From bab8118aff7581364eb5c74472fe7ab4f45f4a12 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sun, 4 Nov 2007 01:11:14 +0100 Subject: [PATCH 19/97] Reuse previous annotation when overwriting a tag When forcing to overwrite an annotated tag, there are good chances one wants to keep the old annotation, or modify it, not start from scratch. This is obviously only triggered for annotated tagging (-a or -s). Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- builtin-tag.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/builtin-tag.c b/builtin-tag.c index 66e5a58307..566d123a8d 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -247,9 +247,37 @@ static int git_tag_config(const char *var, const char *value) return git_default_config(var, value); } +static void write_tag_body(int fd, const unsigned char *sha1) +{ + unsigned long size; + enum object_type type; + char *buf, *sp, *eob; + size_t len; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return; + /* skip header */ + sp = strstr(buf, "\n\n"); + + if (!sp || !size || type != OBJ_TAG) { + free(buf); + return; + } + sp += 2; /* skip the 2 LFs */ + eob = strstr(sp, "\n" PGP_SIGNATURE "\n"); + if (eob) + len = eob - sp; + else + len = buf + size - sp; + write_or_die(fd, sp, len); + + free(buf); +} + static void create_tag(const unsigned char *object, const char *tag, struct strbuf *buf, int message, int sign, - unsigned char *result) + unsigned char *prev, unsigned char *result) { enum object_type type; char header_buf[1024]; @@ -282,7 +310,11 @@ static void create_tag(const unsigned char *object, const char *tag, if (fd < 0) die("could not create file '%s': %s", path, strerror(errno)); - write_or_die(fd, tag_template, strlen(tag_template)); + + if (!is_null_sha1(prev)) + write_tag_body(fd, prev); + else + write_or_die(fd, tag_template, strlen(tag_template)); close(fd); launch_editor(path, buf); @@ -419,7 +451,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("tag '%s' already exists", tag); if (annotate) - create_tag(object, tag, &buf, message, sign, object); + create_tag(object, tag, &buf, message, sign, prev, object); lock = lock_any_ref_for_update(ref, prev, 0); if (!lock) From 4d8b1dc850bafdf2304a525a768fbfc7aa5361ae Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sun, 4 Nov 2007 01:11:15 +0100 Subject: [PATCH 20/97] Add tests for git tag These tests check whether git-tag properly sends a comment into the editor, and whether it reuses previous annotation when overwriting an existing tag. Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- t/t7004-tag.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 0d07bc39c7..096fe33b07 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1004,4 +1004,20 @@ test_expect_failure \ 'verify signed tag fails when public key is not present' \ 'git-tag -v signed-tag' +test_expect_success \ + 'message in editor has initial comment' ' + GIT_EDITOR=cat git tag -a initial-comment > actual || true && + test $(sed -n "/^\(#\|\$\)/p" actual | wc -l) -gt 0 +' + +get_tag_header reuse $commit commit $time >expect +echo "An annotation to be reused" >> expect +test_expect_success \ + 'overwriting an annoted tag should use its previous body' ' + git tag -a -m "An annotation to be reused" reuse && + GIT_EDITOR=true git tag -f -a reuse && + get_tag_msg reuse >actual && + git diff expect actual +' + test_done From a9ee9bf9f9840498f2369ebafe6c220deb4327fd Mon Sep 17 00:00:00 2001 From: Emil Medve Date: Wed, 7 Nov 2007 15:10:27 -0600 Subject: [PATCH 21/97] git-stash: Fix listing stashes Commit bc9e7399af3790918140c30a5b2c85bf9a8f1ad3 "reverted" commit f12e925ac23ad6169e046cfe05b8438a1611ad58 Signed-off-by: Emil Medve Signed-off-by: Junio C Hamano --- git-stash.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git-stash.sh b/git-stash.sh index 04af892531..696b465b7c 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -21,7 +21,7 @@ no_changes () { clear_stash () { if current=$(git rev-parse --verify $ref_stash 2>/dev/null) then - git update-ref -d refs/stash $current + git update-ref -d $ref_stash $current fi } @@ -92,6 +92,10 @@ save_stash () { clear_stash || die "Cannot initialize stash" create_stash "$stash_msg" + + # Make sure the reflog for stash is kept. + : >>"$GIT_DIR/logs/$ref_stash" + git update-ref -m "$stash_msg" $ref_stash $w_commit || die "Cannot save the current status" printf >&2 'Saved "%s"\n' "$stash_msg" From 53ed7b5a5d7a0ad2ffafd4a4ba4a7861f5db624e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 6 Nov 2007 16:30:28 -0500 Subject: [PATCH 22/97] make display of total transferred fully accurate The minimum delay of 1/2 sec between successive throughput updates might not have been elapsed when display_throughput() is called for the last time, potentially making the display of total transferred bytes not right when progress is said to be done. Let's force an update of the throughput display as well when the progress is complete. As a side effect, the total transferred will always be displayed even if the actual transfer rate doesn't have time to kickin. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 64 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/progress.c b/progress.c index a963bd8bfc..0700dcf24a 100644 --- a/progress.c +++ b/progress.c @@ -14,11 +14,12 @@ #define TP_IDX_MAX 8 struct throughput { + off_t curr_total; off_t prev_total; struct timeval prev_tv; unsigned int avg_bytes; - unsigned int last_bytes[TP_IDX_MAX]; unsigned int avg_misecs; + unsigned int last_bytes[TP_IDX_MAX]; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; char display[32]; @@ -109,6 +110,30 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } +static void throughput_string(struct throughput *tp, off_t total, + unsigned int rate) +{ + int l = sizeof(tp->display); + if (total > 1 << 30) { + l -= snprintf(tp->display, l, ", %u.%2.2u GiB", + (int)(total >> 30), + (int)(total & ((1 << 30) - 1)) / 10737419); + } else if (total > 1 << 20) { + l -= snprintf(tp->display, l, ", %u.%2.2u MiB", + (int)(total >> 20), + ((int)(total & ((1 << 20) - 1)) * 100) >> 20); + } else if (total > 1 << 10) { + l -= snprintf(tp->display, l, ", %u.%2.2u KiB", + (int)(total >> 10), + ((int)(total & ((1 << 10) - 1)) * 100) >> 10); + } else { + l -= snprintf(tp->display, l, ", %u bytes", (int)total); + } + if (rate) + snprintf(tp->display + sizeof(tp->display) - l, l, + " | %u KiB/s", rate); +} + void display_throughput(struct progress *progress, off_t total) { struct throughput *tp; @@ -124,11 +149,12 @@ void display_throughput(struct progress *progress, off_t total) if (!tp) { progress->throughput = tp = calloc(1, sizeof(*tp)); if (tp) { - tp->prev_total = total; + tp->prev_total = tp->curr_total = total; tp->prev_tv = tv; } return; } + tp->curr_total = total; /* * We have x = bytes and y = microsecs. We want z = KiB/s: @@ -149,39 +175,21 @@ void display_throughput(struct progress *progress, off_t total) misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; if (misecs > 512) { - int l = sizeof(tp->display); - unsigned int count = total - tp->prev_total; + unsigned int count, rate; + + count = total - tp->prev_total; tp->prev_total = total; tp->prev_tv = tv; tp->avg_bytes += count; tp->avg_misecs += misecs; - - if (total > 1 << 30) { - l -= snprintf(tp->display, l, ", %u.%2.2u GiB", - (int)(total >> 30), - (int)(total & ((1 << 30) - 1)) / 10737419); - } else if (total > 1 << 20) { - l -= snprintf(tp->display, l, ", %u.%2.2u MiB", - (int)(total >> 20), - ((int)(total & ((1 << 20) - 1)) - * 100) >> 20); - } else if (total > 1 << 10) { - l -= snprintf(tp->display, l, ", %u.%2.2u KiB", - (int)(total >> 10), - ((int)(total & ((1 << 10) - 1)) - * 100) >> 10); - } else { - l -= snprintf(tp->display, l, ", %u bytes", (int)total); - } - snprintf(tp->display + sizeof(tp->display) - l, l, - " | %u KiB/s", tp->avg_bytes / tp->avg_misecs); - + rate = tp->avg_bytes / tp->avg_misecs; tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; tp->last_bytes[tp->idx] = count; tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; + throughput_string(tp, total, rate); if (progress->last_value != -1 && progress_update) display(progress, progress->last_value, 0); } @@ -225,6 +233,12 @@ void stop_progress(struct progress **p_progress) *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ + struct throughput *tp = progress->throughput; + if (tp) { + unsigned int rate = !tp->avg_misecs ? 0 : + tp->avg_bytes / tp->avg_misecs; + throughput_string(tp, tp->curr_total, rate); + } progress_update = 1; display(progress, progress->last_value, 1); } From a984a06a07cdd0a843eb6107ad56e346d99ac840 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 8 Nov 2007 15:45:41 -0500 Subject: [PATCH 23/97] nicer display of thin pack completion In the same spirit of prettifying Git's output display for mere mortals, here's a simple extension to the progress API allowing for a final message to be provided when terminating a progress line, and use it for the display of the number of objects needed to complete a thin pack, saving yet one more line of screen display. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 10 +++++----- progress.c | 19 +++++++++++++------ progress.h | 1 + 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/index-pack.c b/index-pack.c index 581a7f5650..469a3307dc 100644 --- a/index-pack.c +++ b/index-pack.c @@ -792,6 +792,7 @@ int main(int argc, char **argv) flush(); } else { if (fix_thin_pack) { + char msg[48]; int nr_unresolved = nr_deltas - nr_resolved_deltas; int nr_objects_initial = nr_objects; if (nr_unresolved <= 0) @@ -800,12 +801,11 @@ int main(int argc, char **argv) (nr_objects + nr_unresolved + 1) * sizeof(*objects)); fix_unresolved_deltas(nr_unresolved); - stop_progress(&progress); - if (verbose) - fprintf(stderr, "%d objects were added to complete this thin pack.\n", - nr_objects - nr_objects_initial); + sprintf(msg, "completed with %d local objects", + nr_objects - nr_objects_initial); + stop_progress_msg(&progress, msg); fixup_pack_header_footer(output_fd, sha1, - curr_pack, nr_objects); + curr_pack, nr_objects); } if (nr_deltas != nr_resolved_deltas) die("pack has %d unresolved deltas", diff --git a/progress.c b/progress.c index 0700dcf24a..4bd650f9ba 100644 --- a/progress.c +++ b/progress.c @@ -69,9 +69,9 @@ static void clear_progress_signal(void) progress_update = 0; } -static int display(struct progress *progress, unsigned n, int done) +static int display(struct progress *progress, unsigned n, const char *done) { - char *eol, *tp; + const char *eol, *tp; if (progress->delay) { if (!progress_update || --progress->delay) @@ -90,7 +90,7 @@ static int display(struct progress *progress, unsigned n, int done) progress->last_value = n; tp = (progress->throughput) ? progress->throughput->display : ""; - eol = done ? ", done. \n" : " \r"; + eol = done ? done : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { @@ -191,13 +191,13 @@ void display_throughput(struct progress *progress, off_t total) throughput_string(tp, total, rate); if (progress->last_value != -1 && progress_update) - display(progress, progress->last_value, 0); + display(progress, progress->last_value, NULL); } } int display_progress(struct progress *progress, unsigned n) { - return progress ? display(progress, n, 0) : 0; + return progress ? display(progress, n, NULL) : 0; } struct progress *start_progress_delay(const char *title, unsigned total, @@ -226,6 +226,11 @@ struct progress *start_progress(const char *title, unsigned total) } void stop_progress(struct progress **p_progress) +{ + stop_progress_msg(p_progress, "done"); +} + +void stop_progress_msg(struct progress **p_progress, const char *msg) { struct progress *progress = *p_progress; if (!progress) @@ -233,6 +238,7 @@ void stop_progress(struct progress **p_progress) *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ + char buf[strlen(msg) + 5]; struct throughput *tp = progress->throughput; if (tp) { unsigned int rate = !tp->avg_misecs ? 0 : @@ -240,7 +246,8 @@ void stop_progress(struct progress **p_progress) throughput_string(tp, tp->curr_total, rate); } progress_update = 1; - display(progress, progress->last_value, 1); + sprintf(buf, ", %s.\n", msg); + display(progress, progress->last_value, buf); } clear_progress_signal(); free(progress->throughput); diff --git a/progress.h b/progress.h index 3912969e60..611e4c4d42 100644 --- a/progress.h +++ b/progress.h @@ -9,5 +9,6 @@ struct progress *start_progress(const char *title, unsigned total); struct progress *start_progress_delay(const char *title, unsigned total, unsigned percent_treshold, unsigned delay); void stop_progress(struct progress **progress); +void stop_progress_msg(struct progress **progress, const char *msg); #endif From 2e458e0575de6478693586d387f71873d280b934 Mon Sep 17 00:00:00 2001 From: Gordon Hopper Date: Thu, 8 Nov 2007 13:15:20 -0700 Subject: [PATCH 24/97] git-cvsimport: fix handling of user name when it is not set in CVSROOT The cvs programs do not default to "anonymous" as the user name, but use the currently logged in user. This patch more closely matches the cvs behavior. Signed-off-by: Gordon Hopper Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index e4bc2b54f6..efa6a0c41a 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -223,7 +223,8 @@ sub conn { } } - $user="anonymous" unless defined $user; + # if username is not explicit in CVSROOT, then use current user, as cvs would + $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user; my $rr2 = "-"; unless ($port) { $rr2 = ":pserver:$user\@$serv:$repo"; From 659c69cfef984e7416decc78841877207e9d5914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Nov 2007 01:49:36 +0100 Subject: [PATCH 25/97] Add strchrnul() As suggested by Pierre Habouzit, add strchrnul(). It's a useful GNU extension and can simplify string parser code. There are several places in git that can be converted to strchrnul(); as a trivial example, this patch introduces its usage to builtin-fetch--tool.c. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Makefile | 13 +++++++++++++ builtin-fetch--tool.c | 8 ++------ compat/strchrnul.c | 8 ++++++++ git-compat-util.h | 5 +++++ 4 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 compat/strchrnul.c diff --git a/Makefile b/Makefile index 621270f623..69dcbb227e 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,8 @@ all:: # # Define NO_MEMMEM if you don't have memmem. # +# Define NO_STRCHRNUL if you don't have strchrnul. +# # Define NO_STRLCPY if you don't have strlcpy. # # Define NO_STRTOUMAX if you don't have strtoumax in the C library. @@ -406,6 +408,7 @@ ifeq ($(uname_S),Darwin) OLD_ICONV = UnfortunatelyYes NO_STRLCPY = YesPlease NO_MEMMEM = YesPlease + NO_STRCHRNUL = YesPlease endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@ -413,6 +416,7 @@ ifeq ($(uname_S),SunOS) SHELL_PATH = /bin/bash NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease + NO_STRCHRNUL = YesPlease NO_HSTRERROR = YesPlease ifeq ($(uname_R),5.8) NEEDS_LIBICONV = YesPlease @@ -438,6 +442,7 @@ ifeq ($(uname_O),Cygwin) NO_D_INO_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease + NO_STRCHRNUL = YesPlease NO_SYMLINK_HEAD = YesPlease NEEDS_LIBICONV = YesPlease NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes @@ -452,12 +457,14 @@ endif ifeq ($(uname_S),FreeBSD) NEEDS_LIBICONV = YesPlease NO_MEMMEM = YesPlease + NO_STRCHRNUL = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease + NO_STRCHRNUL = YesPlease NEEDS_LIBICONV = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib @@ -473,6 +480,7 @@ endif ifeq ($(uname_S),AIX) NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease + NO_STRCHRNUL = YesPlease NO_STRLCPY = YesPlease NEEDS_LIBICONV=YesPlease endif @@ -485,6 +493,7 @@ ifeq ($(uname_S),IRIX64) NO_SETENV=YesPlease NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease + NO_STRCHRNUL = YesPlease NO_STRLCPY = YesPlease NO_SOCKADDR_STORAGE=YesPlease SHELL_PATH=/usr/gnu/bin/bash @@ -695,6 +704,10 @@ ifdef NO_MEMMEM COMPAT_CFLAGS += -DNO_MEMMEM COMPAT_OBJS += compat/memmem.o endif +ifdef NO_STRCHRNUL + COMPAT_CFLAGS += -DNO_STRCHRNUL + COMPAT_OBJS += compat/strchrnul.o +endif ifdef THREADED_DELTA_SEARCH BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 6a78517958..ed60847d9f 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -435,9 +435,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu cp++; if (!*cp) break; - np = strchr(cp, '\n'); - if (!np) - np = cp + strlen(cp); + np = strchrnul(cp, '\n'); if (pass) { lrr_list[i].line = cp; lrr_list[i].name = cp + 41; @@ -461,9 +459,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu rref++; if (!*rref) break; - next = strchr(rref, '\n'); - if (!next) - next = rref + strlen(rref); + next = strchrnul(rref, '\n'); rreflen = next - rref; for (i = 0; i < lrr_count; i++) { diff --git a/compat/strchrnul.c b/compat/strchrnul.c new file mode 100644 index 0000000000..51839feb6e --- /dev/null +++ b/compat/strchrnul.c @@ -0,0 +1,8 @@ +#include "../git-compat-util.h" + +char *gitstrchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} diff --git a/git-compat-util.h b/git-compat-util.h index 7b29d1b905..e72654bc40 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -183,6 +183,11 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif +#ifdef NO_STRCHRNUL +#define strchrnul gitstrchrnul +char *gitstrchrnul(const char *s, int c); +#endif + extern void release_pack_memory(size_t, int); static inline char* xstrdup(const char *str) From cde75e59e1b2d8dd3ba49bc9034692dd06ee3907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 9 Nov 2007 01:49:42 +0100 Subject: [PATCH 26/97] --pretty=format: on-demand format expansion Some of the --pretty=format placeholders expansions are expensive to calculate. This is made worse by the current code's use of interpolate(), which requires _all_ placeholders are to be prepared up front. One way to speed this up is to check which placeholders are present in the format string and to prepare only the expansions that are needed. That still leaves the allocation overhead of interpolate(). Another way is to use a callback based approach together with the strbuf library to keep allocations to a minimum and avoid string copies. That's what this patch does. It introduces a new strbuf function, strbuf_expand(). The function takes a format string, list of placeholder strings, a user supplied function 'fn', and an opaque pointer 'context' to tell 'fn' what thingy to operate on. The function 'fn' is expected to accept a strbuf, a parsed placeholder string and the 'context' pointer, and append the interpolated value for the 'context' thingy, according to the format specified by the placeholder. Thanks to Pierre Habouzit for his suggestion to use strchrnul() and the code surrounding its callsite. And thanks to Junio for most of this commit message. :) Here my measurements of most of Paul Mackerras' test cases that highlighted the performance problem (best of three runs): (master) $ time git log --pretty=oneline >/dev/null real 0m0.390s user 0m0.340s sys 0m0.040s (master) $ time git log --pretty=raw >/dev/null real 0m0.434s user 0m0.408s sys 0m0.016s (master) $ time git log --pretty="format:%H {%P} %ct" >/dev/null real 0m1.347s user 0m0.080s sys 0m1.256s (interp_find_active -- Dscho) $ time ./git log --pretty="format:%H {%P} %ct" >/dev/null real 0m0.694s user 0m0.020s sys 0m0.672s (strbuf_expand -- this patch) $ time ./git log --pretty="format:%H {%P} %ct" >/dev/null real 0m0.395s user 0m0.352s sys 0m0.028s Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 272 ++++++++++++++++++++++++++++++------------------------- strbuf.c | 24 +++++ strbuf.h | 3 + 3 files changed, 178 insertions(+), 121 deletions(-) diff --git a/pretty.c b/pretty.c index 490cede263..9fbd73f748 100644 --- a/pretty.c +++ b/pretty.c @@ -1,6 +1,5 @@ #include "cache.h" #include "commit.h" -#include "interpolate.h" #include "utf8.h" #include "diff.h" #include "revision.h" @@ -283,7 +282,8 @@ static char *logmsg_reencode(const struct commit *commit, return out; } -static void fill_person(struct interp *table, const char *msg, int len) +static void format_person_part(struct strbuf *sb, char part, + const char *msg, int len) { int start, end, tz = 0; unsigned long date; @@ -295,7 +295,10 @@ static void fill_person(struct interp *table, const char *msg, int len) start = end + 1; while (end > 0 && isspace(msg[end - 1])) end--; - table[0].value = xmemdupz(msg, end); + if (part == 'n') { /* name */ + strbuf_add(sb, msg, end); + return; + } if (start >= len) return; @@ -307,7 +310,10 @@ static void fill_person(struct interp *table, const char *msg, int len) if (end >= len) return; - table[1].value = xmemdupz(msg + start, end - start); + if (part == 'e') { /* email */ + strbuf_add(sb, msg + start, end - start); + return; + } /* parse date */ for (start = end + 1; start < len && isspace(msg[start]); start++) @@ -318,7 +324,10 @@ static void fill_person(struct interp *table, const char *msg, int len) if (msg + start == ep) return; - table[5].value = xmemdupz(msg + start, ep - (msg + start)); + if (part == 't') { /* date, UNIX timestamp */ + strbuf_add(sb, msg + start, ep - (msg + start)); + return; + } /* parse tz */ for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) @@ -329,123 +338,107 @@ static void fill_person(struct interp *table, const char *msg, int len) tz = -tz; } - interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL)); - interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822)); - interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE)); - interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); + switch (part) { + case 'd': /* date */ + strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL)); + return; + case 'D': /* date, RFC2822 style */ + strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822)); + return; + case 'r': /* date, relative */ + strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE)); + return; + case 'i': /* date, ISO 8601 */ + strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601)); + return; + } } -void format_commit_message(const struct commit *commit, - const void *format, struct strbuf *sb) +static void format_commit_item(struct strbuf *sb, const char *placeholder, + void *context) { - struct interp table[] = { - { "%H" }, /* commit hash */ - { "%h" }, /* abbreviated commit hash */ - { "%T" }, /* tree hash */ - { "%t" }, /* abbreviated tree hash */ - { "%P" }, /* parent hashes */ - { "%p" }, /* abbreviated parent hashes */ - { "%an" }, /* author name */ - { "%ae" }, /* author email */ - { "%ad" }, /* author date */ - { "%aD" }, /* author date, RFC2822 style */ - { "%ar" }, /* author date, relative */ - { "%at" }, /* author date, UNIX timestamp */ - { "%ai" }, /* author date, ISO 8601 */ - { "%cn" }, /* committer name */ - { "%ce" }, /* committer email */ - { "%cd" }, /* committer date */ - { "%cD" }, /* committer date, RFC2822 style */ - { "%cr" }, /* committer date, relative */ - { "%ct" }, /* committer date, UNIX timestamp */ - { "%ci" }, /* committer date, ISO 8601 */ - { "%e" }, /* encoding */ - { "%s" }, /* subject */ - { "%b" }, /* body */ - { "%Cred" }, /* red */ - { "%Cgreen" }, /* green */ - { "%Cblue" }, /* blue */ - { "%Creset" }, /* reset color */ - { "%n" }, /* newline */ - { "%m" }, /* left/right/bottom */ - }; - enum interp_index { - IHASH = 0, IHASH_ABBREV, - ITREE, ITREE_ABBREV, - IPARENTS, IPARENTS_ABBREV, - IAUTHOR_NAME, IAUTHOR_EMAIL, - IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE, - IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601, - ICOMMITTER_NAME, ICOMMITTER_EMAIL, - ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822, - ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP, - ICOMMITTER_ISO8601, - IENCODING, - ISUBJECT, - IBODY, - IRED, IGREEN, IBLUE, IRESET_COLOR, - INEWLINE, - ILEFT_RIGHT, - }; + const struct commit *commit = context; struct commit_list *p; - char parents[1024]; - unsigned long len; int i; enum { HEADER, SUBJECT, BODY } state; const char *msg = commit->buffer; - if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) - die("invalid interp table!"); - /* these are independent of the commit */ - interp_set_entry(table, IRED, "\033[31m"); - interp_set_entry(table, IGREEN, "\033[32m"); - interp_set_entry(table, IBLUE, "\033[34m"); - interp_set_entry(table, IRESET_COLOR, "\033[m"); - interp_set_entry(table, INEWLINE, "\n"); + switch (placeholder[0]) { + case 'C': + switch (placeholder[3]) { + case 'd': /* red */ + strbuf_addstr(sb, "\033[31m"); + return; + case 'e': /* green */ + strbuf_addstr(sb, "\033[32m"); + return; + case 'u': /* blue */ + strbuf_addstr(sb, "\033[34m"); + return; + case 's': /* reset color */ + strbuf_addstr(sb, "\033[m"); + return; + } + case 'n': /* newline */ + strbuf_addch(sb, '\n'); + return; + } /* these depend on the commit */ if (!commit->object.parsed) parse_object(commit->object.sha1); - interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1)); - interp_set_entry(table, IHASH_ABBREV, - find_unique_abbrev(commit->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1)); - interp_set_entry(table, ITREE_ABBREV, - find_unique_abbrev(commit->tree->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ILEFT_RIGHT, - (commit->object.flags & BOUNDARY) - ? "-" - : (commit->object.flags & SYMMETRIC_LEFT) - ? "<" - : ">"); - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - sha1_to_hex(p->item->object.sha1)); - interp_set_entry(table, IPARENTS, parents + 1); - - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - find_unique_abbrev(p->item->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, IPARENTS_ABBREV, parents + 1); + switch (placeholder[0]) { + case 'H': /* commit hash */ + strbuf_addstr(sb, sha1_to_hex(commit->object.sha1)); + return; + case 'h': /* abbreviated commit hash */ + strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, + DEFAULT_ABBREV)); + return; + case 'T': /* tree hash */ + strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1)); + return; + case 't': /* abbreviated tree hash */ + strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1, + DEFAULT_ABBREV)); + return; + case 'P': /* parent hashes */ + for (p = commit->parents; p; p = p->next) { + if (p != commit->parents) + strbuf_addch(sb, ' '); + strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1)); + } + return; + case 'p': /* abbreviated parent hashes */ + for (p = commit->parents; p; p = p->next) { + if (p != commit->parents) + strbuf_addch(sb, ' '); + strbuf_addstr(sb, find_unique_abbrev( + p->item->object.sha1, DEFAULT_ABBREV)); + } + return; + case 'm': /* left/right/bottom */ + strbuf_addch(sb, (commit->object.flags & BOUNDARY) + ? '-' + : (commit->object.flags & SYMMETRIC_LEFT) + ? '<' + : '>'); + return; + } + /* For the rest we have to parse the commit header. */ for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { int eol; for (eol = i; msg[eol] && msg[eol] != '\n'; eol++) ; /* do nothing */ if (state == SUBJECT) { - table[ISUBJECT].value = xmemdupz(msg + i, eol - i); + if (placeholder[0] == 's') { + strbuf_add(sb, msg + i, eol - i); + return; + } i = eol; } if (i == eol) { @@ -453,29 +446,66 @@ void format_commit_message(const struct commit *commit, /* strip empty lines */ while (msg[eol + 1] == '\n') eol++; - } else if (!prefixcmp(msg + i, "author ")) - fill_person(table + IAUTHOR_NAME, - msg + i + 7, eol - i - 7); - else if (!prefixcmp(msg + i, "committer ")) - fill_person(table + ICOMMITTER_NAME, - msg + i + 10, eol - i - 10); - else if (!prefixcmp(msg + i, "encoding ")) - table[IENCODING].value = - xmemdupz(msg + i + 9, eol - i - 9); + } else if (!prefixcmp(msg + i, "author ")) { + if (placeholder[0] == 'a') { + format_person_part(sb, placeholder[1], + msg + i + 7, eol - i - 7); + return; + } + } else if (!prefixcmp(msg + i, "committer ")) { + if (placeholder[0] == 'c') { + format_person_part(sb, placeholder[1], + msg + i + 10, eol - i - 10); + return; + } + } else if (!prefixcmp(msg + i, "encoding ")) { + if (placeholder[0] == 'e') { + strbuf_add(sb, msg + i + 9, eol - i - 9); + return; + } + } i = eol; } - if (msg[i]) - table[IBODY].value = xstrdup(msg + i); + if (msg[i] && placeholder[0] == 'b') /* body */ + strbuf_addstr(sb, msg + i); +} - len = interpolate(sb->buf + sb->len, strbuf_avail(sb), - format, table, ARRAY_SIZE(table)); - if (len > strbuf_avail(sb)) { - strbuf_grow(sb, len); - interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, - format, table, ARRAY_SIZE(table)); - } - strbuf_setlen(sb, sb->len + len); - interp_clear_table(table, ARRAY_SIZE(table)); +void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb) +{ + const char *placeholders[] = { + "H", /* commit hash */ + "h", /* abbreviated commit hash */ + "T", /* tree hash */ + "t", /* abbreviated tree hash */ + "P", /* parent hashes */ + "p", /* abbreviated parent hashes */ + "an", /* author name */ + "ae", /* author email */ + "ad", /* author date */ + "aD", /* author date, RFC2822 style */ + "ar", /* author date, relative */ + "at", /* author date, UNIX timestamp */ + "ai", /* author date, ISO 8601 */ + "cn", /* committer name */ + "ce", /* committer email */ + "cd", /* committer date */ + "cD", /* committer date, RFC2822 style */ + "cr", /* committer date, relative */ + "ct", /* committer date, UNIX timestamp */ + "ci", /* committer date, ISO 8601 */ + "e", /* encoding */ + "s", /* subject */ + "b", /* body */ + "Cred", /* red */ + "Cgreen", /* green */ + "Cblue", /* blue */ + "Creset", /* reset color */ + "n", /* newline */ + "m", /* left/right/bottom */ + NULL + }; + strbuf_expand(sb, format, placeholders, format_commit_item, (void *)commit); } static void pp_header(enum cmit_fmt fmt, diff --git a/strbuf.c b/strbuf.c index f4201e160d..536b43204e 100644 --- a/strbuf.c +++ b/strbuf.c @@ -129,6 +129,30 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) strbuf_setlen(sb, sb->len + len); } +void strbuf_expand(struct strbuf *sb, const char *format, + const char **placeholders, expand_fn_t fn, void *context) +{ + for (;;) { + const char *percent, **p; + + percent = strchrnul(format, '%'); + strbuf_add(sb, format, percent - format); + if (!*percent) + break; + format = percent + 1; + + for (p = placeholders; *p; p++) { + if (!prefixcmp(format, *p)) + break; + } + if (*p) { + fn(sb, *p, context); + format += strlen(*p); + } else + strbuf_addch(sb, '%'); + } +} + size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { size_t res; diff --git a/strbuf.h b/strbuf.h index cd7f295b65..21d8944154 100644 --- a/strbuf.h +++ b/strbuf.h @@ -102,6 +102,9 @@ static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) { strbuf_add(sb, sb2->buf, sb2->len); } +typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); +extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context); + __attribute__((format(printf,2,3))) extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); From 6fd2f5e60d4d574ff9e5dd8ce1e229328c785d69 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 8 Nov 2007 18:19:08 +0000 Subject: [PATCH 27/97] rebase: operate on a detached HEAD The interactive version of rebase does all the operations on a detached HEAD, so that after a successful rebase, @{1} is the pre-rebase state. The reflogs of "HEAD" still show all the actions in detail. This teaches the non-interactive version to do the same. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase.sh | 56 +++++++++++++++++++++++++++++++++++++---- t/t3402-rebase-merge.sh | 7 +++++- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index 224cca98ee..c02be31f33 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -87,7 +87,7 @@ call_merge () { cmt="$(cat "$dotest/cmt.$1")" echo "$cmt" > "$dotest/current" hd=$(git rev-parse --verify HEAD) - cmt_name=$(git symbolic-ref HEAD) + cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD) msgnum=$(cat "$dotest/msgnum") end=$(cat "$dotest/end") eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' @@ -115,7 +115,24 @@ call_merge () { esac } +move_to_original_branch () { + test -z "$head_name" && + head_name="$(cat "$dotest"/head-name)" && + onto="$(cat "$dotest"/onto)" && + orig_head="$(cat "$dotest"/orig-head)" + case "$head_name" in + refs/*) + message="rebase finished: $head_name onto $onto" + git update-ref -m "$message" \ + $head_name $(git rev-parse HEAD) $orig_head && + git symbolic-ref HEAD $head_name || + die "Could not move back to $head_name" + ;; + esac +} + finish_rb_merge () { + move_to_original_branch rm -r "$dotest" echo "All done." } @@ -173,16 +190,23 @@ do finish_rb_merge exit fi - git am -3 --skip --resolvemsg="$RESOLVEMSG" + head_name=$(cat .dotest/head-name) && + onto=$(cat .dotest/onto) && + orig_head=$(cat .dotest/orig-head) && + git am -3 --skip --resolvemsg="$RESOLVEMSG" && + move_to_original_branch exit ;; --abort) git rerere clear if test -d "$dotest" then + move_to_original_branch rm -r "$dotest" elif test -d .dotest then + dotest=.dotest + move_to_original_branch rm -r .dotest else die "No rebase in progress?" @@ -318,6 +342,19 @@ then GIT_PAGER='' git diff --stat --summary "$mb" "$onto" fi +# move to a detached HEAD +orig_head=$(git rev-parse HEAD^0) +head_name=$(git symbolic-ref HEAD 2> /dev/null) +case "$head_name" in +'') + head_name="detached HEAD" + ;; +*) + git checkout "$orig_head" > /dev/null 2>&1 || + die "could not detach HEAD" + ;; +esac + # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. echo "First, rewinding head to replay your work on top of it..." git-reset --hard "$onto" @@ -327,14 +364,21 @@ git-reset --hard "$onto" if test "$mb" = "$branch" then echo >&2 "Fast-forwarded $branch_name to $onto_name." + move_to_original_branch exit 0 fi if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | - git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" - exit $? + git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" && + move_to_original_branch + ret=$? + test 0 != $ret -a -d .dotest && + echo $head_name > .dotest/head-name && + echo $onto > .dotest/onto && + echo $orig_head > .dotest/orig-head + exit $ret fi # start doing a rebase with git-merge @@ -343,8 +387,10 @@ fi mkdir -p "$dotest" echo "$onto" > "$dotest/onto" echo "$onto_name" > "$dotest/onto_name" -prev_head=`git rev-parse HEAD^0` +prev_head=$orig_head echo "$prev_head" > "$dotest/prev_head" +echo "$orig_head" > "$dotest/orig-head" +echo "$head_name" > "$dotest/head-name" msgnum=0 for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index 0779aaa9ab..7b7d07269a 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -48,9 +48,14 @@ test_expect_success 'reference merge' ' git merge -s recursive "reference merge" HEAD master ' +PRE_REBASE=$(git rev-parse test-rebase) test_expect_success rebase ' git checkout test-rebase && - git rebase --merge master + GIT_TRACE=1 git rebase --merge master +' + +test_expect_success 'test-rebase@{1} is pre rebase' ' + test $PRE_REBASE = $(git rev-parse test-rebase@{1}) ' test_expect_success 'merge and rebase should match' ' From 4bd5b7dacc404e6b733d9ab6744429c5027bb5e1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 10 Nov 2007 00:15:03 -0800 Subject: [PATCH 28/97] ce_match_stat, run_diff_files: use symbolic constants for readability ce_match_stat() can be told: (1) to ignore CE_VALID bit (used under "assume unchanged" mode) and perform the stat comparison anyway; (2) not to perform the contents comparison for racily clean entries and report mismatch of cached stat information; using its "option" parameter. Give them symbolic constants. Similarly, run_diff_files() can be told not to report anything on removed paths. Also give it a symbolic constant for that. Signed-off-by: Junio C Hamano --- builtin-apply.c | 2 +- cache.h | 14 ++++++++++---- check-racy.c | 2 +- diff-lib.c | 16 +++++++++------- diff.h | 4 +++- entry.c | 2 +- read-cache.c | 47 ++++++++++++++++++++++++++++++----------------- unpack-trees.c | 4 ++-- 8 files changed, 57 insertions(+), 34 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 5cc90e68f8..0fff02e0d8 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2099,7 +2099,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) return -1; return 0; } - return ce_match_stat(ce, st, 1); + return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID); } static int check_patch(struct patch *patch, struct patch *prev_patch) diff --git a/cache.h b/cache.h index fc195bc47c..31af16a7ee 100644 --- a/cache.h +++ b/cache.h @@ -174,8 +174,8 @@ extern struct index_state the_index; #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) -#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really)) -#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really)) +#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) +#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) #endif enum object_type { @@ -266,8 +266,14 @@ extern int remove_file_from_index(struct index_state *, const char *path); extern int add_file_to_index(struct index_state *, const char *path, int verbose); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); -extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int); -extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int); + +/* do stat comparison even if CE_VALID is true */ +#define CE_MATCH_IGNORE_VALID 01 +/* do not check the contents but report dirty on racily-clean entries */ +#define CE_MATCH_RACY_IS_DIRTY 02 +extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int); +extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int); + extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); extern int read_fd(int fd, char **return_buf, unsigned long *return_size); diff --git a/check-racy.c b/check-racy.c index d6a08b4a55..00d92a1663 100644 --- a/check-racy.c +++ b/check-racy.c @@ -18,7 +18,7 @@ int main(int ac, char **av) if (ce_match_stat(ce, &st, 0)) dirty++; - else if (ce_match_stat(ce, &st, 2)) + else if (ce_match_stat(ce, &st, CE_MATCH_RACY_IS_DIRTY)) racy++; else clean++; diff --git a/diff-lib.c b/diff-lib.c index da5571302d..9f8afbe71a 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -173,9 +173,10 @@ static int is_in_index(const char *path) } static int handle_diff_files_args(struct rev_info *revs, - int argc, const char **argv, int *silent) + int argc, const char **argv, + unsigned int *options) { - *silent = 0; + *options = 0; /* revs->max_count == -2 means --no-index */ while (1 < argc && argv[1][0] == '-') { @@ -192,7 +193,7 @@ static int handle_diff_files_args(struct rev_info *revs, revs->diffopt.no_index = 1; } else if (!strcmp(argv[1], "-q")) - *silent = 1; + *options |= DIFF_SILENT_ON_REMOVED; else return error("invalid option: %s", argv[1]); argv++; argc--; @@ -305,9 +306,9 @@ int setup_diff_no_index(struct rev_info *revs, int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) { - int silent_on_removed; + unsigned int options; - if (handle_diff_files_args(revs, argc, argv, &silent_on_removed)) + if (handle_diff_files_args(revs, argc, argv, &options)) return -1; if (revs->diffopt.no_index) { @@ -329,13 +330,14 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) perror("read_cache"); return -1; } - return run_diff_files(revs, silent_on_removed); + return run_diff_files(revs, options); } -int run_diff_files(struct rev_info *revs, int silent_on_removed) +int run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; + int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; diff --git a/diff.h b/diff.h index 4546aad219..de533da15d 100644 --- a/diff.h +++ b/diff.h @@ -224,7 +224,9 @@ extern void diff_flush(struct diff_options*); extern const char *diff_unique_abbrev(const unsigned char *, int); -extern int run_diff_files(struct rev_info *revs, int silent_on_removed); +/* do not report anything on removed paths */ +#define DIFF_SILENT_ON_REMOVED 01 +extern int run_diff_files(struct rev_info *revs, unsigned int option); extern int setup_diff_no_index(struct rev_info *revs, int argc, const char ** argv, int nongit, const char *prefix); extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv); diff --git a/entry.c b/entry.c index fc3a506ece..ef88f62ce8 100644 --- a/entry.c +++ b/entry.c @@ -200,7 +200,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); if (!lstat(path, &st)) { - unsigned changed = ce_match_stat(ce, &st, 1); + unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; if (!state->force) { diff --git a/read-cache.c b/read-cache.c index 928e8fa1ae..9e4d4a9136 100644 --- a/read-cache.c +++ b/read-cache.c @@ -194,11 +194,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) } int ie_match_stat(struct index_state *istate, - struct cache_entry *ce, struct stat *st, int options) + struct cache_entry *ce, struct stat *st, + unsigned int options) { unsigned int changed; - int ignore_valid = options & 01; - int assume_racy_is_modified = options & 02; + int ignore_valid = options & CE_MATCH_IGNORE_VALID; + int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY; /* * If it's marked as always valid in the index, it's @@ -238,10 +239,11 @@ int ie_match_stat(struct index_state *istate, } int ie_modified(struct index_state *istate, - struct cache_entry *ce, struct stat *st, int really) + struct cache_entry *ce, struct stat *st, unsigned int options) { int changed, changed_fs; - changed = ie_match_stat(istate, ce, st, really); + + changed = ie_match_stat(istate, ce, st, options); if (!changed) return 0; /* @@ -420,7 +422,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) pos = index_name_pos(istate, ce->name, namelen); if (0 <= pos && !ce_stage(istate->cache[pos]) && - !ie_modified(istate, istate->cache[pos], &st, 1)) { + !ie_modified(istate, istate->cache[pos], &st, CE_MATCH_IGNORE_VALID)) { /* Nothing changed, really */ free(ce); return 0; @@ -782,11 +784,13 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti * to link up the stat cache details with the proper files. */ static struct cache_entry *refresh_cache_ent(struct index_state *istate, - struct cache_entry *ce, int really, int *err) + struct cache_entry *ce, + unsigned int options, int *err) { struct stat st; struct cache_entry *updated; int changed, size; + int ignore_valid = options & CE_MATCH_IGNORE_VALID; if (lstat(ce->name, &st) < 0) { if (err) @@ -794,16 +798,23 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, return NULL; } - changed = ie_match_stat(istate, ce, &st, really); + changed = ie_match_stat(istate, ce, &st, options); if (!changed) { - if (really && assume_unchanged && + /* + * The path is unchanged. If we were told to ignore + * valid bit, then we did the actual stat check and + * found that the entry is unmodified. If the entry + * is not marked VALID, this is the place to mark it + * valid again, under "assume unchanged" mode. + */ + if (ignore_valid && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) ; /* mark this one VALID again */ else return ce; } - if (ie_modified(istate, ce, &st, really)) { + if (ie_modified(istate, ce, &st, options)) { if (err) *err = EINVAL; return NULL; @@ -814,13 +825,14 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, memcpy(updated, ce, size); fill_stat_cache_info(updated, &st); - /* In this case, if really is not set, we should leave - * CE_VALID bit alone. Otherwise, paths marked with - * --no-assume-unchanged (i.e. things to be edited) will - * reacquire CE_VALID bit automatically, which is not - * really what we want. + /* + * If ignore_valid is not set, we should leave CE_VALID bit + * alone. Otherwise, paths marked with --no-assume-unchanged + * (i.e. things to be edited) will reacquire CE_VALID bit + * automatically, which is not really what we want. */ - if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) + if (!ignore_valid && assume_unchanged && + !(ce->ce_flags & htons(CE_VALID))) updated->ce_flags &= ~htons(CE_VALID); return updated; @@ -834,6 +846,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int allow_unmerged = (flags & REFRESH_UNMERGED) != 0; int quiet = (flags & REFRESH_QUIET) != 0; int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; + unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce, *new; @@ -855,7 +868,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen)) continue; - new = refresh_cache_ent(istate, ce, really, &cache_errno); + new = refresh_cache_ent(istate, ce, options, &cache_errno); if (new == ce) continue; if (!new) { diff --git a/unpack-trees.c b/unpack-trees.c index ccfeb6e245..9411c67a06 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -406,7 +406,7 @@ static void verify_uptodate(struct cache_entry *ce, return; if (!lstat(ce->name, &st)) { - unsigned changed = ce_match_stat(ce, &st, 1); + unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return; /* @@ -927,7 +927,7 @@ int oneway_merge(struct cache_entry **src, if (o->reset) { struct stat st; if (lstat(old->name, &st) || - ce_match_stat(old, &st, 1)) + ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID)) old->ce_flags |= htons(CE_UPDATE); } return keep_entry(old, o); From fb63d7f889cf5df417b731b07952689df7f745c8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 9 Nov 2007 18:22:52 -0800 Subject: [PATCH 29/97] git-add: make the entry stat-clean after re-adding the same contents Earlier in commit 0781b8a9b2fe760fc4ed519a3a26e4b9bd6ccffe (add_file_to_index: skip rehashing if the cached stat already matches), add_file_to_index() were taught not to re-add the path if it already matches the index. The change meant well, but was not executed quite right. It used ie_modified() to see if the file on the work tree is really different from the index, and skipped adding the contents if the function says "not modified". This was wrong. There are three possible comparison results between the index and the file in the work tree: - with lstat(2) we _know_ they are different. E.g. if the length or the owner in the cached stat information is different from the length we just obtained from lstat(2), we can tell the file is modified without looking at the actual contents. - with lstat(2) we _know_ they are the same. The same length, the same owner, the same everything (but this has a twist, as described below). - we cannot tell from lstat(2) information alone and need to go to the filesystem to actually compare. The last case arises from what we call 'racy git' situation, that can be caused with this sequence: $ echo hello >file $ git add file $ echo aeiou >file ;# the same length If the second "echo" is done within the same filesystem timestamp granularity as the first "echo", then the timestamp recorded by "git add" and the timestamp we get from lstat(2) will be the same, and we can mistakenly say the file is not modified. The path is called 'racily clean'. We need to reliably detect racily clean paths are in fact modified. To solve this problem, when we write out the index, we mark the index entry that has the same timestamp as the index file itself (that is the time from the point of view of the filesystem) to tell any later code that does the lstat(2) comparison not to trust the cached stat info, and ie_modified() then actually goes to the filesystem to compare the contents for such a path. That's all good, but it should not be used for this "git add" optimization, as the goal of "git add" is to actually update the path in the index and make it stat-clean. With the false optimization, we did _not_ cause any data loss (after all, what we failed to do was only to update the cached stat information), but it made the following sequence leave the file stat dirty: $ echo hello >file $ git add file $ echo hello >file ;# the same contents $ git add file The solution is not to use ie_modified() which goes to the filesystem to see if it is really clean, but instead use ie_match_stat() with "assume racily clean paths are dirty" option, to force re-adding of such a path. There was another problem with "git add -u". The codepath shares the same issue when adding the paths that are found to be modified, but in addition, it asked "git diff-files" machinery run_diff_files() function (which is "git diff-files") to list the paths that are modified. But "git diff-files" machinery uses the same ie_modified() call so that it does not report racily clean _and_ actually clean paths as modified, which is not what we want. The patch allows the callers of run_diff_files() to pass the same "assume racily clean paths are dirty" option, and makes "git-add -u" codepath to use that option, to discover and re-add racily clean _and_ actually clean paths. We could further optimize on top of this patch to differentiate the case where the path really needs re-adding (i.e. the content of the racily clean entry was indeed different) and the case where only the cached stat information needs to be refreshed (i.e. the racily clean entry was actually clean), but I do not think it is worth it. This patch applies to maint and all the way up. Signed-off-by: Junio C Hamano --- builtin-add.c | 2 +- diff-lib.c | 4 +++- diff.h | 2 ++ read-cache.c | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 373f87f9f2..e072320d20 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -123,7 +123,7 @@ static void update(int verbose, const char *prefix, const char **files) rev.diffopt.format_callback_data = &verbose; if (read_cache() < 0) die("index file corrupt"); - run_diff_files(&rev, 0); + run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); } static void refresh(int verbose, const char **pathspec) diff --git a/diff-lib.c b/diff-lib.c index 9f8afbe71a..ec1b5e3d44 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -338,6 +338,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option) int entries, i; int diff_unmerged_stage = revs->max_count; int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; + unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) + ? CE_MATCH_RACY_IS_DIRTY : 0); if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; @@ -443,7 +445,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) ce->sha1, ce->name, NULL); continue; } - changed = ce_match_stat(ce, &st, 0); + changed = ce_match_stat(ce, &st, ce_option); if (!changed && !revs->diffopt.find_copies_harder) continue; oldmode = ntohl(ce->ce_mode); diff --git a/diff.h b/diff.h index de533da15d..efaa8f711a 100644 --- a/diff.h +++ b/diff.h @@ -226,6 +226,8 @@ extern const char *diff_unique_abbrev(const unsigned char *, int); /* do not report anything on removed paths */ #define DIFF_SILENT_ON_REMOVED 01 +/* report racily-clean paths as modified */ +#define DIFF_RACY_IS_MODIFIED 02 extern int run_diff_files(struct rev_info *revs, unsigned int option); extern int setup_diff_no_index(struct rev_info *revs, int argc, const char ** argv, int nongit, const char *prefix); diff --git a/read-cache.c b/read-cache.c index 9e4d4a9136..c3dbf89426 100644 --- a/read-cache.c +++ b/read-cache.c @@ -388,6 +388,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) int size, namelen, pos; struct stat st; struct cache_entry *ce; + unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; if (lstat(path, &st)) die("%s: unable to stat (%s)", path, strerror(errno)); @@ -422,7 +423,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) pos = index_name_pos(istate, ce->name, namelen); if (0 <= pos && !ce_stage(istate->cache[pos]) && - !ie_modified(istate, istate->cache[pos], &st, CE_MATCH_IGNORE_VALID)) { + !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) { /* Nothing changed, really */ free(ce); return 0; From 295dd2ad201c0ebb281563750a13d904bd466e01 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 9 Nov 2007 06:06:10 -0500 Subject: [PATCH 30/97] Fix memory leak in traverse_commit_list If we were listing objects too then the objects were buffered in an array only reachable from a stack allocated structure. When this function returns that array would be leaked as nobody would have a reference to it anymore. Historically this hasn't been a problem as the primary user of traverse_commit_list() (the noble git-rev-list) would terminate as soon as the function was finished, thus allowing the operating system to cleanup memory. However we have been leaking this data in git-pack-objects ever since that program learned how to run the revision listing internally, rather than relying on reading object names from git-rev-list. To better facilitate reuse of traverse_commit_list during other builtin tools (such as git-fetch) we shouldn't leak temporary memory like this and instead we need to clean up properly after ourselves. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- list-objects.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/list-objects.c b/list-objects.c index e5c88c278f..4ef58e7ec0 100644 --- a/list-objects.c +++ b/list-objects.c @@ -170,4 +170,11 @@ void traverse_commit_list(struct rev_info *revs, } for (i = 0; i < objects.nr; i++) show_object(&objects.objects[i]); + free(objects.objects); + if (revs->pending.nr) { + free(revs->pending.objects); + revs->pending.nr = 0; + revs->pending.alloc = 0; + revs->pending.objects = NULL; + } } From c899a57c28ee7cf251f856b575128de21b5a9a12 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sat, 10 Nov 2007 17:47:54 +0100 Subject: [PATCH 31/97] for-each-ref: fix setup of option-parsing for --sort The option value for --sort is already a pointer to a pointer to struct ref_sort, so just use it. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-for-each-ref.c | 2 +- t/t6300-for-each-ref.sh | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index da8c7948e6..e909e66bed 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -847,7 +847,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_GROUP(""), OPT_INTEGER( 0 , "count", &maxcount, "show only matched refs"), OPT_STRING( 0 , "format", &format, "format", "format to use for the output"), - OPT_CALLBACK(0 , "sort", &sort_tail, "key", + OPT_CALLBACK(0 , "sort", sort_tail, "key", "field name to sort on", &opt_parse_sort), OPT_END(), }; diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index d0809eb651..c722635050 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -148,4 +148,26 @@ test_expect_success 'Check format "rfc2822" date fields output' ' git diff expected actual ' +cat >expected <<\EOF +refs/heads/master +refs/tags/testtag +EOF + +test_expect_success 'Verify ascending sort' ' + git-for-each-ref --format="%(refname)" --sort=refname >actual && + git diff expected actual +' + + +cat >expected <<\EOF +refs/tags/testtag +refs/heads/master +EOF + +test_expect_success 'Verify descending sort' ' + git-for-each-ref --format="%(refname)" --sort=-refname >actual && + git diff expected actual +' + + test_done From 570f32266943d3a96d7ed64844986d847209364d Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Sat, 10 Nov 2007 15:17:25 +0100 Subject: [PATCH 32/97] test-lib.sh: move error line after error() declaration This patch removes a spurious "command not found" error and actually makes the "Test script did not set test_description." string follow the command line option "--no-color". Signed-off-by: Michele Ballabio Signed-off-by: Junio C Hamano --- t/test-lib.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 603a8cd5e7..90b6844d00 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -66,9 +66,6 @@ esac tput sgr0 >/dev/null 2>&1 && color=t -test "${test_description}" != "" || -error "Test script did not set test_description." - while test "$#" -ne 0 do case "$1" in @@ -77,8 +74,7 @@ do -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) immediate=t; shift ;; -h|--h|--he|--hel|--help) - echo "$test_description" - exit 0 ;; + help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; -q|--q|--qu|--qui|--quie|--quiet) @@ -124,6 +120,15 @@ say () { say_color info "$*" } +test "${test_description}" != "" || +error "Test script did not set test_description." + +if test "$help" = "t" +then + echo "$test_description" + exit 0 +fi + exec 5>&1 if test "$verbose" = "t" then From a62d6d84c67cebd71e8d88171a2ebe0bf3aca8a0 Mon Sep 17 00:00:00 2001 From: Vincent Zanotti Date: Sat, 10 Nov 2007 19:55:27 +0100 Subject: [PATCH 33/97] gitweb: correct month in date display for atom feeds Signed-off-by: Vincent Zanotti Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3064298f28..9deefce0a6 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1713,7 +1713,7 @@ sub parse_date { $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; $date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ", - 1900+$year, $mon, $mday, $hour ,$min, $sec; + 1900+$year, 1+$mon, $mday, $hour ,$min, $sec; $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; my $local = $epoch + ((int $1 + ($2/60)) * 3600); From f29d59586c2a1666d18776cca5d96752dec0e8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 10 Nov 2007 12:14:20 +0100 Subject: [PATCH 34/97] --pretty=format: parse commit message only once As Jeff King pointed out, some placeholder expansions are related to each other: the steps to calculate one go most of the way towards calculating the other, too. This patch makes format_commit_message() parse the commit message only once, remembering the position of each item. This speeds up handling of format strings containing multiple placeholders from the set %s, %a*, %c*, %e, %b. Here are the timings for the git version in next. The first one is to estimate the overhead of the caching, the second one is taken from http://svn.tue.mpg.de/tentakel/trunk/tentakel/Makefile as an example of a format string found in the wild. The times are the fastest of three consecutive runs in each case: $ time git log --pretty=format:%e >/dev/null real 0m0.381s user 0m0.340s sys 0m0.024s $ time git log --pretty=format:"* %cd %cn%n%n%s%n%b" >/dev/null real 0m0.623s user 0m0.556s sys 0m0.052s And here the times with this patch: $ time git log --pretty=format:%e >/dev/null real 0m0.385s user 0m0.332s sys 0m0.040s $ time git log --pretty=format:"* %cd %cn%n%n%s%n%b" >/dev/null real 0m0.563s user 0m0.504s sys 0m0.048s Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 124 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 42 deletions(-) diff --git a/pretty.c b/pretty.c index 9fbd73f748..17a3010a6e 100644 --- a/pretty.c +++ b/pretty.c @@ -354,14 +354,67 @@ static void format_person_part(struct strbuf *sb, char part, } } +struct chunk { + size_t off; + size_t len; +}; + +struct format_commit_context { + const struct commit *commit; + + /* 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 body_off; +}; + +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++) { + 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 + 1] == '\n') + eol++; + } else if (!prefixcmp(msg + i, "author ")) { + context->author.off = i + 7; + context->author.len = eol - i - 7; + } else if (!prefixcmp(msg + i, "committer ")) { + context->committer.off = i + 10; + context->committer.len = eol - i - 10; + } else if (!prefixcmp(msg + i, "encoding ")) { + context->encoding.off = i + 9; + context->encoding.len = eol - i - 9; + } + i = eol; + } + context->body_off = i; + context->commit_header_parsed = 1; +} + static void format_commit_item(struct strbuf *sb, const char *placeholder, void *context) { - const struct commit *commit = context; - struct commit_list *p; - int i; - enum { HEADER, SUBJECT, BODY } state; + struct format_commit_context *c = context; + const struct commit *commit = c->commit; const char *msg = commit->buffer; + struct commit_list *p; /* these are independent of the commit */ switch (placeholder[0]) { @@ -429,45 +482,28 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder, } /* For the rest we have to parse the commit header. */ - for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { - int eol; - for (eol = i; msg[eol] && msg[eol] != '\n'; eol++) - ; /* do nothing */ + if (!c->commit_header_parsed) + parse_commit_header(c); - if (state == SUBJECT) { - if (placeholder[0] == 's') { - strbuf_add(sb, msg + i, eol - i); - return; - } - i = eol; - } - if (i == eol) { - state++; - /* strip empty lines */ - while (msg[eol + 1] == '\n') - eol++; - } else if (!prefixcmp(msg + i, "author ")) { - if (placeholder[0] == 'a') { - format_person_part(sb, placeholder[1], - msg + i + 7, eol - i - 7); - return; - } - } else if (!prefixcmp(msg + i, "committer ")) { - if (placeholder[0] == 'c') { - format_person_part(sb, placeholder[1], - msg + i + 10, eol - i - 10); - return; - } - } else if (!prefixcmp(msg + i, "encoding ")) { - if (placeholder[0] == 'e') { - strbuf_add(sb, msg + i + 9, eol - i - 9); - return; - } - } - i = eol; + switch (placeholder[0]) { + case 's': + strbuf_add(sb, msg + c->subject.off, c->subject.len); + return; + case 'a': + format_person_part(sb, placeholder[1], + msg + c->author.off, c->author.len); + return; + case 'c': + format_person_part(sb, placeholder[1], + msg + c->committer.off, c->committer.len); + return; + case 'e': + strbuf_add(sb, msg + c->encoding.off, c->encoding.len); + return; + case 'b': + strbuf_addstr(sb, msg + c->body_off); + return; } - if (msg[i] && placeholder[0] == 'b') /* body */ - strbuf_addstr(sb, msg + i); } void format_commit_message(const struct commit *commit, @@ -505,7 +541,11 @@ void format_commit_message(const struct commit *commit, "m", /* left/right/bottom */ NULL }; - strbuf_expand(sb, format, placeholders, format_commit_item, (void *)commit); + struct format_commit_context context; + + memset(&context, 0, sizeof(context)); + context.commit = commit; + strbuf_expand(sb, format, placeholders, format_commit_item, &context); } static void pp_header(enum cmit_fmt fmt, From 91db267ec849279053cf3ac3066c2f2c11db4321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 10 Nov 2007 12:16:05 +0100 Subject: [PATCH 35/97] add strbuf_adddup() Add a new function, strbuf_adddup(), that appends a duplicate of a part of a struct strbuf to end of the latter. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- strbuf.c | 7 +++++++ strbuf.h | 1 + 2 files changed, 8 insertions(+) diff --git a/strbuf.c b/strbuf.c index 536b43204e..dbd8c4bcfb 100644 --- a/strbuf.c +++ b/strbuf.c @@ -106,6 +106,13 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len) strbuf_setlen(sb, sb->len + len); } +void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) +{ + strbuf_grow(sb, len); + memcpy(sb->buf + sb->len, sb->buf + pos, len); + strbuf_setlen(sb, sb->len + len); +} + void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { int len; diff --git a/strbuf.h b/strbuf.h index 21d8944154..13919123dc 100644 --- a/strbuf.h +++ b/strbuf.h @@ -101,6 +101,7 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) { static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) { strbuf_add(sb, sb2->buf, sb2->len); } +extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context); From b9c62321380374d09cc99570cdd1390674532832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 10 Nov 2007 12:18:26 +0100 Subject: [PATCH 36/97] --format=pretty: avoid calculating expensive expansions twice As Jeff King remarked, format strings with duplicate placeholders can be slow to expand, because each instance is calculated anew. This patch makes use of the fact that format_commit_message() and its helper functions only ever add stuff to the end of the strbuf. For certain expensive placeholders, store the offset and length of their expansion with the strbuf at the first occurrence. Later they expansion result can simply be copied from there -- no malloc() or strdup() required. These certain placeholders are the abbreviated commit, tree and parent hashes, as the search for a unique abbreviated hash is quite costly. Here are the times for next (best of three runs): $ time git log --pretty=format:%h >/dev/null real 0m0.611s user 0m0.404s sys 0m0.204s $ time git log --pretty=format:%h%h%h%h >/dev/null real 0m1.206s user 0m0.744s sys 0m0.452s And here those with this patch (and the previous two); the speedup of the single placeholder case is just noise: $ time git log --pretty=format:%h >/dev/null real 0m0.608s user 0m0.416s sys 0m0.192s $ time git log --pretty=format:%h%h%h%h >/dev/null real 0m0.639s user 0m0.488s sys 0m0.140s Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pretty.c b/pretty.c index 17a3010a6e..9db75b4e4f 100644 --- a/pretty.c +++ b/pretty.c @@ -369,8 +369,30 @@ struct format_commit_context { struct chunk committer; struct chunk encoding; size_t body_off; + + /* The following ones are relative to the result struct strbuf. */ + struct chunk abbrev_commit_hash; + struct chunk abbrev_tree_hash; + struct chunk abbrev_parent_hashes; }; +static int add_again(struct strbuf *sb, struct chunk *chunk) +{ + if (chunk->len) { + strbuf_adddup(sb, chunk->off, chunk->len); + return 1; + } + + /* + * We haven't seen this chunk before. Our caller is surely + * going to add it the hard way now. Remember the most likely + * start of the to-be-added chunk: the current end of the + * struct strbuf. + */ + chunk->off = sb->len; + return 0; +} + static void parse_commit_header(struct format_commit_context *context) { const char *msg = context->commit->buffer; @@ -447,15 +469,21 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder, strbuf_addstr(sb, sha1_to_hex(commit->object.sha1)); return; case 'h': /* abbreviated commit hash */ + if (add_again(sb, &c->abbrev_commit_hash)) + return; strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); + c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off; return; case 'T': /* tree hash */ strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1)); return; case 't': /* abbreviated tree hash */ + if (add_again(sb, &c->abbrev_tree_hash)) + return; strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1, DEFAULT_ABBREV)); + c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off; return; case 'P': /* parent hashes */ for (p = commit->parents; p; p = p->next) { @@ -465,12 +493,16 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder, } return; case 'p': /* abbreviated parent hashes */ + if (add_again(sb, &c->abbrev_parent_hashes)) + return; for (p = commit->parents; p; p = p->next) { if (p != commit->parents) strbuf_addch(sb, ' '); strbuf_addstr(sb, find_unique_abbrev( p->item->object.sha1, DEFAULT_ABBREV)); } + c->abbrev_parent_hashes.len = sb->len - + c->abbrev_parent_hashes.off; return; case 'm': /* left/right/bottom */ strbuf_addch(sb, (commit->object.flags & BOUNDARY) From ff350ccf49a800c4c90f817d346fb1bcb96e02e7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 10 Nov 2007 15:00:33 -0500 Subject: [PATCH 37/97] git-hash-object should honor config variables ... such as core.compression. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- hash-object.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hash-object.c b/hash-object.c index 18f5017f51..0a58f3f126 100644 --- a/hash-object.c +++ b/hash-object.c @@ -42,6 +42,8 @@ int main(int argc, char **argv) int prefix_length = -1; int no_more_flags = 0; + git_config(git_default_config); + for (i = 1 ; i < argc; i++) { if (!no_more_flags && argv[i][0] == '-') { if (!strcmp(argv[i], "-t")) { From a91ef6e75b897a255cc17b70014a39e68dd54c7a Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 10 Nov 2007 23:29:10 -0500 Subject: [PATCH 38/97] fix index-pack with packs >4GB containing deltas on 32-bit machines This probably hasn't been properly tested before. Here's a script to create a 8GB repo with the necessary characteristics (copy the test-genrandom executable from the Git build tree to /tmp first): ----- #!/bin/bash git init git config core.compression 0 # create big objects with no deltas for i in $(seq -w 1 2 63) do echo $i /tmp/test-genrandom $i 268435456 > file_$i git add file_$i rm file_$i echo "file_$i -delta" >> .gitattributes done # create "deltifiable" objects in between big objects for i in $(seq -w 2 2 64) do echo "$i $i $i" >> grow cp grow file_$i git add file_$i rm file_$i done rm grow # create a pack with them git commit -q -m "commit of big objects interlaced with small deltas" git repack -a -d ----- Then clone this repo over the Git protocol. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index-pack.c b/index-pack.c index db58e05041..c232e3fc78 100644 --- a/index-pack.c +++ b/index-pack.c @@ -254,7 +254,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ static void *get_data_from_pack(struct object_entry *obj) { - unsigned long from = obj[0].idx.offset + obj[0].hdr_size; + off_t from = obj[0].idx.offset + obj[0].hdr_size; unsigned long len = obj[1].idx.offset - from; unsigned long rdy = 0; unsigned char *src, *data; From 9e79f00f06a5500b30941e6925adda070504e6cf Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Sat, 10 Nov 2007 12:55:48 +0100 Subject: [PATCH 39/97] Simplify strchrnul() compat code strchrnul() was introduced in glibc in April 1999 and included in glibc-2.1. Checking for that version means the majority of all git users would get to use the optimized version in glibc. Of the remaining few some might get to use a slightly slower version than necessary but probably not slower than what we have today. Unfortunately, __GLIBC_PREREQ() macro was not available in glibc 2.1.1 which was short lived but already supported strchrnul(). Odd minority users of that library needs to live with our compatibility inline version. Rediffed-against-next-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Makefile | 13 ------------- compat/strchrnul.c | 8 -------- git-compat-util.h | 9 +++++++-- 3 files changed, 7 insertions(+), 23 deletions(-) delete mode 100644 compat/strchrnul.c diff --git a/Makefile b/Makefile index 69dcbb227e..621270f623 100644 --- a/Makefile +++ b/Makefile @@ -30,8 +30,6 @@ all:: # # Define NO_MEMMEM if you don't have memmem. # -# Define NO_STRCHRNUL if you don't have strchrnul. -# # Define NO_STRLCPY if you don't have strlcpy. # # Define NO_STRTOUMAX if you don't have strtoumax in the C library. @@ -408,7 +406,6 @@ ifeq ($(uname_S),Darwin) OLD_ICONV = UnfortunatelyYes NO_STRLCPY = YesPlease NO_MEMMEM = YesPlease - NO_STRCHRNUL = YesPlease endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@ -416,7 +413,6 @@ ifeq ($(uname_S),SunOS) SHELL_PATH = /bin/bash NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_STRCHRNUL = YesPlease NO_HSTRERROR = YesPlease ifeq ($(uname_R),5.8) NEEDS_LIBICONV = YesPlease @@ -442,7 +438,6 @@ ifeq ($(uname_O),Cygwin) NO_D_INO_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_STRCHRNUL = YesPlease NO_SYMLINK_HEAD = YesPlease NEEDS_LIBICONV = YesPlease NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes @@ -457,14 +452,12 @@ endif ifeq ($(uname_S),FreeBSD) NEEDS_LIBICONV = YesPlease NO_MEMMEM = YesPlease - NO_STRCHRNUL = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_STRCHRNUL = YesPlease NEEDS_LIBICONV = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib @@ -480,7 +473,6 @@ endif ifeq ($(uname_S),AIX) NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease - NO_STRCHRNUL = YesPlease NO_STRLCPY = YesPlease NEEDS_LIBICONV=YesPlease endif @@ -493,7 +485,6 @@ ifeq ($(uname_S),IRIX64) NO_SETENV=YesPlease NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease - NO_STRCHRNUL = YesPlease NO_STRLCPY = YesPlease NO_SOCKADDR_STORAGE=YesPlease SHELL_PATH=/usr/gnu/bin/bash @@ -704,10 +695,6 @@ ifdef NO_MEMMEM COMPAT_CFLAGS += -DNO_MEMMEM COMPAT_OBJS += compat/memmem.o endif -ifdef NO_STRCHRNUL - COMPAT_CFLAGS += -DNO_STRCHRNUL - COMPAT_OBJS += compat/strchrnul.o -endif ifdef THREADED_DELTA_SEARCH BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH diff --git a/compat/strchrnul.c b/compat/strchrnul.c deleted file mode 100644 index 51839feb6e..0000000000 --- a/compat/strchrnul.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "../git-compat-util.h" - -char *gitstrchrnul(const char *s, int c) -{ - while (*s && *s != c) - s++; - return (char *)s; -} diff --git a/git-compat-util.h b/git-compat-util.h index e72654bc40..92d79673f8 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -183,9 +183,14 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif -#ifdef NO_STRCHRNUL +#if !defined(__GLIBC_PREREQ) && !__GLIBC_PREREQ(2, 1) #define strchrnul gitstrchrnul -char *gitstrchrnul(const char *s, int c); +static inline char *gitstrchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} #endif extern void release_pack_memory(size_t, int); From 05ee917a6eaa2a32e49221537e4632621d49b076 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 Nov 2007 15:01:43 +0100 Subject: [PATCH 40/97] push: mention --verbose option in documentation Before this commit, only '-v' was documented. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index e5dd4c1066..4a68aaba34 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git-push' [--all] [--dry-run] [--tags] [--receive-pack=] - [--repo=all] [-f | --force] [-v] [ ...] + [--repo=all] [-f | --force] [-v | --verbose] [ ...] DESCRIPTION ----------- @@ -95,7 +95,7 @@ the remote repository. transfer spends extra cycles to minimize the number of objects to be sent and meant to be used on slower connection. --v:: +-v, \--verbose:: Run verbosely. include::urls-remotes.txt[] From 1b2486d73789aaed0671e8d5cc60f5624d8e7ce5 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 Nov 2007 15:01:44 +0100 Subject: [PATCH 41/97] push: teach push to pass --verbose option to transport layer A --verbose option to push should also be passed to the transport layer, i.e. git-send-pack, git-http-push. git push is modified to do so. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- builtin-push.c | 2 ++ transport.c | 8 ++++++-- transport.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/builtin-push.c b/builtin-push.c index 2c561953fc..6d1da07c46 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -115,6 +115,8 @@ int cmd_push(int argc, const char **argv, const char *prefix) flags |= TRANSPORT_PUSH_FORCE; if (dry_run) flags |= TRANSPORT_PUSH_DRY_RUN; + if (verbose) + flags |= TRANSPORT_PUSH_VERBOSE; if (tags) add_refspec("refs/tags/*"); if (all) diff --git a/transport.c b/transport.c index fa5cfbb09d..e8a2608372 100644 --- a/transport.c +++ b/transport.c @@ -386,7 +386,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons int argc; int err; - argv = xmalloc((refspec_nr + 11) * sizeof(char *)); + argv = xmalloc((refspec_nr + 12) * sizeof(char *)); argv[0] = "http-push"; argc = 1; if (flags & TRANSPORT_PUSH_ALL) @@ -395,6 +395,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons argv[argc++] = "--force"; if (flags & TRANSPORT_PUSH_DRY_RUN) argv[argc++] = "--dry-run"; + if (flags & TRANSPORT_PUSH_VERBOSE) + argv[argc++] = "--verbose"; argv[argc++] = transport->url; while (refspec_nr--) argv[argc++] = *refspec++; @@ -655,7 +657,7 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const int argc; int err; - argv = xmalloc((refspec_nr + 11) * sizeof(char *)); + argv = xmalloc((refspec_nr + 12) * sizeof(char *)); argv[0] = "send-pack"; argc = 1; if (flags & TRANSPORT_PUSH_ALL) @@ -664,6 +666,8 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const argv[argc++] = "--force"; if (flags & TRANSPORT_PUSH_DRY_RUN) argv[argc++] = "--dry-run"; + if (flags & TRANSPORT_PUSH_VERBOSE) + argv[argc++] = "--verbose"; if (data->receivepack) { char *rp = xmalloc(strlen(data->receivepack) + 16); sprintf(rp, "--receive-pack=%s", data->receivepack); diff --git a/transport.h b/transport.h index df12ea7424..2f80ab4b03 100644 --- a/transport.h +++ b/transport.h @@ -30,6 +30,7 @@ struct transport { #define TRANSPORT_PUSH_ALL 1 #define TRANSPORT_PUSH_FORCE 2 #define TRANSPORT_PUSH_DRY_RUN 4 +#define TRANSPORT_PUSH_VERBOSE 8 /* Returns a transport suitable for the url */ struct transport *transport_get(struct remote *, const char *); From 859a4dbcadd200ae955fe36d0c4fb3f4bce0e032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vanicat?= Date: Sun, 11 Nov 2007 13:28:08 +0100 Subject: [PATCH 42/97] Make GIT_INDEX_FILE apply to git-commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, when committing, git-commit ignore the value of GIT_INDEX_FILE, and always use $GIT_DIR/index. This patch fix it. Signed-off-by: Rémi Vanicat Signed-off-by: Junio C Hamano --- git-commit.sh | 2 +- t/t7500-commit.sh | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/git-commit.sh b/git-commit.sh index ab43217be4..fdaa019e8f 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -26,7 +26,7 @@ refuse_partial () { } TMP_INDEX= -THIS_INDEX="$GIT_DIR/index" +THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}" NEXT_INDEX="$GIT_DIR/next-index$$" rm -f "$NEXT_INDEX" save_index () { diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index f11ada8617..26bd8ee469 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -93,4 +93,36 @@ test_expect_success 'commit message from file should override template' ' commit_msg_is "standard input msg" ' +test_expect_success 'using alternate GIT_INDEX_FILE (1)' ' + + cp .git/index saved-index && + ( + echo some new content >file && + GIT_INDEX_FILE=.git/another_index && + export GIT_INDEX_FILE && + git add file && + git commit -m "commit using another index" && + git diff-index --exit-code HEAD && + git diff-files --exit-code + ) && + cmp .git/index saved-index >/dev/null + +' + +test_expect_success 'using alternate GIT_INDEX_FILE (2)' ' + + cp .git/index saved-index && + ( + rm -f .git/no-such-index && + GIT_INDEX_FILE=.git/no-such-index && + export GIT_INDEX_FILE && + git commit -m "commit using nonexistent index" && + test -z "$(git ls-files)" && + test -z "$(git ls-tree HEAD)" + + ) && + cmp .git/index saved-index >/dev/null + +' + test_done From e70f3202512a022898657047b59b46355be1631b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Sun, 11 Nov 2007 18:38:11 +0100 Subject: [PATCH 43/97] t7005-editor.sh: Don't invoke real vi when it is in GIT_EXEC_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The git wrapper executable always prepends the GIT_EXEC_PATH build variable to the current PATH, so prepending "." to the PATH is not enough to give precedence to the fake vi executable. The --exec-path option allows to prepend a directory to PATH even before GIT_EXEC_PATH (which is added anyway), so we can use that instead. Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- t/t7005-editor.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 28643b0da4..ed416e14e0 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -13,7 +13,6 @@ do done unset vi mv e-vi.sh vi -PATH=".:$PATH" unset EDITOR VISUAL GIT_EDITOR test_expect_success setup ' @@ -59,7 +58,7 @@ do ;; esac test_expect_success "Using $i" ' - git commit --amend && + git --exec-path=. commit --amend && git show -s --pretty=oneline | sed -e "s/^[0-9a-f]* //" >actual && diff actual expect @@ -81,7 +80,7 @@ do ;; esac test_expect_success "Using $i (override)" ' - git commit --amend && + git --exec-path=. commit --amend && git show -s --pretty=oneline | sed -e "s/^[0-9a-f]* //" >actual && diff actual expect From a3b0079c6a2e6336b061465623b8f2db308a6978 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 11 Nov 2007 02:29:30 -0500 Subject: [PATCH 44/97] git-fetch: Always fetch tags if the object they reference exists Previously git-fetch.sh used `git cat-file -t` to determine if an object referenced by a tag exists, and if so fetch that tag locally. This was subtly broken during the port to C based builtin-fetch as lookup_object() only works to locate an object if it was previously accessed by the transport. Not all transports will access all objects in this way, so tags were not always being fetched. The rsync transport never loads objects into the internal object table so automated tag following didn't work if rsync was used. Automated tag following also didn't work on the native transport if the new tag was behind the common point(s) negotiated between the two ends of the connection as the tag's referrant would not be loaded into the internal object table. Further the automated tag following was broken with the HTTP commit walker if the new tag's referrant was behind an existing ref, as the walker would stop before loading the tag's referrant into the object table. Switching to has_sha1_file() restores the original behavior from the shell script by checking if the object exists in the ODB, without relying on the state left behind by a transport. Signed-off-by: Shawn O. Pearce --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 5f5b59bfdb..1cb30c52c1 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -384,7 +384,7 @@ static struct ref *find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && - lookup_object(ref->old_sha1)) { + has_sha1_file(ref->old_sha1)) { path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); From b73a4397590df9582dd1c994cac30e55e26b0b1e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 11 Nov 2007 02:29:37 -0500 Subject: [PATCH 45/97] run-command: Support sending stderr to /dev/null Some callers may wish to redirect stderr to /dev/null in some contexts, such as if they are executing a command only to get the exit status and don't want users to see whatever output it may produce as a side-effect of computing that exit status. Signed-off-by: Shawn O. Pearce --- run-command.c | 6 ++++-- run-command.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/run-command.c b/run-command.c index d99a6c4ea7..476d00c218 100644 --- a/run-command.c +++ b/run-command.c @@ -41,7 +41,7 @@ int start_command(struct child_process *cmd) cmd->close_out = 1; } - need_err = cmd->err < 0; + need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { if (need_in) @@ -87,7 +87,9 @@ int start_command(struct child_process *cmd) close(cmd->out); } - if (need_err) { + if (cmd->no_stderr) + dup_devnull(2); + else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); } diff --git a/run-command.h b/run-command.h index 94e1e9d516..1fc781d766 100644 --- a/run-command.h +++ b/run-command.h @@ -23,6 +23,7 @@ struct child_process { unsigned close_out:1; unsigned no_stdin:1; unsigned no_stdout:1; + unsigned no_stderr:1; unsigned git_cmd:1; /* if this is to be git sub-command */ unsigned stdout_to_stderr:1; }; From 27350891de59608d4db689cf0851f7e49158a6e3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 11 Nov 2007 02:29:41 -0500 Subject: [PATCH 46/97] rev-list: Introduce --quiet to avoid /dev/null redirects Some uses of git-rev-list are to run it with --objects to see if a range of objects between two or more commits is fully connected or not. In such a case the caller doesn't care about the actual object names or hash hints so formatting this data only for it to be dumped to /dev/null by a redirect is a waste of CPU time. If all the caller needs is the exit status then --quiet can be used to bypass the commit and object formatting. Signed-off-by: Shawn O. Pearce --- Documentation/git-rev-list.txt | 9 +++++++++ builtin-rev-list.c | 26 ++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 485280423e..989fbf3562 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -20,6 +20,7 @@ SYNOPSIS [ \--not ] [ \--all ] [ \--stdin ] + [ \--quiet ] [ \--topo-order ] [ \--parents ] [ \--timestamp ] @@ -270,6 +271,14 @@ limiting may be applied. In addition to the '' listed on the command line, read them from the standard input. +--quiet:: + + Don't print anything to standard output. This form of + git-rev-list is primarly meant to allow the caller to + test the exit status to see if a range of objects is fully + connected (or not). It is faster than redirecting stdout + to /dev/null as the output does not have to be formatted. + --cherry-pick:: Omit any commit that introduces the same change as diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 697046723f..f5149e59b7 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -26,6 +26,7 @@ static const char rev_list_usage[] = " --remove-empty\n" " --all\n" " --stdin\n" +" --quiet\n" " ordering output:\n" " --topo-order\n" " --date-order\n" @@ -50,6 +51,7 @@ static int show_timestamp; static int hdr_termination; static const char *header_prefix; +static void finish_commit(struct commit *commit); static void show_commit(struct commit *commit) { if (show_timestamp) @@ -93,6 +95,11 @@ static void show_commit(struct commit *commit) strbuf_release(&buf); } maybe_flush_or_die(stdout, "stdout"); + finish_commit(commit); +} + +static void finish_commit(struct commit *commit) +{ if (commit->parents) { free_commit_list(commit->parents); commit->parents = NULL; @@ -101,6 +108,12 @@ static void show_commit(struct commit *commit) commit->buffer = NULL; } +static void finish_object(struct object_array_entry *p) +{ + if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) + die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); +} + static void show_object(struct object_array_entry *p) { /* An object with name "foo\n0000000..." can be used to @@ -108,9 +121,7 @@ static void show_object(struct object_array_entry *p) */ const char *ep = strchr(p->name, '\n'); - if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) - die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); - + finish_object(p); if (ep) { printf("%s %.*s\n", sha1_to_hex(p->item->sha1), (int) (ep - p->name), @@ -527,6 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int read_from_stdin = 0; int bisect_show_vars = 0; int bisect_find_all = 0; + int quiet = 0; git_config(git_default_config); init_revisions(&revs, prefix); @@ -565,6 +577,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) read_revisions_from_stdin(&revs); continue; } + if (!strcmp(arg, "--quiet")) { + quiet = 1; + continue; + } usage(rev_list_usage); } @@ -640,7 +656,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } } - traverse_commit_list(&revs, show_commit, show_object); + traverse_commit_list(&revs, + quiet ? finish_commit : show_commit, + quiet ? finish_object : show_object); return 0; } From 4191c35671f6392173221bea3994f8b305f4f3a8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 11 Nov 2007 02:29:47 -0500 Subject: [PATCH 47/97] git-fetch: avoid local fetching from alternate (again) Back in e3c6f240fd9c5bdeb33f2d47adc859f37935e2df Junio taught git-fetch to avoid copying objects when we are fetching from a repository that is already registered as an alternate object database. In such a case there is no reason to copy any objects as we can already obtain them through the alternate. However we need to ensure the objects are all reachable, so we run `git rev-list --objects $theirs --not --all` to verify this. If any object is missing or unreadable then we need to fetch/copy the objects from the remote. When a missing object is detected the git-rev-list process will exit with a non-zero exit status, making this condition quite easy to detect. Although git-fetch is currently a builtin (and so is rev-list) we cannot invoke the traverse_objects() API at this point in the transport code. The object walker within traverse_objects() calls die() as soon as it finds an object it cannot read. If that happens we want to resume the fetch process by calling do_fetch_pack(). To get around this we spawn git-rev-list into a background process to prevent a die() from killing the foreground fetch process, thus allowing the fetch process to resume into do_fetch_pack() if copying is necessary. We aren't interested in the output of rev-list (a list of SHA-1 object names that are reachable) or its errors (a "spurious" error about an object not being found as we need to copy it) so we redirect both stdout and stderr to /dev/null. We run this git-rev-list based check before any fetch as we may already have the necessary objects local from a prior fetch. If we don't then its very likely the first $theirs object listed on the command line won't exist locally and git-rev-list will die very quickly, allowing us to start the network transfer. This test even on remote URLs may save bandwidth if someone runs `git pull origin`, sees a merge conflict, resets out, then redoes the same pull just a short time later. If the remote hasn't changed between the two pulls and the local repository hasn't had git-gc run in it then there is probably no need to perform network transfer as all of the objects are local. Documentation for the new quickfetch function was suggested and written by Junio, based on his original comment in git-fetch.sh. Signed-off-by: Shawn O. Pearce --- builtin-fetch.c | 69 +++++++++++++++++++++++++++++++++++++++++-- t/t5502-quickfetch.sh | 33 +++++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 1cb30c52c1..c1930aaa0e 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -8,10 +8,12 @@ #include "path-list.h" #include "remote.h" #include "transport.h" +#include "run-command.h" static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack ] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth ] [-v | --verbose] [ ...]"; static int append, force, tags, no_tags, update_head_ok, verbose, quiet; +static const char *depth; static char *default_rla = NULL; static struct transport *transport; @@ -330,9 +332,72 @@ static void store_updated_refs(const char *url, struct ref *ref_map) fclose(fp); } +/* + * We would want to bypass the object transfer altogether if + * everything we are going to fetch already exists and connected + * locally. + * + * The refs we are going to fetch are in to_fetch (nr_heads in + * total). If running + * + * $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all + * + * does not error out, that means everything reachable from the + * refs we are going to fetch exists and is connected to some of + * our existing refs. + */ +static int quickfetch(struct ref *ref_map) +{ + struct child_process revlist; + struct ref *ref; + char **argv; + int i, err; + + /* + * If we are deepening a shallow clone we already have these + * objects reachable. Running rev-list here will return with + * a good (0) exit status and we'll bypass the fetch that we + * really need to perform. Claiming failure now will ensure + * we perform the network exchange to deepen our history. + */ + if (depth) + return -1; + + for (i = 0, ref = ref_map; ref; ref = ref->next) + i++; + if (!i) + return 0; + + argv = xmalloc(sizeof(*argv) * (i + 6)); + i = 0; + argv[i++] = xstrdup("rev-list"); + argv[i++] = xstrdup("--quiet"); + argv[i++] = xstrdup("--objects"); + for (ref = ref_map; ref; ref = ref->next) + argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1)); + argv[i++] = xstrdup("--not"); + argv[i++] = xstrdup("--all"); + argv[i++] = NULL; + + memset(&revlist, 0, sizeof(revlist)); + revlist.argv = (const char**)argv; + revlist.git_cmd = 1; + revlist.no_stdin = 1; + revlist.no_stdout = 1; + revlist.no_stderr = 1; + err = run_command(&revlist); + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); + return err; +} + static int fetch_refs(struct transport *transport, struct ref *ref_map) { - int ret = transport_fetch_refs(transport, ref_map); + int ret = quickfetch(ref_map); + if (ret) + ret = transport_fetch_refs(transport, ref_map); if (!ret) store_updated_refs(transport->url, ref_map); transport_unlock_pack(transport); @@ -468,7 +533,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) static const char **refs = NULL; int ref_nr = 0; int cmd_len = 0; - const char *depth = NULL, *upload_pack = NULL; + const char *upload_pack = NULL; int keep = 0; for (i = 1; i < argc; i++) { diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh index b4760f2dc0..16eadd6b68 100755 --- a/t/t5502-quickfetch.sh +++ b/t/t5502-quickfetch.sh @@ -86,4 +86,37 @@ test_expect_success 'quickfetch should not leave a corrupted repository' ' ' +test_expect_success 'quickfetch should not copy from alternate' ' + + ( + mkdir quickclone && + cd quickclone && + git init-db && + (cd ../.git/objects && pwd) >.git/objects/info/alternates && + git remote add origin .. && + git fetch -k -k + ) && + obj_cnt=$( ( + cd quickclone && + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + pck_cnt=$( ( + cd quickclone && + git count-objects -v | sed -n -e "/packs:/{ + s/packs:// + p + q + }" + ) ) && + origin_master=$( ( + cd quickclone && + git rev-parse origin/master + ) ) && + echo "loose objects: $obj_cnt, packfiles: $pck_cnt" && + test $obj_cnt -eq 0 && + test $pck_cnt -eq 0 && + test z$origin_master = z$(git rev-parse master) + +' + test_done From 25487bde2ab756a423489fc942b37c1550555b93 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 11 Nov 2007 18:44:16 -0800 Subject: [PATCH 48/97] t2200: test more cases of "add -u" Signed-off-by: Junio C Hamano --- t/t2200-add-update.sh | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index eb1ced3c37..24f892f793 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git add -u with path limiting +test_description='git add -u This test creates a working tree state with three files: @@ -9,7 +9,10 @@ This test creates a working tree state with three files: dir/other (untracked) and issues a git add -u with path limiting on "dir" to add -only the updates to dir/sub.' +only the updates to dir/sub. + +Also tested are "git add -u" without limiting, and "git add -u" +without contents changes.' . ./test-lib.sh @@ -85,4 +88,27 @@ test_expect_success 'replace a file with a symlink' ' ' +test_expect_success 'add everything changed' ' + + git add -u && + test -z "$(git diff-files)" + +' + +test_expect_success 'touch and then add -u' ' + + touch check && + git add -u && + test -z "$(git diff-files)" + +' + +test_expect_success 'touch and then add explicitly' ' + + touch check && + git add check && + test -z "$(git diff-files)" + +' + test_done From c8cfa3e4a5b1d1d4c870c82d2dbf162f570f0561 Mon Sep 17 00:00:00 2001 From: Benoit Sigoure Date: Sun, 11 Nov 2007 19:41:41 +0100 Subject: [PATCH 49/97] git-svn: prevent dcommitting if the index is dirty. dcommit uses rebase to sync the history with what has just been pushed to SVN. Trying to dcommit with a dirty index is troublesome for rebase, so now the user will get an error message if he attempts to dcommit with a dirty index. Signed-off-by: Benoit Sigoure Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 3 +++ t/t9106-git-svn-dcommit-clobber-series.sh | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/git-svn.perl b/git-svn.perl index ec25ea4231..4c779b6c6d 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -374,6 +374,9 @@ sub cmd_set_tree { sub cmd_dcommit { my $head = shift; + git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) } + 'Cannot dcommit with a dirty index. Commit your changes first' + . "or stash them with `git stash'.\n"; $head ||= 'HEAD'; my @refs; my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs); diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh index 7eff4cdc05..d59acc8d1a 100755 --- a/t/t9106-git-svn-dcommit-clobber-series.sh +++ b/t/t9106-git-svn-dcommit-clobber-series.sh @@ -53,4 +53,10 @@ test_expect_success 'change file but in unrelated area' " test x\"\`sed -n -e 61p < file\`\" = x6611 " +test_expect_failure 'attempt to dcommit with a dirty index' ' + echo foo >>file && + git add file && + git svn dcommit +' + test_done From 53e780c8f662bb937dc66a698c34fa2407cff31b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 11 Nov 2007 23:07:05 -0500 Subject: [PATCH 50/97] git-branch: remove mention of non-existent '-b' option This looks like a cut and paste error from the git-checkout explanation of --no-track. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-branch.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index b7285bcdbc..37cb8b83b2 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -105,7 +105,7 @@ OPTIONS '--track' were given. --no-track:: - When -b is given and a branch is created off a remote branch, + When a branch is created off a remote branch, set up configuration so that git-pull will not retrieve data from the remote branch, ignoring the branch.autosetupmerge configuration variable. From a74fa1106dd29390077025e7f176cf6b53eada62 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 12 Nov 2007 05:37:25 +0100 Subject: [PATCH 51/97] for-each-ref: fix off by one read. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-for-each-ref.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 29f70aabc3..0327f40306 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -297,7 +297,7 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un if (!eol) return ""; eol++; - if (eol[1] == '\n') + if (*eol == '\n') return ""; /* end of header */ buf = eol; } From a4e57e75c95c66c32da6b106313bc847110794ba Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Mon, 12 Nov 2007 01:32:51 +0100 Subject: [PATCH 52/97] Documentation: Fix references to deprecated commands ... by changing git-tar-tree reference to git-archive and removing seemingly unrelevant footnote about git-ssh-{fetch,upload}. Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- Documentation/core-tutorial.txt | 5 ----- Documentation/git-get-tar-commit-id.txt | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index ebd2492bc4..401d1deca0 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -1090,11 +1090,6 @@ server like git Native transport does. Any stock HTTP server that does not even support directory index would suffice. But you must prepare your repository with `git-update-server-info` to help dumb transport downloaders. -+ -There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload` -programs, which are 'commit walkers'; they outlived their -usefulness when git Native and SSH transports were introduced, -and are not used by `git pull` or `git push` scripts. Once you fetch from the remote repository, you `merge` that with your current branch. diff --git a/Documentation/git-get-tar-commit-id.txt b/Documentation/git-get-tar-commit-id.txt index 9b5f86fc30..76316bbc9e 100644 --- a/Documentation/git-get-tar-commit-id.txt +++ b/Documentation/git-get-tar-commit-id.txt @@ -14,12 +14,12 @@ SYNOPSIS DESCRIPTION ----------- Acts as a filter, extracting the commit ID stored in archives created by -git-tar-tree. It reads only the first 1024 bytes of input, thus its +gitlink:git-archive[1]. It reads only the first 1024 bytes of input, thus its runtime is not influenced by the size of very much. If no commit ID is found, git-get-tar-commit-id quietly exists with a return code of 1. This can happen if had not been created -using git-tar-tree or if the first parameter of git-tar-tree had been +using git-archive or if the parameter of git-archive had been a tree ID instead of a commit ID or tag. From cfbe7ab333d68790eb37341e30f040f99cef6af7 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 11 Nov 2007 23:37:42 -0800 Subject: [PATCH 53/97] git-svn: support for funky branch and project names over HTTP(S) SVN requires that paths be URI-escaped for HTTP(S) repositories. file:// and svn:// repositories do not need these rules. Additionally, accessing individual paths inside repositories (check_path() and get_log() do NOT require escapes to function and in fact it breaks things). Noticed-by: Michael J. Cohen Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 24 ++++++++++++++-- t/t9118-git-svn-funky-branch-names.sh | 40 +++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100755 t/t9118-git-svn-funky-branch-names.sh diff --git a/git-svn.perl b/git-svn.perl index 1e244975ab..e3e00fdcc5 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3223,6 +3223,25 @@ sub _auth_providers () { ] } +sub escape_uri_only { + my ($uri) = @_; + my @tmp; + foreach (split m{/}, $uri) { + s/([^\w.-])/sprintf("%%%02X",ord($1))/eg; + push @tmp, $_; + } + join('/', @tmp); +} + +sub escape_url { + my ($url) = @_; + if ($url =~ m#^(https?)://([^/]+)(.*)$#) { + my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3)); + $url = "$scheme://$domain$uri"; + } + $url; +} + sub new { my ($class, $url) = @_; $url =~ s!/+$!!; @@ -3255,10 +3274,11 @@ sub new { $Git::SVN::Prompt::_no_auth_cache = 1; } } # no warnings 'once' - my $self = SVN::Ra->new(url => $url, auth => $baton, + my $self = SVN::Ra->new(url => escape_url($url), auth => $baton, config => $config, pool => SVN::Pool->new, auth_provider_callbacks => $callbacks); + $self->{url} = $url; $self->{svn_path} = $url; $self->{repos_root} = $self->get_repos_root; $self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##; @@ -3384,7 +3404,7 @@ sub gs_do_switch { my $full_url = $self->{url}; my $old_url = $full_url; - $full_url .= "/$path" if length $path; + $full_url .= '/' . escape_uri_only($path) if length $path; my ($ra, $reparented); if ($old_url ne $full_url) { if ($old_url !~ m#^svn(\+ssh)?://#) { diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh new file mode 100755 index 0000000000..640bb066f3 --- /dev/null +++ b/t/t9118-git-svn-funky-branch-names.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +test_description='git-svn funky branch names' +. ./lib-git-svn.sh + +test_expect_success 'setup svnrepo' " + mkdir project project/trunk project/branches project/tags && + echo foo > project/trunk/foo && + svn import -m '$test_description' project \"$svnrepo/pr ject\" && + rm -rf project && + svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \ + \"$svnrepo/pr ject/branches/fun plugin\" && + svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \ + \"$svnrepo/pr ject/branches/more fun plugin!\" && + start_httpd + " + +test_expect_success 'test clone with funky branch names' " + git svn clone -s \"$svnrepo/pr ject\" project && + cd project && + git rev-parse 'refs/remotes/fun%20plugin' && + git rev-parse 'refs/remotes/more%20fun%20plugin!' && + cd .. + " + +test_expect_success 'test dcommit to funky branch' " + cd project && + git reset --hard 'refs/remotes/more%20fun%20plugin!' && + echo hello >> foo && + git commit -m 'hello' -- foo && + git svn dcommit && + cd .. + " + +stop_httpd + +test_done From 3f735b66543a2221c218fc522272d62a333ebfec Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 12 Nov 2007 13:11:46 +0000 Subject: [PATCH 54/97] rebase: fix "rebase --continue" breakage The --skip case was handled properly when rebasing without --merge, but the --continue case was not. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase.sh | 6 +++++- t/t3403-rebase-skip.sh | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/git-rebase.sh b/git-rebase.sh index c02be31f33..c059749bbd 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -170,7 +170,11 @@ do finish_rb_merge exit fi - git am --resolved --3way --resolvemsg="$RESOLVEMSG" + head_name=$(cat .dotest/head-name) && + onto=$(cat .dotest/onto) && + orig_head=$(cat .dotest/orig-head) && + git am --resolved --3way --resolvemsg="$RESOLVEMSG" && + move_to_original_branch exit ;; --skip) diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh index eab053c3e0..2ee5a00ea7 100755 --- a/t/t3403-rebase-skip.sh +++ b/t/t3403-rebase-skip.sh @@ -39,6 +39,19 @@ test_expect_success 'rebase --skip with am -3' ' git reset --hard HEAD && git rebase --skip ' + +test_expect_success 'rebase moves back to skip-reference' ' + test refs/heads/skip-reference = $(git symbolic-ref HEAD) && + git branch post-rebase && + git reset --hard pre-rebase && + ! git rebase master && + echo "hello" > hello && + git add hello && + git rebase --continue && + test refs/heads/skip-reference = $(git symbolic-ref HEAD) && + git reset --hard post-rebase +' + test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge' test_expect_failure 'rebase with --merge' 'git rebase --merge master' @@ -51,6 +64,10 @@ test_expect_success 'rebase --skip with --merge' ' test_expect_success 'merge and reference trees equal' \ 'test -z "`git diff-tree skip-merge skip-reference`"' +test_expect_success 'moved back to branch correctly' ' + test refs/heads/skip-merge = $(git symbolic-ref HEAD) +' + test_debug 'gitk --all & sleep 1' test_done From f192c5d0fb37a1a89d88e92bb7b21418b57c2129 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 12 Nov 2007 09:27:35 +0100 Subject: [PATCH 55/97] git-clean: Fix error message if clean.requireForce is not set. It was distracting to see this error message: clean.requireForce set and -n or -f not given; refusing to clean even though clean.requireForce was not set at all. This patch distinguishes the cases and gives a different message depending on whether the configuration variable is not set or set to true. While we are here, we also divert the error messages to stderr. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- git-clean.sh | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/git-clean.sh b/git-clean.sh index f4965b8391..521fabc20f 100755 --- a/git-clean.sh +++ b/git-clean.sh @@ -25,10 +25,7 @@ rmrf="rm -rf --" rm_refuse="echo Not removing" echo1="echo" -# requireForce used to default to false but now it defaults to true. -# IOW, lack of explicit "clean.requireForce = false" is taken as -# "clean.requireForce = true". -disabled=$(git config --bool clean.requireForce || echo true) +disabled=$(git config --bool clean.requireForce) while test $# != 0 do @@ -37,10 +34,10 @@ do cleandir=1 ;; -f) - disabled= + disabled=false ;; -n) - disabled= + disabled=false rmf="echo Would remove" rmrf="echo Would remove" rm_refuse="echo Would not remove" @@ -68,10 +65,17 @@ do shift done -if [ "$disabled" = true ]; then - echo "clean.requireForce set and -n or -f not given; refusing to clean" - exit 1 -fi +# requireForce used to default to false but now it defaults to true. +# IOW, lack of explicit "clean.requireForce = false" is taken as +# "clean.requireForce = true". +case "$disabled" in +"") + die "clean.requireForce not set and -n or -f not given; refusing to clean" + ;; +"true") + die "clean.requireForce set and -n or -f not given; refusing to clean" + ;; +esac case "$ignored,$ignoredonly" in 1,1) usage;; From 726c8ef5a5a129d8157d0043f60fe7195d2cdb77 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 12 Nov 2007 11:09:05 +0100 Subject: [PATCH 56/97] Fix preprocessor logic that determines the availablity of strchrnul(). Apart from the error in the condition (&& should actually be ||), the construct #if !defined(A) || !A leads to a syntax error in the C preprocessor if A is indeed not defined. Tested-by: David Symonds Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- git-compat-util.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/git-compat-util.h b/git-compat-util.h index 92d79673f8..ede9408bbd 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -183,7 +183,13 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif -#if !defined(__GLIBC_PREREQ) && !__GLIBC_PREREQ(2, 1) +#ifdef __GLIBC_PREREQ +#if __GLIBC_PREREQ(2, 1) +#define HAVE_STRCHRNUL +#endif +#endif + +#ifndef HAVE_STRCHRNUL #define strchrnul gitstrchrnul static inline char *gitstrchrnul(const char *s, int c) { From 9d87442f03c9e3fea7e24e2821fc0342f8efe1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Mon, 12 Nov 2007 16:15:39 +0100 Subject: [PATCH 57/97] git-commit: Add tests for invalid usage of -a/--interactive with paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-commit was/is broken in that it accepts paths together with -a or --interactive, which it shouldn't. There tests check those usage errors. Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- t/t7501-commit.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index b151b51a34..7f25689bb7 100644 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -33,6 +33,16 @@ test_expect_failure \ "invalid options 2" \ "git-commit -C HEAD -m illegal" +test_expect_failure \ + "using paths with -a" \ + "echo King of the bongo >file && + git-commit -m foo -a file" + +test_expect_failure \ + "using paths with --interactive" \ + "echo bong-o-bong >file && + echo 7 | git-commit -m foo --interactive file" + test_expect_failure \ "using invalid commit with -C" \ "git-commit -C bogus" From 3e0ba4ccdc1f4058a31eacb2f6ccca1ffa094e5a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 15:44:48 +0100 Subject: [PATCH 58/97] Clean up cruft from the MINGW32 section of git-compat-util.h Quite a lot of stuff has accumulated or is now obsolete. The stubs of POSIX functions that are not implemented or that always fail are now implemented as inline functions so that they exist in only one place. --- compat/mingw.c | 43 ---------------------------------------- git-compat-util.h | 50 +++++++++++++++++++++++------------------------ 2 files changed, 24 insertions(+), 69 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index eaf6267025..63c632adaf 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1,28 +1,8 @@ #include -#include -#include #include "../git-compat-util.h" unsigned int _CRT_fmode = _O_BINARY; -int readlink(const char *path, char *buf, size_t bufsiz) -{ - errno = ENOSYS; - return -1; -} - -int symlink(const char *oldpath, const char *newpath) -{ - errno = ENOSYS; - return -1; -} - -int fchmod(int fildes, mode_t mode) -{ - errno = EBADF; - return -1; -} - static inline time_t filetime_to_time_t(const FILETIME *ft) { long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; @@ -175,29 +155,6 @@ int mingw_fstat(int fd, struct mingw_stat *buf) return -1; } -/* missing: link, mkstemp, fchmod, getuid (?), gettimeofday */ -int socketpair(int d, int type, int protocol, int sv[2]) -{ - return -1; -} -int syslog(int type, char *bufp, ...) -{ - return -1; -} -unsigned int alarm(unsigned int seconds) -{ - return 0; -} -#include -int fork() -{ - return -1; -} - -int kill(pid_t pid, int sig) -{ - return -1; -} unsigned int sleep (unsigned int __seconds) { Sleep(__seconds*1000); diff --git a/git-compat-util.h b/git-compat-util.h index bdda57221a..bfdee76749 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -407,46 +407,46 @@ static inline int strtol_i(char const *s, int base, int *result) #ifdef __MINGW32__ -#ifndef S_ISLNK +#include + #define S_IFLNK 0120000 /* Symbolic link */ #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) #define S_ISSOCK(x) 0 -#endif - -#ifndef S_ISGRP -#define S_ISGRP(x) 0 #define S_IRGRP 0 #define S_IWGRP 0 #define S_IXGRP 0 #define S_ISGID 0 #define S_IROTH 0 #define S_IXOTH 0 -#endif -int readlink(const char *path, char *buf, size_t bufsiz); -int symlink(const char *oldpath, const char *newpath); -#define link symlink -int fchmod(int fildes, mode_t mode); -int lstat(const char *file_name, struct stat *buf); +static inline int readlink(const char *path, char *buf, size_t bufsiz) +{ errno = ENOSYS; return -1; } +static inline int symlink(const char *oldpath, const char *newpath) +{ errno = ENOSYS; return -1; } +static inline int link(const char *oldpath, const char *newpath) +{ errno = ENOSYS; return -1; } +static inline int fchmod(int fildes, mode_t mode) +{ errno = ENOSYS; return -1; } +static inline int fork(void) +{ errno = ENOSYS; return -1; } +static inline int kill(pid_t pid, int sig) +{ errno = ENOSYS; return -1; } +static inline unsigned int alarm(unsigned int seconds) +{ return 0; } -int socketpair(int d, int type, int protocol, int sv[2]); -#define AF_UNIX 0 -#define SOCK_STREAM 0 -int syslog(int type, char *bufp, ...); -#define LOG_ERR 1 -#define LOG_INFO 2 -#define LOG_DAEMON 4 -unsigned int alarm(unsigned int seconds); -#include void mingw_execve(const char *cmd, const char **argv, const char **env); #define execve mingw_execve extern void mingw_execvp(const char *cmd, const char **argv); #define execvp mingw_execvp -int fork(); typedef int pid_t; -#define waitpid(pid, status, options) \ - ((options == 0) ? _cwait((status), (pid), 0) \ - : (errno = EINVAL, -1)) +static inline int waitpid(pid_t pid, unsigned *status, unsigned options) +{ + if (options == 0) + return _cwait(status, pid, 0); + else + return errno = EINVAL, -1; +} + #define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */ #define WEXITSTATUS(x) ((x) & 0xff) #define WIFSIGNALED(x) ((unsigned)(x) > 259) @@ -455,9 +455,7 @@ typedef int pid_t; #define SIGKILL 0 #define SIGCHLD 0 #define SIGPIPE 0 -#define ECONNABORTED 0 -int kill(pid_t pid, int sig); unsigned int sleep (unsigned int __seconds); const char *inet_ntop(int af, const void *src, char *dst, size_t cnt); From cf1fd675d2fb05c46ea472bbb3266985b63fbf01 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 16:57:47 +0100 Subject: [PATCH 59/97] Implement a stub for fcntl(). This stub does nothing for F_GETFD and F_SETFD, and fails for all other commands. Signed-off-by: Johannes Sixt --- git-compat-util.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/git-compat-util.h b/git-compat-util.h index bfdee76749..85b5d8806a 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -563,6 +563,13 @@ int sigaction(int sig, struct sigaction *in, struct sigaction *out); sig_handler_t mingw_signal(int sig, sig_handler_t handler); #define signal mingw_signal +#define F_GETFD 1 +#define F_SETFD 2 +#define FD_CLOEXEC 0x1 +static inline int mingw_fcntl(int fd, int cmd, long arg) +{ return cmd == F_GETFD || cmd == F_SETFD ? 0 : (errno = EINVAL, -1); } +#define fcntl mingw_fcntl + #endif /* __MINGW32__ */ #endif From 0102b1b02ea59cafbfa72fb16f4359387154094a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 17:02:09 +0100 Subject: [PATCH 60/97] Revert "Windows does not have the close-on-exec flag." This reverts commit e7a70c5ddfa0f925760c131c2c60c25b814df222. We now have a fcntl stub that makes the bracketed piece of code a no-op. --- sha1_file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 0e527f7c6f..1df56aa417 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -653,7 +653,6 @@ static int open_packed_git_1(struct packed_git *p) } else if (p->pack_size != st.st_size) return error("packfile %s size changed", p->pack_name); -#ifndef __MINGW32__ /* We leave these file descriptors open with sliding mmap; * there is no point keeping them open across exec(), though. */ @@ -663,7 +662,6 @@ static int open_packed_git_1(struct packed_git *p) fd_flag |= FD_CLOEXEC; if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1) return error("cannot set FD_CLOEXEC"); -#endif /* Verify we recognize this pack file format. */ if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr)) From 155e6d080b57b968f853b0076ea3de0f24f4975d Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 17:44:08 +0100 Subject: [PATCH 61/97] Fix setitimer implementation to register only one cleanup with atexit. Signed-off-by: Johannes Sixt --- compat/mingw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compat/mingw.c b/compat/mingw.c index 63c632adaf..9b503a0eec 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -679,6 +679,7 @@ static inline int is_timeval_eq(const struct timeval *i1, const struct timeval * int setitimer(int type, struct itimerval *in, struct itimerval *out) { static const struct timeval zero; + static int atexit_done; if (out != NULL) return errno = EINVAL, @@ -697,7 +698,10 @@ int setitimer(int type, struct itimerval *in, struct itimerval *out) timer_interval = in->it_interval.tv_sec * 1000 + in->it_interval.tv_usec / 1000; one_shot = is_timeval_eq(&in->it_value, &zero); - atexit(stop_timer_thread); + if (!atexit_done) { + atexit(stop_timer_thread); + atexit_done = 1; + } return start_timer_thread(); } From aac5bf0b48c5f23cf482ca7958fa6815fe6502ed Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Nov 2007 13:05:50 -0800 Subject: [PATCH 62/97] t/t3404: fix test for a bogus todo file. The test wants to see if there are still remaining tasks, but checked a wrong file. Signed-off-by: Junio C Hamano --- t/t3404-rebase-interactive.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 6c92d61192..984146b5c2 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -149,7 +149,7 @@ test_expect_success 'stop on conflicting pick' ' diff -u expect .git/.dotest-merge/patch && diff -u expect2 file1 && test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) && - test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l) + test 0 = $(grep -v "^#" < .git/.dotest-merge/git-rebase-todo | wc -l) ' test_expect_success 'abort' ' From 245de36f035f9d549df2b1509787c25648fd611f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Nov 2007 12:28:53 -0800 Subject: [PATCH 63/97] revert/cherry-pick: allow starting from dirty work tree. There is no reason to forbid a dirty work tree when reverting or cherry-picking a change, as long as the index is clean. The scripted version used to allow it: case "$no_commit" in t) # We do not intend to commit immediately. We just want to # merge the differences in. head=$(git-write-tree) || die "Your index file is unmerged." ;; *) head=$(git-rev-parse --verify HEAD) || die "You do not have a valid HEAD" files=$(git-diff-index --cached --name-only $head) || exit if [ "$files" ]; then die "Dirty index: cannot $me (dirty: $files)" fi ;; esac but C rewrite tightened the check, probably by mistake. Signed-off-by: Junio C Hamano --- builtin-revert.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-revert.c b/builtin-revert.c index eafafbc333..94e77771d2 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -264,7 +264,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); wt_status_prepare(&s); - if (s.commitable || s.workdir_dirty) + if (s.commitable) die ("Dirty index: cannot %s", me); discard_cache(); } From 71aa2b8f0ee7cb2e60871f327996af21af88a668 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Nov 2007 13:45:11 -0800 Subject: [PATCH 64/97] Resurrect git-revert.sh example and add comment to builtin-revert.c I had to scratch my head for quite some time figuring out why we cannot optimize out write_tree() we do when --no-commit option is given, whose purpose seem to be only to check if the index is unmerged, with a simple loop over the active_cache[]. So add a comment to describe why the write_tree() is there, and resurrect the last scripted version as a reference material in contrib/example directory with others. Signed-off-by: Junio C Hamano --- builtin-revert.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin-revert.c b/builtin-revert.c index 62ab1fa1f4..afc28845f2 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -246,7 +246,9 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (no_commit) { /* * We do not intend to commit immediately. We just want to - * merge the differences in. + * merge the differences in, so let's compute the tree + * that represents the "current" state for merge-recursive + * to work on. */ if (write_tree(head, 0, NULL)) die ("Your index file is unmerged."); From 436e7a74c638bc9de2e585ce3eb0fd4bd6e06115 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 13 Nov 2007 23:48:42 -0500 Subject: [PATCH 65/97] Don't allow fast-import tree delta chains to exceed maximum depth Brian Downing noticed fast-import can produce tree depths of up to 6,035 objects and even deeper. Long delta chains can create very small packfiles but cause problems during repacking as git needs to unpack each tree to count the reachable blobs. What's happening here is the active branch cache isn't big enough. We're swapping out the branch and thus recycling the tree information (struct tree_content) back into the free pool. When we later reload the tree we set the delta_depth to 0 but we kept the tree we just reloaded as a delta base. So if the tree we reloaded was already at the maximum depth we wouldn't know it and make the new tree a delta. Multiply the number of times the branch cache has to swap out the tree times max_depth (10) and you get the maximum delta depth of a tree created by fast-import. In Brian's case above the active branch cache had to swap the branch out 603/604 times during this import to produce a tree with a delta depth of 6035. Acked-by: Brian Downing Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- fast-import.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fast-import.c b/fast-import.c index c07e3d8ef0..5e83296bf4 100644 --- a/fast-import.c +++ b/fast-import.c @@ -154,13 +154,16 @@ Format of STDIN stream: #define PACK_ID_BITS 16 #define MAX_PACK_ID ((1<depth++; + e->depth = last->depth + 1; hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); @@ -1117,8 +1120,7 @@ static int store_object( write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos); pack_size += sizeof(hdr) - pos; } else { - if (last) - last->depth = 0; + e->depth = 0; hdrlen = encode_header(type, datlen, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); pack_size += hdrlen; @@ -1134,6 +1136,7 @@ static int store_object( free(last->data); last->data = dat; last->offset = e->offset; + last->depth = e->depth; last->len = datlen; } return 0; @@ -1181,7 +1184,7 @@ static void load_tree(struct tree_entry *root) if (myoe && myoe->pack_id != MAX_PACK_ID) { if (myoe->type != OBJ_TREE) die("Not a tree: %s", sha1_to_hex(sha1)); - t->delta_depth = 0; + t->delta_depth = myoe->depth; buf = gfi_unpack_entry(myoe, &size); } else { enum object_type type; @@ -2347,8 +2350,11 @@ int main(int argc, const char **argv) } else if (!prefixcmp(a, "--max-pack-size=")) max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024; - else if (!prefixcmp(a, "--depth=")) + else if (!prefixcmp(a, "--depth=")) { max_depth = strtoul(a + 8, NULL, 0); + if (max_depth > MAX_DEPTH) + die("--depth cannot exceed %u", MAX_DEPTH); + } else if (!prefixcmp(a, "--active-branches=")) max_active_branches = strtoul(a + 18, NULL, 0); else if (!prefixcmp(a, "--import-marks=")) From c05ef93879696638406cad5be54be577a2666d04 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Wed, 14 Nov 2007 08:51:41 +0100 Subject: [PATCH 66/97] Grammar fixes for gitattributes documentation Tweak the "filter" section of the gitattributes documentation to add some missing articles and improve some word choices without changing the semantics of the section. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index dd51aa11ea..cf4ee2ebe5 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -148,22 +148,23 @@ with `$Id$` upon check-in. `filter` ^^^^^^^^ -A `filter` attribute can be set to a string value. This names +A `filter` attribute can be set to a string value that names a filter driver specified in the configuration. -A filter driver consists of `clean` command and `smudge` +A filter driver consists of a `clean` command and a `smudge` command, either of which can be left unspecified. Upon -checkout, when `smudge` command is specified, the command is fed -the blob object from its standard input, and its standard output -is used to update the worktree file. Similarly, `clean` command -is used to convert the contents of worktree file upon checkin. +checkout, when the `smudge` command is specified, the command is +fed the blob object from its standard input, and its standard +output is used to update the worktree file. Similarly, the +`clean` command is used to convert the contents of worktree file +upon checkin. -Missing filter driver definition in the config is not an error +A missing filter driver definition in the config is not an error but makes the filter a no-op passthru. The content filtering is done to massage the content into a shape that is more convenient for the platform, filesystem, and -the user to use. The keyword here is "more convenient" and not +the user to use. The key phrase here is "more convenient" and not "turning something unusable into usable". In other words, the intent is that if someone unsets the filter driver definition, or does not have the appropriate filter program, the project From 97e9a2216f33d1b6cff312f9535b6cf0d0ba4f16 Mon Sep 17 00:00:00 2001 From: Jing Xue Date: Sun, 11 Nov 2007 23:43:00 -0500 Subject: [PATCH 67/97] replace reference to git-rm with git-reset in git-commit doc The message in git-commit suggesting to use 'git rm --cached' to unstage is just plain wrong. It really should mention 'git reset'. Suggested by Jan Hudec. Signed-off-by: Jing Xue Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 1 + Documentation/git-commit.txt | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 2fe7355555..fd82fc19b5 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -224,6 +224,7 @@ See Also -------- gitlink:git-status[1] gitlink:git-rm[1] +gitlink:git-reset[1] gitlink:git-mv[1] gitlink:git-commit[1] gitlink:git-update-index[1] diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index e54fb12103..d4bfd49ce1 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -154,10 +154,13 @@ EXAMPLES -------- When recording your own work, the contents of modified files in your working tree are temporarily stored to a staging area -called the "index" with gitlink:git-add[1]. Removal -of a file is staged with gitlink:git-rm[1]. After building the -state to be committed incrementally with these commands, `git -commit` (without any pathname parameter) is used to record what +called the "index" with gitlink:git-add[1]. A file can be +reverted back, only in the index but not in the working tree, +to that of the last commit with `git-reset HEAD -- `, +which effectively reverts `git-add` and prevents the changes to +this file from participating in the next commit. After building +the state to be committed incrementally with these commands, +`git commit` (without any pathname parameter) is used to record what has been staged so far. This is the most basic form of the command. An example: From 065c5ac1688ca5cde826dc87d9791c2eedec7026 Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Sat, 10 Nov 2007 16:17:33 +0300 Subject: [PATCH 68/97] core-tutorial.txt: Fix argument mistake in an example. One of examples has wrong output given the arguments provided. Fix arguments to match the output. Fix a minor syntax mistake in another place. Signed-off-by: Sergei Organov Signed-off-by: Junio C Hamano --- Documentation/core-tutorial.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 67064dd31a..c126038c1f 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -1149,7 +1149,7 @@ back to the earlier repository with "hello" and "example" file, and bring ourselves back to the pre-merge state: ------------ -$ git show-branch --more=3 master mybranch +$ git show-branch --more=2 master mybranch ! [master] Merge work in mybranch * [mybranch] Merge work in mybranch -- @@ -1212,7 +1212,7 @@ $ git-read-tree -m -u $mb HEAD mybranch This is the same `git-read-tree` command we have already seen, but it takes three trees, unlike previous examples. This reads the contents of each tree into different 'stage' in the index -file (the first tree goes to stage 1, the second stage 2, +file (the first tree goes to stage 1, the second to stage 2, etc.). After reading three trees into three stages, the paths that are the same in all three stages are 'collapsed' into stage 0. Also paths that are the same in two of three stages are From cb5c49b9afb5551659c6819aefaa8e4de26a4366 Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Tue, 13 Nov 2007 21:17:47 +0300 Subject: [PATCH 69/97] git-remote.txt: fix typo Signed-off-by: Sergei Organov Signed-off-by: Junio C Hamano --- Documentation/git-remote.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 61a6022ce8..c386dd0168 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -69,7 +69,7 @@ caution. Fetch updates for a named set of remotes in the repository as defined by remotes.. If a named group is not specified on the command line, the configuration parameter remotes.default will get used; if -remotes.default is not defined, all remotes which do not the +remotes.default is not defined, all remotes which do not have the configuration parameter remote..skipDefaultUpdate set to true will be updated. (See gitlink:git-config[1]). From f141bd804d2cba4500769934f9d9e7f0f7bcf282 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 13 Nov 2007 03:22:44 -0500 Subject: [PATCH 70/97] Handle broken vsnprintf implementations in strbuf Solaris 9's vsnprintf implementation returns -1 if we pass it a buffer of length 0. The only way to get it to give us the actual length necessary for the formatted string is to grow the buffer out to have at least 1 byte available in the strbuf and then ask it to compute the length. If the available space is 0 I'm growing it out by 64 to ensure we will get an accurate length estimate from all implementations. Some callers may need to grow the strbuf again but 64 should be a reasonable enough initial growth. We also no longer silently fail to append to the string when we are faced with a broken vsnprintf implementation. On Solaris 9 this silent failure caused me to no longer be able to execute "git clone" as we tried to exec the empty string rather than "git-clone". Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- strbuf.c | 7 ++++--- trace.c | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/strbuf.c b/strbuf.c index f4201e160d..cbada946c4 100644 --- a/strbuf.c +++ b/strbuf.c @@ -111,12 +111,13 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) int len; va_list ap; + if (!strbuf_avail(sb)) + strbuf_grow(sb, 64); va_start(ap, fmt); len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); - if (len < 0) { - len = 0; - } + if (len < 0) + die("your vsnprintf is broken"); if (len > strbuf_avail(sb)) { strbuf_grow(sb, len); va_start(ap, fmt); diff --git a/trace.c b/trace.c index 69fa05e644..0d89dbe779 100644 --- a/trace.c +++ b/trace.c @@ -72,7 +72,7 @@ void trace_printf(const char *fmt, ...) if (!fd) return; - strbuf_init(&buf, 0); + strbuf_init(&buf, 64); va_start(ap, fmt); len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); @@ -103,7 +103,7 @@ void trace_argv_printf(const char **argv, int count, const char *fmt, ...) if (!fd) return; - strbuf_init(&buf, 0); + strbuf_init(&buf, 64); va_start(ap, fmt); len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); From 7f55cf451c9e7c35cad2efcd0bb7bbd7c7ae29ac Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Wed, 14 Nov 2007 10:38:46 +0100 Subject: [PATCH 71/97] Documentation: Fix man page breakage with DocBook XSL v1.72 From version 1.72 it will replace all dots in roff requests with U+2302 ("house" character), and add escaping in output for all instances of dot that are not in roff requests. This caused the ".ft" hack forcing monospace font in listingblocks to end up as "\&.ft" and being visible in the resulting man page. The fix adds a DOCBOOK_XSL_172 build variable that will disable the hack. To allow this variable to be defined in config.mak it also moves build variable handling below the inclusion of config.mak. Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- Documentation/Makefile | 10 +++++++--- Documentation/asciidoc.conf | 3 +++ Makefile | 2 ++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index 39ec0ede02..d88664177d 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -37,9 +37,6 @@ man7dir=$(mandir)/man7 ASCIIDOC=asciidoc ASCIIDOC_EXTRA = -ifdef ASCIIDOC8 -ASCIIDOC_EXTRA += -a asciidoc7compatible -endif INSTALL?=install RM ?= rm -f DOC_REF = origin/man @@ -52,6 +49,13 @@ DOCBOOK2X_TEXI=docbook2x-texi -include ../config.mak.autogen -include ../config.mak +ifdef ASCIIDOC8 +ASCIIDOC_EXTRA += -a asciidoc7compatible +endif +ifdef DOCBOOK_XSL_172 +ASCIIDOC_EXTRA += -a docbook-xsl-172 +endif + # # Please note that there is a minor bug in asciidoc. # The version after 6.0.3 _will_ include the patch found here: diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index af5b1558a6..99d8874aa0 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -23,7 +23,9 @@ ifdef::backend-docbook[] endif::backend-docbook[] ifdef::backend-docbook[] +ifndef::docbook-xsl-172[] # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. +# v1.72 breaks with this because it replaces dots not in roff requests. [listingblock] {title} @@ -36,6 +38,7 @@ ifdef::doctype-manpage[] endif::doctype-manpage[] {title#} +endif::docbook-xsl-172[] endif::backend-docbook[] ifdef::doctype-manpage[] diff --git a/Makefile b/Makefile index 2331e45adf..41c90ad1d4 100644 --- a/Makefile +++ b/Makefile @@ -109,6 +109,8 @@ all:: # # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 # +# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. +# # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's # MakeMaker (e.g. using ActiveState under Cygwin). # From b57321f57b324320bdcf9f453ec4788da166f8f4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Nov 2007 01:54:43 -0800 Subject: [PATCH 72/97] git-clean: honor core.excludesfile git-clean did not honor core.excludesfile configuration variable, although some other commands such as git-add and git-status did. Fix this inconsistency. Original report and patch from Shun'ichi Fuji. Rewritten by me and bugs and tests are mine. Signed-off-by: Junio C Hamano --- git-clean.sh | 9 ++++++++- t/t7300-clean.sh | 11 +++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/git-clean.sh b/git-clean.sh index 4491738186..931d1aa4e4 100755 --- a/git-clean.sh +++ b/git-clean.sh @@ -75,15 +75,22 @@ esac if [ -z "$ignored" ]; then excl="--exclude-per-directory=.gitignore" + excl_info= excludes_file= if [ -f "$GIT_DIR/info/exclude" ]; then excl_info="--exclude-from=$GIT_DIR/info/exclude" fi + if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl" + then + excludes_file="--exclude-from=$cfg_excl" + fi if [ "$ignoredonly" ]; then excl="$excl --ignored" fi fi -git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" | +git ls-files --others --directory \ + $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \ + -- "$@" | while read -r file; do if [ -d "$file" -a ! -L "$file" ]; then if [ -z "$cleandir" ]; then diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index eb0847afe9..0ed4ae2827 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -177,4 +177,15 @@ test_expect_success 'clean.requireForce and -f' ' ' +test_expect_success 'core.excludesfile' ' + + echo excludes >excludes && + echo included >included && + git config core.excludesfile excludes && + output=$(git clean -n excludes included 2>&1) && + expr "$output" : ".*included" >/dev/null && + ! expr "$output" : ".*excludes" >/dev/null + +' + test_done From 2f29dac5a902a6be58ffddc598c4e17f79a0d841 Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Tue, 13 Nov 2007 21:19:39 +0300 Subject: [PATCH 73/97] user-manual.txt: fix a few mistakes Signed-off-by: Sergei Organov Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index d99adc6f72..60e13853dc 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -475,7 +475,7 @@ Bisecting: 3537 revisions left to test after this If you run "git branch" at this point, you'll see that git has temporarily moved you to a new branch named "bisect". This branch points to a commit (with commit id 65934...) that is reachable from -v2.6.19 but not from v2.6.18. Compile and test it, and see whether +"master" but not from v2.6.18. Compile and test it, and see whether it crashes. Assume it does crash. Then: ------------------------------------------------- @@ -1567,8 +1567,8 @@ old history using, for example, $ git log master@{1} ------------------------------------------------- -This lists the commits reachable from the previous version of the head. -This syntax can be used to with any git command that accepts a commit, +This lists the commits reachable from the previous version of the branch. +This syntax can be used with any git command that accepts a commit, not just with git log. Some other examples: ------------------------------------------------- From 481424e1f100de690849a9f0348fc78f45ab815e Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 14 Nov 2007 00:16:36 +0100 Subject: [PATCH 74/97] Fix dependencies of parse-options test program A stale test-parse-options can break t0040 otherwise. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 18c61f3e63..9c6776e597 100644 --- a/Makefile +++ b/Makefile @@ -998,6 +998,8 @@ test-date$X: date.o ctype.o test-delta$X: diff-delta.o patch-delta.o +test-parse-options$X: parse-options.o + .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS)) test-%$X: test-%.o $(GITLIBS) From 93cbbd7121c34b04576518b663f188c5495d4575 Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Wed, 14 Nov 2007 12:08:15 -0800 Subject: [PATCH 75/97] user-manual: minor rewording for clarity. Junio screwed up when applying the previous round of the patch; rewording from "previous" to "old" does make the description clearer. Also revert the rewording from head to branch. The description is talking about the branch's tip commit and using the word head is clearer. Based on input from Sergei and Bruce. Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 60e13853dc..c7cfbbccfc 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1367,7 +1367,7 @@ If you make a commit that you later wish you hadn't, there are two fundamentally different ways to fix the problem: 1. You can create a new commit that undoes whatever was done - by the previous commit. This is the correct thing if your + by the old commit. This is the correct thing if your mistake has already been made public. 2. You can go back and modify the old commit. You should @@ -1567,7 +1567,7 @@ old history using, for example, $ git log master@{1} ------------------------------------------------- -This lists the commits reachable from the previous version of the branch. +This lists the commits reachable from the previous version of the head. This syntax can be used with any git command that accepts a commit, not just with git log. Some other examples: From 039bc64e886716593d59910694a6c8ed5b72c515 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Nov 2007 00:05:00 -0800 Subject: [PATCH 76/97] core.excludesfile clean-up There are inconsistencies in the way commands currently handle the core.excludesfile configuration variable. The problem is the variable is too new to be noticed by anything other than git-add and git-status. * git-ls-files does not notice any of the "ignore" files by default, as it predates the standardized set of ignore files. The calling scripts established the convention to use .git/info/exclude, .gitignore, and later core.excludesfile. * git-add and git-status know about it because they call add_excludes_from_file() directly with their own notion of which standard set of ignore files to use. This is just a stupid duplication of code that need to be updated every time the definition of the standard set of ignore files is changed. * git-read-tree takes --exclude-per-directory=, not because the flexibility was needed. Again, this was because the option predates the standardization of the ignore files. * git-merge-recursive uses hardcoded per-directory .gitignore and nothing else. git-clean (scripted version) does not honor core.* because its call to underlying ls-files does not know about it. git-clean in C (parked in 'pu') doesn't either. We probably could change git-ls-files to use the standard set when no excludes are specified on the command line and ignore processing was asked, or something like that, but that will be a change in semantics and might break people's scripts in a subtle way. I am somewhat reluctant to make such a change. On the other hand, I think it makes perfect sense to fix git-read-tree, git-merge-recursive and git-clean to follow the same rule as other commands. I do not think of a valid use case to give an exclude-per-directory that is nonstandard to read-tree command, outside a "negative" test in the t1004 test script. This patch is the first step to untangle this mess. The next step would be to teach read-tree, merge-recursive and clean (in C) to use setup_standard_excludes(). Signed-off-by: Junio C Hamano --- builtin-add.c | 22 ++-------------------- cache.h | 1 + config.c | 7 +++++++ dir.c | 12 ++++++++++++ dir.h | 1 + environment.c | 1 + wt-status.c | 15 +-------------- 7 files changed, 25 insertions(+), 34 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 77dcde6936..cf815a0b8e 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -21,7 +21,6 @@ static const char * const builtin_add_usage[] = { }; static int take_worktree_changes; -static const char *excludes_file; static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { @@ -61,12 +60,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, memset(dir, 0, sizeof(*dir)); if (!ignored_too) { dir->collect_ignored = 1; - dir->exclude_per_dir = ".gitignore"; - path = git_path("info/exclude"); - if (!access(path, R_OK)) - add_excludes_from_file(dir, path); - if (excludes_file != NULL && !access(excludes_file, R_OK)) - add_excludes_from_file(dir, excludes_file); + setup_standard_excludes(dir); } /* @@ -141,18 +135,6 @@ static void refresh(int verbose, const char **pathspec) free(seen); } -static int git_add_config(const char *var, const char *value) -{ - if (!strcmp(var, "core.excludesfile")) { - if (!value) - die("core.excludesfile without value"); - excludes_file = xstrdup(value); - return 0; - } - - return git_default_config(var, value); -} - int interactive_add(void) { const char *argv[2] = { "add--interactive", NULL }; @@ -193,7 +175,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) exit(interactive_add()); } - git_config(git_add_config); + git_config(git_default_config); newfd = hold_locked_index(&lock_file, 1); diff --git a/cache.h b/cache.h index f4f27cd70d..33ebccf48d 100644 --- a/cache.h +++ b/cache.h @@ -581,6 +581,7 @@ extern int pager_in_use; extern int pager_use_color; extern char *editor_program; +extern char *excludes_file; /* base85 */ int decode_85(char *dst, const char *line, int linelen); diff --git a/config.c b/config.c index dc3148d456..56e99fc0f4 100644 --- a/config.c +++ b/config.c @@ -431,6 +431,13 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.excludesfile")) { + if (!value) + die("core.excludesfile without value"); + excludes_file = xstrdup(value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/dir.c b/dir.c index 01790ab27d..fa9f9021ea 100644 --- a/dir.c +++ b/dir.c @@ -778,3 +778,15 @@ int remove_dir_recursively(struct strbuf *path, int only_empty) ret = rmdir(path->buf); return ret; } + +void setup_standard_excludes(struct dir_struct *dir) +{ + const char *path; + + dir->exclude_per_dir = ".gitignore"; + path = git_path("info/exclude"); + if (!access(path, R_OK)) + add_excludes_from_file(dir, path); + if (excludes_file && !access(excludes_file, R_OK)) + add_excludes_from_file(dir, excludes_file); +} diff --git a/dir.h b/dir.h index aaa247b256..82009dc13e 100644 --- a/dir.h +++ b/dir.h @@ -71,6 +71,7 @@ extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna extern char *get_relative_cwd(char *buffer, int size, const char *dir); extern int is_inside_dir(const char *dir); +extern void setup_standard_excludes(struct dir_struct *dir); extern int remove_dir_recursively(struct strbuf *path, int only_empty); #endif diff --git a/environment.c b/environment.c index b5a6c69f7c..1dab72ec15 100644 --- a/environment.c +++ b/environment.c @@ -34,6 +34,7 @@ char *pager_program; int pager_in_use; int pager_use_color = 1; char *editor_program; +char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ /* This is set by setup_git_dir_gently() and/or git_default_config() */ diff --git a/wt-status.c b/wt-status.c index 03b5ec4488..9a6ef4a89a 100644 --- a/wt-status.c +++ b/wt-status.c @@ -22,7 +22,6 @@ static const char use_add_rm_msg[] = "use \"git add/rm ...\" to update what will be committed"; static const char use_add_to_include_msg[] = "use \"git add ...\" to include in what will be committed"; -static const char *excludes_file; static int parse_status_slot(const char *var, int offset) { @@ -251,22 +250,16 @@ static void wt_status_print_changed(struct wt_status *s) static void wt_status_print_untracked(struct wt_status *s) { struct dir_struct dir; - const char *x; int i; int shown_header = 0; memset(&dir, 0, sizeof(dir)); - dir.exclude_per_dir = ".gitignore"; if (!s->untracked) { dir.show_other_directories = 1; dir.hide_empty_directories = 1; } - x = git_path("info/exclude"); - if (file_exists(x)) - add_excludes_from_file(&dir, x); - if (excludes_file && file_exists(excludes_file)) - add_excludes_from_file(&dir, excludes_file); + setup_standard_excludes(&dir); read_directory(&dir, ".", "", 0, NULL); for(i = 0; i < dir.nr; i++) { @@ -364,11 +357,5 @@ int git_status_config(const char *k, const char *v) int slot = parse_status_slot(k, 13); color_parse(v, k, wt_status_colors[slot]); } - if (!strcmp(k, "core.excludesfile")) { - if (!v) - die("core.excludesfile without value"); - excludes_file = xstrdup(v); - return 0; - } return git_default_config(k, v); } From 63405283c3c6c55cca465ba21128ec1b354ee799 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:04:56 +0100 Subject: [PATCH 77/97] t5300-pack-object.sh: Split the big verify-pack test into smaller parts. This makes it easier to spot which of the tests failed. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t5300-pack-object.sh | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index ba7579c251..f1106e6542 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -187,49 +187,51 @@ test_expect_success \ test-3-${packname_3}.idx' test_expect_success \ - 'corrupt a pack and see if verify catches' \ + 'verify-pack catches mismatched .idx and .pack files' \ 'cat test-1-${packname_1}.idx >test-3.idx && cat test-2-${packname_2}.pack >test-3.pack && if git verify-pack test-3.idx then false else :; - fi && + fi' - : PACK_SIGNATURE && - cat test-1-${packname_1}.pack >test-3.pack && +test_expect_success \ + 'verify-pack catches a corrupted pack signature' \ + 'cat test-1-${packname_1}.pack >test-3.pack && dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 && if git verify-pack test-3.idx then false else :; - fi && + fi' - : PACK_VERSION && - cat test-1-${packname_1}.pack >test-3.pack && +test_expect_success \ + 'verify-pack catches a corrupted pack version' \ + 'cat test-1-${packname_1}.pack >test-3.pack && dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 && if git verify-pack test-3.idx then false else :; - fi && + fi' - : TYPE/SIZE byte of the first packed object data && - cat test-1-${packname_1}.pack >test-3.pack && +test_expect_success \ + 'verify-pack catches a corrupted type/size of the 1st packed object data' \ + 'cat test-1-${packname_1}.pack >test-3.pack && dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 && if git verify-pack test-3.idx then false else :; - fi && + fi' - : sum of the index file itself && - l=`wc -c test-3.pack && dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l && if git verify-pack test-3.pack then false else :; - fi && - - :' + fi' test_expect_success \ 'build pack index for an existing pack' \ From 41ec097aea91b4be038b1c4bbbe1bcb9d4e7aa53 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:04:57 +0100 Subject: [PATCH 78/97] t7501-commit.sh: Not all seds understand option -i Use mv instead. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t7501-commit.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 9dba104b1f..31a6f63399 100644 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -79,7 +79,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -i -e "s/a file/an amend commit/g" $1 +sed -e "s/a file/an amend commit/g" < $1 > $1- +mv $1- $1 EOF chmod 755 editor @@ -98,7 +99,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -i -e "s/amend/older/g" $1 +sed -e "s/amend/older/g" < $1 > $1- +mv $1- $1 EOF chmod 755 editor From 8ed2fca458d085f12c3c6808ef4ddab6aa40ef14 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:04:58 +0100 Subject: [PATCH 79/97] t5302-pack-index: Skip tests of 64-bit offsets if necessary. There are platforms where off_t is not 64 bits wide. In this case many tests are doomed to fail. Let's skip them. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t5302-pack-index.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 4f58c4c3f9..d93abc4c75 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -61,17 +61,33 @@ test_expect_success \ test_expect_success \ 'index v2: force some 64-bit offsets with pack-objects' \ - 'pack3=$(git pack-objects --index-version=2,0x40000 test-3 &1) || + ! echo "$msg" | grep "pack too large .* off_t" +then + have_64bits=t +else + say "skipping tests concerning 64-bit offsets" +fi + +test "$have_64bits" && +test_expect_success \ + 'index v2: verify a pack with some 64-bit offsets' \ + 'git verify-pack -v "test-3-${pack3}.pack"' + +test "$have_64bits" && test_expect_failure \ '64-bit offsets: should be different from previous index v2 results' \ 'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"' +test "$have_64bits" && test_expect_success \ 'index v2: force some 64-bit offsets with index-pack' \ 'git-index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"' +test "$have_64bits" && test_expect_success \ '64-bit offsets: index-pack result should match pack-objects one' \ 'cmp "test-3-${pack3}.idx" "3.idx"' @@ -113,6 +129,7 @@ test_expect_failure \ '[index v1] 6) newly created pack is BAD !' \ 'git verify-pack -v "test-4-${pack1}.pack"' +test "$have_64bits" && test_expect_success \ '[index v2] 1) stream pack to repository' \ 'rm -f .git/objects/pack/* && @@ -122,6 +139,7 @@ test_expect_success \ cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" && cmp "test-3-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"' +test "$have_64bits" && test_expect_success \ '[index v2] 2) create a stealth corruption in a delta base reference' \ '# this test assumes a delta smaller than 16 bytes at the end of the pack @@ -134,14 +152,17 @@ test_expect_success \ bs=1 count=20 conv=notrunc && git cat-file blob "$delta_sha1" > blob_4 )' +test "$have_64bits" && test_expect_failure \ '[index v2] 3) corrupted delta happily returned wrong data' \ 'cmp blob_3 blob_4' +test "$have_64bits" && test_expect_failure \ '[index v2] 4) confirm that the pack is actually corrupted' \ 'git fsck --full $commit' +test "$have_64bits" && test_expect_failure \ '[index v2] 5) pack-objects refuses to reuse corrupted data' \ 'git pack-objects test-5 Date: Tue, 13 Nov 2007 21:04:59 +0100 Subject: [PATCH 80/97] Skip t3902-quoted.sh if the file system does not support funny names. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t3902-quoted.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh index 245fb3babd..73da45f18c 100755 --- a/t/t3902-quoted.sh +++ b/t/t3902-quoted.sh @@ -20,6 +20,13 @@ LF=' ' DQ='"' +echo foo > "Name and an${HT}HT" +test -f "Name and an${HT}HT" || { + # since FAT/NTFS does not allow tabs in filenames, skip this test + say 'Your filesystem does not allow tabs in filenames, test skipped.' + test_done +} + for_each_name () { for name in \ Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \ From 85dadc389468211a2fc0006ce8ea524490d417f5 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:05:00 +0100 Subject: [PATCH 81/97] Use is_absolute_path() in sha1_file.c. There are some places that test for an absolute path. Use the helper function to ease porting. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- sha1_file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index f007874cbb..560c0e0d08 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -86,7 +86,7 @@ int safe_create_leading_directories(char *path) char *pos = path; struct stat st; - if (*pos == '/') + if (is_absolute_path(path)) pos++; while (pos) { @@ -253,7 +253,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative int entlen = pfxlen + 43; int base_len = -1; - if (*entry != '/' && relative_base) { + if (!is_absolute_path(entry) && relative_base) { /* Relative alt-odb */ if (base_len < 0) base_len = strlen(relative_base) + 1; @@ -262,7 +262,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative } ent = xmalloc(sizeof(*ent) + entlen); - if (*entry != '/' && relative_base) { + if (!is_absolute_path(entry) && relative_base) { memcpy(ent->base, relative_base, base_len - 1); ent->base[base_len - 1] = '/'; memcpy(ent->base + base_len, entry, len); @@ -333,7 +333,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, while (cp < ep && *cp != sep) cp++; if (last != cp) { - if ((*last != '/') && depth) { + if (!is_absolute_path(last) && depth) { error("%s: ignoring relative alternate object store %s", relative_base, last); } else { From 80bbe72b76ab91628b493b55ed74f6eec8432fed Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:05:01 +0100 Subject: [PATCH 82/97] Move #include and to git-compat-util.h. ... since all system headers are pulled in via git-compat-util.h Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- git-compat-util.h | 2 ++ help.c | 1 - pager.c | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/git-compat-util.h b/git-compat-util.h index ede9408bbd..e83195bde5 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -52,6 +52,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/help.c b/help.c index 8217d97787..d340b6a1b6 100644 --- a/help.c +++ b/help.c @@ -7,7 +7,6 @@ #include "builtin.h" #include "exec_cmd.h" #include "common-cmds.h" -#include /* most GUI terminals set COLUMNS (although some don't export it) */ static int term_columns(void) diff --git a/pager.c b/pager.c index 8bac9d9903..fb7a1a625a 100644 --- a/pager.c +++ b/pager.c @@ -1,7 +1,5 @@ #include "cache.h" -#include - /* * This is split up from the rest of git so that we might do * something different on Windows, for example. From 2488df84a28b5eaae5495a59e390b51874d60a03 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:05:02 +0100 Subject: [PATCH 83/97] builtin run_command: do not exit with -1. There are shells that do not correctly detect an exit code of -1 as a failure. We simply truncate the status code to the lower 8 bits. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- git.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.c b/git.c index 4a250f7e8b..37d99d6f50 100644 --- a/git.c +++ b/git.c @@ -256,7 +256,7 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv) status = p->fn(argc, argv, prefix); if (status) - return status; + return status & 0xff; /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) From 4723ee992cba052dc735b7d2b6f43aad26d9150a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 13 Nov 2007 21:05:03 +0100 Subject: [PATCH 84/97] Close files opened by lock_file() before unlinking. This is needed on Windows since open files cannot be unlinked. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- cache.h | 1 + lockfile.c | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cache.h b/cache.h index 33ebccf48d..dc75f9d343 100644 --- a/cache.h +++ b/cache.h @@ -290,6 +290,7 @@ extern int refresh_index(struct index_state *, unsigned int flags, const char ** struct lock_file { struct lock_file *next; + int fd; pid_t owner; char on_list; char filename[PATH_MAX]; diff --git a/lockfile.c b/lockfile.c index 9a1f64d8d7..258fb3f5ef 100644 --- a/lockfile.c +++ b/lockfile.c @@ -12,8 +12,10 @@ static void remove_lock_file(void) while (lock_file_list) { if (lock_file_list->owner == me && - lock_file_list->filename[0]) + lock_file_list->filename[0]) { + close(lock_file_list->fd); unlink(lock_file_list->filename); + } lock_file_list = lock_file_list->next; } } @@ -120,8 +122,6 @@ static char *resolve_symlink(char *p, size_t s) static int lock_file(struct lock_file *lk, const char *path) { - int fd; - if (strlen(path) >= sizeof(lk->filename)) return -1; strcpy(lk->filename, path); /* @@ -130,8 +130,8 @@ static int lock_file(struct lock_file *lk, const char *path) */ resolve_symlink(lk->filename, sizeof(lk->filename)-5); strcat(lk->filename, ".lock"); - fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); - if (0 <= fd) { + lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); + if (0 <= lk->fd) { if (!lock_file_list) { signal(SIGINT, remove_lock_file_on_signal); atexit(remove_lock_file); @@ -148,7 +148,7 @@ static int lock_file(struct lock_file *lk, const char *path) } else lk->filename[0] = 0; - return fd; + return lk->fd; } int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error) @@ -163,6 +163,7 @@ int commit_lock_file(struct lock_file *lk) { char result_file[PATH_MAX]; int i; + close(lk->fd); strcpy(result_file, lk->filename); i = strlen(result_file) - 5; /* .lock */ result_file[i] = 0; @@ -194,7 +195,9 @@ int commit_locked_index(struct lock_file *lk) void rollback_lock_file(struct lock_file *lk) { - if (lk->filename[0]) + if (lk->filename[0]) { + close(lk->fd); unlink(lk->filename); + } lk->filename[0] = 0; } From a47d181380e70cba4e6f641e9485d202a0735c5a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:05:04 +0100 Subject: [PATCH 85/97] Allow a relative builtin template directory. In order to make git relocatable (i.e. not have the prefix compiled-in) the template directory must depend on the location where this git instance is found, which is GIT_EXEC_DIR. The exec path is prepended only if the compiled-in default template directory is to be used and that is relative. Any relative directories that are specified via environment variable or the --exec-dir switch are taken as is. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- builtin-init-db.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index 763fa55e76..e1393b8d1e 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -5,6 +5,7 @@ */ #include "cache.h" #include "builtin.h" +#include "exec_cmd.h" #ifndef DEFAULT_GIT_TEMPLATE_DIR #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates" @@ -131,10 +132,19 @@ static void copy_templates(const char *git_dir, int len, const char *template_di int template_len; DIR *dir; - if (!template_dir) { + if (!template_dir) template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); - if (!template_dir) - template_dir = DEFAULT_GIT_TEMPLATE_DIR; + if (!template_dir) { + /* + * if the hard-coded template is relative, it is + * interpreted relative to the exec_dir + */ + template_dir = DEFAULT_GIT_TEMPLATE_DIR; + if (!is_absolute_path(template_dir)) { + const char *exec_path = git_exec_path(); + template_dir = prefix_path(exec_path, strlen(exec_path), + template_dir); + } } strcpy(template_path, template_dir); template_len = strlen(template_path); From 506b17b136ba8ee29098c2ee26b94f527cea9ebc Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:05:05 +0100 Subject: [PATCH 86/97] Introduce git_etc_gitconfig() that encapsulates access of ETC_GITCONFIG. In a subsequent patch the path to the system-wide config file will be computed. This is a preparation for that change. It turns all accesses of ETC_GITCONFIG into function calls. There is no change in behavior. As a consequence, config.c is the only file that needs the definition of ETC_GITCONFIG. Hence, -DETC_GITCONFIG is removed from the CFLAGS and a special build rule for config.c is introduced. As a side-effect, changing the defintion of ETC_GITCONFIG (e.g. in config.mak) does not trigger a complete rebuild anymore. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 5 ++++- builtin-config.c | 4 ++-- cache.h | 1 + config.c | 9 +++++++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index e830bc7445..817f3c7ba9 100644 --- a/Makefile +++ b/Makefile @@ -754,7 +754,7 @@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) LIBS = $(GITLIBS) $(EXTLIBS) BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \ - -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $(COMPAT_CFLAGS) + $(COMPAT_CFLAGS) LIB_OBJS += $(COMPAT_OBJS) ALL_CFLAGS += $(BASIC_CFLAGS) @@ -903,6 +903,9 @@ exec_cmd.o: exec_cmd.c GIT-CFLAGS builtin-init-db.o: builtin-init-db.c GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $< +config.o: config.c GIT-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $< + http.o: http.c GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $< diff --git a/builtin-config.c b/builtin-config.c index e5e243f27c..f672c9ccca 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -81,7 +81,7 @@ static int get_value(const char* key_, const char* regex_) local = repo_config = xstrdup(git_path("config")); if (home) global = xstrdup(mkpath("%s/.gitconfig", home)); - system_wide = ETC_GITCONFIG; + system_wide = git_etc_gitconfig(); } key = xstrdup(key_); @@ -191,7 +191,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) } } else if (!strcmp(argv[1], "--system")) - setenv(CONFIG_ENVIRONMENT, ETC_GITCONFIG, 1); + setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1); else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { if (argc < 3) usage(git_config_set_usage); diff --git a/cache.h b/cache.h index dc75f9d343..a0b93770d4 100644 --- a/cache.h +++ b/cache.h @@ -557,6 +557,7 @@ extern int git_config_bool(const char *, const char *); extern int git_config_set(const char *, const char *); extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); +extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value); #define MAX_GITNAME (1000) diff --git a/config.c b/config.c index 56e99fc0f4..cee2d26d0c 100644 --- a/config.c +++ b/config.c @@ -459,6 +459,11 @@ int git_config_from_file(config_fn_t fn, const char *filename) return ret; } +const char *git_etc_gitconfig(void) +{ + return ETC_GITCONFIG; +} + int git_config(config_fn_t fn) { int ret = 0; @@ -471,8 +476,8 @@ int git_config(config_fn_t fn) * config file otherwise. */ filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { - if (!access(ETC_GITCONFIG, R_OK)) - ret += git_config_from_file(fn, ETC_GITCONFIG); + if (!access(git_etc_gitconfig(), R_OK)) + ret += git_config_from_file(fn, git_etc_gitconfig()); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!filename) From 7f0e39faa2929541f61039be96d3f40f74207585 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 13 Nov 2007 21:05:06 +0100 Subject: [PATCH 87/97] Allow ETC_GITCONFIG to be a relative path. If ETC_GITCONFIG is not an absolute path, interpret it relative to --exec-dir. This makes the installed binaries relocatable because the prefix is not compiled-in. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- config.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index cee2d26d0c..ed96213c44 100644 --- a/config.c +++ b/config.c @@ -6,6 +6,7 @@ * */ #include "cache.h" +#include "exec_cmd.h" #define MAXNAME (256) @@ -461,7 +462,17 @@ int git_config_from_file(config_fn_t fn, const char *filename) const char *git_etc_gitconfig(void) { - return ETC_GITCONFIG; + static const char *system_wide; + if (!system_wide) { + system_wide = ETC_GITCONFIG; + if (!is_absolute_path(system_wide)) { + /* interpret path relative to exec-dir */ + const char *exec_path = git_exec_path(); + system_wide = prefix_path(exec_path, strlen(exec_path), + system_wide); + } + } + return system_wide; } int git_config(config_fn_t fn) From a5ac001accd3566b910008cb5068802f7968b711 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 3 Nov 2007 22:35:03 +0100 Subject: [PATCH 88/97] upload-pack: rely on finish_command() and finish_async() We don't need explicitly handle the processes anymore. --- upload-pack.c | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index 38b774fa34..34299308f8 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -265,29 +265,6 @@ static void create_pack_file(void) goto fail; } } - - if (finish_command(&pack_objects)) { - error("git-upload-pack: git-pack-objects died with error."); - goto fail; - } - if (finish_async(&rev_list)) - goto fail; /* error was already reported */ - - /* flush the data */ - if (0 <= buffered) { - data[0] = buffered; - sz = send_client_data(1, data, 1); - if (sz < 0) - goto fail; - fprintf(stderr, "flushed.\n"); - } - if (use_sideband) - packet_flush(1); - return; - - fail: - send_client_data(3, abort_msg, sizeof(abort_msg)); - die("git-upload-pack: %s", abort_msg); #else char *cp; @@ -322,6 +299,14 @@ static void create_pack_file(void) } else goto fail; +#endif + + if (finish_command(&pack_objects)) { + error("git-upload-pack: git-pack-objects died with error."); + goto fail; + } + if (finish_async(&rev_list)) + goto fail; /* error was already reported */ /* flush the data */ if (0 <= buffered) { @@ -333,16 +318,11 @@ static void create_pack_file(void) } if (use_sideband) packet_flush(1); - if (waitpid(pack_objects.pid, NULL, 0) < 0) - die("git-upload-pack: waiting for pack-objects: %s", - strerror(errno)); return; fail: - kill(pack_objects.pid, SIGKILL); send_client_data(3, abort_msg, sizeof(abort_msg)); die("git-upload-pack: %s", abort_msg); -#endif } static int got_sha1(char *hex, unsigned char *sha1) From fab21181f4b096a5f2b4f144353a0e22d35c5f7a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 15 Nov 2007 22:22:47 +0100 Subject: [PATCH 89/97] Implement a wrapper of the open() function. The wrapper does two things: - Requests to open /dev/null are redirected to open the nul pseudo file. - A request to open a file that currently exists as a directory, then Windows's open fails with EACCES; this is changed to EISDIR. Signed-off-by: Johannes Sixt --- compat/mingw.c | 20 ++++++++++++++++++++ git-compat-util.h | 5 ++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 9b503a0eec..384b3b67a1 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -3,6 +3,26 @@ unsigned int _CRT_fmode = _O_BINARY; +#undef open +int mingw_open (const char *filename, int oflags, ...) +{ + va_list args; + unsigned mode; + va_start(args, oflags); + mode = va_arg(args, int); + va_end(args); + + if (!strcmp(filename, "/dev/null")) + filename = "nul"; + int fd = open(filename, oflags, mode); + if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) { + DWORD attrs = GetFileAttributes(filename); + if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) + errno = EISDIR; + } + return fd; +} + static inline time_t filetime_to_time_t(const FILETIME *ft) { long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; diff --git a/git-compat-util.h b/git-compat-util.h index 1bbdb2f2fb..bfab97d057 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -500,9 +500,8 @@ static inline int git_unlink(const char *pathname) { } #define unlink git_unlink -#define open(P, F, M...) \ - (__builtin_constant_p(*(P)) && !strcmp(P, "/dev/null") ? \ - open("nul", F, ## M) : open(P, F, ## M)) +int mingw_open (const char *filename, int oflags, ...); +#define open mingw_open #include struct tm *gmtime_r(const time_t *timep, struct tm *result); From e8586bac156301508f66c0644d6614a003229e2c Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 15 Nov 2007 22:26:49 +0100 Subject: [PATCH 90/97] Revert "Work around missing EISDIR errno values." This reverts the remaining part of commit ff61d9fc99a60a7c0320538a278d5f58680e120d. --- refs.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/refs.c b/refs.c index aa8045d89d..aff02cd09d 100644 --- a/refs.c +++ b/refs.c @@ -1059,15 +1059,6 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, if (!(oflags & O_CREAT) && errno == ENOENT) return 0; -#ifdef __MINGW32__ - if ((oflags & O_CREAT) && errno == EACCES) { - struct stat st; - if (stat(log_file, &st) == 0 && S_ISDIR(st.st_mode)) - errno = EISDIR; - else - errno = EACCES; - } -#endif if ((oflags & O_CREAT) && errno == EISDIR) { if (remove_empty_directories(log_file)) { return error("There are still logs under '%s'", From 5f9ffff308845e2439670ec3cb2c12f4865cf430 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 15 Nov 2007 12:24:17 -0500 Subject: [PATCH 91/97] rehabilitate some t5302 tests on 32-bit off_t machines Commit 8ed2fca458d085f12c3c6808ef4ddab6aa40ef14 was a bit draconian in skipping certain tests which should be perfectly valid even on platform with a 32-bit off_t. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- t/t5302-pack-index.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index d93abc4c75..2a2878b572 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -129,17 +129,15 @@ test_expect_failure \ '[index v1] 6) newly created pack is BAD !' \ 'git verify-pack -v "test-4-${pack1}.pack"' -test "$have_64bits" && test_expect_success \ '[index v2] 1) stream pack to repository' \ 'rm -f .git/objects/pack/* && - git-index-pack --index-version=2,0x40000 --stdin < "test-1-${pack1}.pack" && + git-index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" && git prune-packed && git count-objects | ( read nr rest && test "$nr" -eq 1 ) && cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" && - cmp "test-3-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"' + cmp "test-2-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"' -test "$have_64bits" && test_expect_success \ '[index v2] 2) create a stealth corruption in a delta base reference' \ '# this test assumes a delta smaller than 16 bytes at the end of the pack @@ -152,17 +150,14 @@ test_expect_success \ bs=1 count=20 conv=notrunc && git cat-file blob "$delta_sha1" > blob_4 )' -test "$have_64bits" && test_expect_failure \ '[index v2] 3) corrupted delta happily returned wrong data' \ 'cmp blob_3 blob_4' -test "$have_64bits" && test_expect_failure \ '[index v2] 4) confirm that the pack is actually corrupted' \ 'git fsck --full $commit' -test "$have_64bits" && test_expect_failure \ '[index v2] 5) pack-objects refuses to reuse corrupted data' \ 'git pack-objects test-5 Date: Fri, 16 Nov 2007 08:59:29 +0100 Subject: [PATCH 92/97] Remove unnecessary dd function. The test now uses test-genrandom instead of dd if=/dev/random. --- t/t5301-sliding-window.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh index 9a6d6aceac..073ac0c6f9 100755 --- a/t/t5301-sliding-window.sh +++ b/t/t5301-sliding-window.sh @@ -6,10 +6,6 @@ test_description='mmap sliding window tests' . ./test-lib.sh -dd () { - perl -e 'print pack("C", rand(256)) foreach 0 .. 32767' -} - test_expect_success \ 'setup' \ 'rm -f .git/index* From efe7309073bcb56b5de0d2f71cf02013c8a179fe Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 16 Nov 2007 08:53:41 +0100 Subject: [PATCH 93/97] sum.exe is available from GNUWin32 (CoreUtils). --- t/t1002-read-tree-m-u-2way.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index 36642d4119..42e5cf8181 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -10,12 +10,6 @@ This is identical to t1001, but uses -u to update the work tree as well. ' . ./test-lib.sh -sum ./test-lib.sh >/dev/null 2>&1 || { - function sum () { - md5sum "$@" - } -} - _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" compare_change () { From 157737a3d92d067c09908a345ab595fb2b3d89bf Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 16 Nov 2007 08:55:58 +0100 Subject: [PATCH 94/97] stat.exe is available from GNUWin32 (CoreUtils). --- t/t5300-pack-object.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 9a81d84106..169f60e33a 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -26,12 +26,6 @@ corrupt() ) < $1 > $2 } -test "$no_symlinks" && { - stat() { - ls -l "$3" | tr -s ' ' ' ' | cut -d' ' -f5 - } -} - test_expect_success \ 'setup' \ 'rm -f .git/index* From ff1c27fb4e28c253de43b6ca3e8ab00d594fb9c5 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 16 Nov 2007 08:58:13 +0100 Subject: [PATCH 95/97] Revert "Protect {tree} from being expanded by the shell." This reverts commit bff909b88259160b50823f277ea8c1426e4f7aa0. git-tag is now a builtin and does not need the extra escaping anymore. --- t/t5515-fetch-merge-logic.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh index 483a554d94..31c1081617 100755 --- a/t/t5515-fetch-merge-logic.sh +++ b/t/t5515-fetch-merge-logic.sh @@ -20,7 +20,7 @@ test_expect_success setup ' git add file && git commit -a -m One && git tag tag-one && - git-tag tag-one-tree HEAD^\{tree} && + git tag tag-one-tree HEAD^{tree} && git branch one && echo two >> file && @@ -31,7 +31,7 @@ test_expect_success setup ' echo three >> file && git commit -a -m Three && git tag -a -m "Tag Three" tag-three && - git-tag -a -m "Tag Three file" tag-three-file HEAD^\{tree}:file && + git tag -a -m "Tag Three file" tag-three-file HEAD^{tree}:file && git branch three && echo master >> file && From 3dffb74b2c118b1eb58aab27b7bbf76cee7974bb Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 16 Nov 2007 22:53:18 +0100 Subject: [PATCH 96/97] Use start_command() and finish_command() to run the pager. This gets rid of a call to spawnvpe_pipe() (and Windows-specific cwait()). --- pager.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pager.c b/pager.c index a55f4e9d1e..de1bc799f5 100644 --- a/pager.c +++ b/pager.c @@ -1,5 +1,4 @@ #include "cache.h" -#include "spawn-pipe.h" /* * This is split up from the rest of git so that we might do @@ -23,12 +22,18 @@ static void run_pager(const char *pager) execl("/bin/sh", "sh", "-c", pager, NULL); } #else -static pid_t pager_pid; +#include "run-command.h" + +const char *pager_argv[] = { "sh", "-c", NULL, NULL }; +static struct child_process pager_process = { + .argv = pager_argv, + .in = -1 +}; static void collect_pager(void) { fflush(stdout); close(1); /* signals EOF to pager */ - cwait(NULL, pager_pid, 0); + finish_command(&pager_process); } #endif @@ -36,10 +41,8 @@ void setup_pager(void) { #ifndef __MINGW32__ pid_t pid; -#else - const char *pager_argv[] = { "sh", "-c", NULL, NULL }; -#endif int fd[2]; +#endif const char *pager = getenv("GIT_PAGER"); if (!isatty(1)) @@ -58,9 +61,9 @@ void setup_pager(void) pager_in_use = 1; /* means we are emitting to terminal */ +#ifndef __MINGW32__ if (pipe(fd) < 0) return; -#ifndef __MINGW32__ pid = fork(); if (pid < 0) { close(fd[0]); @@ -88,13 +91,12 @@ void setup_pager(void) #else /* spawn the pager */ pager_argv[2] = pager; - pager_pid = spawnvpe_pipe(pager_argv[0], pager_argv, environ, fd, NULL); - if (pager_pid < 0) + if (start_command(&pager_process)) return; /* original process continues, but writes to the pipe */ - dup2(fd[1], 1); - close(fd[1]); + dup2(pager_process.in, 1); + close(pager_process.in); /* this makes sure that the parent terminates after the pager */ atexit(collect_pager); From 7828980b8c727d85e189400fbd21c59ddaa3c666 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 16 Nov 2007 23:35:32 +0100 Subject: [PATCH 97/97] Revert "sum.exe is available from GNUWin32 (CoreUtils)." This reverts commit efe7309073bcb56b5de0d2f71cf02013c8a179fe. This "sum.exe" knows it better and writes CRLF line endings. Hmpf. --- t/t1002-read-tree-m-u-2way.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index 42e5cf8181..36642d4119 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -10,6 +10,12 @@ This is identical to t1001, but uses -u to update the work tree as well. ' . ./test-lib.sh +sum ./test-lib.sh >/dev/null 2>&1 || { + function sum () { + md5sum "$@" + } +} + _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" compare_change () {