Bash prompt "namespace" issue

2014-05-12 Thread szeder

Hi,

Commit 59d3924fb (prompt: fix missing file errors in zsh) added the  
helper function eread() to git-prompt.sh.  That function should be in  
the git "namespace", i.e. prefixed with __git_, otherwise it might  
collide with a function of the same name in the user's environment.


It's already in master and I don't have the means to send a patch  
fixing this ATM, sorry.


Best,
Gábor
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2] completion: Add '--edit-todo' to rebase

2015-07-30 Thread SZEDER Gábor


Quoting Thomas Braun :


Signed-off-by: Thomas Braun 
---

John Keeping  hat am 13. Juli 2015 um 15:11 geschrieben:
git-rebase.sh contains:

if test "$action" = "edit-todo" && test "$type" != "interactive"
then
die "$(gettext "The --edit-todo action can only be used during 
interactive
rebase.")"
fi

I wonder if it's worth doing a similar check here, which presumably
means testing if "$dir"/interactive exists.


Good point. Thanks for the hint.


Perhaps the subject line could say "completion: offer '--edit-todo'  
during interactive rebase" to be a bit more specific.



contrib/completion/git-completion.bash | 6 +-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash
b/contrib/completion/git-completion.bash
index c97c648..b03050e 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1668,7 +1668,11 @@ _git_rebase ()
{
local dir="$(__gitdir)"
if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
-   __gitcomp "--continue --skip --abort"
+   if [ -d "$dir"/interactive ]; then


This doesn't work for me, I think it looks for the right file at the  
wrong place.  During an interactive rebase I have no  
'.git/interactive' file but a '.git/rebase-merge/interactive', so I  
never get '--edit-todo'.


After some playing around and a cursory look at the source it seems to  
me that I have '.git/rebase-apply' during a "regular" rebase and  
'.git/rebase-merge' during an interactive rebase, and git-rebase.sh  
checks the presence of the 'interactive' file only in  
'.git/rebase-merge'.  It's not clear to me yet whether it's possible  
to have a '.git/rebase-merge' without the file 'interactive' in it.   
If it is possible, then I'd like to know with which commands and under  
what circumstances.  If it isn't, then we wouldn't have to look for  
the file at all, because checking the presence of the directory would  
be enough.



Best,
Gábor


+   __gitcomp "--continue --skip --abort --edit-todo"
+   else
+   __gitcomp "--continue --skip --abort"
+   fi
return
fi
__git_complete_strategy && return
--
2.4.5.windows.1


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2] completion: Add '--edit-todo' to rebase

2015-07-31 Thread SZEDER Gábor


Quoting John Keeping :


On Thu, Jul 30, 2015 at 01:24:03PM +0200, SZEDER Gábor wrote:


Quoting Thomas Braun :


Signed-off-by: Thomas Braun 
---

John Keeping  hat am 13. Juli 2015 um 15:11

geschrieben:

git-rebase.sh contains:

if test "$action" = "edit-todo" && test "$type" != "interactive"
then
die "$(gettext "The --edit-todo action can only be used during

interactive

rebase.")"
fi

I wonder if it's worth doing a similar check here, which presumably
means testing if "$dir"/interactive exists.


Good point. Thanks for the hint.


Perhaps the subject line could say "completion: offer '--edit-todo'
during interactive rebase" to be a bit more specific.


contrib/completion/git-completion.bash | 6 +-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash
b/contrib/completion/git-completion.bash
index c97c648..b03050e 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1668,7 +1668,11 @@ _git_rebase ()
{
local dir="$(__gitdir)"
if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
-   __gitcomp "--continue --skip --abort"
+   if [ -d "$dir"/interactive ]; then


This doesn't work for me, I think it looks for the right file at the
wrong place.  During an interactive rebase I have no
'.git/interactive' file but a '.git/rebase-merge/interactive', so I
never get '--edit-todo'.


Just noticed another issue here: it looks for a directory, though it  
should look for a file.




After some playing around and a cursory look at the source it seems to
me that I have '.git/rebase-apply' during a "regular" rebase and
'.git/rebase-merge' during an interactive rebase, and git-rebase.sh
checks the presence of the 'interactive' file only in
'.git/rebase-merge'.  It's not clear to me yet whether it's possible
to have a '.git/rebase-merge' without the file 'interactive' in it.
If it is possible, then I'd like to know with which commands and under
what circumstances.  If it isn't, then we wouldn't have to look for
the file at all, because checking the presence of the directory would
be enough.


"git rebase --merge" will use ".git/rebase-merge" without creating the
"interactive" flag.


Oh, right, thanks.  I should have remembered, I wrote the test of the
prompt script for that case...
(On a related note: is it possible to have a '.git/rebase-apply'
directory, but neither 'rebasing' or 'applying' files within?  The
prompt script has a long if-elif chain with such a branch, and I
remember wondering how I could trigger it for testing.)

Anyway, so this could be something like (modulo likely whitespace damage):

diff --git a/contrib/completion/git-completion.bash
b/contrib/completion/git-completion.bash
index 07c34ef913..fac01d6985 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1667,7 +1667,10 @@ _git_push ()
  _git_rebase ()
  {
local dir="$(__gitdir)"
-   if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
+   if [ -f "$dir"/rebase-merge/interactive ]; then
+   __gitcomp "--continue --skip --abort --edit-todo"
+   return
+   elif [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
__gitcomp "--continue --skip --abort"
return
fi


Best,
Gábor

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2] completion: Add '--edit-todo' to rebase

2015-08-01 Thread SZEDER Gábor


Quoting Thomas Braun :


Am 31.07.2015 um 12:16 schrieb SZEDER Gábor:

Anyway, so this could be something like (modulo likely whitespace damage):

diff --git a/contrib/completion/git-completion.bash
b/contrib/completion/git-completion.bash
index 07c34ef913..fac01d6985 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1667,7 +1667,10 @@ _git_push ()
  _git_rebase ()
  {
  local dir="$(__gitdir)"
-if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
+if [ -f "$dir"/rebase-merge/interactive ]; then
+__gitcomp "--continue --skip --abort --edit-todo"
+return
+elif [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
  __gitcomp "--continue --skip --abort"
  return
  fi


This looks much better than my attempt. Thanks.

How is the protocol now? Do I reroll and add
Helped-By: John Keeping 
Completely-Overhauled-And-Properly-Implemented: SZEDER Gábor  



Ugh :)  I'm quite happy with Helped-by, if you do a proper reroll  
after trying it out to see that it indeed does what it should.



Thanks,
Gábor
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCHv3 1/2] config: add '--names-only' option to list only variable names

2015-08-10 Thread SZEDER Gábor
'git config' can only show values or name-value pairs, so if a shell
script needs the names of set config variables it has to run 'git config
--list' or '--get-regexp' and parse the output to separate config
variable names from their values.  However, such a parsing can't cope
with multi-line values.  Though 'git config' can produce null-terminated
output for newline-safe parsing, that's of no use in such a case, becase
shells can't cope with null characters.

Even our own bash completion script suffers from these issues.

Help the completion script, and shell scripts in general, by introducing
the '--names-only' option to modify the output of '--list' and
'--get-regexp' to list only the names of config variables, so they don't
have to perform error-prone post processing to separate variable names
from their values anymore.

Signed-off-by: SZEDER Gábor 
---
 Documentation/git-config.txt   | 10 +++---
 builtin/config.c   | 14 --
 contrib/completion/git-completion.bash |  1 +
 t/t1300-repo-config.sh | 22 ++
 4 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 02ec096faa..ba76097c06 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -14,13 +14,13 @@ SYNOPSIS
 'git config' [] [type] --replace-all name value [value_regex]
 'git config' [] [type] [-z|--null] --get name [value_regex]
 'git config' [] [type] [-z|--null] --get-all name [value_regex]
-'git config' [] [type] [-z|--null] --get-regexp name_regex 
[value_regex]
+'git config' [] [type] [-z|--null] [--names-only] --get-regexp 
name_regex [value_regex]
 'git config' [] [type] [-z|--null] --get-urlmatch name URL
 'git config' [] --unset name [value_regex]
 'git config' [] --unset-all name [value_regex]
 'git config' [] --rename-section old_name new_name
 'git config' [] --remove-section name
-'git config' [] [-z|--null] -l | --list
+'git config' [] [-z|--null] [--names-only] -l | --list
 'git config' [] --get-color name [default]
 'git config' [] --get-colorbool name [stdout-is-tty]
 'git config' [] -e | --edit
@@ -159,7 +159,7 @@ See also <>.
 
 -l::
 --list::
-   List all variables set in config file.
+   List all variables set in config file, along with their values.
 
 --bool::
'git config' will ensure that the output is "true" or "false"
@@ -190,6 +190,10 @@ See also <>.
output without getting confused e.g. by values that
contain line breaks.
 
+--names-only::
+   Output only the names of config variables for `--list` or
+   `--get-regexp`.
+
 --get-colorbool name [stdout-is-tty]::
 
Find the color setting for `name` (e.g. `color.diff`) and output
diff --git a/builtin/config.c b/builtin/config.c
index 7188405f7e..307980ab50 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -13,6 +13,7 @@ static char *key;
 static regex_t *key_regexp;
 static regex_t *regexp;
 static int show_keys;
+static int omit_values;
 static int use_key_regexp;
 static int do_all;
 static int do_not_match;
@@ -78,6 +79,7 @@ static struct option builtin_config_options[] = {
OPT_BIT(0, "path", &types, N_("value is a path (file or directory 
name)"), TYPE_PATH),
OPT_GROUP(N_("Other")),
OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
+   OPT_BOOL(0, "names-only", &omit_values, N_("names only")),
OPT_BOOL(0, "includes", &respect_includes, N_("respect include 
directives on lookup")),
OPT_END(),
 };
@@ -91,7 +93,7 @@ static void check_argc(int argc, int min, int max) {
 
 static int show_all_config(const char *key_, const char *value_, void *cb)
 {
-   if (value_)
+   if (!omit_values && value_)
printf("%s%c%s%c", key_, delim, value_, term);
else
printf("%s%c", key_, term);
@@ -117,6 +119,10 @@ static int format_config(struct strbuf *buf, const char 
*key_, const char *value
strbuf_addstr(buf, key_);
must_print_delim = 1;
}
+   if (omit_values) {
+   strbuf_addch(buf, term);
+   return 0;
+   }
if (types == TYPE_INT)
sprintf(value, "%"PRId64,
git_config_int64(key_, value_ ? value_ : ""));
@@ -549,7 +555,11 @@ int cmd_config(int argc, const char **argv, const char 
*prefix)
default:
usage_with_options(builtin_config_usage, 
builtin_config

[PATCHv3 0/2] 'git config --names-only' to help the completion script

2015-08-10 Thread SZEDER Gábor
This is a reroll of 'sg/config-name-only'.

  * Instead of the two new listing options of the previous round add one
new option '--names-only' to modify the output of '--list' and
'--get-regexp' options, as suggested in previous discussions.
  * Reorganized the commit messages: don't go into details of the
completion bug in the first patch modifying builtin/config.c, talk
about that in the second patch.

SZEDER Gábor (2):
  config: add '--names-only' option to list only variable names
  completion: list variable names reliably with 'git config
--names-only'

 Documentation/git-config.txt   | 10 +++---
 builtin/config.c   | 14 --
 contrib/completion/git-completion.bash | 16 
 t/t1300-repo-config.sh | 22 ++
 4 files changed, 45 insertions(+), 17 deletions(-)

-- 
2.5.0.245.gff6622b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCHv3 2/2] completion: list variable names reliably with 'git config --names-only'

2015-08-10 Thread SZEDER Gábor
Recenty I created a multi-line branch description with '.' and '='
characters on one of the lines, and noticed that fragments of that line
show up when completing set variable names for 'git config', e.g.:

  $ git config --get branch.b.description
  Branch description to fool the completion script with a
  second line containing dot . and equals = characters.
  $ git config --unset 
  ...
  second line containing dot . and equals
  ...

The completion script runs 'git config --list' and processes its output
to strip the values and keep only the variable names.  It does so by
looking for lines containing '.' and '=' and outputting everything
before the '=', which was fooled by my multi-line branch description.

A similar issue exists with aliases and pretty format aliases with
multi-line values, but in that case 'git config --get-regexp' is run and
lines in its output are simply stripped after the first space, so
subsequent lines don't even have to contain '.' and '=' to fool the
completion script.

Use the new '--names-only' option added in the previous commit to list
config variable names reliably in both cases, without error-prone post
processing.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 15 +++
 1 file changed, 3 insertions(+), 12 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 6eaab141e2..7200828fc4 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -744,9 +744,8 @@ __git_compute_porcelain_commands ()
 __git_get_config_variables ()
 {
local section="$1" i IFS=$'\n'
-   for i in $(git --git-dir="$(__gitdir)" config --get-regexp 
"^$section\..*" 2>/dev/null); do
-   i="${i#$section.}"
-   echo "${i/ */}"
+   for i in $(git --git-dir="$(__gitdir)" config --names-only --get-regexp 
"^$section\..*" 2>/dev/null); do
+   echo "${i#$section.}"
done
 }
 
@@ -1774,15 +1773,7 @@ __git_config_get_set_variables ()
c=$((--c))
done
 
-   git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
-   while read -r line
-   do
-   case "$line" in
-   *.*=*)
-   echo "${line/=*/}"
-   ;;
-   esac
-   done
+   git --git-dir="$(__gitdir)" config $config_file --names-only --list 
2>/dev/null
 }
 
 _git_config ()
-- 
2.5.0.245.gff6622b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCHv3 1/2] config: add '--names-only' option to list only variable names

2015-08-12 Thread SZEDER Gábor


Quoting Junio C Hamano :


SZEDER Gábor  writes:


'git config' can only show values or name-value pairs, so if a shell
script needs the names of set config variables it has to run 'git config
--list' or '--get-regexp' and parse the output to separate config
variable names from their values.  However, such a parsing can't cope
with multi-line values.  Though 'git config' can produce null-terminated
output for newline-safe parsing, that's of no use in such a case, becase


s/becase/because/;


OK.


shells can't cope with null characters.

Even our own bash completion script suffers from these issues.

Help the completion script, and shell scripts in general, by introducing
the '--names-only' option to modify the output of '--list' and
'--get-regexp' to list only the names of config variables, so they don't
have to perform error-prone post processing to separate variable names
from their values anymore.


I agree with Peff that "--names-only" has a subtle difference with
an existing and well known subcommand option and it would be a bit
irritating to remember which options is for which command.


OK.


diff --git a/builtin/config.c b/builtin/config.c
index 7188405f7e..307980ab50 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -13,6 +13,7 @@ static char *key;
 static regex_t *key_regexp;
 static regex_t *regexp;
 static int show_keys;
+static int omit_values;
 static int use_key_regexp;
 static int do_all;
 static int do_not_match;
...
@@ -91,7 +93,7 @@ static void check_argc(int argc, int min, int max) {

 static int show_all_config(const char *key_, const char *value_, void *cb)
 {
-   if (value_)
+   if (!omit_values && value_)


H.  As we have "show_keys",

if (show_values && value_)

would be a lot more intuitive, no?


Well, the name 'omit_values' was suggested by Peff after the first  
round.  I'm happy to rename it to whatever you agree upon :)



@@ -117,6 +119,10 @@ static int format_config(struct strbuf *buf,  
const char *key_, const char *value

strbuf_addstr(buf, key_);
must_print_delim = 1;
}
+   if (omit_values) {
+   strbuf_addch(buf, term);
+   return 0;
+   }


This hunk makes me wonder what the assignment to "must_print_delim"
is about.  When the code is told to show only keys and not values,
it shouldn't even have to worry about key_delim, but that assignment
is done to control exactly that.  It happens that you are lucky that
you can "return 0" early here so that the assignment does not have
any effect, but still conceptually the code structure is made ugly
by this patch.


How about restructuring the function like this?  Perhaps even better  
than a tri-state toggle would be.
(showing the result instead of the diff, because all the indentation  
changes make the diff hard to read).


static int format_config(struct strbuf *buf, const char *key_, const  
char *value_)

{
strbuf_init(buf, 0);

if (show_keys)
strbuf_addstr(buf, key_);
if (!omit_values) {  // or show_values
int must_free_vptr = 0;
int must_add_delim = show_keys;
char value[256];
const char *vptr = value;

if (types == TYPE_INT)
sprintf(value, "%"PRId64,
git_config_int64(key_, value_ ? value_ : ""));
else if (types == TYPE_BOOL)
vptr = git_config_bool(key_, value_) ? "true"  
: "false";

else if (types == TYPE_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key_, value_, &is_bool);
if (is_bool)
vptr = v ? "true" : "false";
else
sprintf(value, "%d", v);
} else if (types == TYPE_PATH) {
if (git_config_pathname(&vptr, key_, value_) < 0)
return -1;
must_free_vptr = 1;
} else if (value_) {
vptr = value_;
} else {
/* Just show the key name */
vptr = "";
must_add_delim = 0;
}

if (must_add_delim)
strbuf_addch(buf, key_delim);
strbuf_addstr(buf, vptr);

if (must_free_vptr)
free((char *)vptr);
}
strbuf_addch(buf, term);
return 0;
}




Isn't it more like the existing "show_keys" can be replaced/enhan

[PATCH] describe: make '--always' fallback work after '--exact-match' failed

2015-08-20 Thread SZEDER Gábor
'git describe [...] --always' should always show the unique abbreviated
object name as a fallback when the given commit cannot be described with
the given set of options, see da2478dbb0 (describe --always: fall back
to showing an abbreviated object name, 2008-03-02).

However, this is not the case when the combination '--exact-match
--always' is given and the commit cannot be described, because in such
cases 'git describe' errors out, as if '--always' were not given at all.

Respect '--always' and print the unique abbreviated object name instead
of erroring out when the commit cannot be described with '--exact-match
--always'.

Signed-off-by: SZEDER Gábor 
---
 builtin/describe.c  | 2 +-
 t/t6120-describe.sh | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/describe.c b/builtin/describe.c
index a36c829e57..ce36032b1f 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -266,7 +266,7 @@ static void describe(const char *arg, int last_one)
return;
}
 
-   if (!max_candidates)
+   if (!always && !max_candidates)
die(_("no tag exactly matches '%s'"), 
sha1_to_hex(cmit->object.sha1));
if (debug)
fprintf(stderr, _("searching to describe %s\n"), arg);
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index c0e5b2a627..57d50156bb 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -113,6 +113,8 @@ check_describe A-3-* --long HEAD^^2
 check_describe c-7-* --tags
 check_describe e-3-* --first-parent --tags
 
+check_describe $(git rev-parse --short HEAD) --exact-match --always HEAD
+
 : >err.expect
 check_describe A --all A^0
 test_expect_success 'no warning was displayed for A' '
-- 
2.5.0.343.g6f38143

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Minor builtin 'git am' side-effect

2015-08-20 Thread SZEDER Gábor


Hi,

The format of the files '.git/rebase-apply/{next,last}' changed slightly
with the recent builtin 'git am' conversion: while these files were
newline-terminated when written by the scripted version, the ones written
by the builtin are not.

This probably  makes no difference for shell scripts looking at these
files, e.g.  our __git_ps1() handles both just fine.  However, it can
break C programs, when after strtol()ing the contents of the files they
get defensive and check for the terminating newline at *endptr (this is
how I noticed, as one of my pet projects did just that).

I'm not saying that the new behavior is bad and should be fixed; I merely
point it out and leave the rest for you to decide.

Best,
Gábor


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] t3020: fix typo in test description

2015-08-20 Thread SZEDER Gábor
Signed-off-by: SZEDER Gábor 
---
 t/t3020-ls-files-error-unmatch.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t3020-ls-files-error-unmatch.sh 
b/t/t3020-ls-files-error-unmatch.sh
index ca01053bcc..124e73b8e6 100755
--- a/t/t3020-ls-files-error-unmatch.sh
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -22,7 +22,7 @@ test_expect_success \
 'test_must_fail git ls-files --error-unmatch foo bar-does-not-match'
 
 test_expect_success \
-'git ls-files --error-unmatch should succeed eith matched paths.' \
+'git ls-files --error-unmatch should succeed with matched paths.' \
 'git ls-files --error-unmatch foo bar'
 
 test_done
-- 
2.5.0.415.g33d64ef

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] wt-status: move #include "pathspec.h" to the header

2015-08-20 Thread SZEDER Gábor
The declaration of 'struct wt_status' requires the declararion of 'struct
pathspec'.

Signed-off-by: SZEDER Gábor 
---
 wt-status.c | 1 -
 wt-status.h | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/wt-status.c b/wt-status.c
index 717fd48d13..c327fe8128 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,5 +1,4 @@
 #include "cache.h"
-#include "pathspec.h"
 #include "wt-status.h"
 #include "object.h"
 #include "dir.h"
diff --git a/wt-status.h b/wt-status.h
index e0a99f75c7..c9b3b744e9 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -4,6 +4,7 @@
 #include 
 #include "string-list.h"
 #include "color.h"
+#include "pathspec.h"
 
 enum color_wt_status {
WT_STATUS_HEADER = 0,
-- 
2.5.0.415.g33d64ef

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] config: restructure format_config() for better control flow

2015-08-20 Thread SZEDER Gábor
Commit 578625fa91 (config: add '--name-only' option to list only
variable names, 2015-08-10) modified format_config() such that it
returned from the middle of the function when showing only keys,
resulting in ugly code structure.

Reorganize the if statements and dealing with the key-value delimiter to
make the function easier to read.

Signed-off-by: SZEDER Gábor 
---

> The topic is now in 'next'; I think I've locally fixed it up for
> these when I originally queued them a few days ago, so if there are
> any remaining issues, please throw incremental polishing patches.

OK, though it's not a major issue, I think this is still worth doing on
top.

 builtin/config.c | 78 +++-
 1 file changed, 37 insertions(+), 41 deletions(-)

diff --git a/builtin/config.c b/builtin/config.c
index 631db458ec..810e104224 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -108,52 +108,48 @@ struct strbuf_list {
 
 static int format_config(struct strbuf *buf, const char *key_, const char 
*value_)
 {
-   int must_free_vptr = 0;
-   int must_print_delim = 0;
-   char value[256];
-   const char *vptr = value;
-
strbuf_init(buf, 0);
 
-   if (show_keys) {
+   if (show_keys)
strbuf_addstr(buf, key_);
-   must_print_delim = 1;
-   }
-   if (omit_values) {
-   strbuf_addch(buf, term);
-   return 0;
-   }
-   if (types == TYPE_INT)
-   sprintf(value, "%"PRId64,
-   git_config_int64(key_, value_ ? value_ : ""));
-   else if (types == TYPE_BOOL)
-   vptr = git_config_bool(key_, value_) ? "true" : "false";
-   else if (types == TYPE_BOOL_OR_INT) {
-   int is_bool, v;
-   v = git_config_bool_or_int(key_, value_, &is_bool);
-   if (is_bool)
-   vptr = v ? "true" : "false";
-   else
-   sprintf(value, "%d", v);
-   } else if (types == TYPE_PATH) {
-   if (git_config_pathname(&vptr, key_, value_) < 0)
-   return -1;
-   must_free_vptr = 1;
-   } else if (value_) {
-   vptr = value_;
-   } else {
-   /* Just show the key name */
-   vptr = "";
-   must_print_delim = 0;
-   }
+   if (!omit_values) {
+   int must_free_vptr = 0;
+   int must_add_delim = show_keys;
+   char value[256];
+   const char *vptr = value;
 
-   if (must_print_delim)
-   strbuf_addch(buf, key_delim);
-   strbuf_addstr(buf, vptr);
+   if (types == TYPE_INT)
+   sprintf(value, "%"PRId64,
+   git_config_int64(key_, value_ ? value_ : ""));
+   else if (types == TYPE_BOOL)
+   vptr = git_config_bool(key_, value_) ? "true" : "false";
+   else if (types == TYPE_BOOL_OR_INT) {
+   int is_bool, v;
+   v = git_config_bool_or_int(key_, value_, &is_bool);
+   if (is_bool)
+   vptr = v ? "true" : "false";
+   else
+   sprintf(value, "%d", v);
+   } else if (types == TYPE_PATH) {
+   if (git_config_pathname(&vptr, key_, value_) < 0)
+   return -1;
+   must_free_vptr = 1;
+   } else if (value_) {
+   vptr = value_;
+   } else {
+   /* Just show the key name */
+   vptr = "";
+   must_add_delim = 0;
+   }
+
+   if (must_add_delim)
+   strbuf_addch(buf, key_delim);
+   strbuf_addstr(buf, vptr);
+
+   if (must_free_vptr)
+   free((char *)vptr);
+   }
strbuf_addch(buf, term);
-
-   if (must_free_vptr)
-   free((char *)vptr);
return 0;
 }
 
-- 
2.5.0.415.g33d64ef

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] wt-status: move #include "pathspec.h" to the header

2015-08-20 Thread SZEDER Gábor


Quoting Junio C Hamano :


SZEDER Gábor  writes:


The declaration of 'struct wt_status' requires the declararion of 'struct
pathspec'.


I think this is fine.

I am guessing that you are saying it is wrong to force wt-status.c
to include pathspec.h before including wt-status.h; I am fine with
that.



This is a tangent, but the above is different from saying that with
a single liner test.c that has

  #include "wt-status.h"

your compilation "cc -c test.c" should succeed.


Sort of, but not quite, rather a combination of both.  My point is a
builtin command, starting with #include "builtin.h" as it should, that
wanted to use only struct wt_status_state and wt_status_get_state()
from the wt-status machinery and has nothing to do with pathspecs, yet
it's not sufficient to #include "wt-status.h", because the required
pathspec.h is included in the .c file instead of the header itsef.
(Actually, that's exactly how I noticed.)



But for that goal,
direct inclusion of  to wt-status.h is also iffy.  We
include the system headers from git-compat-util.h because some
platforms are picky about order of inclusion of system header files
and definitions of feature test macros.

Right now, the codebase is correct only because it is NOT our goal
to guarantee that such a single-liner test.c file compiles.
Instead, everybody is instructed to #include "git-compat-util.h" as
the first thing, either directly or indirectly.

So in that sense, we should also remove that inclusion from
wt-status.h, I think.

Thanks.


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] describe: make '--always' fallback work after '--exact-match' failed

2015-08-21 Thread SZEDER Gábor


Quoting Junio C Hamano :


SZEDER Gábor  writes:


'git describe [...] --always' should always show the unique abbreviated
object name as a fallback when the given commit cannot be described with
the given set of options, see da2478dbb0 (describe --always: fall back
to showing an abbreviated object name, 2008-03-02).

However, this is not the case when the combination '--exact-match
--always' is given and the commit cannot be described, because in such
cases 'git describe' errors out, as if '--always' were not given at all.

Respect '--always' and print the unique abbreviated object name instead
of erroring out when the commit cannot be described with '--exact-match
--always'.

Signed-off-by: SZEDER Gábor 


Well, that can be argued both ways.  Your patch introduces a
regression, as "--exact-match" is an instruction to error out when
no tag exactly matches, and you deliberately break that.


This patch doesn't break '--exact-match', in fact doesn't modify it at all
when it's on its own or combined with other options, but it makes
'--exact-match --always' finally work.

'git describe' errors out by default if it can't describe the given
commit.  So if a user wants an exact match or an error otherwise, then he
should not give '--always' at all, because that's the instruction to not
error out but give the abbreviated object name instead.

Why should '--exact-match' be any different from the other options that
tell 'git describe' what to use for the description?  Why should
'--always' not work with '--exact-match', when it works in the other
cases?

Consider '--contains': it should find a tag that comes after the given
commit or error out if such a tag doesn't exist.  Now, in current git.git:

  $ git describe --contains master
  fatal: cannot describe 'ff86faf2fa02bc21933c9e1dcc75c8d81b3e104a'
  $ git describe --contains --always master
  ff86faf2fa

Or the default behavior without any options: it should find a tag
reachable from the given commit or error out, but what if we pass in a
commit before the first tag?  It recommends '--always':

  $ git describe e83c516
  fatal: No tags can describe 'e83c5163316f89bfbde7d9ab23ca2e25604af290'.
  Try --always, or create some tags.
  $ git describe --always e83c516
  e83c516331



My knee-jerk reaction is that the most sensible way forward is to
make --exact-match and --always mutually incompatible.


That would be wrong, it's perfectly valid to ask for an exactly matching
tag or, if there is no such tag, the abbreviated object name as a
fallback.

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 2/3] format_config: simplify buffer handling

2015-08-21 Thread SZEDER Gábor


Quoting Jeff King :


When formatting a config value into a strbuf, we may end
up stringifying it into a fixed-size buffer using sprintf,
and then copying that buffer into the strbuf. We can
eliminate the middle-man (and drop some calls to sprintf!)
by writing directly to the strbuf.

The reason it was written this way in the first place is
that we need to know before writing the value whether to
insert a delimiter. Instead of delaying the write of the
value, we speculatively write the delimiter, and roll it
back in the single case that cares.

Signed-off-by: Jeff King 
---
I admit the rollback is a little gross.


Indeed it is, but I'm for it, as it gets rit of so much more
other grossness, i.e. the fixed-size buffer and vptr stuff
and the two must_do_this variables.



builtin/config.c | 38 --
1 file changed, 16 insertions(+), 22 deletions(-)

diff --git a/builtin/config.c b/builtin/config.c
index 91aa56f..04befce 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -111,41 +111,35 @@ static int format_config(struct strbuf *buf,  
const char *key_, const char *value

if (show_keys)
strbuf_addstr(buf, key_);
if (!omit_values) {
-   int must_free_vptr = 0;
-   int must_add_delim = show_keys;
-   char value[256];
-   const char *vptr = value;
+   if (show_keys)
+   strbuf_addch(buf, key_delim);

if (types == TYPE_INT)
-   sprintf(value, "%"PRId64,
-   git_config_int64(key_, value_ ? value_ : ""));
+   strbuf_addf(buf, "%"PRId64,
+   git_config_int64(key_, value_ ? value_ : 
""));
else if (types == TYPE_BOOL)
-   vptr = git_config_bool(key_, value_) ? "true" : "false";
+   strbuf_addstr(buf, git_config_bool(key_, value_) ?
+ "true" : "false");
else if (types == TYPE_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key_, value_, &is_bool);
if (is_bool)
-   vptr = v ? "true" : "false";
+   strbuf_addstr(buf, v ? "true" : "false");
else
-   sprintf(value, "%d", v);
+   strbuf_addf(buf, "%d", v);
} else if (types == TYPE_PATH) {
-   if (git_config_pathname(&vptr, key_, value_) < 0)
+   const char *v;
+   if (git_config_pathname(&v, key_, value_) < 0)
return -1;
-   must_free_vptr = 1;
+   strbuf_addstr(buf, v);
+   free((char *)v);
} else if (value_) {
-   vptr = value_;
+   strbuf_addstr(buf, value_);
} else {
-   /* Just show the key name */
-   vptr = "";
-   must_add_delim = 0;
+   /* Just show the key name; back out delimiter */
+   if (show_keys)
+   strbuf_setlen(buf, buf->len - 1);
}
-
-   if (must_add_delim)
-   strbuf_addch(buf, key_delim);
-   strbuf_addstr(buf, vptr);
-
-   if (must_free_vptr)
-   free((char *)vptr);
}
strbuf_addch(buf, term);
return 0;
--
2.5.0.680.g69e7703


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] describe --contains: default to HEAD when no commit-ish is given

