Git development
 help / color / mirror / Atom feed
* Re: [PATCH] commit: allow {--amend|-c foo} when {HEAD|foo} has empty message
From: Jeff King @ 2012-02-28  9:05 UTC (permalink / raw)
  To: Thomas Rast; +Cc: Junio C Hamano, git
In-Reply-To: <8529824c8569a8a0b4c4caf3a562750925758e74.1330419275.git.trast@student.ethz.ch>

On Tue, Feb 28, 2012 at 09:57:05AM +0100, Thomas Rast wrote:

> diff --git a/builtin/commit.c b/builtin/commit.c
> index 3714582..45a57af 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -690,7 +690,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
>  		hook_arg1 = "message";
>  	} else if (use_message) {
>  		buffer = strstr(use_message_buffer, "\n\n");
> -		if (!buffer || buffer[2] == '\0')
> +		if (!amend && !edit_message && (!buffer || buffer[2] == '\0'))
>  			die(_("commit has empty message"));

Hmm. So "buffer" used to never be NULL (because we would die if it is),
and now we might not die if we are doing an amend, no? And the next line
is:

>  		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));

Doesn't this need to handle the case of NULL buffer (i.e., when it does
not already have "\n\n" in it)?

-Peff

^ permalink raw reply

* Tilde spec - befuzzled
From: Luke Diamand @ 2012-02-28  9:07 UTC (permalink / raw)
  To: Git List

The documentation for caret and tilde specs is making my head hurt, even 
though they always _do_ exactly what I want. And I thought I understood 
them until I read more carefully.

   A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   '<rev>{caret}'

So far, so good.

'<rev>{tilde}<n>', e.g. 'master{tilde}3'::
   A suffix '{tilde}<n>' to a revision parameter means the commit
   object that is the <n>th generation grand-parent of the named
   commit object, following only the first parents.

Hang on, *grand*-parents?

So HEAD~1 won't give me the *parent* commit of HEAD, but the 
*grandparent* commit of HEAD (following only the first parents) ?

How do I get to the *parent* commit of HEAD?
Does that mean that HEAD~ != HEAD^
And why does HEAD~1 always look exactly what I would naively call the 
'parent' of HEAD?

I'm pretty sure I'm missing something very obvious. I think it must 
confuse other people as well though, as it's quite easy to find webpages 
around that claim that tildes give the _parent_ commit.

Thanks
Luke

^ permalink raw reply

* Re: [PATCH] commit: allow {--amend|-c foo} when {HEAD|foo} has empty message
From: Jeff King @ 2012-02-28  9:14 UTC (permalink / raw)
  To: Thomas Rast; +Cc: Junio C Hamano, git
In-Reply-To: <20120228090540.GB5757@sigill.intra.peff.net>

On Tue, Feb 28, 2012 at 04:05:40AM -0500, Jeff King wrote:

> >  	} else if (use_message) {
> >  		buffer = strstr(use_message_buffer, "\n\n");
> > -		if (!buffer || buffer[2] == '\0')
> > +		if (!amend && !edit_message && (!buffer || buffer[2] == '\0'))
> >  			die(_("commit has empty message"));
> 
> Hmm. So "buffer" used to never be NULL (because we would die if it is),
> and now we might not die if we are doing an amend, no? And the next line
> is:
> 
> >  		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
> 
> Doesn't this need to handle the case of NULL buffer (i.e., when it does
> not already have "\n\n" in it)?

I wrote that after looking at just your patch. Looking at
builtin/commit.c, I think use_message_buffer will always be a re-encoded
commit object. So that strstr should _never_ fail unless the commit
object is corrupt. So the right thing is probably:

  buffer = strstr(use_message_buffer, "\n\n");
  if (!buffer)
          die(_("commit object has invalid format"));
  if (!amend && !edit_message && buffer[2] == '\0))
          die(_("commit has empty message"));

-Peff

^ permalink raw reply

* Re: [PATCH] commit: allow {--amend|-c foo} when {HEAD|foo} has empty message
From: Thomas Rast @ 2012-02-28  9:20 UTC (permalink / raw)
  To: Jeff King; +Cc: Thomas Rast, Junio C Hamano, git
In-Reply-To: <20120228091422.GC5757@sigill.intra.peff.net>

Jeff King <peff@peff.net> writes:

> On Tue, Feb 28, 2012 at 04:05:40AM -0500, Jeff King wrote:
>
>> >  	} else if (use_message) {
>> >  		buffer = strstr(use_message_buffer, "\n\n");
>> > -		if (!buffer || buffer[2] == '\0')
>> > +		if (!amend && !edit_message && (!buffer || buffer[2] == '\0'))
>> >  			die(_("commit has empty message"));
>> 
>> Hmm. So "buffer" used to never be NULL (because we would die if it is),
>> and now we might not die if we are doing an amend, no? And the next line
>> is:
>> 
>> >  		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
>> 
>> Doesn't this need to handle the case of NULL buffer (i.e., when it does
>> not already have "\n\n" in it)?
>
> I wrote that after looking at just your patch. Looking at
> builtin/commit.c, I think use_message_buffer will always be a re-encoded
> commit object. So that strstr should _never_ fail unless the commit
> object is corrupt. So the right thing is probably:
>
>   buffer = strstr(use_message_buffer, "\n\n");
>   if (!buffer)
>           die(_("commit object has invalid format"));
>   if (!amend && !edit_message && buffer[2] == '\0))
>           die(_("commit has empty message"));

Interesting.  After I got your mail, I started poking around, and it
turns out we're in a funny situation here.  I did this:

  $ git cat-file commit HEAD
  tree 205f6b799e7d5c2524468ca006a0131aa57ecce7
  parent c4938d8e6d23e3a8fe10e6466ecd827662c14846
  author Thomas Rast <trast@student.ethz.ch> 1330417798 +0100
  committer Thomas Rast <trast@student.ethz.ch> 1330417798 +0100

  $ git cat-file commit HEAD | grep -v '^$' | git hash-object -w -t commit --stdin
  f68fcc1996173a9e04bd45d42abbb7c85c79546d
  $ git reset --hard f68fcc1996173a9e04bd45d42abbb7c85c79546d

So now I'm at a commit which does not have that \n\n.  And poking around
gives a very confusing picture:

  $ git fsck
  Checking object directories: 100% (256/256), done.
  $ git show
  Segmentation fault
  $ git log
  Segmentation fault
  $ git format-patch -1
  0001-.txt
  $ cat 0001-.txt  # no output!
  $ git bundle create test.bdl HEAD
  error: rev-list died of signal 11
  error: rev-list died
  $ git rev-list HEAD
  f68fcc1996173a9e04bd45d42abbb7c85c79546d
  c4938d8e6d23e3a8fe10e6466ecd827662c14846
  $ git rev-list --pretty=oneline HEAD
  Segmentation fault

So either there's a lot to be fixed, or fsck needs to catch this.

-- 
Thomas Rast
trast@{inf,student}.ethz.ch

^ permalink raw reply

* Re: Tilde spec - befuzzled
From: Andreas Ericsson @ 2012-02-28  9:50 UTC (permalink / raw)
  To: Luke Diamand; +Cc: Git List
In-Reply-To: <4F4C995D.9000504@diamand.org>

On 02/28/2012 10:07 AM, Luke Diamand wrote:
> The documentation for caret and tilde specs is making my head hurt, even though they always _do_ exactly what I want. And I thought I understood them until I read more carefully.
> 
> A suffix '{caret}' to a revision parameter means the first parent of
> that commit object. '{caret}<n>' means the <n>th parent (i.e.
> '<rev>{caret}'
> 
> So far, so good.
> 
> '<rev>{tilde}<n>', e.g. 'master{tilde}3'::
> A suffix '{tilde}<n>' to a revision parameter means the commit
> object that is the <n>th generation grand-parent of the named
> commit object, following only the first parents.
> 
> Hang on, *grand*-parents?
> 

Replace "grand-parent" with "ancestor" and your head might hurt a
little less. Caret only ever describes parent, while tilde can
describe either parent or n'th generation grand-parent. Since parent
and grand-parent are all ancestors, that would be a better term.

I believe this stems from the fact that most git-gurus use a single
caret to specify 'parent' and only go to the tilde when they want
grandparents. It might be because caret was implemented first, or
because of english keyboard layout. On swedish keyboards, using tilde
is far more convenient than using caret, so that's what I mostly use
when I want the parent of a single-parent commit (it requires only
one hand and no thumb-acrobatics even on my laptop).

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

Considering the successes of the wars on alcohol, poverty, drugs and
terror, I think we should give some serious thought to declaring war
on peace.

^ permalink raw reply

* [PATCH v2] commit: allow {--amend|-c foo} when {HEAD|foo} has empty message
From: Thomas Rast @ 2012-02-28 10:36 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20120228091422.GC5757@sigill.intra.peff.net>

Because --amend (-c foo) internally load the message from HEAD (foo,
resp.) using the same code paths as -C, they erroneously refuse to
work at all when the message of HEAD (foo) is empty.

Remove the corresponding check under --amend and -c.

None of this behavior was ever tested (not even for -C empty_message),
so we add a whole batch of new tests.

Reported-by: Lazar Florentin <florentin.lazar@gmail.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---

Like the last version, plus Peff's guard for the invalid commit
format.  I also received Lazar (starlay) 's identity for the
attribution.


 builtin/commit.c  |    4 +++-
 t/t7501-commit.sh |   66 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 3714582..5e9a832 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -690,7 +690,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		hook_arg1 = "message";
 	} else if (use_message) {
 		buffer = strstr(use_message_buffer, "\n\n");
-		if (!buffer || buffer[2] == '\0')
+		if (!buffer)
+			die(_("commit object has invalid format"));
+		if (!amend && !edit_message && buffer[2] == '\0')
 			die(_("commit has empty message"));
 		strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
 		hook_arg1 = "commit";
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 8bb3833..6ab7712 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -473,4 +473,70 @@ test_expect_success 'amend can copy notes' '
 
 '
 
+test_expect_success 'amend on empty commit message' '
+
+	echo bar > bar &&
+	git add bar &&
+	test_tick &&
+	git commit --allow-empty-message -m "" &&
+	git tag empty_message &&
+	git commit --amend -mnonempty &&
+	git cat-file commit HEAD | grep nonempty
+
+'
+
+test_expect_success 'amend with editor on empty commit message' '
+
+	git reset --hard empty_message &&
+	cat >editor <<-\EOF &&
+	#!/bin/sh
+	echo nonempty_one >"$1"
+	EOF
+	chmod 755 editor &&
+	EDITOR=./editor git commit --amend &&
+	git cat-file commit HEAD | grep nonempty_one
+
+'
+
+test_expect_success '--amend -C empty_message fails' '
+
+	test_commit nonempty &&
+	test_must_fail git commit --amend -C empty_message
+
+'
+
+test_expect_success '-C empty_message fails' '
+
+	echo 1 > bar &&
+	git add bar &&
+	test_must_fail git commit --amend -C empty_message
+
+'
+
+test_expect_success '--amend -c empty_message works' '
+
+	cat >editor <<-\EOF &&
+	#!/bin/sh
+	echo nonempty_two >"$1"
+	EOF
+	chmod 755 editor &&
+	EDITOR=./editor git commit --amend -c empty_message &&
+	git cat-file commit HEAD | grep nonempty_two
+
+'
+
+test_expect_success '-c empty_message works' '
+
+	echo 2 > bar &&
+	git add bar &&
+	cat >editor <<-\EOF &&
+	#!/bin/sh
+	echo nonempty_three >"$1"
+	EOF
+	chmod 755 editor &&
+	EDITOR=./editor git commit -c empty_message &&
+	git cat-file commit HEAD | grep nonempty_three
+
+'
+
 test_done
-- 
1.7.9.2.467.g7fee4

^ permalink raw reply related

* [RFC PATCH 2a] pretty: detect missing \n\n in commit message
From: Thomas Rast @ 2012-02-28 10:37 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <010901fbfffe0f806bb19d556ebc1e512a4697f4.1330425111.git.trast@student.ethz.ch>

get_header()'s exit condition is finding the \n\n that separates the
commit header from its message.  If such a double newline is not
present, it segfaults.  Catch this case and die().

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---

This would be the minimal fix to the pretty machinery so that 'git
rev-list --pretty=something HEAD' works when there are such broken
commits.

If 2b goes in, there isn't really any point as we would never get this
far on such a commit.


 pretty.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/pretty.c b/pretty.c
index 8688b8f..b7f097d 100644
--- a/pretty.c
+++ b/pretty.c
@@ -440,7 +440,10 @@ static char *get_header(const struct commit *commit, const char *key)
 	const char *line = commit->buffer;
 
 	for (;;) {
-		const char *eol = strchr(line, '\n'), *next;
+		const char *eol, *next;
+		if (!line)
+			die (_("malformed commit object: no separating \\n\\n?"));
+		eol = strchr(line, '\n');
 
 		if (line == eol)
 			return NULL;
-- 
1.7.9.2.467.g7fee4

^ permalink raw reply related

* [RFC PATCH 2b] parse_commit: refuse to load commit without \n\n separator
From: Thomas Rast @ 2012-02-28 10:37 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <010901fbfffe0f806bb19d556ebc1e512a4697f4.1330425111.git.trast@student.ethz.ch>

A commit object consists of a series of headers, terminated by a blank
line (\n\n), followed by the message.

Refuse to parse a commit which does not contain this \n\n.  This also
lets git-fsck detect the breakage.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---

This is the more drastic approach compared to 2a.  Since the code in
pretty.c was broken given such input, I would favor this.  It does,
however, remove the possibility of fixing up such a broken commit with
the tools git provides (other than resorting to git-hash-object and
git-replace).


 commit.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/commit.c b/commit.c
index 4b39c19..502675c 100644
--- a/commit.c
+++ b/commit.c
@@ -265,6 +265,9 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
 	if (get_sha1_hex(bufptr + 5, parent) < 0)
 		return error("bad tree pointer in commit %s",
 			     sha1_to_hex(item->object.sha1));
+	if (!memmem(bufptr, size, "\n\n", 2))
+		return error("commit object %s is missing \\n\\n separator",
+			     sha1_to_hex(item->object.sha1));
 	item->tree = lookup_tree(parent);
 	bufptr += 46; /* "tree " + "hex sha1" + "\n" */
 	pptr = &item->parents;
-- 
1.7.9.2.467.g7fee4

^ permalink raw reply related

* Re: [PATCH v6 02/11] Add git-column and column mode parsing
From: Nguyen Thai Ngoc Duy @ 2012-02-28 11:00 UTC (permalink / raw)
  To: Ramsay Jones; +Cc: git, Junio C Hamano
In-Reply-To: <4F4BE300.2060302@ramsay1.demon.co.uk>

2012/2/28 Ramsay Jones <ramsay@ramsay1.demon.co.uk>:
> Nguyễn Thái Ngọc Duy wrote:
> [...]
>> +static int parse_option(const char *arg, int len,
>> +                     unsigned int *mode, int stdout_is_tty)
>> +{
>> +     struct colopt opts[] = {
>> +             { ENABLE, "always",  1 },
>> +             { ENABLE, "never",   0 },
>> +             { ENABLE, "auto",   -1 },
>> +     };
>
> Hmm, I don't recognise this table from last time ...
>
>> +                     }
>> +             }
>> +
>> +             name_len = strlen(opts[i].name);
>> +             if (arg_len != name_len ||
>> +                 strncmp(arg_str, opts[i].name, name_len))
>> +                     continue;
>> +
>> +             switch (opts[i].type) {
>> +             case ENABLE:
>> +                     return set_enable_bit(mode, opts[i].value,
>> +                                           stdout_is_tty);
>
> given the above table, can the following case limbs ever be reached?
> (the "no" prefix is only applied to the OPTION type, so most of the
> above code seems to be useless now ...)

Not in this patch, no. The table is extended later on with more modes
and options.

>
>> +             case MODE:
>> +                     return set_mode(mode, opts[i].value);
>> +             case OPTION:
>> +                     return set_option(mode, opts[i].value, set);
>> +             default:
>> +                     die("BUG: Unknown option type %d", opts[i].type);
>> +             }
>> +     }
>> +
>> +     return error("unsupported style '%s'", arg);
>> +}
>> +

