From 535bb89320ba949f0d64eda530ba5dec0ec6f188 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 23 Feb 2009 06:08:10 +0100 Subject: [PATCH 001/133] Start a library for cvsimport-related tests For now the library just includes code (moved from t/t9600-cvsimport.sh) that checks whether the prerequisites for "git cvsimport" are installed. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- t/lib-cvs.sh | 31 +++++++++++++++++++++++++++++++ t/t9600-cvsimport.sh | 29 +---------------------------- 2 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 t/lib-cvs.sh diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh new file mode 100644 index 0000000000..bfc1c12f3f --- /dev/null +++ b/t/lib-cvs.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +. ./test-lib.sh + +unset CVS_SERVER +# for clean cvsps cache +HOME=$(pwd) +export HOME + +if ! type cvs >/dev/null 2>&1 +then + say 'skipping cvsimport tests, cvs not found' + test_done + exit +fi + +cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` +case "$cvsps_version" in +2.1 | 2.2*) + ;; +'') + say 'skipping cvsimport tests, cvsps not found' + test_done + exit + ;; +*) + say 'skipping cvsimport tests, unsupported cvsps version' + test_done + exit + ;; +esac diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index d2379e7f62..98077ab12b 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -1,37 +1,10 @@ #!/bin/sh test_description='git cvsimport basic tests' -. ./test-lib.sh +. ./lib-cvs.sh CVSROOT=$(pwd)/cvsroot export CVSROOT -unset CVS_SERVER -# for clean cvsps cache -HOME=$(pwd) -export HOME - -if ! type cvs >/dev/null 2>&1 -then - say 'skipping cvsimport tests, cvs not found' - test_done - exit -fi - -cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` -case "$cvsps_version" in -2.1 | 2.2*) - ;; -'') - say 'skipping cvsimport tests, cvsps not found' - test_done - exit - ;; -*) - say 'skipping cvsimport tests, unsupported cvsps version' - test_done - exit - ;; -esac test_expect_success 'setup cvsroot' 'cvs init' From 161261b12b3777bc78ef3fbe84ccf595dd195704 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 23 Feb 2009 06:08:11 +0100 Subject: [PATCH 002/133] Use CVS's -f option if available (ignore user's ~/.cvsrc file) A user's ~/.cvsrc file can change the basic behavior of CVS commands. Therefore we should ignore it in order to ensure consistent results from the test suite. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- t/lib-cvs.sh | 3 +++ t/t9600-cvsimport.sh | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh index bfc1c12f3f..67389012eb 100644 --- a/t/lib-cvs.sh +++ b/t/lib-cvs.sh @@ -14,6 +14,9 @@ then exit fi +CVS="cvs -f" +export CVS + cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` case "$cvsps_version" in 2.1 | 2.2*) diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 98077ab12b..3110a8ce36 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -6,12 +6,12 @@ test_description='git cvsimport basic tests' CVSROOT=$(pwd)/cvsroot export CVSROOT -test_expect_success 'setup cvsroot' 'cvs init' +test_expect_success 'setup cvsroot' '$CVS init' test_expect_success 'setup a cvs module' ' mkdir "$CVSROOT/module" && - cvs co -d module-cvs module && + $CVS co -d module-cvs module && cd module-cvs && cat <o_fortuna && O Fortuna @@ -30,13 +30,13 @@ egestatem, potestatem dissolvit ut glaciem. EOF - cvs add o_fortuna && + $CVS add o_fortuna && cat <message && add "O Fortuna" lyrics These public domain lyrics make an excellent sample text. EOF - cvs commit -F message && + $CVS commit -F message && cd .. ' @@ -74,7 +74,7 @@ translate to English My Latin is terrible. EOF - cvs commit -F message && + $CVS commit -F message && cd .. ' @@ -92,8 +92,8 @@ test_expect_success 'update cvs module' ' cd module-cvs && echo 1 >tick && - cvs add tick && - cvs commit -m 1 + $CVS add tick && + $CVS commit -m 1 cd .. ' @@ -111,7 +111,7 @@ test_expect_success 'cvsimport.module config works' ' test_expect_success 'import from a CVS working tree' ' - cvs co -d import-from-wt module && + $CVS co -d import-from-wt module && cd import-from-wt && git cvsimport -a -z0 && echo 1 >expect && From cefa318ddbc8565b50ac2eb9b6aab93e26cc0abe Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 23 Feb 2009 06:08:12 +0100 Subject: [PATCH 003/133] Test contents of entire cvsimported "master" tree contents Test added for completeness (it passes). Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- t/lib-cvs.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++ t/t9600-cvsimport.sh | 2 ++ 2 files changed, 46 insertions(+) diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh index 67389012eb..1f73c07df8 100644 --- a/t/lib-cvs.sh +++ b/t/lib-cvs.sh @@ -32,3 +32,47 @@ case "$cvsps_version" in exit ;; esac + +test_cvs_co () { + # Usage: test_cvs_co BRANCH_NAME + rm -rf module-cvs-"$1" + if [ "$1" = "master" ] + then + $CVS co -P -d module-cvs-"$1" -A module + else + $CVS co -P -d module-cvs-"$1" -r "$1" module + fi +} + +test_git_co () { + # Usage: test_git_co BRANCH_NAME + (cd module-git && git checkout "$1") +} + +test_cmp_branch_file () { + # Usage: test_cmp_branch_file BRANCH_NAME PATH + # The branch must already be checked out of CVS and git. + test_cmp module-cvs-"$1"/"$2" module-git/"$2" +} + +test_cmp_branch_tree () { + # Usage: test_cmp_branch_tree BRANCH_NAME + # Check BRANCH_NAME out of CVS and git and make sure that all + # of the files and directories are identical. + + test_cvs_co "$1" && + test_git_co "$1" && + ( + cd module-cvs-"$1" + find . -type d -name CVS -prune -o -type f -print + ) | sort >module-cvs-"$1".list && + ( + cd module-git + find . -type d -name .git -prune -o -type f -print + ) | sort >module-git-"$1".list && + test_cmp module-cvs-"$1".list module-git-"$1".list && + cat module-cvs-"$1".list | while read f + do + test_cmp_branch_file "$1" "$f" || return 1 + done +} diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 3110a8ce36..e3734b3ba9 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -121,4 +121,6 @@ test_expect_success 'import from a CVS working tree' ' ' +test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master' + test_done From b225290445210bbdef207e42de21edbf8baa24aa Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 23 Feb 2009 06:08:13 +0100 Subject: [PATCH 004/133] Add some tests of git-cvsimport's handling of vendor branches CVS's handling of vendor branches is tricky; add some tests to check whether revisions added via "cvs imports" then imported to git via "git cvsimport" are reflected correctly on master. One of these tests fail and is therefore marked "test_expect_failure". Cvsimport doesn't realize that subsequent changes on a vendor branch affect master as long as the vendor branch is the default branch. The test CVS repository used for these tests is derived from cvs2svn's test suite. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- t/t9601-cvsimport-vendor-branch.sh | 86 +++++++++++++++++++ t/t9601/cvsroot/.gitattributes | 1 + t/t9601/cvsroot/CVSROOT/.gitignore | 2 + t/t9601/cvsroot/module/added-imported.txt,v | 44 ++++++++++ .../cvsroot/module/imported-anonymously.txt,v | 42 +++++++++ .../module/imported-modified-imported.txt,v | 76 ++++++++++++++++ .../cvsroot/module/imported-modified.txt,v | 59 +++++++++++++ t/t9601/cvsroot/module/imported-once.txt,v | 43 ++++++++++ t/t9601/cvsroot/module/imported-twice.txt,v | 60 +++++++++++++ 9 files changed, 413 insertions(+) create mode 100755 t/t9601-cvsimport-vendor-branch.sh create mode 100644 t/t9601/cvsroot/.gitattributes create mode 100644 t/t9601/cvsroot/CVSROOT/.gitignore create mode 100644 t/t9601/cvsroot/module/added-imported.txt,v create mode 100644 t/t9601/cvsroot/module/imported-anonymously.txt,v create mode 100644 t/t9601/cvsroot/module/imported-modified-imported.txt,v create mode 100644 t/t9601/cvsroot/module/imported-modified.txt,v create mode 100644 t/t9601/cvsroot/module/imported-once.txt,v create mode 100644 t/t9601/cvsroot/module/imported-twice.txt,v diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh new file mode 100755 index 0000000000..3afaf56526 --- /dev/null +++ b/t/t9601-cvsimport-vendor-branch.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +# Description of the files in the repository: +# +# imported-once.txt: +# +# Imported once. 1.1 and 1.1.1.1 should be identical. +# +# imported-twice.txt: +# +# Imported twice. HEAD should reflect the contents of the +# second import (i.e., have the same contents as 1.1.1.2). +# +# imported-modified.txt: +# +# Imported, then modified on HEAD. HEAD should reflect the +# modification. +# +# imported-modified-imported.txt: +# +# Imported, then modified on HEAD, then imported again. +# +# added-imported.txt,v: +# +# Added with 'cvs add' to create 1.1, then imported with +# completely different contents to create 1.1.1.1, therefore the +# vendor branch was never the default branch. +# +# imported-anonymously.txt: +# +# Like imported-twice.txt, but with a vendor branch whose branch +# tag has been removed. + +test_description='git cvsimport handling of vendor branches' +. ./lib-cvs.sh + +CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot +export CVSROOT + +test_expect_success 'import a module with a vendor branch' ' + + git cvsimport -C module-git module + +' + +test_expect_success 'check HEAD out of cvs repository' 'test_cvs_co master' + +test_expect_success 'check master out of git repository' 'test_git_co master' + +test_expect_success 'check a file that was imported once' ' + + test_cmp_branch_file master imported-once.txt + +' + +test_expect_failure 'check a file that was imported twice' ' + + test_cmp_branch_file master imported-twice.txt + +' + +test_expect_success 'check a file that was imported then modified on HEAD' ' + + test_cmp_branch_file master imported-modified.txt + +' + +test_expect_success 'check a file that was imported, modified, then imported again' ' + + test_cmp_branch_file master imported-modified-imported.txt + +' + +test_expect_success 'check a file that was added to HEAD then imported' ' + + test_cmp_branch_file master added-imported.txt + +' + +test_expect_success 'a vendor branch whose tag has been removed' ' + + test_cmp_branch_file master imported-anonymously.txt + +' + +test_done diff --git a/t/t9601/cvsroot/.gitattributes b/t/t9601/cvsroot/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t9601/cvsroot/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore new file mode 100644 index 0000000000..3bb9b34173 --- /dev/null +++ b/t/t9601/cvsroot/CVSROOT/.gitignore @@ -0,0 +1,2 @@ +history +val-tags diff --git a/t/t9601/cvsroot/module/added-imported.txt,v b/t/t9601/cvsroot/module/added-imported.txt,v new file mode 100644 index 0000000000..5f83072ea4 --- /dev/null +++ b/t/t9601/cvsroot/module/added-imported.txt,v @@ -0,0 +1,44 @@ +head 1.1; +access; +symbols + vtag-4:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.15; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.16; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Add a file to the working copy. +@ +text +@Adding this file, before importing it with different contents. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-4). +@ +text +@d1 1 +a1 1 +This is vtag-4 (on vbranchA) of added-then-imported.txt. +@ + diff --git a/t/t9601/cvsroot/module/imported-anonymously.txt,v b/t/t9601/cvsroot/module/imported-anonymously.txt,v new file mode 100644 index 0000000000..55e1b0ca5d --- /dev/null +++ b/t/t9601/cvsroot/module/imported-anonymously.txt,v @@ -0,0 +1,42 @@ +head 1.1; +branch 1.1.1; +access; +symbols + vtag-1:1.1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Initial revision +@ +text +@This is vtag-1 (on vbranchA) of imported-anonymously.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + diff --git a/t/t9601/cvsroot/module/imported-modified-imported.txt,v b/t/t9601/cvsroot/module/imported-modified-imported.txt,v new file mode 100644 index 0000000000..e5830aeb37 --- /dev/null +++ b/t/t9601/cvsroot/module/imported-modified-imported.txt,v @@ -0,0 +1,76 @@ +head 1.2; +access; +symbols + vtag-2:1.1.1.2 + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2004.02.09.15.43.14; author kfogel; state Exp; +branches; +next 1.1; + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next 1.1.1.2; + +1.1.1.2 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@First regular commit, to imported-modified-imported.txt, on HEAD. +@ +text +@This is a modification of imported-modified-imported.txt on HEAD. +It should supersede the version from the vendor branch. +@ + + +1.1 +log +@Initial revision +@ +text +@d1 2 +a2 1 +This is vtag-1 (on vbranchA) of imported-modified-imported.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + +1.1.1.2 +log +@Import (vbranchA, vtag-2). +@ +text +@d1 1 +a1 1 +This is vtag-2 (on vbranchA) of imported-modified-imported.txt. +@ + + diff --git a/t/t9601/cvsroot/module/imported-modified.txt,v b/t/t9601/cvsroot/module/imported-modified.txt,v new file mode 100644 index 0000000000..bbcfe447b9 --- /dev/null +++ b/t/t9601/cvsroot/module/imported-modified.txt,v @@ -0,0 +1,59 @@ +head 1.2; +access; +symbols + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2004.02.09.15.43.14; author kfogel; state Exp; +branches; +next 1.1; + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Commit on HEAD. +@ +text +@This is a modification of imported-modified.txt on HEAD. +It should supersede the version from the vendor branch. +@ + + +1.1 +log +@Initial revision +@ +text +@d1 2 +a2 1 +This is vtag-1 (on vbranchA) of imported-modified.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + diff --git a/t/t9601/cvsroot/module/imported-once.txt,v b/t/t9601/cvsroot/module/imported-once.txt,v new file mode 100644 index 0000000000..c5dd82b12d --- /dev/null +++ b/t/t9601/cvsroot/module/imported-once.txt,v @@ -0,0 +1,43 @@ +head 1.1; +branch 1.1.1; +access; +symbols + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Initial revision +@ +text +@This is vtag-1 (on vbranchA) of imported-once.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + diff --git a/t/t9601/cvsroot/module/imported-twice.txt,v b/t/t9601/cvsroot/module/imported-twice.txt,v new file mode 100644 index 0000000000..d1f3f1b344 --- /dev/null +++ b/t/t9601/cvsroot/module/imported-twice.txt,v @@ -0,0 +1,60 @@ +head 1.1; +branch 1.1.1; +access; +symbols + vtag-2:1.1.1.2 + vtag-1:1.1.1.1 + vbranchA:1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next 1.1.1.2; + +1.1.1.2 +date 2004.02.09.15.43.13; author kfogel; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@Initial revision +@ +text +@This is vtag-1 (on vbranchA) of imported-twice.txt. +@ + + +1.1.1.1 +log +@Import (vbranchA, vtag-1). +@ +text +@@ + + +1.1.1.2 +log +@Import (vbranchA, vtag-2). +@ +text +@d1 1 +a1 1 +This is vtag-2 (on vbranchA) of imported-twice.txt. +@ + + From 0dc062122ee114db087c10007fd374f26725dae7 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 23 Feb 2009 06:08:14 +0100 Subject: [PATCH 005/133] Add a test of "git cvsimport"'s handling of tags and branches 6 out of 11 of these tests fail. The test CVS repository used for these tests is derived from one in cvs2svn's test suite. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- t/t9602-cvsimport-branches-tags.sh | 79 +++++++++++++ t/t9602/README | 62 ++++++++++ t/t9602/cvsroot/.gitattributes | 1 + t/t9602/cvsroot/CVSROOT/.gitignore | 2 + t/t9602/cvsroot/module/default,v | 102 +++++++++++++++++ t/t9602/cvsroot/module/sub1/default,v | 102 +++++++++++++++++ t/t9602/cvsroot/module/sub1/subsubA/default,v | 101 +++++++++++++++++ t/t9602/cvsroot/module/sub1/subsubB/default,v | 107 ++++++++++++++++++ .../module/sub2/Attic/branch_B_MIXED_only,v | 59 ++++++++++ t/t9602/cvsroot/module/sub2/default,v | 102 +++++++++++++++++ t/t9602/cvsroot/module/sub2/subsubA/default,v | 102 +++++++++++++++++ t/t9602/cvsroot/module/sub3/default,v | 102 +++++++++++++++++ 12 files changed, 921 insertions(+) create mode 100755 t/t9602-cvsimport-branches-tags.sh create mode 100644 t/t9602/README create mode 100644 t/t9602/cvsroot/.gitattributes create mode 100644 t/t9602/cvsroot/CVSROOT/.gitignore create mode 100644 t/t9602/cvsroot/module/default,v create mode 100644 t/t9602/cvsroot/module/sub1/default,v create mode 100644 t/t9602/cvsroot/module/sub1/subsubA/default,v create mode 100644 t/t9602/cvsroot/module/sub1/subsubB/default,v create mode 100644 t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v create mode 100644 t/t9602/cvsroot/module/sub2/default,v create mode 100644 t/t9602/cvsroot/module/sub2/subsubA/default,v create mode 100644 t/t9602/cvsroot/module/sub3/default,v diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh new file mode 100755 index 0000000000..67878b2d0c --- /dev/null +++ b/t/t9602-cvsimport-branches-tags.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +# A description of the repository used for this test can be found in +# t9602/README. + +test_description='git cvsimport handling of branches and tags' +. ./lib-cvs.sh + +CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot +export CVSROOT + +test_expect_success 'import module' ' + + git cvsimport -C module-git module + +' + +test_expect_success 'test branch master' ' + + test_cmp_branch_tree master + +' + +test_expect_success 'test branch vendorbranch' ' + + test_cmp_branch_tree vendorbranch + +' + +test_expect_failure 'test branch B_FROM_INITIALS' ' + + test_cmp_branch_tree B_FROM_INITIALS + +' + +test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' ' + + test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE + +' + +test_expect_failure 'test branch B_MIXED' ' + + test_cmp_branch_tree B_MIXED + +' + +test_expect_success 'test branch B_SPLIT' ' + + test_cmp_branch_tree B_SPLIT + +' + +test_expect_failure 'test tag vendortag' ' + + test_cmp_branch_tree vendortag + +' + +test_expect_success 'test tag T_ALL_INITIAL_FILES' ' + + test_cmp_branch_tree T_ALL_INITIAL_FILES + +' + +test_expect_failure 'test tag T_ALL_INITIAL_FILES_BUT_ONE' ' + + test_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE + +' + +test_expect_failure 'test tag T_MIXED' ' + + test_cmp_branch_tree T_MIXED + +' + + +test_done diff --git a/t/t9602/README b/t/t9602/README new file mode 100644 index 0000000000..c231e0f26f --- /dev/null +++ b/t/t9602/README @@ -0,0 +1,62 @@ +This repository is for testing the ability to group revisions +correctly along tags and branches. Here is its history: + + 1. The initial import (revision 1.1 of everybody) created a + directory structure with a file named `default' in each dir: + + ./ + default + sub1/default + subsubA/default + subsubB/default + sub2/default + subsubA/default + sub3/default + + 2. Then tagged everyone with T_ALL_INITIAL_FILES. + + 3. Then tagged everyone except sub1/subsubB/default with + T_ALL_INITIAL_FILES_BUT_ONE. + + 4. Then created branch B_FROM_INITIALS on everyone. + + 5. Then created branch B_FROM_INITIALS_BUT_ONE on everyone except + /sub1/subsubB/default. + + 6. Then committed modifications to two files: sub3/default, and + sub1/subsubA/default. + + 7. Then committed a modification to all 7 files. + + 8. Then backdated sub3/default to revision 1.2, and + sub2/subsubA/default to revision 1.1, and tagged with T_MIXED. + + 9. Same as 8, but tagged with -b to create branch B_MIXED. + + 10. Switched the working copy to B_MIXED, and added + sub2/branch_B_MIXED_only. (That's why the RCS file is in + sub2/Attic/ -- it never existed on trunk.) + + 11. In one commit, modified default, sub1/default, and + sub2/subsubA/default, on branch B_MIXED. + + 12. Did "cvs up -A" on sub2/default, then in one commit, made a + change to sub2/default and sub2/branch_B_MIXED_only. So this + commit should be spread between the branch and the trunk. + + 13. Do "cvs up -A" to get everyone back to trunk, then make a new + branch B_SPLIT on everyone except sub1/subsubB/default,v. + + 14. Switch to branch B_SPLIT (see sub1/subsubB/default disappear) + and commit a change that affects everyone except sub3/default. + + 15. An hour or so later, "cvs up -A" to get sub1/subsubB/default + back, then commit a change on that file, on trunk. (It's + important that this change happened after the previous commits + on B_SPLIT.) + + 16. Branch sub1/subsubB/default to B_SPLIT, then "cvs up -r B_SPLIT" + to switch the whole working copy to the branch. + + 17. Commit a change on B_SPLIT, to sub1/subsubB/default and + sub3/default. diff --git a/t/t9602/cvsroot/.gitattributes b/t/t9602/cvsroot/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t9602/cvsroot/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore new file mode 100644 index 0000000000..3bb9b34173 --- /dev/null +++ b/t/t9602/cvsroot/CVSROOT/.gitignore @@ -0,0 +1,2 @@ +history +val-tags diff --git a/t/t9602/cvsroot/module/default,v b/t/t9602/cvsroot/module/default,v new file mode 100644 index 0000000000..3b68382a3b --- /dev/null +++ b/t/t9602/cvsroot/module/default,v @@ -0,0 +1,102 @@ +head 1.2; +access; +symbols + B_SPLIT:1.2.0.4 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.2.2.1 + 1.2.4.1; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.2.2.1 +date 2003.05.23.00.31.36; author jrandom; state Exp; +branches; +next ; + +1.2.4.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is the file `default' in the top level of the project. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). +@ + + +1.2.4.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a5 2 + +First change on branch B_SPLIT. +@ + + +1.2.2.1 +log +@Modify three files, on branch B_MIXED. +@ +text +@a5 2 + +This line was added on branch B_MIXED only (affecting 3 files). +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub1/default,v b/t/t9602/cvsroot/module/sub1/default,v new file mode 100644 index 0000000000..b7fdccdfdf --- /dev/null +++ b/t/t9602/cvsroot/module/sub1/default,v @@ -0,0 +1,102 @@ +head 1.2; +access; +symbols + B_SPLIT:1.2.0.4 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.2.2.1 + 1.2.4.1; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.2.2.1 +date 2003.05.23.00.31.36; author jrandom; state Exp; +branches; +next ; + +1.2.4.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub1/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). +@ + + +1.2.4.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a5 2 + +First change on branch B_SPLIT. +@ + + +1.2.2.1 +log +@Modify three files, on branch B_MIXED. +@ +text +@a5 2 + +This line was added on branch B_MIXED only (affecting 3 files). +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub1/subsubA/default,v b/t/t9602/cvsroot/module/sub1/subsubA/default,v new file mode 100644 index 0000000000..472b7b2bd9 --- /dev/null +++ b/t/t9602/cvsroot/module/sub1/subsubA/default,v @@ -0,0 +1,101 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.4 + B_MIXED:1.3.0.2 + T_MIXED:1.3 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.3.4.1; +next 1.2; + +1.2 +date 2003.05.23.00.15.26; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.4.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub1/subsubA/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added by the first commit (affecting two files). + +This line was added in the second commit (affecting all 7 files). +@ + + +1.3.4.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a7 2 + +First change on branch B_SPLIT. +@ + + +1.2 +log +@First commit to proj, affecting two files. +@ +text +@d6 2 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub1/subsubB/default,v b/t/t9602/cvsroot/module/sub1/subsubB/default,v new file mode 100644 index 0000000000..fe6efa4554 --- /dev/null +++ b/t/t9602/cvsroot/module/sub1/subsubB/default,v @@ -0,0 +1,107 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.2 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.06.03.04.29.14; author jrandom; state Exp; +branches + 1.3.2.1; +next 1.2; + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.2.1 +date 2003.06.03.04.33.13; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@A trunk change to sub1/subsubB/default. This was committed about an +hour after an earlier change that affected most files on branch +B_SPLIT. This file is not on that branch yet, but after this commit, +we'll branch to B_SPLIT, albeit rooted in a revision that didn't exist +at the time the rest of B_SPLIT was created. +@ +text +@This is sub1/subsubB/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). + +This bit was committed on trunk about an hour after an earlier change +to everyone else on branch B_SPLIT. Afterwards, we'll finally branch +this file to B_SPLIT, but rooted in a revision that didn't exist at +the time the rest of B_SPLIT was created. +@ + + +1.3.2.1 +log +@This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ +text +@a10 4 + +This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@d6 5 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v new file mode 100644 index 0000000000..34c9789f2f --- /dev/null +++ b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v @@ -0,0 +1,59 @@ +head 1.1; +access; +symbols + B_MIXED:1.1.0.2; +locks; strict; +comment @# @; + + +1.1 +date 2003.05.23.00.25.26; author jrandom; state dead; +branches + 1.1.2.1; +next ; + +1.1.2.1 +date 2003.05.23.00.25.26; author jrandom; state Exp; +branches; +next 1.1.2.2; + +1.1.2.2 +date 2003.05.23.00.48.51; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.1 +log +@file branch_B_MIXED_only was initially added on branch B_MIXED. +@ +text +@@ + + +1.1.2.1 +log +@Add a file on branch B_MIXED. +@ +text +@a0 1 +This file was added on branch B_MIXED. It never existed on trunk. +@ + + +1.1.2.2 +log +@A single commit affecting one file on branch B_MIXED and one on trunk. +@ +text +@a1 3 + +The same commit added these two lines here on branch B_MIXED, and two +similar lines to ./default on trunk. +@ + + diff --git a/t/t9602/cvsroot/module/sub2/default,v b/t/t9602/cvsroot/module/sub2/default,v new file mode 100644 index 0000000000..018f7f8ece --- /dev/null +++ b/t/t9602/cvsroot/module/sub2/default,v @@ -0,0 +1,102 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.2 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.05.23.00.48.51; author jrandom; state Exp; +branches + 1.3.2.1; +next 1.2; + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.2.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@A single commit affecting one file on branch B_MIXED and one on trunk. +@ +text +@This is sub2/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). + +The same commit added these two lines here on trunk, and two similar +lines to ./branch_B_MIXED_only on branch B_MIXED. +@ + + +1.3.2.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a8 2 + +First change on branch B_SPLIT. +@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@d6 3 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub2/subsubA/default,v b/t/t9602/cvsroot/module/sub2/subsubA/default,v new file mode 100644 index 0000000000..d13242cb09 --- /dev/null +++ b/t/t9602/cvsroot/module/sub2/subsubA/default,v @@ -0,0 +1,102 @@ +head 1.2; +access; +symbols + B_SPLIT:1.2.0.2 + B_MIXED:1.1.0.2 + T_MIXED:1.1 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.2 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.2.2.1; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1 + 1.1.2.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.1.2.1 +date 2003.05.23.00.31.36; author jrandom; state Exp; +branches; +next ; + +1.2.2.1 +date 2003.06.03.03.20.31; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub2/subsub2/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added in the second commit (affecting all 7 files). +@ + + +1.2.2.1 +log +@First change on branch B_SPLIT. + +This change excludes sub3/default, because it was not part of this +commit, and sub1/subsubB/default, which is not even on the branch yet. +@ +text +@a5 2 + +First change on branch B_SPLIT. +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.2.1 +log +@Modify three files, on branch B_MIXED. +@ +text +@a3 2 + +This line was added on branch B_MIXED only (affecting 3 files). +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/t/t9602/cvsroot/module/sub3/default,v b/t/t9602/cvsroot/module/sub3/default,v new file mode 100644 index 0000000000..88e4567434 --- /dev/null +++ b/t/t9602/cvsroot/module/sub3/default,v @@ -0,0 +1,102 @@ +head 1.3; +access; +symbols + B_SPLIT:1.3.0.2 + B_MIXED:1.2.0.2 + T_MIXED:1.2 + B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4 + B_FROM_INITIALS:1.1.1.1.0.2 + T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1 + T_ALL_INITIAL_FILES:1.1.1.1 + vendortag:1.1.1.1 + vendorbranch:1.1.1; +locks; strict; +comment @# @; + + +1.3 +date 2003.05.23.00.17.53; author jrandom; state Exp; +branches + 1.3.2.1; +next 1.2; + +1.2 +date 2003.05.23.00.15.26; author jrandom; state Exp; +branches; +next 1.1; + +1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches + 1.1.1.1; +next ; + +1.1.1.1 +date 2003.05.22.23.20.19; author jrandom; state Exp; +branches; +next ; + +1.3.2.1 +date 2003.06.03.04.33.13; author jrandom; state Exp; +branches; +next ; + + +desc +@@ + + +1.3 +log +@Second commit to proj, affecting all 7 files. +@ +text +@This is sub3/default. + +Every directory in the `proj' project has a file named `default'. + +This line was added by the first commit (affecting two files). + +This line was added in the second commit (affecting all 7 files). +@ + + +1.3.2.1 +log +@This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ +text +@a7 4 + +This change affects sub3/default and sub1/subsubB/default, on branch +B_SPLIT. Note that the latter file did not even exist on this branch +until after some other files had had revisions committed on B_SPLIT. +@ + + +1.2 +log +@First commit to proj, affecting two files. +@ +text +@d6 2 +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ From 9291ccfd27e1e1958b50df7f408996ac22c0776a Mon Sep 17 00:00:00 2001 From: Heiko Voigt Date: Wed, 18 Mar 2009 18:33:34 +0100 Subject: [PATCH 006/133] cvsimport: add test illustrating a bug in cvsps Some cvs repositories may have time deviations in their recorded commits. This is a test for one of such cases. These kind of repositories can happen if the system time of cvs clients is not fully synchronised. Consider the following sequence of events: * client A commits file a r1.1 * client A commits file a r1.2, b r1.1 * client B commits file b r1.2 using the same timestamp as a r1.1 This can be resolved but due to cvsps ordering its patchsets solely based on the timestamp. It only takes revision odering into account if there is no difference in the timestamp. I hit this bug when importing from a real repository which was originally converted from another rcs based scm. Other import tools can handle this correctly, e.g. parsecvs. Signed-off-by: Heiko Voigt Signed-off-by: Junio C Hamano --- t/t9603-cvsimport-patchsets.sh | 33 ++++++++++++++++++++++++ t/t9603/cvsroot/CVSROOT/.gitignore | 2 ++ t/t9603/cvsroot/module/a,v | 40 ++++++++++++++++++++++++++++++ t/t9603/cvsroot/module/b,v | 40 ++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100755 t/t9603-cvsimport-patchsets.sh create mode 100644 t/t9603/cvsroot/CVSROOT/.gitignore create mode 100644 t/t9603/cvsroot/module/a,v create mode 100644 t/t9603/cvsroot/module/b,v diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh new file mode 100755 index 0000000000..15a971fc4f --- /dev/null +++ b/t/t9603-cvsimport-patchsets.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# Structure of the test cvs repository +# +# Message File:Content Commit Time +# Rev 1 a: 1.1 2009-02-21 19:11:43 +0100 +# Rev 2 a: 1.2 b: 1.1 2009-02-21 19:11:14 +0100 +# Rev 3 b: 1.2 2009-02-21 19:11:43 +0100 +# +# As you can see the commit of Rev 3 has the same time as +# Rev 1 this leads to a broken import because of a cvsps +# bug. + +test_description='git cvsimport testing for correct patchset estimation' +. ./lib-cvs.sh + +CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot +export CVSROOT + +test_expect_failure 'import with criss cross times on revisions' ' + + git cvsimport -p"-x" -C module-git module && + cd module-git && + git log --pretty=format:%s > ../actual && + echo "" >> ../actual && + cd .. && + echo "Rev 3 +Rev 2 +Rev 1" > expect && + test_cmp actual expect +' + +test_done diff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore new file mode 100644 index 0000000000..3bb9b34173 --- /dev/null +++ b/t/t9603/cvsroot/CVSROOT/.gitignore @@ -0,0 +1,2 @@ +history +val-tags diff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v new file mode 100644 index 0000000000..e86adfc108 --- /dev/null +++ b/t/t9603/cvsroot/module/a,v @@ -0,0 +1,40 @@ +head 1.2; +access; +symbols; +locks; strict; +comment @# @; + + +1.2 +date 2009.02.21.18.11.14; author tester; state Exp; +branches; +next 1.1; + +1.1 +date 2009.02.21.18.11.43; author tester; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Rev 2 +@ +text +@1.2 +@ + + +1.1 +log +@Rev 1 +@ +text +@d1 1 +a1 1 +1.1 +@ diff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v new file mode 100644 index 0000000000..ab3089fe56 --- /dev/null +++ b/t/t9603/cvsroot/module/b,v @@ -0,0 +1,40 @@ +head 1.2; +access; +symbols; +locks; strict; +comment @# @; + + +1.2 +date 2009.02.21.18.11.43; author tester; state Exp; +branches; +next 1.1; + +1.1 +date 2009.02.21.18.11.14; author tester; state Exp; +branches; +next ; + + +desc +@@ + + +1.2 +log +@Rev 3 +@ +text +@1.2 +@ + + +1.1 +log +@Rev 2 +@ +text +@d1 1 +a1 1 +1.1 +@ From 3867906b376638cdba6be86755db1d1268ab89c9 Mon Sep 17 00:00:00 2001 From: Heiko Voigt Date: Wed, 18 Mar 2009 18:33:41 +0100 Subject: [PATCH 007/133] cvsimport: extend testcase about patchset order to contain branches This makes sure that timestamps and ordering on branches is not influenced by a fix for cvsps. The test extension does not deal which patchset correction on branches it only verifes that branches are basically handled as before. Signed-off-by: Heiko Voigt Signed-off-by: Junio C Hamano --- t/t9603-cvsimport-patchsets.sh | 17 +++++++--- t/t9603/cvsroot/.gitattributes | 1 + t/t9603/cvsroot/module/a,v | 38 ++++++++++++++++++++-- t/t9603/cvsroot/module/b,v | 58 +++++++++++++++++++++++++++++++--- 4 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 t/t9603/cvsroot/.gitattributes diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh index 15a971fc4f..958bdce4dd 100755 --- a/t/t9603-cvsimport-patchsets.sh +++ b/t/t9603-cvsimport-patchsets.sh @@ -21,13 +21,20 @@ test_expect_failure 'import with criss cross times on revisions' ' git cvsimport -p"-x" -C module-git module && cd module-git && - git log --pretty=format:%s > ../actual && - echo "" >> ../actual && + git log --pretty=format:%s > ../actual-master && + git log A~2..A --pretty="format:%s %ad" -- > ../actual-A && + echo "" >> ../actual-master && + echo "" >> ../actual-A && cd .. && - echo "Rev 3 + echo "Rev 4 +Rev 3 Rev 2 -Rev 1" > expect && - test_cmp actual expect +Rev 1" > expect-master && + test_cmp actual-master expect-master && + + echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000 +Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A && + test_cmp actual-A expect-A ' test_done diff --git a/t/t9603/cvsroot/.gitattributes b/t/t9603/cvsroot/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t9603/cvsroot/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v index e86adfc108..ba8fd5af23 100644 --- a/t/t9603/cvsroot/module/a,v +++ b/t/t9603/cvsroot/module/a,v @@ -1,13 +1,15 @@ head 1.2; access; -symbols; +symbols + A:1.2.0.2; locks; strict; comment @# @; 1.2 date 2009.02.21.18.11.14; author tester; state Exp; -branches; +branches + 1.2.2.1; next 1.1; 1.1 @@ -15,6 +17,16 @@ date 2009.02.21.18.11.43; author tester; state Exp; branches; next ; +1.2.2.1 +date 2009.03.11.19.03.52; author tester; state Exp; +branches; +next 1.2.2.2; + +1.2.2.2 +date 2009.03.11.19.09.10; author tester; state Exp; +branches; +next ; + desc @@ @@ -29,6 +41,28 @@ text @ +1.2.2.1 +log +@Rev 4 Branch A +@ +text +@d1 1 +a1 1 +1.2.2.1 +@ + + +1.2.2.2 +log +@Rev 5 Branch A +@ +text +@d1 1 +a1 1 +1.2.2.2 +@ + + 1.1 log @Rev 1 diff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v index ab3089fe56..d26885518a 100644 --- a/t/t9603/cvsroot/module/b,v +++ b/t/t9603/cvsroot/module/b,v @@ -1,13 +1,20 @@ -head 1.2; +head 1.3; access; -symbols; +symbols + A:1.2.0.2; locks; strict; comment @# @; +1.3 +date 2009.03.11.19.05.08; author tester; state Exp; +branches; +next 1.2; + 1.2 date 2009.02.21.18.11.43; author tester; state Exp; -branches; +branches + 1.2.2.1; next 1.1; 1.1 @@ -15,17 +22,60 @@ date 2009.02.21.18.11.14; author tester; state Exp; branches; next ; +1.2.2.1 +date 2009.03.11.19.03.52; author tester; state Exp; +branches; +next 1.2.2.2; + +1.2.2.2 +date 2009.03.11.19.09.10; author tester; state Exp; +branches; +next ; + desc @@ +1.3 +log +@Rev 4 +@ +text +@1.3 +@ + + 1.2 log @Rev 3 @ text -@1.2 +@d1 1 +a1 1 +1.2 +@ + + +1.2.2.1 +log +@Rev 4 Branch A +@ +text +@d1 1 +a1 1 +1.2.2.1 +@ + + +1.2.2.2 +log +@Rev 5 Branch A +@ +text +@d1 1 +a1 1 +1.2 @ From 0eaadfe625fdb9fe9e469413b5e295f6c2ac46ad Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 5 Apr 2009 01:40:50 -0700 Subject: [PATCH 008/133] t/t9600: remove exit after test_done This cherry-picks part of 5dba35912474770d0df45ed801d78c4c9ed5e949 Signed-off-by: Junio C Hamano --- t/lib-cvs.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh index 1f73c07df8..4b3b793730 100644 --- a/t/lib-cvs.sh +++ b/t/lib-cvs.sh @@ -11,7 +11,6 @@ if ! type cvs >/dev/null 2>&1 then say 'skipping cvsimport tests, cvs not found' test_done - exit fi CVS="cvs -f" @@ -24,12 +23,10 @@ case "$cvsps_version" in '') say 'skipping cvsimport tests, cvsps not found' test_done - exit ;; *) say 'skipping cvsimport tests, unsupported cvsps version' test_done - exit ;; esac From f0cea83f631689331fce73b51f22707e897f7939 Mon Sep 17 00:00:00 2001 From: Nick Edelen Date: Wed, 10 Jun 2009 01:50:18 +0200 Subject: [PATCH 009/133] Shift object enumeration out of upload-pack Offload object enumeration in upload-pack to pack-objects, but fall back on internal revision walker for shallow interaction. Aside from architecturally making more sense, this also leaves the door open for pack-objects to employ a revision cache mechanism. Test t5530 updated in order to explicitly check both enumeration methods. Signed-off-by: Nick Edelen Acked-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- t/t5530-upload-pack-error.sh | 14 +++++++++-- upload-pack.c | 49 ++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh index f5102b902a..757cc19ecc 100755 --- a/t/t5530-upload-pack-error.sh +++ b/t/t5530-upload-pack-error.sh @@ -30,11 +30,12 @@ test_expect_success 'fsck fails' ' test_must_fail git fsck ' -test_expect_success 'upload-pack fails due to error in pack-objects' ' +test_expect_success 'upload-pack fails due to error in pack-objects packing' ' ! echo "0032want $(git rev-parse HEAD) 00000009done 0000" | git upload-pack . > /dev/null 2> output.err && + grep "unable to read" output.err && grep "pack-objects died" output.err ' @@ -51,11 +52,20 @@ test_expect_success 'fsck fails' ' test_expect_success 'upload-pack fails due to error in rev-list' ' ! echo "0032want $(git rev-parse HEAD) -00000009done +0034shallow $(git rev-parse HEAD^)00000009done 0000" | git upload-pack . > /dev/null 2> output.err && grep "waitpid (async) failed" output.err ' +test_expect_success 'upload-pack fails due to error in pack-objects enumeration' ' + + ! echo "0032want $(git rev-parse HEAD) +00000009done +0000" | git upload-pack . > /dev/null 2> output.err && + grep "bad tree object" output.err && + grep "pack-objects died" output.err +' + test_expect_success 'create empty repository' ' mkdir foo && diff --git a/upload-pack.c b/upload-pack.c index edc7861228..397cada43b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -29,6 +29,7 @@ static unsigned long oldest_have; static int multi_ack, nr_our_refs; static int use_thin_pack, use_ofs_delta, use_include_tag; static int no_progress; +static int shallow_nr; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; @@ -107,8 +108,6 @@ static int do_rev_list(int fd, void *create_full_pack) struct rev_info revs; pack_pipe = fdopen(fd, "w"); - if (create_full_pack) - use_thin_pack = 0; /* no point doing it */ init_revisions(&revs, NULL); revs.tag_objects = 1; revs.tree_objects = 1; @@ -155,13 +154,21 @@ static void create_pack_file(void) const char *argv[10]; int arg = 0; - rev_list.proc = do_rev_list; - /* .data is just a boolean: any non-NULL value will do */ - rev_list.data = create_full_pack ? &rev_list : NULL; - if (start_async(&rev_list)) - die("git upload-pack: unable to fork git-rev-list"); + if (shallow_nr) { + rev_list.proc = do_rev_list; + rev_list.data = 0; + if (start_async(&rev_list)) + die("git upload-pack: unable to fork git-rev-list"); + argv[arg++] = "pack-objects"; + } else { + argv[arg++] = "pack-objects"; + argv[arg++] = "--revs"; + if (create_full_pack) + argv[arg++] = "--all"; + else if (use_thin_pack) + argv[arg++] = "--thin"; + } - argv[arg++] = "pack-objects"; argv[arg++] = "--stdout"; if (!no_progress) argv[arg++] = "--progress"; @@ -172,7 +179,7 @@ static void create_pack_file(void) argv[arg++] = NULL; memset(&pack_objects, 0, sizeof(pack_objects)); - pack_objects.in = rev_list.out; /* start_command closes it */ + pack_objects.in = shallow_nr ? rev_list.out : -1; pack_objects.out = -1; pack_objects.err = -1; pack_objects.git_cmd = 1; @@ -181,6 +188,24 @@ static void create_pack_file(void) if (start_command(&pack_objects)) die("git upload-pack: unable to fork git-pack-objects"); + /* pass on revisions we (don't) want */ + if (!shallow_nr) { + FILE *pipe_fd = fdopen(pack_objects.in, "w"); + if (!create_full_pack) { + int i; + for (i = 0; i < want_obj.nr; i++) + fprintf(pipe_fd, "%s\n", sha1_to_hex(want_obj.objects[i].item->sha1)); + fprintf(pipe_fd, "--not\n"); + for (i = 0; i < have_obj.nr; i++) + fprintf(pipe_fd, "%s\n", sha1_to_hex(have_obj.objects[i].item->sha1)); + } + + fprintf(pipe_fd, "\n"); + fflush(pipe_fd); + fclose(pipe_fd); + } + + /* We read from pack_objects.err to capture stderr output for * progress bar, and pack_objects.out to capture the pack data. */ @@ -276,7 +301,7 @@ static void create_pack_file(void) error("git upload-pack: git-pack-objects died with error."); goto fail; } - if (finish_async(&rev_list)) + if (shallow_nr && finish_async(&rev_list)) goto fail; /* error was already reported */ /* flush the data */ @@ -451,6 +476,7 @@ static void receive_needs(void) static char line[1000]; int len, depth = 0; + shallow_nr = 0; if (debug_fd) write_in_full(debug_fd, "#S\n", 3); for (;;) { @@ -534,6 +560,7 @@ static void receive_needs(void) packet_write(1, "shallow %s", sha1_to_hex(object->sha1)); register_shallow(object->sha1); + shallow_nr++; } result = result->next; } @@ -567,6 +594,8 @@ static void receive_needs(void) for (i = 0; i < shallows.nr; i++) register_shallow(shallows.objects[i].item->sha1); } + + shallow_nr += shallows.nr; free(shallows.objects); } From a429d2dd76c81cb52fb17463821a457e541637ce Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 25 Jun 2009 22:14:09 -0700 Subject: [PATCH 010/133] read-tree: convert unhelpful usage()'s to helpful die()'s Printing the usage message when encountering bad option combinations is not very helpful. Instead, die with a message which tells the user exactly what combination is invalid. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-read-tree.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 82e25eaa07..17c96310ab 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -145,9 +145,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) continue; } - /* using -u and -i at the same time makes no sense */ if (1 < opts.index_only + opts.update) - usage(read_tree_usage); + die("-u and -i at the same time makes no sense"); if (get_sha1(arg, sha1)) die("Not a valid object name %s", arg); @@ -156,7 +155,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) stage++; } if ((opts.update||opts.index_only) && !opts.merge) - usage(read_tree_usage); + die("%s is meaningless without -m, --reset, or --prefix", + opts.update ? "-u" : "-i"); if ((opts.dir && !opts.update)) die("--exclude-per-directory is meaningless unless -u"); if (opts.merge && !opts.index_only) From 5a56da58060e50980fab0f4c38203a25440d1530 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 25 Jun 2009 22:14:10 -0700 Subject: [PATCH 011/133] read-tree: migrate to parse-options Cleanup the documentation to explicitly state that --exclude-directory is only meaningful when used with -u. Also make the documentation more consistent with the usage message printed with read-tree --help-all. The -m, --prefix, --reset options are performing similar actions (setting some flags, read_cache_unmerged(), checking for illegal option combinations). Instead of performing these actions when the options are parsed, we delay performing them until after parse-opts has finished. The bit fields in struct unpack_trees_options have been promoted to full unsigned ints. This is necessary to avoid "foo ? 1 : 0" constructs to set these fields. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- Documentation/git-read-tree.txt | 5 +- builtin-read-tree.c | 174 ++++++++++++++------------------ unpack-trees.h | 24 ++--- 3 files changed, 92 insertions(+), 111 deletions(-) diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 7160fa1536..4a932b08c6 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -8,7 +8,10 @@ git-read-tree - Reads tree information into the index SYNOPSIS -------- -'git read-tree' ( | [[-m [--trivial] [--aggressive] | --reset | --prefix=] [-u | -i]] [--exclude-per-directory=] [--index-output=] [ []]) +'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=] + [-u [--exclude-per-directory=] | -i]] + [--index-output=] + [ []] DESCRIPTION diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 17c96310ab..9c2d634d6d 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -12,6 +12,7 @@ #include "unpack-trees.h" #include "dir.h" #include "builtin.h" +#include "parse-options.h" static int nr_trees; static struct tree *trees[MAX_UNPACK_TREES]; @@ -29,7 +30,39 @@ static int list_tree(unsigned char *sha1) return 0; } -static const char read_tree_usage[] = "git read-tree ( | [[-m [--trivial] [--aggressive] | --reset | --prefix=] [-u | -i]] [--exclude-per-directory=] [--index-output=] [ []])"; +static const char * const read_tree_usage[] = { + "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=] [-u [--exclude-per-directory=] | -i]] [--index-output=] [ []]", + NULL +}; + +static int index_output_cb(const struct option *opt, const char *arg, + int unset) +{ + set_alternate_index_output(arg); + return 0; +} + +static int exclude_per_directory_cb(const struct option *opt, const char *arg, + int unset) +{ + struct dir_struct *dir; + struct unpack_trees_options *opts; + + opts = (struct unpack_trees_options *)opt->value; + + if (opts->dir) + die("more than one --exclude-per-directory given."); + + dir = xcalloc(1, sizeof(*opts->dir)); + dir->flags |= DIR_SHOW_IGNORED; + dir->exclude_per_dir = arg; + opts->dir = dir; + /* We do not need to nor want to do read-directory + * here; we are merely interested in reusing the + * per directory ignore stack mechanism. + */ + return 0; +} static struct lock_file lock_file; @@ -39,6 +72,34 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) unsigned char sha1[20]; struct tree_desc t[MAX_UNPACK_TREES]; struct unpack_trees_options opts; + int prefix_set = 0; + const struct option read_tree_options[] = { + { OPTION_CALLBACK, 0, "index-output", NULL, "FILE", + "write resulting index to ", + PARSE_OPT_NONEG, index_output_cb }, + OPT__VERBOSE(&opts.verbose_update), + OPT_GROUP("Merging"), + OPT_SET_INT('m', NULL, &opts.merge, + "perform a merge in addition to a read", 1), + OPT_SET_INT(0, "trivial", &opts.trivial_merges_only, + "3-way merge if no file level merging required", 1), + OPT_SET_INT(0, "aggressive", &opts.aggressive, + "3-way merge in presence of adds and removes", 1), + OPT_SET_INT(0, "reset", &opts.reset, + "same as -m, but discard unmerged entries", 1), + { OPTION_STRING, 0, "prefix", &opts.prefix, "/", + "read the tree into the index under /", + PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP }, + OPT_SET_INT('u', NULL, &opts.update, + "update working tree with merge result", 1), + { OPTION_CALLBACK, 0, "exclude-per-directory", &opts, + "gitignore", + "allow explicitly ignored files to be overwritten", + PARSE_OPT_NONEG, exclude_per_directory_cb }, + OPT_SET_INT('i', NULL, &opts.index_only, + "don't check the working tree after merging", 1), + OPT_END() + }; memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; @@ -49,111 +110,28 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) newfd = hold_locked_index(&lock_file, 1); - for (i = 1; i < argc; i++) { + argc = parse_options(argc, argv, unused_prefix, read_tree_options, + read_tree_usage, 0); + + if (read_cache_unmerged() && (opts.prefix || opts.merge)) + die("You need to resolve your current index first"); + + prefix_set = opts.prefix ? 1 : 0; + if (1 < opts.merge + opts.reset + prefix_set) + die("Which one? -m, --reset, or --prefix?"); + stage = opts.merge = (opts.reset || opts.merge || prefix_set); + + for (i = 0; i < argc; i++) { const char *arg = argv[i]; - /* "-u" means "update", meaning that a merge will update - * the working tree. - */ - if (!strcmp(arg, "-u")) { - opts.update = 1; - continue; - } - - if (!strcmp(arg, "-v")) { - opts.verbose_update = 1; - continue; - } - - /* "-i" means "index only", meaning that a merge will - * not even look at the working tree. - */ - if (!strcmp(arg, "-i")) { - opts.index_only = 1; - continue; - } - - if (!prefixcmp(arg, "--index-output=")) { - set_alternate_index_output(arg + 15); - continue; - } - - /* "--prefix=/" means keep the current index - * entries and put the entries from the tree under the - * given subdirectory. - */ - if (!prefixcmp(arg, "--prefix=")) { - if (stage || opts.merge || opts.prefix) - usage(read_tree_usage); - opts.prefix = arg + 9; - opts.merge = 1; - stage = 1; - if (read_cache_unmerged()) - die("you need to resolve your current index first"); - continue; - } - - /* This differs from "-m" in that we'll silently ignore - * unmerged entries and overwrite working tree files that - * correspond to them. - */ - if (!strcmp(arg, "--reset")) { - if (stage || opts.merge || opts.prefix) - usage(read_tree_usage); - opts.reset = 1; - opts.merge = 1; - stage = 1; - read_cache_unmerged(); - continue; - } - - if (!strcmp(arg, "--trivial")) { - opts.trivial_merges_only = 1; - continue; - } - - if (!strcmp(arg, "--aggressive")) { - opts.aggressive = 1; - continue; - } - - /* "-m" stands for "merge", meaning we start in stage 1 */ - if (!strcmp(arg, "-m")) { - if (stage || opts.merge || opts.prefix) - usage(read_tree_usage); - if (read_cache_unmerged()) - die("you need to resolve your current index first"); - stage = 1; - opts.merge = 1; - continue; - } - - if (!prefixcmp(arg, "--exclude-per-directory=")) { - struct dir_struct *dir; - - if (opts.dir) - die("more than one --exclude-per-directory are given."); - - dir = xcalloc(1, sizeof(*opts.dir)); - dir->flags |= DIR_SHOW_IGNORED; - dir->exclude_per_dir = arg + 24; - opts.dir = dir; - /* We do not need to nor want to do read-directory - * here; we are merely interested in reusing the - * per directory ignore stack mechanism. - */ - continue; - } - - if (1 < opts.index_only + opts.update) - die("-u and -i at the same time makes no sense"); - if (get_sha1(arg, sha1)) die("Not a valid object name %s", arg); if (list_tree(sha1) < 0) die("failed to unpack tree object %s", arg); stage++; } + if (1 < opts.index_only + opts.update) + die("-u and -i at the same time makes no sense"); if ((opts.update||opts.index_only) && !opts.merge) die("%s is meaningless without -m, --reset, or --prefix", opts.update ? "-u" : "-i"); diff --git a/unpack-trees.h b/unpack-trees.h index 1e0e2325f1..d19df44f40 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -17,18 +17,18 @@ struct unpack_trees_error_msgs { }; struct unpack_trees_options { - unsigned int reset:1, - merge:1, - update:1, - index_only:1, - nontrivial_merge:1, - trivial_merges_only:1, - verbose_update:1, - aggressive:1, - skip_unmerged:1, - initial_checkout:1, - diff_index_cached:1, - gently:1; + unsigned int reset, + merge, + update, + index_only, + nontrivial_merge, + trivial_merges_only, + verbose_update, + aggressive, + skip_unmerged, + initial_checkout, + diff_index_cached, + gently; const char *prefix; int pos; struct dir_struct *dir; From 303e7c48eae7e140a9612ff1f9b5a95ca80b65c4 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 4 Jul 2009 21:26:38 +0200 Subject: [PATCH 012/133] MinGW: simplify waitpid() emulation macros Windows does not have signals. At least they cannot be diagnosed by the parent process; all that the parent process can observe is the exit code. This also adds a dummy definition of WTERMSIG. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/mingw.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compat/mingw.h b/compat/mingw.h index c1859c5480..948de66eb5 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -17,9 +17,10 @@ typedef int pid_t; #define S_IROTH 0 #define S_IXOTH 0 -#define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */ +#define WIFEXITED(x) 1 +#define WIFSIGNALED(x) 0 #define WEXITSTATUS(x) ((x) & 0xff) -#define WIFSIGNALED(x) ((unsigned)(x) > 259) +#define WTERMSIG(x) SIGTERM #define SIGHUP 1 #define SIGQUIT 3 From 5709e0363a891b72eb9e9756d7fb121d9bf6a7c7 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 4 Jul 2009 21:26:39 +0200 Subject: [PATCH 013/133] run_command: return exit code as positive value As a general guideline, functions in git's code return zero to indicate success and negative values to indicate failure. The run_command family of functions followed this guideline. But there are actually two different kinds of failure: - failures of system calls; - non-zero exit code of the program that was run. Usually, a non-zero exit code of the program is a failure and means a failure to the caller. Except that sometimes it does not. For example, the exit code of merge programs (e.g. external merge drivers) conveys information about how the merge failed, and not all exit calls are actually failures. Furthermore, the return value of run_command is sometimes used as exit code by the caller. This change arranges that the exit code of the program is returned as a positive value, which can now be regarded as the "result" of the function. System call failures continue to be reported as negative values. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- builtin-merge.c | 2 +- builtin-receive-pack.c | 4 ++-- convert.c | 2 +- git.c | 4 ++-- ll-merge.c | 4 ---- run-command.c | 9 +-------- run-command.h | 1 - 7 files changed, 7 insertions(+), 19 deletions(-) diff --git a/builtin-merge.c b/builtin-merge.c index af9adab300..96ecaf4e48 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -594,7 +594,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, discard_cache(); if (read_cache() < 0) die("failed to read the cache"); - return -ret; + return ret; } } diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 6ec1d056e6..6235903552 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -143,8 +143,8 @@ static int run_status(int code, const char *cmd_name) case -ERR_RUN_COMMAND_WAITPID_NOEXIT: return error("%s died strangely", cmd_name); default: - error("%s exited with error code %d", cmd_name, -code); - return -code; + error("%s exited with error code %d", cmd_name, code); + return code; } } diff --git a/convert.c b/convert.c index 1816e977b7..491e7141b4 100644 --- a/convert.c +++ b/convert.c @@ -267,7 +267,7 @@ static int filter_buffer(int fd, void *data) status = finish_command(&child_process); if (status) - error("external filter %s failed %d", params->cmd, -status); + error("external filter %s failed %d", params->cmd, status); return (write_err || status); } diff --git a/git.c b/git.c index 65ed733fda..d223eab62f 100644 --- a/git.c +++ b/git.c @@ -418,9 +418,9 @@ static void execv_dashed_external(const char **argv) */ status = run_command_v_opt(argv, 0); if (status != -ERR_RUN_COMMAND_EXEC) { - if (IS_RUN_COMMAND_ERR(status)) + if (status < 0) die("unable to run '%s'", argv[0]); - exit(-status); + exit(status); } errno = ENOENT; /* as if we called execvp */ diff --git a/ll-merge.c b/ll-merge.c index a2c13c4c08..31c74578f6 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -192,10 +192,6 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, args[2] = cmd.buf; status = run_command_v_opt(args, 0); - if (status < -ERR_RUN_COMMAND_FORK) - ; /* failure in run-command */ - else - status = -status; fd = open(temp[1], O_RDONLY); if (fd < 0) goto bad; diff --git a/run-command.c b/run-command.c index eb2efc3307..a4e309eeb9 100644 --- a/run-command.c +++ b/run-command.c @@ -241,14 +241,7 @@ static int wait_or_whine(pid_t pid) if (!WIFEXITED(status)) return -ERR_RUN_COMMAND_WAITPID_NOEXIT; code = WEXITSTATUS(status); - switch (code) { - case 127: - return -ERR_RUN_COMMAND_EXEC; - case 0: - return 0; - default: - return -code; - } + return code == 127 ? -ERR_RUN_COMMAND_EXEC : code; } } diff --git a/run-command.h b/run-command.h index e345502843..0211e1d471 100644 --- a/run-command.h +++ b/run-command.h @@ -10,7 +10,6 @@ enum { ERR_RUN_COMMAND_WAITPID_SIGNAL, ERR_RUN_COMMAND_WAITPID_NOEXIT, }; -#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK) struct child_process { const char **argv; From 0ac77ec3150f43a5c2a6b1e47e9db5aafe53fb72 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 4 Jul 2009 21:26:40 +0200 Subject: [PATCH 014/133] run_command: report system call errors instead of returning error codes The motivation for this change is that system call failures are serious errors that should be reported to the user, but only few callers took the burden to decode the error codes that the functions returned into error messages. If at all, then only an unspecific error message was given. A prominent example is this: $ git upload-pack . | : fatal: unable to run 'git-upload-pack' In this example, git-upload-pack, the external command invoked through the git wrapper, dies due to SIGPIPE, but the git wrapper does not bother to report the real cause. In fact, this very error message is copied to the syslog if git-daemon's client aborts the connection early. With this change, system call failures are reported immediately after the failure and only a generic failure code is returned to the caller. In the above example the error is now to the point: $ git upload-pack . | : error: git-upload-pack died of signal Note that there is no error report if the invoked program terminated with a non-zero exit code, because it is reasonable to expect that the invoked program has already reported an error. (But many run_command call sites nevertheless write a generic error message.) There was one special return code that was used to identify the case where run_command failed because the requested program could not be exec'd. This special case is now treated like a system call failure with errno set to ENOENT. No error is reported in this case, because the call site in git.c expects this as a normal result. Therefore, the callers that carefully decoded the return value still check for this condition. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- builtin-receive-pack.c | 22 ++-------- git.c | 6 +-- run-command.c | 85 ++++++++++++++++++++---------------- run-command.h | 10 ----- t/t5530-upload-pack-error.sh | 5 ++- transport.c | 12 +---- 6 files changed, 56 insertions(+), 84 deletions(-) diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 6235903552..1dcdb1a514 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -125,27 +125,11 @@ static const char post_receive_hook[] = "hooks/post-receive"; static int run_status(int code, const char *cmd_name) { - switch (code) { - case 0: - return 0; - case -ERR_RUN_COMMAND_FORK: - return error("fork of %s failed", cmd_name); - case -ERR_RUN_COMMAND_EXEC: + if (code < 0 && errno == ENOENT) return error("execute of %s failed", cmd_name); - case -ERR_RUN_COMMAND_PIPE: - return error("pipe failed"); - case -ERR_RUN_COMMAND_WAITPID: - return error("waitpid failed"); - case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - return error("waitpid is confused"); - case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - return error("%s died of signal", cmd_name); - case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - return error("%s died strangely", cmd_name); - default: + else if (code > 0) error("%s exited with error code %d", cmd_name, code); - return code; - } + return code; } static int run_receive_hook(const char *hook_name) diff --git a/git.c b/git.c index d223eab62f..03726eee5e 100644 --- a/git.c +++ b/git.c @@ -417,12 +417,8 @@ static void execv_dashed_external(const char **argv) * OK to return. Otherwise, we just pass along the status code. */ status = run_command_v_opt(argv, 0); - if (status != -ERR_RUN_COMMAND_EXEC) { - if (status < 0) - die("unable to run '%s'", argv[0]); + if (status >= 0 || errno != ENOENT) exit(status); - } - errno = ENOENT; /* as if we called execvp */ argv[0] = tmp; diff --git a/run-command.c b/run-command.c index a4e309eeb9..e273c6c451 100644 --- a/run-command.c +++ b/run-command.c @@ -19,6 +19,7 @@ int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; + int failed_errno; /* * In case of errors we must keep the promise to close FDs @@ -28,9 +29,10 @@ int start_command(struct child_process *cmd) need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) { + failed_errno = errno; if (cmd->out > 0) close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; + goto fail_pipe; } cmd->in = fdin[1]; } @@ -40,11 +42,12 @@ int start_command(struct child_process *cmd) && cmd->out < 0; if (need_out) { if (pipe(fdout) < 0) { + failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); - return -ERR_RUN_COMMAND_PIPE; + goto fail_pipe; } cmd->out = fdout[0]; } @@ -52,6 +55,7 @@ int start_command(struct child_process *cmd) need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { + failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) @@ -60,7 +64,11 @@ int start_command(struct child_process *cmd) close_pair(fdout); else if (cmd->out) close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; +fail_pipe: + error("cannot create pipe for %s: %s", + cmd->argv[0], strerror(failed_errno)); + errno = failed_errno; + return -1; } cmd->err = fderr[0]; } @@ -122,6 +130,9 @@ int start_command(struct child_process *cmd) strerror(errno)); exit(127); } + if (cmd->pid < 0) + error("cannot fork() for %s: %s", cmd->argv[0], + strerror(failed_errno = errno)); #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ const char **sargv = cmd->argv; @@ -173,6 +184,9 @@ int start_command(struct child_process *cmd) } cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env); + failed_errno = errno; + if (cmd->pid < 0 && errno != ENOENT) + error("cannot spawn %s: %s", cmd->argv[0], strerror(errno)); if (cmd->env) free_environ(env); @@ -189,7 +203,6 @@ int start_command(struct child_process *cmd) #endif if (cmd->pid < 0) { - int err = errno; if (need_in) close_pair(fdin); else if (cmd->in) @@ -200,9 +213,8 @@ int start_command(struct child_process *cmd) close(cmd->out); if (need_err) close_pair(fderr); - return err == ENOENT ? - -ERR_RUN_COMMAND_EXEC : - -ERR_RUN_COMMAND_FORK; + errno = failed_errno; + return -1; } if (need_in) @@ -221,33 +233,41 @@ int start_command(struct child_process *cmd) return 0; } -static int wait_or_whine(pid_t pid) +static int wait_or_whine(pid_t pid, const char *argv0) { - for (;;) { - int status, code; - pid_t waiting = waitpid(pid, &status, 0); + int status, code = -1; + pid_t waiting; + int failed_errno = 0; - if (waiting < 0) { - if (errno == EINTR) - continue; - error("waitpid failed (%s)", strerror(errno)); - return -ERR_RUN_COMMAND_WAITPID; - } - if (waiting != pid) - return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; - if (WIFSIGNALED(status)) - return -ERR_RUN_COMMAND_WAITPID_SIGNAL; + while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR) + ; /* nothing */ - if (!WIFEXITED(status)) - return -ERR_RUN_COMMAND_WAITPID_NOEXIT; + if (waiting < 0) { + failed_errno = errno; + error("waitpid for %s failed: %s", argv0, strerror(errno)); + } else if (waiting != pid) { + error("waitpid is confused (%s)", argv0); + } else if (WIFSIGNALED(status)) { + error("%s died of signal", argv0); + } else if (WIFEXITED(status)) { code = WEXITSTATUS(status); - return code == 127 ? -ERR_RUN_COMMAND_EXEC : code; + /* + * Convert special exit code when execvp failed. + */ + if (code == 127) { + code = -1; + failed_errno = ENOENT; + } + } else { + error("waitpid is confused (%s)", argv0); } + errno = failed_errno; + return code; } int finish_command(struct child_process *cmd) { - return wait_or_whine(cmd->pid); + return wait_or_whine(cmd->pid, cmd->argv[0]); } int run_command(struct child_process *cmd) @@ -331,10 +351,7 @@ int start_async(struct async *async) int finish_async(struct async *async) { #ifndef __MINGW32__ - int ret = 0; - - if (wait_or_whine(async->pid)) - ret = error("waitpid (async) failed"); + int ret = wait_or_whine(async->pid, "child process"); #else DWORD ret = 0; if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0) @@ -378,15 +395,7 @@ int run_hook(const char *index_file, const char *name, ...) hook.env = env; } - ret = start_command(&hook); + ret = run_command(&hook); free(argv); - if (ret) { - warning("Could not spawn %s", argv[0]); - return ret; - } - ret = finish_command(&hook); - if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL) - warning("%s exited due to uncaught signal", argv[0]); - return ret; } diff --git a/run-command.h b/run-command.h index 0211e1d471..ac6c09c896 100644 --- a/run-command.h +++ b/run-command.h @@ -1,16 +1,6 @@ #ifndef RUN_COMMAND_H #define RUN_COMMAND_H -enum { - ERR_RUN_COMMAND_FORK = 10000, - ERR_RUN_COMMAND_EXEC, - ERR_RUN_COMMAND_PIPE, - ERR_RUN_COMMAND_WAITPID, - ERR_RUN_COMMAND_WAITPID_WRONG_PID, - ERR_RUN_COMMAND_WAITPID_SIGNAL, - ERR_RUN_COMMAND_WAITPID_NOEXIT, -}; - struct child_process { const char **argv; pid_t pid; diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh index f5102b902a..82ca3003dd 100755 --- a/t/t5530-upload-pack-error.sh +++ b/t/t5530-upload-pack-error.sh @@ -53,7 +53,10 @@ 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 + # pack-objects survived + grep "Total.*, reused" output.err && + # but there was an error, which must have been in rev-list + grep "bad tree object" output.err ' test_expect_success 'create empty repository' ' diff --git a/transport.c b/transport.c index 501a77b241..0885801a06 100644 --- a/transport.c +++ b/transport.c @@ -417,18 +417,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons argv[argc++] = *refspec++; argv[argc] = NULL; err = run_command_v_opt(argv, RUN_GIT_CMD); - switch (err) { - case -ERR_RUN_COMMAND_FORK: - error("unable to fork for %s", argv[0]); - case -ERR_RUN_COMMAND_EXEC: + if (err < 0 && errno == ENOENT) error("unable to exec %s", argv[0]); - break; - case -ERR_RUN_COMMAND_WAITPID: - case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - error("%s died with strange error", argv[0]); - } return !!err; } From b99d5f40d6a5cba7d7cd7599063b3cd78aa4d219 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 4 Jul 2009 21:26:41 +0200 Subject: [PATCH 015/133] run_command: encode deadly signal number in the return value We now write the signal number in the error message if the program terminated by a signal. The negative return value is constructed such that after truncation to 8 bits it looks like a POSIX shell's $?: $ echo 0000 | { git upload-pack .; echo $? >&2; } | : error: git-upload-pack died of signal 13 141 Previously, the exit code was 255 instead of 141. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- run-command.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index e273c6c451..30c2b3dd88 100644 --- a/run-command.c +++ b/run-command.c @@ -248,7 +248,14 @@ static int wait_or_whine(pid_t pid, const char *argv0) } else if (waiting != pid) { error("waitpid is confused (%s)", argv0); } else if (WIFSIGNALED(status)) { - error("%s died of signal", argv0); + code = WTERMSIG(status); + error("%s died of signal %d", argv0, code); + /* + * This return value is chosen so that code & 0xff + * mimics the exit code that a POSIX shell would report for + * a program that died from this signal. + */ + code -= 128; } else if (WIFEXITED(status)) { code = WEXITSTATUS(status); /* From c024beb56da679839d61f352d088b9a86823233a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 4 Jul 2009 21:26:42 +0200 Subject: [PATCH 016/133] run_command: report failure to execute the program, but optionally don't In the case where a program was not found, it was still the task of the caller to report an error to the user. Usually, this is an interesting case but only few callers actually reported a specific error (though many call sites report a generic error message regardless of the cause). With this change the error is reported by run_command, but since there is one call site in git.c that does not want that, an option is added to struct child_process, which is used to turn the error off. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- builtin-receive-pack.c | 4 +--- git.c | 2 +- run-command.c | 12 ++++++++---- run-command.h | 2 ++ transport.c | 6 +----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 1dcdb1a514..c85507b122 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -125,9 +125,7 @@ static const char post_receive_hook[] = "hooks/post-receive"; static int run_status(int code, const char *cmd_name) { - if (code < 0 && errno == ENOENT) - return error("execute of %s failed", cmd_name); - else if (code > 0) + if (code > 0) error("%s exited with error code %d", cmd_name, code); return code; } diff --git a/git.c b/git.c index 03726eee5e..18240280e8 100644 --- a/git.c +++ b/git.c @@ -416,7 +416,7 @@ static void execv_dashed_external(const char **argv) * if we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code. */ - status = run_command_v_opt(argv, 0); + status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE); if (status >= 0 || errno != ENOENT) exit(status); diff --git a/run-command.c b/run-command.c index 30c2b3dd88..b613bddc71 100644 --- a/run-command.c +++ b/run-command.c @@ -185,7 +185,7 @@ fail_pipe: cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env); failed_errno = errno; - if (cmd->pid < 0 && errno != ENOENT) + if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT)) error("cannot spawn %s: %s", cmd->argv[0], strerror(errno)); if (cmd->env) @@ -233,7 +233,7 @@ fail_pipe: return 0; } -static int wait_or_whine(pid_t pid, const char *argv0) +static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure) { int status, code = -1; pid_t waiting; @@ -264,6 +264,9 @@ static int wait_or_whine(pid_t pid, const char *argv0) if (code == 127) { code = -1; failed_errno = ENOENT; + if (!silent_exec_failure) + error("cannot run %s: %s", argv0, + strerror(ENOENT)); } } else { error("waitpid is confused (%s)", argv0); @@ -274,7 +277,7 @@ static int wait_or_whine(pid_t pid, const char *argv0) int finish_command(struct child_process *cmd) { - return wait_or_whine(cmd->pid, cmd->argv[0]); + return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure); } int run_command(struct child_process *cmd) @@ -294,6 +297,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd, cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0; cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; + cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0; } int run_command_v_opt(const char **argv, int opt) @@ -358,7 +362,7 @@ int start_async(struct async *async) int finish_async(struct async *async) { #ifndef __MINGW32__ - int ret = wait_or_whine(async->pid, "child process"); + int ret = wait_or_whine(async->pid, "child process", 0); #else DWORD ret = 0; if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0) diff --git a/run-command.h b/run-command.h index ac6c09c896..0c00b25ff2 100644 --- a/run-command.h +++ b/run-command.h @@ -31,6 +31,7 @@ struct child_process { unsigned no_stdout:1; unsigned no_stderr:1; unsigned git_cmd:1; /* if this is to be git sub-command */ + unsigned silent_exec_failure:1; unsigned stdout_to_stderr:1; void (*preexec_cb)(void); }; @@ -44,6 +45,7 @@ extern int run_hook(const char *index_file, const char *name, ...); #define RUN_COMMAND_NO_STDIN 1 #define RUN_GIT_CMD 2 /*If this is to be git sub-command */ #define RUN_COMMAND_STDOUT_TO_STDERR 4 +#define RUN_SILENT_EXEC_FAILURE 8 int run_command_v_opt(const char **argv, int opt); /* diff --git a/transport.c b/transport.c index 0885801a06..802ce7f233 100644 --- a/transport.c +++ b/transport.c @@ -396,7 +396,6 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons { const char **argv; int argc; - int err; if (flags & TRANSPORT_PUSH_MIRROR) return error("http transport does not support mirror mode"); @@ -416,10 +415,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons while (refspec_nr--) argv[argc++] = *refspec++; argv[argc] = NULL; - err = run_command_v_opt(argv, RUN_GIT_CMD); - if (err < 0 && errno == ENOENT) - error("unable to exec %s", argv[0]); - return !!err; + return !!run_command_v_opt(argv, RUN_GIT_CMD); } static struct ref *get_refs_via_curl(struct transport *transport, int for_push) From 90e41a89caa464a84e13cbc9378b0348a61f713b Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 4 Jul 2009 21:26:43 +0200 Subject: [PATCH 017/133] receive-pack: remove unnecessary run_status report The function run_status was used to report failures after a hook was run. By now, the only thing that the function itself reported was the exit code of the hook (if it was non-zero). But this is redundant because it can be expected that the hook itself will have reported a suitable error. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- builtin-receive-pack.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index c85507b122..b771fe9b20 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -123,13 +123,6 @@ static struct command *commands; static const char pre_receive_hook[] = "hooks/pre-receive"; static const char post_receive_hook[] = "hooks/post-receive"; -static int run_status(int code, const char *cmd_name) -{ - if (code > 0) - error("%s exited with error code %d", cmd_name, code); - return code; -} - static int run_receive_hook(const char *hook_name) { static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4]; @@ -156,7 +149,7 @@ static int run_receive_hook(const char *hook_name) code = start_command(&proc); if (code) - return run_status(code, hook_name); + return code; for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) { size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", @@ -168,7 +161,7 @@ static int run_receive_hook(const char *hook_name) } } close(proc.in); - return run_status(finish_command(&proc), hook_name); + return finish_command(&proc); } static int run_update_hook(struct command *cmd) @@ -185,9 +178,8 @@ static int run_update_hook(struct command *cmd) argv[3] = sha1_to_hex(cmd->new_sha1); argv[4] = NULL; - return run_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | - RUN_COMMAND_STDOUT_TO_STDERR), - update_hook); + return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | + RUN_COMMAND_STDOUT_TO_STDERR); } static int is_ref_checked_out(const char *ref) @@ -401,7 +393,6 @@ static void run_update_post_hook(struct command *cmd) argv[argc] = NULL; status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_COMMAND_STDOUT_TO_STDERR); - run_status(status, update_post_hook); } static void execute_commands(const char *unpacker_error) @@ -519,7 +510,6 @@ static const char *unpack(void) code = run_command_v_opt(unpacker, RUN_GIT_CMD); if (!code) return NULL; - run_status(code, unpacker[0]); return "unpack-objects abnormal exit"; } else { const char *keeper[7]; @@ -545,7 +535,6 @@ static const char *unpack(void) ip.git_cmd = 1; status = start_command(&ip); if (status) { - run_status(status, keeper[0]); return "index-pack fork failed"; } pack_lockfile = index_pack_lockfile(ip.out); @@ -555,7 +544,6 @@ static const char *unpack(void) reprepare_packed_git(); return NULL; } - run_status(status, keeper[0]); return "index-pack abnormal exit"; } } From 404d42e5efe05ef8dd40713e00001746a060b66b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 7 Jul 2009 22:15:38 -0700 Subject: [PATCH 018/133] write-tree: migrate to parse-options A check for extra options has been dropped, it could never be triggered in the original code as the usage message would be printed instead. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-write-tree.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/builtin-write-tree.c b/builtin-write-tree.c index 3a24ce8157..b223af416f 100644 --- a/builtin-write-tree.c +++ b/builtin-write-tree.c @@ -7,9 +7,12 @@ #include "cache.h" #include "tree.h" #include "cache-tree.h" +#include "parse-options.h" -static const char write_tree_usage[] = -"git write-tree [--missing-ok] [--prefix=/]"; +static const char * const write_tree_usage[] = { + "git write-tree [--missing-ok] [--prefix=/]", + NULL +}; int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) { @@ -17,27 +20,22 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) const char *prefix = NULL; unsigned char sha1[20]; const char *me = "git-write-tree"; + struct option write_tree_options[] = { + OPT_BIT(0, "missing-ok", &flags, "allow missing objects", + WRITE_TREE_MISSING_OK), + { OPTION_STRING, 0, "prefix", &prefix, "/", + "write tree object for a subdirectory " , + PARSE_OPT_LITERAL_ARGHELP }, + { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL, + "only useful for debugging", + PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL, + WRITE_TREE_IGNORE_CACHE_TREE }, + OPT_END() + }; git_config(git_default_config, NULL); - while (1 < argc) { - const char *arg = argv[1]; - if (!strcmp(arg, "--missing-ok")) - flags |= WRITE_TREE_MISSING_OK; - else if (!prefixcmp(arg, "--prefix=")) - prefix = arg + 9; - else if (!prefixcmp(arg, "--ignore-cache-tree")) - /* - * This is only useful for debugging, so I - * do not bother documenting it. - */ - flags |= WRITE_TREE_IGNORE_CACHE_TREE; - else - usage(write_tree_usage); - argc--; argv++; - } - - if (argc > 2) - die("too many options"); + argc = parse_options(argc, argv, unused_prefix, write_tree_options, + write_tree_usage, 0); ret = write_cache_as_tree(sha1, flags, prefix); switch (ret) { From 4855b2a220cacb01b77a2bf431b5b176bfa1d732 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 7 Jul 2009 22:15:39 -0700 Subject: [PATCH 019/133] verify-tag: migrate to parse-options Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin-verify-tag.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index 7f7fda42f9..9f482c29f5 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -10,9 +10,12 @@ #include "tag.h" #include "run-command.h" #include +#include "parse-options.h" -static const char builtin_verify_tag_usage[] = - "git verify-tag [-v|--verbose] ..."; +static const char * const verify_tag_usage[] = { + "git verify-tag [-v|--verbose] ...", + NULL +}; #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" @@ -89,17 +92,17 @@ static int verify_tag(const char *name, int verbose) int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; + const struct option verify_tag_options[] = { + OPT__VERBOSE(&verbose), + OPT_END() + }; git_config(git_default_config, NULL); - if (argc > 1 && - (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) { - verbose = 1; - i++; - } - + argc = parse_options(argc, argv, prefix, verify_tag_options, + verify_tag_usage, PARSE_OPT_KEEP_ARGV0); if (argc <= i) - usage(builtin_verify_tag_usage); + usage_with_options(verify_tag_usage, verify_tag_options); /* sometimes the program was terminated because this signal * was received in the process of writing the gpg input: */ From c9c3c6781c5b97c37b3ce16af7ea9bc613413c7e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 7 Jul 2009 22:15:40 -0700 Subject: [PATCH 020/133] verify-pack: migrate to parse-options OPT__VERBOSE introduces the long option (--verbose) in addition to the already present short option (-v), so document this new addition. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- Documentation/git-verify-pack.txt | 3 ++- builtin-verify-pack.c | 40 ++++++++++++++----------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt index c8611632d1..d791a80819 100644 --- a/Documentation/git-verify-pack.txt +++ b/Documentation/git-verify-pack.txt @@ -8,7 +8,7 @@ git-verify-pack - Validate packed git archive files SYNOPSIS -------- -'git verify-pack' [-v] [--] .idx ... +'git verify-pack' [-v|--verbose] [--] .idx ... DESCRIPTION @@ -23,6 +23,7 @@ OPTIONS The idx files to verify. -v:: +--verbose:: After verifying the pack, show list of objects contained in the pack. \--:: diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c index 0ee0a9af60..ebd6dff940 100644 --- a/builtin-verify-pack.c +++ b/builtin-verify-pack.c @@ -2,6 +2,7 @@ #include "cache.h" #include "pack.h" #include "pack-revindex.h" +#include "parse-options.h" #define MAX_CHAIN 50 @@ -107,36 +108,31 @@ static int verify_one_pack(const char *path, int verbose) return err; } -static const char verify_pack_usage[] = "git verify-pack [-v] ..."; +static const char * const verify_pack_usage[] = { + "git verify-pack [-v|--verbose] ...", + NULL +}; int cmd_verify_pack(int argc, const char **argv, const char *prefix) { int err = 0; int verbose = 0; - int no_more_options = 0; - int nothing_done = 1; + int i; + const struct option verify_pack_options[] = { + OPT__VERBOSE(&verbose), + OPT_END() + }; git_config(git_default_config, NULL); - while (1 < argc) { - if (!no_more_options && argv[1][0] == '-') { - if (!strcmp("-v", argv[1])) - verbose = 1; - else if (!strcmp("--", argv[1])) - no_more_options = 1; - else - usage(verify_pack_usage); - } - else { - if (verify_one_pack(argv[1], verbose)) - err = 1; - discard_revindex(); - nothing_done = 0; - } - argc--; argv++; + argc = parse_options(argc, argv, prefix, verify_pack_options, + verify_pack_usage, 0); + if (argc < 1) + usage_with_options(verify_pack_usage, verify_pack_options); + for (i = 0; i < argc; i++) { + if (verify_one_pack(argv[i], verbose)) + err = 1; + discard_revindex(); } - if (nothing_done) - usage(verify_pack_usage); - return err; } From 7cfe0c9802d6d6c915cba91b73a591622dbcbc93 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 7 Jul 2009 22:15:41 -0700 Subject: [PATCH 021/133] prune-packed: migrate to parse-options Add long options for dry run and quiet to be more consistent with the rest of git. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- Documentation/git-prune-packed.txt | 4 +++- builtin-prune-packed.c | 29 ++++++++++++----------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt index b5f26cee13..abfc6b6ead 100644 --- a/Documentation/git-prune-packed.txt +++ b/Documentation/git-prune-packed.txt @@ -8,7 +8,7 @@ git-prune-packed - Remove extra objects that are already in pack files SYNOPSIS -------- -'git prune-packed' [-n] [-q] +'git prune-packed' [-n|--dry-run] [-q|--quiet] DESCRIPTION @@ -28,10 +28,12 @@ disk storage, etc. OPTIONS ------- -n:: +--dry-run:: Don't actually remove any objects, only show those that would have been removed. -q:: +--quiet:: Squelch the progress indicator. Author diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 00590b1c3c..be99eb0ac4 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "progress.h" +#include "parse-options.h" -static const char prune_packed_usage[] = -"git prune-packed [-n] [-q]"; +static const char * const prune_packed_usage[] = { + "git prune-packed [-n|--dry-run] [-q|--quiet]", + NULL +}; #define DRY_RUN 01 #define VERBOSE 02 @@ -68,24 +71,16 @@ void prune_packed_objects(int opts) int cmd_prune_packed(int argc, const char **argv, const char *prefix) { - int i; int opts = VERBOSE; + const struct option prune_packed_options[] = { + OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN), + OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE), + OPT_END() + }; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, prefix, prune_packed_options, + prune_packed_usage, 0); - if (*arg == '-') { - if (!strcmp(arg, "-n")) - opts |= DRY_RUN; - else if (!strcmp(arg, "-q")) - opts &= ~VERBOSE; - else - usage(prune_packed_usage); - continue; - } - /* Handle arguments here .. */ - usage(prune_packed_usage); - } prune_packed_objects(opts); return 0; } From c4593faf2df8f9c5e8ac409e40be991294a73e54 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 10 Jul 2009 18:38:08 -0700 Subject: [PATCH 022/133] apply: notice creation/removal patches produced by GNU diff Unified context patch generated by GNU diff has UNIX epoch timestamp on the side that does not exist when the patch is about a creation or a deletion event. Notice this convention when reading a non-git diff. Signed-off-by: Junio C Hamano --- builtin-apply.c | 82 +++++++++++++++++++++++++++++++++- t/t4132-apply-removal.sh | 95 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 1 deletion(-) create mode 100755 t/t4132-apply-removal.sh diff --git a/builtin-apply.c b/builtin-apply.c index dc0ff5e08a..39dc96ae02 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -457,6 +457,76 @@ static int guess_p_value(const char *nameline) return val; } +/* + * Does the ---/+++ line has the POSIX timestamp after the last HT? + * GNU diff puts epoch there to signal a creation/deletion event. Is + * this such a timestamp? + */ +static int has_epoch_timestamp(const char *nameline) +{ + /* + * We are only interested in epoch timestamp; any non-zero + * fraction cannot be one, hence "(\.0+)?" in the regexp below. + * For the same reason, the date must be either 1969-12-31 or + * 1970-01-01, and the seconds part must be "00". + */ + const char stamp_regexp[] = + "^(1969-12-31|1970-01-01)" + " " + "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?" + " " + "([-+][0-2][0-9][0-5][0-9])\n"; + const char *timestamp = NULL, *cp; + static regex_t *stamp; + regmatch_t m[10]; + int zoneoffset; + int hourminute; + int status; + + for (cp = nameline; *cp != '\n'; cp++) { + if (*cp == '\t') + timestamp = cp + 1; + } + if (!timestamp) + return 0; + if (!stamp) { + stamp = xmalloc(sizeof(*stamp)); + if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) { + warning("Cannot prepare timestamp regexp %s", + stamp_regexp); + return 0; + } + } + + status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0); + if (status) { + if (status != REG_NOMATCH) + warning("regexec returned %d for input: %s", + status, timestamp); + return 0; + } + + zoneoffset = strtol(timestamp + m[3].rm_so + 1, NULL, 10); + zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100); + if (timestamp[m[3].rm_so] == '-') + zoneoffset = -zoneoffset; + + /* + * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31 + * (west of GMT) or 1970-01-01 (east of GMT) + */ + if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) || + (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10))) + return 0; + + hourminute = (strtol(timestamp + 11, NULL, 10) * 60 + + strtol(timestamp + 14, NULL, 10) - + zoneoffset); + + return ((zoneoffset < 0 && hourminute == 1440) || + (0 <= zoneoffset && !hourminute)); +} + /* * Get the name etc info from the ---/+++ lines of a traditional patch header * @@ -493,7 +563,17 @@ static void parse_traditional_patch(const char *first, const char *second, struc } else { name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB); name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB); - patch->old_name = patch->new_name = name; + if (has_epoch_timestamp(first)) { + patch->is_new = 1; + patch->is_delete = 0; + patch->new_name = name; + } else if (has_epoch_timestamp(second)) { + patch->is_new = 0; + patch->is_delete = 1; + patch->old_name = name; + } else { + patch->old_name = patch->new_name = name; + } } if (!name) die("unable to find filename in patch at line %d", linenr); diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh new file mode 100755 index 0000000000..bb1ffe3b6c --- /dev/null +++ b/t/t4132-apply-removal.sh @@ -0,0 +1,95 @@ +#!/bin/sh +# +# Copyright (c) 2009 Junio C Hamano + +test_description='git-apply notices removal patches generated by GNU diff' + +. ./test-lib.sh + +test_expect_success setup ' + cat <<-EOF >c && + diff -ruN a/file b/file + --- a/file TS0 + +++ b/file TS1 + @@ -0,0 +1 @@ + +something + EOF + + cat <<-EOF >d && + diff -ruN a/file b/file + --- a/file TS0 + +++ b/file TS1 + @@ -1 +0,0 @@ + -something + EOF + + timeWest="1982-09-16 07:00:00.000000000 -0800" && + timeGMT="1982-09-16 15:00:00.000000000 +0000" && + timeEast="1982-09-17 00:00:00.000000000 +0900" && + + epocWest="1969-12-31 16:00:00.000000000 -0800" && + epocGMT="1970-01-01 00:00:00.000000000 +0000" && + epocEast="1970-01-01 09:00:00.000000000 +0900" && + + sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" createWest.patch && + sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" createEast.patch && + sed -e "s/TS0/$epocGMT/" -e "s/TS1/$timeGMT/" createGMT.patch && + + sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" addWest.patch && + sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" addEast.patch && + sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" addGMT.patch && + + sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" emptyWest.patch && + sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" emptyEast.patch && + sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" emptyGMT.patch && + + sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest/" removeWest.patch && + sed -e "s/TS0/$timeEast/" -e "s/TS1/$epocEast/" removeEast.patch && + sed -e "s/TS0/$timeGMT/" -e "s/TS1/$epocGMT/" removeGMT.patch && + + echo something >something && + >empty +' + +for patch in *.patch +do + test_expect_success "test $patch" ' + rm -f file .git/index && + case "$patch" in + create*) + # must be able to create + git apply --index $patch && + test_cmp file something && + # must notice the file is already there + >file && + git add file && + test_must_fail git apply $patch + ;; + add*) + # must be able to create or patch + git apply $patch && + test_cmp file something && + >file && + git apply $patch && + test_cmp file something + ;; + empty*) + # must leave an empty file + cat something >file && + git add file && + git apply --index $patch && + test -f file && + test_cmp empty file + ;; + remove*) + # must remove the file + cat something >file && + git add file && + git apply --index $patch && + ! test -f file + ;; + esac + ' +done + +test_done From 596f91ee70552230636aee5ee6cdabc0082d473a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kiedrowicz?= Date: Sun, 12 Jul 2009 12:24:32 +0200 Subject: [PATCH 023/133] init-db: migrate to parse-options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add missing --bare to init-db synopsis. Signed-off-by: Michał Kiedrowicz Signed-off-by: Junio C Hamano --- Documentation/git-init-db.txt | 2 +- builtin-init-db.c | 49 +++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt index 1fd0ff2610..eba3cb4998 100644 --- a/Documentation/git-init-db.txt +++ b/Documentation/git-init-db.txt @@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository SYNOPSIS -------- -'git init-db' [-q | --quiet] [--template=] [--shared[=]] +'git init-db' [-q | --quiet] [--bare] [--template=] [--shared[=]] DESCRIPTION diff --git a/builtin-init-db.c b/builtin-init-db.c index 4a5600631c..d68f61b9c8 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -6,6 +6,7 @@ #include "cache.h" #include "builtin.h" #include "exec_cmd.h" +#include "parse-options.h" #ifndef DEFAULT_GIT_TEMPLATE_DIR #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates" @@ -370,8 +371,16 @@ static int guess_repository_type(const char *git_dir) return 1; } -static const char init_db_usage[] = -"git init [-q | --quiet] [--bare] [--template=] [--shared[=]]"; +static int shared_callback(const struct option *opt, const char *arg, int unset) +{ + *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP; + return 0; +} + +static const char *const init_db_usage[] = { + "git init [-q | --quiet] [--bare] [--template=] [--shared[=]]", + NULL +}; /* * If you want to, you can share the DB area with any number of branches. @@ -384,25 +393,25 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir; const char *template_dir = NULL; unsigned int flags = 0; - int i; + const struct option init_db_options[] = { + OPT_STRING(0, "template", &template_dir, "template-directory", + "provide the directory from which templates will be used"), + OPT_SET_INT(0, "bare", &is_bare_repository_cfg, + "create a bare repository", 1), + { OPTION_CALLBACK, 0, "shared", &init_shared_repository, + "permissions", + "specify that the git repository is to be shared amongst several users", + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0}, + OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET), + OPT_END() + }; - for (i = 1; i < argc; i++, argv++) { - const char *arg = argv[1]; - if (!prefixcmp(arg, "--template=")) - template_dir = arg+11; - else if (!strcmp(arg, "--bare")) { - static char git_dir[PATH_MAX+1]; - is_bare_repository_cfg = 1; - setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, - sizeof(git_dir)), 0); - } else if (!strcmp(arg, "--shared")) - init_shared_repository = PERM_GROUP; - else if (!prefixcmp(arg, "--shared=")) - init_shared_repository = git_config_perm("arg", arg+9); - else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) - flags |= INIT_DB_QUIET; - else - usage(init_db_usage); + parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); + + if(is_bare_repository_cfg == 1) { + static char git_dir[PATH_MAX+1]; + setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, + sizeof(git_dir)), 0); } if (init_shared_repository != -1) From a418441b4ea2ab69a17636c3020a29394d4c6562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santi=20B=C3=A9jar?= Date: Thu, 16 Jul 2009 02:09:14 +0200 Subject: [PATCH 024/133] t5520-pull: Test for rebased upstream + fetch + pull --rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If your upstream has rebased you can do: git pull --rebase but only if you haven't fetch before. Mark this case as test_expect_failure, in a later patch it will be changed to test_expect_success. Signed-off-by: Santi Béjar Signed-off-by: Junio C Hamano --- t/t5520-pull.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index c5a2e66a09..3ebc886bdc 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -117,6 +117,20 @@ test_expect_success '--rebase with rebased default upstream' ' ' +test_expect_failure 'rebased upstream + fetch + pull --rebase' ' + + git update-ref refs/remotes/me/copy copy-orig && + git reset --hard to-rebase-orig && + git checkout --track -b to-rebase3 me/copy && + git reset --hard to-rebase-orig && + git fetch && + test_must_fail git pull --rebase && + git rebase --abort && + test "conflicting modification" = "$(cat file)" && + test file = "$(cat file2)" + +' + test_expect_success 'pull --rebase dies early with dirty working directory' ' git checkout to-rebase && From d44e71261f91d3cc81293e0976bb40daa8abb583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santi=20B=C3=A9jar?= Date: Sun, 19 Jul 2009 09:45:16 +0200 Subject: [PATCH 025/133] pull: support rebased upstream + fetch + pull --rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You cannot do a "git pull --rebase" with a rebased upstream, if you have already run "git fetch". Try to behave as if the "git fetch" was not run. In other words, find the fork point of the current branch, where the tip of upstream branch used to be, and use it as the upstream parameter of "git rebase". This patch computes the fork point by walking the reflog to find the first commit which is an ancestor of the current branch. Maybe there are smarter ways to compute it, but this is a straight forward implementation. Signed-off-by: Santi Béjar Signed-off-by: Junio C Hamano --- git-pull.sh | 14 +++++++++++--- t/t5520-pull.sh | 5 ++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/git-pull.sh b/git-pull.sh index 4b78a0cd37..0f24182974 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -124,10 +124,18 @@ test true = "$rebase" && { git diff-index --ignore-submodules --cached --quiet HEAD -- || die "refusing to pull with rebase: your working tree is not up-to-date" + oldremoteref= && . git-parse-remote && - reflist="$(get_remote_merge_branch "$@" 2>/dev/null)" && - oldremoteref="$(git rev-parse -q --verify \ - "$reflist")" + remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" && + oldremoteref="$(git rev-parse -q --verify "$remoteref")" && + for reflog in $(git rev-list -g $remoteref 2>/dev/null) + do + if test "$reflog" = "$(git merge-base $reflog $curr_branch)" + then + oldremoteref="$reflog" + break + fi + done } orig_head=$(git rev-parse -q --verify HEAD) git fetch $verbosity --update-head-ok "$@" || exit 1 diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 3ebc886bdc..e78d40242a 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -117,15 +117,14 @@ test_expect_success '--rebase with rebased default upstream' ' ' -test_expect_failure 'rebased upstream + fetch + pull --rebase' ' +test_expect_success 'rebased upstream + fetch + pull --rebase' ' git update-ref refs/remotes/me/copy copy-orig && git reset --hard to-rebase-orig && git checkout --track -b to-rebase3 me/copy && git reset --hard to-rebase-orig && git fetch && - test_must_fail git pull --rebase && - git rebase --abort && + git pull --rebase && test "conflicting modification" = "$(cat file)" && test file = "$(cat file2)" From ca4ca9ed069c8ce13e1a38e79fbaea849754de94 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 17 Jul 2009 19:18:34 -0400 Subject: [PATCH 026/133] show: suppress extra newline when showing annotated tag When showing a tag, our header parsing finishes with the offset pointing to the newline separating the tag header from the tag body. This means that the printed body will always start with a newline. However, we also add an extra newline when printing the tagger information. This leads to an ugly double-newline: $ git show v1.6.3 tag v1.6.3 Tagger: Junio C Hamano Date: Wed May 6 18:16:47 2009 -0700 GIT 1.6.3 -----BEGIN PGP SIGNATURE----- ... This patch removes the extra newline from the end of the tagger headers. This is a better solution than suppressing the separator newline, because it retains the behavior for tags which have no tagger. E.g., "git show v0.99" will continue to look like: $ git show v0.99 tag v0.99 Test-release for wider distribution. ... Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-log.c b/builtin-log.c index 0c2fa0ae2d..b05796d7bc 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -257,7 +257,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev) pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode, git_log_output_encoding ? git_log_output_encoding: git_commit_encoding); - printf("%s\n", out.buf); + printf("%s", out.buf); strbuf_release(&out); } From ae03ee644ec52f2aa2769dfd4929c5ff85c2a899 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 18 Jul 2009 06:14:37 -0400 Subject: [PATCH 027/133] show: add space between multiple items When showing an annotated tag, "git show" will always display the pointed-to object. However, it didn't separate the two with whitespace, making it more difficult to notice where the new object started. For example: $ git tag -m 'my message' foo $ git show foo tag foo Tagger: Jeff King Date: Fri Jul 17 18:46:25 2009 -0400 my message commit 41cabf8fed2694ba33e01d64f9094f2fc5e5805a Author: Jeff King Date: Thu Jul 16 17:31:34 2009 -0400 ... This patch respects and sets the rev.shown_one member to prepend a blank line before showing a second item. We use this member of rev_info instead of a local flag, because the log_tree_commit we call into for showing commits already respects and sets that flag. Meaning that everything will be spaced properly if you intermix commits and tags, like: $ git show v1.6.3 v1.6.2 HEAD In that case, a single blank line will separate the first tag, the commit it points to, the second tag, the commit that one points to, and the final commit. While we're at it, let's also support trees, so that even something as crazy as $ git show HEAD^{tree} HEAD~1^{tree} HEAD will also be spaced in an easy-to-read way. However, we intentionally do _not_ insert blank lines for blobs, so that specifying multiple blobs gives a strict concatenation. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-log.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/builtin-log.c b/builtin-log.c index b05796d7bc..30358166e6 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -329,11 +329,14 @@ int cmd_show(int argc, const char **argv, const char *prefix) case OBJ_TAG: { struct tag *t = (struct tag *)o; + if (rev.shown_one) + putchar('\n'); printf("%stag %s%s\n", diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), t->tag, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); ret = show_object(o->sha1, 1, &rev); + rev.shown_one = 1; if (ret) break; o = parse_object(t->tagged->sha1); @@ -345,12 +348,15 @@ int cmd_show(int argc, const char **argv, const char *prefix) break; } case OBJ_TREE: + if (rev.shown_one) + putchar('\n'); printf("%stree %s%s\n\n", diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), name, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); read_tree_recursive((struct tree *)o, "", 0, 0, NULL, show_tree_object, NULL); + rev.shown_one = 1; break; case OBJ_COMMIT: rev.pending.nr = rev.pending.alloc = 0; From a91f453f641ca9966a438bdd3896656b00423407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kiedrowicz?= Date: Wed, 22 Jul 2009 19:52:15 +0200 Subject: [PATCH 028/133] grep: Add --max-depth option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is useful to grep directories non-recursively, e.g. when one wants to look for all files in the toplevel directory, but not in any subdirectory, or in Documentation/, but not in Documentation/technical/. This patch adds support for --max-depth option to git-grep. If it is given, git-grep descends at most levels of directories below paths specified on the command line. Note that if path specified on command line contains wildcards, this option makes no sense, e.g. $ git grep -l --max-depth 0 GNU -- 'contrib/*' (note the quotes) will search all files in contrib/, even in subdirectories, because '*' matches all files. Documentation updates, bash-completion and simple test cases are also provided. Signed-off-by: Michał Kiedrowicz Signed-off-by: Junio C Hamano --- Documentation/git-grep.txt | 5 +++ builtin-grep.c | 56 +++++++++++++++++++++----- contrib/completion/git-completion.bash | 1 + grep.h | 1 + t/t7002-grep.sh | 51 ++++++++++++++++++++++- 5 files changed, 103 insertions(+), 11 deletions(-) diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index b753c9d76f..8c700200f5 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -17,6 +17,7 @@ SYNOPSIS [-l | --files-with-matches] [-L | --files-without-match] [-z | --null] [-c | --count] [--all-match] + [--max-depth ] [--color | --no-color] [-A ] [-B ] [-C ] [-f ] [-e] @@ -47,6 +48,10 @@ OPTIONS -I:: Don't match the pattern in binary files. +--max-depth :: + For each pathspec given on command line, descend at most + levels of directories. A negative value means no limit. + -w:: --word-regexp:: Match the pattern only at word boundary (either begin at the diff --git a/builtin-grep.c b/builtin-grep.c index f477659100..ad0e0a5385 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -52,26 +52,58 @@ static int grep_config(const char *var, const char *value, void *cb) return git_color_default_config(var, value, cb); } +/* + * Return non-zero if max_depth is negative or path has no more then max_depth + * slashes. + */ +static int accept_subdir(const char *path, int max_depth) +{ + if (max_depth < 0) + return 1; + + while ((path = strchr(path, '/')) != NULL) { + max_depth--; + if (max_depth < 0) + return 0; + path++; + } + return 1; +} + +/* + * Return non-zero if name is a subdirectory of match and is not too deep. + */ +static int is_subdir(const char *name, int namelen, + const char *match, int matchlen, int max_depth) +{ + if (matchlen > namelen || strncmp(name, match, matchlen)) + return 0; + + if (name[matchlen] == '\0') /* exact match */ + return 1; + + if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/') + return accept_subdir(name + matchlen + 1, max_depth); + + return 0; +} + /* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. */ -static int pathspec_matches(const char **paths, const char *name) +static int pathspec_matches(const char **paths, const char *name, int max_depth) { int namelen, i; if (!paths || !*paths) - return 1; + return accept_subdir(name, max_depth); namelen = strlen(name); for (i = 0; paths[i]; i++) { const char *match = paths[i]; int matchlen = strlen(match); const char *cp, *meta; - if (!matchlen || - ((matchlen <= namelen) && - !strncmp(name, match, matchlen) && - (match[matchlen-1] == '/' || - name[matchlen] == '\0' || name[matchlen] == '/'))) + if (is_subdir(name, namelen, match, matchlen, max_depth)) return 1; if (!fnmatch(match, name, 0)) return 1; @@ -421,7 +453,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) int kept; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(paths, ce->name)) + if (!pathspec_matches(paths, ce->name, opt->max_depth)) continue; name = ce->name; if (name[0] == '-') { @@ -478,7 +510,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached, struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(paths, ce->name)) + if (!pathspec_matches(paths, ce->name, opt->max_depth)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry @@ -538,7 +570,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, strbuf_addch(&pathbuf, '/'); down = pathbuf.buf + tn_len; - if (!pathspec_matches(paths, down)) + if (!pathspec_matches(paths, down, opt->max_depth)) ; else if (S_ISREG(entry.mode)) hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len); @@ -692,6 +724,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_SET_INT('I', NULL, &opt.binary, "don't match patterns in binary files", GREP_BINARY_NOMATCH), + { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth", + "descend at most levels", PARSE_OPT_NONEG, + NULL, 1 }, OPT_GROUP(""), OPT_BIT('E', "extended-regexp", &opt.regflags, "use extended POSIX regular expressions", REG_EXTENDED), @@ -768,6 +803,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.pathname = 1; opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; + opt.max_depth = -1; strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD); opt.color = -1; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 887731e830..fb05c4884c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1036,6 +1036,7 @@ _git_grep () --extended-regexp --basic-regexp --fixed-strings --files-with-matches --name-only --files-without-match + --max-depth --count --and --or --not --all-match " diff --git a/grep.h b/grep.h index f00db0e402..28e6b2a8ec 100644 --- a/grep.h +++ b/grep.h @@ -79,6 +79,7 @@ struct grep_opt { int pathname; int null_following_name; int color; + int max_depth; int funcname; char color_match[COLOR_MAXLEN]; const char *color_external; diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index b13aa7e89a..b4709e28b5 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -25,13 +25,17 @@ test_expect_success setup ' echo foo mmap bar_mmap echo foo_mmap bar mmap baz } >file && + echo vvv >v && echo ww w >w && echo x x xx x >x && echo y yy >y && echo zzz > z && mkdir t && echo test >t/t && - git add file w x y z t/t hello.c && + echo vvv >t/v && + mkdir t/a && + echo vvv >t/a/v && + git add . && test_tick && git commit -m initial ' @@ -132,6 +136,51 @@ do ! git grep -c test $H | grep /dev/null ' + test_expect_success "grep --max-depth -1 $L" ' + { + echo ${HC}t/a/v:1:vvv + echo ${HC}t/v:1:vvv + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth -1 -n -e vvv $H >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 0 $L" ' + { + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth 0 -n -e vvv $H >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 0 -- '*' $L" ' + { + echo ${HC}t/a/v:1:vvv + echo ${HC}t/v:1:vvv + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth 0 -n -e vvv $H -- "*" >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 1 $L" ' + { + echo ${HC}t/v:1:vvv + echo ${HC}v:1:vvv + } >expected && + git grep --max-depth 1 -n -e vvv $H >actual && + test_cmp expected actual + ' + + test_expect_success "grep --max-depth 0 -- t $L" ' + { + echo ${HC}t/v:1:vvv + } >expected && + git grep --max-depth 0 -n -e vvv $H -- t >actual && + test_cmp expected actual + ' + done cat >expected < Date: Sat, 25 Jul 2009 00:44:01 +0200 Subject: [PATCH 029/133] gitweb: Make .error style generic Style for td.error was introduced in 1f1ab5f (gitweb: style done with stylesheet, 2006-06-20) to replace inline style for errors in old multi-column "git annotate" based 'blame' view. This view was then since removed (replaced by "git-blame" based 'blame' view, with fewer colums), making this style unused. Make this style more generic by replacing td.error with .error to make it apply to any element. It will be used in 'blame_incremental' view to show error messages. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index d05bc37646..70b7c2f62d 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -262,7 +262,7 @@ td.sha1 { font-family: monospace; } -td.error { +.error { color: red; background-color: yellow; } From 6de9433fd0525632094f3cc172a606f217fa2097 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 25 Jul 2009 00:44:02 +0200 Subject: [PATCH 030/133] gitweb: Mark boundary commits in 'blame' view Use "boundary" class to mark boundary commits, which currently results in using bold weight font for SHA-1 of a commit (to be more exact for all text in the first cell in row, that contains SHA-1 of a commit). Detecting boundary commits is done by watching for "boundary" header in "git blame -p" output. Because this header doesn't carry additional data the regular expression for blame header fields had to be slightly adjusted. With current gitweb API only root (parentless) commits can be boundary commits. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 4 ++++ gitweb/gitweb.perl | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 70b7c2f62d..f47709bac5 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -242,6 +242,10 @@ tr.dark:hover { background-color: #edece6; } +tr.boundary td.sha1 { + font-weight: bold; +} + td { padding: 2px 5px; font-size: 100%; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7fbd5ff89e..3078b9280b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4826,7 +4826,7 @@ HTML while ($data = <$fd>) { chomp $data; last if ($data =~ s/^\t//); # contents of line - if ($data =~ /^(\S+) (.*)$/) { + if ($data =~ /^(\S+)(?: (.*))?$/) { $meta->{$1} = $2; } } @@ -4838,7 +4838,9 @@ HTML if ($group_size) { $current_color = ($current_color + 1) % $num_colors; } - print "\n"; + my $tr_class = $rev_color[$current_color]; + $tr_class .= ' boundary' if (exists $meta->{'boundary'}); + print "\n"; if ($group_size) { print " Date: Sat, 25 Jul 2009 00:44:03 +0200 Subject: [PATCH 031/133] gitweb: Use "previous" header of git-blame -p in 'blame' view Luben Tuikov changed 'lineno' link (line number link) from pointing to 'blame' view at given line at blamed commit, to the one at parent of blamed commit in 244a70e (Blame "linenr" link jumps to previous state at "orig_lineno", 2007-01-04). This made it possible to do data mining using 'blame' view, by going through history of a line using mentioned line number link. Original implementation called "git rev-parse ^" to find SHA-1 of a parent of a given commit once per each blamed line. In 39c19ce (gitweb: cache $parent_commit info in git_blame(), 2008-12-11) this was improved so rev-parse was called once per each unique commit in git-blame output. Alternate solution would be to relax validation for 'hb' parameter by allowing extended SHA-1 syntax of the form ^ (perhaps redirecting to gitweb URL with ^ resolved, in practice moving call to rev-parse to 'the other side of link'). This solution had a bug that it didn't work for boundary commits. Boundary commits don't have parents, so "git rev-parse ^" returned literal "^" (which didn't exists). Gitweb didn't detect this situation and passed this result literally as 'hb' parameter in 'linenr' link. Following such link currently gives 400 - Invalid hash base parameter error; 'hb' parameter is restricted via validate_refname to correct refnames and doesn't allow for extended SHA-1 syntax. This bug could have been fixed alternatively by checking if commit is boundary commit, or check if rev-parse result is unchanged (still ends in '^' prefix). The solution employing rev-parse to find parent of commit had inherent problem if blamed commit renamed file; then name of file would be different in its parent. Solving this outside git-blame would be difficult and costly (at least cost of additional fork for extra git command). Currently gitweb uses information in "previous" header, which was introduced by Junio C Hamano in 96e1170 (blame: show "previous" information in --porcelain/--incremental format, 2008-06-04) This (currently undocumented) header has the following format: "previous " Using "previous" header solves both problem of performance and the problem that blamed commit could have renaming blamed file. Because "previous" header can be repeated for the same commit when blamed commit is merge (has more than one parent), and we are interested usually in _first_ parent, currently we store only first value if blame header repeats. Using first parent (first "previous" line) was what gitweb did before; without this change gitweb would use last parent instead. If there is no previous commit 'linenr' link points to blamed commit and blamed filename, making it work correctly for boundary commits. Acked-by: Luben Tuikov Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3078b9280b..b8a121bb98 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4827,7 +4827,7 @@ HTML chomp $data; last if ($data =~ s/^\t//); # contents of line if ($data =~ /^(\S+)(?: (.*))?$/) { - $meta->{$1} = $2; + $meta->{$1} = $2 unless exists $meta->{$1}; } } my $short_rev = substr($full_rev, 0, 8); @@ -4852,20 +4852,21 @@ HTML esc_html($short_rev)); print "\n"; } - my $parent_commit; - if (!exists $meta->{'parent'}) { - open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^") - or die_error(500, "Open git-rev-parse failed"); - $parent_commit = <$dd>; - close $dd; - chomp($parent_commit); - $meta->{'parent'} = $parent_commit; - } else { - $parent_commit = $meta->{'parent'}; + # 'previous' + if (exists $meta->{'previous'} && + $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) { + $meta->{'parent'} = $1; + $meta->{'file_parent'} = unquote($2); } + my $linenr_commit = + exists($meta->{'parent'}) ? + $meta->{'parent'} : $full_rev; + my $linenr_filename = + exists($meta->{'file_parent'}) ? + $meta->{'file_parent'} : unquote($meta->{'filename'}); my $blamed = href(action => 'blame', - file_name => $meta->{'filename'}, - hash_base => $parent_commit); + file_name => $linenr_filename, + hash_base => $linenr_commit); print ""; print $cgi->a({ -href => "$blamed#l$orig_lineno", -class => "linenr" }, From 3665e7e7f2b210f6896815d563255c364a861a6e Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 25 Jul 2009 00:44:04 +0200 Subject: [PATCH 032/133] gitweb: Mark commits with no "previous" in 'blame' view Use "no-previous" class to mark blamed commits which do not have "previous" header. Those are commits in which blamed file was created (added); this includes boundary commits. This means that 'linenr' link leads to blamed commit, not (one of) parent of blamed commit. Therefore currently line number for such commit uses bold weight font to denote this situation; the effect is subtle. Use "multiple-previous" class in the opposite situation, where blamed commit has multiple "previous" headers (is an evil merge). Currently this class is not used for styling. In this situation 'linenr' link leads to first of "previous" commits (first parent). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 3 ++- gitweb/gitweb.perl | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index f47709bac5..47633376d1 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -242,7 +242,8 @@ tr.dark:hover { background-color: #edece6; } -tr.boundary td.sha1 { +tr.boundary td.sha1, +tr.no-previous td.linenr { font-weight: bold; } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b8a121bb98..128bddd381 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4819,7 +4819,7 @@ HTML my ($full_rev, $orig_lineno, $lineno, $group_size) = ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/); if (!exists $metainfo{$full_rev}) { - $metainfo{$full_rev} = {}; + $metainfo{$full_rev} = { 'nprevious' => 0 }; } my $meta = $metainfo{$full_rev}; my $data; @@ -4829,6 +4829,9 @@ HTML if ($data =~ /^(\S+)(?: (.*))?$/) { $meta->{$1} = $2 unless exists $meta->{$1}; } + if ($data =~ /^previous /) { + $meta->{'nprevious'}++; + } } my $short_rev = substr($full_rev, 0, 8); my $author = $meta->{'author'}; @@ -4840,6 +4843,8 @@ HTML } my $tr_class = $rev_color[$current_color]; $tr_class .= ' boundary' if (exists $meta->{'boundary'}); + $tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0); + $tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1); print "\n"; if ($group_size) { print " Date: Sat, 25 Jul 2009 00:44:05 +0200 Subject: [PATCH 033/133] gitweb: Add author initials in 'blame' view, a la "git gui blame" For example for "Junio C Hamano" initials would be "JH". Of course initials are added (below shortened SHA-1 of blamed commit) only if group of lines that blame the same commit has 2 or more lines in it. Initials are extracted using i18n /\b([[:upper:]])\B/g regexp. Additionally initials help to distinguish boundary commits, as they use bold weight font too (in addition to shortened SHA-1 of commit). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 128bddd381..ea1ab5f846 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4855,6 +4855,14 @@ HTML hash=>$full_rev, file_name=>$file_name)}, esc_html($short_rev)); + if ($group_size >= 2) { + my @author_initials = ($author =~ /\b([[:upper:]])\B/g); + if (@author_initials) { + print "
" . + esc_html(join('', @author_initials)); + # or join('.', ...) + } + } print "\n"; } # 'previous' From aef37684ea713c96dc3e4913cf33962df1efb92b Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 25 Jul 2009 00:44:06 +0200 Subject: [PATCH 034/133] gitweb: Use light/dark for class names also in 'blame' view Instead of using "light2" and "dark2" for class names in 'blame' view (in place of "light" and "dark" classes in other places) to avoid changing style on hover in 'blame' view while doing it for other views (like 'shortlog'), use more advanced CSS, relying on the fact that more specific selector wins. While at it add a few comments to gitweb CSS file, and consolidate some repeated info. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 17 ++++++++++------- gitweb/gitweb.perl | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 47633376d1..8f68fe3091 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -226,22 +226,25 @@ th { text-align: left; } -tr.light:hover { - background-color: #edece6; +/* do not change row style on hover for 'blame' view */ +tr.light, +table.blame .light:hover { + background-color: #ffffff; } -tr.dark { - background-color: #f6f6f0; -} - -tr.dark2 { +tr.dark, +table.blame .dark:hover { background-color: #f6f6f0; } +/* currently both use the same, but it can change */ +tr.light:hover, tr.dark:hover { background-color: #edece6; } +/* boundary commits in 'blame' view */ +/* and commits without "previous" */ tr.boundary td.sha1, tr.no-previous td.linenr { font-weight: bold; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ea1ab5f846..2cb60bedc6 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4801,7 +4801,7 @@ sub git_blame { git_print_page_path($file_name, $ftype, $hash_base); # page body - my @rev_color = qw(light2 dark2); + my @rev_color = qw(light dark); my $num_colors = scalar(@rev_color); my $current_color = 0; my %metainfo = (); From 53d48885931614a43e414e1272a7f126f8d0c901 Mon Sep 17 00:00:00 2001 From: Nanako Shiraishi Date: Sat, 25 Jul 2009 06:59:28 +0900 Subject: [PATCH 035/133] git init: optionally allow a directory argument When starting a new repository, I see my students often say % git init newrepo and curse git. They could say % mkdir newrepo; cd newrepo; git init but allowing it as an obvious short-cut may be nicer. Signed-off-by: Nanako Shiraishi Signed-off-by: Junio C Hamano --- Documentation/git-init.txt | 5 ++- builtin-init-db.c | 56 +++++++++++++++++++++++---- t/t0001-init.sh | 77 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 9 deletions(-) diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index 7151d12f34..f081b24d9d 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one SYNOPSIS -------- -'git init' [-q | --quiet] [--bare] [--template=] [--shared[=]] +'git init' [-q | --quiet] [--bare] [--template=] [--shared[=]] [directory] OPTIONS @@ -74,6 +74,9 @@ By default, the configuration flag receive.denyNonFastForwards is enabled in shared repositories, so that you cannot force a non fast-forwarding push into it. +If you name a (possibly non-existent) directory at the end of the command +line, the command is run inside the directory (possibly after creating it). + -- diff --git a/builtin-init-db.c b/builtin-init-db.c index 4a5600631c..b7f708de1f 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -371,7 +371,7 @@ static int guess_repository_type(const char *git_dir) } static const char init_db_usage[] = -"git init [-q | --quiet] [--bare] [--template=] [--shared[=]]"; +"git init [-q | --quiet] [--bare] [--template=] [--shared[=]] [directory]"; /* * If you want to, you can share the DB area with any number of branches. @@ -384,27 +384,67 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir; const char *template_dir = NULL; unsigned int flags = 0; + int bare_given = 0; int i; for (i = 1; i < argc; i++, argv++) { const char *arg = argv[1]; if (!prefixcmp(arg, "--template=")) template_dir = arg+11; - else if (!strcmp(arg, "--bare")) { - static char git_dir[PATH_MAX+1]; - is_bare_repository_cfg = 1; - setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, - sizeof(git_dir)), 0); - } else if (!strcmp(arg, "--shared")) + else if (!strcmp(arg, "--bare")) + bare_given = is_bare_repository_cfg = 1; + else if (!strcmp(arg, "--shared")) init_shared_repository = PERM_GROUP; else if (!prefixcmp(arg, "--shared=")) init_shared_repository = git_config_perm("arg", arg+9); else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) flags |= INIT_DB_QUIET; - else + else if (arg[0] == '-') usage(init_db_usage); + else + break; } + if (i == argc - 1) { + int mkdir_tried = 0; + retry: + if (chdir(argv[1]) < 0) { + if (!mkdir_tried) { + int saved; + /* + * At this point we haven't read any configuration, + * and we know shared_repository should always be 0; + * but just in case we play safe. + */ + saved = shared_repository; + shared_repository = 0; + switch (safe_create_leading_directories_const(argv[1])) { + case -3: + errno = EEXIST; + /* fallthru */ + case -1: + die_errno("cannot mkdir %s", argv[1]); + break; + default: + break; + } + shared_repository = saved; + if (mkdir(argv[1], 0777) < 0) + die_errno("cannot mkdir %s", argv[1]); + mkdir_tried = 1; + goto retry; + } + die_errno("cannot chdir to %s", argv[1]); + } + } else if (i < argc - 1) { + usage(init_db_usage); + } + if (bare_given == 1) { + static char git_dir[PATH_MAX+1]; + + setenv(GIT_DIR_ENVIRONMENT, + getcwd(git_dir, sizeof(git_dir)), 0); + } if (init_shared_repository != -1) shared_repository = init_shared_repository; diff --git a/t/t0001-init.sh b/t/t0001-init.sh index e3d846420d..49caa29061 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -208,4 +208,81 @@ test_expect_success 'init rejects insanely long --template' ' ) ' +test_expect_success 'init creates a new directory' ' + rm -fr newdir && + ( + git init newdir && + test -d newdir/.git/refs + ) +' + +test_expect_success 'init creates a new bare directory' ' + rm -fr newdir && + ( + git init --bare newdir && + test -d newdir/refs + ) +' + +test_expect_success 'init recreates a directory' ' + rm -fr newdir && + ( + mkdir newdir && + git init newdir && + test -d newdir/.git/refs + ) +' + +test_expect_success 'init recreates a new bare directory' ' + rm -fr newdir && + ( + mkdir newdir && + git init --bare newdir && + test -d newdir/refs + ) +' + +test_expect_success 'init creates a new deep directory' ' + rm -fr newdir && + ( + # Leading directories should honor umask while + # the repository itself should follow "shared" + umask 002 && + git init --bare --shared=0660 newdir/a/b/c && + test -d newdir/a/b/c/refs && + ls -ld newdir/a newdir/a/b > lsab.out && + ! grep -v "^drwxrw[sx]r-x" ls.out && + ls -ld newdir/a/b/c > lsc.out && + ! grep -v "^drwxrw[sx]---" lsc.out + ) +' + +test_expect_success 'init notices EEXIST (1)' ' + rm -fr newdir && + ( + >newdir && + test_must_fail git init newdir && + test -f newdir + ) +' + +test_expect_success 'init notices EEXIST (2)' ' + rm -fr newdir && + ( + mkdir newdir && + >newdir/a + test_must_fail git init newdir/a/b && + test -f newdir/a + ) +' + +test_expect_success POSIXPERM 'init notices EPERM' ' + rm -fr newdir && + ( + mkdir newdir && + chmod -w newdir && + test_must_fail git init newdir/a/b + ) +' + test_done From 6641575963388b61f408f177d91cdacad25d2e26 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 29 Jul 2009 09:33:29 -0700 Subject: [PATCH 036/133] Start 1.6.5 cycle The next major release will be 1.6.5, hopefully with a shorter cycle than the 1.6.4 cycle. After that in 1.7.0 we can make potentially backward incompatible changes if necessary. Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.6.5.txt | 51 ++++++++++++++++++++++++++++++++ GIT-VERSION-GEN | 2 +- RelNotes | 2 +- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 Documentation/RelNotes-1.6.5.txt diff --git a/Documentation/RelNotes-1.6.5.txt b/Documentation/RelNotes-1.6.5.txt new file mode 100644 index 0000000000..856047d16f --- /dev/null +++ b/Documentation/RelNotes-1.6.5.txt @@ -0,0 +1,51 @@ +GIT v1.6.5 Release Notes +======================== + +In git 1.7.0, which is planned to be the release after 1.6.5, "git push" +into a branch that is currently checked out will be refused by default. + +You can choose what should happen upon such a push by setting the +configuration variable receive.denyCurrentBranch in the receiving +repository. + +Also, "git push $there :$killed" to delete the branch $killed in a remote +repository $there, when $killed branch is the current branch pointed at by +its HEAD, will be refused by default. + +You can choose what should happen upon such a push by setting the +configuration variable receive.denyDeleteCurrent in the receiving +repository. + +To ease the transition plan, the receiving repository of such a +push running this release will issue a big warning when the +configuration variable is missing. Please refer to: + + http://git.or.cz/gitwiki/GitFaq#non-bare + http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007 + +for more details on the reason why this change is needed and the +transition plan. + +Updates since v1.6.4 +-------------------- + +(subsystems) + +(portability) + +(performance) + +(usability, bells and whistles) + +(developers) + +Fixes since v1.6.4 +------------------ + +# All of the fixes in v1.6.4.X maintenance series are included in this +# release, unless otherwise noted. + +# Here are fixes that this release has, but have not been backported to +# v1.6.4.X series. + + diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index d8ae315140..d7d9a9a063 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.6.4 +DEF_VER=v1.6.4.GIT LF=' ' diff --git a/RelNotes b/RelNotes index f8e49a5070..b62449d2e2 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.6.4.txt \ No newline at end of file +Documentation/RelNotes-1.6.5.txt \ No newline at end of file From 1c9b2d3aa12ea4e34c1d04cc2af4e07a1eecb964 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 11 May 2009 11:31:42 +0200 Subject: [PATCH 037/133] Add a reminder test case for a merge with F/D transition The problem is that if a file was replaced with a directory containing another file with the same content and mode, an attempt to merge it with a branch descended from a commit before this F->D transition will cause merge-recursive to break. It breaks even if there were no conflicting changes on that other branch. Originally reported by Anders Melchiorsen. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t6020-merge-df.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh index a19d49de28..e71c687f2b 100755 --- a/t/t6020-merge-df.sh +++ b/t/t6020-merge-df.sh @@ -22,4 +22,27 @@ git commit -m "File: dir"' test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master' +test_expect_failure 'F/D conflict' ' + git reset --hard && + git checkout master && + rm .git/index && + + mkdir before && + echo FILE >before/one && + echo FILE >after && + git add . && + git commit -m first && + + rm -f after && + git mv before after && + git commit -m move && + + git checkout -b para HEAD^ && + echo COMPLETELY ANOTHER FILE >another && + git add . && + git commit -m para && + + git merge master +' + test_done From 133cfaeb8ba40f12e4c0ad99bdd3a0a4f8d0ade2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 27 Jul 2009 14:27:47 -0700 Subject: [PATCH 038/133] request-pull: optionally show a patch as well Allow git request-pull to append diff body into the pull request. It's useful for small series of commits. Tested-by: Cyrill Gorcunov Signed-off-by: Junio C Hamano --- git-request-pull.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/git-request-pull.sh b/git-request-pull.sh index fd95beadab..630ceddf03 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -8,13 +8,33 @@ USAGE=' []' LONG_USAGE='Summarizes the changes between two commits to the standard output, and includes the given URL in the generated summary.' SUBDIRECTORY_OK='Yes' -OPTIONS_SPEC= +OPTIONS_SPEC='git request-pull [options] start url [end] +-- +p show patch text as well +' + . git-sh-setup . git-parse-remote GIT_PAGER= export GIT_PAGER +patch= +while case "$#" in 0) break ;; esac +do + case "$1" in + -p) + patch=-p ;; + --) + shift; break ;; + -*) + usage ;; + *) + break ;; + esac + shift +done + base=$1 url=$2 head=${3-HEAD} @@ -54,5 +74,5 @@ echo " $url $branch" echo git shortlog ^$baserev $headrev -git diff -M --stat --summary $merge_base $headrev +git diff -M --stat --summary $patch $merge_base..$headrev exit $status From e6580020057afd207b7cfb9c96905f99e13cfe4d Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 29 Jun 2009 12:32:22 -0300 Subject: [PATCH 039/133] Translate the tutorial to Brazillian Portuguese Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Junio C Hamano --- Documentation/pt_BR/gittutorial.txt | 679 ++++++++++++++++++++++++++++ 1 file changed, 679 insertions(+) create mode 100644 Documentation/pt_BR/gittutorial.txt diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt new file mode 100644 index 0000000000..f368b1b518 --- /dev/null +++ b/Documentation/pt_BR/gittutorial.txt @@ -0,0 +1,679 @@ +gittutorial(7) +============== + +NAME +---- +gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova) + +SYNOPSIS +-------- +git * + +DESCRIPTION +----------- + +Este tutorial explica como importar um novo projeto para o git, +adicionar mudanças a ele, e compartilhar mudanças com outros +desenvolvedores. + +If, ao invés disso, você está interessado primariamente em usar git para +obter um projeto, por exemplo, para testar a última versão, você pode +preferir começar com os primeiros dois capítulos de +link:user-manual.html[O Manual do Usuário Git]. + +Primeiro, note que você pode obter documentação para um comando como +`git log --graph` com: + +------------------------------------------------ +$ man git-log +------------------------------------------------ + +ou: + +------------------------------------------------ +$ git help log +------------------------------------------------ + +Com a última forma, você pode usar o visualizador de manual de sua +escolha; veja linkgit:git-help[1] para maior informação. + +É uma boa idéia se introduzir ao git com seu nome e endereço público de +email antes de fazer qualquer operação. A maneira mais fácil de fazê-lo +é: + +------------------------------------------------ +$ git config --global user.name "Seu Nome Vem Aqui" +$ git config --global user.email voce@seudominio.exemplo.com +------------------------------------------------ + + +Importando um novo projeto +----------------------- + +Assuma que você tem um tarball project.tar.gz com seu trabalho inicial. +Você pode colocá-lo sob controle de revisão git como a seguir. + +------------------------------------------------ +$ tar xzf project.tar.gz +$ cd project +$ git init +------------------------------------------------ + +Git irá responder + +------------------------------------------------ +Initialized empty Git repository in .git/ +------------------------------------------------ + +Você agora iniciou seu diretório de trabalho--você deve ter notado um +novo diretório criado, com o nome de ".git". + +A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os +arquivos sob o diretório corrente (note o '.'), com 'git-add': + +------------------------------------------------ +$ git add . +------------------------------------------------ + +Este instantâneo está agora armazenado em uma área temporária que o git +chama de "index" ou índice. Você pode permanetemente armazenar o +conteúdo do índice no repositório com 'git-commit': + +------------------------------------------------ +$ git commit +------------------------------------------------ + +Isto vai te pedir por uma mensagem de commit. Você agora gravou sua +primeira versão de seu projeto no git. + +Fazendo mudanças +-------------- + +Modifique alguns arquivos, e, então, adicione seu conteúdo atualizado ao +índice: + +------------------------------------------------ +$ git add file1 file2 file3 +------------------------------------------------ + +Você está agora pronto para fazer o commit. Você pode ver o que está +para ser gravado usando 'git-diff' com a opção --cached: + +------------------------------------------------ +$ git diff --cached +------------------------------------------------ + +(Sem --cached, o comando 'git-diff' irá te mostrar quaisquer mudanças +que você tenha feito mas ainda não adicionou ao índice.) Você também +pode obter um breve sumário da situação com 'git-status': + +------------------------------------------------ +$ git status +# On branch master +# Changes to be committed: +# (use "git reset HEAD ..." to unstage) +# +# modified: file1 +# modified: file2 +# modified: file3 +# +------------------------------------------------ + +Se você precisar fazer qualquer outro ajuste, faça-o agora, e, então, +adicione qualquer conteúdo modificado ao índice. Finalmente, grave suas +mudanças com: + +------------------------------------------------ +$ git commit +------------------------------------------------ + +Isto irá novamente te pedir por uma mensagem descrevendo a mudança, e, +então, gravar a nova versão do projeto. + +Alternativamente, ao invés de executar 'git-add' antes, você pode usar + +------------------------------------------------ +$ git commit -a +------------------------------------------------ + +o que irá automaticamente notar quaisquer arquivos modificados (mas não +novos), adicioná-los ao índices, e gravar, tudo em um único passo. + +Uma nota em mensagens de commit: Apesar de não ser exigido, é uma boa +idéia começar a mensagem com uma simples e curta (menos de 50 +caracteres) linha sumarizando a mudança, seguida de uma linha em branco +e, então, uma descrição mais detalhada. Ferramentas que transformam +commits em email, por exemplo, usam a primeira linha no campo de +cabeçalho Subject: e o resto no corpo. + +Git rastreia conteúdo, não arquivos +---------------------------- + +Muitos sistemas de controle de revisão provêem um comando `add` que diz +ao sistema para começar a rastrear mudanças em um novo arquivo. O +comando `add` do git faz algo mais simples e mais poderoso: 'git-add' é +usado tanto para arquivos novos e arquivos recentemente modificados, e +em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena +o conteúdo no índice, pronto para inclusão do próximo commit. + +Visualizando história do projeto +----------------------- + +Em qualquer ponto você pode visualizar a história das suas mudanças +usando + +------------------------------------------------ +$ git log +------------------------------------------------ + +Se você também quer ver a diferença completa a cada passo, use + +------------------------------------------------ +$ git log -p +------------------------------------------------ + +Geralmente, uma visão geral da mudança é útil para ter a sensação de +cada passo + +------------------------------------------------ +$ git log --stat --summary +------------------------------------------------ + +Gerenciando "branches"/ramos +----------------- + +Um simples repositório git pode manter múltiplos ramos de +desenvolvimento. Para criar um novo ramo chamado "experimental", use + +------------------------------------------------ +$ git branch experimental +------------------------------------------------ + +Se você executar agora + +------------------------------------------------ +$ git branch +------------------------------------------------ + +você vai obter uma lista de todos os ramos existentes: + +------------------------------------------------ + experimental +* master +------------------------------------------------ + +O ramo "experimental" é o que você acaba de criar, e o ramo "master" é o +ramo padrão que foi criado pra você automaticamente. O asterisco marca +o ramo em que você está atualmente; digite + +------------------------------------------------ +$ git checkout experimental +------------------------------------------------ + +para mudar para o ramo experimental. Agora edite um arquivo, grave a +mudança, e mude de volta para o ramo master: + +------------------------------------------------ +(edita arquivo) +$ git commit -a +$ git checkout master +------------------------------------------------ + +Verifique que a mudança que você fez não está mais visível, já que ela +foi feita no ramo experimental e você está de volta ao ramo master. + +Você pode fazer uma mudança diferente no ramo master: + +------------------------------------------------ +(edit file) +$ git commit -a +------------------------------------------------ + +neste ponto, os dois ramos divergiram, com diferentes mudanças feitas em +cada um. Para unificar as mudanças feitas no experimental para o +master, execute + +------------------------------------------------ +$ git merge experimental +------------------------------------------------ + +Se as mudanças não conflitam, está pronto. Se existirem conflitos, +marcadores serão deixados nos arquivos problemáticos exibindo o +conflito; + +------------------------------------------------ +$ git diff +------------------------------------------------ + +vai exibir isto. Após você editar os arquivos para resolver os +conflitos, + +------------------------------------------------ +$ git commit -a +------------------------------------------------ + +irá gravar o resultado da unificação. Finalmente, + +------------------------------------------------ +$ gitk +------------------------------------------------ + +vai mostrar uma bela representação gráfica da história resultante. + +Neste ponto você pode remover seu ramo experimental com + +------------------------------------------------ +$ git branch -d experimental +------------------------------------------------ + +Este comando garante que as mudanças no ramo experimental já estão no +ramo atual. + +Se você desenvolve em um ramo ideia-louca, e se arrepende, você pode +sempre remover o ramo com + +------------------------------------- +$ git branch -D crazy-idea +------------------------------------- + +Ramos são baratos e fáceis, então isto é uma boa maneira de experimentar +alguma coisa. + +Usando git para colaboração +--------------------------- + +Suponha que Alice começou um novo projeto com um repositório git em +/home/alice/project, e que Bob, que tem um diretório home na mesma +máquina, quer contribuir. + +Bob começa com: + +------------------------------------------------ +bob$ git clone /home/alice/project myrepo +------------------------------------------------ + +Isso cria um novo diretório "myrepo" contendo um clone do repositório de +Alice. O clone está no mesmo pé que o projeto original, possuindo sua +própria cópia da história do projeto original. + +Bob então faz algumas mudanças e as grava: + +------------------------------------------------ +(editar arquivos) +bob$ git commit -a +(repetir conforme necessário) +------------------------------------------------ + +Quanto está pronto, ele diz a Alice para puxar as mudanças do +repositório em /home/bob/myrepo. Ela o faz com: + +------------------------------------------------ +alice$ cd /home/alice/project +alice$ git pull /home/bob/myrepo master +------------------------------------------------ + +Isto unifica as mudanças do ramo "master" do Bob ao ramo atual de Alice. +Se Alice fez suas próprias mudanças no intervalo, ela, então, pode +precisar corrigir manualmente quaiquer conflitos. (Note que o argumento +"master" no comando acima é, de fato, desnecessário, já que é o padrão.) + +O comando "pull" executa, então, duas operações: ele obtém mudanças de +um ramo remoto, e, então, as unifica no ramo atual. + +Note que, em geral, Alice gostaria que suas mudanças locais fossem +gravadas antes de iniciar este "pull". Se o trabalho de Bobo conflita +com o que Alice fez desde que suas histórias se ramificaram, Alice irá +usar seu diretório de trabalho e o índice para resolver conflitos, e +mudanças locais existentes irão interferir com o processo de resolução +de conflitos (git ainda irá realizar a obtenção mas irá se recusar a +unificar --- Alice terá que se livrar de suas mudanças locais de alguma +forma e puxar de novo quando isso acontecer). + +Alice pode espiar o que Bob fez sem unificar primeiro, usando o comando +"fetch"; isto permite Alice inspecionar o que Bob fez, usando um símbolo +especial "FETCH_HEAD", com o fim de determinar se ele tem alguma coisa +que vale puxar, assim: + +------------------------------------------------ +alice$ git fetch /home/bob/myrepo master +alice$ git log -p HEAD..FETCH_HEAD +------------------------------------------------ + +Esta operação é segura mesmo se Alice tem mudanças locais não gravadas. +A notação de intervalo "HEAD..FETCH_HEAD" significa mostrar tudo que é +alcançável de FETCH_HEAD mas exclua tudo que é alcançável de HEAD. Alcie +já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob tem +em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando. + +Se Alice quer visualizar o que Bob fez desde que suas história +ramificaram, ela pode disparar o seguinte comando: + +------------------------------------------------ +$ gitk HEAD..FETCH_HEAD +------------------------------------------------ + +Isto usar a mesma notação de intervaldo que vimos antes com 'git log'. + +Alice pode querer ver o que ambos fizeram desde que ramificaram. Ela +pode usar a forma com três pontos ao invés da forma com dois pontos: + +------------------------------------------------ +$ gitk HEAD...FETCH_HEAD +------------------------------------------------ + +Isto significa "mostre tudo que é alcançavel de qualquer um, mas exclua +tudo que é alcançavel a partir de ambos". +This means "show everything that is reachable from either one, but +exclude anything that is reachable from both of them". + +Por favor, note que essas notações de intervalo podem ser usadas tanto +com gitk quanto com "git log". + +Apoós inspecionar o que Bob fez, se não há nada urgente, Alice pode +decidir continuar trabalhando sem puxar de Bob. Se a história de Bob +tem alguma coisa que Alice precisa imediatamente, Alice pode optar por +separar seu trabalho em progresso primeiro, fazer um "pull", e, então, +finalmente, retomar seu trabalho em progresso em cima da história +resultante. + +Quanto você está trabalhando em um pequeno grupo unido, não é incomum +interagir com o mesmo repositório várias e várias vezes. Definindo um +repositório remoto antes de tudo, você pode fazê-lo mais facilmente: + +------------------------------------------------ +alice$ git remote add bob /home/bob/myrepo +------------------------------------------------ + +Com isso, Alice pode executar a primeira parte da operação "pull" usando +o comando 'git-fetch' sem unificar suas mudanças com seu próprio ramo, +usando: + +------------------------------------- +alice$ git fetch bob +------------------------------------- + +Diferente da forma longa, quando Alice obteve de Bob usando um +repositório remoto antes definido com 'git-remote', o que foi obtido é +armazenado um ramo remoto, neste caso `bob/master`. Então, após isso: + +------------------------------------- +alice$ git log -p master..bob/master +------------------------------------- + +mostra uma lista de todas as mudanças que Bob fez desde que ramificou do +ramo master de Alice. + +Após examinar essas mudanças, Alice pode unificá-las em seu ramo master: + +------------------------------------- +alice$ git merge bob/master +------------------------------------- + +Esse `merge` pode também ser feito puxando de seu próprio ramo remoto, +assim: + +------------------------------------- +alice$ git pull . remotes/bob/master +------------------------------------- + +Note que 'git pull' sempre unifica ao ramo atual, independente do que +mais foi dado na linha de comando. + +Depois, Bob pode atualizar seu repositório com as últimas mudanças de +Alice, usando + +------------------------------------- +bob$ git pull +------------------------------------- + +Note que ele não precisa dar o caminho do repositório de Alice; quando +Bob clonou seu repositório, o git armazenou a localização de seu +repositório na configuração do repositório, e essa localização é usada +para puxar: + +------------------------------------- +bob$ git config --get remote.origin.url +/home/alice/project +------------------------------------- + +(A configuração completa criada por 'git-clone' é visível usando `git +config -l`, e a página de manual linkgit:git-config[1] explica o +significado de cada opção.) + +Git também mantém uma cópia limpa do ramo master de Alice sob o nome +"origin/master": + +------------------------------------- +bob$ git branch -r + origin/master +------------------------------------- + +Se Bob decidir depois em trabalhar em um host diferente, ele ainda pode +executar clones e puxar usando o protocolo ssh: + +------------------------------------- +bob$ git clone alice.org:/home/alice/project myrepo +------------------------------------- + +Alternativamente, o git tem um protocolo nativo, ou pode usar rsync ou +http; veja linkgit:git-pull[1] para detalhes. + +Git pode também ser usado em um modo parecido com CVS, com um +repositório central para o qual que vários usuários empurram +modificações; veja linkgit:git-push[1] e linkgit:gitcvs-migration[7]. + +Explorando história +----------------- + +A história no git é representada como uma série de commits +interrelacionados. Nós já vimos que o comando 'git-log' pode listar +esses commits. Note que a primeira linha de cama entrada no log também +dá o nome para o commit: + +------------------------------------- +$ git log +commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7 +Author: Junio C Hamano +Date: Tue May 16 17:18:22 2006 -0700 + + merge-base: Clarify the comments on post processing. +------------------------------------- + +Nós podemos dar este nome ao 'git-show' para ver os detalhes sobre este +commit. + +------------------------------------- +$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7 +------------------------------------- + +Mas há outras formas de se referir a commits. Você pode usar qualquer +parte inicial do nome que seja longo o bastante para unicamente +identificar o commit: + +------------------------------------- +$ git show c82a22c39c # os primeiros caracteres do nome são o bastante + # usualmente +$ git show HEAD # a ponta do ramo atual +$ git show experimental # a ponta do ramo "experimental" +------------------------------------- + +Todo commit usualmente tem um commit "pai" que aponta para o estado +anterior do projeto: + +------------------------------------- +$ git show HEAD^ # para ver o pai de HEAD +$ git show HEAD^^ # para ver o avô de HEAD +$ git show HEAD~4 # para ver o trisavô de HEAD +------------------------------------- + +Note que commits de unificação podem ter mais de um pai: + +------------------------------------- +$ git show HEAD^1 # mostra o primeiro pai de HEAD (o mesmo que HEAD^) +$ git show HEAD^2 # mostra o segundo pai de HEAD +------------------------------------- + +Você também pode dar aos commits nomes seus; após executar + +------------------------------------- +$ git tag v2.5 1b2e1d63ff +------------------------------------- + +você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende +compartilhar esse nome com outras pessoas (por exemplo, para identificar +uma versão de lançamento), você deve criar um objeto "tag", e talvez +assiná-lo; veja linkgit:git-tag[1] para detalhes. + +Qualquer comando git que precise conhecer um commit pode receber +quaisquer desses nomes. Por exemplo: + +------------------------------------- +$ git diff v2.5 HEAD # compara o HEAD atual com v2.5 +$ git branch stable v2.5 # inicia um novo ramo chamado "stable" baseado + # em v2.5 +$ git reset --hard HEAD^ # reseta seu ramo atual e seu diretório de + # trabalho a seu estado em HEAD^ +------------------------------------- + +Seja cuidadoso com o último comando: além de perder quaisquer mudanças +em seu diretório de trabalho, ele também remove todos os commits +posteriores desse ramo. Se esse ramo é o único ramo contendo esses +commits, eles serão perdidos. Também, não use 'git-reset' num ramo +publicamente visível de onde outros desenvolvedores puxam, já que vai +forçar unificações desnecessárias para que outros desenvolvedores limpem +a história. Se você precisa desfazer mudanças que você empurrou, use +'git-revert' no lugar. + +O comando 'git-grep' pode buscar strings em qualquer versão de seu +projeto, então + +------------------------------------- +$ git grep "hello" v2.5 +------------------------------------- + +procura por todas as ocorreências de "hello" em v2.5. + +Se você deixar de fora o nome do commit, 'git-grep' irá procurar +quaisquer dos arquivos que ele gerencia no diretório corrente. Então + +------------------------------------- +$ git grep "hello" +------------------------------------- + +é uma forma rápida de buscar somente os arquivos que são rastreados pelo +git. + +Muitos comandos git também recebem um conjunto de commits, o que pode +ser especificado de um bom número de formas. Aqui estão alguns exemplos +com 'git-log': + +------------------------------------- +$ git log v2.5..v2.6 # commits entre v2.5 e v2.6 +$ git log v2.5.. # commits desde v2.5 +$ git log --since="2 weeks ago" # commits das últimas 2 semanas +$ git log v2.5.. Makefile # commits desde v2.5 que modificam + # Makefile +------------------------------------- + +Você também pode dar ao 'git-log' um "intervalo" de commits onde o +primeiro não é necessariamente um ancestral do segundo; por exemplo, se +as pontas dos ramos "stable" e "master" divergiram de um commit +comum algum tempo atrás, então + +------------------------------------- +$ git log stable..master +------------------------------------- + +irá listas os commits feitos no ramo "master" mas não no ramo +"stable", enquanto + +------------------------------------- +$ git log master..stable +------------------------------------- + +irá listar a lista de commits feitos no ramo "stable" mas não no ramo +"master". + +O comando 'git-log' tem uma fraquza: ele precisa mostrar os commits em +uma lista. Quando a história tem linhas de desenvolvimento que +divergiram e então foram unificadas novamente, a ordem em que 'git-log' +apresenta essas mudanças é insignificante. + +A maioria dos projetos com múltiplos contribuidores (como o kernel +linux, ou o git mesmo) tem unificações frequentes, e 'gitk' faz um +trabalho melhor de visualizar sua história. Por exemplo, + +------------------------------------- +$ gitk --since="2 weeks ago" drivers/ +------------------------------------- + +permite você navegar em quaisquer commits desde as últimas duas semanas +de commits que modificaram arquivos sob o diretório "drivers". (Nota: +você pode ajustar as fontes do gitk segurando a tecla control enquanto +pressiona "-" ou "+".) + +Finalmente, a maioria dos comandos que recebem nomes de arquivo +te permitirão opcionalmente preceder qualquer nome de arquivo por um +commit, para especificar uma versão particular do arquivo: + +------------------------------------- +$ git diff v2.5:Makefile HEAD:Makefile.in +------------------------------------- + +Você pode usar 'git-show' para ver tal arquivo: + +------------------------------------- +$ git show v2.5:Makefile +------------------------------------- + +Próximos passos +---------- + +Este tutorial deve ser o bastante para operar controle de revisão +distribuído básico para seus projetos. No entanto, para entender +plenamente a profundidade e o poder do git você precisa entender duas +idéias simples nas quais ele se baseia: + + * A base de objetos é um sistema bem elegante usado para armazenar a + história de seu projeto--arquivos, diretórios, e commits. + + * O arquivo de índica é um cache do estado de uma árvore de diretório, + usado para criar commits, restaurar diretórios de trabalho, e + compreender as várias árvores involvidas em uma unificação. + +Parte dois deste tutorial explica a base de objetos, o arquivo de +índice, e algumas outras coisinhas que você vai precisar pra usar o +máximo do git. Você pode encontrá-la em linkgit:gittutorial-2[7]. + +Se você não quer continuar do jeito certo, algumas outras disgressões +que podem ser interessantes neste ponto são: + + * linkgit:git-format-patch[1], linkgit:git-am[1]: Estes convertem + séries de commits em patches em email, e vice-versa, úteis para + projetos como o kernel linux que dependem pesadamente em patches + enviados por email. + + * linkgit:git-bisect[1]: Quando há uma regressão em seu projeto, uma + forma de rastrear um bug é procurando pela história para encontrar o + commit culpado. Git bisect pode ajudar a executar uma busca binária + por esse commit. Ele é inteligente o bastante para executar uma + busca próxima da ótima mesmo no caso de uma história complexa + não-linear com muitos ramos unificados. + + * link:everyday.html[GIT diariamente com 20 e tantos comandos] + + * linkgit:gitcvs-migration[7]: Git para usuários de CVS. + +Veja Também +-------- +linkgit:gittutorial-2[7], +linkgit:gitcvs-migration[7], +linkgit:gitcore-tutorial[7], +linkgit:gitglossary[7], +linkgit:git-help[1], +link:everyday.html[git diariamente], +link:user-manual.html[O Manual do Usuário git] + +GIT +--- +Parte da suite linkgit:git[1]. From a0f4afbe87ddda7902e36350d163dea146166550 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 30 Jun 2009 15:33:45 -0700 Subject: [PATCH 040/133] clean: require double -f options to nuke nested git repository and work tree When you have an embedded git work tree in your work tree (be it an orphaned submodule, or an independent checkout of an unrelated project), "git clean -d -f" blindly descended into it and removed everything. This is rarely what the user wants. Signed-off-by: Junio C Hamano --- Documentation/git-clean.txt | 3 +++ builtin-clean.c | 7 ++++++- dir.c | 12 ++++++++++-- dir.h | 5 ++++- refs.c | 2 +- t/t7300-clean.sh | 39 +++++++++++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index be894af39f..ae8938b2de 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -27,6 +27,9 @@ OPTIONS ------- -d:: Remove untracked directories in addition to untracked files. + If an untracked directory is managed by a different git + repository, it is not removed by default. Use -f option twice + if you really want to remove such a directory. -f:: If the git configuration specifies clean.requireForce as true, diff --git a/builtin-clean.c b/builtin-clean.c index 2d8c735d48..05c763cbec 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -31,6 +31,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) int i; int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; int ignored_only = 0, baselen = 0, config_set = 0, errors = 0; + int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf directory = STRBUF_INIT; struct dir_struct dir; static const char **pathspec; @@ -69,6 +70,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) die("clean.requireForce%s set and -n or -f not given; " "refusing to clean", config_set ? "" : " not"); + if (force > 1) + rm_flags = 0; + dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; if (!ignored) @@ -131,7 +135,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix) (matches == MATCHED_EXACTLY)) { if (!quiet) printf("Removing %s\n", qname); - if (remove_dir_recursively(&directory, 0) != 0) { + if (remove_dir_recursively(&directory, + rm_flags) != 0) { warning("failed to remove '%s'", qname); errors++; } diff --git a/dir.c b/dir.c index e05b850acf..d0999ba055 100644 --- a/dir.c +++ b/dir.c @@ -861,12 +861,20 @@ int is_empty_dir(const char *path) return ret; } -int remove_dir_recursively(struct strbuf *path, int only_empty) +int remove_dir_recursively(struct strbuf *path, int flag) { - DIR *dir = opendir(path->buf); + DIR *dir; struct dirent *e; int ret = 0, original_len = path->len, len; + int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY); + unsigned char submodule_head[20]; + if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) && + !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) + /* Do not descend and nuke a nested git work tree. */ + return 0; + + dir = opendir(path->buf); if (!dir) return -1; if (path->buf[original_len - 1] != '/') diff --git a/dir.h b/dir.h index a6314464f9..320b6a2f38 100644 --- a/dir.h +++ b/dir.h @@ -88,7 +88,10 @@ static inline int is_dot_or_dotdot(const char *name) extern int is_empty_dir(const char *dir); extern void setup_standard_excludes(struct dir_struct *dir); -extern int remove_dir_recursively(struct strbuf *path, int only_empty); + +#define REMOVE_DIR_EMPTY_ONLY 01 +#define REMOVE_DIR_KEEP_NESTED_GIT 02 +extern int remove_dir_recursively(struct strbuf *path, int flag); /* tries to remove the path with empty directories along it, ignores ENOENT */ extern int remove_path(const char *path); diff --git a/refs.c b/refs.c index e49eaa3089..dd9c9ba3f6 100644 --- a/refs.c +++ b/refs.c @@ -821,7 +821,7 @@ static int remove_empty_directories(const char *file) strbuf_init(&path, 20); strbuf_addstr(&path, file); - result = remove_dir_recursively(&path, 1); + result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY); strbuf_release(&path); diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 929d5d4d3b..118c6ebb18 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -380,4 +380,43 @@ test_expect_success 'removal failure' ' ' chmod 755 foo +test_expect_success 'nested git work tree' ' + rm -fr foo bar && + mkdir foo bar && + ( + cd foo && + git init && + >hello.world + git add . && + git commit -a -m nested + ) && + ( + cd bar && + >goodbye.people + ) && + git clean -f -d && + test -f foo/.git/index && + test -f foo/hello.world && + ! test -d bar +' + +test_expect_success 'force removal of nested git work tree' ' + rm -fr foo bar && + mkdir foo bar && + ( + cd foo && + git init && + >hello.world + git add . && + git commit -a -m nested + ) && + ( + cd bar && + >goodbye.people + ) && + git clean -f -f -d && + ! test -d foo && + ! test -d bar +' + test_done From 4f6339b0c353b5c03b566b605e50a743d97fc08e Mon Sep 17 00:00:00 2001 From: "Pickens, James E" Date: Wed, 29 Jul 2009 14:02:39 -0700 Subject: [PATCH 041/133] Demonstrate bugs when a directory is replaced with a symlink This test creates two directories, a/b and a/b-2, then replaces a/b with a symlink to a/b-2, then merges that change into the 'baseline' commit, which contains an unrelated change. There are two bugs: 1. 'git checkout' incorrectly deletes work tree file a/b-2/d. 2. 'git merge' incorrectly deletes work tree file a/b-2/d. The test goes on to create another branch in which a/b-2 is replaced with a symlink to a/b (i.e., the reverse of what was done the first time), and merge it into the 'baseline' commit. There is a different bug: 3. The merge should be clean, but git reports a conflict. Signed-off-by: James Pickens Signed-off-by: Junio C Hamano --- t/t6035-merge-dir-to-symlink.sh | 87 +++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 t/t6035-merge-dir-to-symlink.sh diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh new file mode 100755 index 0000000000..18d5f9454b --- /dev/null +++ b/t/t6035-merge-dir-to-symlink.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +test_description='merging when a directory was replaced with a symlink' +. ./test-lib.sh + +test_expect_success 'create a commit where dir a/b changed to symlink' ' + mkdir -p a/b/c a/b-2/c && + > a/b/c/d && + > a/b-2/c/d && + > a/x && + git add -A && + git commit -m base && + git tag start && + rm -rf a/b && + ln -s b-2 a/b && + git add -A && + git commit -m "dir to symlink" +' + +test_expect_failure 'keep a/b-2/c/d across checkout' ' + git checkout HEAD^0 && + git reset --hard master && + git rm --cached a/b && + git commit -m "untracked symlink remains" && + git checkout start^0 && + test -f a/b-2/c/d +' + +test_expect_failure 'checkout should not have deleted a/b-2/c/d' ' + git checkout HEAD^0 && + git reset --hard master && + git checkout start^0 && + test -f a/b-2/c/d +' + +test_expect_success 'setup for merge test' ' + git reset --hard && + test -f a/b-2/c/d && + echo x > a/x && + git add a/x && + git commit -m x && + git tag baseline +' + +test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s resolve master && + test -h a/b && + test -f a/b-2/c/d +' + +test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s recursive master && + test -h a/b && + test -f a/b-2/c/d +' + +test_expect_success 'setup a merge where dir a/b-2 changed to symlink' ' + git reset --hard && + git checkout start^0 && + rm -rf a/b-2 && + ln -s b a/b-2 && + git add -A && + git commit -m "dir a/b-2 to symlink" && + git tag test2 +' + +test_expect_failure 'merge should not have conflicts (resolve)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s resolve test2 && + test -h a/b-2 && + test -f a/b/c/d +' + +test_expect_failure 'merge should not have conflicts (recursive)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s recursive test2 && + test -h a/b-2 && + test -f a/b/c/d +' + +test_done From 77716755cbdf970fa0814a5f77c884b1f17693de Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Sun, 14 Jun 2009 15:08:28 +0200 Subject: [PATCH 042/133] lstat_cache: guard against full match of length of 'name' parameter longest_path_match() in symlinks.c does exactly what it's name says, but in some cases that match can be too long, since the has_*_leading_path() functions assumes that the match will newer be as long as the name string given to the function. fix this by adding an extra if test which checks if the match length is equal to the 'len' parameter. Signed-off-by: Junio C Hamano --- symlinks.c | 4 ++++ t/t6035-merge-dir-to-symlink.sh | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/symlinks.c b/symlinks.c index 4bdded39c5..7b0a86d357 100644 --- a/symlinks.c +++ b/symlinks.c @@ -91,6 +91,10 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len, longest_path_match(name, len, cache->path, cache->len, &previous_slash); match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK); + + if (!(track_flags & FL_FULLPATH) && match_len == len) + match_len = last_slash = previous_slash; + if (match_flags && match_len == cache->len) return match_flags; /* diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh index 18d5f9454b..ba90fc53e0 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6035-merge-dir-to-symlink.sh @@ -26,7 +26,7 @@ test_expect_failure 'keep a/b-2/c/d across checkout' ' test -f a/b-2/c/d ' -test_expect_failure 'checkout should not have deleted a/b-2/c/d' ' +test_expect_success 'checkout should not have deleted a/b-2/c/d' ' git checkout HEAD^0 && git reset --hard master && git checkout start^0 && From b6986d8a75812a003a1623e0f0dff93c4a026b44 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 29 Jul 2009 20:22:25 -0700 Subject: [PATCH 043/133] git-checkout: be careful about untracked symlinks This fixes the case where an untracked symlink that points at a directory with tracked paths confuses the checkout logic, demostrated in t6035. Signed-off-by: Junio C Hamano --- cache.h | 3 +++ entry.c | 15 ++++++++++++++- t/t6035-merge-dir-to-symlink.sh | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cache.h b/cache.h index e6c7f3307d..9222774e6c 100644 --- a/cache.h +++ b/cache.h @@ -468,6 +468,9 @@ extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_obje extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); +/* "careful lstat()" */ +extern int check_path(const char *path, int len, struct stat *st); + #define REFRESH_REALLY 0x0001 /* ignore_valid */ #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ diff --git a/entry.c b/entry.c index d3e86c722a..f276cf3b88 100644 --- a/entry.c +++ b/entry.c @@ -175,6 +175,19 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout return 0; } +/* + * This is like 'lstat()', except it refuses to follow symlinks + * in the path. + */ +int check_path(const char *path, int len, struct stat *st) +{ + if (has_symlink_leading_path(path, len)) { + errno = ENOENT; + return -1; + } + return lstat(path, st); +} + int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) { static char path[PATH_MAX + 1]; @@ -188,7 +201,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); len += ce_namelen(ce); - if (!lstat(path, &st)) { + if (!check_path(path, len, &st)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh index ba90fc53e0..a0ddf1e0f4 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6035-merge-dir-to-symlink.sh @@ -17,7 +17,7 @@ test_expect_success 'create a commit where dir a/b changed to symlink' ' git commit -m "dir to symlink" ' -test_expect_failure 'keep a/b-2/c/d across checkout' ' +test_expect_success 'keep a/b-2/c/d across checkout' ' git checkout HEAD^0 && git reset --hard master && git rm --cached a/b && From c94736a27f89bfc268aed0de7264deecdc136a58 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 30 Jul 2009 17:38:15 -0700 Subject: [PATCH 044/133] merge-recursive: don't segfault while handling rename clashes When a branch moves A to B while the other branch created B (or moved C to B), the code tried to rename one of them to B~something to preserve both versions, and failed to register temporary resolution for the original path B at stage#0 during virtual ancestor computation. This left the index in unmerged state and caused a segfault. A better solution is to merge these two versions of B's in place and use the (potentially conflicting) result as the intermediate merge result in the virtual ancestor. Signed-off-by: Junio C Hamano --- merge-recursive.c | 28 ++++++++++++++-- t/t6036-recursive-corner-cases.sh | 55 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100755 t/t6036-recursive-corner-cases.sh diff --git a/merge-recursive.c b/merge-recursive.c index b97026bd5c..54d23b214c 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -947,9 +947,31 @@ static int process_renames(struct merge_options *o, "%s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); - new_path = unique_path(o, ren1_dst, branch2); - output(o, 1, "Adding as %s instead", new_path); - update_file(o, 0, dst_other.sha1, dst_other.mode, new_path); + if (o->call_depth) { + struct merge_file_info mfi; + struct diff_filespec one, a, b; + + one.path = a.path = b.path = + (char *)ren1_dst; + hashcpy(one.sha1, null_sha1); + one.mode = 0; + hashcpy(a.sha1, ren1->pair->two->sha1); + a.mode = ren1->pair->two->mode; + hashcpy(b.sha1, dst_other.sha1); + b.mode = dst_other.mode; + mfi = merge_file(o, &one, &a, &b, + branch1, + branch2); + output(o, 1, "Adding merged %s", ren1_dst); + update_file(o, 0, + mfi.sha, + mfi.mode, + ren1_dst); + } else { + new_path = unique_path(o, ren1_dst, branch2); + output(o, 1, "Adding as %s instead", new_path); + update_file(o, 0, dst_other.sha1, dst_other.mode, new_path); + } } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) { ren2 = item->util; clean_merge = 0; diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh new file mode 100755 index 0000000000..b874141658 --- /dev/null +++ b/t/t6036-recursive-corner-cases.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +test_description='recursive merge corner cases' + +. ./test-lib.sh + +# +# L1 L2 +# o---o +# / \ / \ +# o X ? +# \ / \ / +# o---o +# R1 R2 +# + +test_expect_success setup ' + ten="0 1 2 3 4 5 6 7 8 9" + for i in $ten + do + echo line $i in a sample file + done >one && + for i in $ten + do + echo line $i in another sample file + done >two && + git add one two && + test_tick && git commit -m initial && + + git branch L1 && + git checkout -b R1 && + git mv one three && + test_tick && git commit -m R1 && + + git checkout L1 && + git mv two three && + test_tick && git commit -m L1 && + + git checkout L1^0 && + test_tick && git merge -s ours R1 && + git tag L2 && + + git checkout R1^0 && + test_tick && git merge -s ours L1 && + git tag R2 +' + +test_expect_success merge ' + git reset --hard && + git checkout L2^0 && + + test_must_fail git merge -s recursive R2^0 +' + +test_done From 86b5efb2864ca50d86437f94ec4c26042cba193e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 27 Jul 2009 20:49:56 +0200 Subject: [PATCH 045/133] parse-opt: optionally show "--no-" option string It is usually better to have positive options, to avoid confusing double negations. However, sometimes it is desirable to show the negative option in the help. Introduce the flag PARSE_OPT_NEGHELP to do that. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- parse-options.c | 6 ++++-- parse-options.h | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/parse-options.c b/parse-options.c index f7ce523a61..3b71fbb541 100644 --- a/parse-options.c +++ b/parse-options.c @@ -511,7 +511,7 @@ static int usage_with_options_internal(const char * const *usagestr, continue; pos = fprintf(stderr, " "); - if (opts->short_name) { + if (opts->short_name && !(opts->flags & PARSE_OPT_NEGHELP)) { if (opts->flags & PARSE_OPT_NODASH) pos += fprintf(stderr, "%c", opts->short_name); else @@ -520,7 +520,9 @@ static int usage_with_options_internal(const char * const *usagestr, if (opts->long_name && opts->short_name) pos += fprintf(stderr, ", "); if (opts->long_name) - pos += fprintf(stderr, "--%s", opts->long_name); + pos += fprintf(stderr, "--%s%s", + (opts->flags & PARSE_OPT_NEGHELP) ? "no-" : "", + opts->long_name); if (opts->type == OPTION_NUMBER) pos += fprintf(stderr, "-NUM"); diff --git a/parse-options.h b/parse-options.h index aba30671dc..b32587ad7c 100644 --- a/parse-options.h +++ b/parse-options.h @@ -36,6 +36,7 @@ enum parse_opt_option_flags { PARSE_OPT_LASTARG_DEFAULT = 16, PARSE_OPT_NODASH = 32, PARSE_OPT_LITERAL_ARGHELP = 64, + PARSE_OPT_NEGHELP = 128, }; struct option; @@ -80,6 +81,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets * (i.e. '') in the help message. * Useful for options with multiple parameters. + * PARSE_OPT_NEGHELP: says that the long option should always be shown with + * the --no prefix in the usage message. Sometimes + * useful for users of OPTION_NEGBIT. * * `callback`:: * pointer to the callback to use for OPTION_CALLBACK. From 79559f27be7e2963213d840a857dee92c579843f Mon Sep 17 00:00:00 2001 From: Geoffrey Irving Date: Mon, 27 Jul 2009 22:20:22 -0400 Subject: [PATCH 046/133] git fast-export: add --no-data option When using git fast-export and git fast-import to rewrite the history of a repository with large binary files, almost all of the time is spent dealing with blobs. This is extremely inefficient if all we want to do is rewrite the commits and tree structure. --no-data skips the output of blobs and writes SHA-1s instead of marks, which provides a massive speedup. Signed-off-by: Geoffrey Irving Signed-off-by: Junio C Hamano --- Documentation/git-fast-export.txt | 8 ++++++++ builtin-fast-export.c | 9 ++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt index af2328d401..75b06f33e7 100644 --- a/Documentation/git-fast-export.txt +++ b/Documentation/git-fast-export.txt @@ -82,6 +82,14 @@ marks the same across runs. allow that. So fake a tagger to be able to fast-import the output. +--no-data:: + Skip output of blob objects and instead refer to blobs via + their original SHA-1 hash. This is useful when rewriting the + directory structure or history of a repository without + touching the contents of individual files. Note that the + resulting stream can only be used by a repository which + already contains the necessary objects. + [git-rev-list-args...]:: A list of arguments, acceptable to 'git-rev-parse' and 'git-rev-list', that specifies the specific objects and references diff --git a/builtin-fast-export.c b/builtin-fast-export.c index c48c18d0c8..b0a4029c94 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -26,6 +26,7 @@ static int progress; static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT; static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT; static int fake_missing_tagger; +static int no_data; static int parse_opt_signed_tag_mode(const struct option *opt, const char *arg, int unset) @@ -116,6 +117,9 @@ static void handle_object(const unsigned char *sha1) char *buf; struct object *object; + if (no_data) + return; + if (is_null_sha1(sha1)) return; @@ -173,7 +177,7 @@ static void show_filemodify(struct diff_queue_struct *q, * Links refer to objects in another repositories; * output the SHA-1 verbatim. */ - if (S_ISGITLINK(spec->mode)) + if (no_data || S_ISGITLINK(spec->mode)) printf("M %06o %s %s\n", spec->mode, sha1_to_hex(spec->sha1), spec->path); else { @@ -580,6 +584,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) "Import marks from this file"), OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger, "Fake a tagger when tags lack one"), + { OPTION_NEGBIT, 0, "data", &no_data, NULL, + "Skip output of blob data", + PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 }, OPT_END() }; From ebdaae372b460ffdf5d153dcd0ac235d52b0d2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Thu, 30 Jul 2009 13:41:57 +0200 Subject: [PATCH 047/133] config: Keep inner whitespace verbatim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Configuration values are expected to be quoted when they have leading or trailing whitespace, but inner whitespace should be kept verbatim even if the value is not quoted. This is already documented in git-config(1), but the code caused inner whitespace to be collapsed to a single space, breaking, for example, clones from a path that has two consecutive spaces in it, as future fetches would only see a single space. Reported-by: John te Bokkel Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- config.c | 10 ++++------ t/t1300-repo-config.sh | 5 +++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/config.c b/config.c index 1682273c12..7e5594b65e 100644 --- a/config.c +++ b/config.c @@ -62,7 +62,8 @@ static char *parse_value(void) if (comment) continue; if (isspace(c) && !quote) { - space = 1; + if (len) + space++; continue; } if (!quote) { @@ -71,11 +72,8 @@ static char *parse_value(void) continue; } } - if (space) { - if (len) - value[len++] = ' '; - space = 0; - } + for (; space; space--) + value[len++] = ' '; if (c == '\\') { c = get_next_char(); switch (c) { diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 43ea283242..91cbd551d8 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -733,6 +733,11 @@ echo >>result test_expect_success '--null --get-regexp' 'cmp result expect' +test_expect_success 'inner whitespace kept verbatim' ' + git config section.val "foo bar" && + test "z$(git config section.val)" = "zfoo bar" +' + test_expect_success SYMLINKS 'symlinked configuration' ' ln -s notyet myconfig && From a4782b3d6e298488d2265b6a24cab5aab876f37e Mon Sep 17 00:00:00 2001 From: "Wesley J. Landaker" Date: Thu, 30 Jul 2009 17:08:53 -0600 Subject: [PATCH 048/133] Documentation: git-send-email: fix submission port number The current documentation confuses non-standard SSL smtp port 465 with submission port 587 (RFC 4406). This patch just changes the referenced number. Signed-off-by: Wesley J. Landaker Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index d6b192b7b9..1c943510ad 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -143,7 +143,7 @@ user is prompted for a password while the input is masked for privacy. --smtp-server-port=:: Specifies a port different from the default port (SMTP servers typically listen to smtp port 25 and ssmtp port - 465); symbolic port names (e.g. "submission" instead of 465) + 465); symbolic port names (e.g. "submission" instead of 587) are also accepted. The port can also be set with the 'sendemail.smtpserverport' configuration variable. From 2da846e70945a55b2b3378f4aa5fadcc404f0f01 Mon Sep 17 00:00:00 2001 From: "Wesley J. Landaker" Date: Fri, 31 Jul 2009 11:45:00 -0600 Subject: [PATCH 049/133] Documentation: git-send-email: correct statement about standard ports The current documentation states that servers typically listen on port 465 and calls this "ssmtp". While it's true that many mail servers use port 465 for SSL smtp, this is non-standard, and hails from the days before smtp and submission TLS support, that arrived in RFC2487 and RFC3207. Port 465 is actually assigned by IANA for unrelated purposes, and is mostly still used by mail servers today only to support Outlook Express. In any case, this patch helps the documentation better reflect both standards and reality, while still helpfully mentioning ports numbers that a user may wish to specify. Signed-off-by: Wesley J. Landaker Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 1c943510ad..767cf4d4bd 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -142,8 +142,9 @@ user is prompted for a password while the input is masked for privacy. --smtp-server-port=:: Specifies a port different from the default port (SMTP - servers typically listen to smtp port 25 and ssmtp port - 465); symbolic port names (e.g. "submission" instead of 587) + servers typically listen to smtp port 25, but may also listen to + submission port 587, or the common SSL smtp port 465); + symbolic port names (e.g. "submission" instead of 587) are also accepted. The port can also be set with the 'sendemail.smtpserverport' configuration variable. From 07a4a3b4962e1fd4e40fd877427cddd7428c1bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Goddard=20Rosa?= Date: Fri, 31 Jul 2009 13:50:50 -0300 Subject: [PATCH 050/133] Fix typos on pt_BR/gittutorial.txt translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With extra fixes from Thadeu and Carlos as well. Signed-off-by: André Goddard Rosa Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Carlos R. Mafra Signed-off-by: Junio C Hamano --- Documentation/pt_BR/gittutorial.txt | 142 ++++++++++++++-------------- 1 file changed, 69 insertions(+), 73 deletions(-) diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt index f368b1b518..81e7ad7df4 100644 --- a/Documentation/pt_BR/gittutorial.txt +++ b/Documentation/pt_BR/gittutorial.txt @@ -16,7 +16,7 @@ Este tutorial explica como importar um novo projeto para o git, adicionar mudanças a ele, e compartilhar mudanças com outros desenvolvedores. -If, ao invés disso, você está interessado primariamente em usar git para +Se, ao invés disso, você está interessado primariamente em usar git para obter um projeto, por exemplo, para testar a última versão, você pode preferir começar com os primeiros dois capítulos de link:user-manual.html[O Manual do Usuário Git]. @@ -37,9 +37,8 @@ $ git help log Com a última forma, você pode usar o visualizador de manual de sua escolha; veja linkgit:git-help[1] para maior informação. -É uma boa idéia se introduzir ao git com seu nome e endereço público de -email antes de fazer qualquer operação. A maneira mais fácil de fazê-lo -é: +É uma boa idéia informar ao git seu nome e endereço público de email +antes de fazer qualquer operação. A maneira mais fácil de fazê-lo é: ------------------------------------------------ $ git config --global user.name "Seu Nome Vem Aqui" @@ -51,7 +50,7 @@ Importando um novo projeto ----------------------- Assuma que você tem um tarball project.tar.gz com seu trabalho inicial. -Você pode colocá-lo sob controle de revisão git como a seguir. +Você pode colocá-lo sob controle de revisão git da seguinte forma: ------------------------------------------------ $ tar xzf project.tar.gz @@ -76,7 +75,7 @@ $ git add . ------------------------------------------------ Este instantâneo está agora armazenado em uma área temporária que o git -chama de "index" ou índice. Você pode permanetemente armazenar o +chama de "index" ou índice. Você pode armazenar permanentemente o conteúdo do índice no repositório com 'git-commit': ------------------------------------------------ @@ -142,7 +141,7 @@ novos), adicioná-los ao índices, e gravar, tudo em um único passo. Uma nota em mensagens de commit: Apesar de não ser exigido, é uma boa idéia começar a mensagem com uma simples e curta (menos de 50 caracteres) linha sumarizando a mudança, seguida de uma linha em branco -e, então, uma descrição mais detalhada. Ferramentas que transformam +e, então, uma descrição mais detalhada. Ferramentas que transformam commits em email, por exemplo, usam a primeira linha no campo de cabeçalho Subject: e o resto no corpo. @@ -150,7 +149,7 @@ Git rastreia conteúdo, não arquivos ---------------------------- Muitos sistemas de controle de revisão provêem um comando `add` que diz -ao sistema para começar a rastrear mudanças em um novo arquivo. O +ao sistema para começar a rastrear mudanças em um novo arquivo. O comando `add` do git faz algo mais simples e mais poderoso: 'git-add' é usado tanto para arquivos novos e arquivos recentemente modificados, e em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena @@ -183,7 +182,7 @@ Gerenciando "branches"/ramos ----------------- Um simples repositório git pode manter múltiplos ramos de -desenvolvimento. Para criar um novo ramo chamado "experimental", use +desenvolvimento. Para criar um novo ramo chamado "experimental", use ------------------------------------------------ $ git branch experimental @@ -203,14 +202,14 @@ você vai obter uma lista de todos os ramos existentes: ------------------------------------------------ O ramo "experimental" é o que você acaba de criar, e o ramo "master" é o -ramo padrão que foi criado pra você automaticamente. O asterisco marca +ramo padrão que foi criado pra você automaticamente. O asterisco marca o ramo em que você está atualmente; digite ------------------------------------------------ $ git checkout experimental ------------------------------------------------ -para mudar para o ramo experimental. Agora edite um arquivo, grave a +para mudar para o ramo experimental. Agora edite um arquivo, grave a mudança, e mude de volta para o ramo master: ------------------------------------------------ @@ -230,14 +229,14 @@ $ git commit -a ------------------------------------------------ neste ponto, os dois ramos divergiram, com diferentes mudanças feitas em -cada um. Para unificar as mudanças feitas no experimental para o +cada um. Para unificar as mudanças feitas no experimental para o master, execute ------------------------------------------------ $ git merge experimental ------------------------------------------------ -Se as mudanças não conflitam, está pronto. Se existirem conflitos, +Se as mudanças não conflitarem, estará pronto. Se existirem conflitos, marcadores serão deixados nos arquivos problemáticos exibindo o conflito; @@ -245,7 +244,7 @@ conflito; $ git diff ------------------------------------------------ -vai exibir isto. Após você editar os arquivos para resolver os +vai exibir isto. Após você editar os arquivos para resolver os conflitos, ------------------------------------------------ @@ -273,7 +272,7 @@ Se você desenvolve em um ramo ideia-louca, e se arrepende, você pode sempre remover o ramo com ------------------------------------- -$ git branch -D crazy-idea +$ git branch -D ideia-louca ------------------------------------- Ramos são baratos e fáceis, então isto é uma boa maneira de experimentar @@ -293,7 +292,7 @@ bob$ git clone /home/alice/project myrepo ------------------------------------------------ Isso cria um novo diretório "myrepo" contendo um clone do repositório de -Alice. O clone está no mesmo pé que o projeto original, possuindo sua +Alice. O clone está no mesmo pé que o projeto original, possuindo sua própria cópia da história do projeto original. Bob então faz algumas mudanças e as grava: @@ -305,7 +304,7 @@ bob$ git commit -a ------------------------------------------------ Quanto está pronto, ele diz a Alice para puxar as mudanças do -repositório em /home/bob/myrepo. Ela o faz com: +repositório em /home/bob/myrepo. Ela o faz com: ------------------------------------------------ alice$ cd /home/alice/project @@ -314,14 +313,14 @@ alice$ git pull /home/bob/myrepo master Isto unifica as mudanças do ramo "master" do Bob ao ramo atual de Alice. Se Alice fez suas próprias mudanças no intervalo, ela, então, pode -precisar corrigir manualmente quaiquer conflitos. (Note que o argumento +precisar corrigir manualmente quaisquer conflitos. (Note que o argumento "master" no comando acima é, de fato, desnecessário, já que é o padrão.) O comando "pull" executa, então, duas operações: ele obtém mudanças de um ramo remoto, e, então, as unifica no ramo atual. Note que, em geral, Alice gostaria que suas mudanças locais fossem -gravadas antes de iniciar este "pull". Se o trabalho de Bobo conflita +gravadas antes de iniciar este "pull". Se o trabalho de Bob conflita com o que Alice fez desde que suas histórias se ramificaram, Alice irá usar seu diretório de trabalho e o índice para resolver conflitos, e mudanças locais existentes irão interferir com o processo de resolução @@ -341,18 +340,18 @@ alice$ git log -p HEAD..FETCH_HEAD Esta operação é segura mesmo se Alice tem mudanças locais não gravadas. A notação de intervalo "HEAD..FETCH_HEAD" significa mostrar tudo que é -alcançável de FETCH_HEAD mas exclua tudo que é alcançável de HEAD. Alcie -já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob tem -em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando. +alcançável de FETCH_HEAD mas exclua tudo o que é alcançável de HEAD. +Alice já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob +tem em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando. -Se Alice quer visualizar o que Bob fez desde que suas história +Se Alice quer visualizar o que Bob fez desde que suas histórias se ramificaram, ela pode disparar o seguinte comando: ------------------------------------------------ $ gitk HEAD..FETCH_HEAD ------------------------------------------------ -Isto usar a mesma notação de intervaldo que vimos antes com 'git log'. +Isto usa a mesma notação de intervalo que vimos antes com 'git log'. Alice pode querer ver o que ambos fizeram desde que ramificaram. Ela pode usar a forma com três pontos ao invés da forma com dois pontos: @@ -361,23 +360,21 @@ pode usar a forma com três pontos ao invés da forma com dois pontos: $ gitk HEAD...FETCH_HEAD ------------------------------------------------ -Isto significa "mostre tudo que é alcançavel de qualquer um, mas exclua -tudo que é alcançavel a partir de ambos". -This means "show everything that is reachable from either one, but -exclude anything that is reachable from both of them". +Isto significa "mostre tudo que é alcançável de qualquer um deles, mas +exclua tudo que é alcançável a partir de ambos". Por favor, note que essas notações de intervalo podem ser usadas tanto com gitk quanto com "git log". -Apoós inspecionar o que Bob fez, se não há nada urgente, Alice pode -decidir continuar trabalhando sem puxar de Bob. Se a história de Bob +Após inspecionar o que Bob fez, se não há nada urgente, Alice pode +decidir continuar trabalhando sem puxar de Bob. Se a história de Bob tem alguma coisa que Alice precisa imediatamente, Alice pode optar por separar seu trabalho em progresso primeiro, fazer um "pull", e, então, finalmente, retomar seu trabalho em progresso em cima da história resultante. -Quanto você está trabalhando em um pequeno grupo unido, não é incomum -interagir com o mesmo repositório várias e várias vezes. Definindo um +Quando você está trabalhando em um pequeno grupo unido, não é incomum +interagir com o mesmo repositório várias e várias vezes. Definindo um repositório remoto antes de tudo, você pode fazê-lo mais facilmente: ------------------------------------------------ @@ -394,7 +391,7 @@ alice$ git fetch bob Diferente da forma longa, quando Alice obteve de Bob usando um repositório remoto antes definido com 'git-remote', o que foi obtido é -armazenado um ramo remoto, neste caso `bob/master`. Então, após isso: +armazenado em um ramo remoto, neste caso `bob/master`. Então, após isso: ------------------------------------- alice$ git log -p master..bob/master @@ -417,7 +414,7 @@ alice$ git pull . remotes/bob/master ------------------------------------- Note que 'git pull' sempre unifica ao ramo atual, independente do que -mais foi dado na linha de comando. +mais foi passado na linha de comando. Depois, Bob pode atualizar seu repositório com as últimas mudanças de Alice, usando @@ -428,7 +425,7 @@ bob$ git pull Note que ele não precisa dar o caminho do repositório de Alice; quando Bob clonou seu repositório, o git armazenou a localização de seu -repositório na configuração do repositório, e essa localização é usada +repositório na configuração do mesmo, e essa localização é usada para puxar: ------------------------------------- @@ -459,15 +456,15 @@ Alternativamente, o git tem um protocolo nativo, ou pode usar rsync ou http; veja linkgit:git-pull[1] para detalhes. Git pode também ser usado em um modo parecido com CVS, com um -repositório central para o qual que vários usuários empurram -modificações; veja linkgit:git-push[1] e linkgit:gitcvs-migration[7]. +repositório central para o qual vários usuários empurram modificações; +veja linkgit:git-push[1] e linkgit:gitcvs-migration[7]. Explorando história ----------------- A história no git é representada como uma série de commits -interrelacionados. Nós já vimos que o comando 'git-log' pode listar -esses commits. Note que a primeira linha de cama entrada no log também +interrelacionados. Nós já vimos que o comando 'git-log' pode listar +esses commits. Note que a primeira linha de cada entrada no log também dá o nome para o commit: ------------------------------------- @@ -486,9 +483,9 @@ commit. $ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7 ------------------------------------- -Mas há outras formas de se referir a commits. Você pode usar qualquer -parte inicial do nome que seja longo o bastante para unicamente -identificar o commit: +Mas há outras formas de se referir aos commits. Você pode usar qualquer +parte inicial do nome que seja longo o bastante para identificar +unicamente o commit: ------------------------------------- $ git show c82a22c39c # os primeiros caracteres do nome são o bastante @@ -497,7 +494,7 @@ $ git show HEAD # a ponta do ramo atual $ git show experimental # a ponta do ramo "experimental" ------------------------------------- -Todo commit usualmente tem um commit "pai" que aponta para o estado +Todo commit normalmente tem um commit "pai" que aponta para o estado anterior do projeto: ------------------------------------- @@ -513,19 +510,19 @@ $ git show HEAD^1 # mostra o primeiro pai de HEAD (o mesmo que HEAD^) $ git show HEAD^2 # mostra o segundo pai de HEAD ------------------------------------- -Você também pode dar aos commits nomes seus; após executar +Você também pode dar aos commits nomes à sua escolha; após executar ------------------------------------- $ git tag v2.5 1b2e1d63ff ------------------------------------- -você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende +você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende compartilhar esse nome com outras pessoas (por exemplo, para identificar -uma versão de lançamento), você deve criar um objeto "tag", e talvez +uma versão de lançamento), você deveria criar um objeto "tag", e talvez assiná-lo; veja linkgit:git-tag[1] para detalhes. Qualquer comando git que precise conhecer um commit pode receber -quaisquer desses nomes. Por exemplo: +quaisquer desses nomes. Por exemplo: ------------------------------------- $ git diff v2.5 HEAD # compara o HEAD atual com v2.5 @@ -537,8 +534,8 @@ $ git reset --hard HEAD^ # reseta seu ramo atual e seu diretório de Seja cuidadoso com o último comando: além de perder quaisquer mudanças em seu diretório de trabalho, ele também remove todos os commits -posteriores desse ramo. Se esse ramo é o único ramo contendo esses -commits, eles serão perdidos. Também, não use 'git-reset' num ramo +posteriores desse ramo. Se esse ramo é o único ramo contendo esses +commits, eles serão perdidos. Também, não use 'git-reset' num ramo publicamente visível de onde outros desenvolvedores puxam, já que vai forçar unificações desnecessárias para que outros desenvolvedores limpem a história. Se você precisa desfazer mudanças que você empurrou, use @@ -551,10 +548,10 @@ projeto, então $ git grep "hello" v2.5 ------------------------------------- -procura por todas as ocorreências de "hello" em v2.5. +procura por todas as ocorrências de "hello" em v2.5. Se você deixar de fora o nome do commit, 'git-grep' irá procurar -quaisquer dos arquivos que ele gerencia no diretório corrente. Então +quaisquer dos arquivos que ele gerencia no diretório corrente. Então ------------------------------------- $ git grep "hello" @@ -564,8 +561,7 @@ $ git grep "hello" git. Muitos comandos git também recebem um conjunto de commits, o que pode -ser especificado de um bom número de formas. Aqui estão alguns exemplos -com 'git-log': +ser especificado de várias formas. Aqui estão alguns exemplos com 'git-log': ------------------------------------- $ git log v2.5..v2.6 # commits entre v2.5 e v2.6 @@ -584,7 +580,7 @@ comum algum tempo atrás, então $ git log stable..master ------------------------------------- -irá listas os commits feitos no ramo "master" mas não no ramo +irá listar os commits feitos no ramo "master" mas não no ramo "stable", enquanto ------------------------------------- @@ -594,26 +590,26 @@ $ git log master..stable irá listar a lista de commits feitos no ramo "stable" mas não no ramo "master". -O comando 'git-log' tem uma fraquza: ele precisa mostrar os commits em +O comando 'git-log' tem uma fraqueza: ele precisa mostrar os commits em uma lista. Quando a história tem linhas de desenvolvimento que divergiram e então foram unificadas novamente, a ordem em que 'git-log' -apresenta essas mudanças é insignificante. +apresenta essas mudanças é irrelevante. A maioria dos projetos com múltiplos contribuidores (como o kernel -linux, ou o git mesmo) tem unificações frequentes, e 'gitk' faz um -trabalho melhor de visualizar sua história. Por exemplo, +Linux, ou o próprio git) tem unificações frequentes, e 'gitk' faz um +trabalho melhor de visualizar sua história. Por exemplo, ------------------------------------- $ gitk --since="2 weeks ago" drivers/ ------------------------------------- -permite você navegar em quaisquer commits desde as últimas duas semanas -de commits que modificaram arquivos sob o diretório "drivers". (Nota: +permite a você navegar em quaisquer commits desde as últimas duas semanas +de commits que modificaram arquivos sob o diretório "drivers". (Nota: você pode ajustar as fontes do gitk segurando a tecla control enquanto pressiona "-" ou "+".) -Finalmente, a maioria dos comandos que recebem nomes de arquivo -te permitirão opcionalmente preceder qualquer nome de arquivo por um +Finalmente, a maioria dos comandos que recebem nomes de arquivo permitirão +também, opcionalmente, preceder qualquer nome de arquivo por um commit, para especificar uma versão particular do arquivo: ------------------------------------- @@ -630,33 +626,33 @@ Próximos passos ---------- Este tutorial deve ser o bastante para operar controle de revisão -distribuído básico para seus projetos. No entanto, para entender +distribuído básico para seus projetos. No entanto, para entender plenamente a profundidade e o poder do git você precisa entender duas idéias simples nas quais ele se baseia: * A base de objetos é um sistema bem elegante usado para armazenar a história de seu projeto--arquivos, diretórios, e commits. - * O arquivo de índica é um cache do estado de uma árvore de diretório, + * O arquivo de índice é um cache do estado de uma árvore de diretório, usado para criar commits, restaurar diretórios de trabalho, e - compreender as várias árvores involvidas em uma unificação. + armazenar as várias árvores envolvidas em uma unificação. -Parte dois deste tutorial explica a base de objetos, o arquivo de +A parte dois deste tutorial explica a base de objetos, o arquivo de índice, e algumas outras coisinhas que você vai precisar pra usar o máximo do git. Você pode encontrá-la em linkgit:gittutorial-2[7]. -Se você não quer continuar do jeito certo, algumas outras disgressões -que podem ser interessantes neste ponto são: +Se você não quiser continuar com o tutorial agora nesse momento, algumas +outras digressões que podem ser interessantes neste ponto são: * linkgit:git-format-patch[1], linkgit:git-am[1]: Estes convertem - séries de commits em patches em email, e vice-versa, úteis para - projetos como o kernel linux que dependem pesadamente em patches + séries de commits em patches para email, e vice-versa, úteis para + projetos como o kernel Linux que dependem fortemente de patches enviados por email. * linkgit:git-bisect[1]: Quando há uma regressão em seu projeto, uma forma de rastrear um bug é procurando pela história para encontrar o - commit culpado. Git bisect pode ajudar a executar uma busca binária - por esse commit. Ele é inteligente o bastante para executar uma + commit culpado. Git bisect pode ajudar a executar uma busca binária + por esse commit. Ele é inteligente o bastante para executar uma busca próxima da ótima mesmo no caso de uma história complexa não-linear com muitos ramos unificados. @@ -664,7 +660,7 @@ que podem ser interessantes neste ponto são: * linkgit:gitcvs-migration[7]: Git para usuários de CVS. -Veja Também +VEJA TAMBÉM -------- linkgit:gittutorial-2[7], linkgit:gitcvs-migration[7], From 6639ffc2e026b34b906854b8c60bd72d4b95e78d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 3 Aug 2009 21:13:21 -0700 Subject: [PATCH 051/133] technical-docs: document tree-walking API Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- Documentation/technical/api-tree-walking.txt | 147 ++++++++++++++++++- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt index e3ddf91284..55b728632c 100644 --- a/Documentation/technical/api-tree-walking.txt +++ b/Documentation/technical/api-tree-walking.txt @@ -1,12 +1,145 @@ tree walking API ================ -Talk about , things like +The tree walking API is used to traverse and inspect trees. -* struct tree_desc -* init_tree_desc -* tree_entry_extract -* update_tree_entry -* get_tree_entry +Data Structures +--------------- -(JC, Linus) +`struct name_entry`:: + + An entry in a tree. Each entry has a sha1 identifier, pathname, and + mode. + +`struct tree_desc`:: + + A semi-opaque data structure used to maintain the current state of the + walk. ++ +* `buffer` is a pointer into the memory representation of the tree. It always +points at the current entry being visited. + +* `size` counts the number of bytes left in the `buffer`. + +* `entry` points to the current entry being visited. + +`struct traverse_info`:: + + A structure used to maintain the state of a traversal. ++ +* `prev` points to the traverse_info which was used to descend into the +current tree. If this is the top-level tree `prev` will point to +a dummy traverse_info. + +* `name` is the entry for the current tree (if the tree is a subtree). + +* `pathlen` is the length of the full path for the current tree. + +* `conflicts` can be used by callbacks to maintain directory-file conflicts. + +* `fn` is a callback called for each entry in the tree. See Traversing for more +information. + +* `data` can be anything the `fn` callback would want to use. + +Initializing +------------ + +`init_tree_desc`:: + + Initialize a `tree_desc` and decode its first entry. The buffer and + size parameters are assumed to be the same as the buffer and size + members of `struct tree`. + +`fill_tree_descriptor`:: + + Initialize a `tree_desc` and decode its first entry given the sha1 of + a tree. Returns the `buffer` member if the sha1 is a valid tree + identifier and NULL otherwise. + +`setup_traverse_info`:: + + Initialize a `traverse_info` given the pathname of the tree to start + traversing from. The `base` argument is assumed to be the `path` + member of the `name_entry` being recursed into unless the tree is a + top-level tree in which case the empty string ("") is used. + +Walking +------- + +`tree_entry`:: + + Visit the next entry in a tree. Returns 1 when there are more entries + left to visit and 0 when all entries have been visited. This is + commonly used in the test of a while loop. + +`tree_entry_len`:: + + Calculate the length of a tree entry's pathname. This utilizes the + memory structure of a tree entry to avoid the overhead of using a + generic strlen(). + +`update_tree_entry`:: + + Walk to the next entry in a tree. This is commonly used in conjunction + with `tree_entry_extract` to inspect the current entry. + +`tree_entry_extract`:: + + Decode the entry currently being visited (the one pointed to by + `tree_desc's` `entry` member) and return the sha1 of the entry. The + `pathp` and `modep` arguments are set to the entry's pathname and mode + respectively. + +`get_tree_entry`:: + + Find an entry in a tree given a pathname and the sha1 of a tree to + search. Returns 0 if the entry is found and -1 otherwise. The third + and fourth parameters are set to the entry's sha1 and mode + respectively. + +Traversing +---------- + +`traverse_trees`:: + + Traverse `n` number of trees in parallel. The `fn` callback member of + `traverse_info` is called once for each tree entry. + +`traverse_callback_t`:: + The arguments passed to the traverse callback are as follows: ++ +* `n` counts the number of trees being traversed. + +* `mask` has its nth bit set if something exists in the nth entry. + +* `dirmask` has its nth bit set if the nth tree's entry is a directory. + +* `entry` is an array of size `n` where the nth entry is from the nth tree. + +* `info` maintains the state of the traversal. + ++ +Returning a negative value will terminate the traversal. Otherwise the +return value is treated as an update mask. If the nth bit is set the nth tree +will be updated and if the bit is not set the nth tree entry will be the +same in the next callback invocation. + +`make_traverse_path`:: + + Generate the full pathname of a tree entry based from the root of the + traversal. For example, if the traversal has recursed into another + tree named "bar" the pathname of an entry "baz" in the "bar" + tree would be "bar/baz". + +`traverse_path_len`:: + + Calculate the length of a pathname returned by `make_traverse_path`. + This utilizes the memory structure of a tree entry to avoid the + overhead of using a generic strlen(). + +Authors +------- + +Written by Junio C Hamano and Linus Torvalds + From b7da721f02638083cc3debfd9ae7221d76a96b26 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Fri, 31 Jul 2009 08:48:49 +0200 Subject: [PATCH 052/133] gitweb: fix 'Use of uninitialized value' error in href() Equality between file_parent and file_name was being checked without a preliminary check for existence of the parameters. Fix by wrapping the equality check in appropriate if (defined ...), rearranging the lines to prevent excessive length. Signed-off-by: Giuseppe Bilotta Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7fbd5ff89e..37120a3e60 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -940,10 +940,13 @@ sub href { if (defined $params{'hash_parent_base'}) { $href .= esc_url($params{'hash_parent_base'}); # skip the file_parent if it's the same as the file_name - delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'}; - if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) { - $href .= ":/".esc_url($params{'file_parent'}); - delete $params{'file_parent'}; + if (defined $params{'file_parent'}) { + if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) { + delete $params{'file_parent'}; + } elsif ($params{'file_parent'} !~ /\.\./) { + $href .= ":/".esc_url($params{'file_parent'}); + delete $params{'file_parent'}; + } } $href .= ".."; delete $params{'hash_parent'}; From 5a7a3671b74c043216549b94a718da04cc3ffcd6 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 4 Aug 2009 11:28:40 +0200 Subject: [PATCH 053/133] run-command.c: squelch a "use before assignment" warning i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5490) compiler (and probably others) mistakenly thinks variable failed_errno is used before assigned. Work it around by giving it a fake initialization. Signed-off-by: David Soria Parra Signed-off-by: Junio C Hamano --- run-command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index b613bddc71..71f83368c4 100644 --- a/run-command.c +++ b/run-command.c @@ -19,7 +19,7 @@ int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; - int failed_errno; + int failed_errno = failed_errno; /* * In case of errors we must keep the promise to close FDs From 69931b718307ad5adf85292b03e46d822156840a Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Tue, 4 Aug 2009 21:57:34 +0000 Subject: [PATCH 054/133] send-email: remove debug trace Signed-off-by: Erik Faye-Lund --- git-send-email.perl | 1 - 1 file changed, 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index cccbf4517a..17f930f0f3 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -449,7 +449,6 @@ sub check_file_rev_conflict($) { try { $repo->command('rev-parse', '--verify', '--quiet', $f); if (defined($format_patch)) { - print "foo\n"; return $format_patch; } die(< Date: Mon, 3 Aug 2009 18:41:34 +0200 Subject: [PATCH 055/133] hg-to-git: don't import the unused popen2 module Importing the popen2 module in Python-2.6 results in the "DeprecationWarning: The popen2 module is deprecated. Use the subprocess module." message. The module itself isn't used in fact, so just removing it solves the problem. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 7b03204ed1..2a6839d81e 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -20,7 +20,7 @@ """ import os, os.path, sys -import tempfile, popen2, pickle, getopt +import tempfile, pickle, getopt import re # Maps hg version -> git version From e77095e8b8d541b41c242aa6dbc8319cb99def5e Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Wed, 5 Aug 2009 17:36:28 +0200 Subject: [PATCH 056/133] Better usage string for reflog. Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- builtin-reflog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-reflog.c b/builtin-reflog.c index ddfdf5a3cb..95198c5de4 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -694,7 +694,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) */ static const char reflog_usage[] = -"git reflog (expire | ...)"; +"git reflog [ show | expire | delete ]"; int cmd_reflog(int argc, const char **argv, const char *prefix) { From 30ca4ca7b25082574d93b3a6d46a966d39de0488 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Wed, 5 Aug 2009 09:59:18 +0200 Subject: [PATCH 057/133] t6010-merge-base.sh: Depict the octopus test graph ...so that it is easier to reuse it for other tests. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- t/t6010-merge-base.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 04e4b7c5c2..79124ec761 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -110,6 +110,18 @@ test_expect_success 'compute merge-base (all)' \ # Another set to demonstrate base between one commit and a merge # in the documentation. +# +# * C (MMC) * B (MMB) * A (MMA) +# * o * o * o +# * o * o * o +# * o * o * o +# * o | _______/ +# | |/ +# | * 1 (MM1) +# | _______/ +# |/ +# * root (MMR) + test_expect_success 'merge-base for octopus-step (setup)' ' test_tick && git commit --allow-empty -m root && git tag MMR && From 995bdc73fe0e28d622af0897440f0ea298345585 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Wed, 5 Aug 2009 09:59:19 +0200 Subject: [PATCH 058/133] git-merge-base/git-show-branch: Cleanup documentation and usage Make sure that usage strings and documentation coincide with each other and with the actual code. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/git-merge-base.txt | 5 +++-- Documentation/git-show-branch.txt | 5 +++-- builtin-merge-base.c | 2 +- builtin-show-branch.c | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 767486c770..00e400353c 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -8,12 +8,12 @@ git-merge-base - Find as good common ancestors as possible for a merge SYNOPSIS -------- -'git merge-base' [--all] ... +'git merge-base' [-a|--all] ... DESCRIPTION ----------- -'git-merge-base' finds best common ancestor(s) between two commits to use +'git merge-base' finds best common ancestor(s) between two commits to use in a three-way merge. One common ancestor is 'better' than another common ancestor if the latter is an ancestor of the former. A common ancestor that does not have any better common ancestor is a 'best common @@ -29,6 +29,7 @@ the given two commits. OPTIONS ------- +-a:: --all:: Output all merge bases for the commits, instead of just one. diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index 89ec5364ec..2c78c25713 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -8,11 +8,12 @@ git-show-branch - Show branches and their commits SYNOPSIS -------- [verse] -'git show-branch' [--all] [--remotes] [--topo-order | --date-order] - [--current] [--color | --no-color] +'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order] + [--current] [--color | --no-color] [--sparse] [--more= | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [ | ]... + 'git show-branch' (-g|--reflog)[=[,]] [--list] [] DESCRIPTION diff --git a/builtin-merge-base.c b/builtin-merge-base.c index a6ec2f7ab7..54e7ec2237 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -23,7 +23,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) } static const char * const merge_base_usage[] = { - "git merge-base [--all] ...", + "git merge-base [-a|--all] ...", NULL }; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 01bea3b583..03bdea6863 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -6,8 +6,8 @@ #include "parse-options.h" static const char* show_branch_usage[] = { - "git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base] [--topics] [--color] [...]", - "--reflog[=n[,b]] [--list] [--color] ", + "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color | --no-color] [--sparse] [--more= | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [ | ]...", + "git show-branch (-g|--reflog)[=[,]] [--list] []", NULL }; From f621a8454d19d17fe46e6951b7e3d22bebd92aba Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Wed, 5 Aug 2009 09:59:20 +0200 Subject: [PATCH 059/133] git-merge-base/git-show-branch --merge-base: Documentation and test Currently, the documentation suggests that 'git merge-base -a' and 'git show-branch --merge-base' are equivalent (in fact it claims that the former cannot handle more than two revs). Alas, the handling of more than two revs is very different. Document this by tests and correct the documentation to reflect this. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/git-merge-base.txt | 4 ++++ Documentation/git-show-branch.txt | 8 +++++--- builtin-show-branch.c | 2 +- t/t6010-merge-base.sh | 6 ++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 00e400353c..ce5b369985 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -27,6 +27,10 @@ commits on the command line. As the most common special case, specifying only two commits on the command line means computing the merge base between the given two commits. +As a consequence, the 'merge base' is not necessarily contained in each of the +commit arguments if more than two commits are specified. This is different +from linkgit:git-show-branch[1] when used with the `--merge-base` option. + OPTIONS ------- -a:: diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index 2c78c25713..734336119c 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -82,9 +82,11 @@ OPTIONS Synonym to `--more=-1` --merge-base:: - Instead of showing the commit list, just act like the - 'git-merge-base -a' command, except that it can accept - more than two heads. + Instead of showing the commit list, determine possible + merge bases for the specified commits. All merge bases + will be contained in all specified commits. This is + different from how linkgit:git-merge-base[1] handles + the case of three or more commits. --independent:: Among the s given, display only the ones that diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 03bdea6863..3510a86e38 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -665,7 +665,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) OPT_BOOLEAN(0, "sha1-name", &sha1_name, "name commits with their object names"), OPT_BOOLEAN(0, "merge-base", &merge_base, - "act like git merge-base -a"), + "show possible merge bases"), OPT_BOOLEAN(0, "independent", &independent, "show refs unreachable from any other ref"), OPT_BOOLEAN(0, "topo-order", &lifo, diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 79124ec761..0144d9e858 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -149,6 +149,12 @@ test_expect_success 'merge-base A B C' ' test "$MM1" = "$MB" ' +test_expect_success 'merge-base A B C using show-branch' ' + MB=$(git show-branch --merge-base MMA MMB MMC) && + MMR=$(git rev-parse --verify MMR) && + test "$MMR" = "$MB" +' + test_expect_success 'criss-cross merge-base for octopus-step (setup)' ' git reset --hard MMR && test_tick && git commit --allow-empty -m 1 && git tag CC1 && From 85738ba3df8ae7e01091b479be7f67383ce250b6 Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Sat, 1 Aug 2009 20:49:47 +0200 Subject: [PATCH 060/133] Documentation: git submodule: add missing options to synopsis The option --merge was missing for submodule update and --cached for submodule summary. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano --- Documentation/git-submodule.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 683ba1a1eb..7dd73ae14e 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -14,8 +14,8 @@ SYNOPSIS 'git submodule' [--quiet] status [--cached] [--] [...] 'git submodule' [--quiet] init [--] [...] 'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] - [--reference ] [--] [...] -'git submodule' [--quiet] summary [--summary-limit ] [commit] [--] [...] + [--reference ] [--merge] [--] [...] +'git submodule' [--quiet] summary [--cached] [--summary-limit ] [commit] [--] [...] 'git submodule' [--quiet] foreach 'git submodule' [--quiet] sync [--] [...] From 46068383aa825dfe9026f9255cea07da07e06253 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 4 Aug 2009 17:54:32 +0200 Subject: [PATCH 061/133] gitweb/README: Document $base_url Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/README | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gitweb/README b/gitweb/README index 9056d1e090..66c6a9391d 100644 --- a/gitweb/README +++ b/gitweb/README @@ -165,6 +165,12 @@ not include variables usually directly set during build): Full URL and absolute URL of gitweb script; in earlier versions of gitweb you might have need to set those variables, now there should be no need to do it. + * $base_url + Base URL for relative URLs in pages generated by gitweb, + (e.g. $logo, $favicon, @stylesheets if they are relative URLs), + needed and used only for URLs with nonempty PATH_INFO via + Date: Sun, 2 Aug 2009 09:42:24 +0200 Subject: [PATCH 062/133] gitweb: parse_commit_text encoding fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call to_utf8 when parsing author and committer names, otherwise they will appear with bad encoding if they written by using chop_and_escape_str. Signed-off-by: Zoltán Füzesi Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7fbd5ff89e..4f051942bd 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2570,7 +2570,7 @@ sub parse_commit_text { } elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) { push @parents, $1; } elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) { - $co{'author'} = $1; + $co{'author'} = to_utf8($1); $co{'author_epoch'} = $2; $co{'author_tz'} = $3; if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) { @@ -2580,10 +2580,9 @@ sub parse_commit_text { $co{'author_name'} = $co{'author'}; } } elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) { - $co{'committer'} = $1; + $co{'committer'} = to_utf8($1); $co{'committer_epoch'} = $2; $co{'committer_tz'} = $3; - $co{'committer_name'} = $co{'committer'}; if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) { $co{'committer_name'} = $1; $co{'committer_email'} = $2; From d7c208a92e6b15cdcd159e30cd1fc0177fd967e9 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 5 Aug 2009 16:13:20 -0700 Subject: [PATCH 063/133] Add new optimized C 'block-sha1' routines Based on the mozilla SHA1 routine, but doing the input data accesses a word at a time and with 'htonl()' instead of loading bytes and shifting. It requires an architecture that is ok with unaligned 32-bit loads and a fast htonl(). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 9 +++ block-sha1/sha1.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++ block-sha1/sha1.h | 21 +++++++ 3 files changed, 175 insertions(+) create mode 100644 block-sha1/sha1.c create mode 100644 block-sha1/sha1.h diff --git a/Makefile b/Makefile index daf4296706..e6df8ecde6 100644 --- a/Makefile +++ b/Makefile @@ -84,6 +84,10 @@ all:: # specify your own (or DarwinPort's) include directories and # library directories by defining CFLAGS and LDFLAGS appropriately. # +# Define BLK_SHA1 environment variable if you want the C version +# of the SHA1 that assumes you can do unaligned 32-bit loads and +# have a fast htonl() function. +# # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. # @@ -1167,6 +1171,10 @@ ifdef NO_DEFLATE_BOUND BASIC_CFLAGS += -DNO_DEFLATE_BOUND endif +ifdef BLK_SHA1 + SHA1_HEADER = "block-sha1/sha1.h" + LIB_OBJS += block-sha1/sha1.o +else ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o @@ -1184,6 +1192,7 @@ else endif endif endif +endif ifdef NO_PERL_MAKEMAKER export NO_PERL_MAKEMAKER endif diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c new file mode 100644 index 0000000000..50b2b42b03 --- /dev/null +++ b/block-sha1/sha1.c @@ -0,0 +1,145 @@ +/* + * Based on the Mozilla SHA1 (see mozilla-sha1/sha1.c), + * optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + */ + +#include +#include + +#include "sha1.h" + +/* Hash one 64-byte block of data */ +static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data); + +void blk_SHA1_Init(blk_SHA_CTX *ctx) +{ + ctx->lenW = 0; + ctx->size = 0; + + /* Initialize H with the magic constants (see FIPS180 for constants) + */ + ctx->H[0] = 0x67452301; + ctx->H[1] = 0xefcdab89; + ctx->H[2] = 0x98badcfe; + ctx->H[3] = 0x10325476; + ctx->H[4] = 0xc3d2e1f0; +} + + +void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) +{ + int lenW = ctx->lenW; + + ctx->size += len; + + /* Read the data into W and process blocks as they get full + */ + if (lenW) { + int left = 64 - lenW; + if (len < left) + left = len; + memcpy(lenW + (char *)ctx->W, data, left); + lenW = (lenW + left) & 63; + len -= left; + data += left; + ctx->lenW = lenW; + if (lenW) + return; + blk_SHA1Block(ctx, ctx->W); + } + while (len >= 64) { + blk_SHA1Block(ctx, data); + data += 64; + len -= 64; + } + if (len) { + memcpy(ctx->W, data, len); + ctx->lenW = len; + } +} + + +void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) +{ + static const unsigned char pad[64] = { 0x80 }; + unsigned int padlen[2]; + int i; + + /* Pad with a binary 1 (ie 0x80), then zeroes, then length + */ + padlen[0] = htonl(ctx->size >> (32 - 3)); + padlen[1] = htonl(ctx->size << 3); + + blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - ctx->lenW))); + blk_SHA1_Update(ctx, padlen, 8); + + /* Output hash + */ + for (i = 0; i < 5; i++) + ((unsigned int *)hashout)[i] = htonl(ctx->H[i]); +} + +#define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n)))) + +static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) +{ + int t; + unsigned int A,B,C,D,E,TEMP; + unsigned int W[80]; + + for (t = 0; t < 16; t++) + W[t] = htonl(data[t]); + + /* Unroll it? */ + for (t = 16; t <= 79; t++) + W[t] = SHA_ROT(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); + + A = ctx->H[0]; + B = ctx->H[1]; + C = ctx->H[2]; + D = ctx->H[3]; + E = ctx->H[4]; + +#define T_0_19(t) \ + TEMP = SHA_ROT(A,5) + (((C^D)&B)^D) + E + W[t] + 0x5a827999; \ + E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + + T_0_19( 0); T_0_19( 1); T_0_19( 2); T_0_19( 3); T_0_19( 4); + T_0_19( 5); T_0_19( 6); T_0_19( 7); T_0_19( 8); T_0_19( 9); + T_0_19(10); T_0_19(11); T_0_19(12); T_0_19(13); T_0_19(14); + T_0_19(15); T_0_19(16); T_0_19(17); T_0_19(18); T_0_19(19); + +#define T_20_39(t) \ + TEMP = SHA_ROT(A,5) + (B^C^D) + E + W[t] + 0x6ed9eba1; \ + E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + + T_20_39(20); T_20_39(21); T_20_39(22); T_20_39(23); T_20_39(24); + T_20_39(25); T_20_39(26); T_20_39(27); T_20_39(28); T_20_39(29); + T_20_39(30); T_20_39(31); T_20_39(32); T_20_39(33); T_20_39(34); + T_20_39(35); T_20_39(36); T_20_39(37); T_20_39(38); T_20_39(39); + +#define T_40_59(t) \ + TEMP = SHA_ROT(A,5) + ((B&C)|(D&(B|C))) + E + W[t] + 0x8f1bbcdc; \ + E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + + T_40_59(40); T_40_59(41); T_40_59(42); T_40_59(43); T_40_59(44); + T_40_59(45); T_40_59(46); T_40_59(47); T_40_59(48); T_40_59(49); + T_40_59(50); T_40_59(51); T_40_59(52); T_40_59(53); T_40_59(54); + T_40_59(55); T_40_59(56); T_40_59(57); T_40_59(58); T_40_59(59); + +#define T_60_79(t) \ + TEMP = SHA_ROT(A,5) + (B^C^D) + E + W[t] + 0xca62c1d6; \ + E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + + T_60_79(60); T_60_79(61); T_60_79(62); T_60_79(63); T_60_79(64); + T_60_79(65); T_60_79(66); T_60_79(67); T_60_79(68); T_60_79(69); + T_60_79(70); T_60_79(71); T_60_79(72); T_60_79(73); T_60_79(74); + T_60_79(75); T_60_79(76); T_60_79(77); T_60_79(78); T_60_79(79); + + ctx->H[0] += A; + ctx->H[1] += B; + ctx->H[2] += C; + ctx->H[3] += D; + ctx->H[4] += E; +} diff --git a/block-sha1/sha1.h b/block-sha1/sha1.h new file mode 100644 index 0000000000..7be2d93a2a --- /dev/null +++ b/block-sha1/sha1.h @@ -0,0 +1,21 @@ +/* + * Based on the Mozilla SHA1 (see mozilla-sha1/sha1.h), + * optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + */ + +typedef struct { + unsigned int H[5]; + unsigned int W[16]; + int lenW; + unsigned long long size; +} blk_SHA_CTX; + +void blk_SHA1_Init(blk_SHA_CTX *ctx); +void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len); +void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); + +#define git_SHA_CTX blk_SHA_CTX +#define git_SHA1_Init blk_SHA1_Init +#define git_SHA1_Update blk_SHA1_Update +#define git_SHA1_Final blk_SHA1_Final From afdeeb00eec471d86c9d421781ba8cb545be849c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 5 Aug 2009 16:22:19 -0400 Subject: [PATCH 064/133] push: add --quiet flag Some transports produce output even without "--verbose" turned on. This provides a way to tell them to be more quiet (whereas simply redirecting might lose error messages). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-push.c | 1 + transport.h | 1 + 2 files changed, 2 insertions(+) diff --git a/builtin-push.c b/builtin-push.c index 1d92e22f0a..bc0c91aa4e 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -168,6 +168,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) const char *repo = NULL; /* default repository */ struct option options[] = { + OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET), OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE), OPT_STRING( 0 , "repo", &repo, "repository", "repository"), OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL), diff --git a/transport.h b/transport.h index 51b539778c..f1d3ebf674 100644 --- a/transport.h +++ b/transport.h @@ -36,6 +36,7 @@ struct transport { #define TRANSPORT_PUSH_MIRROR 8 #define TRANSPORT_PUSH_VERBOSE 16 #define TRANSPORT_PUSH_PORCELAIN 32 +#define TRANSPORT_PUSH_QUIET 64 /* Returns a transport suitable for the url */ struct transport *transport_get(struct remote *, const char *); From 120703292b2828d3c35c937b7f5e75ec570a42e9 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 5 Aug 2009 16:22:36 -0400 Subject: [PATCH 065/133] transport: pass "quiet" flag to pack-objects When pushing over the git protocol, pack-objects gives progress reports about the pack being sent. If "push" is given the --quiet flag, it now passes "-q" to pack-objects, suppressing this output. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 3 +++ send-pack.h | 1 + transport.c | 1 + 3 files changed, 5 insertions(+) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 47fb9f7baa..37e528e283 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -44,6 +44,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext NULL, NULL, NULL, + NULL, }; struct child_process po; int i; @@ -53,6 +54,8 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext argv[i++] = "--thin"; if (args->use_ofs_delta) argv[i++] = "--delta-base-offset"; + if (args->quiet) + argv[i++] = "-q"; memset(&po, 0, sizeof(po)); po.argv = argv; po.in = -1; diff --git a/send-pack.h b/send-pack.h index 1d7b1b3b4f..8b3cf028ed 100644 --- a/send-pack.h +++ b/send-pack.h @@ -3,6 +3,7 @@ struct send_pack_args { unsigned verbose:1, + quiet:1, send_mirror:1, force_update:1, use_thin_pack:1, diff --git a/transport.c b/transport.c index de0d5874a3..c3191ed201 100644 --- a/transport.c +++ b/transport.c @@ -892,6 +892,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); args.use_thin_pack = data->thin; args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE); + args.quiet = !!(flags & TRANSPORT_PUSH_QUIET); args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); ret = send_pack(&args, data->fd, data->conn, remote_refs, From 481c7a6db907774d0f06565d00fd06500a7ae7a4 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 5 Aug 2009 16:23:26 -0400 Subject: [PATCH 066/133] transport: don't show push status if --quiet is given When --quiet is given, the user generally only wants to see errors. So let's suppress printing the ref status table unless there is an error, in which case we print out the whole table. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- transport.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/transport.c b/transport.c index c3191ed201..a3d0145116 100644 --- a/transport.c +++ b/transport.c @@ -681,6 +681,21 @@ static int fetch_refs_via_pack(struct transport *transport, return (refs ? 0 : -1); } +static int push_had_errors(struct ref *ref) +{ + for (; ref; ref = ref->next) { + switch (ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + case REF_STATUS_OK: + break; + default: + return 1; + } + } + return 0; +} + static int refs_pushed(struct ref *ref) { for (; ref; ref = ref->next) { @@ -1010,6 +1025,7 @@ int transport_push(struct transport *transport, struct ref *local_refs = get_local_heads(); int match_flags = MATCH_REFS_NONE; int verbose = flags & TRANSPORT_PUSH_VERBOSE; + int quiet = flags & TRANSPORT_PUSH_QUIET; int porcelain = flags & TRANSPORT_PUSH_PORCELAIN; int ret; @@ -1025,7 +1041,9 @@ int transport_push(struct transport *transport, ret = transport->push_refs(transport, remote_refs, flags); - print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain); + if (!quiet || push_had_errors(remote_refs)) + print_push_status(transport->url, remote_refs, + verbose | porcelain, porcelain); if (!(flags & TRANSPORT_PUSH_DRY_RUN)) { struct ref *ref; From 5749b0b2f935dc418c98ba8e7c52c3291451bc4a Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 5 Aug 2009 16:55:07 -0400 Subject: [PATCH 067/133] don't let the delta cache grow unbounded in 'git repack' I have 4GB of RAM on my system which should, in theory, be quite enough to repack a 600 MB repository. However the unbounded delta cache size always pushes it into swap, at which point everything virtually comes to a halt. So unbounded caches are never a good idea. A default of 256MB should be a good compromize between memory usage and speed where medium sized repositories are still likely to fit in the cache with a reasonable memory usage, and larger repositories are going to take quite some time to repack already anyway. While at it, clarify the associated config variable documentation entries a bit. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- Documentation/config.txt | 14 +++++++++++--- builtin-pack-objects.c | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index c6f09f801a..7791c32bc3 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1218,12 +1218,20 @@ pack.compression:: pack.deltaCacheSize:: The maximum memory in bytes used for caching deltas in - linkgit:git-pack-objects[1]. - A value of 0 means no limit. Defaults to 0. + linkgit:git-pack-objects[1] before writing them out to a pack. + This cache is used to speed up the writing object phase by not + having to recompute the final delta result once the best match + for all objects is found. Repacking large repositories on machines + which are tight with memory might be badly impacted by this though, + especially if this cache pushes the system into swapping. + A value of 0 means no limit. The smallest size of 1 byte may be + used to virtually disable this cache. Defaults to 256 MiB. pack.deltaCacheLimit:: The maximum size of a delta, that is cached in - linkgit:git-pack-objects[1]. Defaults to 1000. + linkgit:git-pack-objects[1]. This cache is used to speed up the + writing object phase by not having to recompute the final delta + result once the best match for all objects is found. Defaults to 1000. pack.threads:: Specifies the number of threads to spawn when searching for best diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index ef4bf6bc14..9cc8a8451d 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -86,7 +86,7 @@ static int pack_compression_level = Z_DEFAULT_COMPRESSION; static int pack_compression_seen; static unsigned long delta_cache_size = 0; -static unsigned long max_delta_cache_size = 0; +static unsigned long max_delta_cache_size = 256 * 1024 * 1024; static unsigned long cache_max_small_delta_size = 1000; static unsigned long window_memory_limit = 0; From 8918f5cf962866c2bb36bdf6b417bfcdb523280a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Fri, 31 Jul 2009 09:23:09 +0200 Subject: [PATCH 068/133] git.el: Clarify documentation of git-commit-tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index eace9c18eb..8c70ad8b7f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -429,16 +429,19 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (git-get-string-sha1 (git-call-process-string-display-error "write-tree")))) -(defun git-commit-tree (buffer tree head) - "Call git-commit-tree with buffer as input and return the resulting commit SHA1." +(defun git-commit-tree (buffer tree parent) + "Create a commit and possibly update HEAD. +Create a commit with the message in BUFFER using the tree with hash TREE. +Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\", +update the \"HEAD\" reference to the new commit." (let ((author-name (git-get-committer-name)) (author-email (git-get-committer-email)) (subject "commit (initial): ") author-date log-start log-end args coding-system-for-write) - (when head + (when parent (setq subject "commit: ") (push "-p" args) - (push head args)) + (push parent args)) (with-current-buffer buffer (goto-char (point-min)) (if @@ -474,7 +477,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (apply #'git-run-command-region buffer log-start log-end env "commit-tree" tree (nreverse args)))))) - (when commit (git-update-ref "HEAD" commit head subject)) + (when commit (git-update-ref "HEAD" commit parent subject)) commit))) (defun git-empty-db-p () From 1a3d834f06260e51396a6047ec746fc7895de316 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Aug 2009 09:42:33 -0700 Subject: [PATCH 069/133] git-rev-list.txt: Clarify the use of multiple revision arguments If one thinks of a revision as the set of commits which can be reached from the rev, and of ^rev as the complement, then multiple arguments to git rev-list can be neither understood as the intersection nor the union of the individual sets. But set language is the natural as well as logical language in which to phrase this. So, add a paragraph which explains multiple arguments using set language. Suggested-by: Michael J Gruber Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index a765cfa4d2..bf98c8449c 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -51,20 +51,26 @@ SYNOPSIS DESCRIPTION ----------- -Lists commit objects in reverse chronological order starting at the -given commit(s), taking ancestry relationship into account. This is -useful to produce human-readable log output. +List commits that are reachable by following the `parent` links from the +given commit(s), but exclude commits that are reachable from the one(s) +given with a '{caret}' in front of them. The output is given in reverse +chronological order by default. -Commits which are stated with a preceding '{caret}' cause listing to -stop at that point. Their parents are implied. Thus the following -command: +You can think of this as a set operation. Commits given on the command +line form a set of commits that are reachable from any of them, and then +commits reachable from any of the ones given with '{caret}' in front are +subtracted from that set. The remaining commits are what comes out in the +command's output. Various other options and paths parameters can be used +to further limit the result. + +Thus, the following command: ----------------------------------------------------------------------- $ git rev-list foo bar ^baz ----------------------------------------------------------------------- -means "list all the commits which are included in 'foo' and 'bar', but -not in 'baz'". +means "list all the commits which are reachable from 'foo' or 'bar', but +not from 'baz'". A special notation "''..''" can be used as a short-hand for "{caret}'' ''". For example, either of From 18b07930363d54778e616f1b86de8ccd5ebe88fc Mon Sep 17 00:00:00 2001 From: Jari Aalto Date: Thu, 6 Aug 2009 00:20:32 +0300 Subject: [PATCH 070/133] git-tag(1): Refer to git-check-ref-format(1) for Explain briefly what characters are prohibited in tag and point to git-check-ref-format(1) manual page for further information. Signed-off-by: Jari Aalto Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index fa733214ab..1118ce22dc 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -17,7 +17,10 @@ SYNOPSIS DESCRIPTION ----------- -Adds a 'tag' reference in `.git/refs/tags/` + +Adds a 'tag' reference in `.git/refs/tags/`. The tag must pass +linkgit:git-check-ref-format[1] which basicly means that control characters, +space, ~, ^, :, ?, *, [ and \ are prohibited. Unless `-f` is given, the tag must not yet exist in `.git/refs/tags/` directory. From 4cfbe06fc788aad7bb4e5ea0c2c93345840acc28 Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Wed, 5 Aug 2009 16:56:25 +0200 Subject: [PATCH 071/133] rebase: consistent error messages for staged and unstaged changes. Previous version expose the output of the plumbing update-index to the user, which novice users have difficulty to understand. We still need to run update-index to refresh the cache (if diff.autorefreshindex is false, git diff won't do it). Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- git-rebase.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index 18bc6946cf..3555d17a5d 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -382,8 +382,10 @@ else fi # The tree must be really really clean. -if ! git update-index --ignore-submodules --refresh; then - die "cannot rebase: you have unstaged changes" +if ! git update-index --ignore-submodules --refresh > /dev/null; then + echo >&2 "cannot rebase: you have unstaged changes" + git diff --name-status -r --ignore-submodules -- >&2 + exit 1 fi diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --) case "$diff" in From d68dc34cb41c30515e64766c303c2930994e5085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Thu, 6 Aug 2009 14:15:14 +0200 Subject: [PATCH 072/133] git-describe: Die early if there are no possible descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we find no refs that may be used for git-describe with the current options, then die early instead of pointlessly walking the whole history. In git.git with all the tags dropped, this makes "git describe" go down from 0.244 to 0.003 seconds for me. This is especially noticeable with "git submodule status" which calls describe with increasing levels of allowed refs to be matched. For a submodule without tags, this means that it walks the whole history in the submodule twice (first annotated, then plain tags), just to find out that it can't describe the commit anyway. Signed-off-by: Björn Steinbrink Signed-off-by: Junio C Hamano --- builtin-describe.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin-describe.c b/builtin-describe.c index 7a662980d1..df67a733ae 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -20,6 +20,7 @@ static int tags; /* Allow lightweight tags */ static int longformat; static int abbrev = DEFAULT_ABBREV; static int max_candidates = 10; +static int found_names; static const char *pattern; static int always; @@ -49,6 +50,7 @@ static void add_to_known_names(const char *path, memcpy(e->path, path, len); commit->util = e; } + found_names = 1; } static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data) @@ -195,6 +197,9 @@ static void describe(const char *arg, int last_one) for_each_ref(get_name, NULL); } + if (!found_names) + die("cannot describe '%s'", sha1_to_hex(sha1)); + n = cmit->util; if (n) { /* From b26a9d50899a5d65bafcb521c5495f03b2e2e0e9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Aug 2009 13:56:19 -0700 Subject: [PATCH 073/133] block-sha1: undo ctx->size change Undo the change I picked up from the mailing list discussion suggested by Nico, not because it is wrong, but it will be done at the end of the follow-up series. Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 50b2b42b03..eef32f7859 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -31,7 +31,7 @@ void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) { int lenW = ctx->lenW; - ctx->size += len; + ctx->size += (unsigned long long) len << 3; /* Read the data into W and process blocks as they get full */ @@ -68,8 +68,8 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ - padlen[0] = htonl(ctx->size >> (32 - 3)); - padlen[1] = htonl(ctx->size << 3); + padlen[0] = htonl(ctx->size >> 32); + padlen[1] = htonl(ctx->size); blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - ctx->lenW))); blk_SHA1_Update(ctx, padlen, 8); From b8e48a89b8f581eaf95b57782bb8e620ca30e968 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 5 Aug 2009 19:42:15 -0700 Subject: [PATCH 074/133] block-sha1: try to use rol/ror appropriately Use the one with the smaller constant. It _can_ generate slightly smaller code (a constant of 1 is special), but perhaps more importantly it's possibly faster on any uarch that does a rotate with a loop. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index eef32f7859..a45a3dec1e 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -80,7 +80,19 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) ((unsigned int *)hashout)[i] = htonl(ctx->H[i]); } -#define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n)))) +#if defined(__i386__) || defined(__x86_64__) + +#define SHA_ASM(op, x, n) ({ unsigned int __res; asm(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) +#define SHA_ROL(x,n) SHA_ASM("rol", x, n) +#define SHA_ROR(x,n) SHA_ASM("ror", x, n) + +#else + +#define SHA_ROT(X,n) (((X) << (l)) | ((X) >> (r))) +#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) +#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) + +#endif static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) { @@ -93,7 +105,7 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) /* Unroll it? */ for (t = 16; t <= 79; t++) - W[t] = SHA_ROT(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); + W[t] = SHA_ROL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); A = ctx->H[0]; B = ctx->H[1]; @@ -102,8 +114,8 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) E = ctx->H[4]; #define T_0_19(t) \ - TEMP = SHA_ROT(A,5) + (((C^D)&B)^D) + E + W[t] + 0x5a827999; \ - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + TEMP = SHA_ROL(A,5) + (((C^D)&B)^D) + E + W[t] + 0x5a827999; \ + E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; T_0_19( 0); T_0_19( 1); T_0_19( 2); T_0_19( 3); T_0_19( 4); T_0_19( 5); T_0_19( 6); T_0_19( 7); T_0_19( 8); T_0_19( 9); @@ -111,8 +123,8 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) T_0_19(15); T_0_19(16); T_0_19(17); T_0_19(18); T_0_19(19); #define T_20_39(t) \ - TEMP = SHA_ROT(A,5) + (B^C^D) + E + W[t] + 0x6ed9eba1; \ - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + TEMP = SHA_ROL(A,5) + (B^C^D) + E + W[t] + 0x6ed9eba1; \ + E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; T_20_39(20); T_20_39(21); T_20_39(22); T_20_39(23); T_20_39(24); T_20_39(25); T_20_39(26); T_20_39(27); T_20_39(28); T_20_39(29); @@ -120,8 +132,8 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) T_20_39(35); T_20_39(36); T_20_39(37); T_20_39(38); T_20_39(39); #define T_40_59(t) \ - TEMP = SHA_ROT(A,5) + ((B&C)|(D&(B|C))) + E + W[t] + 0x8f1bbcdc; \ - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + TEMP = SHA_ROL(A,5) + ((B&C)|(D&(B|C))) + E + W[t] + 0x8f1bbcdc; \ + E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; T_40_59(40); T_40_59(41); T_40_59(42); T_40_59(43); T_40_59(44); T_40_59(45); T_40_59(46); T_40_59(47); T_40_59(48); T_40_59(49); @@ -129,8 +141,8 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) T_40_59(55); T_40_59(56); T_40_59(57); T_40_59(58); T_40_59(59); #define T_60_79(t) \ - TEMP = SHA_ROT(A,5) + (B^C^D) + E + W[t] + 0xca62c1d6; \ - E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP; + TEMP = SHA_ROL(A,5) + (B^C^D) + E + W[t] + 0xca62c1d6; \ + E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; T_60_79(60); T_60_79(61); T_60_79(62); T_60_79(63); T_60_79(64); T_60_79(65); T_60_79(66); T_60_79(67); T_60_79(68); T_60_79(69); From fd536d3439fa2a06730884df31e2e98c9006c947 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Aug 2009 13:52:58 -0700 Subject: [PATCH 075/133] block-sha1: minor fixups Bert Wesarg noticed non-x86 version of SHA_ROT() had a typo. Also spell in-line assembly as __asm__(), otherwise I seem to get error: implicit declaration of function 'asm' from my compiler. Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index a45a3dec1e..698e435a39 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -82,13 +82,13 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) #if defined(__i386__) || defined(__x86_64__) -#define SHA_ASM(op, x, n) ({ unsigned int __res; asm(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) +#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) #define SHA_ROL(x,n) SHA_ASM("rol", x, n) #define SHA_ROR(x,n) SHA_ASM("ror", x, n) #else -#define SHA_ROT(X,n) (((X) << (l)) | ((X) >> (r))) +#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r))) #define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) #define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) From 139e3456ecf18fc03a75eda7a77441e8fec344b9 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 5 Aug 2009 20:28:07 -0700 Subject: [PATCH 076/133] block-sha1: make the 'ntohl()' part of the first SHA1 loop This helps a teeny bit. But what I -really- want to do is to avoid the whole 80-array loop, and do the xor updates as I go along.. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 698e435a39..13da511b78 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -100,27 +100,31 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) unsigned int A,B,C,D,E,TEMP; unsigned int W[80]; - for (t = 0; t < 16; t++) - W[t] = htonl(data[t]); - - /* Unroll it? */ - for (t = 16; t <= 79; t++) - W[t] = SHA_ROL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); - A = ctx->H[0]; B = ctx->H[1]; C = ctx->H[2]; D = ctx->H[3]; E = ctx->H[4]; -#define T_0_19(t) \ +#define T_0_15(t) \ + TEMP = htonl(data[t]); W[t] = TEMP; \ + TEMP += SHA_ROL(A,5) + (((C^D)&B)^D) + E + 0x5a827999; \ + E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; \ + + T_0_15( 0); T_0_15( 1); T_0_15( 2); T_0_15( 3); T_0_15( 4); + T_0_15( 5); T_0_15( 6); T_0_15( 7); T_0_15( 8); T_0_15( 9); + T_0_15(10); T_0_15(11); T_0_15(12); T_0_15(13); T_0_15(14); + T_0_15(15); + + /* Unroll it? */ + for (t = 16; t <= 79; t++) + W[t] = SHA_ROL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); + +#define T_16_19(t) \ TEMP = SHA_ROL(A,5) + (((C^D)&B)^D) + E + W[t] + 0x5a827999; \ E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; - T_0_19( 0); T_0_19( 1); T_0_19( 2); T_0_19( 3); T_0_19( 4); - T_0_19( 5); T_0_19( 6); T_0_19( 7); T_0_19( 8); T_0_19( 9); - T_0_19(10); T_0_19(11); T_0_19(12); T_0_19(13); T_0_19(14); - T_0_19(15); T_0_19(16); T_0_19(17); T_0_19(18); T_0_19(19); + T_16_19(16); T_16_19(17); T_16_19(18); T_16_19(19); #define T_20_39(t) \ TEMP = SHA_ROL(A,5) + (B^C^D) + E + W[t] + 0x6ed9eba1; \ From 7b5075fcfb069fc36ba4cfe5567234974793ab58 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 5 Aug 2009 20:49:41 -0700 Subject: [PATCH 077/133] block-sha1: re-use the temporary array as we calculate the SHA1 The mozilla-SHA1 code did this 80-word array for the 80 iterations. But the SHA1 state is really just 512 bits, and you can actually keep it in a kind of "circular queue" of just 16 words instead. This requires us to do the xor updates as we go along (rather than as a pre-phase), but that's really what we want to do anyway. This gets me really close to the OpenSSL performance on my Nehalem. Look ma, all C code (ok, there's the rol/ror hack, but that one doesn't strictly even matter on my Nehalem, it's just a local optimization). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 13da511b78..8c4c216f93 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -96,9 +96,8 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) { - int t; unsigned int A,B,C,D,E,TEMP; - unsigned int W[80]; + unsigned int array[16]; A = ctx->H[0]; B = ctx->H[1]; @@ -107,8 +106,8 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) E = ctx->H[4]; #define T_0_15(t) \ - TEMP = htonl(data[t]); W[t] = TEMP; \ - TEMP += SHA_ROL(A,5) + (((C^D)&B)^D) + E + 0x5a827999; \ + TEMP = htonl(data[t]); array[t] = TEMP; \ + TEMP += SHA_ROL(A,5) + (((C^D)&B)^D) + E + 0x5a827999; \ E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; \ T_0_15( 0); T_0_15( 1); T_0_15( 2); T_0_15( 3); T_0_15( 4); @@ -116,18 +115,21 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) T_0_15(10); T_0_15(11); T_0_15(12); T_0_15(13); T_0_15(14); T_0_15(15); - /* Unroll it? */ - for (t = 16; t <= 79; t++) - W[t] = SHA_ROL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) +#define SHA_XOR(t) \ + TEMP = SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1); W(t) = TEMP; #define T_16_19(t) \ - TEMP = SHA_ROL(A,5) + (((C^D)&B)^D) + E + W[t] + 0x5a827999; \ - E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; + SHA_XOR(t); \ + TEMP += SHA_ROL(A,5) + (((C^D)&B)^D) + E + 0x5a827999; \ + E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; \ T_16_19(16); T_16_19(17); T_16_19(18); T_16_19(19); #define T_20_39(t) \ - TEMP = SHA_ROL(A,5) + (B^C^D) + E + W[t] + 0x6ed9eba1; \ + SHA_XOR(t); \ + TEMP += SHA_ROL(A,5) + (B^C^D) + E + 0x6ed9eba1; \ E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; T_20_39(20); T_20_39(21); T_20_39(22); T_20_39(23); T_20_39(24); @@ -136,7 +138,8 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) T_20_39(35); T_20_39(36); T_20_39(37); T_20_39(38); T_20_39(39); #define T_40_59(t) \ - TEMP = SHA_ROL(A,5) + ((B&C)|(D&(B|C))) + E + W[t] + 0x8f1bbcdc; \ + SHA_XOR(t); \ + TEMP += SHA_ROL(A,5) + ((B&C)|(D&(B|C))) + E + 0x8f1bbcdc; \ E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; T_40_59(40); T_40_59(41); T_40_59(42); T_40_59(43); T_40_59(44); @@ -145,7 +148,8 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) T_40_59(55); T_40_59(56); T_40_59(57); T_40_59(58); T_40_59(59); #define T_60_79(t) \ - TEMP = SHA_ROL(A,5) + (B^C^D) + E + W[t] + 0xca62c1d6; \ + SHA_XOR(t); \ + TEMP += SHA_ROL(A,5) + (B^C^D) + E + 0xca62c1d6; \ E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; T_60_79(60); T_60_79(61); T_60_79(62); T_60_79(63); T_60_79(64); From ab14c823dfbf1245712c8179952b51822135d8a8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 6 Aug 2009 07:20:54 -0700 Subject: [PATCH 078/133] block-sha1: macroize the rounds a bit further Avoid repeating the shared parts of the different rounds by adding a macro layer or two. It was already more cpp than C. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 56 ++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 8c4c216f93..53c93ba603 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -94,6 +94,27 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) #endif +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) + +/* + * Where do we get the source from? The first 16 iterations get it from + * the input data, the next mix it from the 512-bit array. + */ +#define SHA_SRC(t) htonl(data[t]) +#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) + +#define SHA_ROUND(t, input, fn, constant) \ + TEMP = input(t); W(t) = TEMP; \ + TEMP += SHA_ROL(A,5) + (fn) + E + (constant); \ + E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP + +#define T_0_15(t) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999 ) +#define T_16_19(t) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999 ) +#define T_20_39(t) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1 ) +#define T_40_59(t) SHA_ROUND(t, SHA_MIX, ((B&C)|(D&(B|C))) , 0x8f1bbcdc ) +#define T_60_79(t) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6 ) + static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) { unsigned int A,B,C,D,E,TEMP; @@ -105,53 +126,28 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) D = ctx->H[3]; E = ctx->H[4]; -#define T_0_15(t) \ - TEMP = htonl(data[t]); array[t] = TEMP; \ - TEMP += SHA_ROL(A,5) + (((C^D)&B)^D) + E + 0x5a827999; \ - E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; \ - + /* Round 1 - iterations 0-16 take their input from 'data' */ T_0_15( 0); T_0_15( 1); T_0_15( 2); T_0_15( 3); T_0_15( 4); T_0_15( 5); T_0_15( 6); T_0_15( 7); T_0_15( 8); T_0_15( 9); T_0_15(10); T_0_15(11); T_0_15(12); T_0_15(13); T_0_15(14); T_0_15(15); -/* This "rolls" over the 512-bit array */ -#define W(x) (array[(x)&15]) -#define SHA_XOR(t) \ - TEMP = SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1); W(t) = TEMP; - -#define T_16_19(t) \ - SHA_XOR(t); \ - TEMP += SHA_ROL(A,5) + (((C^D)&B)^D) + E + 0x5a827999; \ - E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; \ - + /* Round 1 - tail. Input from 512-bit mixing array */ T_16_19(16); T_16_19(17); T_16_19(18); T_16_19(19); -#define T_20_39(t) \ - SHA_XOR(t); \ - TEMP += SHA_ROL(A,5) + (B^C^D) + E + 0x6ed9eba1; \ - E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; - + /* Round 2 */ T_20_39(20); T_20_39(21); T_20_39(22); T_20_39(23); T_20_39(24); T_20_39(25); T_20_39(26); T_20_39(27); T_20_39(28); T_20_39(29); T_20_39(30); T_20_39(31); T_20_39(32); T_20_39(33); T_20_39(34); T_20_39(35); T_20_39(36); T_20_39(37); T_20_39(38); T_20_39(39); -#define T_40_59(t) \ - SHA_XOR(t); \ - TEMP += SHA_ROL(A,5) + ((B&C)|(D&(B|C))) + E + 0x8f1bbcdc; \ - E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; - + /* Round 3 */ T_40_59(40); T_40_59(41); T_40_59(42); T_40_59(43); T_40_59(44); T_40_59(45); T_40_59(46); T_40_59(47); T_40_59(48); T_40_59(49); T_40_59(50); T_40_59(51); T_40_59(52); T_40_59(53); T_40_59(54); T_40_59(55); T_40_59(56); T_40_59(57); T_40_59(58); T_40_59(59); -#define T_60_79(t) \ - SHA_XOR(t); \ - TEMP += SHA_ROL(A,5) + (B^C^D) + E + 0xca62c1d6; \ - E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP; - + /* Round 4 */ T_60_79(60); T_60_79(61); T_60_79(62); T_60_79(63); T_60_79(64); T_60_79(65); T_60_79(66); T_60_79(67); T_60_79(68); T_60_79(69); T_60_79(70); T_60_79(71); T_60_79(72); T_60_79(73); T_60_79(74); From e869e113c8f91999f9a433436e0b863fe2727b61 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 6 Aug 2009 07:27:57 -0700 Subject: [PATCH 079/133] block-sha1: Use '(B&C)+(D&(B^C))' instead of '(B&C)|(D&(B|C))' in round 3 It's an equivalent expression, but the '+' gives us some freedom in instruction selection (for example, we can use 'lea' rather than 'add'), and associates with the other additions around it to give some minor scheduling freedom. Suggested-by: linux@horizon.com Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 53c93ba603..5bf1b36bd1 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -112,7 +112,7 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) #define T_0_15(t) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999 ) #define T_16_19(t) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999 ) #define T_20_39(t) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1 ) -#define T_40_59(t) SHA_ROUND(t, SHA_MIX, ((B&C)|(D&(B|C))) , 0x8f1bbcdc ) +#define T_40_59(t) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc ) #define T_60_79(t) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6 ) static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) From 5d5210c35aa83342163ab0ab80b8e6d6fa3ce931 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 6 Aug 2009 07:45:46 -0700 Subject: [PATCH 080/133] block-sha1: get rid of redundant 'lenW' context .. and simplify the ctx->size logic. We now count the size in bytes, which means that 'lenW' was always just the low 6 bits of the total size, so we don't carry it around separately any more. And we do the 'size in bits' shift at the end. Suggested by Nicolas Pitre and linux@horizon.com. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 17 +++++++---------- block-sha1/sha1.h | 1 - 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 5bf1b36bd1..fdd400f22d 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -14,7 +14,6 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data); void blk_SHA1_Init(blk_SHA_CTX *ctx) { - ctx->lenW = 0; ctx->size = 0; /* Initialize H with the magic constants (see FIPS180 for constants) @@ -29,9 +28,9 @@ void blk_SHA1_Init(blk_SHA_CTX *ctx) void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) { - int lenW = ctx->lenW; + int lenW = ctx->size & 63; - ctx->size += (unsigned long long) len << 3; + ctx->size += len; /* Read the data into W and process blocks as they get full */ @@ -43,7 +42,6 @@ void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) lenW = (lenW + left) & 63; len -= left; data += left; - ctx->lenW = lenW; if (lenW) return; blk_SHA1Block(ctx, ctx->W); @@ -53,10 +51,8 @@ void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) data += 64; len -= 64; } - if (len) { + if (len) memcpy(ctx->W, data, len); - ctx->lenW = len; - } } @@ -68,10 +64,11 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ - padlen[0] = htonl(ctx->size >> 32); - padlen[1] = htonl(ctx->size); + padlen[0] = htonl(ctx->size >> 29); + padlen[1] = htonl(ctx->size << 3); - blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - ctx->lenW))); + i = ctx->size & 63; + blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i))); blk_SHA1_Update(ctx, padlen, 8); /* Output hash diff --git a/block-sha1/sha1.h b/block-sha1/sha1.h index 7be2d93a2a..c1ae74d3da 100644 --- a/block-sha1/sha1.h +++ b/block-sha1/sha1.h @@ -7,7 +7,6 @@ typedef struct { unsigned int H[5]; unsigned int W[16]; - int lenW; unsigned long long size; } blk_SHA_CTX; From 4d590f0607184021c7b52430aa41483e6fd0d044 Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Thu, 6 Aug 2009 17:11:48 +0200 Subject: [PATCH 081/133] git-ls-files.txt: clarify what "other files" mean for --other Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- Documentation/git-ls-files.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 057a021eb5..021066e95d 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -44,7 +44,7 @@ OPTIONS -o:: --others:: - Show other files in the output + Show other (i.e. untracked) files in the output -i:: --ignored:: From 0fcb2caf29d2ff9822cbb3440c5c2c495cf430f0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Aug 2009 20:08:12 -0500 Subject: [PATCH 082/133] am: allow individual e-mail files as input We traditionally allowed a mbox file or a directory name of a maildir (but never an individual file inside a maildir) to be given to "git am". Even though an individual file in a maildir (or more generally, a piece of RFC2822 e-mail) is not a mbox file, it contains enough information to create a commit out of it, so there is no reason to reject one. Running mailsplit on such a file feels stupid, but it does not hurt. This builds on top of a5a6755 (git-am foreign patch support: introduce patch_format, 2009-05-27) that introduced mailbox format detection. The codepath to deal with a mbox requires it to begin with "From " line and also allows it to begin with "From: ", but a random piece of e-mail can and often do begin with any valid RFC2822 header lines. Instead of checking the first line, we extract all the lines up to the first empty line, and make sure they look like e-mail headers. A test is added to t4150 to demonstrate this feature. Signed-off-by: Junio C Hamano Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- git-am.sh | 14 ++++++++++++++ t/t4150-am.sh | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/git-am.sh b/git-am.sh index d64d997535..dd60f5d9cb 100755 --- a/git-am.sh +++ b/git-am.sh @@ -191,6 +191,20 @@ check_patch_format () { esac ;; esac + if test -z "$patch_format" && + test -n "$l1" && + test -n "$l2" && + test -n "$l3" + then + # This begins with three non-empty lines. Is this a + # piece of e-mail a-la RFC2822? Grab all the headers, + # discarding the indented remainder of folded lines, + # and see if it looks like that they all begin with the + # header field names... + sed -n -e '/^$/q' -e '/^[ ]/d' -e p "$1" | + egrep -v '^[A-Za-z]+(-[A-Za-z]+)*:' >/dev/null || + patch_format=mbox + fi } < "$1" || clean_abort } diff --git a/t/t4150-am.sh b/t/t4150-am.sh index a12bf84623..8296605234 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -77,6 +77,12 @@ test_expect_success setup ' git commit -s -F msg && git tag second && git format-patch --stdout first >patch1 && + { + echo "X-Fake-Field: Line One" && + echo "X-Fake-Field: Line Two" && + echo "X-Fake-Field: Line Three" && + git format-patch --stdout first | sed -e "1d" + } > patch1.eml && sed -n -e "3,\$p" msg >file && git add file && test_tick && @@ -108,6 +114,15 @@ test_expect_success 'am applies patch correctly' ' test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" ' +test_expect_success 'am applies patch e-mail not in a mbox' ' + git checkout first && + git am patch1.eml && + ! test -d .git/rebase-apply && + test -z "$(git diff second)" && + test "$(git rev-parse second)" = "$(git rev-parse HEAD)" && + test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" +' + GIT_AUTHOR_NAME="Another Thor" GIT_AUTHOR_EMAIL="a.thor@example.com" GIT_COMMITTER_NAME="Co M Miter" From 46caf5053f0a784911c66530928e6e4361a783f2 Mon Sep 17 00:00:00 2001 From: Nicolas Sebrecht Date: Thu, 6 Aug 2009 20:08:13 -0500 Subject: [PATCH 083/133] git-am: print fair error message when format detection fails Avoid git ending with this message: "Patch format is not supported." With improved error message in the format detection failure case by Giuseppe Bilotta. Signed-off-by: Nicolas Sebrecht Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- git-am.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git-am.sh b/git-am.sh index dd60f5d9cb..f719f6e654 100755 --- a/git-am.sh +++ b/git-am.sh @@ -268,7 +268,11 @@ split_patches () { msgnum= ;; *) - clean_abort "Patch format $patch_format is not supported." + if test -n "$parse_patch" ; then + clean_abort "Patch format $patch_format is not supported." + else + clean_abort "Patch format detection failed." + fi ;; esac } From 1c370ea4e519f8facaf321b633f79eb68a14a0db Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Thu, 6 Aug 2009 12:47:21 +0200 Subject: [PATCH 084/133] Show usage string for 'git log -h', 'git show -h' and 'git diff -h' Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- builtin-diff.c | 2 ++ builtin-log.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/builtin-diff.c b/builtin-diff.c index 2e51f408f9..ffcdd055ca 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -218,6 +218,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->max_count = 3; else if (!strcmp(argv[1], "-q")) options |= DIFF_SILENT_ON_REMOVED; + else if (!strcmp(argv[1], "-h")) + usage(builtin_diff_usage); else return error("invalid option: %s", argv[1]); argv++; argc--; diff --git a/builtin-log.c b/builtin-log.c index 30358166e6..3817bf1186 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -27,6 +27,10 @@ static int default_show_root = 1; static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; +static const char * const builtin_log_usage = + "git log [] [..] [[--] ...]\n" + " or: git show [options] ..."; + static void cmd_log_init(int argc, const char **argv, const char *prefix, struct rev_info *rev) { @@ -61,6 +65,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->show_decorations = 1; } else if (!strcmp(arg, "--source")) { rev->show_source = 1; + } else if (!strcmp(arg, "-h")) { + usage(builtin_log_usage); } else die("unrecognized argument: %s", arg); } From 2e674a9d090f0d9be71c8143ae6c0fba04a160cc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 7 Aug 2009 20:12:13 -0700 Subject: [PATCH 085/133] t5510: harden the way verify-pack is used The test ignored the exit status from verify pack command, and also relied on not seeing any delta chain statistics. Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 52094e78dc..61a02a91a1 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -9,6 +9,11 @@ test_description='Per branch config variables affects "git fetch". D=`pwd` +test_bundle_object_count () { + git verify-pack -v "$1" >verify.out && + test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l) +} + test_expect_success setup ' echo >file original && git add file && @@ -146,6 +151,7 @@ test_expect_success 'unbundle 1' ' test_must_fail git fetch "$D/bundle1" master:master ' + test_expect_success 'bundle 1 has only 3 files ' ' cd "$D" && ( @@ -156,8 +162,7 @@ test_expect_success 'bundle 1 has only 3 files ' ' cat ) bundle.pack && git index-pack bundle.pack && - verify=$(git verify-pack -v bundle.pack) && - test 4 = $(echo "$verify" | wc -l) + test_bundle_object_count bundle.pack 3 ' test_expect_success 'unbundle 2' ' @@ -180,7 +185,7 @@ test_expect_success 'bundle does not prerequisite objects' ' cat ) bundle.pack && git index-pack bundle.pack && - test 4 = $(git verify-pack -v bundle.pack | wc -l) + test_bundle_object_count bundle.pack 3 ' test_expect_success 'bundle should be able to create a full history' ' From 262b04fa8dce84596738fd8c0dbff52994388efa Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 7 Aug 2009 15:36:31 -0700 Subject: [PATCH 086/133] verify-pack -v: do not report "chain length 0" When making a histogram of delta chain length in the pack, the program collects number of objects whose delta depth exceeds the MAX_CHAIN limit in histogram[0], and showed it as the number of items that exceeds the limit correctly. HOWEVER, it also showed the same number labeled as "chain length = 0". In fact, we are not showing the number of objects whose chain length is zero, i.e. the base objects. Correct this. Signed-off-by: Junio C Hamano --- builtin-verify-pack.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c index 25a29f11a4..e8fd68bfac 100644 --- a/builtin-verify-pack.c +++ b/builtin-verify-pack.c @@ -7,10 +7,13 @@ static void show_pack_info(struct packed_git *p) { - uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1]; + uint32_t nr_objects, i; + int cnt; + unsigned long chain_histogram[MAX_CHAIN+1], baseobjects; nr_objects = p->num_objects; memset(chain_histogram, 0, sizeof(chain_histogram)); + baseobjects = 0; for (i = 0; i < nr_objects; i++) { const unsigned char *sha1; @@ -29,9 +32,11 @@ static void show_pack_info(struct packed_git *p) &delta_chain_length, base_sha1); printf("%s ", sha1_to_hex(sha1)); - if (!delta_chain_length) + if (!delta_chain_length) { printf("%-6s %lu %lu %"PRIuMAX"\n", type, size, store_size, (uintmax_t)offset); + baseobjects++; + } else { printf("%-6s %lu %lu %"PRIuMAX" %u %s\n", type, size, store_size, (uintmax_t)offset, @@ -43,15 +48,21 @@ static void show_pack_info(struct packed_git *p) } } - for (i = 0; i <= MAX_CHAIN; i++) { - if (!chain_histogram[i]) + if (baseobjects) + printf("non delta: %lu object%s\n", + baseobjects, baseobjects > 1 ? "s" : ""); + + for (cnt = 1; cnt <= MAX_CHAIN; cnt++) { + if (!chain_histogram[cnt]) continue; - printf("chain length = %"PRIu32": %"PRIu32" object%s\n", i, - chain_histogram[i], chain_histogram[i] > 1 ? "s" : ""); + printf("chain length = %d: %lu object%s\n", cnt, + chain_histogram[cnt], + chain_histogram[cnt] > 1 ? "s" : ""); } if (chain_histogram[0]) - printf("chain length > %d: %"PRIu32" object%s\n", MAX_CHAIN, - chain_histogram[0], chain_histogram[0] > 1 ? "s" : ""); + printf("chain length > %d: %lu object%s\n", MAX_CHAIN, + chain_histogram[0], + chain_histogram[0] > 1 ? "s" : ""); } static int verify_one_pack(const char *path, int verbose) From 30d12d4c16abc052e8961c07651f97bea2c061bd Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 6 Aug 2009 12:41:00 -0700 Subject: [PATCH 087/133] block-sha1: perform register rotation using cpp Instead of letting the compiler to figure out the optimal way to rotate register usage, explicitly rotate the register names with cpp. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 117 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 27 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index fdd400f22d..b715916675 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -101,20 +101,20 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) #define SHA_SRC(t) htonl(data[t]) #define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) -#define SHA_ROUND(t, input, fn, constant) \ - TEMP = input(t); W(t) = TEMP; \ - TEMP += SHA_ROL(A,5) + (fn) + E + (constant); \ - E = D; D = C; C = SHA_ROR(B, 2); B = A; A = TEMP +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + unsigned int TEMP = input(t); W(t) = TEMP; \ + TEMP += E + SHA_ROL(A,5) + (fn) + (constant); \ + B = SHA_ROR(B, 2); E = TEMP; } while (0) -#define T_0_15(t) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999 ) -#define T_16_19(t) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999 ) -#define T_20_39(t) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1 ) -#define T_40_59(t) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc ) -#define T_60_79(t) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6 ) +#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) +#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) +#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) { - unsigned int A,B,C,D,E,TEMP; + unsigned int A,B,C,D,E; unsigned int array[16]; A = ctx->H[0]; @@ -124,31 +124,94 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) E = ctx->H[4]; /* Round 1 - iterations 0-16 take their input from 'data' */ - T_0_15( 0); T_0_15( 1); T_0_15( 2); T_0_15( 3); T_0_15( 4); - T_0_15( 5); T_0_15( 6); T_0_15( 7); T_0_15( 8); T_0_15( 9); - T_0_15(10); T_0_15(11); T_0_15(12); T_0_15(13); T_0_15(14); - T_0_15(15); + T_0_15( 0, A, B, C, D, E); + T_0_15( 1, E, A, B, C, D); + T_0_15( 2, D, E, A, B, C); + T_0_15( 3, C, D, E, A, B); + T_0_15( 4, B, C, D, E, A); + T_0_15( 5, A, B, C, D, E); + T_0_15( 6, E, A, B, C, D); + T_0_15( 7, D, E, A, B, C); + T_0_15( 8, C, D, E, A, B); + T_0_15( 9, B, C, D, E, A); + T_0_15(10, A, B, C, D, E); + T_0_15(11, E, A, B, C, D); + T_0_15(12, D, E, A, B, C); + T_0_15(13, C, D, E, A, B); + T_0_15(14, B, C, D, E, A); + T_0_15(15, A, B, C, D, E); /* Round 1 - tail. Input from 512-bit mixing array */ - T_16_19(16); T_16_19(17); T_16_19(18); T_16_19(19); + T_16_19(16, E, A, B, C, D); + T_16_19(17, D, E, A, B, C); + T_16_19(18, C, D, E, A, B); + T_16_19(19, B, C, D, E, A); /* Round 2 */ - T_20_39(20); T_20_39(21); T_20_39(22); T_20_39(23); T_20_39(24); - T_20_39(25); T_20_39(26); T_20_39(27); T_20_39(28); T_20_39(29); - T_20_39(30); T_20_39(31); T_20_39(32); T_20_39(33); T_20_39(34); - T_20_39(35); T_20_39(36); T_20_39(37); T_20_39(38); T_20_39(39); + T_20_39(20, A, B, C, D, E); + T_20_39(21, E, A, B, C, D); + T_20_39(22, D, E, A, B, C); + T_20_39(23, C, D, E, A, B); + T_20_39(24, B, C, D, E, A); + T_20_39(25, A, B, C, D, E); + T_20_39(26, E, A, B, C, D); + T_20_39(27, D, E, A, B, C); + T_20_39(28, C, D, E, A, B); + T_20_39(29, B, C, D, E, A); + T_20_39(30, A, B, C, D, E); + T_20_39(31, E, A, B, C, D); + T_20_39(32, D, E, A, B, C); + T_20_39(33, C, D, E, A, B); + T_20_39(34, B, C, D, E, A); + T_20_39(35, A, B, C, D, E); + T_20_39(36, E, A, B, C, D); + T_20_39(37, D, E, A, B, C); + T_20_39(38, C, D, E, A, B); + T_20_39(39, B, C, D, E, A); /* Round 3 */ - T_40_59(40); T_40_59(41); T_40_59(42); T_40_59(43); T_40_59(44); - T_40_59(45); T_40_59(46); T_40_59(47); T_40_59(48); T_40_59(49); - T_40_59(50); T_40_59(51); T_40_59(52); T_40_59(53); T_40_59(54); - T_40_59(55); T_40_59(56); T_40_59(57); T_40_59(58); T_40_59(59); + T_40_59(40, A, B, C, D, E); + T_40_59(41, E, A, B, C, D); + T_40_59(42, D, E, A, B, C); + T_40_59(43, C, D, E, A, B); + T_40_59(44, B, C, D, E, A); + T_40_59(45, A, B, C, D, E); + T_40_59(46, E, A, B, C, D); + T_40_59(47, D, E, A, B, C); + T_40_59(48, C, D, E, A, B); + T_40_59(49, B, C, D, E, A); + T_40_59(50, A, B, C, D, E); + T_40_59(51, E, A, B, C, D); + T_40_59(52, D, E, A, B, C); + T_40_59(53, C, D, E, A, B); + T_40_59(54, B, C, D, E, A); + T_40_59(55, A, B, C, D, E); + T_40_59(56, E, A, B, C, D); + T_40_59(57, D, E, A, B, C); + T_40_59(58, C, D, E, A, B); + T_40_59(59, B, C, D, E, A); /* Round 4 */ - T_60_79(60); T_60_79(61); T_60_79(62); T_60_79(63); T_60_79(64); - T_60_79(65); T_60_79(66); T_60_79(67); T_60_79(68); T_60_79(69); - T_60_79(70); T_60_79(71); T_60_79(72); T_60_79(73); T_60_79(74); - T_60_79(75); T_60_79(76); T_60_79(77); T_60_79(78); T_60_79(79); + T_60_79(60, A, B, C, D, E); + T_60_79(61, E, A, B, C, D); + T_60_79(62, D, E, A, B, C); + T_60_79(63, C, D, E, A, B); + T_60_79(64, B, C, D, E, A); + T_60_79(65, A, B, C, D, E); + T_60_79(66, E, A, B, C, D); + T_60_79(67, D, E, A, B, C); + T_60_79(68, C, D, E, A, B); + T_60_79(69, B, C, D, E, A); + T_60_79(70, A, B, C, D, E); + T_60_79(71, E, A, B, C, D); + T_60_79(72, D, E, A, B, C); + T_60_79(73, C, D, E, A, B); + T_60_79(74, B, C, D, E, A); + T_60_79(75, A, B, C, D, E); + T_60_79(76, E, A, B, C, D); + T_60_79(77, D, E, A, B, C); + T_60_79(78, C, D, E, A, B); + T_60_79(79, B, C, D, E, A); ctx->H[0] += A; ctx->H[1] += B; From 66c9c6c0fbba0894ebce3da572f62eb05162e547 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 7 Aug 2009 21:16:46 -0700 Subject: [PATCH 088/133] block-sha1: improved SHA1 hashing I think I have found a way to avoid the gcc crazyness. Lookie here: # TIME[s] SPEED[MB/s] rfc3174 5.094 119.8 rfc3174 5.098 119.7 linus 1.462 417.5 linusas 2.008 304 linusas2 1.878 325 mozilla 5.566 109.6 mozillaas 5.866 104.1 openssl 1.609 379.3 spelvin 1.675 364.5 spelvina 1.601 381.3 nettle 1.591 383.6 notice? I outperform all the hand-tuned asm on 32-bit too. By quite a margin, in fact. Now, I didn't try a P4, and it's possible that it won't do that there, but the 32-bit code generation sure looks impressive on my Nehalem box. The magic? I force the stores to the 512-bit hash bucket to be done in order. That seems to help a lot. The diff is trivial (on top of the "rename registers with cpp" patch), as appended. And it does seem to fix the P4 issues too, although I can obviously (once again) only test Prescott, and only in 64-bit mode: # TIME[s] SPEED[MB/s] rfc3174 1.662 36.73 rfc3174 1.64 37.22 linus 0.2523 241.9 linusas 0.4367 139.8 linusas2 0.4487 136 mozilla 0.9704 62.9 mozillaas 0.9399 64.94 that's some really impressive improvement. All from just saying "do the stores in the order I told you to, dammit!" to the compiler. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index b715916675..886bcf25e2 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -93,6 +93,7 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) /* This "rolls" over the 512-bit array */ #define W(x) (array[(x)&15]) +#define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) /* * Where do we get the source from? The first 16 iterations get it from @@ -102,9 +103,9 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) #define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) #define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ - unsigned int TEMP = input(t); W(t) = TEMP; \ - TEMP += E + SHA_ROL(A,5) + (fn) + (constant); \ - B = SHA_ROR(B, 2); E = TEMP; } while (0) + unsigned int TEMP = input(t); setW(t, TEMP); \ + E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \ + B = SHA_ROR(B, 2); } while (0) #define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) #define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) From 0b91322311b649a4b5f9581fec2dca9e1c2da716 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 8 Aug 2009 22:44:20 +0200 Subject: [PATCH 089/133] api-run-command.txt: describe error behavior of run_command functions Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Documentation/technical/api-run-command.txt | 31 +++++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt index 2efe7a40be..b26c28133c 100644 --- a/Documentation/technical/api-run-command.txt +++ b/Documentation/technical/api-run-command.txt @@ -35,12 +35,32 @@ Functions Convenience functions that encapsulate a sequence of start_command() followed by finish_command(). The argument argv specifies the program and its arguments. The argument opt is zero - or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or - `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members - .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`. + or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, + `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE` + that correspond to the members .no_stdin, .git_cmd, + .stdout_to_stderr, .silent_exec_failure of `struct child_process`. The argument dir corresponds the member .dir. The argument env corresponds to the member .env. +The functions above do the following: + +. If a system call failed, errno is set and -1 is returned. A diagnostic + is printed. + +. If the program was not found, then -1 is returned and errno is set to + ENOENT; a diagnostic is printed only if .silent_exec_failure is 0. + +. Otherwise, the program is run. If it terminates regularly, its exit + code is returned. No diagnistic is printed, even if the exit code is + non-zero. + +. If the program terminated due to a signal, then the return value is the + signal number - 128, ie. it is negative and so indicates an unusual + condition; a diagnostic is printed. This return value can be passed to + exit(2), which will report the same code to the parent process that a + POSIX shell's $? would report for a program that died from the signal. + + `start_async`:: Run a function asynchronously. Takes a pointer to a `struct @@ -143,6 +163,11 @@ string pointers (NULL terminated) in .env: To specify a new initial working directory for the sub-process, specify it in the .dir member. +If the program cannot be found, the functions return -1 and set +errno to ENOENT. Normally, an error message is printed, but if +.silent_exec_failure is set to 1, no message is printed for this +special error condition. + * `struct async` From b6b0737d02e3bb9b3015ec66f5b6454d5b83d57b Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 9 Aug 2009 17:35:02 +0200 Subject: [PATCH 090/133] t6035-merge-dir-to-symlink depends on SYMLINKS prerequisite Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t6035-merge-dir-to-symlink.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh index a0ddf1e0f4..5b96fb0b37 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6035-merge-dir-to-symlink.sh @@ -3,6 +3,12 @@ test_description='merging when a directory was replaced with a symlink' . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + test_expect_success 'create a commit where dir a/b changed to symlink' ' mkdir -p a/b/c a/b-2/c && > a/b/c/d && From 7d53a07a2833116cdf52ca256375c6b37f4d6b46 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 9 Aug 2009 17:38:04 +0200 Subject: [PATCH 091/133] t0001-init: fix a file name Without this change, grep fails because it does not find the file instead of because it does not find the text in the file. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t0001-init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 49caa29061..07e011d9ef 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -251,7 +251,7 @@ test_expect_success 'init creates a new deep directory' ' git init --bare --shared=0660 newdir/a/b/c && test -d newdir/a/b/c/refs && ls -ld newdir/a newdir/a/b > lsab.out && - ! grep -v "^drwxrw[sx]r-x" ls.out && + ! grep -v "^drwxrw[sx]r-x" lsab.out && ls -ld newdir/a/b/c > lsc.out && ! grep -v "^drwxrw[sx]---" lsc.out ) From d82e75e86c8d1555aaa6415034aff215633fe7f2 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 9 Aug 2009 18:02:55 +0200 Subject: [PATCH 092/133] t0001-init: split the existence test from the permission test The test for correct permissions after init created a deep directory must be guarded by POSIXPERM. But testing that the deep dirctory exists is good even on platforms that do not provide the POSIXPERM prerequiste. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t0001-init.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 07e011d9ef..5386504790 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -243,6 +243,12 @@ test_expect_success 'init recreates a new bare directory' ' ' test_expect_success 'init creates a new deep directory' ' + rm -fr newdir && + git init newdir/a/b/c && + test -d newdir/a/b/c/.git/refs +' + +test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' ' rm -fr newdir && ( # Leading directories should honor umask while From eaf0551d56b4fd3a97c8d0f5ded1ea6e891f9005 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 9 Aug 2009 04:37:52 -0400 Subject: [PATCH 093/133] tests: use "$TEST_DIRECTORY" instead of ".." The $TEST_DIRECTORY variable allows tests to find the top-level test directory regardless of the current working directory. In the past, this has been used to accomodate tests which change directories, but it is also the first step to being able to move trash directories outside of the $TEST_DIRECTORY hierarchy. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t3404-rebase-interactive.sh | 2 +- t/t3411-rebase-preserve-around-merges.sh | 2 +- t/t3414-rebase-preserve-onto.sh | 2 +- t/t4020-diff-external.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index a973628e8e..4cae019521 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -10,7 +10,7 @@ that the result still makes sense. ' . ./test-lib.sh -. ../lib-rebase.sh +. "$TEST_DIRECTORY"/lib-rebase.sh set_fake_editor diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index 6533505218..14a23cd872 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -10,7 +10,7 @@ a merge to before the merge. ' . ./test-lib.sh -. ../lib-rebase.sh +. "$TEST_DIRECTORY"/lib-rebase.sh set_fake_editor diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh index 80019ee072..ee0a6cccfd 100755 --- a/t/t3414-rebase-preserve-onto.sh +++ b/t/t3414-rebase-preserve-onto.sh @@ -10,7 +10,7 @@ aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM. ' . ./test-lib.sh -. ../lib-rebase.sh +. "$TEST_DIRECTORY"/lib-rebase.sh # Set up branches like this: # A1---B1---E1---F1---G1 diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 4ea42e00da..a7602cf923 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -166,7 +166,7 @@ test_expect_success 'diff --cached' ' git update-index --assume-unchanged file && echo second >file && git diff --cached >actual && - test_cmp ../t4020/diff.NUL actual + test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual ' test_done From 91c8b825ce2a301e9861be60c3d7853fefa93c04 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 9 Aug 2009 04:38:11 -0400 Subject: [PATCH 094/133] tests: provide $TRASH_DIRECTORY variable Most scripts don't care about the absolute path to the trash directory. The one exception was t4014 script, which pieced together $TEST_DIRECTORY and $test itself to get an absolute directory. Instead, let's provide a $TRASH_DIRECTORY which specifies the same thing. This keeps the $test variable internal to test-lib.sh and paves the way for trash directories in other locations. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t4014-format-patch.sh | 3 +-- t/test-lib.sh | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 922a8941ed..531f5b795c 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -493,13 +493,12 @@ test_expect_success 'format-patch from a subdirectory (2)' ' ' test_expect_success 'format-patch from a subdirectory (3)' ' - here="$TEST_DIRECTORY/$test" && rm -f 0* && filename=$( rm -rf sub && mkdir -p sub/dir && cd sub/dir && - git format-patch -1 -o "$here" + git format-patch -1 -o "$TRASH_DIRECTORY" ) && basename=$(expr "$filename" : ".*/\(.*\)") && test -f "$basename" diff --git a/t/test-lib.sh b/t/test-lib.sh index 5fdc5d94a2..e6a7bb1b45 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -645,7 +645,8 @@ fi # Test repository test="trash directory.$(basename "$0" .sh)" -test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test" +TRASH_DIRECTORY="$TEST_DIRECTORY/$test" +test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY rm -fr "$test" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" From f423ef5f2bec35efbc328f02e468a18612970242 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 9 Aug 2009 04:39:45 -0400 Subject: [PATCH 095/133] tests: allow user to specify trash directory location The tests generate a large amount of I/O activity creating and destroying repositories and files. We can improve the time it takes to run the test suite by creating trash directories on filesystems with better performance characteristic, even though we may not want the rest of the git repository on those filesystems (e.g., because they are not network connected, or because they are temporary ramdisks). For example, on a dual processor system: $ cd t && time make -j32 real 1m51.562s user 0m59.260s sys 1m20.933s # /dev/shm is tmpfs $ cd t && time make -j32 GIT_TEST_OPTS="--root=/dev/shm" real 1m1.484s user 0m53.555s sys 1m5.264s We almost halve the wall clock time, and we utilize the dual processors much better. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/test-lib.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index e6a7bb1b45..a5b8d03db0 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -114,6 +114,9 @@ do valgrind=t; verbose=t; shift ;; --tee) shift ;; # was handled already + --root=*) + root=$(expr "z$1" : 'z[^=]*=\(.*\)') + shift ;; *) echo "error: unknown test option '$1'" >&2; exit 1 ;; esac @@ -645,7 +648,11 @@ fi # Test repository test="trash directory.$(basename "$0" .sh)" -TRASH_DIRECTORY="$TEST_DIRECTORY/$test" +test -n "$root" && test="$root/$test" +case "$test" in +/*) TRASH_DIRECTORY="$test" ;; + *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;; +esac test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY rm -fr "$test" || { GIT_EXIT_OK=t From c14417c4f5f8d7765e232f1e908b85d2ae14ec34 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 9 Aug 2009 04:50:37 -0400 Subject: [PATCH 096/133] t/Makefile: include config.mak This is useful if you want to specify GIT_TEST_OPTS that you always use. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/Makefile b/t/Makefile index bf816fc850..bd09390d32 100644 --- a/t/Makefile +++ b/t/Makefile @@ -3,6 +3,8 @@ # Copyright (c) 2005 Junio C Hamano # +-include ../config.mak + #GIT_TEST_OPTS=--verbose --debug SHELL_PATH ?= $(SHELL) TAR ?= $(TAR) From ce0646184696ae6d87c0e243d058cf5df6459c4f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 9 Aug 2009 06:01:48 -0400 Subject: [PATCH 097/133] add tests for merge message headings When calling "git merge $X", we automatically generate a commit message containing something like "Merge branch '$X'". This test script checks that those messages say what they should, and exposes a failure when merging a refname that is ambiguous between a tag and a branch. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t7608-merge-messages.sh | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100755 t/t7608-merge-messages.sh diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh new file mode 100755 index 0000000000..9d10583916 --- /dev/null +++ b/t/t7608-merge-messages.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='test auto-generated merge messages' +. ./test-lib.sh + +check_oneline() { + echo "$1" | sed "s/Q/'/g" >expect && + git log -1 --pretty=tformat:%s >actual && + test_cmp expect actual +} + +test_expect_success 'merge local branch' ' + test_commit master-1 && + git checkout -b local-branch && + test_commit branch-1 && + git checkout master && + test_commit master-2 && + git merge local-branch && + check_oneline "Merge branch Qlocal-branchQ" +' + +test_expect_success 'merge octopus branches' ' + git checkout -b octopus-a master && + test_commit octopus-1 && + git checkout -b octopus-b master && + test_commit octopus-2 && + git checkout master && + git merge octopus-a octopus-b && + check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ" +' + +test_expect_success 'merge tag' ' + git checkout -b tag-branch master && + test_commit tag-1 && + git checkout master && + test_commit master-3 && + git merge tag-1 && + check_oneline "Merge commit Qtag-1Q" +' + +test_expect_failure 'ambiguous tag' ' + git checkout -b ambiguous master && + test_commit ambiguous && + git checkout master && + test_commit master-4 && + git merge ambiguous && + check_oneline "Merge commit QambiguousQ" +' + +test_done From 751c59746c7522982214528eb653dfb61d372257 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 9 Aug 2009 06:02:24 -0400 Subject: [PATCH 098/133] merge: fix incorrect merge message for ambiguous tag/branch If we have both a tag and a branch named "foo", then calling "git merge foo" will warn about the ambiguous ref, but merge the tag. When generating the commit message, though, we simply checked whether "refs/heads/foo" existed, and if it did, assumed it was a branch. This led to the statement "Merge branch 'foo'" in the commit message, which is quite wrong. Instead, we should use dwim_ref to find the actual ref used, and describe it appropriately. In addition to the test in t7608, we must also tweak the expected output of t4202, which was accidentally triggering this bug. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-merge.c | 15 +++++++-------- t/t4202-log.sh | 4 ++-- t/t7608-merge-messages.sh | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/builtin-merge.c b/builtin-merge.c index 82b546689c..f7db14846e 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -358,6 +358,7 @@ static void merge_name(const char *remote, struct strbuf *msg) struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; const char *ptr; + char *found_ref; int len, early; strbuf_branchname(&bname, remote); @@ -368,14 +369,12 @@ static void merge_name(const char *remote, struct strbuf *msg) if (!remote_head) die("'%s' does not point to a commit", remote); - strbuf_addstr(&buf, "refs/heads/"); - strbuf_addstr(&buf, remote); - resolve_ref(buf.buf, branch_head, 0, NULL); - - if (!hashcmp(remote_head->sha1, branch_head)) { - strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", - sha1_to_hex(branch_head), remote); - goto cleanup; + if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { + if (!prefixcmp(found_ref, "refs/heads/")) { + strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", + sha1_to_hex(branch_head), remote); + goto cleanup; + } } /* See if remote matches ^^^.. or ~ */ diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 48e0088b47..1e952ca55b 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -320,11 +320,11 @@ test_expect_success 'set up more tangled history' ' ' cat > expect <<\EOF -* Merge branch 'reach' +* Merge commit 'reach' |\ | \ | \ -*-. \ Merge branches 'octopus-a' and 'octopus-b' +*-. \ Merge commit 'octopus-a'; commit 'octopus-b' |\ \ \ * | | | seventh | | * | octopus-b diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh index 9d10583916..81ced8ac3c 100755 --- a/t/t7608-merge-messages.sh +++ b/t/t7608-merge-messages.sh @@ -38,7 +38,7 @@ test_expect_success 'merge tag' ' check_oneline "Merge commit Qtag-1Q" ' -test_expect_failure 'ambiguous tag' ' +test_expect_success 'ambiguous tag' ' git checkout -b ambiguous master && test_commit ambiguous && git checkout master && From 69a8b7c74192dfa7bde3937d2c84324a2cd1506b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 9 Aug 2009 06:02:51 -0400 Subject: [PATCH 099/133] merge: indicate remote tracking branches in merge message Previously when merging directly from a local tracking branch like: git merge origin/master The merge message said: Merge commit 'origin/master' * commit 'origin/master': ... Instead, let's be more explicit about what we are merging: Merge remote branch 'origin/master' * origin/master: ... We accomplish this by recognizing remote tracking branches in git-merge when we build the simulated FETCH_HEAD output that we feed to fmt-merge-msg. In addition to a new test in t7608, we have to tweak the expected output of t3409, which does such a merge. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-merge.c | 5 +++++ t/t3409-rebase-preserve-merges.sh | 2 +- t/t7608-merge-messages.sh | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/builtin-merge.c b/builtin-merge.c index f7db14846e..f4de73fa9d 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -375,6 +375,11 @@ static void merge_name(const char *remote, struct strbuf *msg) sha1_to_hex(branch_head), remote); goto cleanup; } + if (!prefixcmp(found_ref, "refs/remotes/")) { + strbuf_addf(msg, "%s\t\tremote branch '%s' of .\n", + sha1_to_hex(branch_head), remote); + goto cleanup; + } } /* See if remote matches ^^^.. or ~ */ diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh index e6c832780f..297d165476 100755 --- a/t/t3409-rebase-preserve-merges.sh +++ b/t/t3409-rebase-preserve-merges.sh @@ -71,7 +71,7 @@ test_expect_success 'rebase -p fakes interactive rebase' ' git fetch && git rebase -p origin/topic && test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && - test 1 = $(git rev-list --all --pretty=oneline | grep "Merge commit" | wc -l) + test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote branch " | wc -l) ) ' diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh index 81ced8ac3c..28d56797b1 100755 --- a/t/t7608-merge-messages.sh +++ b/t/t7608-merge-messages.sh @@ -47,4 +47,14 @@ test_expect_success 'ambiguous tag' ' check_oneline "Merge commit QambiguousQ" ' +test_expect_success 'remote branch' ' + git checkout -b remote master && + test_commit remote-1 && + git update-ref refs/remotes/origin/master remote && + git checkout master && + test_commit master-5 && + git merge origin/master && + check_oneline "Merge remote branch Qorigin/masterQ" +' + test_done From f5f1e164bd4234683e5a1fc4a49a77b1b01855f8 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sun, 9 Aug 2009 02:47:36 +0200 Subject: [PATCH 100/133] Document 'stash clear' recovery via unreachable commits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an example to the stash documentation that shows how to quickly find candidate commits among the 'git fsck --unreachable' output. Unless you have merges of branch names containing WIP, or edit your merge messages to say WIP, there will be no false positives. Snippet written by Björn "doener" Steinbrink and me after zepolen_ asked on IRC. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 1c64a02fe5..2f5ca7b1a3 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -114,7 +114,8 @@ no conflicts. clear:: Remove all the stashed states. Note that those states will then - be subject to pruning, and may be difficult or impossible to recover. + be subject to pruning, and may be impossible to recover (see + 'Examples' below for a possible strategy). drop [-q|--quiet] []:: @@ -217,6 +218,20 @@ $ edit/build/test remaining parts $ git commit foo -m 'Remaining parts' ---------------------------------------------------------------- +Recovering stashes that were cleared/dropped erroneously:: + +If you mistakenly drop or clear stashes, they cannot be recovered +through the normal safety mechanisms. However, you can try the +following incantation to get a list of stashes that are still in your +repository, but not reachable any more: ++ +---------------------------------------------------------------- +git fsck --unreachable | +grep commit | cut -d\ -f3 | +xargs git log --merges --no-walk --grep=WIP +---------------------------------------------------------------- + + SEE ALSO -------- linkgit:git-checkout[1], From f0e588dffc1a592ec7879cdb610654937cbbe25e Mon Sep 17 00:00:00 2001 From: Mark A Rada Date: Sat, 8 Aug 2009 18:24:02 -0400 Subject: [PATCH 101/133] git-instaweb: fix mod_perl detection for apache2 The script was looking for something that matched the '^our $gitbin' regex, which no longer exists in gitweb.cgi. Now it looks for 'MOD_PERL', which should be on the line that checks to see if the script is running in a mod_perl environment. Signed-off-by: Mark Rada Signed-off-by: Junio C Hamano --- git-instaweb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-instaweb.sh b/git-instaweb.sh index 32f6496b0d..5f5cac75ea 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -278,7 +278,7 @@ EOF # check to see if Dennis Stosberg's mod_perl compatibility patch # (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied - if test -f "$module_path/mod_perl.so" && grep '^our $gitbin' \ + if test -f "$module_path/mod_perl.so" && grep 'MOD_PERL' \ "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null then # favor mod_perl if available From 4ebe6e92c3b62d5192b6cc8158fde701bf2c0dcb Mon Sep 17 00:00:00 2001 From: Adam Brewster Date: Mon, 3 Aug 2009 21:40:37 -0400 Subject: [PATCH 102/133] svn: Add && to t9107-git-svn-migrate.sh It was probably intended for the test to fail unless all of the commands succeed. [ew: fixed tests to actually work] Signed-off-by: Eric Wong --- t/t9107-git-svn-migrate.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index 3a9e07768d..c0098d9808 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -56,7 +56,15 @@ test_expect_success 'initialize a multi-repository repo' ' git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" && for i in tags/0.1 tags/0.2 tags/0.3; do git config --add svn-remote.svn.fetch \ - $i:refs/remotes/$i || exit 1; done + $i:refs/remotes/$i || exit 1; done && + git config --get-all svn-remote.svn.fetch > fetch.out && + grep "^trunk:refs/remotes/trunk$" fetch.out && + grep "^branches/a:refs/remotes/a$" fetch.out && + grep "^branches/b:refs/remotes/b$" fetch.out && + grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out && + grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out && + grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out && + grep "^:refs/${remotes_git_svn}" fetch.out ' # refs should all be different, but the trees should all be the same: @@ -86,14 +94,14 @@ test_expect_success 'migrate --minimize on old inited layout' ' echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1; done && git svn migrate --minimize && - test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" && + test -z "`git config -l | grep "^svn-remote\.git-svn\."`" && git config --get-all svn-remote.svn.fetch > fetch.out && grep "^trunk:refs/remotes/trunk$" fetch.out && grep "^branches/a:refs/remotes/a$" fetch.out && grep "^branches/b:refs/remotes/b$" fetch.out && grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out && grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out && - grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out + grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out && grep "^:refs/${remotes_git_svn}" fetch.out ' From 63de84ad60984105b1cd60fb888968b352539313 Mon Sep 17 00:00:00 2001 From: Adam Brewster Date: Mon, 3 Aug 2009 21:40:38 -0400 Subject: [PATCH 103/133] svn: Honor --prefix option in init without --stdlayout Most users who type git svn init file:///tmp/repo --prefix=my-svn/ would expect the root of the svn repository to be tracked by refs/remotes/my-svn/git-svn. Acked-by: Eric Wong --- git-svn.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index d075810724..fa8583963b 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3317,7 +3317,8 @@ sub _new { $repo_id = $Git::SVN::default_repo_id; } unless (defined $ref_id && length $ref_id) { - $_[2] = $ref_id = $Git::SVN::default_ref_id; + $_prefix = '' unless defined($_prefix); + $_[2] = $ref_id = $_prefix . $Git::SVN::default_ref_id; } $_[1] = $repo_id; my $dir = "$ENV{GIT_DIR}/svn/$ref_id"; From a7d72544b112f093b6189a86d05b198b2be73828 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 7 Aug 2009 21:21:21 +0200 Subject: [PATCH 104/133] git-svn: ignore leading blank lines in svn:ignore Subversion ignores all blank lines in svn:ignore properties. The old git-svn code ignored blank lines everywhere except for the first line of the svn:ignore property. This patch makes the "git svn show-ignore" and "git svn create-ignore" commands ignore leading blank lines, too. Also include leading blank lines in the test suite. Signed-off-by: Michael Haggerty Acked-by: Eric Wong --- git-svn.perl | 2 ++ t/t9101-git-svn-props.sh | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index fa8583963b..b0bfb74792 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -764,6 +764,7 @@ sub cmd_show_ignore { print STDOUT "\n# $path\n"; my $s = $props->{'svn:ignore'} or return; $s =~ s/[\r\n]+/\n/g; + $s =~ s/^\n+//; chomp $s; $s =~ s#^#$path#gm; print STDOUT "$s\n"; @@ -801,6 +802,7 @@ sub cmd_create_ignore { open(GITIGNORE, '>', $ignore) or fatal("Failed to open `$ignore' for writing: $!"); $s =~ s/[\r\n]+/\n/g; + $s =~ s/^\n+//; chomp $s; # Prefix all patterns so that the ignore doesn't apply # to sub-directories. diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 9da4178c94..929499e996 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -142,7 +142,9 @@ test_expect_success 'test show-ignore' " touch deeply/nested/directory/.keep && svn_cmd add deeply && svn_cmd up && - svn_cmd propset -R svn:ignore 'no-such-file*' . + svn_cmd propset -R svn:ignore ' +no-such-file* +' . svn_cmd commit -m 'propset svn:ignore' cd .. && git svn show-ignore > show-ignore.got && @@ -171,6 +173,7 @@ test_expect_success 'test create-ignore' " " cat >prop.expect <<\EOF + no-such-file* EOF From 5ae9ebfd582a6e8c2d6fd3d27b7df6cb12a98839 Mon Sep 17 00:00:00 2001 From: Tay Ray Chuan Date: Mon, 10 Aug 2009 23:55:48 +0800 Subject: [PATCH 105/133] http.c: free preq when aborting Free preq in new_http_pack_request when aborting. preq was allocated before jumping to the 'abort' label so this is safe. Signed-off-by: Tay Ray Chuan Signed-off-by: Junio C Hamano --- http.c | 1 + 1 file changed, 1 insertion(+) diff --git a/http.c b/http.c index a2720d576d..cfe32f5f0e 100644 --- a/http.c +++ b/http.c @@ -1059,6 +1059,7 @@ struct http_pack_request *new_http_pack_request( abort: free(filename); + free(preq); return NULL; } From bb99190e27c455867bb364059f9a44c0571fc914 Mon Sep 17 00:00:00 2001 From: Tay Ray Chuan Date: Mon, 10 Aug 2009 23:59:55 +0800 Subject: [PATCH 106/133] http.c: replace usage of temporary variable for urls Use preq->url in new_http_pack_request and freq->url in new_http_object_request when calling curl_setopt(CURLOPT_URL), instead of using an intermediate variable, 'url'. Signed-off-by: Tay Ray Chuan Signed-off-by: Junio C Hamano --- http.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/http.c b/http.c index cfe32f5f0e..98f9707625 100644 --- a/http.c +++ b/http.c @@ -1004,7 +1004,6 @@ int finish_http_pack_request(struct http_pack_request *preq) struct http_pack_request *new_http_pack_request( struct packed_git *target, const char *base_url) { - char *url; char *filename; long prev_posn = 0; char range[RANGE_HEADER_SIZE]; @@ -1018,8 +1017,7 @@ struct http_pack_request *new_http_pack_request( end_url_with_slash(&buf, base_url); strbuf_addf(&buf, "objects/pack/pack-%s.pack", sha1_to_hex(target->sha1)); - url = strbuf_detach(&buf, NULL); - preq->url = xstrdup(url); + preq->url = strbuf_detach(&buf, NULL); filename = sha1_pack_name(target->sha1); snprintf(preq->filename, sizeof(preq->filename), "%s", filename); @@ -1035,7 +1033,7 @@ struct http_pack_request *new_http_pack_request( preq->slot->local = preq->packfile; curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile); curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite); - curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url); + curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url); curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header); @@ -1059,6 +1057,7 @@ struct http_pack_request *new_http_pack_request( abort: free(filename); + free(preq->url); free(preq); return NULL; } @@ -1099,7 +1098,6 @@ struct http_object_request *new_http_object_request(const char *base_url, char *hex = sha1_to_hex(sha1); char *filename; char prevfile[PATH_MAX]; - char *url; int prevlocal; unsigned char prev_buf[PREV_BUF_SIZE]; ssize_t prev_read = 0; @@ -1153,8 +1151,7 @@ struct http_object_request *new_http_object_request(const char *base_url, git_SHA1_Init(&freq->c); - url = get_remote_object_url(base_url, hex, 0); - freq->url = xstrdup(url); + freq->url = get_remote_object_url(base_url, hex, 0); /* * If a previous temp file is present, process what was already @@ -1199,7 +1196,7 @@ struct http_object_request *new_http_object_request(const char *base_url, curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq); curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file); curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr); - curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url); + curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url); curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header); /* @@ -1219,9 +1216,9 @@ struct http_object_request *new_http_object_request(const char *base_url, return freq; - free(url); abort: free(filename); + free(freq->url); free(freq); return NULL; } From 0c4f21e452dca5a705c36ca7e363c1a3b3c2a5b8 Mon Sep 17 00:00:00 2001 From: Jeff Lasslett Date: Tue, 11 Aug 2009 00:05:06 +0800 Subject: [PATCH 107/133] Check return value of ftruncate call in http.c In new_http_object_request(), check ftruncate() call return value and handle possible errors. Signed-off-by: Jeff Lasslett Signed-off-by: Tay Ray Chuan Signed-off-by: Junio C Hamano --- http.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/http.c b/http.c index 98f9707625..14d535747d 100644 --- a/http.c +++ b/http.c @@ -1187,7 +1187,11 @@ struct http_object_request *new_http_object_request(const char *base_url, if (prev_posn>0) { prev_posn = 0; lseek(freq->localfile, 0, SEEK_SET); - ftruncate(freq->localfile, 0); + if (ftruncate(freq->localfile, 0) < 0) { + error("Couldn't truncate temporary file %s for %s: %s", + freq->tmpfile, freq->filename, strerror(errno)); + goto abort; + } } } From 28e9cf6512cae1b50a2d2003bb59da4392d99e2e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 10 Aug 2009 18:22:18 +0200 Subject: [PATCH 108/133] Expose the has_non_ascii() function This function is useful outside of log-tree.c, too. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.h | 1 + log-tree.c | 12 ------------ pretty.c | 12 ++++++++++++ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/commit.h b/commit.h index ba9f63813e..4886544b63 100644 --- a/commit.h +++ b/commit.h @@ -64,6 +64,7 @@ enum cmit_fmt { }; extern int non_ascii(int); +extern int has_non_ascii(const char *text); struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ extern char *reencode_commit_message(const struct commit *commit, const char **encoding_p); diff --git a/log-tree.c b/log-tree.c index 6f73c17d74..a3b4c0692c 100644 --- a/log-tree.c +++ b/log-tree.c @@ -168,18 +168,6 @@ static unsigned int digits_in_number(unsigned int number) return result; } -static int has_non_ascii(const char *s) -{ - int ch; - if (!s) - return 0; - while ((ch = *s++) != '\0') { - if (non_ascii(ch)) - return 1; - } - return 0; -} - void get_patch_filename(struct commit *commit, int nr, const char *suffix, struct strbuf *buf) { diff --git a/pretty.c b/pretty.c index e5328dab5b..3b2ecdd20e 100644 --- a/pretty.c +++ b/pretty.c @@ -86,6 +86,18 @@ int non_ascii(int ch) return !isascii(ch) || ch == '\033'; } +int has_non_ascii(const char *s) +{ + int ch; + if (!s) + return 0; + while ((ch = *s++) != '\0') { + if (non_ascii(ch)) + return 1; + } + return 0; +} + static int is_rfc2047_special(char ch) { return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); From 0a7f448355f38b8a626ec3c120ad7118690897fe Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 10 Aug 2009 18:22:22 +0200 Subject: [PATCH 109/133] Correctly mark cover letters' encodings if they are not pure ASCII MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If your name is, say, Üwë, you want your cover letters to appear correctly. Convince format-patch to mark it as 8-bit. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin-log.c b/builtin-log.c index 0c2fa0ae2d..1929e0a16c 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -658,6 +658,10 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, log_write_email_headers(rev, head, &subject_start, &extra_headers, &need_8bit_cte); + for (i = 0; !need_8bit_cte && i < nr; i++) + if (has_non_ascii(list[i]->buffer)) + need_8bit_cte = 1; + msg = body; pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822, encoding); From 584c43567be838c8e2dd89c881cac028f5bd4565 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Aug 2009 20:08:12 -0500 Subject: [PATCH 110/133] am: allow individual e-mail files as input We traditionally allowed a mbox file or a directory name of a maildir (but never an individual file inside a maildir) to be given to "git am". Even though an individual file in a maildir (or more generally, a piece of RFC2822 e-mail) is not a mbox file, it contains enough information to create a commit out of it, so there is no reason to reject one. Running mailsplit on such a file feels stupid, but it does not hurt. This builds on top of a5a6755 (git-am foreign patch support: introduce patch_format, 2009-05-27) that introduced mailbox format detection. The codepath to deal with a mbox requires it to begin with "From " line and also allows it to begin with "From: ", but a random piece of e-mail can and often do begin with any valid RFC2822 header lines. Instead of checking the first line, we extract all the lines up to the first empty line, and make sure they look like e-mail headers. A test is added to t4150 to demonstrate this feature. Signed-off-by: Junio C Hamano Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- git-am.sh | 14 ++++++++++++++ t/t4150-am.sh | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/git-am.sh b/git-am.sh index d64d997535..dd60f5d9cb 100755 --- a/git-am.sh +++ b/git-am.sh @@ -191,6 +191,20 @@ check_patch_format () { esac ;; esac + if test -z "$patch_format" && + test -n "$l1" && + test -n "$l2" && + test -n "$l3" + then + # This begins with three non-empty lines. Is this a + # piece of e-mail a-la RFC2822? Grab all the headers, + # discarding the indented remainder of folded lines, + # and see if it looks like that they all begin with the + # header field names... + sed -n -e '/^$/q' -e '/^[ ]/d' -e p "$1" | + egrep -v '^[A-Za-z]+(-[A-Za-z]+)*:' >/dev/null || + patch_format=mbox + fi } < "$1" || clean_abort } diff --git a/t/t4150-am.sh b/t/t4150-am.sh index a12bf84623..8296605234 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -77,6 +77,12 @@ test_expect_success setup ' git commit -s -F msg && git tag second && git format-patch --stdout first >patch1 && + { + echo "X-Fake-Field: Line One" && + echo "X-Fake-Field: Line Two" && + echo "X-Fake-Field: Line Three" && + git format-patch --stdout first | sed -e "1d" + } > patch1.eml && sed -n -e "3,\$p" msg >file && git add file && test_tick && @@ -108,6 +114,15 @@ test_expect_success 'am applies patch correctly' ' test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" ' +test_expect_success 'am applies patch e-mail not in a mbox' ' + git checkout first && + git am patch1.eml && + ! test -d .git/rebase-apply && + test -z "$(git diff second)" && + test "$(git rev-parse second)" = "$(git rev-parse HEAD)" && + test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" +' + GIT_AUTHOR_NAME="Another Thor" GIT_AUTHOR_EMAIL="a.thor@example.com" GIT_COMMITTER_NAME="Co M Miter" From af12fb7b3048c5bdd2e90885a6736ebc29d84f01 Mon Sep 17 00:00:00 2001 From: Nicolas Sebrecht Date: Thu, 6 Aug 2009 20:08:13 -0500 Subject: [PATCH 111/133] git-am: print fair error message when format detection fails Avoid git ending with this message: "Patch format is not supported." With improved error message in the format detection failure case by Giuseppe Bilotta. Signed-off-by: Nicolas Sebrecht Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- git-am.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git-am.sh b/git-am.sh index dd60f5d9cb..f719f6e654 100755 --- a/git-am.sh +++ b/git-am.sh @@ -268,7 +268,11 @@ split_patches () { msgnum= ;; *) - clean_abort "Patch format $patch_format is not supported." + if test -n "$parse_patch" ; then + clean_abort "Patch format $patch_format is not supported." + else + clean_abort "Patch format detection failed." + fi ;; esac } From 926172c5e4808726244713ef70398cd38b055f1e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 10 Aug 2009 16:52:07 -0700 Subject: [PATCH 112/133] block-sha1: improve code on large-register-set machines For x86 performance (especially in 32-bit mode) I added that hack to write the SHA1 internal temporary hash using a volatile pointer, in order to get gcc to not try to cache the array contents. Because gcc will do all the wrong things, and then spill things in insane random ways. But on architectures like PPC, where you have 32 registers, it's actually perfectly reasonable to put the whole temporary array[] into the register set, and gcc can do so. So make the 'volatile unsigned int *' cast be dependent on a SMALL_REGISTER_SET preprocessor symbol, and enable it (currently) on just x86 and x86-64. With that, the routine is fairly reasonable even when compared to the hand-scheduled PPC version. Ben Herrenschmidt reports on a G5: * Paulus asm version: about 3.67s * Yours with no change: about 5.74s * Yours without "volatile": about 3.78s so with this the C version is within about 3% of the asm one. And add a lot of commentary on what the heck is going on. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 886bcf25e2..304cd0452d 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -82,6 +82,7 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) #define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) #define SHA_ROL(x,n) SHA_ASM("rol", x, n) #define SHA_ROR(x,n) SHA_ASM("ror", x, n) +#define SMALL_REGISTER_SET #else @@ -93,7 +94,29 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) /* This "rolls" over the 512-bit array */ #define W(x) (array[(x)&15]) -#define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) + +/* + * If you have 32 registers or more, the compiler can (and should) + * try to change the array[] accesses into registers. However, on + * machines with less than ~25 registers, that won't really work, + * and at least gcc will make an unholy mess of it. + * + * So to avoid that mess which just slows things down, we force + * the stores to memory to actually happen (we might be better off + * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as + * suggested by Artur Skawina - that will also make gcc unable to + * try to do the silly "optimize away loads" part because it won't + * see what the value will be). + * + * Ben Herrenschmidt reports that on PPC, the C version comes close + * to the optimized asm with this (ie on PPC you don't want that + * 'volatile', since there are lots of registers). + */ +#ifdef SMALL_REGISTER_SET + #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) +#else + #define setW(x, val) (W(x) = (val)) +#endif /* * Where do we get the source from? The first 16 iterations get it from From 75f492ace708afb4603b922a99ced318d980b8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20N=C4=9Bmec?= Date: Tue, 11 Aug 2009 02:52:07 +0200 Subject: [PATCH 113/133] Fix typos in git-remote.txt and git-symbolic-ref.txt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Štěpán Němec Signed-off-by: Junio C Hamano --- Documentation/git-remote.txt | 4 ++-- Documentation/git-symbolic-ref.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 9e2b4eaa38..82a3d29673 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -114,14 +114,14 @@ These stale branches have already been removed from the remote repository referenced by , but are still locally available in "remotes/". + -With `--dry-run` option, report what branches will be pruned, but do no +With `--dry-run` option, report what branches will be pruned, but do not actually prune them. 'update':: 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 +the configuration parameter remotes.default will be used; if remotes.default is not defined, all remotes which do not have the configuration parameter remote..skipDefaultUpdate set to true will be updated. (See linkgit:git-config[1]). diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt index 210fde03a1..6392538807 100644 --- a/Documentation/git-symbolic-ref.txt +++ b/Documentation/git-symbolic-ref.txt @@ -14,9 +14,9 @@ DESCRIPTION Given one argument, reads which branch head the given symbolic ref refers to and outputs its path, relative to the `.git/` directory. Typically you would give `HEAD` as the -argument to see on which branch your working tree is on. +argument to see which branch your working tree is on. -Give two arguments, create or update a symbolic ref to +Given two arguments, creates or updates a symbolic ref to point at the given branch . A symbolic ref is a regular file that stores a string that From 30ba0de726d92ccfc93009eb60f2c30b0886f61b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 12 Aug 2009 15:45:48 -0400 Subject: [PATCH 114/133] block-sha1: move code around Move the code around so specific architecture hacks are defined first. Also make one line comments actually one line. No code change. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 129 +++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 69 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 304cd0452d..c3f1ae59b9 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -9,74 +9,6 @@ #include "sha1.h" -/* Hash one 64-byte block of data */ -static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data); - -void blk_SHA1_Init(blk_SHA_CTX *ctx) -{ - ctx->size = 0; - - /* Initialize H with the magic constants (see FIPS180 for constants) - */ - ctx->H[0] = 0x67452301; - ctx->H[1] = 0xefcdab89; - ctx->H[2] = 0x98badcfe; - ctx->H[3] = 0x10325476; - ctx->H[4] = 0xc3d2e1f0; -} - - -void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) -{ - int lenW = ctx->size & 63; - - ctx->size += len; - - /* Read the data into W and process blocks as they get full - */ - if (lenW) { - int left = 64 - lenW; - if (len < left) - left = len; - memcpy(lenW + (char *)ctx->W, data, left); - lenW = (lenW + left) & 63; - len -= left; - data += left; - if (lenW) - return; - blk_SHA1Block(ctx, ctx->W); - } - while (len >= 64) { - blk_SHA1Block(ctx, data); - data += 64; - len -= 64; - } - if (len) - memcpy(ctx->W, data, len); -} - - -void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) -{ - static const unsigned char pad[64] = { 0x80 }; - unsigned int padlen[2]; - int i; - - /* Pad with a binary 1 (ie 0x80), then zeroes, then length - */ - padlen[0] = htonl(ctx->size >> 29); - padlen[1] = htonl(ctx->size << 3); - - i = ctx->size & 63; - blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i))); - blk_SHA1_Update(ctx, padlen, 8); - - /* Output hash - */ - for (i = 0; i < 5; i++) - ((unsigned int *)hashout)[i] = htonl(ctx->H[i]); -} - #if defined(__i386__) || defined(__x86_64__) #define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) @@ -136,7 +68,7 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) #define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) #define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) -static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) +static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data) { unsigned int A,B,C,D,E; unsigned int array[16]; @@ -243,3 +175,62 @@ static void blk_SHA1Block(blk_SHA_CTX *ctx, const unsigned int *data) ctx->H[3] += D; ctx->H[4] += E; } + +void blk_SHA1_Init(blk_SHA_CTX *ctx) +{ + ctx->size = 0; + + /* Initialize H with the magic constants (see FIPS180 for constants) */ + ctx->H[0] = 0x67452301; + ctx->H[1] = 0xefcdab89; + ctx->H[2] = 0x98badcfe; + ctx->H[3] = 0x10325476; + ctx->H[4] = 0xc3d2e1f0; +} + +void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) +{ + int lenW = ctx->size & 63; + + ctx->size += len; + + /* Read the data into W and process blocks as they get full */ + if (lenW) { + int left = 64 - lenW; + if (len < left) + left = len; + memcpy(lenW + (char *)ctx->W, data, left); + lenW = (lenW + left) & 63; + len -= left; + data += left; + if (lenW) + return; + blk_SHA1_Block(ctx, ctx->W); + } + while (len >= 64) { + blk_SHA1_Block(ctx, data); + data += 64; + len -= 64; + } + if (len) + memcpy(ctx->W, data, len); +} + +void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) +{ + static const unsigned char pad[64] = { 0x80 }; + unsigned int padlen[2]; + int i; + + /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ + padlen[0] = htonl(ctx->size >> 29); + padlen[1] = htonl(ctx->size << 3); + + i = ctx->size & 63; + blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i))); + blk_SHA1_Update(ctx, padlen, 8); + + /* Output hash */ + for (i = 0; i < 5; i++) + ((unsigned int *)hashout)[i] = htonl(ctx->H[i]); +} From dc52fd29738c2af98f3e986691eca34addfd4914 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 12 Aug 2009 15:46:41 -0400 Subject: [PATCH 115/133] block-sha1: split the different "hacks" to be individually selected This is to make it easier for them to be selected individually depending on the architecture instead of the other way around i.e. having each architecture select a list of hacks up front. That makes for clearer documentation as well. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index c3f1ae59b9..67c9bd0723 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -11,10 +11,16 @@ #if defined(__i386__) || defined(__x86_64__) +/* + * Force usage of rol or ror by selecting the one with the smaller constant. + * It _can_ generate slightly smaller code (a constant of 1 is special), but + * perhaps more importantly it's possibly faster on any uarch that does a + * rotate with a loop. + */ + #define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) #define SHA_ROL(x,n) SHA_ASM("rol", x, n) #define SHA_ROR(x,n) SHA_ASM("ror", x, n) -#define SMALL_REGISTER_SET #else @@ -24,9 +30,6 @@ #endif -/* This "rolls" over the 512-bit array */ -#define W(x) (array[(x)&15]) - /* * If you have 32 registers or more, the compiler can (and should) * try to change the array[] accesses into registers. However, on @@ -43,13 +46,23 @@ * Ben Herrenschmidt reports that on PPC, the C version comes close * to the optimized asm with this (ie on PPC you don't want that * 'volatile', since there are lots of registers). + * + * On ARM we get the best code generation by forcing a full memory barrier + * between each SHA_ROUND, otherwise gcc happily get wild with spilling and + * the stack frame size simply explode and performance goes down the drain. */ -#ifdef SMALL_REGISTER_SET + +#if defined(__i386__) || defined(__x86_64__) #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) +#elif defined(__arm__) + #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) #else #define setW(x, val) (W(x) = (val)) #endif +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) + /* * Where do we get the source from? The first 16 iterations get it from * the input data, the next mix it from the 512-bit array. From 660231aa9727d29c7d2c16319bc6a3fa8bed3e0e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 12 Aug 2009 15:47:55 -0400 Subject: [PATCH 116/133] block-sha1: support for architectures with memory alignment restrictions This is needed on architectures with poor or non-existent unaligned memory support and/or no fast byte swap instruction (such as ARM) by using byte accesses to memory and shifting the result together. This also makes the code portable, therefore the byte access methods are the defaults. Any architecture that properly supports unaligned word accesses in hardware simply has to enable the alternative methods. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 67c9bd0723..d3121f7a02 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -60,6 +60,34 @@ #define setW(x, val) (W(x) = (val)) #endif +/* + * Performance might be improved if the CPU architecture is OK with + * unaligned 32-bit loads and a fast ntohl() is available. + * Otherwise fall back to byte loads and shifts which is portable, + * and is faster on architectures with memory alignment issues. + */ + +#if defined(__i386__) || defined(__x86_64__) + +#define get_be32(p) ntohl(*(unsigned int *)(p)) +#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) + +#else + +#define get_be32(p) ( \ + (*((unsigned char *)(p) + 0) << 24) | \ + (*((unsigned char *)(p) + 1) << 16) | \ + (*((unsigned char *)(p) + 2) << 8) | \ + (*((unsigned char *)(p) + 3) << 0) ) +#define put_be32(p, v) do { \ + unsigned int __v = (v); \ + *((unsigned char *)(p) + 0) = __v >> 24; \ + *((unsigned char *)(p) + 1) = __v >> 16; \ + *((unsigned char *)(p) + 2) = __v >> 8; \ + *((unsigned char *)(p) + 3) = __v >> 0; } while (0) + +#endif + /* This "rolls" over the 512-bit array */ #define W(x) (array[(x)&15]) @@ -67,7 +95,7 @@ * Where do we get the source from? The first 16 iterations get it from * the input data, the next mix it from the 512-bit array. */ -#define SHA_SRC(t) htonl(data[t]) +#define SHA_SRC(t) get_be32(data + t) #define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) #define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ @@ -245,5 +273,5 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) /* Output hash */ for (i = 0; i < 5; i++) - ((unsigned int *)hashout)[i] = htonl(ctx->H[i]); + put_be32(hashout + i*4, ctx->H[i]); } From 19a7fcbf16276321f83e0c1748b3935a2ea42675 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 11 Aug 2009 23:27:40 -0400 Subject: [PATCH 117/133] allow pull --rebase on branch yet to be born When doing a "pull --rebase", we check to make sure that the index and working tree are clean. The index-clean check compares the index against HEAD. The test erroneously reports dirtiness if we don't have a HEAD yet. In such an "unborn branch" case, by definition, a non-empty index won't be based on whatever we are pulling down from the remote, and will lose the local change. Just check if $GIT_DIR/index exists and error out. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-pull.sh | 18 +++++++++++++----- t/t5520-pull.sh | 11 +++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/git-pull.sh b/git-pull.sh index 0f24182974..0bbd5bf7df 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -119,11 +119,19 @@ error_on_no_merge_candidates () { } test true = "$rebase" && { - git update-index --ignore-submodules --refresh && - git diff-files --ignore-submodules --quiet && - git diff-index --ignore-submodules --cached --quiet HEAD -- || - die "refusing to pull with rebase: your working tree is not up-to-date" - + if ! git rev-parse -q --verify HEAD >/dev/null + then + # On an unborn branch + if test -f "$GIT_DIR/index" + then + die "updating an unborn branch with changes added to the index" + fi + else + git update-index --ignore-submodules --refresh && + git diff-files --ignore-submodules --quiet && + git diff-index --ignore-submodules --cached --quiet HEAD -- || + die "refusing to pull with rebase: your working tree is not up-to-date" + fi oldremoteref= && . git-parse-remote && remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" && diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index e78d40242a..dd2ee842e0 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -149,4 +149,15 @@ test_expect_success 'pull --rebase dies early with dirty working directory' ' ' +test_expect_success 'pull --rebase works on branch yet to be born' ' + git rev-parse master >expect && + mkdir empty_repo && + (cd empty_repo && + git init && + git pull --rebase .. master && + git rev-parse HEAD >../actual + ) && + test_cmp expect actual +' + test_done From 0b74f5dc3a27a058cd5dfe45b5ada4d2853dc447 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 11 Aug 2009 12:10:21 +0200 Subject: [PATCH 118/133] help.c: give correct structure's size to memset() These two structures are of the same type, but we'd better be consistent. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- help.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help.c b/help.c index fd87bb5aee..1203c7291e 100644 --- a/help.c +++ b/help.c @@ -302,7 +302,7 @@ const char *help_unknown_cmd(const char *cmd) struct cmdnames main_cmds, other_cmds; memset(&main_cmds, 0, sizeof(main_cmds)); - memset(&other_cmds, 0, sizeof(main_cmds)); + memset(&other_cmds, 0, sizeof(other_cmds)); memset(&aliases, 0, sizeof(aliases)); git_config(git_unknown_cmd_config, NULL); From 22f1fb66be859fb5ab1620a7dce8e54702d4a07c Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 11 Aug 2009 15:03:58 +0200 Subject: [PATCH 119/133] Documentation: merge: one is required merge only requires one , so "..." should be used in the synopsis (and not " ..."). Signed-off-by: Paul Bolle Signed-off-by: Junio C Hamano --- Documentation/git-merge.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index c04ae739ed..af68d694a0 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git merge' [-n] [--stat] [--no-commit] [--squash] [-s ]... - [-m ] ... + [-m ] ... 'git merge' HEAD ... DESCRIPTION From 57f6ec029090f64377ec5c0926b6e2e39b0caa4f Mon Sep 17 00:00:00 2001 From: Ori Avtalion Date: Fri, 7 Aug 2009 17:24:21 +0300 Subject: [PATCH 120/133] Change mentions of "git programs" to "git commands" Most of the docs and printouts refer to "commands" when discussing what the end users call via the "git" top-level program. We should refer them as "git programs" when we discuss the fact that the commands are implemented as separate programs, but in other contexts, it is better to use the term "git commands" consistently. Signed-off-by: Ori Avtalion Signed-off-by: Nanako Shiraishi Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++-- Documentation/fetch-options.txt | 2 +- Documentation/git-rev-list.txt | 2 +- Documentation/git.txt | 2 +- Documentation/gitattributes.txt | 2 +- Documentation/gitcore-tutorial.txt | 4 ++-- Documentation/user-manual.txt | 2 +- help.c | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index ae0e5db50c..181c2f9bde 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -604,7 +604,7 @@ color.interactive.:: Use customized color for 'git-add --interactive' output. `` may be `prompt`, `header`, `help` or `error`, for four distinct types of normal output from interactive - programs. The values of these variables may be specified as + commands. The values of these variables may be specified as in color.branch.. color.pager:: @@ -1100,7 +1100,7 @@ instaweb.port:: linkgit:git-instaweb[1]. interactive.singlekey:: - In interactive programs, allow the user to provide one-letter + In interactive commands, allow the user to provide one-letter input with a single key (i.e., without hitting enter). Currently this is used only by the `\--patch` mode of linkgit:git-add[1]. Note that this setting is silently diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index d313795fdb..ea3b1bc19f 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -1,7 +1,7 @@ -q:: --quiet:: Pass --quiet to git-fetch-pack and silence any other internally - used programs. + used git commands. -v:: --verbose:: diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 1c9cc28895..ae1186e340 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -83,7 +83,7 @@ between the two operands. The following two commands are equivalent: $ git rev-list A...B ----------------------------------------------------------------------- -'git-rev-list' is a very essential git program, since it +'rev-list' is a very essential git command, since it provides the ability to build and traverse commit ancestry graphs. For this reason, it has a lot of different options that enables it to be used by commands as different as 'git-bisect' and diff --git a/Documentation/git.txt b/Documentation/git.txt index 3589a12e49..8fbe187fb3 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -315,7 +315,7 @@ Synching repositories include::cmds-synchingrepositories.txt[] -The following are helper programs used by the above; end users +The following are helper commands used by the above; end users typically do not use them directly. include::cmds-synchelpers.txt[] diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index aaa073efc8..1195e83b6e 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -404,7 +404,7 @@ Performing a three-way merge The attribute `merge` affects how three versions of a file is merged when a file-level merge is necessary during `git merge`, -and other programs such as `git revert` and `git cherry-pick`. +and other commands such as `git revert` and `git cherry-pick`. Set:: diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt index 7ba5e589d7..b3640c4e64 100644 --- a/Documentation/gitcore-tutorial.txt +++ b/Documentation/gitcore-tutorial.txt @@ -12,7 +12,7 @@ git * DESCRIPTION ----------- -This tutorial explains how to use the "core" git programs to set up and +This tutorial explains how to use the "core" git commands to set up and work with a git repository. If you just need to use git as a revision control system you may prefer @@ -1328,7 +1328,7 @@ into it later. Obviously, this repository creation needs to be done only once. [NOTE] -'git-push' uses a pair of programs, +'git-push' uses a pair of commands, 'git-send-pack' on your local machine, and 'git-receive-pack' on the remote machine. The communication between the two over the network internally uses an SSH connection. diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 0b88a51d0b..67ebffa568 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -4131,7 +4131,7 @@ What does this mean? `git rev-list` is the original version of the revision walker, which _always_ printed a list of revisions to stdout. It is still functional, -and needs to, since most new Git programs start out as scripts using +and needs to, since most new Git commands start out as scripts using `git rev-list`. `git rev-parse` is not as important any more; it was only used to filter out diff --git a/help.c b/help.c index 1203c7291e..da0cca0a15 100644 --- a/help.c +++ b/help.c @@ -334,7 +334,7 @@ const char *help_unknown_cmd(const char *cmd) const char *assumed = main_cmds.names[0]->name; main_cmds.names[0] = NULL; clean_cmdnames(&main_cmds); - fprintf(stderr, "WARNING: You called a Git program named '%s', " + fprintf(stderr, "WARNING: You called a Git command named '%s', " "which does not exist.\n" "Continuing under the assumption that you meant '%s'\n", cmd, assumed); From e89df7dcda9836518a68cce27231625f90d94749 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 11 Aug 2009 15:03:10 +0200 Subject: [PATCH 121/133] Documentation: add: ... is optional ... is optional (e.g. when the --all or --update options are used) so use square brackets in the synopsis. Signed-off-by: Paul Bolle Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index ab1943c712..e67b7e875e 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N] - [--refresh] [--ignore-errors] [--] ... + [--refresh] [--ignore-errors] [--] [...] DESCRIPTION ----------- From 07436e43daf1a97d3d702090d6289f745bd5ad90 Mon Sep 17 00:00:00 2001 From: Matthieu Moy Date: Sat, 8 Aug 2009 09:51:08 +0200 Subject: [PATCH 122/133] push: point to 'git pull' and 'git push --force' in case of non-fast forward 'git push' failing because of non-fast forward is a very common situation, and a beginner does not necessarily understand "fast forward" immediately. Add a new section to the git-push documentation and refer them to it. Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano Signed-off-by: Nanako Shiraishi Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 86 ++++++++++++++++++++++++++++++++++++++ builtin-push.c | 9 +++- transport.c | 10 +++-- transport.h | 3 +- 4 files changed, 103 insertions(+), 5 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 2653388fd8..58d2bd5d4a 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -195,6 +195,92 @@ reason:: refs, no explanation is needed. For a failed ref, the reason for failure is described. +Note about fast-forwards +------------------------ + +When an update changes a branch (or more in general, a ref) that used to +point at commit A to point at another commit B, it is called a +fast-forward update if and only if B is a descendant of A. + +In a fast-forward update from A to B, the set of commits that the original +commit A built on top of is a subset of the commits the new commit B +builds on top of. Hence, it does not lose any history. + +In contrast, a non-fast-forward update will lose history. For example, +suppose you and somebody else started at the same commit X, and you built +a history leading to commit B while the other person built a history +leading to commit A. The history looks like this: + +---------------- + + B + / + ---X---A + +---------------- + +Further suppose that the other person already pushed changes leading to A +back to the original repository you two obtained the original commit X. + +The push done by the other person updated the branch that used to point at +commit X to point at commit A. It is a fast-forward. + +But if you try to push, you will attempt to update the branch (that +now points at A) with commit B. This does _not_ fast-forward. If you did +so, the changes introduced by commit A will be lost, because everybody +will now start building on top of B. + +The command by default does not allow an update that is not a fast-forward +to prevent such loss of history. + +If you do not want to lose your work (history from X to B) nor the work by +the other person (history from X to A), you would need to first fetch the +history from the repository, create a history that contains changes done +by both parties, and push the result back. + +You can perform "git pull", resolve potential conflicts, and "git push" +the result. A "git pull" will create a merge commit C between commits A +and B. + +---------------- + + B---C + / / + ---X---A + +---------------- + +Updating A with the resulting merge commit will fast-forward and your +push will be accepted. + +Alternatively, you can rebase your change between X and B on top of A, +with "git pull --rebase", and push the result back. The rebase will +create a new commit D that builds the change between X and B on top of +A. + +---------------- + + B D + / / + ---X---A + +---------------- + +Again, updating A with this commit will fast-forward and your push will be +accepted. + +There is another common situation where you may encounter non-fast-forward +rejection when you try to push, and it is possible even when you are +pushing into a repository nobody else pushes into. After you push commit +A yourself (in the first picture in this section), replace it with "git +commit --amend" to produce commit B, and you try to push it out, because +forgot that you have pushed A out already. In such a case, and only if +you are certain that nobody in the meantime fetched your earlier commit A +(and started building on top of it), you can run "git push --force" to +overwrite it. In other words, "git push --force" is a method reserved for +a case where you do mean to lose history. + + Examples -------- diff --git a/builtin-push.c b/builtin-push.c index 1d92e22f0a..50328f4b08 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -140,6 +140,7 @@ static int do_push(const char *repo, int flags) struct transport *transport = transport_get(remote, url[i]); int err; + int nonfastforward; if (receivepack) transport_set_option(transport, TRANS_OPT_RECEIVEPACK, receivepack); @@ -148,13 +149,19 @@ static int do_push(const char *repo, int flags) if (flags & TRANSPORT_PUSH_VERBOSE) fprintf(stderr, "Pushing to %s\n", url[i]); - err = transport_push(transport, refspec_nr, refspec, flags); + err = transport_push(transport, refspec_nr, refspec, flags, + &nonfastforward); err |= transport_disconnect(transport); if (!err) continue; error("failed to push some refs to '%s'", url[i]); + if (nonfastforward) { + printf("To prevent you from losing history, non-fast-forward updates were rejected.\n" + "Merge the remote changes before pushing again.\n" + "See 'non-fast forward' section of 'git push --help' for details.\n"); + } errs++; } return !!errs; diff --git a/transport.c b/transport.c index de0d5874a3..f231b355f2 100644 --- a/transport.c +++ b/transport.c @@ -820,7 +820,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i } static void print_push_status(const char *dest, struct ref *refs, - int verbose, int porcelain) + int verbose, int porcelain, int * nonfastforward) { struct ref *ref; int n = 0; @@ -835,11 +835,14 @@ static void print_push_status(const char *dest, struct ref *refs, if (ref->status == REF_STATUS_OK) n += print_one_push_status(ref, dest, n, porcelain); + *nonfastforward = 0; for (ref = refs; ref; ref = ref->next) { if (ref->status != REF_STATUS_NONE && ref->status != REF_STATUS_UPTODATE && ref->status != REF_STATUS_OK) n += print_one_push_status(ref, dest, n, porcelain); + if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) + *nonfastforward = 1; } } @@ -997,7 +1000,8 @@ int transport_set_option(struct transport *transport, } int transport_push(struct transport *transport, - int refspec_nr, const char **refspec, int flags) + int refspec_nr, const char **refspec, int flags, + int * nonfastforward) { verify_remote_names(refspec_nr, refspec); @@ -1024,7 +1028,7 @@ int transport_push(struct transport *transport, ret = transport->push_refs(transport, remote_refs, flags); - print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain); + print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, nonfastforward); if (!(flags & TRANSPORT_PUSH_DRY_RUN)) { struct ref *ref; diff --git a/transport.h b/transport.h index 51b539778c..639f13dcfe 100644 --- a/transport.h +++ b/transport.h @@ -68,7 +68,8 @@ int transport_set_option(struct transport *transport, const char *name, const char *value); int transport_push(struct transport *connection, - int refspec_nr, const char **refspec, int flags); + int refspec_nr, const char **refspec, int flags, + int * nonfastforward); const struct ref *transport_get_remote_refs(struct transport *transport); From b186a261b1f7ec1fbda8c5f6d84595f3a7716d92 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 12 Aug 2009 16:01:59 -0700 Subject: [PATCH 123/133] svn: initial "master" points to trunk if possible Since "trunk" is a convention for the main development branch in the SVN world, try to make that the master branch upon initial checkout if it exists. This is probably less surprising based on user requests. t9135 was the only test which relied on the previous behavior and thus needed to be modified. Signed-off-by: Eric Wong --- git-svn.perl | 11 ++++++++++ t/t9135-git-svn-moved-branch-empty-file.sh | 7 +++++- t/t9145-git-svn-master-branch.sh | 25 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100755 t/t9145-git-svn-master-branch.sh diff --git a/git-svn.perl b/git-svn.perl index b0bfb74792..fad29605db 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1156,6 +1156,17 @@ sub post_fetch_checkout { my $gs = $Git::SVN::_head or return; return if verify_ref('refs/heads/master^0'); + # look for "trunk" ref if it exists + my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}}; + my $fetch = $remote->{fetch}; + if ($fetch) { + foreach my $p (keys %$fetch) { + basename($fetch->{$p}) eq 'trunk' or next; + $gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p); + last; + } + } + my $valid_head = verify_ref('HEAD^0'); command_noisy(qw(update-ref refs/heads/master), $gs->refname); return if ($valid_head || !verify_ref('HEAD^0')); diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh index 03705fa4ce..5280e5f1e4 100755 --- a/t/t9135-git-svn-moved-branch-empty-file.sh +++ b/t/t9135-git-svn-moved-branch-empty-file.sh @@ -10,7 +10,12 @@ test_expect_success 'load svn dumpfile' ' test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x' test_expect_success 'test that b1 exists and is empty' ' - (cd x && test -f b1 && ! test -s b1) + ( + cd x && + git reset --hard branch-c && + test -f b1 && + ! test -s b1 + ) ' test_done diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh new file mode 100755 index 0000000000..16852d26ae --- /dev/null +++ b/t/t9145-git-svn-master-branch.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong +# +test_description='git svn initial master branch is "trunk" if possible' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository' ' + mkdir i && + > i/a && + svn_cmd import -m trunk i "$svnrepo/trunk" && + svn_cmd import -m b/a i "$svnrepo/branches/a" && + svn_cmd import -m b/b i "$svnrepo/branches/b" +' + +test_expect_success 'git svn clone --stdlayout sets up trunk as master' ' + git svn clone -s "$svnrepo" g && + ( + cd g && + test x`git rev-parse --verify refs/remotes/trunk^0` = \ + x`git rev-parse --verify refs/heads/master^0` + ) +' + +test_done From 6f5748e14cc5bb0a836b649fb8e2d6a5eb166f1d Mon Sep 17 00:00:00 2001 From: Adam Brewster Date: Tue, 11 Aug 2009 23:14:27 -0400 Subject: [PATCH 124/133] svn: allow branches outside of refs/remotes It may be convenient for some users to store svn remote tracking branches outside of the refs/remotes/ heirarchy. To accomplish this feat, this patch includes the entire path to the ref in $r->{'refname'} in &read_all_remotes and tries to change references to this entry so the new value makes sense. [ew: fixed backwards compatibility, long lines] Signed-off-by: Adam Brewster Signed-off-by: Eric Wong --- git-svn.perl | 83 ++++++++++++++++++-------------- t/lib-git-svn.sh | 2 +- t/t9104-git-svn-follow-parent.sh | 10 ++-- t/t9107-git-svn-migrate.sh | 14 +++--- t/t9143-git-svn-gc.sh | 10 ++-- t/t9144-git-svn-old-rev_map.sh | 31 ++++++++++++ 6 files changed, 96 insertions(+), 54 deletions(-) create mode 100755 t/t9144-git-svn-old-rev_map.sh diff --git a/git-svn.perl b/git-svn.perl index fad29605db..a2934e8651 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -909,7 +909,7 @@ sub cmd_multi_init { } do_git_init_db(); if (defined $_trunk) { - my $trunk_ref = $_prefix . 'trunk'; + my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk'; # try both old-style and new-style lookups: my $gs_trunk = eval { Git::SVN->new($trunk_ref) }; unless ($gs_trunk) { @@ -1654,23 +1654,23 @@ sub resolve_local_globs { return unless defined $glob_spec; my $ref = $glob_spec->{ref}; my $path = $glob_spec->{path}; - foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) { - next unless m#^refs/remotes/$ref->{regex}$#; + foreach (command(qw#for-each-ref --format=%(refname) refs/#)) { + next unless m#^$ref->{regex}$#; my $p = $1; my $pathname = desanitize_refname($path->full_path($p)); my $refname = desanitize_refname($ref->full_path($p)); if (my $existing = $fetch->{$pathname}) { if ($existing ne $refname) { die "Refspec conflict:\n", - "existing: refs/remotes/$existing\n", - " globbed: refs/remotes/$refname\n"; + "existing: $existing\n", + " globbed: $refname\n"; } - my $u = (::cmt_metadata("refs/remotes/$refname"))[0]; + my $u = (::cmt_metadata("$refname"))[0]; $u =~ s!^\Q$url\E(/|$)!! or die - "refs/remotes/$refname: '$url' not found in '$u'\n"; + "$refname: '$url' not found in '$u'\n"; if ($pathname ne $u) { warn "W: Refspec glob conflict ", - "(ref: refs/remotes/$refname):\n", + "(ref: $refname):\n", "expected path: $pathname\n", " real path: $u\n", "Continuing ahead with $u\n"; @@ -1748,33 +1748,35 @@ sub read_all_remotes { my $use_svm_props = eval { command_oneline(qw/config --bool svn.useSvmProps/) }; $use_svm_props = $use_svm_props eq 'true' if $use_svm_props; + my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*}; foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) { - if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) { - my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3); - die("svn-remote.$remote: remote ref '$_remote_ref' " - . "must start with 'refs/remotes/'\n") - unless $_remote_ref =~ m{^refs/remotes/(.+)}; - my $remote_ref = $1; - $local_ref =~ s{^/}{}; + if (m!^(.+)\.fetch=$svn_refspec$!) { + my ($remote, $local_ref, $remote_ref) = ($1, $2, $3); + die("svn-remote.$remote: remote ref '$remote_ref' " + . "must start with 'refs/'\n") + unless $remote_ref =~ m{^refs/}; $r->{$remote}->{fetch}->{$local_ref} = $remote_ref; $r->{$remote}->{svm} = {} if $use_svm_props; } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) { $r->{$1}->{svm} = {}; } elsif (m!^(.+)\.url=\s*(.*)\s*$!) { $r->{$1}->{url} = $2; - } elsif (m!^(.+)\.(branches|tags)= - (.*):refs/remotes/(.+)\s*$/!x) { - my ($p, $g) = ($3, $4); + } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) { + my ($remote, $t, $local_ref, $remote_ref) = + ($1, $2, $3, $4); + die("svn-remote.$remote: remote ref '$remote_ref' ($t) " + . "must start with 'refs/'\n") + unless $remote_ref =~ m{^refs/}; my $rs = { - t => $2, - remote => $1, - path => Git::SVN::GlobSpec->new($p), - ref => Git::SVN::GlobSpec->new($g) }; + t => $t, + remote => $remote, + path => Git::SVN::GlobSpec->new($local_ref), + ref => Git::SVN::GlobSpec->new($remote_ref) }; if (length($rs->{ref}->{right}) != 0) { die "The '*' glob character must be the last ", - "character of '$g'\n"; + "character of '$remote_ref'\n"; } - push @{ $r->{$1}->{$2} }, $rs; + push @{ $r->{$remote}->{$t} }, $rs; } } @@ -1882,9 +1884,9 @@ sub init_remote_config { } } my ($xrepo_id, $xpath) = find_ref($self->refname); - if (defined $xpath) { + if (!$no_write && defined $xpath) { die "svn-remote.$xrepo_id.fetch already set to track ", - "$xpath:refs/remotes/", $self->refname, "\n"; + "$xpath:", $self->refname, "\n"; } unless ($no_write) { command_noisy('config', @@ -1959,7 +1961,7 @@ sub find_ref { my ($ref_id) = @_; foreach (command(qw/config -l/)) { next unless m!^svn-remote\.(.+)\.fetch= - \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x; + \s*/?(.*?)\s*:\s*(.+?)\s*$!x; my ($repo_id, $path, $ref) = ($1, $2, $3); if ($ref eq $ref_id) { $path = '' if ($path =~ m#^\./?#); @@ -1976,16 +1978,16 @@ sub new { if (!defined $repo_id) { die "Could not find a \"svn-remote.*.fetch\" key ", "in the repository configuration matching: ", - "refs/remotes/$ref_id\n"; + "$ref_id\n"; } } my $self = _new($class, $repo_id, $ref_id, $path); if (!defined $self->{path} || !length $self->{path}) { my $fetch = command_oneline('config', '--get', "svn-remote.$repo_id.fetch", - ":refs/remotes/$ref_id\$") or + ":$ref_id\$") or die "Failed to read \"svn-remote.$repo_id.fetch\" ", - "\":refs/remotes/$ref_id\$\" in config\n"; + "\":$ref_id\$\" in config\n"; ($self->{path}, undef) = split(/\s*:\s*/, $fetch); } $self->{url} = command_oneline('config', '--get', @@ -1996,7 +1998,7 @@ sub new { } sub refname { - my ($refname) = "refs/remotes/$_[0]->{ref_id}" ; + my ($refname) = $_[0]->{ref_id} ; # It cannot end with a slash /, we'll throw up on this because # SVN can't have directories with a slash in their name, either: @@ -3331,12 +3333,23 @@ sub _new { } unless (defined $ref_id && length $ref_id) { $_prefix = '' unless defined($_prefix); - $_[2] = $ref_id = $_prefix . $Git::SVN::default_ref_id; + $_[2] = $ref_id = + "refs/remotes/$_prefix$Git::SVN::default_ref_id"; } $_[1] = $repo_id; my $dir = "$ENV{GIT_DIR}/svn/$ref_id"; + + # Older repos imported by us used $GIT_DIR/svn/foo instead of + # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo + if ($ref_id =~ m{^refs/remotes/(.*)}) { + my $old_dir = "$ENV{GIT_DIR}/svn/$1"; + if (-d $old_dir && ! -d $dir) { + $dir = $old_dir; + } + } + $_[3] = $path = '' unless (defined $path); - mkpath(["$ENV{GIT_DIR}/svn"]); + mkpath([$dir]); bless { ref_id => $ref_id, dir => $dir, index => "$dir/index", path => $path, config => "$ENV{GIT_DIR}/svn/config", @@ -5509,7 +5522,7 @@ sub minimize_connections { my $pfx = "svn-remote.$x->{old_repo_id}"; my $old_fetch = quotemeta("$x->{old_path}:". - "refs/remotes/$x->{ref_id}"); + "$x->{ref_id}"); command_noisy(qw/config --unset/, "$pfx.fetch", '^'. $old_fetch . '$'); delete $r->{$x->{old_repo_id}}-> @@ -5578,7 +5591,7 @@ sub new { my ($class, $glob) = @_; my $re = $glob; $re =~ s!/+$!!g; # no need for trailing slashes - $re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!; + $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!; my $temp = $re; my ($left, $right) = ($1, $3); $re = $2; diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 5654962343..fd8631f906 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -14,7 +14,7 @@ if ! test_have_prereq PERL; then fi GIT_DIR=$PWD/.git -GIT_SVN_DIR=$GIT_DIR/svn/git-svn +GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn SVN_TREE=$GIT_SVN_DIR/svn-tree svn >/dev/null 2>&1 diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index 78610b61e6..bbfd7f4793 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -172,11 +172,11 @@ test_expect_success "follow-parent is atomic" ' git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 && git update-ref -d refs/remotes/stunk && git config --unset svn-remote.svn.fetch stunk && - mkdir -p "$GIT_DIR"/svn/flunk@18 && - rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) && - dd if="$GIT_DIR"/svn/stunk/$rev_map \ - of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 && - rm -rf "$GIT_DIR"/svn/stunk && + mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 && + rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) && + dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \ + of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 && + rm -rf "$GIT_DIR"/svn/refs/remotes/stunk && git svn init --minimize-url -i flunk "$svnrepo"/flunk && git svn fetch -i flunk && git svn init --minimize-url -i stunk "$svnrepo"/stunk && diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index c0098d9808..901b8e09fb 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -16,9 +16,7 @@ test_expect_success 'setup old-looking metadata' ' cd .. && git svn init "$svnrepo" && git svn fetch && - mv "$GIT_DIR"/svn/* "$GIT_DIR"/ && - mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ && - rmdir "$GIT_DIR"/svn && + rm -rf "$GIT_DIR"/svn && git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} && git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} && git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn} @@ -87,7 +85,7 @@ test_expect_success 'migrate --minimize on old inited layout' ' rm -rf "$GIT_DIR"/svn && for i in `cat fetch.out`; do path=`expr $i : "\([^:]*\):.*$"` - ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"` + ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"` if test -z "$ref"; then continue; fi if test -n "$path"; then path="/$path"; fi ( mkdir -p "$GIT_DIR"/svn/$ref/info/ && @@ -107,16 +105,16 @@ test_expect_success 'migrate --minimize on old inited layout' ' test_expect_success ".rev_db auto-converted to .rev_map.UUID" ' git svn fetch -i trunk && - test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && - expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" && + test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" && + expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" && test -n "$expect" && rev_db="$(echo $expect | sed -e "s,_map,_db,")" && convert_to_rev_db "$expect" "$rev_db" && rm -f "$expect" && test -f "$rev_db" && git svn fetch -i trunk && - test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && - test ! -e "$GIT_DIR"/svn/trunk/.rev_db && + test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" && + test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db && test -f "$expect" ' diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh index f2ba2d1da3..99f69c6a0b 100755 --- a/t/t9143-git-svn-gc.sh +++ b/t/t9143-git-svn-gc.sh @@ -28,26 +28,26 @@ test_expect_success 'Setup repo' 'git svn init "$svnrepo"' test_expect_success 'Fetch repo' 'git svn fetch' test_expect_success 'make backup copy of unhandled.log' ' - cp .git/svn/git-svn/unhandled.log tmp + cp .git/svn/refs/remotes/git-svn/unhandled.log tmp ' -test_expect_success 'create leftover index' '> .git/svn/git-svn/index' +test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index' test_expect_success 'git svn gc runs' 'git svn gc' -test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index' +test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index' if perl -MCompress::Zlib -e 0 2>/dev/null then test_expect_success 'git svn gc produces a valid gzip file' ' - gunzip .git/svn/git-svn/unhandled.log.gz + gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz ' else say "Perl Compress::Zlib unavailable, skipping gunzip test" fi test_expect_success 'git svn gc does not change unhandled.log files' ' - test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log + test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log ' test_done diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh new file mode 100755 index 0000000000..7600a35cd4 --- /dev/null +++ b/t/t9144-git-svn-old-rev_map.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong + +test_description='git svn old rev_map preservd' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository with old layout' ' + mkdir i && + (cd i && > a) && + svn_cmd import -m- i "$svnrepo" && + git svn init "$svnrepo" && + git svn fetch && + test -d .git/svn/refs/remotes/git-svn/ && + ! test -e .git/svn/git-svn/ && + mv .git/svn/refs/remotes/git-svn .git/svn/ && + rm -r .git/svn/refs +' + +test_expect_success 'old layout continues to work' ' + svn_cmd import -m- i "$svnrepo/b" && + git svn rebase && + echo a >> b/a && + git add b/a && + git commit -m- -a && + git svn dcommit && + ! test -d .git/svn/refs/ && + test -e .git/svn/git-svn/ +' + +test_done From c83f4e686445c83edbe9c1d3055c0c43921e1ab8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 12 Aug 2009 22:20:02 -0700 Subject: [PATCH 125/133] svn: (cleanup) use predefined constant for rev_map_fmt This makes life easier in case we ever need to change the internal format of the rev_maps. Signed-off-by: Eric Wong --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index a2934e8651..1da9f0781c 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3277,7 +3277,7 @@ sub _rev_map_get { my $i = int(($l/24 + $u/24) / 2) * 24; sysseek($fh, $i, SEEK_SET) or croak "seek: $!"; sysread($fh, my $buf, 24) == 24 or croak "read: $!"; - my ($r, $c) = unpack('NH40', $buf); + my ($r, $c) = unpack(rev_map_fmt, $buf); if ($r < $rev) { $l = $i + 24; From 097e11182213afa4166c0fe58bd8135d71b52fb5 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 21 Jul 2009 10:09:48 +0200 Subject: [PATCH 126/133] gitk: Do not hard-code "encoding" in attribute lookup functions Commit 39ee47e (Clean up file encoding code and add enable/disable option, 2008-10-15) rewrote the attribute lookup functions gitattr and cache_gitattr, but in the process hard-coded the attribute name "encoding" instead of using the functions' parameters. This fixes it. This is not a serious regression because currently all callers look only for "encoding". Further note that this fix assumes that future callers will not pass an attribute name that contains regex special characters. Signed-off-by: Johannes Sixt Signed-off-by: Paul Mackerras --- gitk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index 4604c831fe..8aafb191aa 100755 --- a/gitk +++ b/gitk @@ -10901,7 +10901,7 @@ proc gitattr {path attr default} { } else { set r "unspecified" if {![catch {set line [exec git check-attr $attr -- $path]}]} { - regexp "(.*): encoding: (.*)" $line m f r + regexp "(.*): $attr: (.*)" $line m f r } set path_attr_cache($attr,$path) $r } @@ -10929,7 +10929,7 @@ proc cache_gitattr {attr pathlist} { set newlist [lrange $newlist $lim end] if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} { foreach row [split $rlist "\n"] { - if {[regexp "(.*): encoding: (.*)" $row m path value]} { + if {[regexp "(.*): $attr: (.*)" $row m path value]} { if {[string index $path 0] eq "\""} { set path [encoding convertfrom [lindex $path 0]] } From ffe15297b031814c5cda5d82a6555683c8e80dcc Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 3 Aug 2009 23:53:36 +0200 Subject: [PATCH 127/133] gitk: New option to hide remote refs In repositories with lots of remotes, looking at the history in gitk can be borderline insane with all the red labels for remote refs. Introduce a new option in the preferences that makes gitk ignore remote refs entirely, so they don't take up space in the display. Wished-for-by: Thell Fowler Signed-off-by: Thomas Rast Signed-off-by: Paul Mackerras --- gitk | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/gitk b/gitk index 8aafb191aa..d0ab575d13 100755 --- a/gitk +++ b/gitk @@ -1677,6 +1677,7 @@ proc readrefs {} { global tagids idtags headids idheads tagobjid global otherrefids idotherrefs mainhead mainheadid global selecthead selectheadid + global hideremotes foreach v {tagids idtags headids idheads otherrefids idotherrefs} { catch {unset $v} @@ -1689,7 +1690,7 @@ proc readrefs {} { if {![string match "refs/*" $ref]} continue set name [string range $ref 5 end] if {[string match "remotes/*" $name]} { - if {![string match "*/HEAD" $name]} { + if {![string match "*/HEAD" $name] && !$hideremotes} { set headids($name) $id lappend idheads($id) $name } @@ -2520,6 +2521,7 @@ proc savestuff {w} { global cmitmode wrapcomment datetimeformat limitdiffs global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor global autoselect extdifftool perfile_attrs markbgcolor + global hideremotes if {$stuffsaved} return if {![winfo viewable .]} return @@ -2539,6 +2541,7 @@ proc savestuff {w} { puts $f [list set wrapcomment $wrapcomment] puts $f [list set autoselect $autoselect] puts $f [list set showneartags $showneartags] + puts $f [list set hideremotes $hideremotes] puts $f [list set showlocalchanges $showlocalchanges] puts $f [list set datetimeformat $datetimeformat] puts $f [list set limitdiffs $limitdiffs] @@ -10383,6 +10386,7 @@ proc doprefs {} { global oldprefs prefstop showneartags showlocalchanges global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor global tabstop limitdiffs autoselect extdifftool perfile_attrs + global hideremotes set top .gitkprefs set prefstop $top @@ -10391,7 +10395,7 @@ proc doprefs {} { return } foreach v {maxwidth maxgraphpct showneartags showlocalchanges \ - limitdiffs tabstop perfile_attrs} { + limitdiffs tabstop perfile_attrs hideremotes} { set oldprefs($v) [set $v] } toplevel $top @@ -10423,6 +10427,9 @@ proc doprefs {} { checkbutton $top.ntag -text [mc "Display nearby tags"] \ -font optionfont -variable showneartags grid x $top.ntag -sticky w + checkbutton $top.hideremotes -text [mc "Hide remote refs"] \ + -font optionfont -variable hideremotes + grid x $top.hideremotes -sticky w checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \ -font optionfont -variable limitdiffs grid x $top.ldiff -sticky w @@ -10547,7 +10554,7 @@ proc prefscan {} { global oldprefs prefstop foreach v {maxwidth maxgraphpct showneartags showlocalchanges \ - limitdiffs tabstop perfile_attrs} { + limitdiffs tabstop perfile_attrs hideremotes} { global $v set $v $oldprefs($v) } @@ -10561,6 +10568,7 @@ proc prefsok {} { global oldprefs prefstop showneartags showlocalchanges global fontpref mainfont textfont uifont global limitdiffs treediffs perfile_attrs + global hideremotes catch {destroy $prefstop} unset prefstop @@ -10606,6 +10614,9 @@ proc prefsok {} { $limitdiffs != $oldprefs(limitdiffs)} { reselectline } + if {$hideremotes != $oldprefs(hideremotes)} { + rereadrefs + } } proc formatdate {d} { @@ -11011,6 +11022,7 @@ set mingaplen 100 set cmitmode "patch" set wrapcomment "none" set showneartags 1 +set hideremotes 0 set maxrefs 20 set maxlinelen 200 set showlocalchanges 1 From 2b1fbf90aa9c0fb2a1033793728aebf77c11334d Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 5 Aug 2009 23:15:36 +0200 Subject: [PATCH 128/133] gitk: Fix direction of symmetric difference in optimized mode ee66e08 (gitk: Make updates go faster, 2008-05-09) implemented an optimized mode where gitk parses the arguments with rev-parse, and manually reads history in chunks. As mentioned in the commit message, symmetric differences are a problem there: One wrinkle is that we have to turn symmetric diff arguments (of the form a...b) back into symmetric diff form so that --left-right still works, as git rev parse turns a...b into a b ^merge_base(a,b). However, git-rev-parse returns a...b in the swapped order b a ^merge_base(a,b) This has been the case since at least 1f8115b (the state of master at the time of the abovementioned ee66e08; Merge branch 'maint', 2008-05-08). So gitk flipped the sides of symmetric differences whenever it was in optimized mode. Fix this by swapping the sides of the reconstruction code. Signed-off-by: Thomas Rast Signed-off-by: Paul Mackerras --- gitk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitk b/gitk index d0ab575d13..ef4ba3c984 100755 --- a/gitk +++ b/gitk @@ -288,7 +288,7 @@ proc parseviewrevs {view revs} { if {$sdm != 2} { lappend ret $id } else { - lset ret end [lindex $ret end]...$id + lset ret end $id...[lindex $ret end] } lappend pos $id } From 9bf3acfadafe9e5961296ff22a1677a9f5afe508 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Thu, 13 Aug 2009 09:25:03 +0200 Subject: [PATCH 129/133] gitk: Parse arbitrary commit-ish in SHA1 field We only accepted either SHA1s or heads/tags that have been read. This meant the user could not, e.g., enter HEAD to go back to the current commit. This adds code to call out to git rev-parse --verify if all other methods of interpreting the string the user entered fail. (git-rev-parse alone is not enough as we really want a single revision.) The error paths change slighly, because we now know from the rev-parse invocation whether the expression was valid at all. The previous "unknown" path is now only triggered if the revision does exist, but is not in the current view display. Signed-off-by: Thomas Rast Signed-off-by: Paul Mackerras --- gitk | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index ef4ba3c984..8c08310e6d 100755 --- a/gitk +++ b/gitk @@ -7909,6 +7909,11 @@ proc gotocommit {} { } set id [lindex $matches 0] } + } else { + if {[catch {set id [exec git rev-parse --verify $sha1string]}]} { + error_popup [mc "Revision %s is not known" $sha1string] + return + } } } if {[commitinview $id $curview]} { @@ -7918,7 +7923,7 @@ proc gotocommit {} { if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} { set msg [mc "SHA1 id %s is not known" $sha1string] } else { - set msg [mc "Tag/Head %s is not known" $sha1string] + set msg [mc "Revision %s is not in the current view" $sha1string] } error_popup $msg } From ee7dc310af660f423732369e955651ef2f05011d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 13 Aug 2009 00:29:14 -0400 Subject: [PATCH 130/133] block-sha1: more good unaligned memory access candidates In addition to X86, PowerPC and S390 are capable of unaligned memory accesses. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index d3121f7a02..e5a100754e 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -67,7 +67,10 @@ * and is faster on architectures with memory alignment issues. */ -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) || defined(__x86_64__) || \ + defined(__ppc__) || defined(__ppc64__) || \ + defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__s390__) || defined(__s390x__) #define get_be32(p) ntohl(*(unsigned int *)(p)) #define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) From a12218572f2875e91b6c3c12559b076c4949a675 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 14 Aug 2009 17:52:15 -0500 Subject: [PATCH 131/133] block-sha1/sha1.c: silence compiler complaints by casting void * to char * Some compilers produce errors when arithmetic is attempted on pointers to void. We want computations done on byte addresses, so cast them to char * to work them around. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- block-sha1/sha1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index e5a100754e..464cb258aa 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -246,14 +246,14 @@ void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) memcpy(lenW + (char *)ctx->W, data, left); lenW = (lenW + left) & 63; len -= left; - data += left; + data = ((const char *)data + left); if (lenW) return; blk_SHA1_Block(ctx, ctx->W); } while (len >= 64) { blk_SHA1_Block(ctx, data); - data += 64; + data = ((const char *)data + 64); len -= 64; } if (len) From 67e56eca34e38545bc4fb673e0f13f3e97dc474c Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Mon, 10 Aug 2009 14:09:00 +0200 Subject: [PATCH 132/133] gitweb: Optimize git-favicon.png Reduce size of git-favicon.png using a combination of optipng and pngout. From 164 bytes to 115 bytes (30% reduction). Also reduce git-logo.png's size by one byte using advcomp. Signed-off-by: Benjamin Kramer Signed-off-by: Junio C Hamano --- gitweb/git-favicon.png | Bin 164 -> 115 bytes gitweb/git-logo.png | Bin 208 -> 207 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/gitweb/git-favicon.png b/gitweb/git-favicon.png index de637c0608090162a6ce6b51d5f9bfe512cf8bcf..aae35a70e70351fe6dcb3e905e2e388cf0cb0ac3 100644 GIT binary patch delta 85 zcmZ3&SUf?+pEJNG#Pt9J149GD|NsBH{?u>)*{Yr{jv*Y^lOtGJcy4sCvGS>LGzvuT nGSco!%*slUXkjQ0+{(x>@rZKt$^5c~Kn)C@u6{1-oD!MmdKI;RB2CICA_GnfDX diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png index 16ae8d5382de5ffe63b54139245143513a87446e..f4ede2e944868b9a08401dafeb2b944c7166fd0a 100644 GIT binary patch delta 156 zcmV;N0Av5q0nY)D7zqdi0002!DLE{WAt!%wNklotGhl0|P~i$)-spEt!DmG!4Oj(E`y{=&}QVPEVav`S8+sy%-3K4v`F-e93n=ACyP7?a2n|X$Q?(91Ro%p-ctYo000O{ KMNUMnLSTYNOFs$# delta 157 zcmV;O0Al~o0nh=E7zqRe0000j+0-qOAt!%xNklX^XCMShTo?)nDq>S%AOZ*t zX+^A|p#d-qlVCO`QjxI%5!T^VWNd8AY-|jo3~(x91}ec~9kT%fQ=|y!00S5%RuR Date: Fri, 14 Aug 2009 08:58:31 +0200 Subject: [PATCH 133/133] git-cvsimport: add support for cvs pserver password scrambling. Instead of a cleartext password, the CVS pserver expects a scrambled one in the authentication request. With this patch it is possible to import CVS repositories only accessible via pserver and user/password. Signed-off-by: Dirk Hoerner Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index e439202961..593832d813 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -252,7 +252,8 @@ sub conn { } }; } - $pass="A" unless $pass; + + $pass = $self->_scramble($pass); my ($s, $rep); if ($proxyhost) { @@ -484,6 +485,42 @@ sub _fetchfile { return $res; } +sub _scramble { + my ($self, $pass) = @_; + my $scrambled = "A"; + + return $scrambled unless $pass; + + my $pass_len = length($pass); + my @pass_arr = split("", $pass); + my $i; + + # from cvs/src/scramble.c + my @shifts = ( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87, + 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105, + 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35, + 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56, + 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, + 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223, + 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190, + 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193, + 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212, + 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246, + 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176, + 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127, + 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195, + 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 + ); + + for ($i = 0; $i < $pass_len; $i++) { + $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]); + } + + return $scrambled; +} package main;