2015-08-21 Thread SZEDER Gábor
'git describe --contains' doesn't default to HEAD when no commit is
given, and it doesn't produce any output, not even an error:

  ~/src/git ((v2.5.0))$ ./git describe --contains
  ~/src/git ((v2.5.0))$ ./git describe --contains HEAD
  v2.5.0^0

Unlike other 'git describe' options, the '--contains' code path is
implemented by calling 'name-rev' with a bunch of options plus all the
commit-ishes that were passed to 'git describe'.  If no commit-ish was
present, then 'name-rev' got invoked with none, which then leads to the
behavior illustrated above.

Porcelain commands usually default to HEAD when no commit-ish is given,
and 'git describe' already does so in all other cases, so it should do
so with '--contains' as well.

Pass HEAD to 'name-rev' when no commit-ish is given on the command line
to make '--contains' behave consistently with other 'git describe'
options.

'git describe's short help already indicates that the commit-ish is
optional, but the synopsis in the man page doesn't, so update it
accordingly as well.

Signed-off-by: SZEDER Gábor 
---
 Documentation/git-describe.txt |  4 ++--
 builtin/describe.c | 11 +++
 t/t6120-describe.sh|  8 
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index e045fc73f8..c8f28c8c86 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -9,7 +9,7 @@ git-describe - Describe a commit using the most recent tag 
reachable from it
 SYNOPSIS
 
 [verse]
-'git describe' [--all] [--tags] [--contains] [--abbrev=] ...
+'git describe' [--all] [--tags] [--contains] [--abbrev=] [...]
 'git describe' [--all] [--tags] [--contains] [--abbrev=] --dirty[=]
 
 DESCRIPTION
@@ -27,7 +27,7 @@ see the -a and -s options to linkgit:git-tag[1].
 OPTIONS
 ---
 ...::
-   Commit-ish object names to describe.
+   Commit-ish object names to describe.  Defaults to HEAD if omitted.
 
 --dirty[=]::
Describe the working tree.
diff --git a/builtin/describe.c b/builtin/describe.c
index ce36032b1f..0e31ac5cb9 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -443,10 +443,13 @@ int cmd_describe(int argc, const char **argv, const char 
*prefix)
if (pattern)
argv_array_pushf(&args, "--refs=refs/tags/%s", 
pattern);
}
-   while (*argv) {
-   argv_array_push(&args, *argv);
-   argv++;
-   }
+   if (argc)
+   while (*argv) {
+   argv_array_push(&args, *argv);
+   argv++;
+   }
+   else
+   argv_array_push(&args, "HEAD");
return cmd_name_rev(args.argc, args.argv, prefix);
}
 
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 57d50156bb..bf52a0a1cc 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -115,6 +115,14 @@ check_describe e-3-* --first-parent --tags
 
 check_describe $(git rev-parse --short HEAD) --exact-match --always HEAD
 
+test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
+   echo "A^0" >expect &&
+   git checkout A &&
+   test_when_finished "git checkout -" &&
+   git describe --contains >actual &&
+   test_cmp expect actual
+'
+
 : >err.expect
 check_describe A --all A^0
 test_expect_success 'no warning was displayed for A' '
-- 
2.5.0.416.g84b07b4

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [RFC/PATCH] contrib: teach completion about git-worktree options and arguments

2015-08-21 Thread SZEDER Gábor


Quoting Junio C Hamano :


Eric Sunshine  writes:

On Thu, Jul 23, 2015 at 4:49 PM, Eric Sunshine  
 wrote:

Complete subcommands 'add' and 'prune', as well as their respective
options --force, --detach, --dry-run, --verbose, and --expire. Also
complete 'refname' in "git worktree add [-b ] 
".


Ping[1]?

[1]: http://article.gmane.org/gmane.comp.version-control.git/274526


Ping indeed?



Yeah, right, sorry.  Non-subscribed occasional gmane-reader here amidst
job hunting, so thanks for the ping.  And the re-ping...


Signed-off-by: Eric Sunshine 
---

This is RFC since this is my first foray into the Git completion script,
and there are likely better and more idiomatic ways to accomplish this.


Using __git_find_on_cmdline() to find subcommands and case
"$subcommand,$cur" to limit the number of nested case statements is as
idiomatic as you can get in the completion script.

And I hear you, that " first,  second" syntax makes it way
too complicated, especially since they can follow '-b '.
I wrote a completion function for 'git worktree' as well, turns out a week
or two before you posted this, but I never submitted it as it was way too
convoluted.  Anyway, will send it in reply to this, just for reference.



 contrib/completion/git-completion.bash | 32  


 1 file changed, 32 insertions(+)

diff --git a/contrib/completion/git-completion.bash  
b/contrib/completion/git-completion.bash

index c97c648..07c34ef 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2564,6 +2564,38 @@ _git_whatchanged ()
_git_log
 }

+_git_worktree ()
+{
+   local subcommands='add prune'
+   local subcommand="$(__git_find_on_cmdline "$subcommands")"
+   local c=2 n=0 refpos=2


A more descriptive variable name for 'n' would be great.


+   if [ -z "$subcommand" ]; then
+   __gitcomp "$subcommands"
+   else
+   case "$subcommand,$cur" in
+   add,--*)
+   __gitcomp "--force --detach"


We usually don't offer '--force', because that option must be
handled with care.


+   ;;
+   add,*)
+   while [ $c -lt $cword ]; do
+   case "${words[c]}" in
+   --*) ;;
+   -[bB]) ((refpos++)) ;;
+   *) ((n++)) ;;
+   esac
+   ((c++))
+   done
+   if [ $n -eq $refpos ]; then


I suppose here you wanted to calculate where (i.e. at which word on
the command line) we should offer refs and fall back to bash builtin
filename completion otherwise.  It works well in the common cases,
but:

  - it doesn't offer refs after -b or -B

  - it gets fooled by options to the git command, e.g. 'git
--git-dir=.git worktree add ' offers refs instead of files,
'git --git-dir=.git worktree add ../some/path ' offers
refs, etc.


+   __gitcomp_nl "$(__git_refs)"
+   fi
+   ;;
+   prune,--*)
+   __gitcomp "--dry-run --verbose --expire"
+   ;;
+   esac
+   fi
+}
+
 __git_main ()
 {
local i c=1 command __git_dir
--
2.5.0.rc3.407.g68aafd0



--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PoC PATCH] completion: support 'git worktree'

2015-08-21 Thread SZEDER Gábor
Signed-off-by: SZEDER Gábor 
---

> I wrote a completion function for 'git worktree' as well, turns out a week
> or two before you posted this, but I never submitted it as it was way too
> convoluted.  Anyway, will send it in reply to this, just for reference.

And here it is.
>From the number of indentation levels and comment lines you can see why
I haven't submitted this patch yet :)

OTOH it offers refs for -b and -B, and there are only fairly narrow
corner cases when 'git --options' can fool it (but that's a general
issue with __git_find_on_cmdline(), I wouldn't go into that).

 contrib/completion/git-completion.bash | 59 ++
 1 file changed, 59 insertions(+)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index c97c648d7e..20a17e2c50 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2564,6 +2564,65 @@ _git_whatchanged ()
_git_log
 }
 
+_git_worktree ()
+{
+   local subcommand subcommand_idx sc c=1
+   local subcommands="add prune"
+
+   while [ $c -lt $cword ] && [ -z "$subcommand" ]; do
+   for sc in $subcommands; do
+   if [ "$sc" = "${words[c]}" ]; then
+   subcommand=$sc
+   subcommand_idx=$c
+   break
+   fi
+   done
+   ((c++))
+   done
+
+   case "$subcommand,$cur" in
+   ,*)
+   __gitcomp "$subcommands"
+   ;;
+   add,--*)
+   __gitcomp "--detach"
+   ;;
+   add,*)
+   case "$prev" in
+   -b|-B)
+   __gitcomp_nl "$(__git_refs)"
+   ;;
+   -*) # $prev is an option without argument: have to complete
+   # the path for the new worktree, fall back to bash
+   # filename completion
+   ;;
+   *)  # $prev is not an option, so it must be either the
+   # 'add' subcommand, an argument of an option (e.g.
+   # branch for -b|-B), or the path for the new worktree
+   if [ $cword -eq $((subcommand_idx+1)) ]; then
+   # right after the 'add' subcommand, have to
+   # complete the path
+   :
+   else
+   case "${words[cword-2]}" in
+   -b|-B)  # after '-b ', have to complete
+   # the path
+   ;;
+   *)  # after the path, have to complete the
+   # branch to be checked out
+   __gitcomp_nl "$(__git_refs)"
+   ;;
+   esac
+   fi
+   ;;
+   esac
+   ;;
+   prune,--*)
+   __gitcomp "--dry-run --verbose --expire"
+   ;;
+   esac
+}
+
 __git_main ()
 {
local i c=1 command __git_dir
-- 
2.5.0.418.gdd37a9b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] am: terminate state files with a newline

2015-08-23 Thread SZEDER Gábor

Hi,

Quoting Paul Tan :


Did we ever explictly allow external programs to poke around the
contents of the .git/rebase-apply directory? I think it may not be so
good, as it means that it may not be possible to switch the storage
format in the future (e.g. to allow atomic modifications, maybe?) :-/ .


Think of e.g. libgit2, JGit/EGit and all the other git implementations.
They should be able to look everywhere in .git, shouldn't they?

I don't think we will just "switch" the storage format of any parts of the
repo.  Whatever new formats may come (ref backends, index v5, pack v4),
they will be an opt-in feature for a long time before becoming default,
and there must be an even longer deprecation period before the old format
gets phased out, if ever.


Gábor
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] describe --contains: default to HEAD when no commit-ish is given

2015-08-24 Thread SZEDER Gábor


Quoting Junio C Hamano :

@@ -443,10 +443,13 @@ int cmd_describe(int argc, const char **argv,  
const char *prefix)

if (pattern)
argv_array_pushf(&args, "--refs=refs/tags/%s", 
pattern);
}
-   while (*argv) {
-   argv_array_push(&args, *argv);
-   argv++;
-   }
+   if (argc)


"What would this code do to 'describe --all --contains'?" was my
knee-jerk reaction, but the options are all parsed by this code and
nothing is delegated to name-rev, so 'if (!argc)' here is truly the
lack of any revisions to be described, which is good.


Exactly.  parse-opts removes all --options from argv as it processes
them, barfs at --unknown-options, so all what remains must be treated
as a commit-ish.  And if nothing is left, well, then there was none
given.


+   while (*argv) {
+   argv_array_push(&args, *argv);
+   argv++;
+   }
+   else
+   argv_array_push(&args, "HEAD");


By the way, I usually prefer a fatter 'else' clause when everything
else is equal, i.e.

if (!argc)
argv_array_push(&args, "HEAD"); /* default to HEAD */
else {
while (*argv) {
...
}
}

because it is easy to miss tiny else-clause while reading code, but
it is harder to miss tiny then-clause.  In this case, however, the
while loop can be replaced with argv_array_pushv() these days, so
perhaps

if (!argc)
argv_array_push(&args, "HEAD"); /* default to HEAD ... */
else
argv_array_pushv(&args, argv); /* or relay what we got */

or something?


Indeed, I didn't notice argv_array_pushv() being added, log tells me
it happened quite recently.  I suppose with both branches becoming a
one-liner the order of them can remain what it was in the patch,
this sparing the negation from 'if (!argc)'.

v2 comes in a minute.


Gábor

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2] describe --contains: default to HEAD when no commit-ish is given

2015-08-24 Thread SZEDER Gábor
'git describe --contains' doesn't default to HEAD when no commit is
given, and it doesn't produce any output, not even an error:

  ~/src/git ((v2.5.0))$ ./git describe --contains
  ~/src/git ((v2.5.0))$ ./git describe --contains HEAD
  v2.5.0^0

Unlike other 'git describe' options, the '--contains' code path is
implemented by calling 'name-rev' with a bunch of options plus all the
commit-ishes that were passed to 'git describe'.  If no commit-ish was
present, then 'name-rev' got invoked with none, which then leads to the
behavior illustrated above.

Porcelain commands usually default to HEAD when no commit-ish is given,
and 'git describe' already does so in all other cases, so it should do
so with '--contains' as well.

Pass HEAD to 'name-rev' when no commit-ish is given on the command line
to make '--contains' behave consistently with other 'git describe'
options.  While at it, use argv_array_pushv() instead of the loop to
pass commit-ishes to 'git name-rev'.

'git describe's short help already indicates that the commit-ish is
optional, but the synopsis in the man page doesn't, so update it
accordingly as well.

Signed-off-by: SZEDER Gábor 
---
 Documentation/git-describe.txt | 4 ++--
 builtin/describe.c | 8 
 t/t6120-describe.sh| 8 
 3 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index e045fc73f8..c8f28c8c86 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -9,7 +9,7 @@ git-describe - Describe a commit using the most recent tag 
reachable from it
 SYNOPSIS
 
 [verse]
-'git describe' [--all] [--tags] [--contains] [--abbrev=] ...
+'git describe' [--all] [--tags] [--contains] [--abbrev=] [...]
 'git describe' [--all] [--tags] [--contains] [--abbrev=] --dirty[=]
 
 DESCRIPTION
@@ -27,7 +27,7 @@ see the -a and -s options to linkgit:git-tag[1].
 OPTIONS
 ---
 ...::
-   Commit-ish object names to describe.
+   Commit-ish object names to describe.  Defaults to HEAD if omitted.
 
 --dirty[=]::
Describe the working tree.
diff --git a/builtin/describe.c b/builtin/describe.c
index ce36032b1f..9dadfb58c8 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -443,10 +443,10 @@ int cmd_describe(int argc, const char **argv, const char 
*prefix)
if (pattern)
argv_array_pushf(&args, "--refs=refs/tags/%s", 
pattern);
}
-   while (*argv) {
-   argv_array_push(&args, *argv);
-   argv++;
-   }
+   if (argc)
+   argv_array_pushv(&args, argv);
+   else
+   argv_array_push(&args, "HEAD");
return cmd_name_rev(args.argc, args.argv, prefix);
}
 
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 57d50156bb..bf52a0a1cc 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -115,6 +115,14 @@ check_describe e-3-* --first-parent --tags
 
 check_describe $(git rev-parse --short HEAD) --exact-match --always HEAD
 
+test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
+   echo "A^0" >expect &&
+   git checkout A &&
+   test_when_finished "git checkout -" &&
+   git describe --contains >actual &&
+   test_cmp expect actual
+'
+
 : >err.expect
 check_describe A --all A^0
 test_expect_success 'no warning was displayed for A' '
-- 
2.5.0.418.gdd37a9b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] stash: Add stash.showFlag config variable

2015-08-27 Thread SZEDER Gábor
Hi,

I haven't made up my mind about this feature yet, but have a few
comments about its implementation.

> diff --git a/git-stash.sh b/git-stash.sh
> index 1d5ba7a..8432435 100755
> --- a/git-stash.sh
> +++ b/git-stash.sh
> @@ -33,6 +33,12 @@ else
> reset_color=
>  fi
>  
> +if git config --get stash.showflag > /dev/null 2> /dev/null; then
> + show_flag=$(git config --get stash.showflag)
> +else
> + show_flag=--stat
> +fi
> +

Forking and executing processes are costly on some important platforms
we care about, so we should strive to avoid them whenever possible.

 - This hunk runs the the exact same 'git config' command twice.  Run it
   only once, perhaps something like this:

 show_flag=$(git config --get stash.showflag || echo --stat)

   (I hope there are no obscure crazy 'echo' implemtations out there
   that might barf on the unknown option '--stat'...)

 - It runs 'git config' in the main code path, i.e. even for subcommands
   other than 'show'.  Run it only for 'git stash show'.

 - This config setting is not relevant if there were options given on the
   command line.  Run it only if there are no options given, i.e. when
   $FLAGS is empty.


>  no_changes () {
>   git diff-index --quiet --cached HEAD --ignore-submodules -- &&
>   git diff-files --quiet --ignore-submodules &&
> @@ -305,7 +311,7 @@ show_stash () {
>   ALLOW_UNKNOWN_FLAGS=t
>   assert_stash_like "$@"
>  
> - git diff ${FLAGS:---stat} $b_commit $w_commit
> + git diff ${FLAGS:-${show_flag}} $b_commit $w_commit
>  }
>  
>  show_help () {
> -- 
> 2.5.0
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] completion: fix completion after 'git -C path'

2015-10-05 Thread SZEDER Gábor
The main completion function finds the name of the git command by
iterating through all the words on the command line in search for the
first non-option-looking word.  As it is not aware of 'git -C's
mandatory path argument, if the '-C path' option is present, 'path' will
be the first such word and it will be mistaken for a git command.  This
breaks the completion script in various ways:

 - If 'path' happens to match one of the commands supported by the
   completion script, then its options will be offered.

 - If 'path' doesn't match a supported command and doesn't contain any
   characters not allowed in Bash identifier names, then the completion
   script does basically nothing and lets Bash to fall back to filename
   completion.

 - Otherwise, if 'path' contains such unallowed characters, then it
   leads to a more or less ugly error in the middle of the command line.
   The standard '/' directory separator is such a character, and it
   happens to trigger one of the uglier errors:

 $ git -C some/path sh.exe": declare: `_git_some/path': not a valid 
identifier
 error: invalid key: alias.some/path

Fix this by skipping 'git -C's mandatory path argument while iterating
over the words on the command line.  Extend the relevant test with this
case and, while at it, with cases that needed similar treatment in the
past ('--git-dir', '-c', '--work-tree' and '--namespace').
Additionally, shut up standard error of the 'declare' commands looking
for the associated completion function and of the 'git config' query for
the aliased command, so if git learns a new option with a mandatory
argument in the future, then at least the command line will not be
utterly disrupted by those error messages.

Note, that this change merely fixes the breakage related to 'git -C
path', but the completion script will not take it into account as it
does '--git-dir path'.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 8 
 t/t9902-completion.sh  | 7 ++-
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 482ca84b45..80dc717fe2 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -763,7 +763,7 @@ __git_aliases ()
 __git_aliased_command ()
 {
local word cmdline=$(git --git-dir="$(__gitdir)" \
-   config --get "alias.$1")
+   config --get "alias.$1" 2>/dev/null)
for word in $cmdline; do
case "$word" in
\!gitk|gitk)
@@ -2571,7 +2571,7 @@ __git_main ()
--git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
--bare)  __git_dir="." ;;
--help) command="help"; break ;;
-   -c|--work-tree|--namespace) ((c++)) ;;
+   -c|-C|--work-tree|--namespace) ((c++)) ;;
-*) ;;
*) command="$i"; break ;;
esac
@@ -2604,13 +2604,13 @@ __git_main ()
fi
 
local completion_func="_git_${command//-/_}"
-   declare -f $completion_func >/dev/null && $completion_func && return
+   declare -f $completion_func >/dev/null 2>/dev/null && $completion_func 
&& return
 
local expansion=$(__git_aliased_command "$command")
if [ -n "$expansion" ]; then
words[1]=$expansion
completion_func="_git_${expansion//-/_}"
-   declare -f $completion_func >/dev/null && $completion_func
+   declare -f $completion_func >/dev/null 2>/dev/null && 
$completion_func
fi
 }
 
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2ba62fbc17..f5a669918d 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -480,7 +480,12 @@ test_expect_success 'general options plus command' '
test_completion "git --namespace=foo check" "checkout " &&
test_completion "git --paginate check" "checkout " &&
test_completion "git --info-path check" "checkout " &&
-   test_completion "git --no-replace-objects check" "checkout "
+   test_completion "git --no-replace-objects check" "checkout " &&
+   test_completion "git --git-dir some/path check" "checkout " &&
+   test_completion "git -c conf.var=value check" "checkout " &&
+   test_completion "git -C some/path check" "checkout " &&
+   test_completion "git --work-tree some/path check" "checkout " &&
+   test_completion "git --namespace name/space check" "checkout "
 '
 
 test_expect_success 'git --help completion' '
-- 
2.6.0.rc2.22.g7128296

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] completion: fix completion after 'git -C path'

2015-10-06 Thread SZEDER Gábor


Quoting Michael J Gruber :


SZEDER Gábor venit, vidit, dixit 05.10.2015 14:02:

The main completion function finds the name of the git command by
iterating through all the words on the command line in search for the
first non-option-looking word.  As it is not aware of 'git -C's
mandatory path argument, if the '-C path' option is present, 'path' will
be the first such word and it will be mistaken for a git command.  This
breaks the completion script in various ways:

 - If 'path' happens to match one of the commands supported by the
   completion script, then its options will be offered.

 - If 'path' doesn't match a supported command and doesn't contain any
   characters not allowed in Bash identifier names, then the completion
   script does basically nothing and lets Bash to fall back to filename
   completion.

 - Otherwise, if 'path' contains such unallowed characters, then it
   leads to a more or less ugly error in the middle of the command line.
   The standard '/' directory separator is such a character, and it
   happens to trigger one of the uglier errors:

 $ git -C some/path sh.exe": declare: `_git_some/path':  
not a valid identifier

 error: invalid key: alias.some/path

Fix this by skipping 'git -C's mandatory path argument while iterating
over the words on the command line.  Extend the relevant test with this
case and, while at it, with cases that needed similar treatment in the
past ('--git-dir', '-c', '--work-tree' and '--namespace').
Additionally, shut up standard error of the 'declare' commands looking
for the associated completion function and of the 'git config' query for
the aliased command, so if git learns a new option with a mandatory
argument in the future, then at least the command line will not be
utterly disrupted by those error messages.

Note, that this change merely fixes the breakage related to 'git -C
path', but the completion script will not take it into account as it
does '--git-dir path'.


I don't understand the "as it does" part. Do you mean that the
completion script does '--git-dir path', or that git does it?


When the user specifies '--git-dir path' on the command line the
completion script respects that (most of the time, I noticed a few
missing spots), i.e.

  git --git-dir /somewhere/else/.git log 

gives you all the refs from the specified repository, which is good.
However, that's not the case with '-C /somewhere/else', as it will lead to
the error mentioned in the commit message on current git, or will be
ignored with this patch.


In any case, "git -C path ..." is more like "cd path && git ...". That
is, completion should take it into account at least when determining
GIT_DIR (though -C does not specifiy the git-dir directly), and possibly
also for completion of untracked files. Otherwise, it's going by the
wrong repo (unless path is a subdir of cwd).


I agree, it should, but it doesn't.  That will be the next step in some
future patches.


Gábor
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2 1/2] completion: Add sequencer function

2015-05-30 Thread SZEDER Gábor


Quoting Thomas Braun :


Signed-off-by: Thomas Braun 
---
 contrib/completion/git-completion.bash | 48  
+++---

 1 file changed, 33 insertions(+), 15 deletions(-)


I don't see the benefits of this change.  This patch adds more than  
twice as many lines as it removes, and patch 2/2 adds 8 new lines  
although it could get away with only 5 without this function.  To  
offer sequencer options we currently go through a single if statement,  
with this patch we'd go through a case statement, an if statement and  
finally an &&.


Gábor


diff --git a/contrib/completion/git-completion.bash  
b/contrib/completion/git-completion.bash

index bfc74e9..f6e5bf6 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -851,15 +851,40 @@ __git_count_arguments ()
printf "%d" $c
 }

+__git_complete_sequencer ()
+{
+   local dir="$(__gitdir)"
+
+   case "$1" in
+   am)
+   if [ -d "$dir"/rebase-apply ]; then
+   __gitcomp "--skip --continue --resolved --abort"
+   return 0
+   fi
+   ;;
+   cherry-pick)
+   if [ -f "$dir"/CHERRY_PICK_HEAD ]; then
+   __gitcomp "--continue --quit --abort"
+   return 0
+   fi
+   ;;
+   rebase)
+   if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; 
then
+   __gitcomp "--continue --skip --abort"
+   return 0
+   fi
+   ;;
+   esac
+
+   return 1
+}
+
 __git_whitespacelist="nowarn warn error error-all fix"

 _git_am ()
 {
-   local dir="$(__gitdir)"
-   if [ -d "$dir"/rebase-apply ]; then
-   __gitcomp "--skip --continue --resolved --abort"
-   return
-   fi
+   __git_complete_sequencer "am" && return
+
case "$cur" in
--whitespace=*)
__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
@@ -1044,11 +1069,8 @@ _git_cherry ()

 _git_cherry_pick ()
 {
-   local dir="$(__gitdir)"
-   if [ -f "$dir"/CHERRY_PICK_HEAD ]; then
-   __gitcomp "--continue --quit --abort"
-   return
-   fi
+   __git_complete_sequencer "cherry-pick" && return
+
case "$cur" in
--*)
__gitcomp "--edit --no-commit --signoff --strategy= --mainline"
@@ -1666,11 +1688,7 @@ _git_push ()

 _git_rebase ()
 {
-   local dir="$(__gitdir)"
-   if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
-   __gitcomp "--continue --skip --abort"
-   return
-   fi
+   __git_complete_sequencer "rebase" && return
__git_complete_strategy && return
case "$cur" in
--whitespace=*)



--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2 1/2] completion: Add sequencer function

2015-06-01 Thread SZEDER Gábor


Quoting Junio C Hamano :


SZEDER Gábor  writes:


I don't see the benefits of this change.  This patch adds more than
twice as many lines as it removes, and patch 2/2 adds 8 new lines
although it could get away with only 5 without this function.  To
offer sequencer options we currently go through a single if statement,
with this patch we'd go through a case statement, an if statement and
finally an &&.

Gábor


Perhaps, especially given that I'd imagine we won't be adding 47 new
commands that drive the sequencer in the near future ;-)

I presume that you are OK with Thomas's original version, then?


Yes, definitely.

It's a shame all these sequencing commands have different sets of  
sequencer options.  Perhaps something to clean up for, say, v3.0 :)



Gábor
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] commit: cope with scissors lines in commit message

2015-06-07 Thread SZEDER Gábor
The diff and submodule shortlog appended to the commit message template
by 'git commit --verbose' are not stripped when the commit message
contains an indented scissors line.

When cleaning up a commit message with 'git commit --verbose' or
'--cleanup=scissors' the code is careful and triggers only on a pure
scissors line, i.e. a line containing nothing but a comment character, a
space, and the scissors cut.  This is good, because people can embed
scissor lines in the commit message while using 'git commit --verbose',
and the text they write after their indented scissors line doesn't get
deleted.

While doing so, however, the cleanup function only looks at the first
line matching the scissors pattern and if it doesn't start at the
beginning of the line, then the function just returns without performing
any cleanup.  This is bad, because a "real" scissors line added by 'git
commit --verbose' might follow, and in that case the diff and submodule
shortlog get included in the commit message.

Don't bail out if a scissors line doesn't start at the beginning of the
line, but keep looking for a non-indented scissors line to fix this.

Signed-off-by: SZEDER Gábor 
---
 t/t7502-commit.sh | 25 +
 wt-status.c   | 12 
 2 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 2e0d557243..77db3a31c3 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -239,6 +239,31 @@ EOF
 
 '
 