-- 
Duy

^ permalink raw reply

* Re: Tilde spec - befuzzled
From: Thomas Rast @ 2012-02-28 11:34 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: Luke Diamand, Git List
In-Reply-To: <4F4CA354.2070503@op5.se>

Andreas Ericsson <ae@op5.se> writes:

> On 02/28/2012 10:07 AM, Luke Diamand wrote:
>> The documentation for caret and tilde specs is making my head hurt, even though they always _do_ exactly what I want. And I thought I understood them until I read more carefully.
>> 
>> A suffix '{caret}' to a revision parameter means the first parent of
>> that commit object. '{caret}<n>' means the <n>th parent (i.e.
>> '<rev>{caret}'
>> 
>> So far, so good.
>> 
>> '<rev>{tilde}<n>', e.g. 'master{tilde}3'::
>> A suffix '{tilde}<n>' to a revision parameter means the commit
>> object that is the <n>th generation grand-parent of the named
>> commit object, following only the first parents.
>> 
>> Hang on, *grand*-parents?
>> 
>
> Replace "grand-parent" with "ancestor" and your head might hurt a
> little less. Caret only ever describes parent, while tilde can
> describe either parent or n'th generation grand-parent. Since parent
> and grand-parent are all ancestors, that would be a better term.

Perhaps we should reword it as "n-th first-parent ancestor"?  Barring
confusion about the position of the dashes, that leaves little room for
error.

-- 
Thomas Rast
trast@{inf,student}.ethz.ch

^ permalink raw reply

* [PATCH 2/3] http: try standard proxy env vars when http.proxy config option is not set
From: Nelson Benitez Leon @ 2012-02-28 12:54 UTC (permalink / raw)
  To: git; +Cc: peff, sam.vilain, sam

Signed-off-by: Nelson Benitez Leon <nbenitezl@gmail.com>
---
 http.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/http.c b/http.c
index 8ac8eb6..79cbe50 100644
--- a/http.c
+++ b/http.c
@@ -295,6 +295,16 @@ static CURL *get_curl_handle(void)
 	if (curl_ftp_no_epsv)
 		curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
 
+	if (!curl_http_proxy) {
+		const char *env_proxy;
+		env_proxy = getenv("HTTP_PROXY");
+		if (!env_proxy) {
+			env_proxy = getenv("http_proxy");
+		}
+		if (env_proxy) {
+			curl_http_proxy = xstrdup(env_proxy);
+		}
+	}
 	if (curl_http_proxy) {
 		curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
 		curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH 3/3] http: when proxy url has username but no password, ask for password
From: Nelson Benitez Leon @ 2012-02-28 12:56 UTC (permalink / raw)
  To: git; +Cc: peff, sam, sam.vilain

Signed-off-by: Nelson Benitez Leon <nbenitezl@gmail.com>
---
 http.c |   36 +++++++++++++++++++++++++++++++++++-
 1 files changed, 35 insertions(+), 1 deletions(-)

diff --git a/http.c b/http.c
index 79cbe50..68e3f7d 100644
--- a/http.c
+++ b/http.c
@@ -306,7 +306,41 @@ static CURL *get_curl_handle(void)
 		}
 	}
 	if (curl_http_proxy) {
-		curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
+		char *at, *colon, *proxyuser;
+		const char *cp;
+		cp = strstr(curl_http_proxy, "://");
+		if (cp == NULL) {
+			cp = curl_http_proxy;
+		} else {
+			cp += 3;
+		}
+		at = strchr(cp, '@');
+		colon = strchr(cp, ':');
+		if (at && (!colon || at < colon)) {
+			/* proxy string has username but no password, ask for password */
+			char *ask_str, *proxyuser, *proxypass;
+			int len;
+			struct strbuf pbuf = STRBUF_INIT;
+			len = at - cp;
+			proxyuser = xmalloc(len + 1);
+			memcpy(proxyuser, cp, len);
+			proxyuser[len] = '\0';
+			
+			strbuf_addf(&pbuf, "Enter password for proxy %s...", at+1);
+			ask_str = strbuf_detach(&pbuf, NULL);
+			proxypass = xstrdup(git_getpass(ask_str));
+			
+			strbuf_insert(&pbuf, 0, curl_http_proxy, cp - curl_http_proxy);
+			strbuf_addf(&pbuf, "%s:%s", proxyuser, proxypass);
+			strbuf_add(&pbuf, at, strlen(at));
+			curl_easy_setopt(result, CURLOPT_PROXY, strbuf_detach(&pbuf, NULL));
+			
+			free(ask_str);
+			free(proxyuser);
+			free(proxypass);
+		} else {
+			curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
+		}
 		curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
 	}
 
