From 4fe00b4f0ab148de78db18790955d5e381377b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 17 Jun 2013 22:34:16 +0200 Subject: [PATCH 01/16] bash prompt: fix redirection coding style in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use '>file' instead of '> file', in accordance with the coding guidelines. Signed-off-by: SZEDER Gábor --- t/t9903-bash-prompt.sh | 232 ++++++++++++++++++++--------------------- 1 file changed, 116 insertions(+), 116 deletions(-) diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 15521cc4f..7c7f8b974 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -14,98 +14,98 @@ actual="$TRASH_DIRECTORY/actual" test_expect_success 'setup for prompt tests' ' mkdir -p subdir/subsubdir && git init otherrepo && - echo 1 > file && + echo 1 >file && git add file && test_tick && git commit -m initial && git tag -a -m msg1 t1 && git checkout -b b1 && - echo 2 > file && + echo 2 >file && git commit -m "second b1" file && - echo 3 > file && + echo 3 >file && git commit -m "third b1" file && git tag -a -m msg2 t2 && git checkout -b b2 master && - echo 0 > file && + echo 0 >file && git commit -m "second b2" file && - echo 00 > file && + echo 00 >file && git commit -m "another b2" file && - echo 000 > file && + echo 000 >file && git commit -m "yet another b2" file && git checkout master ' test_expect_success 'gitdir - from command line (through $__git_dir)' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" > expected && + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && ( __git_dir="$TRASH_DIRECTORY/otherrepo/.git" && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'gitdir - repo as argument' ' - echo "otherrepo/.git" > expected && - __gitdir "otherrepo" > "$actual" && + echo "otherrepo/.git" >expected && + __gitdir "otherrepo" >"$actual" && test_cmp expected "$actual" ' test_expect_success 'gitdir - remote as argument' ' - echo "remote" > expected && - __gitdir "remote" > "$actual" && + echo "remote" >expected && + __gitdir "remote" >"$actual" && test_cmp expected "$actual" ' test_expect_success 'gitdir - .git directory in cwd' ' - echo ".git" > expected && - __gitdir > "$actual" && + echo ".git" >expected && + __gitdir >"$actual" && test_cmp expected "$actual" ' test_expect_success 'gitdir - .git directory in parent' ' - echo "$(pwd -P)/.git" > expected && + echo "$(pwd -P)/.git" >expected && ( cd subdir/subsubdir && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'gitdir - cwd is a .git directory' ' - echo "." > expected && + echo "." >expected && ( cd .git && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'gitdir - parent is a .git directory' ' - echo "$(pwd -P)/.git" > expected && + echo "$(pwd -P)/.git" >expected && ( cd .git/refs/heads && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'gitdir - $GIT_DIR set while .git directory in cwd' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" > expected && + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && ( GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && export GIT_DIR && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'gitdir - $GIT_DIR set while .git directory in parent' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" > expected && + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && ( GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && export GIT_DIR && cd subdir && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' @@ -119,36 +119,36 @@ test_expect_success 'gitdir - non-existing $GIT_DIR' ' ' test_expect_success 'gitdir - gitfile in cwd' ' - echo "$(pwd -P)/otherrepo/.git" > expected && - echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git && + echo "$(pwd -P)/otherrepo/.git" >expected && + echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && test_when_finished "rm -f subdir/.git" && ( cd subdir && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'gitdir - gitfile in parent' ' - echo "$(pwd -P)/otherrepo/.git" > expected && - echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git && + echo "$(pwd -P)/otherrepo/.git" >expected && + echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && test_when_finished "rm -f subdir/.git" && ( cd subdir/subsubdir && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' ' - echo "$(pwd -P)/otherrepo/.git" > expected && + echo "$(pwd -P)/otherrepo/.git" >expected && mkdir otherrepo/dir && test_when_finished "rm -rf otherrepo/dir" && ln -s otherrepo/dir link && test_when_finished "rm -f link" && ( cd link && - __gitdir > "$actual" + __gitdir >"$actual" ) && test_cmp expected "$actual" ' @@ -163,96 +163,96 @@ test_expect_success 'gitdir - not a git repository' ' ' test_expect_success 'prompt - branch name' ' - printf " (master)" > expected && - __git_ps1 > "$actual" && + printf " (master)" >expected && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - detached head' ' - printf " ((%s...))" $(git log -1 --format="%h" b1^) > expected && + printf " ((%s...))" $(git log -1 --format="%h" b1^) >expected && git checkout b1^ && test_when_finished "git checkout master" && - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - describe detached head - contains' ' - printf " ((t2~1))" > expected && + printf " ((t2~1))" >expected && git checkout b1^ && test_when_finished "git checkout master" && ( GIT_PS1_DESCRIBE_STYLE=contains && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - describe detached head - branch' ' - printf " ((b1~1))" > expected && + printf " ((b1~1))" >expected && git checkout b1^ && test_when_finished "git checkout master" && ( GIT_PS1_DESCRIBE_STYLE=branch && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - describe detached head - describe' ' - printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) > expected && + printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected && git checkout b1^ && test_when_finished "git checkout master" && ( GIT_PS1_DESCRIBE_STYLE=describe && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - describe detached head - default' ' - printf " ((t2))" > expected && + printf " ((t2))" >expected && git checkout --detach b1 && test_when_finished "git checkout master" && - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - inside .git directory' ' - printf " (GIT_DIR!)" > expected && + printf " (GIT_DIR!)" >expected && ( cd .git && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - deep inside .git directory' ' - printf " (GIT_DIR!)" > expected && + printf " (GIT_DIR!)" >expected && ( cd .git/refs/heads && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - inside bare repository' ' - printf " (BARE:master)" > expected && + printf " (BARE:master)" >expected && git init --bare bare.git && test_when_finished "rm -rf bare.git" && ( cd bare.git && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - interactive rebase' ' - printf " (b1|REBASE-i 2/3)" > expected + printf " (b1|REBASE-i 2/3)" >expected echo "#!$SHELL_PATH" >fake_editor.sh && cat >>fake_editor.sh <<\EOF && -echo "exec echo" > "$1" -echo "edit $(git log -1 --format="%h")" >> "$1" -echo "exec echo" >> "$1" +echo "exec echo" >"$1" +echo "edit $(git log -1 --format="%h")" >>"$1" +echo "exec echo" >>"$1" EOF test_when_finished "rm -f fake_editor.sh" && chmod a+x fake_editor.sh && @@ -261,277 +261,277 @@ EOF test_when_finished "git checkout master" && git rebase -i HEAD^ && test_when_finished "git rebase --abort" - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - rebase merge' ' - printf " (b2|REBASE-m 1/3)" > expected && + printf " (b2|REBASE-m 1/3)" >expected && git checkout b2 && test_when_finished "git checkout master" && test_must_fail git rebase --merge b1 b2 && test_when_finished "git rebase --abort" && - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - rebase' ' - printf " (b2|REBASE 1/3)" > expected && + printf " (b2|REBASE 1/3)" >expected && git checkout b2 && test_when_finished "git checkout master" && test_must_fail git rebase b1 b2 && test_when_finished "git rebase --abort" && - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - merge' ' - printf " (b1|MERGING)" > expected && + printf " (b1|MERGING)" >expected && git checkout b1 && test_when_finished "git checkout master" && test_must_fail git merge b2 && test_when_finished "git reset --hard" && - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - cherry-pick' ' - printf " (master|CHERRY-PICKING)" > expected && + printf " (master|CHERRY-PICKING)" >expected && test_must_fail git cherry-pick b1 && test_when_finished "git reset --hard" && - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - bisect' ' - printf " (master|BISECTING)" > expected && + printf " (master|BISECTING)" >expected && git bisect start && test_when_finished "git bisect reset" && - __git_ps1 > "$actual" && + __git_ps1 >"$actual" && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - clean' ' - printf " (master)" > expected && + printf " (master)" >expected && ( GIT_PS1_SHOWDIRTYSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - dirty worktree' ' - printf " (master *)" > expected && - echo "dirty" > file && + printf " (master *)" >expected && + echo "dirty" >file && test_when_finished "git reset --hard" && ( GIT_PS1_SHOWDIRTYSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - dirty index' ' - printf " (master +)" > expected && - echo "dirty" > file && + printf " (master +)" >expected && + echo "dirty" >file && test_when_finished "git reset --hard" && git add -u && ( GIT_PS1_SHOWDIRTYSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - dirty index and worktree' ' - printf " (master *+)" > expected && - echo "dirty index" > file && + printf " (master *+)" >expected && + echo "dirty index" >file && test_when_finished "git reset --hard" && git add -u && - echo "dirty worktree" > file && + echo "dirty worktree" >file && ( GIT_PS1_SHOWDIRTYSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - before root commit' ' - printf " (master #)" > expected && + printf " (master #)" >expected && ( GIT_PS1_SHOWDIRTYSTATE=y && cd otherrepo && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' ' - printf " (master)" > expected && - echo "dirty" > file && + printf " (master)" >expected && + echo "dirty" >file && test_when_finished "git reset --hard" && test_config bash.showDirtyState false && ( sane_unset GIT_PS1_SHOWDIRTYSTATE && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' ' - printf " (master)" > expected && - echo "dirty" > file && + printf " (master)" >expected && + echo "dirty" >file && test_when_finished "git reset --hard" && test_config bash.showDirtyState true && ( sane_unset GIT_PS1_SHOWDIRTYSTATE && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' ' - printf " (master)" > expected && - echo "dirty" > file && + printf " (master)" >expected && + echo "dirty" >file && test_when_finished "git reset --hard" && test_config bash.showDirtyState false && ( GIT_PS1_SHOWDIRTYSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' ' - printf " (master *)" > expected && - echo "dirty" > file && + printf " (master *)" >expected && + echo "dirty" >file && test_when_finished "git reset --hard" && test_config bash.showDirtyState true && ( GIT_PS1_SHOWDIRTYSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' ' - printf " (GIT_DIR!)" > expected && - echo "dirty" > file && + printf " (GIT_DIR!)" >expected && + echo "dirty" >file && test_when_finished "git reset --hard" && ( GIT_PS1_SHOWDIRTYSTATE=y && cd .git && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - stash status indicator - no stash' ' - printf " (master)" > expected && + printf " (master)" >expected && ( GIT_PS1_SHOWSTASHSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - stash status indicator - stash' ' - printf " (master $)" > expected && + printf " (master $)" >expected && echo 2 >file && git stash && test_when_finished "git stash drop" && ( GIT_PS1_SHOWSTASHSTATE=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - stash status indicator - not shown inside .git directory' ' - printf " (GIT_DIR!)" > expected && + printf " (GIT_DIR!)" >expected && echo 2 >file && git stash && test_when_finished "git stash drop" && ( GIT_PS1_SHOWSTASHSTATE=y && cd .git && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - untracked files status indicator - no untracked files' ' - printf " (master)" > expected && + printf " (master)" >expected && ( GIT_PS1_SHOWUNTRACKEDFILES=y && cd otherrepo && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - untracked files status indicator - untracked files' ' - printf " (master %%)" > expected && + printf " (master %%)" >expected && ( GIT_PS1_SHOWUNTRACKEDFILES=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' ' - printf " (master)" > expected && + printf " (master)" >expected && test_config bash.showUntrackedFiles false && ( sane_unset GIT_PS1_SHOWUNTRACKEDFILES && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' ' - printf " (master)" > expected && + printf " (master)" >expected && test_config bash.showUntrackedFiles true && ( sane_unset GIT_PS1_SHOWUNTRACKEDFILES && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' ' - printf " (master)" > expected && + printf " (master)" >expected && test_config bash.showUntrackedFiles false && ( GIT_PS1_SHOWUNTRACKEDFILES=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' ' - printf " (master %%)" > expected && + printf " (master %%)" >expected && test_config bash.showUntrackedFiles true && ( GIT_PS1_SHOWUNTRACKEDFILES=y && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' ' - printf " (GIT_DIR!)" > expected && + printf " (GIT_DIR!)" >expected && ( GIT_PS1_SHOWUNTRACKEDFILES=y && cd .git && - __git_ps1 > "$actual" + __git_ps1 >"$actual" ) && test_cmp expected "$actual" ' test_expect_success 'prompt - format string starting with dash' ' - printf -- "-master" > expected && - __git_ps1 "-%s" > "$actual" && + printf -- "-master" >expected && + __git_ps1 "-%s" >"$actual" && test_cmp expected "$actual" ' From 7412290cc4b5aa0efc205f81e4775dd7df9ea9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 24 Aug 2012 20:03:58 +0200 Subject: [PATCH 02/16] bash prompt: use 'write_script' helper in interactive rebase test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Helped-by: Jeff King Signed-off-by: SZEDER Gábor --- t/t9903-bash-prompt.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 7c7f8b974..442b9a20f 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -248,14 +248,12 @@ test_expect_success 'prompt - inside bare repository' ' test_expect_success 'prompt - interactive rebase' ' printf " (b1|REBASE-i 2/3)" >expected - echo "#!$SHELL_PATH" >fake_editor.sh && - cat >>fake_editor.sh <<\EOF && -echo "exec echo" >"$1" -echo "edit $(git log -1 --format="%h")" >>"$1" -echo "exec echo" >>"$1" -EOF + write_script fake_editor.sh <<-\EOF && + echo "exec echo" >"$1" + echo "edit $(git log -1 --format="%h")" >>"$1" + echo "exec echo" >>"$1" + EOF test_when_finished "rm -f fake_editor.sh" && - chmod a+x fake_editor.sh && test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" && git checkout b1 && test_when_finished "git checkout master" && From c9a102e81ffedde3fed4b88199ea13a3a5ee5f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 17 Jun 2013 20:31:51 +0200 Subject: [PATCH 03/16] completion, bash prompt: move __gitdir() tests to completion test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently __gitdir() is duplicated in the git completion and prompt scripts, while its tests are in the prompt test suite. This patch series is about to change __git_ps1() in a way that it won't need __gitdir() anymore and __gitdir() will be removed from the prompt script. So move all __gitdir() tests from the prompt test suite over to the completion test suite. Update the setup tests so that they perform only those steps that are necessary for each test suite. Signed-off-by: SZEDER Gábor --- t/t9902-completion.sh | 134 +++++++++++++++++++++++++++++++++++++++++ t/t9903-bash-prompt.sh | 128 --------------------------------------- 2 files changed, 134 insertions(+), 128 deletions(-) diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 81a1657ef..5469dee8d 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -122,6 +122,140 @@ test_gitcomp_nl () invalid_variable_name='${foo.bar}' +actual="$TRASH_DIRECTORY/actual" + +test_expect_success 'setup for __gitdir tests' ' + mkdir -p subdir/subsubdir && + git init otherrepo +' + +test_expect_success '__gitdir - from command line (through $__git_dir)' ' + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && + ( + __git_dir="$TRASH_DIRECTORY/otherrepo/.git" && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - repo as argument' ' + echo "otherrepo/.git" >expected && + __gitdir "otherrepo" >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - remote as argument' ' + echo "remote" >expected && + __gitdir "remote" >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - .git directory in cwd' ' + echo ".git" >expected && + __gitdir >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - .git directory in parent' ' + echo "$(pwd -P)/.git" >expected && + ( + cd subdir/subsubdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - cwd is a .git directory' ' + echo "." >expected && + ( + cd .git && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - parent is a .git directory' ' + echo "$(pwd -P)/.git" >expected && + ( + cd .git/refs/heads && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' ' + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && + ( + GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + export GIT_DIR && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' ' + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && + ( + GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + export GIT_DIR && + cd subdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - non-existing $GIT_DIR' ' + ( + GIT_DIR="$TRASH_DIRECTORY/non-existing" && + export GIT_DIR && + test_must_fail __gitdir + ) +' + +test_expect_success '__gitdir - gitfile in cwd' ' + echo "$(pwd -P)/otherrepo/.git" >expected && + echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && + test_when_finished "rm -f subdir/.git" && + ( + cd subdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - gitfile in parent' ' + echo "$(pwd -P)/otherrepo/.git" >expected && + echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && + test_when_finished "rm -f subdir/.git" && + ( + cd subdir/subsubdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' ' + echo "$(pwd -P)/otherrepo/.git" >expected && + mkdir otherrepo/dir && + test_when_finished "rm -rf otherrepo/dir" && + ln -s otherrepo/dir link && + test_when_finished "rm -f link" && + ( + cd link && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - not a git repository' ' + ( + cd subdir/subsubdir && + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" && + export GIT_CEILING_DIRECTORIES && + test_must_fail __gitdir + ) +' + test_expect_success '__gitcomp - trailing space - options' ' test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message= --reset-author" <<-EOF diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 442b9a20f..df36239a5 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -12,7 +12,6 @@ test_description='test git-specific bash prompt functions' actual="$TRASH_DIRECTORY/actual" test_expect_success 'setup for prompt tests' ' - mkdir -p subdir/subsubdir && git init otherrepo && echo 1 >file && git add file && @@ -35,133 +34,6 @@ test_expect_success 'setup for prompt tests' ' git checkout master ' -test_expect_success 'gitdir - from command line (through $__git_dir)' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && - ( - __git_dir="$TRASH_DIRECTORY/otherrepo/.git" && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - repo as argument' ' - echo "otherrepo/.git" >expected && - __gitdir "otherrepo" >"$actual" && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - remote as argument' ' - echo "remote" >expected && - __gitdir "remote" >"$actual" && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - .git directory in cwd' ' - echo ".git" >expected && - __gitdir >"$actual" && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - .git directory in parent' ' - echo "$(pwd -P)/.git" >expected && - ( - cd subdir/subsubdir && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - cwd is a .git directory' ' - echo "." >expected && - ( - cd .git && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - parent is a .git directory' ' - echo "$(pwd -P)/.git" >expected && - ( - cd .git/refs/heads && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - $GIT_DIR set while .git directory in cwd' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && - ( - GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && - export GIT_DIR && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - $GIT_DIR set while .git directory in parent' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && - ( - GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && - export GIT_DIR && - cd subdir && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - non-existing $GIT_DIR' ' - ( - GIT_DIR="$TRASH_DIRECTORY/non-existing" && - export GIT_DIR && - test_must_fail __gitdir - ) -' - -test_expect_success 'gitdir - gitfile in cwd' ' - echo "$(pwd -P)/otherrepo/.git" >expected && - echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && - test_when_finished "rm -f subdir/.git" && - ( - cd subdir && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - gitfile in parent' ' - echo "$(pwd -P)/otherrepo/.git" >expected && - echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && - test_when_finished "rm -f subdir/.git" && - ( - cd subdir/subsubdir && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' ' - echo "$(pwd -P)/otherrepo/.git" >expected && - mkdir otherrepo/dir && - test_when_finished "rm -rf otherrepo/dir" && - ln -s otherrepo/dir link && - test_when_finished "rm -f link" && - ( - cd link && - __gitdir >"$actual" - ) && - test_cmp expected "$actual" -' - -test_expect_success 'gitdir - not a git repository' ' - ( - cd subdir/subsubdir && - GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" && - export GIT_CEILING_DIRECTORIES && - test_must_fail __gitdir - ) -' - test_expect_success 'prompt - branch name' ' printf " (master)" >expected && __git_ps1 >"$actual" && From 868dc1acecdb8b661e415c6c5f09db5370b35fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 24 Aug 2012 19:52:48 +0200 Subject: [PATCH 04/16] bash prompt: add a test for symbolic link symbolic refs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor --- t/t9903-bash-prompt.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index df36239a5..416e6219c 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -40,6 +40,15 @@ test_expect_success 'prompt - branch name' ' test_cmp expected "$actual" ' +test_expect_success SYMLINKS 'prompt - branch name - symlink symref' ' + printf " (master)" >expected && + test_when_finished "git checkout master" && + test_config core.preferSymlinkRefs true && + git checkout master && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + test_expect_success 'prompt - detached head' ' printf " ((%s...))" $(git log -1 --format="%h" b1^) >expected && git checkout b1^ && From e8f21caf94287d838cfffe8301b28fdc45480ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 24 Jun 2013 01:55:42 +0200 Subject: [PATCH 05/16] bash prompt: print unique detached HEAD abbreviated object name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When describing a detached HEAD according to the $GIT_PS1_DESCRIBE environment variable fails, __git_ps1() runs 'cut -c1-7 .git/HEAD' to show the 7 hexdigits abbreviated commit object name in the prompt. Obviously, this neither respects core.abbrev nor produces a unique object name. Fix this by using 'git rev-parse --short HEAD' instead and adjust the corresponding test to use non-standard number of hexdigits. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 2 +- t/t9903-bash-prompt.sh | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 07a6218d1..3c5e62bb9 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -392,7 +392,7 @@ __git_ps1 () git describe --tags --exact-match HEAD ;; esac 2>/dev/null)" || - b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || + b="$(git rev-parse --short HEAD 2>/dev/null)..." || b="unknown" b="($b)" } diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 416e6219c..0d53aa6d6 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -50,7 +50,8 @@ test_expect_success SYMLINKS 'prompt - branch name - symlink symref' ' ' test_expect_success 'prompt - detached head' ' - printf " ((%s...))" $(git log -1 --format="%h" b1^) >expected && + printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected && + test_config core.abbrev 13 && git checkout b1^ && test_when_finished "git checkout master" && __git_ps1 >"$actual" && From 96ea404757ac3f97335277062a7d7c6e8975cc4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 5 Sep 2011 20:53:37 +0200 Subject: [PATCH 06/16] bash prompt: return early from __git_ps1() when not in a git repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... to gain one level of indentation for the bulk of the function. (The patch looks quite unreadable, you'd better check it with 'git diff -w'.) Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 201 ++++++++++++++++--------------- 1 file changed, 101 insertions(+), 100 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 3c5e62bb9..a915b04c1 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -341,121 +341,122 @@ __git_ps1 () #In PC mode PS1 always needs to be set PS1="$ps1pc_start$ps1pc_end" fi + return + fi + + local r="" + local b="" + local step="" + local total="" + if [ -d "$g/rebase-merge" ]; then + b="$(cat "$g/rebase-merge/head-name" 2>/dev/null)" + step=$(cat "$g/rebase-merge/msgnum" 2>/dev/null) + total=$(cat "$g/rebase-merge/end" 2>/dev/null) + if [ -f "$g/rebase-merge/interactive" ]; then + r="|REBASE-i" + else + r="|REBASE-m" + fi else - local r="" - local b="" - local step="" - local total="" - if [ -d "$g/rebase-merge" ]; then - b="$(cat "$g/rebase-merge/head-name" 2>/dev/null)" - step=$(cat "$g/rebase-merge/msgnum" 2>/dev/null) - total=$(cat "$g/rebase-merge/end" 2>/dev/null) - if [ -f "$g/rebase-merge/interactive" ]; then - r="|REBASE-i" + if [ -d "$g/rebase-apply" ]; then + step=$(cat "$g/rebase-apply/next" 2>/dev/null) + total=$(cat "$g/rebase-apply/last" 2>/dev/null) + if [ -f "$g/rebase-apply/rebasing" ]; then + b="$(cat "$g/rebase-apply/head-name" 2>/dev/null)" + r="|REBASE" + elif [ -f "$g/rebase-apply/applying" ]; then + r="|AM" else - r="|REBASE-m" - fi - else - if [ -d "$g/rebase-apply" ]; then - step=$(cat "$g/rebase-apply/next" 2>/dev/null) - total=$(cat "$g/rebase-apply/last" 2>/dev/null) - if [ -f "$g/rebase-apply/rebasing" ]; then - b="$(cat "$g/rebase-apply/head-name" 2>/dev/null)" - r="|REBASE" - elif [ -f "$g/rebase-apply/applying" ]; then - r="|AM" - else - r="|AM/REBASE" - fi - elif [ -f "$g/MERGE_HEAD" ]; then - r="|MERGING" - elif [ -f "$g/CHERRY_PICK_HEAD" ]; then - r="|CHERRY-PICKING" - elif [ -f "$g/REVERT_HEAD" ]; then - r="|REVERTING" - elif [ -f "$g/BISECT_LOG" ]; then - r="|BISECTING" + r="|AM/REBASE" fi + elif [ -f "$g/MERGE_HEAD" ]; then + r="|MERGING" + elif [ -f "$g/CHERRY_PICK_HEAD" ]; then + r="|CHERRY-PICKING" + elif [ -f "$g/REVERT_HEAD" ]; then + r="|REVERTING" + elif [ -f "$g/BISECT_LOG" ]; then + r="|BISECTING" + fi - test -n "$b" || - b="$(git symbolic-ref HEAD 2>/dev/null)" || { - detached=yes - b="$( - case "${GIT_PS1_DESCRIBE_STYLE-}" in - (contains) - git describe --contains HEAD ;; - (branch) - git describe --contains --all HEAD ;; - (describe) - git describe HEAD ;; - (* | default) - git describe --tags --exact-match HEAD ;; - esac 2>/dev/null)" || + test -n "$b" || + b="$(git symbolic-ref HEAD 2>/dev/null)" || { + detached=yes + b="$( + case "${GIT_PS1_DESCRIBE_STYLE-}" in + (contains) + git describe --contains HEAD ;; + (branch) + git describe --contains --all HEAD ;; + (describe) + git describe HEAD ;; + (* | default) + git describe --tags --exact-match HEAD ;; + esac 2>/dev/null)" || - b="$(git rev-parse --short HEAD 2>/dev/null)..." || - b="unknown" - b="($b)" - } - fi + b="$(git rev-parse --short HEAD 2>/dev/null)..." || + b="unknown" + b="($b)" + } + fi - if [ -n "$step" ] && [ -n "$total" ]; then - r="$r $step/$total" - fi + if [ -n "$step" ] && [ -n "$total" ]; then + r="$r $step/$total" + fi - local w="" - local i="" - local s="" - local u="" - local c="" - local p="" + local w="" + local i="" + local s="" + local u="" + local c="" + local p="" - if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then - if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then - c="BARE:" + if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then + if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then + c="BARE:" + else + b="GIT_DIR!" + fi + elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] && + [ "$(git config --bool bash.showDirtyState)" != "false" ] + then + git diff --no-ext-diff --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet HEAD -- || i="+" else - b="GIT_DIR!" - fi - elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then - if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] && - [ "$(git config --bool bash.showDirtyState)" != "false" ] - then - git diff --no-ext-diff --quiet --exit-code || w="*" - if git rev-parse --quiet --verify HEAD >/dev/null; then - git diff-index --cached --quiet HEAD -- || i="+" - else - i="#" - fi - fi - if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then - git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" + i="#" fi + fi + if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then + git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" + fi - if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] && - [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] && - [ -n "$(git ls-files --others --exclude-standard)" ] - then - u="%${ZSH_VERSION+%}" - fi + if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] && + [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] && + [ -n "$(git ls-files --others --exclude-standard)" ] + then + u="%${ZSH_VERSION+%}" + fi - if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then - __git_ps1_show_upstream - fi + if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then + __git_ps1_show_upstream fi + fi - local z="${GIT_PS1_STATESEPARATOR-" "}" - local f="$w$i$s$u" - if [ $pcmode = yes ]; then - local gitstring= - if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then - __git_ps1_colorize_gitstring - else - gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p" - fi - gitstring=$(printf -- "$printf_format" "$gitstring") - PS1="$ps1pc_start$gitstring$ps1pc_end" + local z="${GIT_PS1_STATESEPARATOR-" "}" + local f="$w$i$s$u" + if [ $pcmode = yes ]; then + local gitstring= + if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then + __git_ps1_colorize_gitstring else - # NO color option unless in PROMPT_COMMAND mode - printf -- "$printf_format" "$c${b##refs/heads/}${f:+$z$f}$r$p" + gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p" fi + gitstring=$(printf -- "$printf_format" "$gitstring") + PS1="$ps1pc_start$gitstring$ps1pc_end" + else + # NO color option unless in PROMPT_COMMAND mode + printf -- "$printf_format" "$c${b##refs/heads/}${f:+$z$f}$r$p" fi } From 511ad159049fc64a13ef3e9565cc9634acb6404b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 17 Jun 2013 23:55:16 +0200 Subject: [PATCH 07/16] bash prompt: run 'git rev-parse --git-dir' directly instead of __gitdir() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __git_ps1() finds out the path to the repository by using the __gitdir() helper function. __gitdir() is basically just a wrapper around 'git rev-parse --git-dir', extended with support for recognizing a remote repository given as argument, to use the path given on the command line, and with a few shortcuts to recognize a git repository in cwd or at $GIT_DIR quickly without actually running 'git rev-parse'. However, the former two is only necessary for the completion script but makes no sense for the bash prompt, while the latter shortcuts are performance optimizations __git_ps1() can do without (they just avoid the overhead of fork()+exec()ing a git process). Run 'git rev-parse --git-dir' directly in __git_ps1(), because it will allow this patch series to combine several $(git rev-parse ...) command substitutions in the main code path, and the overall performance benefit will far outweigh the loss of those few shortcuts in __gitdir(). Furthermore, since __gitdir() is not needed anymore for the prompt, remove it from the prompt script finally eliminating its duplication between the prompt and completion scripts. Also remove the comment from the completion script warning about this code duplication. Signed-off-by: SZEDER Gábor --- contrib/completion/git-completion.bash | 2 -- contrib/completion/git-prompt.sh | 26 +------------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6c3bafeea..ebc40d484 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -33,8 +33,6 @@ esac # returns location of .git repo __gitdir () { - # Note: this function is duplicated in git-prompt.sh - # When updating it, make sure you update the other one to match. if [ -z "${1-}" ]; then if [ -n "${__git_dir-}" ]; then echo "$__git_dir" diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index a915b04c1..0563dea8a 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -80,30 +80,6 @@ # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on # the colored output of "git status -sb". -# __gitdir accepts 0 or 1 arguments (i.e., location) -# returns location of .git repo -__gitdir () -{ - # Note: this function is duplicated in git-completion.bash - # When updating it, make sure you update the other one to match. - if [ -z "${1-}" ]; then - if [ -n "${__git_dir-}" ]; then - echo "$__git_dir" - elif [ -n "${GIT_DIR-}" ]; then - test -d "${GIT_DIR-}" || return 1 - echo "$GIT_DIR" - elif [ -d .git ]; then - echo .git - else - git rev-parse --git-dir 2>/dev/null - fi - elif [ -d "$1/.git" ]; then - echo "$1/.git" - else - echo "$1" - fi -} - # stores the divergence from upstream in $p # used by GIT_PS1_SHOWUPSTREAM __git_ps1_show_upstream () @@ -335,7 +311,7 @@ __git_ps1 () ;; esac - local g="$(__gitdir)" + local g="$(git rev-parse --git-dir 2>/dev/null)" if [ -z "$g" ]; then if [ $pcmode = yes ]; then #In PC mode PS1 always needs to be set From b91b935f04e8dcb1cc9f247627fbd0346ce949f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 1 Apr 2011 00:25:16 +0200 Subject: [PATCH 08/16] bash prompt: use bash builtins to find out rebase state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During an ongoing interactive rebase __git_ps1() finds out the name of the rebased branch, the total number of patches and the number of the current patch by executing a '$(cat .git/rebase-merge/)' command substitution for each. That is not quite the most efficient way to read single line single word files, because it imposes the overhead of fork()ing a subshell and fork()+exec()ing 'cat' several times. Use the 'read' bash builtin instead to avoid those overheads. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 0563dea8a..bc402f56b 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -325,9 +325,9 @@ __git_ps1 () local step="" local total="" if [ -d "$g/rebase-merge" ]; then - b="$(cat "$g/rebase-merge/head-name" 2>/dev/null)" - step=$(cat "$g/rebase-merge/msgnum" 2>/dev/null) - total=$(cat "$g/rebase-merge/end" 2>/dev/null) + read b 2>/dev/null <"$g/rebase-merge/head-name" + read step 2>/dev/null <"$g/rebase-merge/msgnum" + read total 2>/dev/null <"$g/rebase-merge/end" if [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" else @@ -335,10 +335,10 @@ __git_ps1 () fi else if [ -d "$g/rebase-apply" ]; then - step=$(cat "$g/rebase-apply/next" 2>/dev/null) - total=$(cat "$g/rebase-apply/last" 2>/dev/null) + read step 2>/dev/null <"$g/rebase-apply/next" + read total 2>/dev/null <"$g/rebase-apply/last" if [ -f "$g/rebase-apply/rebasing" ]; then - b="$(cat "$g/rebase-apply/head-name" 2>/dev/null)" + read b 2>/dev/null <"$g/rebase-apply/head-name" r="|REBASE" elif [ -f "$g/rebase-apply/applying" ]; then r="|AM" From 3a43c4b5bd19528229ef36b28d648d5ac98f15f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 31 Mar 2011 23:41:18 +0200 Subject: [PATCH 09/16] bash prompt: use bash builtins to find out current branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __git_ps1() runs the '$(git symbolic-ref HEAD)' command substitution to find out whether we are on a branch and to find out the name of that branch. This imposes the overhead of fork()ing a subshell and fork()+exec()ing a git process. Since HEAD is in most cases a single-line file and the symbolic ref format is quite simple to recognize and parse, read and parse it using only bash builtins, thereby sparing all that fork()+exec() overhead. Don't display the git prompt if reading HEAD fails, because a readable HEAD is required for a git repository. HEAD can also be a symlink symbolic ref (due to 'core.preferSymlinkRefs'), so use bash builtins for reading HEAD only when HEAD is not a symlink. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 51 +++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index bc402f56b..c2050b69b 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -355,25 +355,40 @@ __git_ps1 () r="|BISECTING" fi - test -n "$b" || - b="$(git symbolic-ref HEAD 2>/dev/null)" || { - detached=yes - b="$( - case "${GIT_PS1_DESCRIBE_STYLE-}" in - (contains) - git describe --contains HEAD ;; - (branch) - git describe --contains --all HEAD ;; - (describe) - git describe HEAD ;; - (* | default) - git describe --tags --exact-match HEAD ;; - esac 2>/dev/null)" || + if [ -n "$b" ]; then + : + elif [ -h "$g/HEAD" ]; then + # symlink symbolic ref + b="$(git symbolic-ref HEAD 2>/dev/null)" + else + local head="" + if ! read head 2>/dev/null <"$g/HEAD"; then + if [ $pcmode = yes ]; then + PS1="$ps1pc_start$ps1pc_end" + fi + return + fi + # is it a symbolic ref? + b="${head#ref: }" + if [ "$head" = "$b" ]; then + detached=yes + b="$( + case "${GIT_PS1_DESCRIBE_STYLE-}" in + (contains) + git describe --contains HEAD ;; + (branch) + git describe --contains --all HEAD ;; + (describe) + git describe HEAD ;; + (* | default) + git describe --tags --exact-match HEAD ;; + esac 2>/dev/null)" || - b="$(git rev-parse --short HEAD 2>/dev/null)..." || - b="unknown" - b="($b)" - } + b="$(git rev-parse --short HEAD 2>/dev/null)..." || + b="unknown" + b="($b)" + fi + fi fi if [ -n "$step" ] && [ -n "$total" ]; then From efaa0c153297f551a42fd1e21f28f51f4924f316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 17 Jun 2013 22:58:42 +0200 Subject: [PATCH 10/16] bash prompt: combine 'git rev-parse' executions in the main code path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are a couple of '$(git rev-parse --)' command substitutions in __git_ps1() and three of them are executed in the main code path: - the first to get the path to the .git directory ('--git-dir'), - the second to check whether we're inside the .git directory ('--is-inside-git-dir'), - and the last, depending on the results of the second, either * to check whether it's a bare repo ('--is-bare-repository'), or * to check whether inside a work tree ('--is-inside-work-tree'). Naturally, this imposes the overhead of fork()ing three subshells and fork()+exec()ing three git commands. Combine these four 'git rev-parse' queries into a single one and use bash parameter expansions to parse the combined output, i.e. to separate the path to the .git directory from the true/false of '--is-inside-git-dir', etc. This way we can eliminate two of the three subshells and git commands. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index c2050b69b..7d2262512 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -311,8 +311,9 @@ __git_ps1 () ;; esac - local g="$(git rev-parse --git-dir 2>/dev/null)" - if [ -z "$g" ]; then + local repo_info="$(git rev-parse --git-dir --is-inside-git-dir \ + --is-bare-repository --is-inside-work-tree 2>/dev/null)" + if [ -z "$repo_info" ]; then if [ $pcmode = yes ]; then #In PC mode PS1 always needs to be set PS1="$ps1pc_start$ps1pc_end" @@ -320,6 +321,13 @@ __git_ps1 () return fi + local inside_worktree="${repo_info##*$'\n'}" + repo_info="${repo_info%$'\n'*}" + local bare_repo="${repo_info##*$'\n'}" + repo_info="${repo_info%$'\n'*}" + local inside_gitdir="${repo_info##*$'\n'}" + local g="${repo_info%$'\n'*}" + local r="" local b="" local step="" @@ -402,13 +410,13 @@ __git_ps1 () local c="" local p="" - if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then - if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then + if [ "true" = "$inside_gitdir" ]; then + if [ "true" = "$bare_repo" ]; then c="BARE:" else b="GIT_DIR!" fi - elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then + elif [ "true" = "$inside_worktree" ]; then if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] && [ "$(git config --bool bash.showDirtyState)" != "false" ] then From e3e0b9378b6e51ea50c023d92d4d2a1f4d4cc676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 24 Jun 2013 02:16:02 +0200 Subject: [PATCH 11/16] bash prompt: combine 'git rev-parse' for detached head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When describing a detached HEAD according to the $GIT_PS1_DESCRIBE environment variable fails, __git_ps1() now runs the '$(git rev-parse --short HEAD)' command substitution to get the abbreviated detached HEAD commit object name. This imposes the overhead of fork()ing a subshell and fork()+exec()ing a git process. Avoid this overhead by combining this command substitution with the "main" 'git rev-parse' execution for getting the path to the .git directory & co. This means that we'll look for the abbreviated commit object name even when it's not necessary, because we're on a branch or the detached HEAD can be described. It doesn't matter, however, because once 'git rev-parse' is up and running to fulfill all those other queries, the additional overhead of looking for the abbreviated commit object name is not measurable because it's lost in the noise. There is a caveat, however, when we are on an unborn branch, because in that case HEAD doesn't point to a valid commit, hence the query for the abbreviated commit object name fails. Therefore, '--short HEAD' must be the last options to 'git rev-parse' in order to get all the other necessary information for the prompt even on an unborn branch. Furthermore, in that case, and in that case only, 'git rev-parse' doesn't output the last line containing the abbreviated commit object name, obviously, so we have to take care to only parse it if 'git rev-parse' exited without any error. Although there are tests already excercising __git_ps1() on unborn branches, they all do so implicitly. Add a test that checks this explicitly. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 16 ++++++++++++---- t/t9903-bash-prompt.sh | 8 ++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 7d2262512..88d6121d2 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -311,8 +311,12 @@ __git_ps1 () ;; esac - local repo_info="$(git rev-parse --git-dir --is-inside-git-dir \ - --is-bare-repository --is-inside-work-tree 2>/dev/null)" + local repo_info rev_parse_exit_code + repo_info="$(git rev-parse --git-dir --is-inside-git-dir \ + --is-bare-repository --is-inside-work-tree \ + --short HEAD 2>/dev/null)" + rev_parse_exit_code="$?" + if [ -z "$repo_info" ]; then if [ $pcmode = yes ]; then #In PC mode PS1 always needs to be set @@ -321,6 +325,11 @@ __git_ps1 () return fi + local short_sha + if [ "$rev_parse_exit_code" = "0" ]; then + short_sha="${repo_info##*$'\n'}" + repo_info="${repo_info%$'\n'*}" + fi local inside_worktree="${repo_info##*$'\n'}" repo_info="${repo_info%$'\n'*}" local bare_repo="${repo_info##*$'\n'}" @@ -392,8 +401,7 @@ __git_ps1 () git describe --tags --exact-match HEAD ;; esac 2>/dev/null)" || - b="$(git rev-parse --short HEAD 2>/dev/null)..." || - b="unknown" + b="$short_sha..." b="($b)" fi fi diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 0d53aa6d6..b9895c797 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -49,6 +49,14 @@ test_expect_success SYMLINKS 'prompt - branch name - symlink symref' ' test_cmp expected "$actual" ' +test_expect_success 'prompt - unborn branch' ' + printf " (unborn)" >expected && + git checkout --orphan unborn && + test_when_finished "git checkout master" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + test_expect_success 'prompt - detached head' ' printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected && test_config core.abbrev 13 && From 0f37c125814afc8ad2fa43fecd8b200216ebfab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 24 Jun 2013 12:49:19 +0200 Subject: [PATCH 12/16] bash prompt: use bash builtins to check for unborn branch for dirty state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the dirty work tree and index status indicator is enabled, __git_ps1() checks for changes in the index by running 'git diff-index --cached --quiet HEAD --' and looking at its exit code. However, that makes sense only when HEAD points to a valid commit: on an unborn branch the failure of said command would be caused by the invalid HEAD, not by changes in the index. Therefore, __git_ps1() first checks for a valid HEAD by running 'git rev-parse --quiet --verify HEAD'. Since the previous patch we implicitly check HEAD's validity by running 'git rev-parse ... --short HEAD', making the dirty status indicator's 'git rev-parse' check redundant. It's sufficient to check for non-emptyness of the variable holding the abbreviated commit object name, thereby sparing the overhead of fork()+exec()ing a git process. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 88d6121d2..6e8f486e6 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -429,7 +429,7 @@ __git_ps1 () [ "$(git config --bool bash.showDirtyState)" != "false" ] then git diff --no-ext-diff --quiet --exit-code || w="*" - if git rev-parse --quiet --verify HEAD >/dev/null; then + if [ -n "$short_sha" ]; then git diff-index --cached --quiet HEAD -- || i="+" else i="#" From dd0b72cbd9e64c782a31c6acfca2ba9cf2ffb266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 1 Apr 2011 17:47:37 +0200 Subject: [PATCH 13/16] bash prompt: use bash builtins to check stash state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the environment variable $GIT_PS1_SHOWSTASHSTATE is set __git_ps1() checks the presence of stashes by running 'git rev-parse --verify refs/stash'. This command not only checks that the 'refs/stash' ref exists but also, well, verifies that it's a valid ref. However, we don't need to be that thorough for the bash prompt. We can omit that verification and only check whether 'refs/stash' exists or not. Since 'git pack-refs' never packs 'refs/stash', it's a matter of checking the existence of a ref file. Perform this check using only bash builtins to spare the overhead of fork()+exec()ing a git process. Also run 'git pack-refs --all' in the corresponding test to document that the prompt script depends on 'git pack-refs' not packing 'refs/stash' and to catch possible breakages should this behavior ever change. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 5 +++-- t/t9903-bash-prompt.sh | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 6e8f486e6..afa867030 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -435,8 +435,9 @@ __git_ps1 () i="#" fi fi - if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then - git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" + if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] && + [ -r "$g/refs/stash" ]; then + s="$" fi if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] && diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index b9895c797..c05458cbe 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -328,6 +328,7 @@ test_expect_success 'prompt - stash status indicator - stash' ' echo 2 >file && git stash && test_when_finished "git stash drop" && + git pack-refs --all && ( GIT_PS1_SHOWSTASHSTATE=y && __git_ps1 >"$actual" From 14d7649748265fe9fe991439ca6ae0c9db7a27ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 12 Aug 2011 14:17:12 +0200 Subject: [PATCH 14/16] bash prompt: avoid command substitution when checking for untracked files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When enabled, the bash prompt can indicate the presence of untracked files with a '%' sign. __git_ps1() checks for untracked files by running the '$(git ls-files --others --exclude-standard)' command substitution, and displays the indicator when there is no output. Avoid this command substitution by additionally passing '--error-unmatch *', and checking the command's return value. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index afa867030..5ea6a68bc 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -442,7 +442,7 @@ __git_ps1 () if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] && [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] && - [ -n "$(git ls-files --others --exclude-standard)" ] + git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null then u="%${ZSH_VERSION+%}" fi From 69a8141a5d81925b7e08cb228535e9ea4a7a02e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 17 Jun 2013 21:42:55 +0200 Subject: [PATCH 15/16] bash prompt: avoid command substitution when finalizing gitstring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before setting $PS1, __git_ps1() uses a command substitution to redirect the output from a printf into a variable. Spare the overhead of fork()ing a subshell by using 'printf -v ' to directly assign the output to that variable. zsh's printf doesn't support the '-v ' option, so stick with the command substitution when under zsh. Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 5ea6a68bc..7152ae49f 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -461,7 +461,11 @@ __git_ps1 () else gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p" fi - gitstring=$(printf -- "$printf_format" "$gitstring") + if [[ -n ${ZSH_VERSION-} ]]; then + gitstring=$(printf -- "$printf_format" "$gitstring") + else + printf -v gitstring -- "$printf_format" "$gitstring" + fi PS1="$ps1pc_start$gitstring$ps1pc_end" else # NO color option unless in PROMPT_COMMAND mode From a694258457e51f20e92854075914c8d3a4593367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 24 Jun 2013 02:28:02 +0200 Subject: [PATCH 16/16] bash prompt: mention that PROMPT_COMMAND mode is faster MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __git_ps1() is usually added to the prompt inside a command substitution, imposing the overhead of fork()ing a subshell. Using __git_ps1() for $PROMPT_COMMAND is slightly faster, because it avoids that command substitution. Mention this in the comments about setting up the git prompt. The whole series speeds up the bash prompt on Windows/MSysGit considerably. Here are some timing results in three scenarios, each repeated 10 times: At the top of the work tree, before: $ time for i in {0..9} ; do prompt="$(__git_ps1)" ; done real 0m1.716s user 0m0.301s sys 0m0.772s After: real 0m0.687s user 0m0.075s sys 0m0.396s After, from $PROMPT_COMMAND: $ time for i in {0..9} ; do __git_ps1 '\h:\w' '$ ' ; done real 0m0.546s user 0m0.075s sys 0m0.181s At the top of the work tree, detached head, before: real 0m2.574s user 0m0.376s sys 0m1.207s After: real 0m1.139s user 0m0.151s sys 0m0.500s After, from $PROMPT_COMMAND: real 0m1.030s user 0m0.245s sys 0m0.336s In a subdirectory, during rebase, stash status indicator enabled, before: real 0m3.557s user 0m0.495s sys 0m1.767s After: real 0m0.717s user 0m0.120s sys 0m0.300s After, from $PROMPT_COMMAND: real 0m0.577s user 0m0.047s sys 0m0.258s On Linux the speedup ratio is comparable to Windows, but overall it was about an order of magnitude faster to begin with. The last case from above, repeated 100 times, before: $ time for i in {0..99} ; do prompt="$(__git_ps1)" ; done real 0m2.806s user 0m0.180s sys 0m0.264s After: real 0m0.857s user 0m0.020s sys 0m0.028s Signed-off-by: SZEDER Gábor --- contrib/completion/git-prompt.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 7152ae49f..daed6a1d0 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -15,11 +15,11 @@ # Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' # ZSH: PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ ' # the optional argument will be used as format string. -# 3b) Alternatively, if you are using bash, __git_ps1 can be -# used for PROMPT_COMMAND with two parameters,
 and
-#        , which are strings you would put in $PS1 before
-#        and after the status string generated by the git-prompt
-#        machinery.  e.g.
+#    3b) Alternatively, for a slighly faster prompt, if you are
+#        using bash, __git_ps1 can be used for PROMPT_COMMAND
+#        with two parameters, 
 and , which are strings
+#        you would put in $PS1 before and after the status string
+#        generated by the git-prompt machinery.  e.g.
 #        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
 #        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
 #        will show username, at-sign, host, colon, cwd, then