+test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors 
line in commit message)' '
+   echo >>negative &&
+   cat >text <8 
+  # to be kept, too
+#  >8 
+to be removed
+#  >8 
+to be removed, too
+EOF
+
+   cat >expect <8 
+  # to be kept, too
+EOF
+   git commit --cleanup=scissors -e -F text -a &&
+   git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+   test_cmp expect actual
+'
+
 test_expect_success 'cleanup commit messages (strip option,-F)' '
 
echo >>negative &&
diff --git a/wt-status.c b/wt-status.c
index c56c78fb6f..e6d171a0cb 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -822,13 +822,17 @@ conclude:
 
 void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
 {
-   const char *p;
+   const char *p = buf->buf;
struct strbuf pattern = STRBUF_INIT;
 
strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
-   p = strstr(buf->buf, pattern.buf);
-   if (p && (p == buf->buf || p[-1] == '\n'))
-   strbuf_setlen(buf, p - buf->buf);
+   while ((p = strstr(p, pattern.buf))) {
+   if (p == buf->buf || p[-1] == '\n') {
+   strbuf_setlen(buf, p - buf->buf);
+   break;
+   }
+   p++;
+   }
strbuf_release(&pattern);
 }
 
-- 
2.4.2.423.gad3a03f

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] completion: teach 'scissors' mode to 'git commit --cleanup='

2015-06-07 Thread SZEDER Gábor
Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index bfc74e9d57..a1098765f6 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1108,7 +1108,7 @@ _git_commit ()
 
case "$cur" in
--cleanup=*)
-   __gitcomp "default strip verbatim whitespace
+   __gitcomp "default scissors strip verbatim whitespace
" "" "${cur##--cleanup=}"
return
;;
-- 
2.4.3.384.g605df7b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] commit: cope with scissors lines in commit message

2015-06-08 Thread SZEDER Gábor


Quoting Junio C Hamano :


SZEDER Gábor  writes:


The diff and submodule shortlog appended to the commit message template
by 'git commit --verbose' are not stripped when the commit message
contains an indented scissors line.

When cleaning up a commit message with 'git commit --verbose' or
'--cleanup=scissors' the code is careful and triggers only on a pure
scissors line, i.e. a line containing nothing but a comment character, a
space, and the scissors cut.  This is good, because people can embed
scissor lines in the commit message while using 'git commit --verbose',
and the text they write after their indented scissors line doesn't get
deleted.

While doing so, however, the cleanup function only looks at the first
line matching the scissors pattern and if it doesn't start at the
beginning of the line, then the function just returns without performing
any cleanup.  This is bad, because a "real" scissors line added by 'git
commit --verbose' might follow, and in that case the diff and submodule
shortlog get included in the commit message.


Yikes; this is not just "bad" but simply "wrong".  Thanks for
noticing.


Great, the other day I scored a "Gaaah" from Peff, now a "Yikes" from  
you...  Doin' good! ;)



 void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
 {
-   const char *p;
+   const char *p = buf->buf;
struct strbuf pattern = STRBUF_INIT;

strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
-   p = strstr(buf->buf, pattern.buf);
-   if (p && (p == buf->buf || p[-1] == '\n'))
-   strbuf_setlen(buf, p - buf->buf);
+   while ((p = strstr(p, pattern.buf))) {
+   if (p == buf->buf || p[-1] == '\n') {
+   strbuf_setlen(buf, p - buf->buf);
+   break;
+   }
+   p++;
+   }


I however wonder if we should make strstr() do more work for us.

strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
if (starts_with(buf->buf, pattern.buf + 1))
strbuf_setlen(buf, 0);
else if ((p = strstr(buf->buf, pattern.buf)) != NULL)
strbuf_setlen(buf, p - buf->buf + 1);
strbuf_release(&pattern);

perhaps?


Though this has one more if statement, it doesn't add a loop and  
eliminates the if (... || ...).

Good, will try to reroll in the evening.

Gábor
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2] commit: cope with scissors lines in commit message

2015-06-08 Thread SZEDER Gábor
The diff and submodule shortlog appended to the commit message template
by 'git commit --verbose' are not stripped when the commit message
contains an indented scissors line.

When cleaning up a commit message with 'git commit --verbose' or
'--cleanup=scissors' the code is careful and triggers only on a pure
scissors line, i.e. a line containing nothing but a comment character, a
space, and the scissors cut.  This is good, because people can embed
scissor lines in the commit message while using 'git commit --verbose',
and the text they write after their indented scissors line doesn't get
deleted.

While doing so, however, the cleanup function only looks at the first
line matching the scissors pattern and if it doesn't start at the
beginning of the line, then the function just returns without performing
any cleanup.  This is wrong, because a "real" scissors line added by
'git commit --verbose' might follow, and in that case the diff and
submodule shortlog get included in the commit message.

Fix this by changing the scissors pattern to match only at the beginning
of the line, yet be careful to catch scissors on the first line as well.

Helped-by: Junio C Hamano 
Signed-off-by: SZEDER Gábor 
---
Changes besides incorporating Junio's suggestion and updating the commit
message accordingly:

 * Instead of adding a new test, modify the existing one to check
   handling indented scissors lines.
 * Add a test to check scissors on the first line
 
 t/t7502-commit.sh | 24 +++-
 wt-status.c   |  9 +
 2 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 2e0d557243..b39e313ac2 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -229,14 +229,36 @@ test_expect_success 'cleanup commit messages (scissors 
option,-F,-e)' '
cat >text <8 
+# to be kept, too
 #  >8 
 to be removed
+#  >8 
+to be removed, too
+EOF
+
+   cat >expect <8 
+# to be kept, too
 EOF
-   echo "# to be kept" >expect &&
git commit --cleanup=scissors -e -F text -a &&
git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
test_cmp expect actual
+'
 
+test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors 
on first line)' '
+
+   echo >>negative &&
+   cat >text <8 
+to be removed
+EOF
+   git commit --cleanup=scissors -e -F text -a --allow-empty-message &&
+   git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+   test_must_be_empty actual
 '
 
 test_expect_success 'cleanup commit messages (strip option,-F)' '
diff --git a/wt-status.c b/wt-status.c
index c56c78fb6f..eaed4fed32 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -825,10 +825,11 @@ void wt_status_truncate_message_at_cut_line(struct strbuf 
*buf)
const char *p;
struct strbuf pattern = STRBUF_INIT;
 
-   strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
-   p = strstr(buf->buf, pattern.buf);
-   if (p && (p == buf->buf || p[-1] == '\n'))
-   strbuf_setlen(buf, p - buf->buf);
+   strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
+   if (starts_with(buf->buf, pattern.buf + 1))
+   strbuf_setlen(buf, 0);
+   else if ((p = strstr(buf->buf, pattern.buf)))
+   strbuf_setlen(buf, p - buf->buf + 1);
strbuf_release(&pattern);
 }
 
-- 
2.4.3.384.g605df7b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v3] commit: cope with scissors lines in commit message

2015-06-08 Thread SZEDER Gábor
The diff and submodule shortlog appended to the commit message template
by 'git commit --verbose' are not stripped when the commit message
contains an indented scissors line.

When cleaning up a commit message with 'git commit --verbose' or
'--cleanup=scissors' the code is careful and triggers only on a pure
scissors line, i.e. a line containing nothing but a comment character, a
space, and the scissors cut.  This is good, because people can embed
scissors lines in the commit message while using 'git commit --verbose',
and the text they write after their indented scissors line doesn't get
deleted.

While doing so, however, the cleanup function only looks at the first
line matching the scissors pattern and if it doesn't start at the
beginning of the line, then the function just returns without performing
any cleanup.  This is wrong, because a "real" scissors line added by
'git commit --verbose' might follow, and in that case the diff and
submodule shortlog get included in the commit message.

Fix this by changing the scissors pattern to match only at the beginning
of the line, yet be careful to catch scissors on the first line as well.

Helped-by: Junio C Hamano 
Signed-off-by: SZEDER Gábor 
---
Fixed a typo in the commit message.
 
 t/t7502-commit.sh | 24 +++-
 wt-status.c   |  9 +
 2 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 2e0d557243..b39e313ac2 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -229,14 +229,36 @@ test_expect_success 'cleanup commit messages (scissors 
option,-F,-e)' '
cat >text <8 
+# to be kept, too
 #  >8 
 to be removed
+#  >8 
+to be removed, too
+EOF
+
+   cat >expect <8 
+# to be kept, too
 EOF
-   echo "# to be kept" >expect &&
git commit --cleanup=scissors -e -F text -a &&
git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
test_cmp expect actual
+'
 
+test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors 
on first line)' '
+
+   echo >>negative &&
+   cat >text <8 
+to be removed
+EOF
+   git commit --cleanup=scissors -e -F text -a --allow-empty-message &&
+   git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+   test_must_be_empty actual
 '
 
 test_expect_success 'cleanup commit messages (strip option,-F)' '
diff --git a/wt-status.c b/wt-status.c
index c56c78fb6f..eaed4fed32 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -825,10 +825,11 @@ void wt_status_truncate_message_at_cut_line(struct strbuf 
*buf)
const char *p;
struct strbuf pattern = STRBUF_INIT;
 
-   strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
-   p = strstr(buf->buf, pattern.buf);
-   if (p && (p == buf->buf || p[-1] == '\n'))
-   strbuf_setlen(buf, p - buf->buf);
+   strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
+   if (starts_with(buf->buf, pattern.buf + 1))
+   strbuf_setlen(buf, 0);
+   else if ((p = strstr(buf->buf, pattern.buf)))
+   strbuf_setlen(buf, p - buf->buf + 1);
strbuf_release(&pattern);
 }
 
-- 
2.4.3.384.g605df7b

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


rebase -i might leave CHERRY_PICK_HEAD behind

2015-06-16 Thread SZEDER Gábor


Hi,

When skipping an empty commit with 'git rebase --continue' a
CHERRY_PICK_HEAD file might be left behind.

What I did boils down to this:

echo one >file
git add file
git commit -m first
echo two >file
git commit -a -m second
echo three >file
git commit -a -m third
git rebase -i HEAD^^
# change todo list to edit the "second" commit

echo three >file
git commit -a --amend
# this effectively makes the third commit an empty commit
# and rebase will ask what to do:
git rebase --continue
The previous cherry-pick is now empty, possibly due to conflict  
resolution.

If you wish to commit it anyway, use:

git commit --allow-empty

Otherwise, please use 'git reset'
rebase in progress; onto 7335bbe7a5
You are currently rebasing branch 'master' on '7335bbe7a5'.

nothing to commit, working directory clean
Could not apply d19f82ac6d467247117fd734ed039b03ef923c86... third

# I didn't want an empty commit, but didn't read that carefully, so I did:
git rebase --continue
Successfully rebased and updated refs/heads/master.
# and was rewarded for my lack of attention with the following  
bash prompt:

test/rebase-empty-continue (master|CHERRY-PICKING)$
# indeed:
ls -l .git/CHERRY_PICK_HEAD
-rw-r--r-- 1 szeder szeder 41 Jun 16 13:22 .git/CHERRY_PICK_HEAD

On one hand, it's user error: it told me to run 'git reset' to achive
what I want but I didn't.
Note, however, how it told me about 'git reset': while 'git commit
--allow-empty' is greatly emphasized by indentation and empty lines
before and after, 'git reset' blends in quite well into the rebase
progress.  It was late, I was tired, and there was a questionable
penalty on Copa América as well ;), so I simply didn't notice.

On the other hand,

   1. 'git rebase' claimed that "Successfully rebased...", yet it left
  cruft behind.  I think it shouldn't.
   2. 'git rebase --continue' didn't complain by the lack of prior
  'git reset' and finished doing exacly what I expected from it to
  do (except leaving CHERRY_PICK_HEAD behind, of course).
  Perhaps it should complain, like it does when the worktree is
  dirty.
  Alternatively, it could just continue to DWIM, as it does
  already, but then it should remove CHERRY_PICK_HEAD as well.

Gábor

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] git-prompt.sh: Document GIT_PS1_STATESEPARATOR

2015-06-16 Thread SZEDER Gábor


Quoting Joe Cridge :

The environment variable GIT_PS1_STATESEPARATOR can be used to set the
separator between the branch name and the state symbols in the prompt.

At present the variable is not mentioned in the inline documentation which
makes it difficult for the casual user to identify.


Thanks, makes sense.


Signed-off-by: Joe Cridge 
---
 contrib/completion/git-prompt.sh | 4 
 1 file changed, 4 insertions(+)

diff --git a/contrib/completion/git-prompt.sh  
b/contrib/completion/git-prompt.sh

index f18aedc..366f0bc 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -66,6 +66,10 @@
 # git   always compare HEAD to @{upstream}
 # svn   always compare HEAD to your SVN upstream
 #
+# You can change the separator between the branch name and the above
+# state symbols by setting GIT_PS1_STATESEPARATOR. The default separator
+# is SP.


This is not a specification of a protocol or file or input/output  
format, where we formally use SP and LF.  Perhaps we could spell out  
SP as a space here, for the sake of the "casual user"?



+#
 # By default, __git_ps1 will compare HEAD to your SVN upstream if it can
 # find one, or @{upstream} otherwise.  Once you have set
 # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
--
2.4.2



--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 2/2] rebase -i: do not leave a CHERRY_PICK_HEAD file behind

2015-06-17 Thread SZEDER Gábor

Hi,

Quoting Johannes Schindelin :

When skipping commits whose changes were already applied via `git rebase
--continue`, we need to clean up said file explicitly.

The same is not true for `git rebase --skip` because that will execute
`git reset --hard` as part of the "skip" handling in git-rebase.sh, even
before git-rebase--interactive.sh is called.

Signed-off-by: Johannes Schindelin 


Nice quick turnaround, thanks.

So, with this change the 'git reset' won't be necessary at all, right?


---
 git-rebase--interactive.sh| 6 +-
 t/t3404-rebase-interactive.sh | 2 +-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index dc3133f..16e0a82 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -849,7 +849,11 @@ continue)
# do we have anything to commit?
if git diff-index --cached --quiet HEAD --
then
-   : Nothing to commit -- skip this
+   : Nothing to commit -- skip this commit


"While at it", perhaps you could turn this into a proper comment with '#".
Now that this if-branch starts to actually do something, there's no  
reason to continue (ab)using the null command.



+
+   test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
+   rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
+   die "Could not remove CHERRY_PICK_HEAD"
else
if ! test -f "$author_script"
then
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 5d52775..241d4d1 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1102,7 +1102,7 @@ test_expect_success 'rebase -i commits that  
overwrite untracked files (no ff)' '

test $(git cat-file commit HEAD | sed -ne \$p) = I
 '

-test_expect_failure 'rebase --continue removes CHERRY_PICK_HEAD' '
+test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
git checkout -b commit-to-skip &&
for double in X 3 1
do
--
2.3.1.windows.1.9.g8c01ab4



--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: Git completion not using ls-remote to auto-complete during push

2015-06-18 Thread SZEDER Gábor
Quoting Robert Dailey 
> I do the following:
> 
> $ git push origin :topic
> 
> If I stop halfway through typing 'topic' and hit TAB, auto-completion
> does not work if I do not have a local branch by that name (sometimes
> I delete my local branch first, then I push to delete it remotely). I
> thought that git completion code was supposed to use ls-remote to auto
> complete refs used in push operations. Is this supposed to work?

It's intentional.  Running 'git ls-remote' with a far away remote can
take ages, so instead we grab the refs on the remote from the locally
stored refs under 'refs/remotes//'.

See e832f5c096 (completion: avoid ls-remote in certain scenarios,
2013-05-28).  The commit message mentions that you can "force"
completion of remote refs via 'git ls-remote' by starting with the full
refname, i.e.  'refs/', however, that seems to work only on the
left hand side of the colon in the push refspec.

Gábor

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: Git completion not using ls-remote to auto-complete during push

2015-06-18 Thread SZEDER Gábor


Quoting Robert Dailey :


On Thu, Jun 18, 2015 at 6:29 AM, SZEDER Gábor  wrote:

Quoting Robert Dailey 

I do the following:

$ git push origin :topic

If I stop halfway through typing 'topic' and hit TAB, auto-completion
does not work if I do not have a local branch by that name (sometimes
I delete my local branch first, then I push to delete it remotely). I
thought that git completion code was supposed to use ls-remote to auto
complete refs used in push operations. Is this supposed to work?


It's intentional.  Running 'git ls-remote' with a far away remote can
take ages, so instead we grab the refs on the remote from the locally
stored refs under 'refs/remotes//'.

See e832f5c096 (completion: avoid ls-remote in certain scenarios,
2013-05-28).  The commit message mentions that you can "force"
completion of remote refs via 'git ls-remote' by starting with the full
refname, i.e.  'refs/', however, that seems to work only on the
left hand side of the colon in the push refspec.

Gábor



If that's indeed the case, then completion should work. I have a
'refs/remotes/origin/topic'. Why will auto complete not work even
though this exists? Do multiple remotes cause issues (in theory there
is no reason why it should cause problems, since it should know I'm
auto-completing a ref on 'origin')?


The number of remotes doesn't matter.
What matters is which side of the colon the ref to be completed is.

You can complete

  git push origin refs/

and

  git fetch origin refs/

will even list you refs freshly queried via 'git ls-remote'.
However,

  git push origin :refs/
  git push origin branch:refs/

don't work, because there are no refs starting with the prefix  
':refs/' or 'branch:refs/'.


Gábor

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 1/2] bash prompt: test untracked files status indicator with untracked dirs

2015-07-19 Thread SZEDER Gábor
The next commit will tweak the way __git_ps1() decides whether to display
the untracked files status indicator in the presence of untracked
directories.  Add tests to make sure it doesn't change current behavior,
in particular that an empty untracked directory doesn't trigger the
untracked files status indicator.

Signed-off-by: SZEDER Gábor 
---
 t/t9903-bash-prompt.sh | 25 +
 1 file changed, 25 insertions(+)

diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 49d58e6726..6b68777b98 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -397,6 +397,31 @@ test_expect_success 'prompt - untracked files status 
indicator - untracked files
test_cmp expected "$actual"
 '
 
+test_expect_success 'prompt - untracked files status indicator - empty 
untracked dir' '
+   printf " (master)" >expected &&
+   mkdir otherrepo/untracked-dir &&
+   test_when_finished "rm -rf otherrepo/untracked-dir" &&
+   (
+   GIT_PS1_SHOWUNTRACKEDFILES=y &&
+   cd otherrepo &&
+   __git_ps1 >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - untracked files status indicator - non-empty 
untracked dir' '
+   printf " (master %%)" >expected &&
+   mkdir otherrepo/untracked-dir &&
+   test_when_finished "rm -rf otherrepo/untracked-dir" &&
+   >otherrepo/untracked-dir/untracked-file &&
+   (
+   GIT_PS1_SHOWUNTRACKEDFILES=y &&
+   cd otherrepo &&
+   __git_ps1 >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
 test_expect_success 'prompt - untracked files status indicator - untracked 
files outside cwd' '
printf " (master %%)" >expected &&
(
-- 
2.5.0.rc2.15.gd82f7f6

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 2/2] bash prompt: faster untracked status indicator with untracked directories

2015-07-19 Thread SZEDER Gábor
If the untracked status indicator is enabled, __git_ps1() looks for
untracked files by running 'git ls-files'.  This can be perceptibly slow
in case of an untracked directory containing lot of files, because it
lists all files found in the untracked directory only to be redirected
into /dev/null right away (this is the actual command run by __git_ps1()):

  $ ls untracked-dir/ |wc -l
  10
  $ time git ls-files --others --exclude-standard --error-unmatch \
-- ':/*' >/dev/null 2>/dev/null

  real  0m0.955s
  user  0m0.936s
  sys   0m0.016s

Eliminate this delay by additionally passing the '--directory
--no-empty-directory' options to 'git ls-files' to show only the name of
non-empty untracked directories instead of all their content:

  $ time git ls-files --others --exclude-standard --directory \
--no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null

  real  0m0.010s
  user  0m0.008s
  sys   0m0.000s

This follows suit of ea95c7b8f5 (completion: improve untracked directory
filtering for filename completion, 2013-09-18).

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 366f0bc1e9..07b52bedf1 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -491,7 +491,7 @@ __git_ps1 ()
 
if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
   [ "$(git config --bool bash.showUntrackedFiles)" != "false" 
] &&
-  git ls-files --others --exclude-standard --error-unmatch -- 
':/*' >/dev/null 2>/dev/null
+  git ls-files --others --exclude-standard --directory 
--no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null
then
u="%${ZSH_VERSION+%}"
fi
-- 
2.5.0.rc2.15.gd82f7f6

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCHv2 0/7] Fix and generalize version sort reordering

2016-12-20 Thread SZEDER Gábor
On Wed, Dec 14, 2016 at 6:08 PM, Jeff King  wrote:
> On Thu, Dec 08, 2016 at 03:23:54PM +0100, SZEDER Gábor wrote:
>
>> > With my patches it looks like this:
>> >
>> > $ git -c versionsort.prereleasesuffix=-pre \
>> >   -c versionsort.prereleasesuffix=-prerelease \
>> >   tag -l --sort=version:refname
>> > v1.0.0-prerelease1
>> > v1.0.0-pre1
>> > v1.0.0-pre2
>> > v1.0.0
>>
>> And when there happen to be more than one matching suffixes starting
>> at the same earliest position, then we should pick the longest of
>> them.  The new patch 6/7 implements this behavior.
>
> The whole approach taken by the suffix code (before your patches) leaves
> me with the nagging feeling that the comparison is not always going to
> be transitive (i.e., that "a < b && b < c" does not always imply "a <
> c"), which is going to cause nonsensical sorting results.
>
> And that may be part of the issue your 6/7 fixes.
>
> I spent some time playing with this the other day, though, and couldn't
> come up with a specific example that fails the condition above.
>
> It just seems like the whole thing would conceptually easier if we
> pre-parsed the versions into a sequence of elements, then the comparison
> between any two elements would just walk that sequence. The benefit
> there is that you can implement whatever rules you like for the parsing
> (like "prefer longer suffixes to shorter"), but you know the comparison
> will always be consistent.

I considered parsing tagnames into prefix, version number and suffix,
and then work from that, but decided against it.

versioncmp() is taken from glibc, so I assume that it's thoroughly
tested, even in corner cases (e.g. multiple leading zeros).
Furthermore, I think it's a good thing that by default (i.e. without
suffix reordering) our version sort orders the same way as glibc's
version sort does.  Introducing a different algorithm would risk bugs
in the more subtle cases.

Then there are all the weird release suffixes out there, and I didn't
want to decide on a policy for splitting them sanely; don't know
whether there exist any universal rules for this splitting at
all.  E.g. one of the packages here has the following version (let's
ignore the fact that because of the '~' this is an invalid refname in
git):

  1.1.0~rc1-2ubuntu7-1linuxmint1

Now, it's clear that the version number is "1.1.0", and the user
should configure the suffix "~rc" for prerelease reordering.  But what
about the rest?  How should we split it "into a sequence of elements",
is it { "1.1.0", "~rc1", "-2ubuntu7", "-1linuxmint1" } or { "1.1.0",
"~rc1-2", "ubuntu7-1", "linuxmint1" }?
What if there is a hard-working developer who is involved in a lot of
Debian derivatives (and derivatives of derivatives...), and, for
whatever reason, wants to put derivative-specific versions in a
particular order?  With my series, or conceptually even with master if
it weren't buggy, it's possible to specify the order of suffixes of
suffixes, and that dev could do this:

  $ git -c versionsort.suffix=-rc
-c versionsort.suffix=linuxmint
-c versionsort.suffix=YADoaDD
tag -l --sort=version:refname
  '1.1.0*'
  1.1.0-rc1-2ubuntu7-1linuxmint1
  1.1.0-rc1-2ubuntu7-1YADoaDD2
  1.1.0
  1.1.0-2ubuntu7-1linuxmint1
  1.1.0-2ubuntu7-1YADoaDD2

and would get Linux Mint-specific tags before "Yet Another Derivative
of a Debian Derivative"-specific ones.  Not sure whether this is
relevant in practice, but I think it's a nice property nonetheless.

(Btw, just for fun, I also found a package version
2.0.0~beta2+isreally1.8.6-0ubuntu1.  "isreally".  Oh yeah :)

> It would also be more efficient, I think (it seems like the sort is
> O(nr_tags * lg(nr_tags) * nr_suffixes) due to parsing suffixes in the
> comparator). Though that probably doesn't matter much in practice.

I don't think there will be more than only a few configured suffixes
in any repository.  However, if you consider O as "number of
starts_with() invocations", then there is an additional suffix_length
factor.  But then again, these suffixes tend to be short.

> I dunno. I think maybe your 6/7 has converged on an equivalent behavior.
> And I am certainly not volunteering to re-write it, so if what you have
> works, I'm not opposed to it.
>
> -Peff


Re: [PATCH] git-prompt.sh: add submodule indicator

2017-01-20 Thread SZEDER Gábor
I'm not well-versed in submodules, so I won't comment on whether this
is the right way to determine that a repository is a submodule, but I
was surprised to see how much you have to work to find this out.

My comments will mostly focus on how to eliminate or at least limit
the scope of subshells and command executions, because fork()s and
exec()s are rather expensive on Windows.

On Fri, Jan 20, 2017 at 1:07 AM, Benjamin Fuchs  wrote:
> I expirienced that working with submodules can be confusing. This indicator
> will make you notice very easy when you switch into a submodule.
> The new prompt will look like this: (sub:master)
> Adding a new optional env variable for the new feature.
>
> Signed-off-by: Benjamin Fuchs 
> ---
>  contrib/completion/git-prompt.sh | 37 -
>  1 file changed, 36 insertions(+), 1 deletion(-)

Tests?

> diff --git a/contrib/completion/git-prompt.sh 
> b/contrib/completion/git-prompt.sh
> index 97eacd7..4c82e7f 100644
> --- a/contrib/completion/git-prompt.sh
> +++ b/contrib/completion/git-prompt.sh
> @@ -93,6 +93,10 @@
>  # directory is set up to be ignored by git, then set
>  # GIT_PS1_HIDE_IF_PWD_IGNORED to a nonempty value. Override this on the
>  # repository level by setting bash.hideIfPwdIgnored to "false".
> +#
> +# If you would like __git_ps1 to indicate that you are in a submodule,
> +# set GIT_PS1_SHOWSUBMODULE. In this case a "sub:" will be added before
> +# the branch name.
>
>  # check whether printf supports -v
>  __git_printf_supports_v=
> @@ -284,6 +288,32 @@ __git_eread ()
> test -r "$f" && read "$@" <"$f"
>  }
>
> +# __git_is_submodule
> +# Based on:
> +# 
> http://stackoverflow.com/questions/7359204/git-command-line-know-if-in-submodule
> +__git_is_submodule ()

Use the '__git_ps1' prefix in the function name, like the other
functions.

> +{
> +   local git_dir parent_git module_name path
> +   # Find the root of this git repo, then check if its parent dir is 
> also a repo
> +   git_dir="$(git rev-parse --show-toplevel)"

 1. This is a somewhat misleading variable name, because git_dir (with
or without underscore or dash) refers to the path to the .git
directory of a repository through the codebase, documentation and
CLI options, not the top-level directory of the worktree.

 2. In a bare repository or in the .git directory of a "regular"
repository '--show-toplevel' doesn't output anything, leading to
an empty $module_name below, which ultimately results in no
submodule indicator.

As fas as behavior is concerned, this is good in the bare
repository case, because as I understand it there is no such thing
as a bare submodule.  I'm not sure whether the submodule indicator
should be displayed in the ".git dir of a submodule" case.  I
leave it up to you and Stefan to discuss.

However, the info about whether we are in a bare repository or
.git dir is already availabe in certain variables, so we know
upfront when the current repository can't be a submodule.  In
those cases this function should not be called only to spend
several subshells and command executions to figure out what we
already knew anyway.

 3. The path to the .git directory of the current repository
is already available in the (not particularly descriptively named)
$g variable from __git_ps1.  Perhaps you could use that variable
instead, thus avoiding a subshell and executing a git command
here.

> +   module_name="$(basename "$git_dir")"

This is executed even when there is no repository in the parent
directories, but it's only needed when there _is_ a repo up there.
Please move it into the conditional below, to avoid a subshell and
command execution in the main code path.

Since this $git_dir comes directly from our own 'git rev-parse' do we
have to be prepared for a Windows-style path there?  If it were always
a UNIX-style path, then we could strip all directories with shell
parameter expansion, eliminating both the subshell and the basename
execution.

> +   parent_git="$(cd "$git_dir/.." && git rev-parse --show-toplevel 2> 
> /dev/null)"

Style nit: no space after redirection, i.e. it should be '2>/dev/null'.

More importantly, I don't think you really need this variable:

 1. The existence of a parent git repository can be determined from
'git rev-parse's exit code alone.

 2. When you run 'git submodule' below, you don't have to cd to the
_top-level_ directory of the parent repository's worktree.  You
just have to cd to _any_ directory in the parent, and you can do
that with 'git -C "$git_dir/.." submodule ...', without knowing
the parent's top-level directory.

This means that you don't need 'git rev-parse's output, thus there is
no need for the command substitution.  Yet another subshell spared! :)
However, then you have to be careful with changing directories, and
should write it as 'git -C "$git_dir/.." rev-parse ...'.

> +

Re: [PATCH 2/2] ref-filter: add function to parse atoms from a nul-terminated string