-- 
1.7.7.6

^ permalink raw reply related

* [PATCH v7 00/10] Column display
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-1-git-send-email-pclouds@gmail.com>

Changes from v6 [1]:

 - Merge the first two patches into one
 - Make "help -a" support column.ui
 - Remove redundant code Ramsay pointed out in 01/10
 - Reword COL_DENSE commit message, 04/10

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

Nguyễn Thái Ngọc Duy (10):
  Add git-column for columnar display
  Stop starting pager recursively
  column: add columnar layout
  column: add dense layout support
  column: add column.ui for default column output settings
  help: reuse print_columns() for help -a
  branch: add --column
  status: add --column
  column: support piping stdout to external git-column process
  tag: add --column

 .gitignore                   |    1 +
 Documentation/config.txt     |   38 ++++
 Documentation/git-branch.txt |    9 +
 Documentation/git-column.txt |   53 +++++
 Documentation/git-status.txt |    7 +
 Documentation/git-tag.txt    |    9 +
 Makefile                     |    3 +
 builtin.h                    |    1 +
 builtin/branch.c             |   32 +++-
 builtin/column.c             |   62 ++++++
 builtin/commit.c             |    6 +
 builtin/help.c               |    7 +-
 builtin/tag.c                |   26 ++-
 column.c                     |  476 ++++++++++++++++++++++++++++++++++++++++++
 column.h                     |   40 ++++
 command-list.txt             |    1 +
 git.c                        |    1 +
 help.c                       |   54 ++----
 help.h                       |    2 +-
 pager.c                      |    2 +-
 parse-options.h              |    2 +
 t/t3200-branch.sh            |   77 +++++++
 t/t7004-tag.sh               |   44 ++++
 t/t7508-status.sh            |   24 ++
 t/t9002-column.sh            |  161 ++++++++++++++
 wt-status.c                  |   28 +++-
 wt-status.h                  |    1 +
 27 files changed, 1119 insertions(+), 48 deletions(-)
 create mode 100644 Documentation/git-column.txt
 create mode 100644 builtin/column.c
 create mode 100644 column.c
 create mode 100644 column.h
 create mode 100755 t/t9002-column.sh

-- 
1.7.8.36.g69ee2

^ permalink raw reply

* [PATCH v7 01/10] Add git-column for columnar display
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

This command is not particularly tied to anything specific to git. The
command is added for supporting column display in various commands,
either by reusing code or piping output to this command for layour.
The command at this stage is just a skeleton, no layout code yet.

A column option string consists of many token separated by either
space of commas. A token belongs to one of three groups:

 - enabling: always, never and auto
 - layout mode: to be implemented
 - other tuning, which could be negated be prefix 'no'

A command line option without argument (e.g. --column) will enable
column output and reuse existing settings (layout mode and options..).
--no-column disables columnar output.

Thanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore                   |    1 +
 Documentation/git-column.txt |   49 ++++++++++++
 Makefile                     |    3 +
 builtin.h                    |    1 +
 builtin/column.c             |   41 ++++++++++
 column.c                     |  170 ++++++++++++++++++++++++++++++++++++++++++
 column.h                     |   30 ++++++++
 command-list.txt             |    1 +
 git.c                        |    1 +
 parse-options.h              |    2 +
 t/t9002-column.sh            |   27 +++++++
 11 files changed, 326 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-column.txt
 create mode 100644 builtin/column.c
 create mode 100644 column.c
 create mode 100644 column.h
 create mode 100755 t/t9002-column.sh

diff --git a/.gitignore b/.gitignore
index 87fcc5f..2540264 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@
 /git-cherry-pick
 /git-clean
 /git-clone
+/git-column
 /git-commit
 /git-commit-tree
 /git-config
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
new file mode 100644
index 0000000..508b85f
--- /dev/null
+++ b/Documentation/git-column.txt
@@ -0,0 +1,49 @@
+git-column(1)
+=============
+
+NAME
+----
+git-column - Display data in columns
+
+SYNOPSIS
+--------
+[verse]
+'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]
+	     [--indent=<string>] [--nl=<string>] [--pading=<n>]
+
+DESCRIPTION
+-----------
+This command formats its input into multiple columns.
+
+OPTIONS
+-------
+--mode=<mode>::
+	Specify layout mode. See configuration variable column.ui for option
+	syntax.
+
+--rawmode=<n>::
+	Same as --mode but take mode encoded as a number. This is mainly used
+	by other commands that have already parsed layout mode.
+
+--width=<width>::
+	Specify the terminal width. By default 'git column' will detect the
+	terminal width, or fall back to 80 if it is unable to do so.
+
+--indent=<string>::
+	String to be printed at the beginning of each line.
+
+--nl=<N>::
+	String to be printed at the end of each line,
+	including newline character.
+
+--padding=<N>::
+	The number of spaces between columns. One space by default.
+
+
+Author
+------
+Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index a0de4e9..0998f0d 100644
--- a/Makefile
+++ b/Makefile
@@ -646,6 +646,7 @@ LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
 LIB_OBJS += color.o
+LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
 LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
@@ -774,6 +775,7 @@ BUILTIN_OBJS += builtin/checkout-index.o
 BUILTIN_OBJS += builtin/checkout.o
 BUILTIN_OBJS += builtin/clean.o
 BUILTIN_OBJS += builtin/clone.o
+BUILTIN_OBJS += builtin/column.o
 BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
 BUILTIN_OBJS += builtin/config.o
@@ -2166,6 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
+column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
diff --git a/builtin.h b/builtin.h
index 857b9c8..338f540 100644
--- a/builtin.h
+++ b/builtin.h
@@ -61,6 +61,7 @@ extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
 extern int cmd_clone(int argc, const char **argv, const char *prefix);
 extern int cmd_clean(int argc, const char **argv, const char *prefix);
+extern int cmd_column(int argc, const char **argv, const char *prefix);
 extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
diff --git a/builtin/column.c b/builtin/column.c
new file mode 100644
index 0000000..3b0f74e
--- /dev/null
+++ b/builtin/column.c
@@ -0,0 +1,41 @@
+#include "builtin.h"
+#include "cache.h"
+#include "strbuf.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "column.h"
+
+static const char * const builtin_column_usage[] = {
+	"git column [options]",
+	NULL
+};
+static unsigned int colopts;
+
+int cmd_column(int argc, const char **argv, const char *prefix)
+{
+	struct string_list list = STRING_LIST_INIT_DUP;
+	struct strbuf sb = STRBUF_INIT;
+	struct column_options copts;
+	struct option options[] = {
+		OPT_COLUMN(0, "mode", &colopts, "layout to use"),
+		OPT_INTEGER(0, "rawmode", &colopts, "layout to use"),
+		OPT_INTEGER(0, "width", &copts.width, "Maximum width"),
+		OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"),
+		OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"),
+		OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"),
+		OPT_END()
+	};
+
+	memset(&copts, 0, sizeof(copts));
+	copts.width = term_columns();
+	copts.padding = 1;
+	argc = parse_options(argc, argv, "", options, builtin_column_usage, 0);
+	if (argc)
+		usage_with_options(builtin_column_usage, options);
+
+	while (!strbuf_getline(&sb, stdin, '\n'))
+		string_list_append(&list, sb.buf);
+
+	print_columns(&list, colopts, &copts);
+	return 0;
+}
diff --git a/column.c b/column.c
new file mode 100644
index 0000000..d61da81
--- /dev/null
+++ b/column.c
@@ -0,0 +1,170 @@
+#include "cache.h"
+#include "column.h"
+#include "string-list.h"
+#include "parse-options.h"
+
+#define MODE(mode) ((mode) & COL_MODE)
+
+/* Display without layout when COL_ENABLED is not set */
+static void display_plain(const struct string_list *list,
+			  const char *indent, const char *nl)
+{
+	int i;
+
+	for (i = 0; i < list->nr; i++)
+		printf("%s%s%s", indent, list->items[i].string, nl);
+}
+
+void print_columns(const struct string_list *list, unsigned int mode,
+		   struct column_options *opts)
+{
+	const char *indent = "", *nl = "\n";
+	int padding = 1, width = term_columns();
+
+	if (!list->nr)
+		return;
+	if (opts) {
+		if (opts->indent)
+			indent = opts->indent;
+		if (opts->nl)
+			nl = opts->nl;
+		if (opts->width)
+			width = opts->width;
+		padding = opts->padding;
+	}
+	if (width <= 1 || !(mode & COL_ENABLED)) {
+		display_plain(list, indent, nl);
+		return;
+	}
+	die("BUG: invalid mode %d", MODE(mode));
+}
+
+struct colopt {
+	enum {
+		ENABLE,
+		MODE,
+		OPTION
+	} type;
+	const char *name;
+	int value;
+};
+
+/*
+ * Set COL_ENABLED and COL_ENABLED_SET. If 'set' is -1, check if
+ * stdout is tty.
+ */
+static int set_enable_bit(unsigned int *mode, int set, int stdout_is_tty)
+{
+	if (set < 0) {	/* auto */
+		if (stdout_is_tty < 0)
+			stdout_is_tty = isatty(1);
+		set = stdout_is_tty || (pager_in_use() && pager_use_color);
+	}
+	if (set)
+		*mode = *mode | COL_ENABLED | COL_ENABLED_SET;
+	else
+		*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;
+	return 0;
+}
+
+/*
+ * Set COL_MODE_*. mode is intially copied from column.ui. If
+ * COL_ENABLED_SET is not set, then neither 'always', 'never' nor
+ * 'auto' has been used. Default to 'always'.
+ */
+static int set_mode(unsigned int *mode, unsigned int value)
+{
+	*mode = (*mode & ~COL_MODE) | value;
+	if (!(*mode & COL_ENABLED_SET))
+		*mode |= COL_ENABLED | COL_ENABLED_SET;
+
+	return 0;
+}
+
+/* Set or unset other COL_* */
+static int set_option(unsigned int *mode, unsigned int opt, int set)
+{
+	if (set)
+		*mode |= opt;
+	else
+		*mode &= ~opt;
+	return 0;
+}
+
+static int parse_option(const char *arg, int len,
+			unsigned int *mode, int stdout_is_tty)
+{
+	struct colopt opts[] = {
+		{ ENABLE, "always",  1 },
+		{ ENABLE, "never",   0 },
+		{ ENABLE, "auto",   -1 },
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(opts); i++) {
+		int set = 1, arg_len = len, name_len;
+		const char *arg_str = arg;
+
+		if (opts[i].type == OPTION) {
+			if (arg_len > 2 && !strncmp(arg_str, "no", 2)) {
+				arg_str += 2;
+				arg_len -= 2;
+				set = 0;
+			}
+		}
+
+		name_len = strlen(opts[i].name);
+		if (arg_len != name_len ||
+		    strncmp(arg_str, opts[i].name, name_len))
+			continue;
+
+		switch (opts[i].type) {
+		case ENABLE:
+			return set_enable_bit(mode, opts[i].value,
+					      stdout_is_tty);
+		case MODE:
+			return set_mode(mode, opts[i].value);
+		case OPTION:
+			return set_option(mode, opts[i].value, set);
+		default:
+			die("BUG: Unknown option type %d", opts[i].type);
+		}
+	}
+
+	return error("unsupported style '%s'", arg);
+}
+
+int git_config_column(unsigned int *mode, const char *value,
+		      int stdout_is_tty)
+{
+	const char *sep = " ,";
+
+	while (*value) {
+		int len = strcspn(value, sep);
+		if (len) {
+			if (parse_option(value, len, mode, stdout_is_tty))
+				return -1;
+
+			value += len;
+		}
+		value += strspn(value, sep);
+	}
+	return 0;
+}
+
+int parseopt_column_callback(const struct option *opt,
+			     const char *arg, int unset)
+{
+	unsigned int *mode = opt->value;
+	*mode |= COL_PARSEOPT;
+	if (unset) {
+		*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;
+		return 0;
+	}
+	if (arg)
+		return git_config_column(mode, arg, -1);
+
+	/* no arg, turn it on */
+	*mode |= COL_ENABLED | COL_ENABLED_SET;
+	return 0;
+}
diff --git a/column.h b/column.h
new file mode 100644
index 0000000..67b1c4f
--- /dev/null
+++ b/column.h
@@ -0,0 +1,30 @@
+#ifndef COLUMN_H
+#define COLUMN_H
+
+#define COL_MODE          0x000F
+#define COL_ENABLED      (1 << 4)
+#define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */
+#define COL_PARSEOPT     (1 << 8)  /* --column is given */
+
+#define explicitly_enable_column(c) \
+	(((c) & (COL_PARSEOPT | COL_ENABLED)) == (COL_PARSEOPT | COL_ENABLED))
+
+struct column_options {
+	int width;
+	int padding;
+	const char *indent;
+	const char *nl;
+};
+
+extern int term_columns(void);
+extern void print_columns(const struct string_list *list,
+			  unsigned int mode,
+			  struct column_options *opts);
+extern int git_config_column(unsigned int *mode, const char *value,
+			     int stdout_is_tty);
+
+struct option;
+extern int parseopt_column_callback(const struct option *opt,
+				    const char *arg, int unset);
+
+#endif
diff --git a/command-list.txt b/command-list.txt
index a36ee9b..fe06f15 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -20,6 +20,7 @@ git-cherry-pick                         mainporcelain
 git-citool                              mainporcelain
 git-clean                               mainporcelain
 git-clone                               mainporcelain common
+git-column                              purehelpers
 git-commit                              mainporcelain common
 git-commit-tree                         plumbingmanipulators
 git-config                              ancillarymanipulators
diff --git a/git.c b/git.c
index 3805616..419e3cc 100644
--- a/git.c
+++ b/git.c
@@ -348,6 +348,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
 		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
 		{ "clone", cmd_clone },
+		{ "column", cmd_column },
 		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
 		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
 		{ "config", cmd_config, RUN_SETUP_GENTLY },
diff --git a/parse-options.h b/parse-options.h
index 2e811dc..56fcafd 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -238,5 +238,7 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);
 	  PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
 #define OPT__COLOR(var, h) \
 	OPT_COLOR_FLAG(0, "color", (var), (h))
+#define OPT_COLUMN(s, l, v, h) \
+	{ OPTION_CALLBACK, (s), (l), (v), "style", (h), PARSE_OPT_OPTARG, parseopt_column_callback }
 
 #endif
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
new file mode 100755
index 0000000..b0b6d62
--- /dev/null
+++ b/t/t9002-column.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='git column'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	cat >lista <<\EOF
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+'
+
+test_expect_success 'never' '
+	git column --mode=never <lista >actual &&
+	test_cmp lista actual
+'
+
+test_done
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 02/10] Stop starting pager recursively
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

git-column can be used as a pager for other git commands, something
like this:

    GIT_PAGER="git -p column --mode='dense color'" git -p branch

The problem with this is that "git -p column" also has $GIT_PAGER
set so the pager runs itself again as a pager, then again and again.

Stop this.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 pager.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/pager.c b/pager.c
index 05584de..4dcb08d 100644
--- a/pager.c
+++ b/pager.c
@@ -73,7 +73,7 @@ void setup_pager(void)
 {
 	const char *pager = git_pager(isatty(1));
 
-	if (!pager)
+	if (!pager || pager_in_use())
 		return;
 
 	/*
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 03/10] column: add columnar layout
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

COL_MODE_COLUMN and COL_MODE_ROW fill column by column (or row by row
respectively), given the terminal width and how many space between
columns.

Strings are supposed to be in UTF-8. If strings contain ANSI escape
strings, COL_ANSI must be specified for correct length calculation.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 column.c          |  131 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 column.h          |    3 +
 t/t9002-column.sh |   86 +++++++++++++++++++++++++++++++++++
 3 files changed, 219 insertions(+), 1 deletions(-)

diff --git a/column.c b/column.c
index d61da81..6aaa829 100644
--- a/column.c
+++ b/column.c
@@ -2,8 +2,66 @@
 #include "column.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "utf8.h"
 
 #define MODE(mode) ((mode) & COL_MODE)
+#define XY2LINEAR(d, x, y) (MODE((d)->mode) == COL_MODE_COLUMN ? \
+			    (x) * (d)->rows + (y) : \
+			    (y) * (d)->cols + (x))
+
+struct column_data {
+	const struct string_list *list; /* list of all cells */
+	int mode;			/* COL_MODE */
+	int total_width;		/* terminal width */
+	int padding;			/* cell padding */
+	const char *indent;		/* left most column indentation */
+	const char *nl;
+
+	int rows, cols;
+	int *len;			/* cell length */
+};
+
+/* return length of 's' in letters, ANSI escapes stripped */
+static int item_length(int mode, const char *s)
+{
+	int len, i = 0;
+	struct strbuf str = STRBUF_INIT;
+
+	if (!(mode & COL_ANSI))
+		return utf8_strwidth(s);
+
+	strbuf_addstr(&str, s);
+	while ((s = strstr(str.buf + i, "\033[")) != NULL) {
+		int len = strspn(s + 2, "0123456789;");
+		i = s - str.buf;
+		strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */
+	}
+	len = utf8_strwidth(str.buf);
+	strbuf_release(&str);
+	return len;
+}
+
+/*
+ * Calculate cell width, rows and cols for a table of equal cells, given
+ * table width and how many spaces between cells.
+ */
+static void layout(struct column_data *data, int *width)
+{
+	int i;
+
+	*width = 0;
+	for (i = 0; i < data->list->nr; i++)
+		if (*width < data->len[i])
+			*width = data->len[i];
+
+	*width += data->padding;
+
+	data->cols = (data->total_width - strlen(data->indent)) / *width;
+	if (data->cols == 0)
+		data->cols = 1;
+
+	data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
+}
 
 /* Display without layout when COL_ENABLED is not set */
 static void display_plain(const struct string_list *list,
@@ -15,6 +73,65 @@ static void display_plain(const struct string_list *list,
 		printf("%s%s%s", indent, list->items[i].string, nl);
 }
 
+/* Print a cell to stdout with all necessary leading/traling space */
+static int display_cell(struct column_data *data, int initial_width,
+			const char *empty_cell, int x, int y)
+{
+	int i, len, newline;
+
+	i = XY2LINEAR(data, x, y);
+	if (i >= data->list->nr)
+		return -1;
+	len = data->len[i];
+	if (MODE(data->mode) == COL_MODE_COLUMN)
+		newline = i + data->rows >= data->list->nr;
+	else
+		newline = x == data->cols - 1 || i == data->list->nr - 1;
+
+	printf("%s%s%s",
+			x == 0 ? data->indent : "",
+			data->list->items[i].string,
+			newline ? data->nl : empty_cell + len);
+	return 0;
+}
+
+/* Display COL_MODE_COLUMN or COL_MODE_ROW */
+static void display_table(const struct string_list *list,
+			  int mode, int total_width,
+			  int padding, const char *indent,
+			  const char *nl)
+{
+	struct column_data data;
+	int x, y, i, initial_width;
+	char *empty_cell;
+
+	memset(&data, 0, sizeof(data));
+	data.list = list;
+	data.mode = mode;
+	data.total_width = total_width;
+	data.padding = padding;
+	data.indent = indent;
+	data.nl = nl;
+
+	data.len = xmalloc(sizeof(*data.len) * list->nr);
+	for (i = 0; i < list->nr; i++)
+		data.len[i] = item_length(mode, list->items[i].string);
+
+	layout(&data, &initial_width);
+
+	empty_cell = xmalloc(initial_width + 1);
+	memset(empty_cell, ' ', initial_width);
+	empty_cell[initial_width] = '\0';
+	for (y = 0; y < data.rows; y++) {
+		for (x = 0; x < data.cols; x++)
+			if (display_cell(&data, initial_width, empty_cell, x, y))
+				break;
+	}
+
+	free(data.len);
+	free(empty_cell);
+}
+
 void print_columns(const struct string_list *list, unsigned int mode,
 		   struct column_options *opts)
 {
@@ -36,7 +153,16 @@ void print_columns(const struct string_list *list, unsigned int mode,
 		display_plain(list, indent, nl);
 		return;
 	}
-	die("BUG: invalid mode %d", MODE(mode));
+
+	switch (MODE(mode)) {
+	case COL_MODE_ROW:
+	case COL_MODE_COLUMN:
+		display_table(list, mode, width, padding, indent, nl);
+		break;
+
+	default:
+		die("BUG: invalid mode %d", MODE(mode));
+	}
 }
 
 struct colopt {
@@ -98,6 +224,9 @@ static int parse_option(const char *arg, int len,
 		{ ENABLE, "always",  1 },
 		{ ENABLE, "never",   0 },
 		{ ENABLE, "auto",   -1 },
+		{ MODE,   "column", COL_MODE_COLUMN },
+		{ MODE,   "row",    COL_MODE_ROW },
+		{ OPTION, "color",  COL_ANSI },
 	};
 	int i;
 
diff --git a/column.h b/column.h
index 67b1c4f..d9d27c6 100644
--- a/column.h
+++ b/column.h
@@ -2,8 +2,11 @@
 #define COLUMN_H
 
 #define COL_MODE          0x000F
+#define COL_MODE_COLUMN        0   /* Fill columns before rows */
+#define COL_MODE_ROW           1   /* Fill rows before columns */
 #define COL_ENABLED      (1 << 4)
 #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */
+#define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */
 #define COL_PARSEOPT     (1 << 8)  /* --column is given */
 
 #define explicitly_enable_column(c) \
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index b0b6d62..cffb029 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -24,4 +24,90 @@ test_expect_success 'never' '
 	test_cmp lista actual
 '
 
+test_expect_success '80 columns' '
+	cat >expected <<\EOF &&
+one    two    three  four   five   six    seven  eight  nine   ten    eleven
+EOF
+	COLUMNS=80 git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'COLUMNS = 1' '
+	cat >expected <<\EOF &&
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+	COLUMNS=1 git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+	git column --mode=column --width=1 <lista >actual &&
+	test_cmp expected actual
+'
+
+COLUMNS=20
+export COLUMNS
+
+test_expect_success '20 columns' '
+	cat >expected <<\EOF &&
+one    seven
+two    eight
+three  nine
+four   ten
+five   eleven
+six
+EOF
+	git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, padding 2' '
+	cat >expected <<\EOF &&
+one     seven
+two     eight
+three   nine
+four    ten
+five    eleven
+six
+EOF
+	git column --mode=column --padding 2 <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, indented' '
+	cat >expected <<\EOF &&
+  one    seven
+  two    eight
+  three  nine
+  four   ten
+  five   eleven
+  six
+EOF
+	git column --mode=column --indent="  " <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first' '
+	cat >expected <<\EOF &&
+one    two
+three  four
+five   six
+seven  eight
+nine   ten
+eleven
+EOF
+	git column --mode=row <lista >actual &&
+	test_cmp expected actual
+'
+
 test_done
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 04/10] column: add dense layout support
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

Normally, all items will be gone through once. The largest cell will set
column width for all columns. With this strategy, one long item will
stretch out all columns, wasting space in between them.

With COL_DENSE enabled, it shrinks all columns to minimum, then attempts
to push the last row's cells over to the next column with hope that
everything still fits even there's one row less. The process is repeated
until the new layout cannot fit in given width anymore, or there's only
one row left (perfect!).

Apparently, this mode consumes more cpu than the former, but makes
better use of terminal space. For layouting one or two screens, cpu
usage should not be a problem.

Thanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 column.c          |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 column.h          |    2 +
 t/t9002-column.sh |   48 +++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+), 0 deletions(-)