2017-01-26 Thread SZEDER Gábor
On Wed, Dec 7, 2016 at 5:09 PM, SZEDER Gábor  wrote:
> ref-filter's parse_ref_filter_atom() function parses an atom between
> the start and end pointers it gets as arguments.  This is fine for two
> of its callers, which process '%(atom)' format specifiers and the end
> pointer comes directly from strchr() looking for the closing ')'.
> However, it's not quite so straightforward for its other two callers,
> which process sort specifiers given as plain nul-terminated strings.
> Especially not for ref_default_sorting(), which has the default
> hard-coded as a string literal, but can't use it directly, because a
> pointer to the end of that string literal is needed as well.
> The next patch will add yet another caller using a string literal.
>
> Add a helper function around parse_ref_filter_atom() to parse an atom
> up to the terminating nul, and call this helper in those two callers.
>
> Signed-off-by: SZEDER Gábor 
> ---
>  ref-filter.c | 8 ++--
>  ref-filter.h | 4 
>  2 files changed, 6 insertions(+), 6 deletions(-)

Ping?

It looks like that this little two piece cleanup series fell on the floor.



> diff --git a/ref-filter.c b/ref-filter.c
> index dfadf577c..3c6fd4ba7 100644
> --- a/ref-filter.c
> +++ b/ref-filter.c
> @@ -1658,19 +1658,16 @@ void show_ref_array_item(struct ref_array_item *info, 
> const char *format, int qu
>  /*  If no sorting option is given, use refname to sort as default */
>  struct ref_sorting *ref_default_sorting(void)
>  {
> -   static const char cstr_name[] = "refname";
> -
> struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));
>
> sorting->next = NULL;
> -   sorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + 
> strlen(cstr_name));
> +   sorting->atom = parse_ref_filter_atom_from_string("refname");
> return sorting;
>  }
>
>  void parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
>  {
> struct ref_sorting *s;
> -   int len;
>
> s = xcalloc(1, sizeof(*s));
> s->next = *sorting_tail;
> @@ -1683,8 +1680,7 @@ void parse_sorting_string(const char *arg, struct 
> ref_sorting **sorting_tail)
> if (skip_prefix(arg, "version:", &arg) ||
> skip_prefix(arg, "v:", &arg))
> s->version = 1;
> -   len = strlen(arg);
> -   s->atom = parse_ref_filter_atom(arg, arg+len);
> +   s->atom = parse_ref_filter_atom_from_string(arg);
>  }
>
>  int parse_opt_ref_sorting(const struct option *opt, const char *arg, int 
> unset)
> diff --git a/ref-filter.h b/ref-filter.h
> index 49466a17d..1250537cf 100644
> --- a/ref-filter.h
> +++ b/ref-filter.h
> @@ -94,6 +94,10 @@ int filter_refs(struct ref_array *array, struct ref_filter 
> *filter, unsigned int
>  void ref_array_clear(struct ref_array *array);
>  /*  Parse format string and sort specifiers */
>  int parse_ref_filter_atom(const char *atom, const char *ep);
> +static inline int parse_ref_filter_atom_from_string(const char *atom)
> +{
> +   return parse_ref_filter_atom(atom, atom+strlen(atom));
> +}
>  /*  Used to verify if the given format is correct and to parse out the used 
> atoms */
>  int verify_ref_format(const char *format);
>  /*  Sort the given ref_array as per the ref_sorting provided */
> --
> 2.11.0.78.g5a2d011
>


Re: [PATCH 2/4] git-prompt.sh: rework of submodule indicator

2017-01-31 Thread SZEDER Gábor

> Rework of the first patch. The prompt now will look like this:
> (+name:master). I tried to considere all suggestions.
> Tests still missing.
> 
> Signed-off-by: Benjamin Fuchs 
> ---
>  contrib/completion/git-prompt.sh | 49 
> 
>  1 file changed, 24 insertions(+), 25 deletions(-)
> 
> diff --git a/contrib/completion/git-prompt.sh 
> b/contrib/completion/git-prompt.sh
> index 4c82e7f..c44b9a2 100644
> --- a/contrib/completion/git-prompt.sh
> +++ b/contrib/completion/git-prompt.sh
> @@ -95,8 +95,10 @@
>  # repository level by setting bash.hideIfPwdIgnored to "false".
>  #
>  # If you would like __git_ps1 to indicate that you are in a submodule,
> -# set GIT_PS1_SHOWSUBMODULE. In this case a "sub:" will be added before
> -# the branch name.
> +# set GIT_PS1_SHOWSUBMODULE to a nonempty value. In this case the name
> +# of the submodule will be prepended to the branch name (e.g. module:master).
> +# The name will be prepended by "+" if the currently checked out submodule
> +# commit does not match the SHA-1 found in the index of the containing 
> repository.
>  
>  # check whether printf supports -v
>  __git_printf_supports_v=
> @@ -288,30 +290,27 @@ __git_eread ()
>   test -r "$f" && read "$@" <"$f"
>  }
>  
> -# __git_is_submodule
> -# Based on:
> -# 
> http://stackoverflow.com/questions/7359204/git-command-line-know-if-in-submodule
> -__git_is_submodule ()
> -{
> - local git_dir parent_git module_name path
> - # Find the root of this git repo, then check if its parent dir is also 
> a repo
> - git_dir="$(git rev-parse --show-toplevel)"
> - module_name="$(basename "$git_dir")"
> - parent_git="$(cd "$git_dir/.." && git rev-parse --show-toplevel 2> 
> /dev/null)"
> - if [[ -n $parent_git ]]; then
> - # List all the submodule paths for the parent repo
> - while read path
> - do
> - if [[ "$path" != "$module_name" ]]; then continue; fi
> - if [[ -d "$git_dir/../$path" ]];then return 0; fi
> - done < <(cd $parent_git && git submodule --quiet foreach 'echo 
> $path' 2> /dev/null)
> -fi
> -return 1
> -}
> -
> +# __git_ps1_submodule
> +#
> +# $1 - git_dir path
> +#
> +# Returns the name of the submodule if we are currently inside one. The name
> +# will be prepended by "+" if the currently checked out submodule commit does
> +# not match the SHA-1 found in the index of the containing repository.
> +# NOTE: git_dir looks like in a ...
> +# - submodule: "GIT_PARENT/.git/modules/PATH_TO_SUBMODULE"
> +# - parent: "GIT_PARENT/.git" (exception "." in .git)
>  __git_ps1_submodule ()
>  {
> - __git_is_submodule && printf "sub:"
> + local git_dir="$1"
> + local submodule_name="$(basename "$git_dir")"
> + if [ "$submodule_name" != ".git" ] && [ "$submodule_name" != "." ]; then
> + local parent_top="${git_dir%.git*}"
> + local submodule_top="${git_dir#*modules}"
> + local status=""
> + git diff -C "$parent_top" --no-ext-diff 
> --ignore-submodules=dirty --quiet -- "$submodule_top" 2>/dev/null || 
> status="+"

This 'git diff' has to read the index of the parent repository, right?
That can be potentially very expensive, if the parent repository, and
thus its index, is big.

You might want to provide finer granularity controls and separate the
"$PWD is in a submodule" indicator from the "submodule commit doesn't
match" indicator.  Even if someone is in general interested in the
former, he might have some huge repositories where he would prefer to
disable the latter, because executing 'git diff' would make the prompt
lag.

I'm not sure we need brand new control knobs, though.  Perhaps we can
get away by checking the env and config variables used for the dirty
index and worktree status indicator, after all they, too, are about
whether to run 'git diff' for the prompt in a repository or not.

> + printf "$status$submodule_name:"
> + fi
>  }
>  
>  # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
> @@ -545,7 +544,7 @@ __git_ps1 ()
>  
>   local sub=""
>   if [ -n "${GIT_PS1_SHOWSUBMODULE}" ]; then
> - sub="$(__git_ps1_submodule)"
> + sub="$(__git_ps1_submodule $g)"

In Bash, and in shells in general, the visibility of variables works
differently than in "regular" programming languages:

  - Any variable existing in a caller, even the ones declared as
'local' in the caller, are visible in all callees.

This means you don't have to pass $g as parameter to
__git_ps1_submodule(), because you can access it inside that
function directly.  This has the benefit that there is one less
place where you can forget to quote a path variable :)

  - If the callee modifies any variable that isn't declared as local
in the callee, then the caller will see the modified value of that
variable, unless the callee was invoked in a subsh

Re: [PATCH 4/4] git-prompt.sh: add tests for submodule indicator

2017-01-31 Thread SZEDER Gábor
On Mon, Jan 30, 2017 at 9:44 PM, Benjamin Fuchs  wrote:
> Signed-off-by: Benjamin Fuchs 
> ---
>  t/t9903-bash-prompt.sh | 43 +++
>  1 file changed, 43 insertions(+)
>
> diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
> index 97c9b32..4dce366 100755
> --- a/t/t9903-bash-prompt.sh
> +++ b/t/t9903-bash-prompt.sh
> @@ -37,6 +37,11 @@ test_expect_success 'setup for prompt tests' '
> git commit -m "yet another b2" file &&
> mkdir ignored_dir &&
> echo "ignored_dir/" >>.gitignore &&
> +   git checkout -b submodule &&
> +   git submodule add ./. sub &&

./. ?

> +   git -C sub checkout master &&
> +   git add sub &&
> +   git commit -m submodule &&
> git checkout master
>  '
>
> @@ -755,4 +760,42 @@ test_expect_success 'prompt - hide if pwd ignored - 
> inside gitdir (stderr)' '
> test_cmp expected "$actual"
>  '
>
> +test_expect_success 'prompt - submodule indicator' '
> +   printf " (sub:master)" >expected &&
> +   git checkout submodule &&
> +   test_when_finished "git checkout master" &&
> +   (
> +   cd sub &&
> +   GIT_PS1_SHOWSUBMODULE=1 &&
> +   __git_ps1 >"$actual"
> +   ) &&
> +   test_cmp expected "$actual"
> +'
> +
> +test_expect_success 'prompt - submodule indicator - verify false' '

I was puzzled by the "verify false" here.  You mean "disabled", right?

> +   printf " (master)" >expected &&
> +   git checkout submodule &&
> +   test_when_finished "git checkout master" &&
> +   (
> +   cd sub &&
> +   GIT_PS1_SHOWSUBMODULE= &&
> +   __git_ps1 >"$actual"
> +   ) &&
> +   test_cmp expected "$actual"
> +'
> +
> +test_expect_success 'prompt - submodule indicator - dirty status indicator' '
> +   printf " (+sub:b1)" >expected &&
> +   git checkout submodule &&
> +   git -C sub checkout b1 &&
> +   test_when_finished "git checkout master" &&
> +   (
> +   cd sub &&
> +   GIT_PS1_SHOWSUBMODULE=1 &&
> +   __git_ps1 >"$actual"
> +   ) &&
> +   test_cmp expected "$actual"
> +'
> +
> +
>  test_done
> --
> 2.7.4
>


[PATCH] .mailmap: update Gábor Szeder's email address

2017-01-31 Thread SZEDER Gábor
Signed-off-by: SZEDER Gábor 
---
 .mailmap | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.mailmap b/.mailmap
index 9c87a3840..ab59b2fac 100644
--- a/.mailmap
+++ b/.mailmap
@@ -225,6 +225,7 @@ Steven Walter  

 Steven Walter  
 Sven Verdoolaege  
 Sven Verdoolaege  
+SZEDER Gábor  
 Tay Ray Chuan 
 Ted Percival  
 Theodore Ts'o 
-- 
2.11.0.555.g967c1bcb3



Re: [PATCH v2 7/7] completion: recognize more long-options

2017-01-31 Thread SZEDER Gábor
On Fri, Jan 27, 2017 at 10:17 PM,   wrote:
> From: Cornelius Weig 
>
> Recognize several new long-options for bash completion in the following
> commands:

Adding more long options that git commands learn along the way is
always an improvement.  However, seeing "_several_ new long options"
(or "some long options" in one of the other patches in the series)
makes the reader wonder: are these the only new long options missing
or are there more?  If there are more, why only these are added?  If
there aren't any more missing long options left, then please say so,
e.g. "Add all missing long options, except the potentially
desctructive ones, for the following commands: "


>  - apply: --recount --directory=
>  - archive: --output
>  - branch: --column --no-column --sort= --points-at
>  - clone: --no-single-branch --shallow-submodules
>  - commit: --patch --short --date --allow-empty
>  - describe: --first-parent
>  - fetch, pull: --unshallow --update-shallow
>  - fsck: --name-objects
>  - grep: --break --heading --show-function --function-context
>  --untracked --no-index
>  - mergetool: --prompt --no-prompt
>  - reset: --keep
>  - revert: --strategy= --strategy-option=
>  - rm: --force

'--force' is a potentially destructive option, too.

>  - shortlog: --email
>  - tag: --merged --no-merged --create-reflog
>
> Signed-off-by: Cornelius Weig 
> Helped-by: Johannes Sixt 
> ---
>  contrib/completion/git-completion.bash | 31 +--
>  1 file changed, 21 insertions(+), 10 deletions(-)
>
> diff --git a/contrib/completion/git-completion.bash 
> b/contrib/completion/git-completion.bash
> index 0e09519..933bb6e 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -936,6 +936,7 @@ _git_apply ()
> --apply --no-add --exclude=
> --ignore-whitespace --ignore-space-change
> --whitespace= --inaccurate-eof --verbose
> +   --recount --directory=
> "
> return
> esac
> @@ -974,7 +975,7 @@ _git_archive ()
> --*)
> __gitcomp "
> --format= --list --verbose
> -   --prefix= --remote= --exec=
> +   --prefix= --remote= --exec= --output
> "
> return
> ;;
> @@ -1029,6 +1030,7 @@ _git_branch ()
> --track --no-track --contains --merged --no-merged
> --set-upstream-to= --edit-description --list
> --unset-upstream --delete --move --remotes
> +   --column --no-column --sort= --points-at
> "
> ;;
> *)
> @@ -1142,6 +1144,8 @@ _git_clone ()
> --single-branch
> --branch
> --recurse-submodules
> +   --no-single-branch
> +   --shallow-submodules
> "
> return
> ;;
> @@ -1183,6 +1187,7 @@ _git_commit ()
> --reset-author --file= --message= --template=
> --cleanup= --untracked-files --untracked-files=
> --verbose --quiet --fixup= --squash=
> +   --patch --short --date --allow-empty
> "
> return
> esac
> @@ -1201,7 +1206,7 @@ _git_describe ()
> --*)
> __gitcomp "
> --all --tags --contains --abbrev= --candidates=
> -   --exact-match --debug --long --match --always
> +   --exact-match --debug --long --match --always 
> --first-parent
> "
> return
> esac
> @@ -1284,6 +1289,7 @@ __git_fetch_recurse_submodules="yes on-demand no"
>  __git_fetch_options="
> --quiet --verbose --append --upload-pack --force --keep --depth=
> --tags --no-tags --all --prune --dry-run --recurse-submodules=
> +   --unshallow --update-shallow
>  "
>
>  _git_fetch ()
> @@ -1333,7 +1339,7 @@ _git_fsck ()
> --*)
> __gitcomp "
> --tags --root --unreachable --cache --no-reflogs 
> --full
> -   --strict --verbose --lost-found
> +   --strict --verbose --lost-found --name-objects
> "
> return
> ;;
> @@ -1377,6 +1383,8 @@ _git_grep ()
> --max-depth
> --count
> --and --or --not --all-match
> +   --break --heading --show-function --function-context
> +   --untracked --no-index
> "
> return
> ;;
> @@ -1576,7 +1584,7 @@ _git_mergetool

Re: [PATCH 3/7] completion: improve bash completion for git-add

2017-01-31 Thread SZEDER Gábor
> Add some long-options for git-add and improve path completion when the
> --update flag is given.

Please tell us in the commit message _how_ this improves path
completion.

> ---
>  contrib/completion/git-completion.bash | 10 +++---
>  1 file changed, 7 insertions(+), 3 deletions(-)
> 
> diff --git a/contrib/completion/git-completion.bash 
> b/contrib/completion/git-completion.bash
> index 8329f09..652c7e2 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -947,13 +947,17 @@ _git_add ()
>   --*)
>   __gitcomp "
>   --interactive --refresh --patch --update --dry-run
> - --ignore-errors --intent-to-add
> + --ignore-errors --intent-to-add --force --edit --chmod=

I almost started complaining that '--force' should be used with care,
but then realized that for 'git add' it only means adding ignored
files.  So in this particular case '--force' is not destructive and we
can offer it among other long options.  Good.

>   "
>   return
>   esac
>  
> - # XXX should we check for --update and --all options ?
> - __git_complete_index_file "--others --modified --directory 
> --no-empty-directory"
> + local complete_opt="--others --modified --directory 
> --no-empty-directory"
> + if test -n "$(__git_find_on_cmdline "-u --update")"
> + then
> + complete_opt="--modified"
> + fi
> + __git_complete_index_file "$complete_opt"
>  }
>  
>  _git_archive ()
> -- 
> 2.10.2




Re: [PATCH 2/7] completion: add subcommand completion for rerere

2017-02-01 Thread SZEDER Gábor
> Managing recorded resolutions requires command-line usage of git-rerere.
> Added subcommand completion for rerere and path completion for its
> subcommand forget.
> ---
>  contrib/completion/git-completion.bash | 11 +++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/contrib/completion/git-completion.bash 
> b/contrib/completion/git-completion.bash
> index c54a557..8329f09 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -2401,6 +2401,17 @@ _git_replace ()
>   __gitcomp_nl "$(__git_refs)"
>  }
>  
> +_git_rerere ()
> +{
> + local subcommands="clear forget diff remaining status gc"
> + local subcommand="$(__git_find_on_cmdline "$subcommands")"
> + if test -z "$subcommand"
> + then
> + __gitcomp "$subcommands"
> + return
> + fi
> +}
> +
>  _git_reset ()
>  {
>   __git_has_doubledash && return
> -- 
> 2.10.2

You didn't add 'rerere' to the list of porcelain commands, i.e. it
won't be listed after 'git '.  I'm not saying it should be
listed there, because I can't decide whether 'rerere' is considered
porcelain or plumbing...  Just wanted to make sure that this omission
was intentional.



Re: [PATCH 6/7] completion: teach remote subcommands option completion

2017-02-01 Thread SZEDER Gábor

> Git-remote needs to complete remote names, its subcommands, and options
> thereof. In addition to the existing subcommand and remote name
> completion, do also complete the options
> 
>  - add: --track --master --fetch --tags --no-tags --mirror=

Oh, '--track' and '--master' are not even in the manpage or in 'git
remote -h', I could only find them after looking at the source code...

Good eyes :)

>  - set-url: --push --add --delete
>  - get-url: --push --all
>  - prune: --dry-run
> ---
>  contrib/completion/git-completion.bash | 36 
> +++---
>  1 file changed, 29 insertions(+), 7 deletions(-)
> 
> diff --git a/contrib/completion/git-completion.bash 
> b/contrib/completion/git-completion.bash
> index e76cbd7..0e09519 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -2384,24 +2384,46 @@ _git_config ()
>  
>  _git_remote ()
>  {
> - local subcommands="add rename remove set-head set-branches set-url show 
> prune update"
> + local subcommands="
> + add rename remove set-head set-branches
> + get-url set-url show prune update
> + "
>   local subcommand="$(__git_find_on_cmdline "$subcommands")"
>   if [ -z "$subcommand" ]; then
> - __gitcomp "$subcommands"
> + case "$cur" in
> + --*)
> + __gitcomp "--verbose"
> + ;;
> + *)
> + __gitcomp "$subcommands"
> + ;;
> + esac
>   return
>   fi
>  
> - case "$subcommand" in
> - rename|remove|set-url|show|prune)
> - __gitcomp_nl "$(__git_remotes)"
> + case "$subcommand,$cur" in
> + add,--*)
> + __gitcomp "--track --master --fetch --tags --no-tags --mirror="
>   ;;
> - set-head|set-branches)
> + add,*)
> + ;;
> + set-head,*|set-branches,*)

The 'set-head' subcommand has '--auto' and '--delete' options, and
'set-branches' has '--add'.

>   __git_complete_remote_or_refspec
>   ;;
> - update)
> + update,*)

The 'update' subcommand has a '--prune' option.

Otherwise it all looks good.


>   __gitcomp "$(__git_get_config_variables "remotes")"
>   ;;
> + set-url,--*)
> + __gitcomp "--push --add --delete"
> + ;;
> + get-url,--*)
> + __gitcomp "--push --all"
> + ;;
> + prune,--*)
> + __gitcomp "--dry-run"
> + ;;
>   *)
> + __gitcomp_nl "$(__git_remotes)"
>   ;;
>   esac
>  }
> -- 
> 2.10.2




Re: [PATCH 4/7] completion: teach ls-remote to complete options

2017-02-01 Thread SZEDER Gábor

> ls-remote needs to complete remote names and its own options.

And refnames, too.

> In
> addition to the existing remote name completions, do also complete
> the options --heads, --tags, --refs, and --get-url.

Why only these four options and not the other four?

There are eight options in total here, so there is really no chance
for cluttered terminals, and all eight are mentioned in the synopsis.

> ---
>  contrib/completion/git-completion.bash | 6 ++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/contrib/completion/git-completion.bash 
> b/contrib/completion/git-completion.bash
> index 652c7e2..36fe439 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1449,6 +1449,12 @@ _git_ls_files ()
>  
>  _git_ls_remote ()
>  {
> + case "$cur" in
> + --*)
> + __gitcomp "--heads --tags --refs --get-url"
> + return
> + ;;
> + esac
>   __gitcomp_nl "$(__git_remotes)"
>  }
>  
> -- 
> 2.10.2




Re: [PATCH v2 7/7] completion: recognize more long-options

2017-02-01 Thread SZEDER Gábor
On Wed, Feb 1, 2017 at 5:49 PM, Cornelius Weig
 wrote:
> Hi Gabor,
>
>  thanks for taking a look at these commits.
>
> On 01/31/2017 11:17 PM, SZEDER Gábor wrote:
>> On Fri, Jan 27, 2017 at 10:17 PM,   wrote:
>>> From: Cornelius Weig 
>>>
>>> Recognize several new long-options for bash completion in the following
>>> commands:
>>
>> Adding more long options that git commands learn along the way is
>> always an improvement.  However, seeing "_several_ new long options"
>> (or "some long options" in one of the other patches in the series)
>> makes the reader wonder: are these the only new long options missing
>> or are there more?  If there are more, why only these are added?  If
>> there aren't any more missing long options left, then please say so,
>> e.g. "Add all missing long options, except the potentially
>> desctructive ones, for the following commands: "
>
> Personally, I agree with you that
>> Adding more long options that git commands learn along the way is
>> always an improvement.
> However, people may start complaining that their terminal becomes too
> cluttered when doing a double-Tab. In my cover letter, I go to length
> about this. My assumption was that all options that are mentioned in the
> introduction of the command man-page should be important enough to have
> them in the completion list.

But that doesn't mean that the ones not mentioned in the synopsis
section are not worth completing.

The list of options listed by the completion script for several of
these commands fits on a single line.  The command with the most
options among these is 'git pull', and even its options don't fill
more than half of a 80x25 screen.  I see no danger of people coming
complaining.

> I'll change my commit message accordingly.
>
>>>  - rm: --force
>>
>> '--force' is a potentially destructive option, too.
>
> Thanks for spotting this.
>
> Btw, I haven't found that non-destructive options should not be eligible
> for completion. To avoid confusion about this in the future, I suggest
> to also change the documentation:
>
> index 933bb6e..96f1c7f 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -13,7 +13,7 @@
>  #*) git email aliases for git-send-email
>  #*) tree paths within 'ref:path/to/file' expressions
>  #*) file paths within current working directory and index
> -#*) common --long-options
> +#*) common non-destructive --long-options

I don't mind such a change, but I don't think that list was ever meant
to be comprehensive or decisive.  It is definitely not the former, as
it's missing several things that the completion script does support.
OTOH, it talks about .git/remotes, which has been considered legacy
for quite some years (though it's right, because the completion script
still supports it).

> I take it you have also looked at the code itself? Then I would gladly
> mention you as reviewer in my sign-off.

Yeah, most of the changes was rather straightforward, except the
completion of 'git remote's subcommands' options, but that looks
good, too.

Gábor


[PATCHv2 16/21] completion: respect 'git -C '

2017-02-02 Thread SZEDER Gábor
'git -C ' option(s) on the command line should be taken into
account during completion, because

  - like '--git-dir=', it can lead us to a different repository,

  - a few git commands executed in the completion script do care about
in which directory they are executed, and

  - the command for which we are providing completion might care about
in which directory it will be executed.

However, unlike '--git-dir=', the '-C ' option can be
specified multiple times and their effect is cumulative, so we can't
just store a single '' in a variable.  Nor can we simply
concatenate a path from '-C  -C  ...', because e.g. (in
an arguably pathological corner case) a relative path might be
followed by an absolute path.

Instead, store all '-C ' options word by word in the
$__git_C_args array in the main git completion function, and pass this
array, if present, to 'git rev-parse --absolute-git-dir' when
discovering the repository in __gitdir(), and let it take care of
multiple options, relative paths, absolute paths and everything.

Also pass all '-C  options via the $__git_C_args array to those
git executions which require a worktree and for which it matters from
which directory they are executed from.  There are only three such
cases:

  - 'git diff-index' and 'git ls-files' in __git_ls_files_helper()
used for git-aware filename completion, and

  - the 'git ls-tree' used for completing the 'ref:path' notation.

The other git commands executed in the completion script don't need
these '-C ' options, because __gitdir() already took those
options into account.  It would not hurt them, either, but let's not
induce unnecessary code churn.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 19 ++--
 t/t9902-completion.sh  | 87 ++
 2 files changed, 101 insertions(+), 5 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index ac5eb9ced..e003afd54 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -39,7 +39,11 @@ esac
 __gitdir ()
 {
if [ -z "${1-}" ]; then
-   if [ -n "${__git_dir-}" ]; then
+   if [ -n "${__git_C_args-}" ]; then
+   git "${__git_C_args[@]}" \
+   ${__git_dir:+--git-dir="$__git_dir"} \
+   rev-parse --absolute-git-dir 2>/dev/null
+   elif [ -n "${__git_dir-}" ]; then
test -d "$__git_dir" || return 1
echo "$__git_dir"
elif [ -n "${GIT_DIR-}" ]; then
@@ -286,10 +290,10 @@ __git_ls_files_helper ()
local dir="$(__gitdir)"
 
if [ "$2" == "--committable" ]; then
-   git --git-dir="$dir" -C "$1" diff-index --name-only --relative 
HEAD
+   git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C 
"$1" diff-index --name-only --relative HEAD
else
# NOTE: $2 is not quoted in order to support multiple options
-   git --git-dir="$dir" -C "$1" ls-files --exclude-standard $2
+   git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C 
"$1" ls-files --exclude-standard $2
fi 2>/dev/null
 }
 
@@ -519,7 +523,7 @@ __git_complete_revlist_file ()
*)   pfx="$ref:$pfx" ;;
esac
 
-   __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 
2>/dev/null \
+   __gitcomp_nl "$(git ${__git_C_args:+"${__git_C_args[@]}"} 
--git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
| sed '/^100... blob /{
   s,^.*,,
   s,$, ,
@@ -2792,6 +2796,7 @@ _git_worktree ()
 __git_main ()
 {
local i c=1 command __git_dir
+   local __git_C_args C_args_count=0
 
while [ $c -lt $cword ]; do
i="${words[c]}"
@@ -2800,7 +2805,11 @@ __git_main ()
--git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
--bare)  __git_dir="." ;;
--help) command="help"; break ;;
-   -c|-C|--work-tree|--namespace) ((c++)) ;;
+   -c|--work-tree|--namespace) ((c++)) ;;
+   -C) __git_C_args[C_args_count++]=-C
+   ((c++))
+   __git_C_args[C_args_count++]="${words[c]}"
+

[PATCHv2 11/21] completion: don't list 'HEAD' when trying refs completion outside of a repo

2017-02-02 Thread SZEDER Gábor
When refs completion is attempted while not in a git repository, the
completion script offers 'HEAD' erroneously.

Check early in __git_refs() that there is either a repository or a
remote to work on, and return early if neither is given.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 8 ++--
 t/t9902-completion.sh  | 2 +-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index fd0ba1f3b..6b489b7c8 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -346,7 +346,11 @@ __git_refs ()
local list_refs_from=path remote="${1-}"
local format refs pfx
 
-   if [ -n "$remote" ]; then
+   if [ -z "$remote" ]; then
+   if [ -z "$dir" ]; then
+   return
+   fi
+   else
if __git_is_configured_remote "$remote"; then
# configured remote takes precedence over a
# local directory with the same name
@@ -360,7 +364,7 @@ __git_refs ()
fi
fi
 
-   if [ "$list_refs_from" = path ] && [ -d "$dir" ]; then
+   if [ "$list_refs_from" = path ]; then
case "$cur" in
refs|refs/*)
format="refname"
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index a201b5212..5b4defaa5 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -599,7 +599,7 @@ test_expect_success '__git_refs - non-existing URL remote - 
full refs' '
test_must_be_empty "$actual"
 '
 
-test_expect_failure '__git_refs - not in a git repository' '
+test_expect_success '__git_refs - not in a git repository' '
(
GIT_CEILING_DIRECTORIES="$ROOT" &&
export GIT_CEILING_DIRECTORIES &&
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 07/21] completion: ensure that the repository path given on the command line exists

2017-02-02 Thread SZEDER Gábor
The __gitdir() helper function prints the path to the git repository
to its stdout or stays silent and returns with error when it can't
find a repository or when the repository given via $GIT_DIR doesn't
exist.

This is not the case, however, when the path in $__git_dir, i.e. the
path to the repository specified on the command line via 'git
--git-dir=', doesn't exist: __gitdir() still outputs it as if it
were a real existing repository, making some completion functions
believe that they operate on an existing repository.

Check that the path in $__git_dir exists and return with error without
printing anything to stdout if it doesn't.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 1 +
 t/t9902-completion.sh  | 8 
 2 files changed, 9 insertions(+)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index ee6fb2259..5b2bd6721 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -40,6 +40,7 @@ __gitdir ()
 {
if [ -z "${1-}" ]; then
if [ -n "${__git_dir-}" ]; then
+   test -d "$__git_dir" || return 1
echo "$__git_dir"
elif [ -n "${GIT_DIR-}" ]; then
test -d "${GIT_DIR-}" || return 1
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 7956cb9b1..7667baabf 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -211,6 +211,14 @@ test_expect_success '__gitdir - $GIT_DIR set while .git 
directory in parent' '
test_cmp expected "$actual"
 '
 
+test_expect_success '__gitdir - non-existing path in $__git_dir' '
+   (
+   __git_dir="non-existing" &&
+   test_must_fail __gitdir >"$actual"
+   ) &&
+   test_must_be_empty "$actual"
+'
+
 test_expect_success '__gitdir - non-existing $GIT_DIR' '
(
GIT_DIR="$ROOT/non-existing" &&
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 12/21] completion: list short refs from a remote given as a URL

2017-02-02 Thread SZEDER Gábor
e832f5c09680 (completion: avoid ls-remote in certain scenarios,
2013-05-28) turned a 'git ls-remote ' query into a 'git
for-each-ref refs/remotes//' to improve responsiveness of
remote refs completion by avoiding potential network communication.
However, it inadvertently made impossible to complete short refs from
a remote given as a URL, e.g. 'git fetch git://server.com/repo.git
', because there is, of course, no such thing as
'refs/remotes/git://server.com/repo.git'.

Since the previous commit we tell apart configured remotes, i.e. those
that can have a hierarchy under 'refs/remotes/', from others that
don't, including remotes given as URL, so we know when we can't use
the faster 'git for-each-ref'-based approach.

Resurrect the old, pre-e832f5c09680 'git ls-remote'-based code for the
latter case to support listing short refs from remotes given as a URL.
The code is slightly updated from the original to

  - take into account the path to the repository given on the command
line (if any), and
  - omit 'ORIG_HEAD' from the query, as 'git ls-remote' will never
list it anyway.

When the remote given to __git_refs() doesn't exist, then it will be
handled by this resurrected 'git ls-remote' query.  This code path
doesn't list 'HEAD' unconditionally, which has the nice side effect of
fixing two more expected test failures.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 19 ---
 t/t9902-completion.sh  |  6 +++---
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 6b489b7c8..4ded44977 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -338,6 +338,7 @@ __git_tags ()
 # Lists refs from the local (by default) or from a remote repository.
 # It accepts 0, 1 or 2 arguments:
 # 1: The remote to list refs from (optional; ignored, if set but empty).
+#Can be the name of a configured remote, a path, or a URL.
 # 2: In addition to local refs, list unique branches from refs/remotes/ for
 #'git checkout's tracking DWIMery (optional; ignored, if set but empty).
 __git_refs ()
@@ -410,9 +411,21 @@ __git_refs ()
done
;;
*)
-   echo "HEAD"
-   git --git-dir="$dir" for-each-ref --format="%(refname:short)" \
-   "refs/remotes/$remote/" 2>/dev/null | sed -e 
"s#^$remote/##"
+   if [ "$list_refs_from" = remote ]; then
+   echo "HEAD"
+   git --git-dir="$dir" for-each-ref 
--format="%(refname:short)" \
+   "refs/remotes/$remote/" 2>/dev/null | sed -e 
"s#^$remote/##"
+   else
+   git --git-dir="$dir" ls-remote "$remote" HEAD \
+   "refs/tags/*" "refs/heads/*" "refs/remotes/*" 
2>/dev/null |
+   while read -r hash i; do
+   case "$i" in
+   *^{})   ;;
+   refs/*) echo "${i#refs/*/}" ;;
+   *)  echo "$i" ;;  # symbolic refs
+   esac
+   done
+   fi
;;
esac
 }
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 5b4defaa5..500505dca 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -540,7 +540,7 @@ test_expect_success '__git_refs - configured remote - 
remote name matches a dire
test_cmp expected "$actual"
 '
 
-test_expect_failure '__git_refs - URL remote' '
+test_expect_success '__git_refs - URL remote' '
cat >expected <<-EOF &&
HEAD
branch-in-other
@@ -567,7 +567,7 @@ test_expect_success '__git_refs - URL remote - full refs' '
test_cmp expected "$actual"
 '
 
-test_expect_failure '__git_refs - non-existing remote' '
+test_expect_success '__git_refs - non-existing remote' '
(
cur= &&
__git_refs non-existing >"$actual"
@@ -583,7 +583,7 @@ test_expect_success '__git_refs - non-existing remote - 
full refs' '
test_must_be_empty "$actual"
 '
 
-test_expect_failure '__git_refs - non-existing URL remote' '
+test_expect_success '__git_refs - non-existing URL remote' '
(
cur= &&
__git_refs "file://$ROOT/non-existing" >"$actual"
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 05/21] completion tests: check __gitdir()'s output in the error cases

2017-02-02 Thread SZEDER Gábor
The __gitdir() helper function shouldn't output anything if not in a
git repository.  The relevant tests only checked its error code, so
extend them to ensure that there's no output.

Signed-off-by: SZEDER Gábor 
---
 t/t9902-completion.sh | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 030a16e77..f7f7d49fb 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -215,8 +215,9 @@ test_expect_success '__gitdir - non-existing $GIT_DIR' '
(
GIT_DIR="$ROOT/non-existing" &&
export GIT_DIR &&
-   test_must_fail __gitdir
-   )
+   test_must_fail __gitdir >"$actual"
+   ) &&
+   test_must_be_empty "$actual"
 '
 
 test_expect_success '__gitdir - gitfile in cwd' '
@@ -255,7 +256,8 @@ test_expect_success SYMLINKS '__gitdir - resulting path 
avoids symlinks' '
 '
 
 test_expect_success '__gitdir - not a git repository' '
-   nongit test_must_fail __gitdir
+   nongit test_must_fail __gitdir >"$actual" &&
+   test_must_be_empty "$actual"
 '
 
 test_expect_success '__gitcomp - trailing space - options' '
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 03/21] completion tests: make the $cur variable local to the test helper functions

2017-02-02 Thread SZEDER Gábor
The test helper functions test_gitcomp() and test_gitcomp_nl() leak
the $cur variable into the test environment.  Since this variable has
a special role in the Bash completion script (it holds the word
currently being completed) it influences the behavior of most
completion functions and thus this leakage could interfere with
subsequent tests.  Although there are no such issues in the current
tests, early versions of the new tests that will be added later in
this series suffered because of this.

It's better to play safe and declare $cur local in those test helper
functions.  'local' is bashism, of course, but the tests of the Bash
completion script are run under Bash anyway, and there are already
other variables declared local in this test script.

Signed-off-by: SZEDER Gábor 
---
 t/t9902-completion.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index f490c1d05..294a08cfe 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -98,7 +98,7 @@ test_gitcomp ()
 {
local -a COMPREPLY &&
sed -e 's/Z$//' >expected &&
-   cur="$1" &&
+   local cur="$1" &&
shift &&
__gitcomp "$@" &&
print_comp &&
@@ -113,7 +113,7 @@ test_gitcomp_nl ()
 {
local -a COMPREPLY &&
sed -e 's/Z$//' >expected &&
-   cur="$1" &&
+   local cur="$1" &&
shift &&
__gitcomp_nl "$@" &&
print_comp &&
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 04/21] completion tests: consolidate getting path of current working directory

2017-02-02 Thread SZEDER Gábor
Some tests of the __gitdir() helper function use the $TRASH_DIRECTORY
variable in direct path comparisons.  In general this should be
avoided, because it might contain symbolic links.  There happens to be
no issues with this here, however, because those tests use
$TRASH_DIRECTORY both for specifying the expected result and for
specifying input which in turn is just 'echo'ed verbatim.

Other __gitdir() tests ask for the path of the trash directory by
running $(pwd -P) in each test, sometimes even twice in a single test.

Run $(pwd) only once at the beginning of the test script to store the
path of the trash directory in a variable, and use that variable in
all __gitdir() tests.

Signed-off-by: SZEDER Gábor 
---
 t/t9902-completion.sh | 44 +---
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 294a08cfe..030a16e77 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -124,15 +124,22 @@ invalid_variable_name='${foo.bar}'
 
 actual="$TRASH_DIRECTORY/actual"
 
+if test_have_prereq MINGW
+then
+   ROOT="$(pwd -W)"
+else
+   ROOT="$(pwd)"
+fi
+
 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 &&
+   echo "$ROOT/otherrepo/.git" >expected &&
(
-   __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
+   __git_dir="$ROOT/otherrepo/.git" &&
__gitdir >"$actual"
) &&
test_cmp expected "$actual"
@@ -157,7 +164,7 @@ test_expect_success '__gitdir - .git directory in cwd' '
 '
 
 test_expect_success '__gitdir - .git directory in parent' '
-   echo "$(pwd -P)/.git" >expected &&
+   echo "$ROOT/.git" >expected &&
(
cd subdir/subsubdir &&
__gitdir >"$actual"
@@ -175,7 +182,7 @@ test_expect_success '__gitdir - cwd is a .git directory' '
 '
 
 test_expect_success '__gitdir - parent is a .git directory' '
-   echo "$(pwd -P)/.git" >expected &&
+   echo "$ROOT/.git" >expected &&
(
cd .git/refs/heads &&
__gitdir >"$actual"
@@ -184,9 +191,9 @@ test_expect_success '__gitdir - parent is a .git directory' 
'
 '
 
 test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
-   echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+   echo "$ROOT/otherrepo/.git" >expected &&
(
-   GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+   GIT_DIR="$ROOT/otherrepo/.git" &&
export GIT_DIR &&
__gitdir >"$actual"
) &&
@@ -194,9 +201,9 @@ test_expect_success '__gitdir - $GIT_DIR set while .git 
directory in cwd' '
 '
 
 test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
-   echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+   echo "$ROOT/otherrepo/.git" >expected &&
(
-   GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+   GIT_DIR="$ROOT/otherrepo/.git" &&
export GIT_DIR &&
cd subdir &&
__gitdir >"$actual"
@@ -206,24 +213,15 @@ test_expect_success '__gitdir - $GIT_DIR set while .git 
directory in parent' '
 
 test_expect_success '__gitdir - non-existing $GIT_DIR' '
(
-   GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+   GIT_DIR="$ROOT/non-existing" &&
export GIT_DIR &&
test_must_fail __gitdir
)
 '
 
-function pwd_P_W () {
-   if test_have_prereq MINGW
-   then
-   pwd -W
-   else
-   pwd -P
-   fi
-}
-
 test_expect_success '__gitdir - gitfile in cwd' '
-   echo "$(pwd_P_W)/otherrepo/.git" >expected &&
-   echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git &&
+   echo "$ROOT/otherrepo/.git" >expected &&
+   echo "gitdir: $ROOT/otherrepo/.git" >subdir/.git &&
test_when_finished "rm -f

[PATCHv2 02/21] completion tests: don't add test cruft to the test repository

2017-02-02 Thread SZEDER Gábor
While preparing commits, three tests added newly created files to the
index using 'git add .', which added not only the files in question
but leftover test cruft from previous tests like the files 'expected'
and 'actual' as well.  Luckily, this had no effect on the tests'
correctness.

Add only the files we are actually interested in.

Signed-off-by: SZEDER Gábor 
---
 t/t9902-completion.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index a34e55f87..f490c1d05 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -486,7 +486,7 @@ test_expect_success 'git --help completion' '
 test_expect_success 'setup for ref completion' '
echo content >file1 &&
echo more >file2 &&
-   git add . &&
+   git add file1 file2 &&
git commit -m one &&
git branch mybranch &&
git tag mytag
@@ -517,7 +517,7 @@ test_expect_success ': completes paths' '
 
 test_expect_success 'complete tree filename with spaces' '
echo content >"name with spaces" &&
-   git add . &&
+   git add "name with spaces" &&
git commit -m spaces &&
test_completion "git show HEAD:nam" <<-\EOF
name with spaces Z
@@ -526,7 +526,7 @@ test_expect_success 'complete tree filename with spaces' '
 
 test_expect_success 'complete tree filename with metacharacters' '
echo content >"name with \${meta}" &&
-   git add . &&
+   git add "name with \${meta}" &&
git commit -m meta &&
test_completion "git show HEAD:nam" <<-\EOF
name with ${meta} Z
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 00/21] completion: various __gitdir()-related improvements

2017-02-02 Thread SZEDER Gábor
This is a long overdue reroll of a bunch of bugfixes, cleanups and
optimizations related to how the completion script finds the path to
the repository and how it uses that path.  Most importantly this
series adds support for completion following 'git -C path', and it
eliminates a few subshells and git processes, for the sake of
fork()+exec() challenged OSes.

The first round is at [1].  It made its way to pu back then, but since
the reroll didn't come it was eventually discarded.

What did NOT change since v1 is that the new option added to 'git
rev-parse' is still called '--absolute-git-dir'.  There was a
suggestion [2] to turn it into an orthogonal '--absolute-dir' option
that works with other path-querying options, too, but I really doubt
it's worth it.  In short, regular scripts don't care, because a
relative path doesn't make any difference for them, and before we do
this orthogonal thing we have to decide a bunch of questions first,
see [3].

Changes since v1:

 - Use our real_path() instead of system realpath() to implement 'git
   rev-parse --absolute-git-dir'.
 - Refactored a bit how __git_refs() determines where it should list
   refs from.
 - Renamed a few refnames and remote in the tests (this accounts for
   the bulk of the interdiff).
 - Misc small adjustments: a few more comments, removed unnecessary
   disambiguating '--', typofix and more consistent quoting.
 - Improved commit messages.
 - Rebased to current master.

The interdiff below is compared to v1 rebased on top of current master.

This series is also available at

  https://github.com/szeder/git completion-gitdir-improvements


[1] - 
http://public-inbox.org/git/1456440650-32623-1-git-send-email-sze...@ira.uka.de/T/

[2] - 
http://public-inbox.org/git/CANoM8SXO_Rz_CVOz9ptsaVCzcQ2D1FQrSuFFW4vZ4SdRYMzD=w...@mail.gmail.com/

[3] - 
http://public-inbox.org/git/20160518185825.horde.epd2njnvqew_vx4b01yw...@webmail.informatik.kit.edu/

SZEDER Gábor (21):
  completion: improve __git_refs()'s in-code documentation
  completion tests: don't add test cruft to the test repository
  completion tests: make the $cur variable local to the test helper
functions
  completion tests: consolidate getting path of current working
directory
  completion tests: check __gitdir()'s output in the error cases
  completion tests: add tests for the __git_refs() helper function
  completion: ensure that the repository path given on the command line
exists
  completion: fix most spots not respecting 'git --git-dir='
  completion: respect 'git --git-dir=' when listing remote refs
  completion: list refs from remote when remote's name matches a
directory
  completion: don't list 'HEAD' when trying refs completion outside of a
repo
  completion: list short refs from a remote given as a URL
  completion: don't offer commands when 'git --opt' needs an argument
  completion: fix completion after 'git -C '
  rev-parse: add '--absolute-git-dir' option
  completion: respect 'git -C '
  completion: don't use __gitdir() for git commands
  completion: consolidate silencing errors from git commands
  completion: don't guard git executions with __gitdir()
  completion: extract repository discovery from __gitdir()
  completion: cache the path to the repository

 Documentation/git-rev-parse.txt|   4 +
 builtin/rev-parse.c|  26 +-
 contrib/completion/git-completion.bash | 250 ++-
 t/t1500-rev-parse.sh   |  17 +-
 t/t9902-completion.sh  | 561 +
 5 files changed, 690 insertions(+), 168 deletions(-)

-- 
2.11.0.555.g967c1bcb3

diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 4040b3c86..1967bafba 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -820,10 +820,7 @@ int cmd_rev_parse(int argc, const char **argv, const char 
*prefix)
if (!gitdir && !prefix)
gitdir = ".git";
if (gitdir) {
-   char absolute_path[PATH_MAX];
-   if (!realpath(gitdir, 
absolute_path))
-   die_errno(_("unable to 
get absolute path"));
-   puts(absolute_path);
+   puts(real_path(gitdir));
continue;
}
}
diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 0184d0ebc..ed06cb621 100644
--- a/contrib/completion/git-completion.bash
+++ b/

[PATCHv2 13/21] completion: don't offer commands when 'git --opt' needs an argument

2017-02-02 Thread SZEDER Gábor
The main git options '--git-dir', '-c', '-C', '--worktree' and
'--namespace' require an argument, but attempting completion right
after them lists git commands.

Don't offer anything right after these options, thus let Bash fall
back to filename completion, because

  - the three options '--git-dir', '-C' and '--worktree' do actually
require a path argument, and

  - we don't complete the required argument of '-c' and '--namespace',
and in that case the "standard" behavior of our completion script
is to not offer anything, but fall back to filename completion.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 4ded44977..7d25b33b8 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2808,6 +2808,17 @@ __git_main ()
done
 
if [ -z "$command" ]; then
+   case "$prev" in
+   --git-dir|-C|--work-tree)
+   # these need a path argument, let's fall back to
+   # Bash filename completion
+   return
+   ;;
+   -c|--namespace)
+   # we don't support completing these options' arguments
+   return
+   ;;
+   esac
case "$cur" in
--*)   __gitcomp "
--paginate
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 18/21] completion: consolidate silencing errors from git commands

2017-02-02 Thread SZEDER Gábor
Outputting error messages during completion is bad: they disrupt the
command line, can't be deleted, and the user is forced to Ctrl-C and
start over most of the time.  We already silence stderr of many git
commands in our Bash completion script, but there are still some in
there that can spew error messages when something goes wrong.

We could add the missing stderr redirections to all the remaining
places, but instead let's leverage that git commands are now executed
through the previously introduced __git() wrapper function, and
redirect standard error to /dev/null only in that function.  This way
we need only one redirection to take care of errors from almost all
git commands.  Redirecting standard error of the __git() wrapper
function thus became redundant, remove them.

The exceptions, i.e. the repo-independent git executions and those in
the __gitdir() function that don't go through __git() already have
their standard error silenced.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 28 
 1 file changed, 12 insertions(+), 16 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index e7c0b56ea..1a849761f 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -66,7 +66,7 @@ __gitdir ()
 __git ()
 {
git ${__git_C_args:+"${__git_C_args[@]}"} \
-   ${__git_dir:+--git-dir="$__git_dir"} "$@"
+   ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null
 }
 
 # The following function is based on code from:
@@ -300,7 +300,7 @@ __git_ls_files_helper ()
else
# NOTE: $2 is not quoted in order to support multiple options
__git -C "$1" ls-files --exclude-standard $2
-   fi 2>/dev/null
+   fi
 }
 
 
@@ -410,7 +410,7 @@ __git_refs ()
fi
case "$cur" in
refs|refs/*)
-   __git ls-remote "$remote" "$cur*" 2>/dev/null | \
+   __git ls-remote "$remote" "$cur*" | \
while read -r hash i; do
case "$i" in
*^{}) ;;
@@ -422,10 +422,10 @@ __git_refs ()
if [ "$list_refs_from" = remote ]; then
echo "HEAD"
__git for-each-ref --format="%(refname:short)" \
-   "refs/remotes/$remote/" 2>/dev/null | sed -e 
"s#^$remote/##"
+   "refs/remotes/$remote/" | sed -e "s#^$remote/##"
else
__git ls-remote "$remote" HEAD \
-   "refs/tags/*" "refs/heads/*" "refs/remotes/*" 
2>/dev/null |
+   "refs/tags/*" "refs/heads/*" "refs/remotes/*" |
while read -r hash i; do
case "$i" in
*^{})   ;;
@@ -451,7 +451,7 @@ __git_refs2 ()
 __git_refs_remotes ()
 {
local i hash
-   __git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
+   __git ls-remote "$1" 'refs/heads/*' | \
while read -r hash i; do
echo "$i:refs/remotes/$1/${i#refs/heads/}"
done
@@ -527,7 +527,7 @@ __git_complete_revlist_file ()
*)   pfx="$ref:$pfx" ;;
esac
 
-   __gitcomp_nl "$(__git ls-tree "$ls" 2>/dev/null \
+   __gitcomp_nl "$(__git ls-tree "$ls" \
| sed '/^100... blob /{
   s,^.*,,
   s,$, ,
@@ -805,7 +805,7 @@ __git_compute_porcelain_commands ()
 __git_get_config_variables ()
 {
local section="$1" i IFS=$'\n'
-   for i in $(__git config --name-only --get-regexp "^$section\..*" 
2>/dev/null); do
+   for i in $(__git config --name-only --get-regexp "^$section\..*"); do
echo "${i#$section.}"
done
 }
@@ -823,7 +823,7 @@ __git_aliases ()
 # __git_aliased_command requires 1 argument
 __git_aliased_command ()
 {
-   local word cmdline=$(__git config --get "alias.$1" 2>/dev/null)
+   local word cmdline=$(__git config --get "alias.$1")
for word in $cmdline; do
case "$word" in
\!gitk|gitk)
@@ -1841,9 +1841,7 @@ _git_send_email ()
 {
case "$prev" in
--to|--cc|--bcc|--from)
-   __gitcomp "
-   $(__git send-email --dump-aliase

[PATCHv2 21/21] completion: cache the path to the repository

2017-02-02 Thread SZEDER Gábor
After the previous changes in this series there are only a handful of
$(__gitdir) command substitutions left in the completion script, but
there is still a bit of room for improvements:

  1. The command substitution involves the forking of a subshell,
 which has considerable overhead on some platforms.

  2. There are a few cases, where this command substitution is
 executed more than once during a single completion, which means
 multiple subshells and possibly multiple 'git rev-parse'
 executions.  __gitdir() is invoked twice while completing refs
 for e.g. 'git log', 'git rebase', 'gitk', or while completing
 remote refs for 'git fetch' or 'git push'.

Both of these points can be addressed by using the
__git_find_repo_path() helper function introduced in the previous
commit:

  1. __git_find_repo_path() stores the path to the repository in a
 variable instead of printing it, so the command substitution
 around the function can be avoided.  Or rather: the command
 substitution should be avoided to make the new value of the
 variable set inside the function visible to the callers.
 (Yes, there is now a command substitution inside
 __git_find_repo_path() around each 'git rev-parse', but that's
 executed only if necessary, and only once per completion, see
 point 2. below.)

  2. $__git_repo_path, the variable holding the path to the
 repository, is declared local in the toplevel completion
 functions __git_main() and __gitk_main().  Thus, once set, the
 path is visible in all completion functions, including all
 subsequent calls to __git_find_repo_path(), meaning that they
 wouldn't have to re-discover the path to the repository.

So call __git_find_repo_path() and use $__git_repo_path instead of the
$(__gitdir) command substitution to access paths in the .git
directory.  Turn tests checking __gitdir()'s repository discovery into
tests of __git_find_repo_path() such that only the tested function
changes but the expected results don't, ensuring that repo discovery
keeps working as it did before.

As __gitdir() is not used anymore in the completion script, mark it as
deprecated and direct users' attention to __git_find_repo_path() and
$__git_repo_path.  Yet keep four __gitdir() tests to ensure that it
handles success and failure of __git_find_repo_path() and that it
still handles its optional remote argument, because users' custom
completion scriptlets might depend on it.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash |  46 ++
 t/t9902-completion.sh  | 161 +
 2 files changed, 132 insertions(+), 75 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 7775411cd..ed06cb621 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -39,6 +39,11 @@ esac
 # variable.
 __git_find_repo_path ()
 {
+   if [ -n "$__git_repo_path" ]; then
+   # we already know where it is
+   return
+   fi
+
if [ -n "${__git_C_args-}" ]; then
__git_repo_path="$(git "${__git_C_args[@]}" \
${__git_dir:+--git-dir="$__git_dir"} \
@@ -56,6 +61,7 @@ __git_find_repo_path ()
fi
 }
 
+# Deprecated: use __git_find_repo_path() and $__git_repo_path instead
 # __gitdir accepts 0 or 1 arguments (i.e., location)
 # returns location of .git repo
 __gitdir ()
@@ -350,10 +356,13 @@ __git_tags ()
 #'git checkout's tracking DWIMery (optional; ignored, if set but empty).
 __git_refs ()
 {
-   local i hash dir="$(__gitdir)" track="${2-}"
+   local i hash dir track="${2-}"
local list_refs_from=path remote="${1-}"
local format refs pfx
 
+   __git_find_repo_path
+   dir="$__git_repo_path"
+
if [ -z "$remote" ]; then
if [ -z "$dir" ]; then
return
@@ -458,8 +467,8 @@ __git_refs_remotes ()
 
 __git_remotes ()
 {
-   local d="$(__gitdir)"
-   test -d "$d/remotes" && ls -1 "$d/remotes"
+   __git_find_repo_path
+   test -d "$__git_repo_path/remotes" && ls -1 "$__git_repo_path/remotes"
__git remote
 }
 
@@ -957,8 +966,8 @@ __git_whitespacelist="nowarn warn error error-all fix"
 
 _git_am ()
 {
-   local dir="$(__gitdir)"
-   if [ -d "$dir"/rebase-apply ]; then
+   __git_find_repo_path
+   if [ -d "$__git_repo_path"/rebase-apply ]; then
__gitcomp "--skip --continue --resolved --abort"
return
fi
@@ -1041,7 +1050,8 @@ _git_bise

[PATCHv2 08/21] completion: fix most spots not respecting 'git --git-dir='

2017-02-02 Thread SZEDER Gábor
The completion script already respects the path to the repository
specified on the command line most of the time, here we add the
necessary '--git-dir=$(__gitdir)' options to most of the places where
git was executed without it.  The exceptions where said option is not
added are the git invocations:

  - in __git_refs() which are non-trivial and will be the subject of
the following patch,

  - getting the list of git commands, merge strategies and archive
formats, because these are independent from the repository and
thus don't need it, and

  - the 'git rev-parse --git-dir' in __gitdir() itself.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 12 +++-
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 5b2bd6721..295f6de24 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -283,11 +283,13 @@ __gitcomp_file ()
 # argument, and using the options specified in the second argument.
 __git_ls_files_helper ()
 {
+   local dir="$(__gitdir)"
+
if [ "$2" == "--committable" ]; then
-   git -C "$1" diff-index --name-only --relative HEAD
+   git --git-dir="$dir" -C "$1" diff-index --name-only --relative 
HEAD
else
# NOTE: $2 is not quoted in order to support multiple options
-   git -C "$1" ls-files --exclude-standard $2
+   git --git-dir="$dir" -C "$1" ls-files --exclude-standard $2
fi 2>/dev/null
 }
 
@@ -408,7 +410,7 @@ __git_refs2 ()
 __git_refs_remotes ()
 {
local i hash
-   git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
+   git --git-dir="$(__gitdir)" ls-remote "$1" 'refs/heads/*' 2>/dev/null | 
\
while read -r hash i; do
echo "$i:refs/remotes/$1/${i#refs/heads/}"
done
@@ -1186,7 +1188,7 @@ _git_commit ()
return
esac
 
-   if git rev-parse --verify --quiet HEAD >/dev/null; then
+   if git --git-dir="$(__gitdir)" rev-parse --verify --quiet HEAD 
>/dev/null; then
__git_complete_index_file "--committable"
else
# This is the first commit
@@ -1486,7 +1488,7 @@ _git_log ()
 {
__git_has_doubledash && return
 
-   local g="$(git rev-parse --git-dir 2>/dev/null)"
+   local g="$(__gitdir)"
local merge=""
if [ -f "$g/MERGE_HEAD" ]; then
merge="--merge"
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 20/21] completion: extract repository discovery from __gitdir()

2017-02-02 Thread SZEDER Gábor
To prepare for caching the path to the repository in the following
commit, extract the repository discovering part of __gitdir() into the
__git_find_repo_path() helper function, which stores the found path in
the $__git_repo_path variable instead of printing it.  Make __gitdir()
a wrapper around this new function.  Declare $__git_repo_path local in
the toplevel completion functions __git_main() and __gitk_main() to
ensure that it never leaks into the environment and influences
subsequent completions (though this isn't necessary right now, as
__gitdir() is still only executed in subshells, but will matter for
the following commit).

Adjust tests checking __gitdir() or any other completion function
calling __gitdir() to perform those checks in a subshell to prevent
$__git_repo_path from leaking into the test environment.  Otherwise
leave the tests unchanged to demonstrate that this change doesn't
alter __gitdir()'s behavior.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 42 +-
 t/t9902-completion.sh  | 22 +-
 2 files changed, 42 insertions(+), 22 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 1c7493ff2..7775411cd 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -34,26 +34,35 @@ case "$COMP_WORDBREAKS" in
 *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
 esac
 
+# Discovers the path to the git repository taking any '--git-dir=' and
+# '-C ' options into account and stores it in the $__git_repo_path
+# variable.
+__git_find_repo_path ()
+{
+   if [ -n "${__git_C_args-}" ]; then
+   __git_repo_path="$(git "${__git_C_args[@]}" \
+   ${__git_dir:+--git-dir="$__git_dir"} \
+   rev-parse --absolute-git-dir 2>/dev/null)"
+   elif [ -n "${__git_dir-}" ]; then
+   test -d "$__git_dir" &&
+   __git_repo_path="$__git_dir"
+   elif [ -n "${GIT_DIR-}" ]; then
+   test -d "${GIT_DIR-}" &&
+   __git_repo_path="$GIT_DIR"
+   elif [ -d .git ]; then
+   __git_repo_path=.git
+   else
+   __git_repo_path="$(git rev-parse --git-dir 2>/dev/null)"
+   fi
+}
+
 # __gitdir accepts 0 or 1 arguments (i.e., location)
 # returns location of .git repo
 __gitdir ()
 {
if [ -z "${1-}" ]; then
-   if [ -n "${__git_C_args-}" ]; then
-   git "${__git_C_args[@]}" \
-   ${__git_dir:+--git-dir="$__git_dir"} \
-   rev-parse --absolute-git-dir 2>/dev/null
-   elif [ -n "${__git_dir-}" ]; then
-   test -d "$__git_dir" || return 1
-   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
+   __git_find_repo_path || return 1
+   echo "$__git_repo_path"
elif [ -d "$1/.git" ]; then
echo "$1/.git"
else
@@ -2783,7 +2792,7 @@ _git_worktree ()
 
 __git_main ()
 {
-   local i c=1 command __git_dir
+   local i c=1 command __git_dir __git_repo_path
local __git_C_args C_args_count=0
 
while [ $c -lt $cword ]; do
@@ -2855,6 +2864,7 @@ __gitk_main ()
 {
__git_has_doubledash && return
 
+   local __git_repo_path
local g="$(__gitdir)"
local merge=""
if [ -f "$g/MERGE_HEAD" ]; then
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 984df05b2..1816ed2e0 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -147,19 +147,25 @@ test_expect_success '__gitdir - from command line 
(through $__git_dir)' '
 
 test_expect_success '__gitdir - repo as argument' '
echo "otherrepo/.git" >expected &&
-   __gitdir "otherrepo" >"$actual" &&
+   (
+   __gitdir "otherrepo" >"$actual"
+   ) &&
test_cmp expected "$actual"
 '
 
 test_expect_success '__gitdir - remote as argument' '
echo "remote" >expected &&
-   __gitdir "remote" >"$actual" &&
+   (
+   __

[PATCHv2 10/21] completion: list refs from remote when remote's name matches a directory

2017-02-02 Thread SZEDER Gábor
If the remote given to __git_refs() happens to match both the name of
a configured remote and the name of a directory in the current working
directory, then that directory is assumed to be a git repository, and
listing refs from that directory will be attempted.  This is wrong,
because in such a situation git commands (e.g. 'git fetch|pull|push
' whom these refs will eventually be passed to) give
precedence to the configured remote.  Therefore, __git_refs() should
list refs from the configured remote as well.

Add the helper function __git_is_configured_remote() that checks
whether its argument matches the name of a configured remote.  Use
this helper to decide how to handle the remote passed to __git_refs().

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 20 ++--
 t/t9902-completion.sh  | 11 ++-
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 7d7e8b9d9..fd0ba1f3b 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -347,12 +347,16 @@ __git_refs ()
local format refs pfx
 
if [ -n "$remote" ]; then
-   if [ -d "$remote/.git" ]; then
+   if __git_is_configured_remote "$remote"; then
+   # configured remote takes precedence over a
+   # local directory with the same name
+   list_refs_from=remote
+   elif [ -d "$remote/.git" ]; then
dir="$remote/.git"
elif [ -d "$remote" ]; then
dir="$remote"
else
-   list_refs_from=remote
+   list_refs_from=url
fi
fi
 
@@ -435,6 +439,18 @@ __git_remotes ()
git --git-dir="$d" remote
 }
 
+# Returns true if $1 matches the name of a configured remote, false otherwise.
+__git_is_configured_remote ()
+{
+   local remote
+   for remote in $(__git_remotes); do
+   if [ "$remote" = "$1" ]; then
+   return 0
+   fi
+   done
+   return 1
+}
+
 __git_list_merge_strategies ()
 {
git merge -s help 2>&1 |
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 6e64cd6ba..a201b5212 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -373,6 +373,15 @@ test_expect_success '__git_remotes - list remotes from 
$GIT_DIR/remotes and from
test_cmp expect actual
 '
 
+test_expect_success '__git_is_configured_remote' '
+   test_when_finished "git remote remove remote_1" &&
+   git remote add remote_1 git://remote_1 &&
+   test_when_finished "git remote remove remote_2" &&
+   git remote add remote_2 git://remote_2 &&
+   verbose __git_is_configured_remote remote_2 &&
+   test_must_fail __git_is_configured_remote non-existent
+'
+
 test_expect_success 'setup for ref completion' '
git commit --allow-empty -m initial &&
git branch matching-branch &&
@@ -516,7 +525,7 @@ test_expect_success '__git_refs - configured remote - full 
refs - repo given on
test_cmp expected "$actual"
 '
 
-test_expect_failure '__git_refs - configured remote - remote name matches a 
directory' '
+test_expect_success '__git_refs - configured remote - remote name matches a 
directory' '
cat >expected <<-EOF &&
HEAD
branch-in-other
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 17/21] completion: don't use __gitdir() for git commands

2017-02-02 Thread SZEDER Gábor
Several completion functions contain the following pattern to run git
commands respecting the path to the repository specified on the
command line:

  git --git-dir="$(__gitdir)"  

This imposes the overhead of fork()ing a subshell for the command
substitution and potentially fork()+exec()ing 'git rev-parse' inside
__gitdir().

Now, if neither '--gitdir=' nor '-C ' options are
specified on the command line, then those git commands are perfectly
capable to discover the repository on their own.  If either one or
both of those options are specified on the command line, then, again,
the git commands could discover the repository, if we pass them all of
those options from the command line.

This means we don't have to run __gitdir() at all for git commands and
can spare its fork()+exec() overhead.

Use Bash parameter expansions to check the $__git_dir variable and
$__git_C_args array and to assemble the appropriate '--git-dir='
and '-C ' options if either one or both are present on the
command line.  These parameter expansions are, however, rather long,
so instead of changing all git executions and make already long lines
even longer, encapsulate running git with '--git-dir= -C '
options into the new __git() wrapper function.  Furthermore, this
wrapper function will also enable us to silence error messages from
git commands uniformly in one place in a later commit.

There's one tricky case, though: in __git_refs() local refs are listed
with 'git for-each-ref', where "local" is not necessarily the
repository we are currently in, but it might mean a remote repository
in the filesystem (e.g. listing refs for 'git fetch /some/other/repo
').  Use one-shot variable assignment to override $__git_dir with
the path of the repository where the refs should come from.  Although
one-shot variable assignments in front of shell functions are to be
avoided in our scripts in general, in the Bash completion script we
can do that safely.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 60 ++
 1 file changed, 31 insertions(+), 29 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index e003afd54..e7c0b56ea 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -61,6 +61,14 @@ __gitdir ()
fi
 }
 
+# Runs git with all the options given as argument, respecting any
+# '--git-dir=' and '-C ' options present on the command line
+__git ()
+{
+   git ${__git_C_args:+"${__git_C_args[@]}"} \
+   ${__git_dir:+--git-dir="$__git_dir"} "$@"
+}
+
 # The following function is based on code from:
 #
 #   bash_completion - programmable completion functions for bash 3.2+
@@ -287,13 +295,11 @@ __gitcomp_file ()
 # argument, and using the options specified in the second argument.
 __git_ls_files_helper ()
 {
-   local dir="$(__gitdir)"
-
if [ "$2" == "--committable" ]; then
-   git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C 
"$1" diff-index --name-only --relative HEAD
+   __git -C "$1" diff-index --name-only --relative HEAD
else
# NOTE: $2 is not quoted in order to support multiple options
-   git ${__git_C_args:+"${__git_C_args[@]}"} --git-dir="$dir" -C 
"$1" ls-files --exclude-standard $2
+   __git -C "$1" ls-files --exclude-standard $2
fi 2>/dev/null
 }
 
@@ -323,8 +329,7 @@ __git_heads ()
 {
local dir="$(__gitdir)"
if [ -d "$dir" ]; then
-   git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
-   refs/heads
+   __git for-each-ref --format='%(refname:short)' refs/heads
return
fi
 }
@@ -333,8 +338,7 @@ __git_tags ()
 {
local dir="$(__gitdir)"
if [ -d "$dir" ]; then
-   git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
-   refs/tags
+   __git for-each-ref --format='%(refname:short)' refs/tags
return
fi
 }
@@ -385,14 +389,14 @@ __git_refs ()
refs="refs/tags refs/heads refs/remotes"
;;
esac
-   git --git-dir="$dir" for-each-ref --format="$pfx%($format)" \
+   __git_dir="$dir" __git for-each-ref --format="$pfx%($format)" \
$refs
if [ -n "$track" ]; then
# employ the heuristic used by git checkou

[PATCHv2 19/21] completion: don't guard git executions with __gitdir()

2017-02-02 Thread SZEDER Gábor
Three completion functions, namely __git_index_files(), __git_heads()
and __git_tags(), first run __gitdir() and check that the path it
outputs exists, i.e. that there is a git repository, and run a git
command only if there is one.

After the previous changes in this series there are no further uses of
__gitdir()'s output in these functions besides those checks.  And
those checks are unnecessary, because we can just execute those git
commands outside of a repository and let them error out.  We don't
perform such a check in other places either.

Remove this check and the __gitdir() call from these functions,
sparing the fork()+exec() overhead of the command substitution and the
potential 'git rev-parse' execution.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 32 +++-
 1 file changed, 11 insertions(+), 21 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 1a849761f..1c7493ff2 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -312,35 +312,25 @@ __git_ls_files_helper ()
 #slash.
 __git_index_files ()
 {
-   local dir="$(__gitdir)" root="${2-.}" file
-
-   if [ -d "$dir" ]; then
-   __git_ls_files_helper "$root" "$1" |
-   while read -r file; do
-   case "$file" in
-   ?*/*) echo "${file%%/*}" ;;
-   *) echo "$file" ;;
-   esac
-   done | sort | uniq
-   fi
+   local root="${2-.}" file
+
+   __git_ls_files_helper "$root" "$1" |
+   while read -r file; do
+   case "$file" in
+   ?*/*) echo "${file%%/*}" ;;
+   *) echo "$file" ;;
+   esac
+   done | sort | uniq
 }
 
 __git_heads ()
 {
-   local dir="$(__gitdir)"
-   if [ -d "$dir" ]; then
-   __git for-each-ref --format='%(refname:short)' refs/heads
-   return
-   fi
+   __git for-each-ref --format='%(refname:short)' refs/heads
 }
 
 __git_tags ()
 {
-   local dir="$(__gitdir)"
-   if [ -d "$dir" ]; then
-   __git for-each-ref --format='%(refname:short)' refs/tags
-   return
-   fi
+   __git for-each-ref --format='%(refname:short)' refs/tags
 }
 
 # Lists refs from the local (by default) or from a remote repository.
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 15/21] rev-parse: add '--absolute-git-dir' option

2017-02-02 Thread SZEDER Gábor
The output of 'git rev-parse --git-dir' can be either a relative or an
absolute path, depending on whether the current working directory is
at the top of the worktree or the .git directory or not, or how the
path to the repository is specified via the '--git-dir=' option
or the $GIT_DIR environment variable.  And if that output is a
relative path, then it is relative to the directory where any 'git
-C ' options might have led us.

This doesn't matter at all for regular scripts, because the git
wrapper automatically takes care of changing directories according to
the '-C ' options, and the scripts can then simply follow any
path returned by 'git rev-parse --git-dir', even if it's a relative
path.

Our Bash completion script, however, is unique in that it must run
directly in the user's interactive shell environment.  This means that
it's not executed through the git wrapper and would have to take care
of any '-C  options on its own, and it can't just change
directories as it pleases.  Consequently, adding support for taking
any '-C ' options on the command line into account during
completion turned out to be considerably more difficult, error prone
and required more subshells and git processes when it had to cope with
a relative path to the .git directory.

Help this rather special use case and teach 'git rev-parse' a new
'--absolute-git-dir' option which always outputs a canonicalized
absolute path to the .git directory, regardless of whether the path is
discovered automatically or is specified via $GIT_DIR or 'git
--git-dir='.

Signed-off-by: SZEDER Gábor 
---
 Documentation/git-rev-parse.txt |  4 
 builtin/rev-parse.c | 26 ++
 t/t1500-rev-parse.sh| 17 +
 3 files changed, 31 insertions(+), 16 deletions(-)

diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 7241e9689..91c02b8c8 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -217,6 +217,10 @@ If `$GIT_DIR` is not defined and the current directory
 is not detected to lie in a Git repository or work tree
 print a message to stderr and exit with nonzero status.
 
+--absolute-git-dir::
+   Like `--git-dir`, but its output is always the canonicalized
+   absolute path.
+
 --git-common-dir::
Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index ff13e59e1..1967bafba 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -802,17 +802,27 @@ int cmd_rev_parse(int argc, const char **argv, const char 
*prefix)
putchar('\n');
continue;
}
-   if (!strcmp(arg, "--git-dir")) {
+   if (!strcmp(arg, "--git-dir") ||
+   !strcmp(arg, "--absolute-git-dir")) {
const char *gitdir = 
getenv(GIT_DIR_ENVIRONMENT);
char *cwd;
int len;
-   if (gitdir) {
-   puts(gitdir);
-   continue;
-   }
-   if (!prefix) {
-   puts(".git");
-   continue;
+   if (arg[2] == 'g') {/* --git-dir */
+   if (gitdir) {
+   puts(gitdir);
+   continue;
+   }
+   if (!prefix) {
+   puts(".git");
+   continue;
+   }
+   } else {/* --absolute-git-dir */
+   if (!gitdir && !prefix)
+   gitdir = ".git";
+   if (gitdir) {
+   puts(real_path(gitdir));
+   continue;
+   }
}
cwd = xgetcwd();
len = strlen(cwd);
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 038e24c40..8b62ed85b 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -3,7 +3,7 @@
 test_description='test git rev-parse'
 . ./test-lib.sh
 
-# usage: [options] label is-bare is-inside-git is-inside-work prefix gi

[PATCHv2 01/21] completion: improve __git_refs()'s in-code documentation

2017-02-02 Thread SZEDER Gábor
That "first argument is passed to __gitdir()" statement in particular
is not really helpful, and after this series it won't be the case
anyway.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 6721ff80f..ee6fb2259 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -332,9 +332,11 @@ __git_tags ()
fi
 }
 
-# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments
-# presence of 2nd argument means use the guess heuristic employed
-# by checkout for tracking branches
+# Lists refs from the local (by default) or from a remote repository.
+# It accepts 0, 1 or 2 arguments:
+# 1: The remote to list refs from (optional; ignored, if set but empty).
+# 2: In addition to local refs, list unique branches from refs/remotes/ for
+#'git checkout's tracking DWIMery (optional; ignored, if set but empty).
 __git_refs ()
 {
local i hash dir="$(__gitdir "${1-}")" track="${2-}"
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 06/21] completion tests: add tests for the __git_refs() helper function

2017-02-02 Thread SZEDER Gábor
Check how __git_refs() lists refs in different scenarios, i.e.

  - short and full refs,
  - from a local or from a remote repository,
  - remote specified via path, name or URL,
  - with or without a repository specified on the command line,
  - non-existing remote,
  - unique remote branches for 'git checkout's tracking DWIMery,
  - not in a git repository, and
  - interesting combinations of the above.

Seven of these tests expect failure, mostly demonstrating bugs related
to listing refs from a remote repository:

  - ignoring the repository specified on the command line (2 tests),
  - listing refs from the wrong place when the name of a configured
remote happens to match a directory,
  - listing only 'HEAD' but no short refs from a remote given as URL,
  - listing 'HEAD' even from non-existing remotes (2 tests), and
  - listing 'HEAD' when not in a repository.

Signed-off-by: SZEDER Gábor 
---
 t/t9902-completion.sh | 265 +-
 1 file changed, 264 insertions(+), 1 deletion(-)

diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index f7f7d49fb..7956cb9b1 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -365,6 +365,269 @@ test_expect_success '__git_remotes - list remotes from 
$GIT_DIR/remotes and from
test_cmp expect actual
 '
 