diff --git a/column.c b/column.c
index 6aaa829..95faec5 100644
--- a/column.c
+++ b/column.c
@@ -19,6 +19,7 @@ struct column_data {
 
 	int rows, cols;
 	int *len;			/* cell length */
+	int *width;			/* index to the longest row in column */
 };
 
 /* return length of 's' in letters, ANSI escapes stripped */
@@ -63,6 +64,69 @@ static void layout(struct column_data *data, int *width)
 	data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
 }
 
+static void compute_column_width(struct column_data *data)
+{
+	int i, x, y;
+	for (x = 0; x < data->cols; x++) {
+		data->width[x] = XY2LINEAR(data, x, 0);
+		for (y = 0; y < data->rows; y++) {
+			i = XY2LINEAR(data, x, y);
+			if (i >= data->list->nr)
+				continue;
+			if (data->len[data->width[x]] < data->len[i])
+				data->width[x] = i;
+		}
+	}
+}
+
+/*
+ * Shrink all columns by shortening them one row each time (and adding
+ * more columns along the way). Hopefully the longest cell will be
+ * moved to the next column, column is shrunk so we have more space
+ * for new columns. The process ends when the whole thing no longer
+ * fits in data->total_width.
+ */
+static void shrink_columns(struct column_data *data)
+{
+	int x, y, total_width, cols, rows;
+
+	data->width = xrealloc(data->width,
+			       sizeof(*data->width) * data->cols);
+	for (x = 0; x < data->cols; x++) {
+		data->width[x] = 0;
+		for (y = 0; y < data->rows; y++) {
+			int len1 = data->len[data->width[x]];
+			int len2 = data->len[XY2LINEAR(data, x, y)];
+			if (len1 < len2)
+				data->width[x] = y;
+		}
+	}
+
+	while (data->rows > 1) {
+		rows = data->rows;
+		cols = data->cols;
+
+		data->rows--;
+		data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
+		if (data->cols != cols)
+			data->width = xrealloc(data->width, sizeof(*data->width) * data->cols);
+
+		compute_column_width(data);
+
+		total_width = strlen(data->indent);
+		for (x = 0; x < data->cols; x++) {
+			total_width += data->len[data->width[x]];
+			total_width += data->padding;
+		}
+		if (total_width > data->total_width) {
+			data->rows = rows;
+			data->cols = cols;
+			compute_column_width(data);
+			break;
+		}
+	}
+}
+
 /* Display without layout when COL_ENABLED is not set */
 static void display_plain(const struct string_list *list,
 			  const char *indent, const char *nl)
@@ -82,7 +146,18 @@ static int display_cell(struct column_data *data, int initial_width,
 	i = XY2LINEAR(data, x, y);
 	if (i >= data->list->nr)
 		return -1;
+
 	len = data->len[i];
+	if (data->width && data->len[data->width[x]] < initial_width) {
+		/*
+		 * empty_cell has initial_width chars, if real column
+		 * is narrower, increase len a bit so we fill less
+		 * space.
+		 */
+		len += initial_width - data->len[data->width[x]];
+		len -= data->padding;
+	}
+
 	if (MODE(data->mode) == COL_MODE_COLUMN)
 		newline = i + data->rows >= data->list->nr;
 	else
@@ -119,6 +194,9 @@ static void display_table(const struct string_list *list,
 
 	layout(&data, &initial_width);
 
+	if (mode & COL_DENSE)
+		shrink_columns(&data);
+
 	empty_cell = xmalloc(initial_width + 1);
 	memset(empty_cell, ' ', initial_width);
 	empty_cell[initial_width] = '\0';
@@ -129,6 +207,7 @@ static void display_table(const struct string_list *list,
 	}
 
 	free(data.len);
+	free(data.width);
 	free(empty_cell);
 }
 
@@ -227,6 +306,7 @@ static int parse_option(const char *arg, int len,
 		{ MODE,   "column", COL_MODE_COLUMN },
 		{ MODE,   "row",    COL_MODE_ROW },
 		{ OPTION, "color",  COL_ANSI },
+		{ OPTION, "dense",  COL_DENSE },
 	};
 	int i;
 
diff --git a/column.h b/column.h
index d9d27c6..eb03c6c 100644
--- a/column.h
+++ b/column.h
@@ -7,6 +7,8 @@
 #define COL_ENABLED      (1 << 4)
 #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */
 #define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */
+#define COL_DENSE        (1 << 7)  /* Shrink columns when possible,
+				      making space for more columns */
 #define COL_PARSEOPT     (1 << 8)  /* --column is given */
 
 #define explicitly_enable_column(c) \
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index cffb029..fe7a30e 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -71,6 +71,30 @@ EOF
 	test_cmp expected actual
 '
 
+test_expect_success '20 columns, nodense' '
+	cat >expected <<\EOF &&
+one    seven
+two    eight
+three  nine
+four   ten
+five   eleven
+six
+EOF
+	git column --mode=column,nodense < lista > actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, dense' '
+	cat >expected <<\EOF &&
+one   five  nine
+two   six   ten
+three seven eleven
+four  eight
+EOF
+	git column --mode=column,dense < lista > actual &&
+	test_cmp expected actual
+'
+
 test_expect_success '20 columns, padding 2' '
 	cat >expected <<\EOF &&
 one     seven
@@ -110,4 +134,28 @@ EOF
 	test_cmp expected actual
 '
 
+test_expect_success '20 columns, row first, nodense' '
+	cat >expected <<\EOF &&
+one    two
+three  four
+five   six
+seven  eight
+nine   ten
+eleven
+EOF
+	git column --mode=row,nodense <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, dense' '
+	cat >expected <<\EOF &&
+one   two    three
+four  five   six
+seven eight  nine
+ten   eleven
+EOF
+	git column --mode=row,dense <lista >actual &&
+	test_cmp expected actual
+'
+
 test_done
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 05/10] column: add column.ui for default column output settings
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config.txt     |   26 ++++++++++++++++++++++++++
 Documentation/git-column.txt |    6 +++++-
 builtin/column.c             |   21 +++++++++++++++++++++
 column.c                     |   28 ++++++++++++++++++++++++++++
 column.h                     |    2 ++
 5 files changed, 82 insertions(+), 1 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index abeb82b..5216598 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -821,6 +821,32 @@ color.ui::
 	`never` if you prefer git commands not to use color unless enabled
 	explicitly with some other configuration or the `--color` option.
 
+column.ui::
+	Specify whether supported commands should output in columns.
+	This variable consists of a list of tokens separated by spaces
+	or commas:
++
+--
+`always`;;
+	always show in columns
+`never`;;
+	never show in columns
+`auto`;;
+	show in columns if the output is to the terminal
+`column`;;
+	fill columns before rows (default)
+`row`;;
+	fill rows before columns
+`dense`;;
+	make unequal size columns to utilize more space
+`nodense`;;
+	make equal size columns
+`color`;;
+	input contains ANSI escape sequence for coloring
+--
++
+	This option defaults to 'never'.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
index 508b85f..94fd7ac 100644
--- a/Documentation/git-column.txt
+++ b/Documentation/git-column.txt
@@ -8,7 +8,7 @@ git-column - Display data in columns
 SYNOPSIS
 --------
 [verse]
-'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]
+'git column' [--command=<name>] [--[raw]mode=<mode>] [--width=<width>]
 	     [--indent=<string>] [--nl=<string>] [--pading=<n>]
 
 DESCRIPTION
@@ -17,6 +17,10 @@ This command formats its input into multiple columns.
 
 OPTIONS
 -------
+--command=<name>::
+	Look up layout mode using configuration variable column.<name> and
+	column.ui.
+
 --mode=<mode>::
 	Specify layout mode. See configuration variable column.ui for option
 	syntax.
diff --git a/builtin/column.c b/builtin/column.c
index 3b0f74e..102d71b 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -11,12 +11,19 @@ static const char * const builtin_column_usage[] = {
 };
 static unsigned int colopts;
 
+static int column_config(const char *var, const char *value, void *cb)
+{
+	return git_column_config(var, value, cb, &colopts);
+}
+
 int cmd_column(int argc, const char **argv, const char *prefix)
 {
 	struct string_list list = STRING_LIST_INIT_DUP;
 	struct strbuf sb = STRBUF_INIT;
 	struct column_options copts;
+	const char *command = NULL, *real_command = NULL;
 	struct option options[] = {
+		OPT_STRING(0, "command", &real_command, "name", "lookup config vars"),
 		OPT_COLUMN(0, "mode", &colopts, "layout to use"),
 		OPT_INTEGER(0, "rawmode", &colopts, "layout to use"),
 		OPT_INTEGER(0, "width", &copts.width, "Maximum width"),
@@ -26,6 +33,15 @@ int cmd_column(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
+	/* This one is special and must be the first one */
+	if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
+		int nonitok = 0;
+		setup_git_directory_gently(&nonitok);
+
+		command = argv[1] + 10;
+		git_config(column_config, (void *)command);
+	}
+
 	memset(&copts, 0, sizeof(copts));
 	copts.width = term_columns();
 	copts.padding = 1;
@@ -33,6 +49,11 @@ int cmd_column(int argc, const char **argv, const char *prefix)
 	if (argc)
 		usage_with_options(builtin_column_usage, options);
 
+	if (real_command || command) {
+		if (!real_command || !command || strcmp(real_command, command))
+			die(_("--command must be the first argument"));
+	}
+
 	while (!strbuf_getline(&sb, stdin, '\n'))
 		string_list_append(&list, sb.buf);
 
diff --git a/column.c b/column.c
index 95faec5..67bfd06 100644
--- a/column.c
+++ b/column.c
@@ -2,6 +2,7 @@
 #include "column.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "color.h"
 #include "utf8.h"
 
 #define MODE(mode) ((mode) & COL_MODE)
@@ -361,6 +362,33 @@ int git_config_column(unsigned int *mode, const char *value,
 	return 0;
 }
 
+static int column_config(const char *var, const char *value,
+			 const char *key, unsigned int *colopts)
+{
+	if (git_config_column(colopts, value, -1))
+		return error("invalid %s mode %s", key, value);
+	return 0;
+}
+
+int git_column_config(const char *var, const char *value,
+		      const char *command, unsigned int *colopts)
+{
+	if (!strcmp(var, "column.ui"))
+		return column_config(var, value, "column.ui", colopts);
+
+	if (command) {
+		struct strbuf sb = STRBUF_INIT;
+		int ret = 0;
+		strbuf_addf(&sb, "column.%s", command);
+		if (!strcmp(var, sb.buf))
+			ret = column_config(var, value, sb.buf, colopts);
+		strbuf_release(&sb);
+		return ret;
+	}
+
+	return 0;
+}
+
 int parseopt_column_callback(const struct option *opt,
 			     const char *arg, int unset)
 {
diff --git a/column.h b/column.h
index eb03c6c..43528da 100644
--- a/column.h
+++ b/column.h
@@ -27,6 +27,8 @@ extern void print_columns(const struct string_list *list,
 			  struct column_options *opts);
 extern int git_config_column(unsigned int *mode, const char *value,
 			     int stdout_is_tty);
+extern int git_column_config(const char *var, const char *value,
+			     const char *command, unsigned int *colopts);
 
 struct option;
 extern int parseopt_column_callback(const struct option *opt,
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 06/10] help: reuse print_columns() for help -a
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/help.c |    7 ++++++-
 help.c         |   54 ++++++++++++++++++------------------------------------
 help.h         |    2 +-
 3 files changed, 25 insertions(+), 38 deletions(-)

diff --git a/builtin/help.c b/builtin/help.c
index 61ff798..c64f152 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -9,6 +9,7 @@
 #include "common-cmds.h"
 #include "parse-options.h"
 #include "run-command.h"
+#include "column.h"
 #include "help.h"
 
 static struct man_viewer_list {
@@ -30,6 +31,7 @@ enum help_format {
 };
 
 static int show_all = 0;
+static unsigned int colopts;
 static enum help_format help_format = HELP_FORMAT_NONE;
 static struct option builtin_help_options[] = {
 	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
@@ -251,6 +253,8 @@ static int add_man_viewer_info(const char *var, const char *value)
 
 static int git_help_config(const char *var, const char *value, void *cb)
 {
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "help", &colopts);
 	if (!strcmp(var, "help.format")) {
 		if (!value)
 			return config_error_nonbool(var);
@@ -424,8 +428,9 @@ int cmd_help(int argc, const char **argv, const char *prefix)
 	parsed_help_format = help_format;
 
 	if (show_all) {
+		git_config(git_help_config, NULL);
 		printf("usage: %s\n\n", git_usage_string);
-		list_commands("git commands", &main_cmds, &other_cmds);
+		list_commands("git commands", colopts, &main_cmds, &other_cmds);
 		printf("%s\n", git_more_info_string);
 		return 0;
 	}
diff --git a/help.c b/help.c
index 14eefc9..3155c68 100644
--- a/help.c
+++ b/help.c
@@ -4,6 +4,8 @@
 #include "levenshtein.h"
 #include "help.h"
 #include "common-cmds.h"
+#include "string-list.h"
+#include "column.h"
 
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
@@ -70,31 +72,20 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
 	cmds->cnt = cj;
 }
 
-static void pretty_print_string_list(struct cmdnames *cmds, int longest)
+static void pretty_print_string_list(struct cmdnames *cmds, unsigned int colopts)
 {
-	int cols = 1, rows;
-	int space = longest + 1; /* min 1 SP between words */
-	int max_cols = term_columns() - 1; /* don't print *on* the edge */
-	int i, j;
-
-	if (space < max_cols)
-		cols = max_cols / space;
-	rows = DIV_ROUND_UP(cmds->cnt, cols);
-
-	for (i = 0; i < rows; i++) {
-		printf("  ");
+	struct string_list list = STRING_LIST_INIT_NODUP;
+	struct column_options copts;
+	int i;
 
-		for (j = 0; j < cols; j++) {
-			int n = j * rows + i;
-			int size = space;
-			if (n >= cmds->cnt)
-				break;
-			if (j == cols-1 || n + rows >= cmds->cnt)
-				size = 1;
-			printf("%-*s", size, cmds->names[n]->name);
-		}
-		putchar('\n');
-	}
+	for (i = 0; i < cmds->cnt; i++)
+		string_list_append(&list, cmds->names[i]->name);
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	/* always enable column display here */
+	print_columns(&list, colopts | COL_ENABLED, &copts);
+	string_list_clear(&list, 0);
 }
 
 static int is_executable(const char *name)
@@ -203,25 +194,16 @@ void load_command_list(const char *prefix,
 	exclude_cmds(other_cmds, main_cmds);
 }
 
-void list_commands(const char *title, struct cmdnames *main_cmds,
-		   struct cmdnames *other_cmds)
+void list_commands(const char *title, unsigned int colopts,
+		   struct cmdnames *main_cmds, struct cmdnames *other_cmds)
 {
-	int i, longest = 0;
-
-	for (i = 0; i < main_cmds->cnt; i++)
-		if (longest < main_cmds->names[i]->len)
-			longest = main_cmds->names[i]->len;
-	for (i = 0; i < other_cmds->cnt; i++)
-		if (longest < other_cmds->names[i]->len)
-			longest = other_cmds->names[i]->len;
-
 	if (main_cmds->cnt) {
 		const char *exec_path = git_exec_path();
 		printf("available %s in '%s'\n", title, exec_path);
 		printf("----------------");
 		mput_char('-', strlen(title) + strlen(exec_path));
 		putchar('\n');
-		pretty_print_string_list(main_cmds, longest);
+		pretty_print_string_list(main_cmds, colopts);
 		putchar('\n');
 	}
 
@@ -230,7 +212,7 @@ void list_commands(const char *title, struct cmdnames *main_cmds,
 		printf("---------------------------------------");
 		mput_char('-', strlen(title));
 		putchar('\n');
-		pretty_print_string_list(other_cmds, longest);
+		pretty_print_string_list(other_cmds, colopts);
 		putchar('\n');
 	}
 }
diff --git a/help.h b/help.h
index b6b12d5..854d2d4 100644
--- a/help.h
+++ b/help.h
@@ -25,7 +25,7 @@ extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);
 /* Here we require that excludes is a sorted list. */
 extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
 extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
-extern void list_commands(const char *title,
+extern void list_commands(const char *title, unsigned int colopts,
 			  struct cmdnames *main_cmds,
 			  struct cmdnames *other_cmds);
 
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 07/10] branch: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config.txt     |    4 ++
 Documentation/git-branch.txt |    9 +++++
 Makefile                     |    2 +-
 builtin/branch.c             |   32 +++++++++++++++--
 t/t3200-branch.sh            |   77 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 119 insertions(+), 5 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5216598..c14db27 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -847,6 +847,10 @@ column.ui::
 +
 	This option defaults to 'never'.
 
+column.branch::
+	Specify whether to output branch listing in `git branch` in columns.
+	See `column.ui` for details.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0427e80..ba5cccb 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
 	[--list] [-v [--abbrev=<length> | --no-abbrev]]
+	[--column[=<options>] | --no-column]
 	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -107,6 +108,14 @@ OPTIONS
 	default to color output.
 	Same as `--color=never`.
 
+--column[=<options>]::
+--no-column::
+	Display branch listing in columns. See configuration variable
+	column.branch for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable in non-verbose mode.
+
 -r::
 --remotes::
 	List or delete (if used with -d) the remote-tracking branches.
diff --git a/Makefile b/Makefile
index 0998f0d..320d3f8 100644
--- a/Makefile
+++ b/Makefile
@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
-column.o help.o pager.o: column.h
+builtin/branch.o column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
diff --git a/builtin/branch.c b/builtin/branch.c
index cb17bc3..611cc0e 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -15,6 +15,8 @@
 #include "branch.h"
 #include "diff.h"
 #include "revision.h"
+#include "string-list.h"
+#include "column.h"
 
 static const char * const builtin_branch_usage[] = {
 	"git branch [options] [-r | -a] [--merged | --no-merged]",
@@ -53,6 +55,9 @@ static enum merge_filter {
 } merge_filter;
 static unsigned char merge_filter_ref[20];
 
+static struct string_list output = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
+
 static int parse_branch_color_slot(const char *var, int ofs)
 {
 	if (!strcasecmp(var+ofs, "plain"))
@@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs)
 
 static int git_branch_config(const char *var, const char *value, void *cb)
 {
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "branch", &colopts);
 	if (!strcmp(var, "color.branch")) {
 		branch_use_color = git_config_colorbool(var, value);
 		return 0;
@@ -474,7 +481,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
 	else if (verbose)
 		/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
 		add_verbose_info(&out, item, verbose, abbrev);
-	printf("%s\n", out.buf);
+	if (colopts & COL_ENABLED) {
+		assert(!verbose && "--column and --verbose are incompatible");
+		string_list_append(&output, out.buf);
+	} else {
+		printf("%s\n", out.buf);
+	}
 	strbuf_release(&name);
 	strbuf_release(&out);
 }
@@ -727,6 +739,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
 			opt_parse_merge_filter, (intptr_t) "HEAD",
 		},
+		OPT_COLUMN(0, "column", &colopts, "list branches in columns"),
 		OPT_END(),
 	};
 
@@ -749,6 +762,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	}
 	hashcpy(merge_filter_ref, head_sha1);
 
+
+	colopts |= COL_ANSI;
 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
 			     0);
 
@@ -760,12 +775,21 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	if (abbrev == -1)
 		abbrev = DEFAULT_ABBREV;
+	if (verbose) {
+		if (explicitly_enable_column(colopts))
+			die(_("--column and --verbose are incompatible"));
+		colopts = 0;
+	}
 
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (list)
-		return print_ref_list(kinds, detached, verbose, abbrev,
-				      with_commit, argv);
+	else if (list) {
+		int ret = print_ref_list(kinds, detached, verbose, abbrev,
+					 with_commit, argv);
+		print_columns(&output, colopts, NULL);
+		string_list_clear(&output, 0);
+		return ret;
+	}
 	else if (edit_description) {
 		const char *branch_name;
 		struct strbuf branch_ref = STRBUF_INIT;
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index dd1aceb..c38592a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -160,6 +160,83 @@ test_expect_success 'git branch --list -d t should fail' '
 	test_path_is_missing .git/refs/heads/t
 '
 
+test_expect_success 'git branch --column' '
+	COLUMNS=80 git branch --column=column >actual &&
+	cat >expected <<\EOF &&
+  a/b/c     bam       foo       l       * master    n         o/p       r
+  abc       bar       j/k       m/m       master2   o/o       q
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'git branch --column with an extremely long branch name' '
+	long=this/is/a/part/of/long/branch/name &&
+	long=z$long/$long/$long/$long &&
+	test_when_finished "git branch -d $long" &&
+	git branch $long &&
+	COLUMNS=80 git branch --column=column >actual &&
+	cat >expected <<EOF &&
+  a/b/c
+  abc
+  bam
+  bar
+  foo
+  j/k
+  l
+  m/m
+* master
+  master2
+  n
+  o/o
+  o/p
+  q
+  r
+  $long
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'git branch with column.*' '
+	git config column.ui column &&
+	git config column.branch "dense" &&
+	COLUMNS=80 git branch >actual &&
+	git config --unset column.branch &&
+	git config --unset column.ui &&
+	cat >expected <<\EOF &&
+  a/b/c   bam   foo   l   * master    n     o/p   r
+  abc     bar   j/k   m/m   master2   o/o   q
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'git branch --column -v should fail' '
+	test_must_fail git branch --column -v
+'
+
+test_expect_success 'git branch -v with column.ui ignored' '
+	git config column.ui column &&
+	COLUMNS=80 git branch -v | cut -c -10 | sed "s/ *$//" >actual &&
+	git config --unset column.ui &&
+	cat >expected <<\EOF &&
+  a/b/c
+  abc
+  bam
+  bar
+  foo
+  j/k
+  l
+  m/m
+* master
+  master2
+  n
+  o/o
+  o/p
+  q
+  r
+EOF
+	test_cmp expected actual
+'
+
 mv .git/config .git/config-saved
 
 test_expect_success 'git branch -m q q2 without config should succeed' '
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 08/10] status: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config.txt     |    4 ++++
 Documentation/git-status.txt |    7 +++++++
 Makefile                     |    2 +-
 builtin/commit.c             |    6 ++++++
 t/t7508-status.sh            |   24 ++++++++++++++++++++++++
 wt-status.c                  |   28 ++++++++++++++++++++++++++--
 wt-status.h                  |    1 +
 7 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index c14db27..ebb210c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -851,6 +851,10 @@ column.branch::
 	Specify whether to output branch listing in `git branch` in columns.
 	See `column.ui` for details.
 
+column.status::
+	Specify whether to output untracked files in `git status` in columns.
+	See `column.ui` for details.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 3d51717..2f87207 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].
 	Terminate entries with NUL, instead of LF.  This implies
 	the `--porcelain` output format if no other format is given.
 