+test_expect_success 'setup for ref completion' '
+   git commit --allow-empty -m initial &&
+   git branch matching-branch &&
+   git tag matching-tag &&
+   (
+   cd otherrepo &&
+   git commit --allow-empty -m initial &&
+   git branch -m master master-in-other &&
+   git branch branch-in-other &&
+   git tag tag-in-other
+   ) &&
+   git remote add other "$ROOT/otherrepo/.git" &&
+   git fetch --no-tags other &&
+   rm -f .git/FETCH_HEAD &&
+   git init thirdrepo
+'
+
+test_expect_success '__git_refs - simple' '
+   cat >expected <<-EOF &&
+   HEAD
+   master
+   matching-branch
+   other/branch-in-other
+   other/master-in-other
+   matching-tag
+   EOF
+   (
+   cur= &&
+   __git_refs >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - full refs' '
+   cat >expected <<-EOF &&
+   refs/heads/master
+   refs/heads/matching-branch
+   EOF
+   (
+   cur=refs/heads/ &&
+   __git_refs >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - repo given on the command line' '
+   cat >expected <<-EOF &&
+   HEAD
+   branch-in-other
+   master-in-other
+   tag-in-other
+   EOF
+   (
+   __git_dir="$ROOT/otherrepo/.git" &&
+   cur= &&
+   __git_refs >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - remote on local file system' '
+   cat >expected <<-EOF &&
+   HEAD
+   branch-in-other
+   master-in-other
+   tag-in-other
+   EOF
+   (
+   cur= &&
+   __git_refs otherrepo >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - remote on local file system - full refs' '
+   cat >expected <<-EOF &&
+   refs/heads/branch-in-other
+   refs/heads/master-in-other
+   refs/tags/tag-in-other
+   EOF
+   (
+   cur=refs/ &&
+   __git_refs otherrepo >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - configured remote' '
+   cat >expected <<-EOF &&
+   HEAD
+   branch-in-other
+   master-in-other
+   EOF
+   (
+   cur= &&
+   __git_refs other >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - configured remote - full refs' '
+   cat >expected <<-EOF &&
+   refs/heads/branch-in-other
+   refs/heads/master-in-other
+   refs/tags/tag-in-other
+   EOF
+   (
+   cur=refs/ &&
+   __git_refs other >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_exp

[PATCHv2 09/21] completion: respect 'git --git-dir=' when listing remote refs

2017-02-02 Thread SZEDER Gábor
In __git_refs() the git commands listing refs, both short and full,
from a given remote repository are run without giving them the path to
the git repository which might have been specified on the command line
via 'git --git-dir='.  This is bad, those git commands should
access the 'refs/remotes//' hierarchy or the remote and
credentials configuration in that specified repository.

Use the __gitdir() helper only to find the path to the .git directory
and pass the resulting path to the 'git ls-remote' and 'for-each-ref'
executions that list remote refs.  While modifying that 'for-each-ref'
line, remove the superfluous disambiguating doubledash.

Don't use __gitdir() to check that the given remote is on the file
system: basically it performs only a single if statement for us at the
considerable cost of fork()ing a subshell for a command substitution.
We are better off to perform all the necessary checks of the remote in
__git_refs().

Though __git_refs() was the last remaining callsite that passed a
remote to __gitdir(), don't delete __gitdir()'s remote-handling part
yet, just in case some users' custom completion scriptlets depend on
it.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 22 +-
 t/t9902-completion.sh  |  4 ++--
 2 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 295f6de24..7d7e8b9d9 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -342,9 +342,21 @@ __git_tags ()
 #'git checkout's tracking DWIMery (optional; ignored, if set but empty).
 __git_refs ()
 {
-   local i hash dir="$(__gitdir "${1-}")" track="${2-}"
+   local i hash dir="$(__gitdir)" track="${2-}"
+   local list_refs_from=path remote="${1-}"
local format refs pfx
-   if [ -d "$dir" ]; then
+
+   if [ -n "$remote" ]; then
+   if [ -d "$remote/.git" ]; then
+   dir="$remote/.git"
+   elif [ -d "$remote" ]; then
+   dir="$remote"
+   else
+   list_refs_from=remote
+   fi
+   fi
+
+   if [ "$list_refs_from" = path ] && [ -d "$dir" ]; then
case "$cur" in
refs|refs/*)
format="refname"
@@ -381,7 +393,7 @@ __git_refs ()
fi
case "$cur" in
refs|refs/*)
-   git ls-remote "$dir" "$cur*" 2>/dev/null | \
+   git --git-dir="$dir" ls-remote "$remote" "$cur*" 2>/dev/null | \
while read -r hash i; do
case "$i" in
*^{}) ;;
@@ -391,8 +403,8 @@ __git_refs ()
;;
*)
echo "HEAD"
-   git for-each-ref --format="%(refname:short)" -- \
-   "refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
+   git --git-dir="$dir" for-each-ref --format="%(refname:short)" \
+   "refs/remotes/$remote/" 2>/dev/null | sed -e 
"s#^$remote/##"
;;
esac
 }
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 7667baabf..6e64cd6ba 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -486,7 +486,7 @@ test_expect_success '__git_refs - configured remote - full 
refs' '
test_cmp expected "$actual"
 '
 
-test_expect_failure '__git_refs - configured remote - repo given on the 
command line' '
+test_expect_success '__git_refs - configured remote - repo given on the 
command line' '
cat >expected <<-EOF &&
HEAD
branch-in-other
@@ -501,7 +501,7 @@ test_expect_failure '__git_refs - configured remote - repo 
given on the command
test_cmp expected "$actual"
 '
 
-test_expect_failure '__git_refs - configured remote - full refs - repo given 
on the command line' '
+test_expect_success '__git_refs - configured remote - full refs - repo given 
on the command line' '
cat >expected <<-EOF &&
refs/heads/branch-in-other
refs/heads/master-in-other
-- 
2.11.0.555.g967c1bcb3



[PATCHv2 14/21] completion: fix completion after 'git -C '

2017-02-02 Thread SZEDER Gábor
The main completion function finds the name of the git command by
iterating through all the words on the command line in search for the
first non-option-looking word.  As it is not aware of 'git -C's
mandatory path argument, if the '-C ' option is present, 'path'
will be the first such word and it will be mistaken for a git command.
This breaks completion in various ways:

 - If 'path' happens to match one of the commands supported by the
   completion script, then options of that command will be offered.

 - If 'path' doesn't match a supported command and doesn't contain any
   characters not allowed in Bash identifier names, then the
   completion script does basically nothing and Bash in turn falls
   back to filename completion for all subsequent words.

 - Otherwise, if 'path' does contain such an unallowed character, then
   it leads to a more or less ugly error message in the middle of the
   command line.  The standard '/' directory separator is such a
   character, and it happens to trigger one of the uglier errors:

 $ git -C some/path sh.exe": declare: `_git_some/path': not a valid 
identifier
 error: invalid key: alias.some/path

Fix this by skipping 'git -C's mandatory path argument while iterating
over the words on the command line.  Extend the relevant test with
this case and, while at it, with cases that needed similar treatment
in the past ('--git-dir', '-c', '--work-tree' and '--namespace').

Additionally, silence the standard error of the 'declare' builtins
looking for the completion function associated with the git command
and of the 'git config' query for the aliased command.  So if git ever
learns a new option with a mandatory argument in the future, then,
though the completion script will again misbehave, at least the
command line will not be utterly disrupted by those error messages.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 8 
 t/t9902-completion.sh  | 7 ++-
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 7d25b33b8..ac5eb9ced 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -816,7 +816,7 @@ __git_aliases ()
 __git_aliased_command ()
 {
local word cmdline=$(git --git-dir="$(__gitdir)" \
-   config --get "alias.$1")
+   config --get "alias.$1" 2>/dev/null)
for word in $cmdline; do
case "$word" in
\!gitk|gitk)
@@ -2800,7 +2800,7 @@ __git_main ()
--git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
--bare)  __git_dir="." ;;
--help) command="help"; break ;;
-   -c|--work-tree|--namespace) ((c++)) ;;
+   -c|-C|--work-tree|--namespace) ((c++)) ;;
-*) ;;
*) command="$i"; break ;;
esac
@@ -2844,13 +2844,13 @@ __git_main ()
fi
 
local completion_func="_git_${command//-/_}"
-   declare -f $completion_func >/dev/null && $completion_func && return
+   declare -f $completion_func >/dev/null 2>/dev/null && $completion_func 
&& return
 
local expansion=$(__git_aliased_command "$command")
if [ -n "$expansion" ]; then
words[1]=$expansion
completion_func="_git_${expansion//-/_}"
-   declare -f $completion_func >/dev/null && $completion_func
+   declare -f $completion_func >/dev/null 2>/dev/null && 
$completion_func
fi
 }
 
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 500505dca..be2ed8704 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -755,7 +755,12 @@ test_expect_success 'general options plus command' '
test_completion "git --namespace=foo check" "checkout " &&
test_completion "git --paginate check" "checkout " &&
test_completion "git --info-path check" "checkout " &&
-   test_completion "git --no-replace-objects check" "checkout "
+   test_completion "git --no-replace-objects check" "checkout " &&
+   test_completion "git --git-dir some/path check" "checkout " &&
+   test_completion "git -c conf.var=value check" "checkout " &&
+   test_completion "git -C some/path check" "checkout " &&
+   test_completion "git --work-tree some/path check" "checkout " &&
+   test_completion "git --namespace name/space check" "checkout "
 '
 
 test_expect_success 'git --help completion' '
-- 
2.11.0.555.g967c1bcb3



[PATCH 01/12] completion: remove redundant __gitcomp_nl() options from _git_commit()

2017-02-02 Thread SZEDER Gábor
Those two options are specifying the default values that
__gitcomp_nl() would use anyway when invoked with no options at all.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index ed06cb621..f20d4600c 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1216,7 +1216,7 @@ _git_commit ()
 {
case "$prev" in
-c|-C)
-   __gitcomp_nl "$(__git_refs)" "" "${cur}"
+   __gitcomp_nl "$(__git_refs)"
return
;;
esac
-- 
2.11.0.555.g967c1bcb3



[PATCH 00/12] completion: speed up refs completion

2017-02-02 Thread SZEDER Gábor
This series speeds up refs completion for large number of refs, partly
by giving up disambiguating ambiguous refs (patch 6) and partly by
eliminating most of the shell processing between 'git for-each-ref'
and 'ls-remote' and Bash's completion facility.  The rest is a bit of
preparatory reorganization, cleanup and bugfixes.

The last patch touches the ZSH wrapper, too.  By a lucky educated
guess I managed to get it work on the first try, but I don't really
know what I've actually done, so...  ZSH users, please have a closer
look.

At the end of this series refs completion from a local repository is
as fast as it can possibly get, at least as far as the completion
script is concerned, because it basically does nothing anymore :)  All
it does is run 'git for-each-ref' with assorted options to do all the
work, and feed its output directly, without any processing into Bash's
COMPREPLY array.  There is still room for improvements in the code
paths using 'git ls-remote', but for that we would need enhancements
to 'ls-remote'.

It goes on top of the __gitdir() improvements series I just posted at:

  http://public-inbox.org/git/20170203024829.8071-1-szeder@gmail.com/T/

This series is also available at:

  https://github.com/szeder/git completion-refs-speedup


SZEDER Gábor (12):
  completion: remove redundant __gitcomp_nl() options from _git_commit()
  completion: wrap __git_refs() for better option parsing
  completion: support completing full refs after '--option=refs/'
  completion: support excluding full refs
  completion: don't disambiguate tags and branches
  completion: don't disambiguate short refs
  completion: let 'for-each-ref' and 'ls-remote' filter matching refs
  completion: let 'for-each-ref' strip the remote name from remote
branches
  completion: let 'for-each-ref' filter remote branches for 'checkout'
DWIMery
  completion: let 'for-each-ref' sort remote branches for 'checkout'
DWIMery
  completion: list only matching symbolic and pseudorefs when completing
refs
  completion: fill COMPREPLY directly when completing refs

 contrib/completion/git-completion.bash | 205 
 contrib/completion/git-completion.zsh  |   9 ++
 t/t9902-completion.sh  | 282 +
 3 files changed, 430 insertions(+), 66 deletions(-)

-- 
2.11.0.555.g967c1bcb3



[PATCH 10/12] completion: let 'for-each-ref' sort remote branches for 'checkout' DWIMery

2017-02-02 Thread SZEDER Gábor
When listing unique remote branches for 'git checkout's tracking
DWIMery, __git_refs() runs the classic '... |sort |uniq -u' pattern to
filter out duplicate remote branches.

Let 'git for-each-ref' do the sorting, sparing the overhead of
fork()+exec()ing 'sort' and a stage in the pipeline where potentially
relatively large amount of data can be passed between two subsequent
pipeline stages.

This speeds up refs completion for 'git checkout' a bit when a lot of
remote branches match the current word to be completed.  Listing a
single local and 100k remote branches, all packed, best of five:

  On Linux, before:

$ time __git_complete_refs --track

real0m1.856s
user0m1.816s
sys 0m0.060s

  After:

real0m1.550s
user0m1.512s
sys 0m0.060s

  On Windows, before:

real0m3.128s
user0m2.155s
sys 0m0.183s

  After:

real0m2.781s
user0m1.826s
sys 0m0.136s

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index e2c4794f3..9f5a6f44e 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -414,8 +414,9 @@ __git_refs ()
# Try to find a remote branch that matches the 
completion word
# but only output if the branch name is unique
__git for-each-ref --format="%(refname:strip=3)" \
+   --sort="refname:strip=3" \
"refs/remotes/*/$cur_*" 
"refs/remotes/*/$cur_*/**" | \
-   sort | uniq -u
+   uniq -u
fi
return
fi
-- 
2.11.0.555.g967c1bcb3



[PATCH 09/12] completion: let 'for-each-ref' filter remote branches for 'checkout' DWIMery

2017-02-02 Thread SZEDER Gábor
The code listing unique remote branches for 'git checkout's tracking
DWIMery outputs only remote branches that match the current word to be
completed, but the filtering is done in a shell loop iterating over
all remote refs.

Let 'git for-each-ref' do the filtering, as it can do so much more
efficiently and we can remove that shell loop entirely.

This speeds up refs completion for 'git checkout' considerably when
there are a lot of non-matching remote refs to be filtered out.
Uniquely completing a branch in a repository with 100k remote
branches, all packed, best of five:

  On Linux, before:

$ time __git_complete_refs --cur=maste --track

real0m1.993s
user0m1.740s
sys 0m0.304s

  After:

real0m0.266s
user0m0.248s
sys 0m0.012s

  On Windows, before:

real0m6.187s
user0m3.358s
sys 0m2.121s

  After:

real0m0.750s
user0m0.015s
sys 0m0.090s

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 12 +++-
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 8f1203025..e2c4794f3 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -413,15 +413,9 @@ __git_refs ()
# employ the heuristic used by git checkout
# Try to find a remote branch that matches the 
completion word
# but only output if the branch name is unique
-   local ref entry
-   __git for-each-ref --shell 
--format="ref=%(refname:strip=3)" \
-   "refs/remotes/" | \
-   while read -r entry; do
-   eval "$entry"
-   if [[ "$ref" == "$cur_"* ]]; then
-   echo "$ref"
-   fi
-   done | sort | uniq -u
+   __git for-each-ref --format="%(refname:strip=3)" \
+   "refs/remotes/*/$cur_*" 
"refs/remotes/*/$cur_*/**" | \
+   sort | uniq -u
fi
return
fi
-- 
2.11.0.555.g967c1bcb3



[PATCH 06/12] completion: don't disambiguate short refs

2017-02-02 Thread SZEDER Gábor
When the completion script lists short refs it does so using the 'git
for-each-ref' format 'refname:short', which makes sure that all listed
refs are unambiguous.  While disambiguating refs is technically
correct in this case, as opposed to the cases discussed in the
previous patch, this disambiguation involves several stat() syscalls
for each ref, thus, unfortunately, comes at a steep cost especially on
Windows and/or when there are a lot of refs to be listed.  A user of
Git for Windows reported[1] 'git checkout ' taking ~11 seconds in
a repository with just about 4000 refs.

However, it's questionable whether ambiguous refs are really that bad
to justify that much extra cost:

  - Ambiguous refs are not that common,
  - even if a repository contains ambiguous refs, they only hurt when
the user actually happens to want to do something with one of the
ambiguous refs, and
  - the issue can be easily circumvented by renaming those ambiguous
refs.

  - On the other hand, apparently not that many refs are needed to
make refs completion unacceptably slow on Windows,
  - and this slowness bites each and every time the user attempts refs
completion, even when the repository doesn't contain any ambiguous
refs.
  - Furthermore, circumventing the issue might not be possible or
might be considerably more difficult and requires various
trade-offs (e.g. working in a repository with only a few selected
important refs while keeping a separate repository with all refs
for reference).

Arguably, in this case the benefits of technical correctness are
rather minor compared to the price we pay for it, and we are better
off opting for performance over correctness.

Use the 'git for-each-ref' format 'refname:strip=2' to list short refs
to spare the substantial cost of disambiguating.

This speeds up refs completion considerably.  Uniquely completing a
branch in a repository with 100k local branches, all packed, best of
five:

  On Linux, before:

$ time __git_complete_refs --cur=maste

real0m1.662s
user0m1.368s
sys 0m0.296s

  After:

real0m0.831s
user0m0.808s
sys 0m0.028s

  On Windows, before:

real0m12.457s
user0m1.016s
sys 0m0.092s

  After:

real0m1.480s
user0m1.031s
sys 0m0.060s

[1] - https://github.com/git-for-windows/git/issues/524

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 19799e3ba..d55eadfd1 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -401,7 +401,7 @@ __git_refs ()
for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
if [ -e "$dir/$i" ]; then echo $pfx$i; fi
done
-   format="refname:short"
+   format="refname:strip=2"
refs="refs/tags refs/heads refs/remotes"
;;
esac
@@ -412,7 +412,7 @@ __git_refs ()
# Try to find a remote branch that matches the 
completion word
# but only output if the branch name is unique
local ref entry
-   __git for-each-ref --shell 
--format="ref=%(refname:short)" \
+   __git for-each-ref --shell 
--format="ref=%(refname:strip=2)" \
"refs/remotes/" | \
while read -r entry; do
eval "$entry"
@@ -437,7 +437,7 @@ __git_refs ()
*)
if [ "$list_refs_from" = remote ]; then
echo "HEAD"
-   __git for-each-ref --format="%(refname:short)" \
+   __git for-each-ref --format="%(refname:strip=2)" \
"refs/remotes/$remote/" | sed -e "s#^$remote/##"
else
__git ls-remote "$remote" HEAD \
-- 
2.11.0.555.g967c1bcb3



[PATCH 12/12] completion: fill COMPREPLY directly when completing refs

2017-02-02 Thread SZEDER Gábor
__gitcomp_nl() iterates over all the possible completion words it gets
as argument

  - filtering matching words,
  - appending a trailing space to each matching word (in all but two
cases),
  - prepending a prefix to each matching word (when completing words
after e.g. '--option=' or 'master..'), and
  - adding each matching word to the COMPREPLY array.

This takes a while when a lot of refs are passed to __gitcomp_nl().

The previous changes in this series ensure that __git_refs() lists
only refs matching the current word to be completed, making a second
filtering in __gitcomp_nl() redundant.

Adding the necessary prefix and suffix could be done in __git_refs()
as well:

  - When refs come from 'git for-each-ref', then that prefix and
suffix could be added much more efficiently using a 'git
for-each-ref' format containing said prefix and suffix.
  - When refs come from 'git ls-remote', then that prefix and suffix
can be added in the shell loop that has to process 'git
ls-remote's output anyway.
  - Finally, the prefix and suffix can be added to that handful of
potentially matching symbolic and pseudo refs right away in the
shell loop listing them.

And then all what is still left to do is to assign a bunch of
newline-separated words to a shell array, which can be done without a
shell loop iterating over each word, basically making all of
__gitcomp_nl() unnecessary for refs completion.

Add the helper function __gitcomp_direct() to fill the COMPREPLY array
with prefiltered and preprocessed words without any additional
processing, without a shell loop, with just one single compound
assignment.  Modify __git_refs() to accept prefix and suffix
parameters and add them to each and every listed ref.  Modify
__git_complete_refs() to pass the prefix and suffix parameters to
__git_refs() and to feed __git_refs()'s output to __gitcomp_direct()
instead of __gitcomp_nl().

This speeds up refs completion when there are a lot of refs matching
the current word to be completed.  Listing all branches for completion
in a repo with 100k local branches, all packed, best of five:

  On Linux, near the beginning of this series, for reference:

$ time __git_complete_refs

real0m2.028s
user0m1.692s
sys 0m0.344s

  Before this patch:

real0m1.135s
user0m1.112s
sys 0m0.024s

  After:

real0m0.367s
user0m0.352s
sys 0m0.020s

  On Windows, near the beginning:

real0m13.078s
user0m1.609s
sys 0m0.060s

  Before this patch:

real0m2.093s
user0m1.641s
sys 0m0.060s

  After:

real0m0.683s
user0m0.203s
sys 0m0.076s

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 54 --
 contrib/completion/git-completion.zsh  |  9 ++
 t/t9902-completion.sh  | 16 ++
 3 files changed, 64 insertions(+), 15 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 0ad02cec6..dbbb62f5f 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -213,6 +213,20 @@ _get_comp_words_by_ref ()
 }
 fi
 
+# Fills the COMPREPLY array with prefiltered words without any additional
+# processing.
+# Callers must take care of providing only words that match the current word
+# to be completed and adding any prefix and/or suffix (trailing space!), if
+# necessary.
+# 1: List of newline-separated matching completion words, complete with
+#prefix and suffix.
+__gitcomp_direct ()
+{
+   local IFS=$'\n'
+
+   COMPREPLY=($1)
+}
+
 __gitcompappend ()
 {
local x i=${#COMPREPLY[@]}
@@ -354,17 +368,19 @@ __git_tags ()
 #Can be the name of a configured remote, a path, or a URL.
 # 2: In addition to local refs, list unique branches from refs/remotes/ for
 #'git checkout's tracking DWIMery (optional; ignored, if set but empty).
-# 3: Currently ignored.
+# 3: A prefix to be added to each listed ref (optional).
 # 4: List only refs matching this word instead of the current word being
-#completed (optional).
+#completed (optional; NOT ignored, if empty, but lists all refs).
+# 5: A suffix to be appended to each listed ref (optional; ignored, if set
+#but empty).
 #
 # Use __git_complete_refs() instead.
 __git_refs ()
 {
local i hash dir track="${2-}"
local list_refs_from=path remote="${1-}"
-   local format refs pfx
-   local cur_="${4-$cur}"
+   local format refs
+   local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}"
 
__git_find_repo_path
dir="$__git_repo_path"
@@ -389,7 +405,7 @@ __git_refs ()
 
if [ "$list_refs_from" = path ]; then
if [[ "$cur_&quo

[PATCH 03/12] completion: support completing full refs after '--option=refs/'

2017-02-02 Thread SZEDER Gábor
Completing full refs currently only works when the full ref stands on
in its own on the command line, but doesn't work when the current word
to be completed contains a prefix before the full ref, e.g.
'--option=refs/' or 'master..refs/bis'.

The reason is that __git_refs() looks at the current word to be
completed ($cur) as a whole to decide whether it has to list full (if
it starts with 'refs/') or short refs (otherwise).  However, $cur also
holds said '--option=' or 'master..' prefixes, which of course throw
off this decision.  Luckily, the default action is to list short refs,
that's why completing short refs happens to work even after a
'master..' prefix and similar cases.

Pass only the ref part of the current word to be completed to
__git_refs() as a new positional parameter, so it can make the right
decision even if the whole current word contains some kind of a
prefix.

Make this new parameter the 4. positional parameter and leave the 3.
as an ignored placeholder for now (it will be used later in this patch
series).

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 21 ++---
 t/t9902-completion.sh  | 31 +++
 2 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 7f19e2a4f..67a03cfd4 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -354,6 +354,8 @@ __git_tags ()
 #Can be the name of a configured remote, a path, or a URL.
 # 2: In addition to local refs, list unique branches from refs/remotes/ for
 #'git checkout's tracking DWIMery (optional; ignored, if set but empty).
+# 3: Currently ignored.
+# 4: The current ref to be completed (optional).
 #
 # Use __git_complete_refs() instead.
 __git_refs ()
@@ -361,6 +363,7 @@ __git_refs ()
local i hash dir track="${2-}"
local list_refs_from=path remote="${1-}"
local format refs pfx
+   local cur_="${4-$cur}"
 
__git_find_repo_path
dir="$__git_repo_path"
@@ -384,14 +387,17 @@ __git_refs ()
fi
 
if [ "$list_refs_from" = path ]; then
-   case "$cur" in
+   case "$cur_" in
refs|refs/*)
format="refname"
-   refs="${cur%/*}"
+   refs="${cur_%/*}"
track=""
;;
*)
-   [[ "$cur" == ^* ]] && pfx="^"
+   if [[ "$cur_" == ^* ]]; then
+   pfx="^"
+   cur_=${cur_#^}
+   fi
for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
if [ -e "$dir/$i" ]; then echo $pfx$i; fi
done
@@ -411,16 +417,16 @@ __git_refs ()
while read -r entry; do
eval "$entry"
ref="${ref#*/}"
-   if [[ "$ref" == "$cur"* ]]; then
+   if [[ "$ref" == "$cur_"* ]]; then
echo "$ref"
fi
done | sort | uniq -u
fi
return
fi
-   case "$cur" in
+   case "$cur_" in
refs|refs/*)
-   __git ls-remote "$remote" "$cur*" | \
+   __git ls-remote "$remote" "$cur_*" | \
while read -r hash i; do
case "$i" in
*^{}) ;;
@@ -475,7 +481,8 @@ __git_complete_refs ()
shift
done
 
-   __gitcomp_nl "$(__git_refs "$remote" "$track")" "$pfx" "$cur_" "$sfx"
+   __gitcomp_nl "$(__git_refs "$remote" "$track" "" "$cur_")" \
+   "$pfx" "$cur_" "$sfx"
 }
 
 # __git_refs2 requires 1 argument (to pass to __git_refs)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 50c534072..8fe748839 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -775,6 +775,37 @@ test_expect_success '__git_refs - unique remote branches 
for git checkout DWIMer
test_cmp expected "$actual"
 '
 
+test_expect_success '__git_refs - after --opt=' '
+   cat >expected <<-EOF &&
+   HEAD
+   mast

[PATCH 07/12] completion: let 'for-each-ref' and 'ls-remote' filter matching refs

2017-02-02 Thread SZEDER Gábor
When completing refs, several __git_refs() code paths list all the
refs from the refs/{heads,tags,remotes}/ hierarchy and then
__gitcomp_nl() iterates over those refs in a shell loop to filter out
refs not matching the current word to be completed.  This comes with a
considerable performance penalty when a repository contains a lot of
refs but the current word can be uniquely completed or when only a
handful of refs match the current word.

Specify appropriate globbing patterns to 'git for-each-ref' and 'git
ls-remote' to list only those refs that match the current word to be
completed.  This reduces the number of iterations in __gitcomp_nl()
from the number of refs to the number of matching refs.

This speeds up refs completion considerably when there are a lot of
non-matching refs to be filtered out.  Uniquely completing a branch in
a repository with 100k local branches, all packed, best of five:

  On Linux, before:

$ time __git_complete_refs --cur=maste

real0m0.831s
user0m0.808s
sys 0m0.028s

  After:

real0m0.119s
user0m0.104s
sys 0m0.008s

  On Windows, before:

real0m1.480s
user0m1.031s
sys 0m0.060s

  After:

real0m0.377s
user0m0.015s
sys 0m0.030s

Strictly speaking this is a fundamental behavior change in
__git_refs(), a helper function that is likely used in users' custom
completion scriptlets.  However, arguably this change should be
harmless, because:

  - __git_refs() was only ever intended to feed refs to Bash's
completion machinery, for which non-matching refs have to be
filtered out anyway.

  - There were already code paths that list only matching refs
(listing unique remote branches for 'git checkout's tracking
DWIMery and listing full remote refs via 'git ls-remote').

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash |  14 +++--
 t/t9902-completion.sh  | 102 +
 2 files changed, 111 insertions(+), 5 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index d55eadfd1..615292f8b 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -394,7 +394,7 @@ __git_refs ()
case "$cur_" in
refs|refs/*)
format="refname"
-   refs="${cur_%/*}"
+   refs=("$cur_*" "$cur_*/**")
track=""
;;
*)
@@ -402,11 +402,13 @@ __git_refs ()
if [ -e "$dir/$i" ]; then echo $pfx$i; fi
done
format="refname:strip=2"
-   refs="refs/tags refs/heads refs/remotes"
+   refs=("refs/tags/$cur_*" "refs/tags/$cur_*/**"
+   "refs/heads/$cur_*" "refs/heads/$cur_*/**"
+   "refs/remotes/$cur_*" "refs/remotes/$cur_*/**")
;;
esac
__git_dir="$dir" __git for-each-ref --format="$pfx%($format)" \
-   $refs
+   "${refs[@]}"
if [ -n "$track" ]; then
# employ the heuristic used by git checkout
# Try to find a remote branch that matches the 
completion word
@@ -438,10 +440,12 @@ __git_refs ()
if [ "$list_refs_from" = remote ]; then
echo "HEAD"
__git for-each-ref --format="%(refname:strip=2)" \
-   "refs/remotes/$remote/" | sed -e "s#^$remote/##"
+   "refs/remotes/$remote/$cur_*" \
+   "refs/remotes/$remote/$cur_*/**" | sed -e 
"s#^$remote/##"
else
__git ls-remote "$remote" HEAD \
-   "refs/tags/*" "refs/heads/*" "refs/remotes/*" |
+   "refs/tags/$cur_*" "refs/heads/$cur_*" \
+   "refs/remotes/$cur_*" |
while read -r hash i; do
case "$i" in
*^{})   ;;
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 7b42a686c..499be5879 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -837,6 +837,108 @@ test_expect_success '__git refs - exluding full refs' '
test_cmp expected "$act

[PATCH 11/12] completion: list only matching symbolic and pseudorefs when completing refs

2017-02-02 Thread SZEDER Gábor
The previous changes in this series ensure that __git_refs() lists
only refs that match the current word to be completed, but
non-matching symbolic and pseudo refs still show up in its output.

Filter out non-matching symbolic and pseudo refs as well, so anything
__git_refs() outputs matches the current word to be completed, as it
will allow us to optimize how refs are placed into the COMPREPLY
array.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 20 
 t/t9902-completion.sh  |  4 
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 9f5a6f44e..0ad02cec6 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -355,7 +355,8 @@ __git_tags ()
 # 2: In addition to local refs, list unique branches from refs/remotes/ for
 #'git checkout's tracking DWIMery (optional; ignored, if set but empty).
 # 3: Currently ignored.
-# 4: The current ref to be completed (optional).
+# 4: List only refs matching this word instead of the current word being
+#completed (optional).
 #
 # Use __git_complete_refs() instead.
 __git_refs ()
@@ -399,7 +400,12 @@ __git_refs ()
;;
*)
for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
-   if [ -e "$dir/$i" ]; then echo $pfx$i; fi
+   case "$i" in
+   $cur_*) if [ -e "$dir/$i" ]; then
+   echo $pfx$i
+   fi
+   ;;
+   esac
done
format="refname:strip=2"
refs=("refs/tags/$cur_*" "refs/tags/$cur_*/**"
@@ -432,12 +438,18 @@ __git_refs ()
;;
*)
if [ "$list_refs_from" = remote ]; then
-   echo "HEAD"
+   case "HEAD" in
+   $cur_*) echo "HEAD" ;;
+   esac
__git for-each-ref --format="%(refname:strip=3)" \
"refs/remotes/$remote/$cur_*" \
"refs/remotes/$remote/$cur_*/**"
else
-   __git ls-remote "$remote" HEAD \
+   local query_symref
+   case "HEAD" in
+   $cur_*) query_symref="HEAD" ;;
+   esac
+   __git ls-remote "$remote" $query_symref \
"refs/tags/$cur_*" "refs/heads/$cur_*" \
"refs/remotes/$cur_*" |
while read -r hash i; do
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 499be5879..5e06acc17 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -847,7 +847,6 @@ test_expect_success 'setup for filtering matching refs' '
 
 test_expect_success '__git_refs - only matching refs' '
cat >expected <<-EOF &&
-   HEAD
matching-branch
matching/branch
matching-tag
@@ -874,7 +873,6 @@ test_expect_success '__git_refs - only matching refs - full 
refs' '
 
 test_expect_success '__git_refs - only matching refs - remote on local file 
system' '
cat >expected <<-EOF &&
-   HEAD
master-in-other
matching/branch-in-other
EOF
@@ -887,7 +885,6 @@ test_expect_success '__git_refs - only matching refs - 
remote on local file syst
 
 test_expect_success '__git_refs - only matching refs - configured remote' '
cat >expected <<-EOF &&
-   HEAD
master-in-other
matching/branch-in-other
EOF
@@ -912,7 +909,6 @@ test_expect_success '__git_refs - only matching refs - 
remote - full refs' '
 
 test_expect_success '__git_refs - only matching refs - checkout DWIMery' '
cat >expected <<-EOF &&
-   HEAD
matching-branch
matching/branch
matching-tag
-- 
2.11.0.555.g967c1bcb3



[PATCH 02/12] completion: wrap __git_refs() for better option parsing

2017-02-02 Thread SZEDER Gábor
__git_refs() currently accepts two optional positional parameters: a
remote and a flag for 'git checkout's tracking DWIMery.  To fix a
minor bug, and, more importantly, for faster refs completion, this
series will add three more parameters: a prefix, the current word to
be completed and a suffix, i.e. the options accepted by __gitcomp() &
friends, and will change __git_refs() to list only refs matching that
given current word and to add that given prefix and suffix to the
listed refs.

However, __git_refs() is the helper function that is most likely used
in users' custom completion scriptlets for their own git commands, and
we don't want to break those, so

  - we can't change __git_refs()'s default output format, i.e. we
can't by default append a trailing space to every listed ref,
meaning that the suffix parameter containing the default trailing
space would have to be specified on every invocation, and

  - we can't change the position of existing positional parameters
either, so there would have to be plenty of set-but-empty
placeholder positional parameters all over the completion script.

Furthermore, with five positional parameters it would be really hard
to remember which position means what.

To keep callsites simple, add the new wrapper function
__git_complete_refs() around __git_refs(), which:

  - instead of positional parameters accepts real '--opt=val'-style
options and with minimalistic option parsing translates them to
__git_refs()'s and __gitcomp_nl()'s positional parameters, and

  - includes the '__gitcomp_nl "$(__git_refs ...)" ...' command
substitution to make its behavior match its name and the behavior
of other __git_complete_* functions, and to limit future changes
in this series to __git_refs() and this new wrapper function.

Call this wrapper function instead of __git_refs() wherever possible
throughout the completion script, i.e. when __git_refs()'s output is
fed to __gitcomp_nl() right away without further processing, which
means all callsites except a single one in the __git_refs2() helper.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 102 ---
 t/t9902-completion.sh  | 106 +
 2 files changed, 173 insertions(+), 35 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index f20d4600c..7f19e2a4f 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -354,6 +354,8 @@ __git_tags ()
 #Can be the name of a configured remote, a path, or a URL.
 # 2: In addition to local refs, list unique branches from refs/remotes/ for
 #'git checkout's tracking DWIMery (optional; ignored, if set but empty).
+#
+# Use __git_complete_refs() instead.
 __git_refs ()
 {
local i hash dir track="${2-}"
@@ -446,6 +448,36 @@ __git_refs ()
esac
 }
 
+# Completes refs, short and long, local and remote, symbolic and pseudo.
+#
+# Usage: __git_complete_refs []...
+# --remote=: The remote to list refs from, can be the name of a
+#configured remote, a path, or a URL.
+# --track: List unique remote branches for 'git checkout's tracking DWIMery.
+# --pfx=: A prefix to be added to each ref.
+# --cur=: The current ref to be completed.  Defaults to the current
+#   word to be completed.
+# --sfx=: A suffix to be appended to each ref instead of the default
+# space.
+__git_complete_refs ()
+{
+   local remote track pfx cur_="$cur" sfx=" "
+
+   while test $# != 0; do
+   case "$1" in
+   --remote=*) remote="${1##--remote=}" ;;
+   --track)track="yes" ;;
+   --pfx=*)pfx="${1##--pfx=}" ;;
+   --cur=*)cur_="${1##--cur=}" ;;
+   --sfx=*)sfx="${1##--sfx=}" ;;
+   *)  return 1 ;;
+   esac
+   shift
+   done
+
+   __gitcomp_nl "$(__git_refs "$remote" "$track")" "$pfx" "$cur_" "$sfx"
+}
+
 # __git_refs2 requires 1 argument (to pass to __git_refs)
 __git_refs2 ()
 {
@@ -554,15 +586,15 @@ __git_complete_revlist_file ()
*...*)
pfx="${cur_%...*}..."
cur_="${cur_#*...}"
-   __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+   __git_complete_refs --pfx="$pfx" --cur="$cur_"
;;
*..*)
pfx="${cur_%..*}.."
cur_="${cur_#*..}"
-   __gitcomp_nl "$(__git_refs)" "$pfx" &quo

[PATCH 04/12] completion: support excluding full refs

2017-02-02 Thread SZEDER Gábor
Commit 49416ad22 (completion: support excluding refs, 2016-08-24) made
possible to complete short refs with a '^' prefix.

Extend the support to full refs to make completing '^refs/...' work.

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash |  8 
 t/t9902-completion.sh  | 31 +++
 2 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 67a03cfd4..63e803154 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -387,6 +387,10 @@ __git_refs ()
fi
 
if [ "$list_refs_from" = path ]; then
+   if [[ "$cur_" == ^* ]]; then
+   pfx="^"
+   cur_=${cur_#^}
+   fi
case "$cur_" in
refs|refs/*)
format="refname"
@@ -394,10 +398,6 @@ __git_refs ()
track=""
;;
*)
-   if [[ "$cur_" == ^* ]]; then
-   pfx="^"
-   cur_=${cur_#^}
-   fi
for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
if [ -e "$dir/$i" ]; then echo $pfx$i; fi
done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 8fe748839..7b42a686c 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -806,6 +806,37 @@ test_expect_success '__git_refs - after --opt= - full 
refs' '
test_cmp expected "$actual"
 '
 
+test_expect_success '__git refs - exluding refs' '
+   cat >expected <<-EOF &&
+   ^HEAD
+   ^master
+   ^matching-branch
+   ^other/branch-in-other
+   ^other/master-in-other
+   ^matching-tag
+   EOF
+   (
+   cur=^ &&
+   __git_refs >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
+test_expect_success '__git refs - exluding full refs' '
+   cat >expected <<-EOF &&
+   ^refs/heads/master
+   ^refs/heads/matching-branch
+   ^refs/remotes/other/branch-in-other
+   ^refs/remotes/other/master-in-other
+   ^refs/tags/matching-tag
+   EOF
+   (
+   cur=^refs/ &&
+   __git_refs >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
 test_expect_success '__git_complete_refs - simple' '
sed -e "s/Z$//g" >expected <<-EOF &&
HEAD Z
-- 
2.11.0.555.g967c1bcb3



[PATCH 05/12] completion: don't disambiguate tags and branches

2017-02-02 Thread SZEDER Gábor
When the completion script has to list only tags or only branches, it
uses the 'git for-each-ref' format 'refname:short', which makes sure
that all listed tags and branches are unambiguous.  However,
disambiguating tags and branches in these cases is wrong, because:

  - __git_tags(), the helper function listing possible tagname
arguments for 'git tag', lists an ambiguous tag
'refs/tags/ambiguous' as 'tags/ambiguous'.  Its only consumer,
'git tag' expects its tagname argument to be under 'refs/tags/',
thus it interprets that abgiguous tag as
'refs/tags/tags/ambiguous'.  Clearly wrong.

  - __git_heads() lists possible branchname arguments for 'git branch'
and possible 'branch.' configuration subsections.
Both of these expect branchnames to be under 'refs/heads/' and
misinterpret a disambiguated branchname like 'heads/ambiguous'.

Furthermore, disambiguation involves several stat() syscalls for each
tag or branch, thus comes at a steep cost especially on Windows and/or
when there are a lot of tags or branches to be listed.

Use the 'git for-each-ref' format 'refname:strip=2' instead of
'refname:short' to avoid harmful disambiguation of tags and branches
in __git_tags() and __git_heads().

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 63e803154..19799e3ba 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -340,12 +340,12 @@ __git_index_files ()
 
 __git_heads ()
 {
-   __git for-each-ref --format='%(refname:short)' refs/heads
+   __git for-each-ref --format='%(refname:strip=2)' refs/heads
 }
 
 __git_tags ()
 {
-   __git for-each-ref --format='%(refname:short)' refs/tags
+   __git for-each-ref --format='%(refname:strip=2)' refs/tags
 }
 
 # Lists refs from the local (by default) or from a remote repository.
-- 
2.11.0.555.g967c1bcb3



[PATCH 08/12] completion: let 'for-each-ref' strip the remote name from remote branches

2017-02-02 Thread SZEDER Gábor
The code listing unique remote branches for 'git checkout's tracking
DWIMery uses a shell parameter expansion in a loop iterating over each
listed ref to remove the remote's name from the remote branches, i.e.
the leading path component from the short ref.  When listing refs from
a configured remote repository, '| sed s///' is used for the same
purpose.

Let 'git for-each-ref' strip one more leading path component from the
refs, i.e. use the format 'refname:strip=3' instead of '=2', making
that parameter expansion and 'sed' execution unnecessary.

This speeds up refs completion for 'git checkout'.  Uniquely
completing a branch for 'git checkout maste' in a repo with 100k
remote branches, all packed, best of five:

  On Linux, near the beginning of this series, for reference:

$ time __git_complete_refs --cur=maste --track

real0m8.185s
user0m6.896s
sys 0m1.616s

  Before this patch:

real0m2.714s
user0m2.344s
sys 0m0.436s

  After:

real0m1.993s
user0m1.740s
sys 0m0.304s

  On Windows, near the beginning:

real1m8.421s
user0m7.591s
sys 0m3.557s

  Before this patch:

real0m8.191s
user0m4.638s
sys 0m2.918s

  After:

real0m6.187s
user0m3.358s
sys 0m2.121s

Signed-off-by: SZEDER Gábor 
---
 contrib/completion/git-completion.bash | 7 +++
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 615292f8b..8f1203025 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -414,11 +414,10 @@ __git_refs ()
# Try to find a remote branch that matches the 
completion word
# but only output if the branch name is unique
local ref entry
-   __git for-each-ref --shell 
--format="ref=%(refname:strip=2)" \
+   __git for-each-ref --shell 
--format="ref=%(refname:strip=3)" \
"refs/remotes/" | \
while read -r entry; do
eval "$entry"
-   ref="${ref#*/}"
if [[ "$ref" == "$cur_"* ]]; then
echo "$ref"
fi
@@ -439,9 +438,9 @@ __git_refs ()
*)
if [ "$list_refs_from" = remote ]; then
echo "HEAD"
-   __git for-each-ref --format="%(refname:strip=2)" \
+   __git for-each-ref --format="%(refname:strip=3)" \
"refs/remotes/$remote/$cur_*" \
-   "refs/remotes/$remote/$cur_*/**" | sed -e 
"s#^$remote/##"
+   "refs/remotes/$remote/$cur_*/**"
else
__git ls-remote "$remote" HEAD \
"refs/tags/$cur_*" "refs/heads/$cur_*" \
-- 
2.11.0.555.g967c1bcb3



[PATCH] squash! completion: fill COMPREPLY directly when completing refs

2017-02-06 Thread SZEDER Gábor
Care should be taken, though, because that prefix might contain
'for-each-ref' format specifiers as part of the left hand side of a
'..' range or '...' symmetric difference notation or fetch/push/etc.
refspec, e.g. 'git log "evil-%(refname)..br'.  Doubling every '%'
in the prefix will prevent 'git for-each-ref' from interpolating any
of those contained format specifiers.
---

This is really pathological, and I'm sure this has nothing to do with
whatever breakage Jacob experienced.
The shell metacharacters '(' and ')' still cause us trouble in various
ways, but that's nothing new and has been the case for quite a while
(always?).

It's already incorporated into (the rewritten)

  https://github.com/szeder/git completion-refs-speedup

 contrib/completion/git-completion.bash |  8 +---
 t/t9902-completion.sh  | 11 +++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index dbbb62f5f..058f1d0ee 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -381,6 +381,7 @@ __git_refs ()
local list_refs_from=path remote="${1-}"
local format refs
local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}"
+   local fer_pfx="${pfx//\%/%%}"
 
__git_find_repo_path
dir="$__git_repo_path"
@@ -406,6 +407,7 @@ __git_refs ()
if [ "$list_refs_from" = path ]; then
if [[ "$cur_" == ^* ]]; then
pfx="$pfx^"
+   fer_pfx="$fer_pfx^"
cur_=${cur_#^}
fi
case "$cur_" in
@@ -429,13 +431,13 @@ __git_refs ()
"refs/remotes/$cur_*" "refs/remotes/$cur_*/**")
;;
esac
-   __git_dir="$dir" __git for-each-ref 
--format="$pfx%($format)$sfx" \
+   __git_dir="$dir" __git for-each-ref 
--format="$fer_pfx%($format)$sfx" \
"${refs[@]}"
if [ -n "$track" ]; then
# employ the heuristic used by git checkout
# Try to find a remote branch that matches the 
completion word
# but only output if the branch name is unique
-   __git for-each-ref 
--format="$pfx%(refname:strip=3)$sfx" \
+   __git for-each-ref 
--format="$fer_pfx%(refname:strip=3)$sfx" \
--sort="refname:strip=3" \
"refs/remotes/*/$cur_*" 
"refs/remotes/*/$cur_*/**" | \
uniq -u
@@ -457,7 +459,7 @@ __git_refs ()
case "HEAD" in
$cur_*) echo "${pfx}HEAD$sfx" ;;
esac
-   __git for-each-ref 
--format="$pfx%(refname:strip=3)$sfx" \
+   __git for-each-ref 
--format="$fer_pfx%(refname:strip=3)$sfx" \
"refs/remotes/$remote/$cur_*" \
"refs/remotes/$remote/$cur_*/**"
else
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 1206a38ed..be584c069 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -951,6 +951,17 @@ test_expect_success 'teardown after filtering matching 
refs' '
git update-ref -d refs/remotes/other/matching/branch-in-other
 '
 
+test_expect_success '__git_refs - for-each-ref format specifiers in prefix' '
+   cat >expected <<-EOF &&
+   evil-%%-%42-%(refname)..master
+   EOF
+   (
+   cur="evil-%%-%42-%(refname)..mas" &&
+   __git_refs "" "" "evil-%%-%42-%(refname).." mas >"$actual"
+   ) &&
+   test_cmp expected "$actual"
+'
+
 test_expect_success '__git_complete_refs - simple' '
sed -e "s/Z$//g" >expected <<-EOF &&
HEAD Z
-- 
2.11.1.499.gb6fcc8b3a



Re: [PATCH 00/12] completion: speed up refs completion

2017-02-06 Thread SZEDER Gábor
On Mon, Feb 6, 2017 at 7:31 PM, Jacob Keller  wrote:
> On Fri, Feb 3, 2017 at 7:15 PM, Jacob Keller  wrote:
>> I haven't had a chance to further investigate, but I tried this series
>> out (from your github) and it appears that this series (or the
>> previous series for __gitdir work) breaks "git log" ref completion.
>> I'll have further details when I am able to investigate a it more.
>>
>> Thanks,
>> Jake
>
> At first I had the same problem, but I verified by re-installing the
> completion script and the problem appears to have gone away. I suspect
> what happened is that the original time, I forgot to actually install
> the new version of git, and only installed the completion script, so
> when some of the commands were run with new options they (silently)
> failed and the result was missing completion values.
>
> Once I properly re-installed everything it appears to work as
> expected. I haven't found any other issues yet.

Thanks, that's good to hear.

Still, I'm a bit puzzled as to what exactly might have caused your
problem.  Considering new options:

  - the __gitdir()-related series added the 'git rev-parse
--absolute-git-dir' option, but only ever used it if you invoked
completion after 'git -C some/where'.

 - The refs completion speedup didn't add any new options but started
   to use two that it previously didn't:

   - 'git for-each-ref --sort=' option, but that's with us since
 the earliest ever 'for-each-ref' version from more than a decade
 ago...

   - 'git for-each-ref' format modifier 'strip=2', which was
 introduced in v2.7.1~15^2 (tag: do not show ambiguous tag names
 as "tags/foo", 2016-01-25), only about a year ago.  Were you
 using a pre-2.7.1 version when seeing the problems?

Gábor


Re: What's cooking in git.git (Feb 2017, #02; Mon, 6)

2017-02-06 Thread SZEDER Gábor
> * sg/completion-refs-speedup (2017-02-06) 13 commits
>  - squash! completion: fill COMPREPLY directly when completing refs
>  - completion: fill COMPREPLY directly when completing refs
>  - completion: list only matching symbolic and pseudorefs when completing refs
>  - completion: let 'for-each-ref' sort remote branches for 'checkout' DWIMery
>  - completion: let 'for-each-ref' filter remote branches for 'checkout' 
> DWIMery
>  - completion: let 'for-each-ref' strip the remote name from remote branches
>  - completion: let 'for-each-ref' and 'ls-remote' filter matching refs
>  - completion: don't disambiguate short refs
>  - completion: don't disambiguate tags and branches
>  - completion: support excluding full refs
>  - completion: support completing full refs after '--option=refs/'
>  - completion: wrap __git_refs() for better option parsing
>  - completion: remove redundant __gitcomp_nl() options from _git_commit()
>  (this branch uses sg/completion.)
>
>  Will hold.
>  This seems to break 9902 when merged to 'pu'.

All failing tests fail with the same error:

  fatal: unrecognized %(refname:strip=2) argument: strip=2

That's because of this topic:

> * kn/ref-filter-branch-list (2017-01-31) 20 commits
>   (merged to 'next' on 2017-01-31 at e7592a5461)
>  + branch: implement '--format' option
>  + branch: use ref-filter printing APIs
>  + branch, tag: use porcelain output
>  + ref-filter: allow porcelain to translate messages in the output
>  + ref-filter: add an 'rstrip=' option to atoms which deal with refnames
>  + ref-filter: modify the 'lstrip=' option to work with negative ''
>  + ref-filter: Do not abruptly die when using the 'lstrip=' option
>  + ref-filter: rename the 'strip' option to 'lstrip'

And in particular this commit, which, well, does what it's subject
says it's doing, thus breaking backwards compatibility.


>  + ref-filter: make remote_ref_atom_parser() use 
> refname_atom_parser_internal()
>  + ref-filter: introduce refname_atom_parser()
>  + ref-filter: introduce refname_atom_parser_internal()
>  + ref-filter: make "%(symref)" atom work with the ':short' modifier
>  + ref-filter: add support for %(upstream:track,nobracket)
>  + ref-filter: make %(upstream:track) prints "[gone]" for invalid upstreams
>  + ref-filter: introduce format_ref_array_item()
>  + ref-filter: move get_head_description() from branch.c
>  + ref-filter: modify "%(objectname:short)" to take length
>  + ref-filter: implement %(if:equals=) and %(if:notequals=)
>  + ref-filter: include reference to 'used_atom' within 'atom_value'
>  + ref-filter: implement %(if), %(then), and %(else) atoms
>
>  The code to list branches in "git branch" has been consolidated
>  with the more generic ref-filter API.
>
>  Will cook in 'next'.


[PATCH] completion: restore removed line continuating backslash

2017-02-13 Thread SZEDER Gábor
Recent commit 1cd23e9e0 (completion: don't use __gitdir() for git
commands, 2017-02-03) rewrapped a couple of long lines, and while
doing so it inadvertently removed a '\' from the end of a line, thus
breaking completion for 'git config remote.name.push '.

Signed-off-by: SZEDER Gábor 
---

I wanted this to be a fixup!, but then noticed that this series is
already in next, hence the proper commit message.

I see the last What's cooking marks this series as "will merge to
master".  That doesn't mean that it will be in v2.12, does it?

 contrib/completion/git-completion.bash | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 993085af6..f2b294aac 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2086,7 +2086,7 @@ _git_config ()
remote.*.push)
local remote="${prev#remote.}"
remote="${remote%.push}"
-   __gitcomp_nl "$(__git for-each-ref
+   __gitcomp_nl "$(__git for-each-ref \
--format='%(refname):%(refname)' refs/heads)"
return
;;
-- 
2.11.1.499.ge87add2e7



Re: [PATCH] squash! completion: fill COMPREPLY directly when completing refs

2017-02-13 Thread SZEDER Gábor
On Fri, Feb 10, 2017 at 10:44 PM, Junio C Hamano  wrote:

> Should I expect a reroll to come, or is this the only fix-up to the
> series that begins at <20170203025405.8242-1-szeder@gmail.com>?
>
> No hurries.

Yes, definitely.

I found a minor bug in the middle of the series, and haven't quite
made up my mind about it and its fix yet.  Perhaps in a day or three.

Regarding the 'strip' -> 'lstrip' 'for-each-ref' format modifier
rename that broke pu: I should keep using 'strip', right?

Gábor


Re: [PATCH] completion: complete modified files for checkout with '--'

2017-02-13 Thread SZEDER Gábor
On Tue, Feb 14, 2017 at 12:33 AM,   wrote:
> From: Cornelius Weig 
>
> The command line completion for git-checkout bails out when seeing '--'
> as an isolated argument. For git-checkout this signifies the start of a
> list of files which are to be checked out. Checkout of files makes only
> sense for modified files,

No, there is e.g. 'git checkout that-branch this-path', too.


> therefore completion can be a bit smarter:
> Instead of bailing out, offer modified files for completion.
>
> Signed-off-by: Cornelius Weig 
> ---
>  contrib/completion/git-completion.bash | 5 -
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/contrib/completion/git-completion.bash 
> b/contrib/completion/git-completion.bash
> index 6c6e1c7..d6523fd 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1059,7 +1059,10 @@ _git_bundle ()
>
>  _git_checkout ()
>  {
> -   __git_has_doubledash && return
> +   __git_has_doubledash && {
> +   __git_complete_index_file "--modified"
> +   return
> +   }
>
> case "$cur" in
> --conflict=*)
> --
> 2.10.2
>


Re: [PATCH] completion: restore removed line continuating backslash

2017-02-14 Thread SZEDER Gábor
On Mon, Feb 13, 2017 at 9:45 PM, Junio C Hamano  wrote:
> SZEDER Gábor  writes:
>
>> Recent commit 1cd23e9e0 (completion: don't use __gitdir() for git
>> commands, 2017-02-03) rewrapped a couple of long lines, and while
>> doing so it inadvertently removed a '\' from the end of a line, thus
>> breaking completion for 'git config remote.name.push '.
>>
>> Signed-off-by: SZEDER Gábor 
>> ---
>>
>> I wanted this to be a fixup!, but then noticed that this series is
>> already in next, hence the proper commit message.
>
> We get "--format=... : command not found"?

Yeah, that's the one.

> Thanks for a set of sharp eyes.

Heh.  Sharp?!  It took over five minutes to notice after I first got
that error...

Furthermore, that '\' was already missing in v1, almost a year ago :)

>> I see the last What's cooking marks this series as "will merge to
>> master".  That doesn't mean that it will be in v2.12, does it?
>
> I actually was hoping that these can go in.  Others that can (or
> should) wait are marked as "Will cook in 'next'".
>
> If you feel uncomfortable and want these to cook longer, please tell
> me so.

Well, it was mainly my surprise that a 20+ patch series arriving so
late that it gets queued on top of -rc0 would still make it into the
release.  However, I have been using this series with only minor
modifications for the better part of a year now, so it's unlikely that
there will be any big issues with it.  Maybe something small, like
this missing '\', but we will deal with it when it arises.

Gábor


Re: [PATCH v2 2/2] completion: checkout: complete paths when ref given

2017-02-14 Thread SZEDER Gábor
On Tue, Feb 14, 2017 at 10:24 PM,   wrote:
> From: Cornelius Weig 
>
> Git-checkout completes words starting with '--' as options and other
> words as refs. Even after specifying a ref, further words not starting
> with '--' are completed as refs, which is invalid for git-checkout.

Refs completion is never attempted for words after the disambiguating
double-dash.

Even when refs completion is attempted, if it is unsuccessful, i.e.
there is no ref that matches the current word to be completed, then
Bash falls back to standard filename completion.  No refs match
'./'.

Furthermore, Bash performs filename completion on Alt-/ independently
from any completion function.

Granted, none of these will limit to only modified files...  But that
might be a good thing, see below.

> This commit ensures that after specifying a ref, further non-option
> words are completed as paths. Four cases are considered:
>
>  - If the word contains a ':', do not treat it as reference and use
>regular revlist completion.
>  - If no ref is found on the command line, complete non-options as refs
>as before.
>  - If the ref is HEAD or @, complete only with modified files because
>checking out unmodified files is a noop.

Here you use "modified" in the 'ls-files --modified' sense, but that
doesn't include modifications already staged in the index, see below.

>This case also applies if no ref is given, but '--' is present.
>  - If a ref other than HEAD or @ is found, offer only valid paths from
>that revision.
>
> Note that one corner-case is not covered by the current implementation:
> if a refname contains a ':' and is followed by '--' the completion would
> not recognize the valid refname.

I'm not sure what you mean here.  Refnames can't contain ':'.

> Signed-off-by: Cornelius Weig 
> ---
>  contrib/completion/git-completion.bash | 39 
> +++---
>  1 file changed, 31 insertions(+), 8 deletions(-)
>
> diff --git a/contrib/completion/git-completion.bash 
> b/contrib/completion/git-completion.bash
> index 4ab119d..df46f62 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1068,7 +1068,7 @@ _git_bundle ()
>
>  _git_checkout ()
>  {
> -   __git_has_doubledash && return
> +   local i c=2 ref="" seen_double_dash=""
>
> case "$cur" in
> --conflict=*)
> @@ -1081,13 +1081,36 @@ _git_checkout ()
> "
> ;;
> *)
> -   # check if --track, --no-track, or --no-guess was specified
> -   # if so, disable DWIM mode
> -   local flags="--track --no-track --no-guess" track=1
> -   if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
> -   track=''
> -   fi
> -   __gitcomp_nl "$(__git_refs '' $track)"
> +   while [ $c -lt $cword ]; do
> +   i="${words[c]}"
> +   case "$i" in
> +   --) seen_double_dash=1 ;;
> +   -*|?*:*) ;;
> +   *) ref="$i"; break ;;

I haven't tried it, but this would trigger on e.g. 'git checkout -b
new-feature ', wouldn't it?

> +   esac
> +   ((c++))
> +   done
> +
> +   case "$ref,$seen_double_dash,$cur" in
> +   ,,*:*)
> +   __git_complete_revlist_file
> +   ;;
> +   ,,*)
> +   # check for --track, --no-track, or --no-guess
> +   # if so, disable DWIM mode
> +   local flags="--track --no-track --no-guess" track=1
> +   if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
> +   track=''
> +   fi
> +   __gitcomp_nl "$(__git_refs '' $track)"
> +   ;;
> +   ,1,*|@,*|HEAD,*)
> +   __git_complete_index_file "--modified"

What about

  $ echo "unintentional change" >>tracked-file && git add -u
  $ git rm important-file
  $ git checkout HEAD 

?  It seems it will offer neither 'tracked-file' nor 'important-file',
but I think it should offer both.


We have __git_complete_index_file() for a while now, but only use it
for commands that accept only --options and filenames, e.g. 'add',
'clean', 'rm'.  This would be the first case when we would use it for
a command that accept both refs and filenames.  Perhaps similar corner
cases and the easy ways to trigger filename completion are why no one
thought it's worth it.

> +   ;;
> +   *)
> +   __git_complete_tree_file "$ref" "$cur"

Well, here you could go all-in, and say that this should complete only
files that are different from the version in $ref, because checking
out files that are still the same is a noop :)

> +   ;;
> +  

Re: [PATCH] completion: restore removed line continuating backslash

2017-02-15 Thread SZEDER Gábor
On Wed, Feb 15, 2017 at 3:41 AM, Junio C Hamano  wrote:
> SZEDER Gábor  writes:
>
>>> If you feel uncomfortable and want these to cook longer, please tell
>>> me so.
>>
>> Well, it was mainly my surprise that a 20+ patch series arriving so
>> late that it gets queued on top of -rc0 would still make it into the
>> release.
>
> It all depends on what area the changes are about ;-)

Sure.  I actually wanted to end that email with something like "and
it's in contrib anyway :)", but by the time I got there I forgot about
it.


Re: [PATCH v2 2/2] completion: checkout: complete paths when ref given

2017-02-15 Thread SZEDER Gábor
On Tue, Feb 14, 2017 at 10:24 PM,   wrote:

> +   *)
> +   __git_complete_tree_file "$ref" "$cur"
> +   ;;

There is one more caveat here.

Both our __git_complete_index_file() and Bash's builtin filename
completion lists matching paths like this:

  $ git rm contrib/co
  coccinelle/contacts/
  completion/convert-grafts-to-replace-refs.sh

i.e. the leading path components are not redundantly repeated.

Now, with this patch in this code path the list would look like this:

  $ git checkout completion-refs-speedup contrib/co
  contrib/coccinelle/
  contrib/completion/
  contrib/contacts/
  contrib/convert-grafts-to-replace-refs.sh

See the difference?

I once made a feeble attempt to make completion of the :
notation (i.e. what you extracted into __git_complete_tree_file())
look like regular filename completion, but couldn't.

Gábor


Re: [PATCH 5/5] versioncmp: cope with common leading parts in versionsort.prereleaseSuffix

2016-10-04 Thread SZEDER Gábor


Quoting SZEDER Gábor :


Quoting SZEDER Gábor :


Version sort with prerelease reordering sometimes puts tagnames in the
wrong order, when the common part of two compared tagnames ends with
the leading character(s) of one or more configured prerelease
suffixes.

$ git config --get-all versionsort.prereleaseSuffix
-beta
$ git tag -l --sort=version:refname v2.1.*
v2.1.0-beta-2
v2.1.0-beta-3
v2.1.0
v2.1.0-RC1
v2.1.0-RC2
v2.1.0-beta-1
v2.1.1
v2.1.2

The reason is that when comparing a pair of tagnames, first
versioncmp() looks for the first different character in a pair of
tagnames, and then the swap_prereleases() helper function checks for
prerelease suffixes _starting at_ that character.  Thus, when in the
above example the sorting algorithm happens to compare the tagnames
"v2.1.0-beta-1" and "v2.1.0-RC2", swap_prereleases() will try to match
the suffix "-beta" against "beta-1" to no avail, and the two tagnames
erroneously end up being ordered lexicographically.

To fix this issue change swap_prereleases() to look for configured
prerelease suffixes containing that first different character.


Now, while I believe this is the right thing to do to fix this bug,
there is a corner case, where multiple configured prerelease suffixes
might match the same tagname:

 $ git config --get-all versionsort.prereleaseSuffix
 -bar
 -baz
 -foo-bar
 $ ~/src/git/git tag -l --sort=version:refname
 v1.0-foo-bar
 v1.0-foo-baz

I.e. when comparing these two tags, both "-bar" and "-foo-bar" would
match "v1.0-foo-bar", and as "-bar" comes first in the config file,
it wins, and "v1.0-foo-bar" is ordered first.  An argument could be
made to prefer longer matches, in which case "v1.0-foo-bar" would be
ordered according to "-foo-bar", i.e. as second.  However, I don't
know what that argument could be, to me neither behavior is better
than the other, but the implementation of the "longest match counts"
would certainly be more complicated.

The argument I would make is that this is a pathological corner case
that doesn't worth worrying about.


After having slept on this a couple of times, I think the longest
matching prerelease suffix should determine the sorting order.

A release tag usually consists of an optional prefix, e.g. 'v' or
'snapshot-', followed by the actual version number, followed by an
optional suffix.  In the contrived example quoted above this suffix
is '-foo-bar', thus it feels wrong to match '-bar' against it, when
the user explicitly configured '-foo-bar' as prerelease suffix as
well.

Then here is a more realistic case for sorting based on the longest
matching suffix, where

  - a longer suffix starts with the shorter one,
  - and the longer suffix comes after the shorter one in the
configuration.

With my patches it looks like this:

   $ git -c versionsort.prereleasesuffix=-pre \
 -c versionsort.prereleasesuffix=-prerelease \
 tag -l --sort=version:refname
   v1.0.0-prerelease1
   v1.0.0-pre1
   v1.0.0-pre2
   v1.0.0

Yeah, having both '-pre' and '-prerelease' suffixes seems somewhat
silly, but who knows what similar but more reasonable prerelease
suffixes e.g. non-English speaking users might use in their native
language.

Anyway, my intuition says that any '-prereleaseX' tags should come
after all the '-preX' tags.  (Ironically, current git just happens
to get this particular case right.)

My patches get this wrong, because they look for prerelease suffixes
_containing_ the first different character.  However, when a '-preX'
and a '-prereleaseX' tag are compared, then the whole '-pre' suffix
is part of the common part, thus it doesn't match, only '-prerelease'
matches.

So, to sort this case right the implementation should

  - look for a prerelease suffix containing the first different
character or ending right before the first different character,
(This means that when comparing 'v1.0.0-pre1' and 'v1.0.0-pre2'
swap_prereleases() would match '-pre' in both: no big deal, it
should return "undecided" and let the caller do the right thing
by sorting based on '1' and '2')

  - and sort a tag based on the longest matching prerelease suffix.
(In my quoted email above I alluded that its implementation must
be more complicated.  No, it turns out that it actually isn't.)

(Just for the record: it's still not 100% foolproof, though.  Someone
asking for trouble might use letters instead of numbers to indicate
subsequent prereleases, and might have tags 'v1.0-prea', 'v1.0-preb',
..., 'v1.0-prer', and 'v1.0-prerelease'.  In this case the sorting
algorithm might happen to decide to compare 'v1.0-prer

Re: [PATCH 5/5] versioncmp: cope with common leading parts in versionsort.prereleaseSuffix

2016-10-05 Thread SZEDER Gábor


Quoting Junio C Hamano :


SZEDER Gábor  writes:


And a final sidenote: sorting based on the longest matching suffix
also allows us to (ab)use version sort with prerelease suffixes to
sort postrelease tags as we please, too:

 $ ~/src/git/git -c versionsort.prereleasesuffix=-alpha \
 -c versionsort.prereleasesuffix=-beta \
 -c versionsort.prereleasesuffix= \
 -c versionsort.prereleasesuffix=-gamma \
 -c versionsort.prereleasesuffix=-delta \
 tag -l --sort=version:refname 'v3.0*'
 v3.0-alpha1
 v3.0-beta1
 v3.0
 v3.0-gamma1
 v3.0-delta1


Assuming that gamma and delta are meant to indicate "these are
post-release updates",


Indeed they were meant as post-release suffixes.  Naturally following
alpha and beta, they were the first to spring to mind that should be
sorted in non-lexicographical order, so I could show of postrelease
reordering.  It's just that we don't have a config like
'versionsort.postreleasesuffix', which is half the abuse.  The other
half of the abuse is that I had to explicitly indicate the position
of suffixless versions with an empty suffix between pre- and
postrelease suffixes.  The empty suffix matches on every tag, but
then it's overridden by all configured suffixes, so such a version
just stays in the middle.


I think a mechanism that can yield the above
result is fantastic ;-)


Heh.

Gut feeling tells me that I should take this as a subtle
encouragement to look into adding 'versionsort.postreleasesuffix',
shouldn't I ;)



Re: [PATCH/RFC] git.c: support "!!" aliases that do not move cwd

2016-10-11 Thread SZEDER Gábor


Quoting Duy Nguyen :


On Fri, Oct 7, 2016 at 10:55 PM, Jakub Narębski  wrote:

W dniu 07.10.2016 o 16:19, Johannes Schindelin pisze:

On Fri, 7 Oct 2016, Jakub Narębski wrote:



Note that we would have to teach git completion about new syntax;
or new configuration variable if we go that route.


Why would we? Git's completion does not expand aliases, it only completes
the aliases' names, not their values.


Because Git completion finds out which _options_ and _arguments_
to complete for an alias based on its expansion.

Yes, this is nice bit of trickery...


It's c63437c (bash: improve aliased command recognition - 2010-02-23)
isn't it? This may favor j6t's approach [1] because retrieving alias
attributes is much easier.

[1]
https://public-inbox.org/git/20161006190720.4ecf3ptl6mszt...@sigill.intra.peff.net/T/#mb1d7b8f31d595b05105b8ea2137756761e13dbf4
--
Duy


The completion script is concerned in three ways:

  1. it has to get the names of all aliases, to offer them along with
 git commands for 'git ' or 'git help ',

  2. it has to get the command executed in place of the alias, and

  3. strip everything that can't be a git command, so it can offer the
 right options for the aliased command.


The '!!' syntax is the easiest, I think it will just work even with
the current completion code, no changes necessary.

The '(nocd)' form is still easy, we only have to add this '(nocd)' to
that list of stripped words for (3), but no further changes necessary
for (1) and (2).

'alias.d2u.command' is tricky.  Both (1) and (2) require adjustments,
preferably in a way that doesn't involve additional git processes, at
least for (1), as it is executed often, for every 'git ', for the
sake of users on platforms with not particularly stellar fork()+exec()
performance.  I think it would be possible to have only one 'git
config --get-regexp' and a little bit of post processing in each case,
but I didn't think too hard about possible pitfalls, especially when
dealing with ambiguity when both 'alias.d2u' and 'alias.d2u.command'
are set.  And I won't think any more about it until a conclusion is
reached that we'll go this route :)




Re: [Bug?] git notes are not copied during rebase

2016-11-17 Thread SZEDER Gábor
> I am currently a heavy user of rebasing and noticed that my notes
> don't get correctly applied, even if notes.rewrite.rebase is set
> explicitly to true (though manual says that is the default).

Setting 'notes.rewrite.rebase' is, as you mentioned, not necessary,
but not sufficient either.  See here, especially the second paragraph:

   notes.rewriteRef
   When copying notes during a rewrite, specifies the (fully
   qualified) ref whose notes should be copied. May be a glob, in
   which case notes in all matching refs will be copied. You may also
   specify this configuration several times.

   Does not have a default value; you must configure this variable to
   enable note rewriting.

   Can be overridden with the GIT_NOTES_REWRITE_REF environment
   variable.

Gábor



  1   2   3   4   5   6   7   8   9   10   >