+--column[=<options>]::
+--no-column::
+	Display untracked files in columns. See configuration variable
+	column.status for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never'
+	respectively.
+
 
 OUTPUT
 ------
diff --git a/Makefile b/Makefile
index 320d3f8..d9c5f00 100644
--- a/Makefile
+++ b/Makefile
@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
-builtin/branch.o column.o help.o pager.o: column.h
+builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
diff --git a/builtin/commit.c b/builtin/commit.c
index 3714582..b42b83f 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -27,6 +27,7 @@
 #include "quote.h"
 #include "submodule.h"
 #include "gpg-interface.h"
+#include "column.h"
 
 static const char * const builtin_commit_usage[] = {
 	"git commit [options] [--] <filepattern>...",
@@ -88,6 +89,7 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int no_post_rewrite, allow_empty_message;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
 static char *sign_commit;
+static unsigned int colopts;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -1145,6 +1147,8 @@ static int git_status_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
 
+	if (!prefixcmp(k, "column."))
+		return git_column_config(k, v, "status", &colopts);
 	if (!strcmp(k, "status.submodulesummary")) {
 		int is_bool;
 		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
@@ -1210,6 +1214,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
 		  "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
 		  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+		OPT_COLUMN(0, "column", &colopts, "list untracked files in columns"),
 		OPT_END(),
 	};
 
@@ -1223,6 +1228,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix,
 			     builtin_status_options,
 			     builtin_status_usage, 0);
+	s.colopts = colopts;
 
 	if (null_termination && status_format == STATUS_FORMAT_LONG)
 		status_format = STATUS_FORMAT_PORCELAIN;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index fc57b13..8f5cfac 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -59,6 +59,30 @@ test_expect_success 'status (1)' '
 	test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
 '
 
+test_expect_success 'status --column' '
+	COLUMNS=50 git status --column="column dense" >output &&
+	cat >expect <<\EOF &&
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#	new file:   dir2/added
+#
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#	modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#	dir1/untracked dir2/untracked untracked
+#	dir2/modified  output
+EOF
+	test_cmp expect output
+'
+
 cat >expect <<\EOF
 # On branch master
 # Changes to be committed:
diff --git a/wt-status.c b/wt-status.c
index 9ffc535..eee059e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -11,6 +11,7 @@
 #include "remote.h"
 #include "refs.h"
 #include "submodule.h"
+#include "column.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
 	GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -641,6 +642,8 @@ static void wt_status_print_other(struct wt_status *s,
 {
 	int i;
 	struct strbuf buf = STRBUF_INIT;
+	static struct string_list output = STRING_LIST_INIT_DUP;
+	struct column_options copts;
 
 	if (!l->nr)
 		return;
@@ -649,12 +652,33 @@ static void wt_status_print_other(struct wt_status *s,
 
 	for (i = 0; i < l->nr; i++) {
 		struct string_list_item *it;
+		const char *path;
 		it = &(l->items[i]);
+		path = quote_path(it->string, strlen(it->string),
+				  &buf, s->prefix);
+		if (s->colopts & COL_ENABLED) {
+			string_list_append(&output, path);
+			continue;
+		}
 		status_printf(s, color(WT_STATUS_HEADER, s), "\t");
 		status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
-			"%s\n", quote_path(it->string, strlen(it->string),
-					    &buf, s->prefix));
+				   "%s\n", path);
 	}
+
+	strbuf_release(&buf);
+	if ((s->colopts & COL_ENABLED) == 0)
+		return;
+
+	strbuf_addf(&buf, "%s#\t%s",
+		    color(WT_STATUS_HEADER, s),
+		    color(WT_STATUS_UNTRACKED, s));
+	memset(&copts, 0, sizeof(copts));
+	copts.padding = 1;
+	copts.indent = buf.buf;
+	if (want_color(s->use_color))
+		copts.nl = GIT_COLOR_RESET "\n";
+	print_columns(&output, s->colopts, &copts);
+	string_list_clear(&output, 0);
 	strbuf_release(&buf);
 }
 
diff --git a/wt-status.h b/wt-status.h
index 682b4c8..6dd7207 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -56,6 +56,7 @@ struct wt_status {
 	enum untracked_status_type show_untracked_files;
 	const char *ignore_submodule_arg;
 	char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
+	int colopts;
 
 	/* These are computed during processing of the individual sections */
 	int commitable;
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 09/10] column: support piping stdout to external git-column process
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

For too complicated output handling, it'd be easier to just spawn
git-column and redirect stdout to it. This patch provides helpers
to do that.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 column.c |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 column.h |    3 ++
 2 files changed, 72 insertions(+), 0 deletions(-)

diff --git a/column.c b/column.c
index 67bfd06..103d98c 100644
--- a/column.c
+++ b/column.c
@@ -2,6 +2,7 @@
 #include "column.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "run-command.h"
 #include "color.h"
 #include "utf8.h"
 
@@ -405,3 +406,71 @@ int parseopt_column_callback(const struct option *opt,
 	*mode |= COL_ENABLED | COL_ENABLED_SET;
 	return 0;
 }
+
+static int fd_out = -1;
+static struct child_process column_process;
+
+int run_column_filter(int colopts, const struct column_options *opts)
+{
+	const char *av[10];
+	int ret, ac = 0;
+	struct strbuf sb_colopt  = STRBUF_INIT;
+	struct strbuf sb_width   = STRBUF_INIT;
+	struct strbuf sb_padding = STRBUF_INIT;
+
+	if (fd_out != -1)
+		return -1;
+
+	av[ac++] = "column";
+	strbuf_addf(&sb_colopt, "--rawmode=%d", colopts);
+	av[ac++] = sb_colopt.buf;
+	if (opts->width) {
+		strbuf_addf(&sb_width, "--width=%d", opts->width);
+		av[ac++] = sb_width.buf;
+	}
+	if (opts->indent) {
+		av[ac++] = "--indent";
+		av[ac++] = opts->indent;
+	}
+	if (opts->padding) {
+		strbuf_addf(&sb_padding, "--padding=%d", opts->padding);
+		av[ac++] = sb_padding.buf;
+	}
+	av[ac] = NULL;
+
+	fflush(stdout);
+	memset(&column_process, 0, sizeof(column_process));
+	column_process.in = -1;
+	column_process.out = dup(1);
+	column_process.git_cmd = 1;
+	column_process.argv = av;
+
+	ret = start_command(&column_process);
+
+	strbuf_release(&sb_colopt);
+	strbuf_release(&sb_width);
+	strbuf_release(&sb_padding);
+
+	if (ret)
+		return -2;
+
+	fd_out = dup(1);
+	close(1);
+	dup2(column_process.in, 1);
+	close(column_process.in);
+	return 0;
+}
+
+int stop_column_filter(void)
+{
+	if (fd_out == -1)
+		return -1;
+
+	fflush(stdout);
+	close(1);
+	finish_command(&column_process);
+	dup2(fd_out, 1);
+	close(fd_out);
+	fd_out = -1;
+	return 0;
+}
diff --git a/column.h b/column.h
index 43528da..cb81c8a 100644
--- a/column.h
+++ b/column.h
@@ -34,4 +34,7 @@ struct option;
 extern int parseopt_column_callback(const struct option *opt,
 				    const char *arg, int unset);
 
+extern int run_column_filter(int colopts, const struct column_options *);
+extern int stop_column_filter();
+
 #endif
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v7 10/10] tag: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Ramsay Jones,
	Nguyễn Thái Ngọc Duy
In-Reply-To: <1330430331-19945-1-git-send-email-pclouds@gmail.com>

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config.txt  |    4 ++++
 Documentation/git-tag.txt |    9 +++++++++
 Makefile                  |    2 +-
 builtin/tag.c             |   26 +++++++++++++++++++++++---
 t/t7004-tag.sh            |   44 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index ebb210c..145336a 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -855,6 +855,10 @@ column.status::
 	Specify whether to output untracked files in `git status` in columns.
 	See `column.ui` for details.
 
+column.tag::
+	Specify whether to output tag listing in `git tag` in columns.
+	See `column.ui` for details.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 8d32b9a..e36a7c3 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 	<tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
+	[--column[=<options>] | --no-column] [<pattern>...]
 	[<pattern>...]
 'git tag' -v <tagname>...
 
@@ -84,6 +85,14 @@ OPTIONS
 	using fnmatch(3)).  Multiple patterns may be given; if any of
 	them matches, the tag is shown.
 
+--column[=<options>]::
+--no-column::
+	Display tag listing in columns. See configuration variable
+	column.tag for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable when listing tags without annotation lines.
+
 --contains <commit>::
 	Only list tags which contain the specified commit.
 
diff --git a/Makefile b/Makefile
index d9c5f00..65fc6b9 100644
--- a/Makefile
+++ b/Makefile
@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
-builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h
+builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
diff --git a/builtin/tag.c b/builtin/tag.c
index fe7e5e5..e99cbff 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -16,6 +16,7 @@
 #include "revision.h"
 #include "gpg-interface.h"
 #include "sha1-array.h"
+#include "column.h"
 
 static const char * const git_tag_usage[] = {
 	"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -33,6 +34,7 @@ struct tag_filter {
 };
 
 static struct sha1_array points_at;
+static unsigned int colopts;
 
 static int match_pattern(const char **patterns, const char *ref)
 {
@@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 	int status = git_gpg_config(var, value, cb);
 	if (status)
 		return status;
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "tag", &colopts);
 	return git_default_config(var, value, cb);
 }
 
@@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_STRING('u', "local-user", &keyid, "key-id",
 					"use another key to sign the tag"),
 		OPT__FORCE(&force, "replace the tag if exists"),
+		OPT_COLUMN(0, "column", &colopts, "show tag list in columns"),
 
 		OPT_GROUP("Tag listing options"),
 		{
@@ -495,9 +500,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
 	if (list + delete + verify > 1)
 		usage_with_options(git_tag_usage, options);
-	if (list)
-		return list_tags(argv, lines == -1 ? 0 : lines,
-				 with_commit);
+	if (list && lines != -1) {
+		if (explicitly_enable_column(colopts))
+			die(_("--column and -n are incompatible"));
+		colopts = 0;
+	}
+	if (list) {
+		int ret;
+		if (colopts & COL_ENABLED) {
+			struct column_options copts;
+			memset(&copts, 0, sizeof(copts));
+			copts.padding = 2;
+			run_column_filter(colopts, &copts);
+		}
+		ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
+		if (colopts & COL_ENABLED)
+			stop_column_filter();
+		return ret;
+	}
 	if (lines != -1)
 		die(_("-n option is only allowed with -l."));
 	if (with_commit)
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index f8c247a..5189446 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -263,6 +263,50 @@ test_expect_success 'tag -l can accept multiple patterns' '
 	test_cmp expect actual
 '
 
+test_expect_success 'listing tags in column' '
+	COLUMNS=40 git tag -l --column=row >actual &&
+	cat >expected <<\EOF &&
+a1      aa1     cba     t210    t211
+v0.2.1  v1.0    v1.0.1  v1.1.3
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'listing tags in column with column.*' '
+	git config column.tag row &&
+	git config column.ui dense &&
+	COLUMNS=40 git tag -l >actual &&
+	git config --unset column.ui &&
+	git config --unset column.tag &&
+	cat >expected <<\EOF &&
+a1      aa1   cba     t210    t211
+v0.2.1  v1.0  v1.0.1  v1.1.3
+EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'listing tag with -n --column should fail' '
+	test_must_fail git tag --column -n
+'
+
+test_expect_success 'listing tags -n in column with column.ui ignored' '
+	git config column.ui "row dense" &&
+	COLUMNS=40 git tag -l -n >actual &&
+	git config --unset column.ui &&
+	cat >expected <<\EOF &&
+a1              Foo
+aa1             Foo
+cba             Foo
+t210            Foo
+t211            Foo
+v0.2.1          Foo
+v1.0            Foo
+v1.0.1          Foo
+v1.1.3          Foo
+EOF
+	test_cmp expected actual
+'
+
 # creating and verifying lightweight tags:
 
 test_expect_success \
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* Re: [PATCH 2/3] http: try standard proxy env vars when http.proxy config option is not set
From: Thomas Rast @ 2012-02-28 12:19 UTC (permalink / raw)
  To: Nelson Benitez Leon; +Cc: git, peff, sam.vilain, sam
In-Reply-To: <4F4CCE8A.4010800@seap.minhap.es>

Nelson Benitez Leon <nelsonjesus.benitez@seap.minhap.es> writes:

> +	if (!curl_http_proxy) {
> +		const char *env_proxy;
> +		env_proxy = getenv("HTTP_PROXY");
> +		if (!env_proxy) {
> +			env_proxy = getenv("http_proxy");
> +		}
> +		if (env_proxy) {
> +			curl_http_proxy = xstrdup(env_proxy);
> +		}
> +	}

Admittedly I'm mostly clueless about curl, but while investigating the
NTLM login thing I noticed this bit in curl(1):

ENVIRONMENT
       The environment variables can be specified in lower case or upper
       case. The lower case version has precedence. http_proxy is an
       exception as it is only available in lower case.

Which raises the questions:

* Why is this needed?  Does git's use of libcurl ignore http_proxy?  [1]
  seems to indicate that libcurl respects <protocol>_proxy
  automatically.

* Why do you (need to?) support HTTP_PROXY when curl doesn't?


[1] http://curl.haxx.se/libcurl/c/libcurl-tutorial.html, "Environment Variables"

-- 
Thomas Rast
trast@{inf,student}.ethz.ch

^ permalink raw reply

* Re: [RFC PATCH 2a] pretty: detect missing \n\n in commit message
From: Nguyen Thai Ngoc Duy @ 2012-02-28 12:56 UTC (permalink / raw)
  To: Thomas Rast; +Cc: Jeff King, Junio C Hamano, git
In-Reply-To: <5234ba4babd28d9430750d227d629b4d4386b131.1330425111.git.trast@student.ethz.ch>

On Tue, Feb 28, 2012 at 5:37 PM, Thomas Rast <trast@student.ethz.ch> wrote:
> get_header()'s exit condition is finding the \n\n that separates the
> commit header from its message.  If such a double newline is not
> present, it segfaults.  Catch this case and die().

I'd prefer a gentler approach: if there's no \n\n, accept that this
commit has no body. I think I encountered such commits in the test
suite when I tried to convert commit encoding partially and made the
assumption that \n\n must exist.
-- 
Duy

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox