* Re: [PATCH 0/9] respect binary attribute in grep
From: Pete Wyckoff @ 2012-02-04 19:22 UTC (permalink / raw)
To: Jeff King
Cc: Junio C Hamano, Thomas Rast, Conrad Irwin, git,
Nguyen Thai Ngoc Duy, Dov Grobgeld
In-Reply-To: <20120202081747.GA10271@sigill.intra.peff.net>
I took a look at this series. It's nice. My worry was that the
extra open() of non-existent .gitattributes files in all the
directories would cause performance problems across networked
filesystems like NFS.
My usual (non-public) repository has order:
100k files
10k directories
and no files marked as binary. The grep string is such that it
is disk-bound, and not expected to match in any file (or binary):
"time ~/src/git/bin-wrappers/git grep unfindable-string".
With your change, there are 10k new open() calls looking for
.gitattributes in each directory, all of which return ENOENT.
This turns out to have an insignificant impact on performance due
to the much bigger time sink of stat()-ing all the files.
I think this happens to be true because the gitattributes lookups
run in parallel to all the file stat work, as the main thread
dispatches file work while doing its own gitattributes lookups.
It could be plausible that deep directory structures with few
grep-able files will suffer with this change. For example, many
big binary blobs in deep directory hierarchies, but also some
useful files here and there.
One could argue that with the use of .gitattributes to specify
which blobs should not be searched, this series makes this faster
by not having to to read the binary blobs at all. And I'd be
okay with that.
Just FYI that there may be a performance impact on certain
repositories.
-- Pete
^ permalink raw reply
* [PATCH/RFCv2 5/5] gitweb: Highlight matched part of shortened project description
From: Jakub Narebski @ 2012-02-04 18:56 UTC (permalink / raw)
To: git
In-Reply-To: <1328359648-29511-6-git-send-email-jnareb@gmail.com>
Previous commit make gitweb use esc_html_match_hl() to mark match in
the _whole_ description of a project when searching projects.
This commit makes gitweb highlight match in _shortened_ description,
based on match in whole description, using esc_html_match_hl_chopped()
subroutine.
If match is in removed (chopped) part, even partially, then trailing
"... " is highlighted.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This version removes code duplication from v1.
It is still marked as RFC, because I am not sure if it is right way
to highlight match in shortened string, or if we better use full string,
or full string if match is in chopped part.
gitweb/gitweb.perl | 40 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c650268..f71afe0 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1720,11 +1720,46 @@ sub esc_html_match_hl {
my ($str, $regexp) = @_;
return esc_html($str) unless defined $regexp;
+ return esc_html_match_hl_chopped($str, undef, $regexp);
+}
+
+
+# highlight match (if any) of shortened string, and escape HTML
+sub esc_html_match_hl_chopped {
+ my ($str, $chopped, $regexp) = @_;
+ return esc_html($chopped) unless defined $regexp;
+
my @matches;
while ($str =~ /$regexp/g) {
push @matches, [$-[0], $+[0]];
}
- return esc_html($str) unless @matches;
+ return esc_html(defined $chopped ? $chopped : $str) unless @matches;
+
+ # filter matches so that we mark chopped string, if it is present
+ if (defined $chopped) {
+ my $tail = "... "; # see chop_str
+ unless ($chopped =~ s/\Q$tail\E$//) {
+ $tail = '';
+ }
+ my $chop_len = length($chopped);
+ my $tail_len = length($tail);
+ my @filtered;
+
+ for my $m (@matches) {
+ if ($m->[0] > $chop_len) {
+ push @filtered, [ $chop_len, $chop_len + $tail_len ] if ($tail_len > 0);
+ last;
+ } elsif ($m->[1] > $chop_len) {
+ push @filtered, [ $m->[0], $chop_len + $tail_len ];
+ last;
+ }
+ push @filtered, $m;
+ }
+
+ # further operations are on chopped string
+ $str = $chopped . $tail;
+ @matches = @filtered;
+ }
my $out = '';
my $pos = 0;
@@ -5372,7 +5407,8 @@ sub git_project_list_rows {
"<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
-class => "list", -title => $pr->{'descr_long'}},
$search_regexp
- ? esc_html_match_hl($pr->{'descr_long'}, $search_regexp)
+ ? esc_html_match_hl_chopped($pr->{'descr_long'},
+ $pr->{'descr'}, $search_regexp)
: esc_html($pr->{'descr'})) .
"</td>\n" .
"<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
--
1.7.9
^ permalink raw reply related
* [bug] blame duplicates trailing ">" in mailmapped emails
From: Jeff King @ 2012-02-04 18:26 UTC (permalink / raw)
To: Felipe Contreras; +Cc: Junio C Hamano, Jonathan Nieder, git, SZEDER Gábor
In-Reply-To: <CAMP44s2QdJ4+qgg4fF5-DOWHx3Btd0pTivTT9s_E=qqxg16YLQ@mail.gmail.com>
On Sat, Feb 04, 2012 at 05:46:05PM +0200, Felipe Contreras wrote:
> In any case, the one to blame for the header corruption is git:
> [...]
> f2bb9f88 (<spearce@spearce.org>> 2006-11-27 03:41:01 -0500 952)
>
> Notice the mail is wrong.
Ugh. The fault lies in this code:
$ sed -n 1405,1414p builtin/blame.c
if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
/* Add a trailing '>' to email, since map_user returns plain emails
Note: It already has '<', since we replace from mail+1 */
mailpos = memchr(mail, '\0', mail_len);
if (mailpos && mailpos-mail < mail_len - 1) {
*mailpos = '>';
*(mailpos+1) = '\0';
}
}
}
But that comment is wrong. If there's no email mapping needed, map_user
will leave the "mail" buffer intact, in which case it will have the
trailing ">" (because we feed the address with enclosing angle
brackets). So while map_user tries to accept either "foo@example.com\0"
and "foo@example.com>", it is up to the contents of the mailmap whether
you get back something with the closing angle bracket or not. Which is a
pretty error-prone interface.
You can fix it with this:
diff --git a/builtin/blame.c b/builtin/blame.c
index 5a67c20..9b886fa 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1406,7 +1406,8 @@ static void get_ac_line(const char *inbuf, const char *what,
/* Add a trailing '>' to email, since map_user returns plain emails
Note: It already has '<', since we replace from mail+1 */
mailpos = memchr(mail, '\0', mail_len);
- if (mailpos && mailpos-mail < mail_len - 1) {
+ if (mailpos && mailpos-mail < mail_len - 1 &&
+ mailpos > mail && *(mailpos-1) != '>') {
*mailpos = '>';
*(mailpos+1) = '\0';
}
but it feels like the fix should go into map_user. I tried a few things,
like "git log -1 --format=%aE", and couldn't find other code paths with
this problem. So presumably they are all feeding email addresses without
the closing ">" (so one option is to just say "map_user needs to get
NUL-terminated strings).
-Peff
^ permalink raw reply related
* RE: Git performance results on a large repository
From: Joshua Redstone @ 2012-02-04 18:05 UTC (permalink / raw)
To: Nguyen Thai Ngoc Duy; +Cc: git@vger.kernel.org
In-Reply-To: <CACsJy8DkLCK0ZUKNz_PJazsxjsRbWVVZwjAU5n2EAjJfCYtpoQ@mail.gmail.com>
[ wanted to reply to my initial msg, but wasn't subscribed to the list at time of mailing, so replying to most recent post instead ]
Thanks to everyone for the questions and suggestions. I'll try to respond here. One high-level clarification - this synthetic repo for which I've reported perf times is representative of where we think we'll be in the future. Git is slow but marginally acceptable for today. We want to start planning now for any big changes we need to make going forward.
Evgeny Sazhin, Slinky and Ævar Arnfjörð Bjarmason suggested splitting up the repo into multiple, smaller repos. I indicated before that we have a lot of cross-dependencies. Our largest repo by number of files and commits is the repo containing the front-end server. It is a large code base in which the tight integration of various components results in many of the cross dependencies. We are working slowly to split things up more, for example into services, but that is a long-term process.
To get a bit abstract for a moment, in an ideal world, it doesn't seem like performance constraints of a source-control-system should dictate how we choose to structure our code. Ideally, seems like we should be able to choose to structure our code in whatever way we feel maximizes developer productivity. If development and code/release management seem easier in a single repo, than why not make an SCM that can handle it? This is one reason I've been leaning towards figuring out an SCM approach that can work well with our current practices rather than changing them as a prerequisite for good SCM performance.
Sam Vilain: Thanks for the pointer, i didn't realize that fast-import was bi-directional. I used it for generating the synthetic repo. Will look into using it the other way around. Though that still won't speed up things like git-blame, presumably? The sparse-checkout issue you mention is a good one. There is a good question of how to support quick checkout, branch switching, clone, push and so forth. I'll look into the approaches you suggest. One consideration is coming up with a high-leverage approach - i.e. not doing heavy dev work if we can avoid it. On the other hand, it would be nice if we (including the entire community :) ) improve git in areas that others that share similar issues benefit from as well.
Matt Graham: I don't have file stats at the moment. It's mostly code files, with a few larger data files here and there. We also don't do sparse checkouts, primarily because most people use git (whether on top of SVN or not), which doesn't support it.
Chris Lee: When I was building up the repo (e.g., doing lots of commits, before I started using fast-import), i noticed that flash was not much faster - stat'ing the whole repo takes a lot of kernel time, even with flash. My hunch is that we'd see similar issues with other operations, like git-blame.
Zeki Mokhtarzada: Dumping history I think would speed up operations for which we don't care about old history, like git-blame in which we only want to see recent modifications. We'd also need a good story for other kinds of operations. In my mental model of git scalability, I categorize git structures into three kinds: those for reasoning about history, those for the index and those for the working directory (yeah, I know these don't map precisely to actual on-disk things like the object store, including trees, etc.). One scaling approach we've been thinking of is to focus on each individually: develop a specialized thing to handle history commands efficiently (git-blame, git-log, git-diff, etc.), something to speed up or bypass the index, and something to make large changes to the working directly quickly.
Joey Hess: Separating the factors is a good suggestion. My hunch is that the various git operations test the performance issues in isolation. For example, git-status performance depends just on the number of files, not on the depth of history. On the other hand, my guess is that git-blame performance is more a function of the length of history rather than the number of files. Though, certainly with compression and indexing in pack files, I could imagine there being cross-effects between length of history and number of files. The git-status suggestion definitely helps when you know which directory you are concerned about. Often I'm lazy and stat the repo root so I trade-off slowness for being more sure I'm not missing anything.
@Joey, I think you're also touching on a good meta point which is that, there's probably no silver bullet here. If we want git to efficiently handle repos that are large across a number of dimensions (size, # commits, # files, etc.), there's multiple parts of git that would need enhancement of some form.
Nguyen Thai Ngoc Duy: At which point in the test flow should I insert git-update-index? I'm happy to try it out. Will compress index when I next get to a terminal. My guess is it'll compress a bunch. It's also conceivable that, if there were an external interface in git to attach other systems to efficiently report which files have changed (e.g., via file-system integration), it's possible that we could omit managing the index in many cases. I know that would be a big change, but the benefits are intriguing.
Cheers,
Josh
________________________________________
From: Nguyen Thai Ngoc Duy [pclouds@gmail.com]
Sent: Friday, February 03, 2012 10:53 PM
To: Joshua Redstone
Cc: git@vger.kernel.org
Subject: Re: Git performance results on a large repository
On Fri, Feb 3, 2012 at 9:20 PM, Joshua Redstone <joshua.redstone@fb.com> wrote:
> I timed a few common operations with both a warm OS file cache and a cold
> cache. i.e., I did a 'echo 3 | tee /proc/sys/vm/drop_caches' and then did
> the operation in question a few times (first timing is the cold timing,
> the next few are the warm timings). The following results are on a server
> with average hard drive (I.e., not flash) and > 10GB of ram.
>
> 'git status' : 39 minutes cold, and 24 seconds warm.
>
> 'git blame': 44 minutes cold, 11 minutes warm.
>
> 'git add' (appending a few chars to the end of a file and adding it): 7
> seconds cold and 5 seconds warm.
>
> 'git commit -m "foo bar3" --no-verify --untracked-files=no --quiet
> --no-status': 41 minutes cold, 20 seconds warm. I also hacked a version
> of git to remove the three or four places where 'git commit' stats every
> file in the repo, and this dropped the times to 30 minutes cold and 8
> seconds warm.
Have you tried "git update-index --assume-unchaged"? That should
reduce mass lstat() and hopefully improve the above numbers. The
interface is not exactly easy-to-use, but if it has significant gain,
then we can try to improve UI.
On the index size issue, ideally we should make minimum writes to
index instead of rewriting 191 MB index. An improvement we could do
now is to compress it, reduce disk footprint, thus disk I/O. If you
compress the index with gzip, how big is it?
--
Duy
^ permalink raw reply
* [PATCH] send-email: add extra safetly in address sanitazion
From: Felipe Contreras @ 2012-02-04 16:32 UTC (permalink / raw)
To: git
Cc: Felipe Contreras, Brandon Casey, Uwe Kleine-König,
Brian Gernhardt, Robin H. Johnson,
Ævar Arnfjörð Bjarmason
Currently bad addresses like 'Foo Bar <foo@bar.com>>' will just be sent
verbatim -- that's not good; we should either error out, or sanitize
them.
The following patch adds extra sanitazion so the following
transformations are performed:
'Foo Bar <foo@bar.com>' -> 'Foo Bar <foo@bar.com>'
'"Foo Bar" <foo@bar.com>' -> '"Foo Bar" <foo@bar.com>'
'foo@bar.com' -> 'foo@bar.com'
'<foo@bar.com>' -> 'foo@bar.com'
'Foo Bar' -> 'Foo Bar'
'Foo Bar <foo@bar.com>>' -> 'Foo Bar <foo@bar.com>'
'"Foo Bar" <foo@bar.com>>' -> '"Foo Bar" <foo@bar.com>'
'<foo@bar.com>>' -> 'foo@bar.com'
Basically, we try to check that the address is in the form of
"Name <email>", and if not, assume it's "email". According to commit
155197e[1], the "prhase" should not be empty, so if it is, remove the
<>. Extra characters after the first ">" are ignored.
[1] send-email: rfc822 forbids using <address@domain> without a non-empty "phrase"
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
git-send-email.perl | 14 ++++++++++----
1 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index ef30c55..19c600f 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -889,15 +889,21 @@ sub is_rfc2047_quoted {
# use the simplest quoting being able to handle the recipient
sub sanitize_address {
my ($recipient) = @_;
- my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
+ my ($recipient_name, $recipient_addr);
+
+ if ($recipient =~ /^(.*?)\s*<(.*?)>/) {
+ ($recipient_name, $recipient_addr) = ($1, $2);
+ } else {
+ $recipient_addr = $recipient;
+ }
if (not $recipient_name) {
- return $recipient;
+ return $recipient_addr;
}
# if recipient_name is already quoted, do nothing
if (is_rfc2047_quoted($recipient_name)) {
- return $recipient;
+ return "$recipient_name <$recipient_addr>";
}
# rfc2047 is needed if a non-ascii char is included
@@ -912,7 +918,7 @@ sub sanitize_address {
$recipient_name = qq["$recipient_name"];
}
- return "$recipient_name $recipient_addr";
+ return "$recipient_name <$recipient_addr>";
}
--
1.7.9
^ permalink raw reply related
* [PATCH v2] branch: borrow --sort and --count from for-each-ref
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 16:19 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326805907-19416-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Fix sorted refs being fnmatch()'d twice.
Documentation/git-branch.txt | 13 +++++++++
Makefile | 1 +
builtin/branch.c | 60 +++++++++++++++++++++++++++++++++--------
builtin/for-each-ref.c | 33 +++-------------------
builtin/for-each-ref.h | 32 ++++++++++++++++++++++
5 files changed, 99 insertions(+), 40 deletions(-)
create mode 100644 builtin/for-each-ref.h
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0427e80..b6f2826 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]]
+ [--count=<count>] [(--sort=<key>)...]
[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -192,6 +193,18 @@ start-point is either a local or remote-tracking branch.
The new name for an existing branch. The same restrictions as for
<branchname> apply.
+<count>::
+ By default the command shows all refs that match
+ `<pattern>`. This option makes it stop after showing
+ that many refs.
+
+<key>::
+ A field name to sort on. Prefix `-` to sort in descending
+ order of the value. When unspecified, `refname` is used. You
+ may use the --sort=<key> option multiple times, in which case
+ the last key becomes the primary key. See
+ linkgit:for-each-ref[1] for field name details.
+
Examples
--------
diff --git a/Makefile b/Makefile
index a782409..daf3e46 100644
--- a/Makefile
+++ b/Makefile
@@ -2108,6 +2108,7 @@ builtin/log.o builtin/shortlog.o: shortlog.h
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
+builtin/branch.o builtin/for-each-ref.o: builtin/for-each-ref.h
connect.o transport.o url.o http-backend.o: url.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 7095718..97bf96c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -15,6 +15,7 @@
#include "branch.h"
#include "diff.h"
#include "revision.h"
+#include "for-each-ref.h"
static const char * const builtin_branch_usage[] = {
"git branch [options] [-r | -a] [--merged | --no-merged]",
@@ -30,6 +31,9 @@ static const char * const builtin_branch_usage[] = {
static const char *head;
static unsigned char head_sha1[20];
+static struct ref_sort *sort = NULL, **sort_tail = &sort;
+static int maxcount;
+
static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
@@ -312,7 +316,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
if ((kind & ref_list->kinds) == 0)
return 0;
- if (!match_patterns(cb->pattern, refname))
+ if (cb->pattern && !match_patterns(cb->pattern, refname))
return 0;
commit = NULL;
@@ -510,10 +514,37 @@ static void show_detached(struct ref_list *ref_list)
}
}
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
+static int fetch_branches(struct ref_list *ref_list,
+ const char **pattern)
{
- int i;
struct append_ref_cb cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ref_list = ref_list;
+ if (sort) {
+ struct grab_ref_cbdata cbdata;
+ int i;
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.grab_pattern = pattern;
+ for_each_rawref(grab_single_ref, &cbdata);
+ sort_refs(sort, cbdata.grab_array, cbdata.grab_cnt);
+ for (i = 0; i < cbdata.grab_cnt; i++) {
+ struct refinfo *ri = cbdata.grab_array[i];
+ append_ref(ri->refname, ri->objectname, ri->flag, &cb);
+ }
+ } else {
+ cb.pattern = pattern;
+ for_each_rawref(append_ref, &cb);
+ qsort(ref_list->list, ref_list->index,
+ sizeof(struct ref_item), ref_cmp);
+ }
+ return cb.ret;
+}
+
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev,
+ struct commit_list *with_commit,
+ const char **pattern)
+{
+ int i, ret;
struct ref_list ref_list;
memset(&ref_list, 0, sizeof(ref_list));
@@ -523,10 +554,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
ref_list.with_commit = with_commit;
if (merge_filter != NO_FILTER)
init_revisions(&ref_list.revs, NULL);
- cb.ref_list = &ref_list;
- cb.pattern = pattern;
- cb.ret = 0;
- for_each_rawref(append_ref, &cb);
+ ret = fetch_branches(&ref_list, pattern);
if (merge_filter != NO_FILTER) {
struct commit *filter;
filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -539,13 +567,13 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
ref_list.maxwidth = calc_maxwidth(&ref_list);
}
- qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
-
detached = (detached && (kinds & REF_LOCAL_BRANCH));
if (detached && match_patterns(pattern, "HEAD"))
show_detached(&ref_list);
- for (i = 0; i < ref_list.index; i++) {
+ if (!maxcount)
+ maxcount = ref_list.index;
+ for (i = 0; i < maxcount; i++) {
int current = !detached &&
(ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
!strcmp(ref_list.list[i].name, head);
@@ -558,10 +586,10 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
free_ref_list(&ref_list);
- if (cb.ret)
+ if (ret)
error(_("some refs could not be read"));
- return cb.ret;
+ return ret;
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -702,6 +730,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
parse_opt_with_commit, (intptr_t) "HEAD",
},
OPT__ABBREV(&abbrev),
+ OPT_CALLBACK(0 , "sort", sort_tail, "key",
+ "field name to sort on", &opt_parse_sort),
+ OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
OPT_GROUP("Specific git-branch actions:"),
OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches",
@@ -752,6 +783,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
+ if (maxcount < 0) {
+ error("invalid --count argument: `%d'", maxcount);
+ usage_with_options(builtin_branch_usage, options);
+ }
+
if (!delete && !rename && !edit_description && argc == 0)
list = 1;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index b01d76a..7b25c54 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -9,6 +9,7 @@
#include "quote.h"
#include "parse-options.h"
#include "remote.h"
+#include "for-each-ref.h"
/* Quoting styles */
#define QUOTE_NONE 0
@@ -19,25 +20,6 @@
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
-struct atom_value {
- const char *s;
- unsigned long ul; /* used for sorting when not FIELD_STR */
-};
-
-struct ref_sort {
- struct ref_sort *next;
- int atom; /* index into used_atom array */
- unsigned reverse : 1;
-};
-
-struct refinfo {
- char *refname;
- unsigned char objectname[20];
- int flag;
- const char *symref;
- struct atom_value *value;
-};
-
static struct {
const char *name;
cmp_type cmp_type;
@@ -765,17 +747,12 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
*v = &ref->value[atom];
}
-struct grab_ref_cbdata {
- struct refinfo **grab_array;
- const char **grab_pattern;
- int grab_cnt;
-};
-
/*
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+int grab_single_ref(const char *refname, const unsigned char *sha1,
+ int flag, void *cb_data)
{
struct grab_ref_cbdata *cb = cb_data;
struct refinfo *ref;
@@ -858,7 +835,7 @@ static int compare_refs(const void *a_, const void *b_)
return 0;
}
-static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
+void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
{
ref_sort = sort;
qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
@@ -953,7 +930,7 @@ static struct ref_sort *default_sort(void)
return sort;
}
-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
+int opt_parse_sort(const struct option *opt, const char *arg, int unset)
{
struct ref_sort **sort_tail = opt->value;
struct ref_sort *s;
diff --git a/builtin/for-each-ref.h b/builtin/for-each-ref.h
new file mode 100644
index 0000000..8542d66
--- /dev/null
+++ b/builtin/for-each-ref.h
@@ -0,0 +1,32 @@
+struct atom_value {
+ const char *s;
+ unsigned long ul; /* used for sorting when not FIELD_STR */
+};
+
+struct ref_sort {
+ struct ref_sort *next;
+ int atom; /* index into used_atom array */
+ unsigned reverse : 1;
+};
+
+struct refinfo {
+ char *refname;
+ unsigned char objectname[20];
+ int flag;
+ const char *symref;
+ struct atom_value *value;
+};
+
+struct grab_ref_cbdata {
+ struct refinfo **grab_array;
+ const char **grab_pattern;
+ int grab_cnt;
+};
+
+extern int grab_single_ref(const char *refname,
+ const unsigned char *sha1,
+ int flag, void *cb_data);
+extern int opt_parse_sort(const struct option *opt,
+ const char *arg, int unset);
+extern void sort_refs(struct ref_sort *sort,
+ struct refinfo **refs, int num_refs);
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 07/12] column: add column.ui for default column output settings
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 16:12 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-8-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Really get rid of git_colopts (aka git_column_mode in v5). Sorry.
Documentation/config.txt | 26 ++++++++++++++++++++++++++
Documentation/git-column.txt | 6 +++++-
builtin/column.c | 21 +++++++++++++++++++++
column.c | 36 ++++++++++++++++++++++++++++++++++++
column.h | 2 ++
5 files changed, 90 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..351375f 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 fcff2fd..e3cec1c 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)
@@ -356,6 +357,41 @@ 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 (!strcmp(var, key)) {
+ int ret = git_config_column(colopts, value, -1);
+ if (ret)
+ die("invalid %s mode %s", key, value);
+ return 0;
+ }
+ return 1; /* go on */
+}
+
+int git_column_config(const char *var, const char *value,
+ const char *command, unsigned int *colopts)
+{
+ int ret;
+
+ *colopts &= ~COL_ENABLED_SET;
+ ret = column_config(var, value, "column.ui", colopts);
+ if (ret <= 0)
+ return ret;
+
+ if (command) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addf(&sb, "column.%s", command);
+ *colopts &= ~COL_ENABLED_SET;
+ ret = column_config(var, value, sb.buf, colopts);
+ strbuf_release(&sb);
+ if (ret <= 0)
+ return ret;
+ }
+
+ return 1; /* go on */
+}
+
int parseopt_column_callback(const struct option *opt,
const char *arg, int unset)
{
diff --git a/column.h b/column.h
index 2255851..3d2ed52 100644
--- a/column.h
+++ b/column.h
@@ -23,6 +23,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 v6 03/12] Add git-column and column mode parsing
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 16:11 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-4-git-send-email-pclouds@gmail.com>
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.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Turn colopts to unsigned int from the beginning.
.gitignore | 1 +
Documentation/git-column.txt | 49 ++++++++++++++++
Makefile | 1 +
builtin.h | 1 +
builtin/column.c | 41 ++++++++++++++
column.c | 125 ++++++++++++++++++++++++++++++++++++++++++
column.h | 7 ++
command-list.txt | 1 +
git.c | 1 +
parse-options.h | 2 +
t/t9002-column.sh | 27 +++++++++
11 files changed, 256 insertions(+), 0 deletions(-)
create mode 100644 Documentation/git-column.txt
create mode 100644 builtin/column.c
create mode 100755 t/t9002-column.sh
diff --git a/.gitignore b/.gitignore
index 3b7680e..039e5ef 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 5f0531b..92700ca 100644
--- a/Makefile
+++ b/Makefile
@@ -766,6 +766,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
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
index 742ae18..d20cf8f 100644
--- a/column.c
+++ b/column.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "column.h"
#include "string-list.h"
+#include "parse-options.h"
#define MODE(mode) ((mode) & COL_MODE)
@@ -37,3 +38,127 @@ void print_columns(const struct string_list *list, unsigned int mode,
}
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, set, name_len;
+
+ for (i = 0; i < ARRAY_SIZE(opts); i++) {
+ if (opts[i].type == OPTION) {
+ if (len > 2 && !strncmp(arg, "no", 2)) {
+ arg += 2;
+ len -= 2;
+ set = 0;
+ }
+ else
+ set = 1;
+ }
+
+ name_len = strlen(opts[i].name);
+ if (len != name_len ||
+ strncmp(arg, 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;
+ 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
index 8e4fdaa..f48fa81 100644
--- a/column.h
+++ b/column.h
@@ -3,6 +3,7 @@
#define COL_MODE 0x000F
#define COL_ENABLED (1 << 4)
+#define COL_ENABLED_SET (1 << 5) /* Has COL_ENABLED been set by config? */
struct column_options {
int width;
@@ -15,5 +16,11 @@ 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 v5 12/12] tag: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 4 ++++
Documentation/git-tag.txt | 11 ++++++++++-
Makefile | 2 +-
builtin/tag.c | 30 +++++++++++++++++++++++++++---
t/t7004-tag.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 86 insertions(+), 5 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 53ff5f6..5ead91e 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -12,7 +12,8 @@ SYNOPSIS
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
<tagname> [<commit> | <object>]
'git tag' -d <tagname>...
-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>...]
+'git tag' [-n[<num>]] -l [--contains <commit>]
+ [--column[=<options>] | --no-column] [<pattern>...]
'git tag' -v <tagname>...
DESCRIPTION
@@ -83,6 +84,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 b2644bc..eb7b6fc 100644
--- a/Makefile
+++ b/Makefile
@@ -2116,7 +2116,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 31f02e8..d01f918 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -15,6 +15,7 @@
#include "diff.h"
#include "revision.h"
#include "gpg-interface.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>]",
@@ -30,6 +31,8 @@ struct tag_filter {
struct commit_list *with_commit;
};
+static unsigned int colopts;
+
static int match_pattern(const char **patterns, const char *ref)
{
/* no pattern means match everything */
@@ -230,6 +233,9 @@ 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;
+ status = git_column_config(var, value, "tag", &colopts);
+ if (status <= 0)
+ return status;
return git_default_config(var, value, cb);
}
@@ -409,6 +415,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"),
{
@@ -424,6 +431,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
memset(&opt, 0, sizeof(opt));
+ colopts &= ~COL_ENABLED_SET;
argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
if (keyid) {
@@ -441,9 +449,25 @@ 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) {
+ /* only die when --column is given explicitly */
+ if ((colopts & (COL_ENABLED_SET | COL_ENABLED)) == (COL_ENABLED_SET | COL_ENABLED))
+ die(_("--column and -n are incompatible"));
+ colopts = 0;
+ }
+ if (list) {
+ int ret;
+ if (lines == -1 && 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 (lines == -1 && 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 e93ac73..579a298 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
* [PATCH v5 11/12] column: support piping stdout to external git-column process
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-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>
---
column.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
column.h | 3 ++
2 files changed, 72 insertions(+), 0 deletions(-)
diff --git a/column.c b/column.c
index c1233a7..4098f03 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"
@@ -409,3 +410,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()
+{
+ 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 3d2ed52..b9dec64 100644
--- a/column.h
+++ b/column.h
@@ -30,4 +30,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 v5 10/12] status: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 4 ++++
Documentation/git-status.txt | 7 +++++++
Makefile | 2 +-
builtin/commit.c | 8 ++++++++
t/t7508-status.sh | 24 ++++++++++++++++++++++++
wt-status.c | 28 ++++++++++++++++++++++++++--
wt-status.h | 1 +
7 files changed, 71 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 061f6e5..b2644bc 100644
--- a/Makefile
+++ b/Makefile
@@ -2116,7 +2116,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 eba1377..b73211c 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
@@ -1138,7 +1140,11 @@ static int parse_status_slot(const char *var, int offset)
static int git_status_config(const char *k, const char *v, void *cb)
{
struct wt_status *s = cb;
+ int status;
+ status = git_column_config(k, v, "status", &colopts);
+ if (status <= 0)
+ return status;
if (!strcmp(k, "status.submodulesummary")) {
int is_bool;
s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
@@ -1204,6 +1210,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(),
};
@@ -1217,6 +1224,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..63ce7c9 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 v5 09/12] branch: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 4 +++
Documentation/git-branch.txt | 9 +++++++
Makefile | 2 +-
builtin/branch.c | 38 +++++++++++++++++++++++++++++--
t/t3200-branch.sh | 50 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 99 insertions(+), 4 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 92700ca..061f6e5 100644
--- a/Makefile
+++ b/Makefile
@@ -2116,7 +2116,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 7095718..5014b9d 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,9 @@ static int parse_branch_color_slot(const char *var, int ofs)
static int git_branch_config(const char *var, const char *value, void *cb)
{
+ int status = git_column_config(var, value, "branch", &colopts);
+ if (status <= 0)
+ return status;
if (!strcmp(var, "color.branch")) {
branch_use_color = git_config_colorbool(var, value);
return 0;
@@ -474,7 +482,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 +740,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 +763,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
}
hashcpy(merge_filter_ref, head_sha1);
+
+ colopts = (colopts & ~COL_ENABLED_SET) | COL_ANSI;
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
@@ -761,11 +777,27 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (abbrev == -1)
abbrev = DEFAULT_ABBREV;
+ if (verbose) {
+ /* only die when --column is given explicitly */
+ if ((colopts & (COL_ENABLED_SET | COL_ENABLED)) == (COL_ENABLED_SET | COL_ENABLED))
+ die(_("--column and --verbose are incompatible"));
+ else
+ colopts = 0;
+ }
+
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
- else if (list)
- return print_ref_list(kinds, detached, verbose, abbrev,
+ else if (list) {
+ int ret;
+ if (verbose)
+ colopts = 0;
+
+ 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;
if (detached)
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ea82424..9728ee6 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -163,6 +163,56 @@ 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 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 >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 v5 08/12] help: reuse print_columns() for help -a
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
help.c | 47 +++++++++++++----------------------------------
1 files changed, 13 insertions(+), 34 deletions(-)
diff --git a/help.c b/help.c
index 672561b..d6d2e19 100644
--- a/help.c
+++ b/help.c
@@ -4,6 +4,7 @@
#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)
@@ -71,31 +72,18 @@ 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)
{
- 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 = " ";
+ print_columns(&list, COL_MODE_COLUMN | COL_ENABLED, &copts);
+ string_list_clear(&list, 0);
}
static int is_executable(const char *name)
@@ -207,22 +195,13 @@ void load_command_list(const char *prefix,
void list_commands(const char *title, 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);
putchar('\n');
}
@@ -231,7 +210,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);
putchar('\n');
}
}
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v5 07/12] column: add column.ui for default column output settings
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 26 ++++++++++++++++++++++++++
Documentation/git-column.txt | 6 +++++-
builtin/column.c | 23 ++++++++++++++++++++++-
column.c | 38 ++++++++++++++++++++++++++++++++++++++
column.h | 2 ++
5 files changed, 93 insertions(+), 2 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 c4a0431..351375f 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -9,14 +9,21 @@ static const char * const builtin_column_usage[] = {
"git column [options]",
NULL
};
-static int colopts;
+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 fcff2fd..c1233a7 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)
@@ -22,6 +23,8 @@ struct column_data {
int *width; /* index to the longest row in column */
};
+unsigned int git_column_mode;
+
/* return length of 's' in letters, ANSI escapes stripped */
static int item_length(int mode, const char *s)
{
@@ -356,6 +359,41 @@ 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 (!strcmp(var, key)) {
+ int ret = git_config_column(colopts, value, -1);
+ if (ret)
+ die("invalid %s mode %s", key, value);
+ return 0;
+ }
+ return 1; /* go on */
+}
+
+int git_column_config(const char *var, const char *value,
+ const char *command, unsigned int *colopts)
+{
+ int ret;
+
+ git_column_mode &= ~COL_ENABLED_SET;
+ ret = column_config(var, value, "column.ui", colopts);
+ if (ret <= 0)
+ return ret;
+
+ if (command) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addf(&sb, "column.%s", command);
+ git_column_mode &= ~COL_ENABLED_SET;
+ ret = column_config(var, value, sb.buf, colopts);
+ strbuf_release(&sb);
+ if (ret <= 0)
+ return ret;
+ }
+
+ return 1; /* go on */
+}
+
int parseopt_column_callback(const struct option *opt,
const char *arg, int unset)
{
diff --git a/column.h b/column.h
index 2255851..3d2ed52 100644
--- a/column.h
+++ b/column.h
@@ -23,6 +23,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 v5 06/12] column: support columns with different widths
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
column.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
column.h | 2 +
t/t9002-column.sh | 22 ++++++++++++++
3 files changed, 104 insertions(+), 0 deletions(-)
diff --git a/column.c b/column.c
index be4e07c..fcff2fd 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, set, name_len;
diff --git a/column.h b/column.h
index 52181d4..2255851 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 */
struct column_options {
int width;
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index cffb029..23d340e 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -71,6 +71,17 @@ EOF
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 +121,15 @@ EOF
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 v5 05/12] column: add columnar layout
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-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>
---
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 d20cf8f..be4e07c 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, set, name_len;
diff --git a/column.h b/column.h
index f48fa81..52181d4 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 */
struct column_options {
int width;
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 v5 04/12] Stop starting pager recursively
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-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>
---
| 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
--git a/pager.c b/pager.c
index 772a5a6..e4353dc 100644
--- a/pager.c
+++ b/pager.c
@@ -89,7 +89,7 @@ void setup_pager(void)
const char *pager = git_pager(isatty(1));
int width;
- if (!pager)
+ if (!pager || pager_in_use())
return;
setenv("GIT_PAGER_IN_USE", "true", 1);
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v5 03/12] Add git-column and column mode parsing
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
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.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
.gitignore | 1 +
Documentation/git-column.txt | 49 ++++++++++++++++
Makefile | 1 +
builtin.h | 1 +
builtin/column.c | 41 ++++++++++++++
column.c | 125 ++++++++++++++++++++++++++++++++++++++++++
column.h | 7 ++
command-list.txt | 1 +
git.c | 1 +
parse-options.h | 2 +
t/t9002-column.sh | 27 +++++++++
11 files changed, 256 insertions(+), 0 deletions(-)
create mode 100644 Documentation/git-column.txt
create mode 100644 builtin/column.c
create mode 100755 t/t9002-column.sh
diff --git a/.gitignore b/.gitignore
index 3b7680e..039e5ef 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 5f0531b..92700ca 100644
--- a/Makefile
+++ b/Makefile
@@ -766,6 +766,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
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..c4a0431
--- /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 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
index 742ae18..d20cf8f 100644
--- a/column.c
+++ b/column.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "column.h"
#include "string-list.h"
+#include "parse-options.h"
#define MODE(mode) ((mode) & COL_MODE)
@@ -37,3 +38,127 @@ void print_columns(const struct string_list *list, unsigned int mode,
}
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, set, name_len;
+
+ for (i = 0; i < ARRAY_SIZE(opts); i++) {
+ if (opts[i].type == OPTION) {
+ if (len > 2 && !strncmp(arg, "no", 2)) {
+ arg += 2;
+ len -= 2;
+ set = 0;
+ }
+ else
+ set = 1;
+ }
+
+ name_len = strlen(opts[i].name);
+ if (len != name_len ||
+ strncmp(arg, 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;
+ 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
index 8e4fdaa..f48fa81 100644
--- a/column.h
+++ b/column.h
@@ -3,6 +3,7 @@
#define COL_MODE 0x000F
#define COL_ENABLED (1 << 4)
+#define COL_ENABLED_SET (1 << 5) /* Has COL_ENABLED been set by config? */
struct column_options {
int width;
@@ -15,5 +16,11 @@ 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 v5 02/12] column: add API to print items in columns
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
Simple code that print line-by-line and wants to columnize can do
this:
struct string_list list;
string_list_append(&list, ...);
string_list_append(&list, ...);
...
print_columns(&list, ...);
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Makefile | 3 ++-
column.c | 39 +++++++++++++++++++++++++++++++++++++++
column.h | 13 +++++++++++++
3 files changed, 54 insertions(+), 1 deletions(-)
create mode 100644 column.c
diff --git a/Makefile b/Makefile
index cbbc699..5f0531b 100644
--- a/Makefile
+++ b/Makefile
@@ -637,6 +637,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
@@ -2114,7 +2115,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
-help.o pager.o: column.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/column.c b/column.c
new file mode 100644
index 0000000..742ae18
--- /dev/null
+++ b/column.c
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "column.h"
+#include "string-list.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));
+}
diff --git a/column.h b/column.h
index 55d8067..8e4fdaa 100644
--- a/column.h
+++ b/column.h
@@ -1,6 +1,19 @@
#ifndef COLUMN_H
#define COLUMN_H
+#define COL_MODE 0x000F
+#define COL_ENABLED (1 << 4)
+
+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);
#endif
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v5 00/12] Column display
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-1-git-send-email-pclouds@gmail.com>
- Turn column bitflag type to unsigned int
- Get rid of global var git_colopts
- Get rid of ill-designed print_cell()
- Refuse to run "git branch -v --column" and "git tag -l -n --column"
- Fix "git status --column --no-color" always prints ANSI reset sequence
- Drop OPT_INTEGER hexadecimal support (it's not essential, the column
helper functions can pass arguments are decimal)
- Add some more tests for column display
Nguyễn Thái Ngọc Duy (12):
Save terminal width before setting up pager
column: add API to print items in columns
Add git-column and column mode parsing
Stop starting pager recursively
column: add columnar layout
column: support columns with different widths
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 | 11 +-
Makefile | 3 +
builtin.h | 1 +
builtin/branch.c | 38 +++-
builtin/column.c | 62 ++++++
builtin/commit.c | 8 +
builtin/tag.c | 30 +++-
column.c | 480 ++++++++++++++++++++++++++++++++++++++++++
column.h | 36 +++
command-list.txt | 1 +
git.c | 1 +
help.c | 70 ++-----
pager.c | 37 +++-
parse-options.h | 2 +
t/t3200-branch.sh | 50 +++++
t/t7004-tag.sh | 44 ++++
t/t7508-status.sh | 24 ++
t/t9002-column.sh | 135 ++++++++++++
wt-status.c | 28 +++-
wt-status.h | 1 +
25 files changed, 1104 insertions(+), 66 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 v5 01/12] Save terminal width before setting up pager
From: Nguyễn Thái Ngọc Duy @ 2012-02-04 15:59 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1328371156-4009-1-git-send-email-pclouds@gmail.com>
term_columns() checks for terminal width via ioctl(2). After
redirecting, stdin is no longer terminal to get terminal width.
Check terminal width and save it before redirect stdin in
setup_pager() and let term_columns() reuse the value.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Makefile | 1 +
column.h | 6 ++++++
help.c | 23 +----------------------
| 35 +++++++++++++++++++++++++++++++++++
4 files changed, 43 insertions(+), 22 deletions(-)
create mode 100644 column.h
diff --git a/Makefile b/Makefile
index c457c34..cbbc699 100644
--- a/Makefile
+++ b/Makefile
@@ -2114,6 +2114,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
+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/column.h b/column.h
new file mode 100644
index 0000000..55d8067
--- /dev/null
+++ b/column.h
@@ -0,0 +1,6 @@
+#ifndef COLUMN_H
+#define COLUMN_H
+
+extern int term_columns(void);
+
+#endif
diff --git a/help.c b/help.c
index cbbe966..672561b 100644
--- a/help.c
+++ b/help.c
@@ -4,28 +4,7 @@
#include "levenshtein.h"
#include "help.h"
#include "common-cmds.h"
-
-/* most GUI terminals set COLUMNS (although some don't export it) */
-static int term_columns(void)
-{
- char *col_string = getenv("COLUMNS");
- int n_cols;
-
- if (col_string && (n_cols = atoi(col_string)) > 0)
- return n_cols;
-
-#ifdef TIOCGWINSZ
- {
- struct winsize ws;
- if (!ioctl(1, TIOCGWINSZ, &ws)) {
- if (ws.ws_col)
- return ws.ws_col;
- }
- }
-#endif
-
- return 80;
-}
+#include "column.h"
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
{
--git a/pager.c b/pager.c
index 975955b..772a5a6 100644
--- a/pager.c
+++ b/pager.c
@@ -6,6 +6,21 @@
#define DEFAULT_PAGER "less"
#endif
+static int spawned_pager;
+static int max_columns;
+
+static int retrieve_terminal_width(void)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if (ioctl(1, TIOCGWINSZ, &ws)) /* e.g., ENOSYS */
+ return 0;
+ return ws.ws_col;
+#else
+ return 0;
+#endif
+}
+
/*
* This is split up from the rest of git so that we can do
* something different on Windows.
@@ -72,12 +87,17 @@ const char *git_pager(int stdout_is_tty)
void setup_pager(void)
{
const char *pager = git_pager(isatty(1));
+ int width;
if (!pager)
return;
setenv("GIT_PAGER_IN_USE", "true", 1);
+ width = retrieve_terminal_width();
+ if (width)
+ max_columns = width;
+
/* spawn the pager */
pager_argv[0] = pager;
pager_process.use_shell = 1;
@@ -110,3 +130,18 @@ int pager_in_use(void)
env = getenv("GIT_PAGER_IN_USE");
return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
}
+
+int term_columns()
+{
+ char *col_string = getenv("COLUMNS");
+ int n_cols;
+
+ if (col_string && (n_cols = atoi(col_string)) > 0)
+ return n_cols;
+
+ if (spawned_pager && max_columns)
+ return max_columns;
+
+ n_cols = retrieve_terminal_width();
+ return n_cols ? n_cols : 80;
+}
--
1.7.8.36.g69ee2
^ permalink raw reply related
* Re: [PATCH v3 1/4] completion: be nicer with zsh
From: Felipe Contreras @ 2012-02-04 15:46 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jonathan Nieder, git, SZEDER Gábor
In-Reply-To: <7vehuboe5g.fsf@alter.siamese.dyndns.org>
On Fri, Feb 3, 2012 at 10:28 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
>> On Fri, Feb 3, 2012 at 2:17 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> ...
>>> Here is what I ended up in preparation for queuing the series. I still
>>> haven't seen any version of 4/4, but please check $gmane/189683 and see if
>>> that matches what you intended. Also I am assuming $gmane/189606 relayed
>>> by Jonathan is a squash between your 2 and 3 (which didn't reach me), so
>>> please advise if that does not match what you want to have.
>>
>> This is getting ridiculous, now I sent the patches directly to you, is
>> your pobox.com server also silently dropping them for no reason?
>
> Do not blame pobox.com; they have nothing to do with the corruption of
> your headers.
No, but they have everything to do with *silently* dropping it. Why
couldn't they _at least_ return an error saying that the headers are
wrong? Note that other servers didn't even complain, they processed
the mail happily.
In any case, the one to blame for the header corruption is git:
% git blame -e -L 947,+7 contrib/completion/git-completion.bash v1.7.9
eaa4e6ee (<jrnieder@gmail.com> 2009-11-17 18:49:10 -0600 947)
__git_compute_porcelain_commands ()
eaa4e6ee (<jrnieder@gmail.com> 2009-11-17 18:49:10 -0600 948) {
eaa4e6ee (<jrnieder@gmail.com> 2009-11-17 18:49:10 -0600 949)
__git_compute_all_commands
eaa4e6ee (<jrnieder@gmail.com> 2009-11-17 18:49:10 -0600 950)
: ${__git_porcelain_commands:=$(__git_list_porcelain_commands)}
eaa4e6ee (<jrnieder@gmail.com> 2009-11-17 18:49:10 -0600 951) }
f2bb9f88 (<spearce@spearce.org>> 2006-11-27 03:41:01 -0500 952)
c3898111 (<szeder@ira.uka.de> 2010-10-11 00:06:22 +0200 953)
__git_pretty_aliases ()
Notice the mail is wrong.
And then, 'git send-email' is happy to send such headers. I sent a
patch to the list to improve that situation, but looks like
sanitize_address() could be improved a lot.
You can blame it on 'git send-email', or 'git blame', or my cccmd
script[1], but as I discussed before, this is *precisely* the reason
why it would be nice to have an official cccmd script.
> You just caught me at the wrong moment when there were much more important
> messages on the list (more refers to the volume, not all of them are more
> important) and I was working on them (not limited to your issue) from top
> to bottom in the mailing list "newsgroup". I however wanted to get the
> zsh issue resolved sooner, and because you seemed to have been having so
> much trouble with your MUA (I only so 0/4 even for v4), I tried to help
> out by sending what I thought is already good, hoping that a message that
> only has to say "that looks good, thanks" would be easier to make it to
> the list.
I was not nor am I blaming you, I am just saying the situation is ridiculous.
And my MUA is 'git send-email'.
> People say "Oops, our mails crossed." and go on without making too much
> fuss about it. E-mail communications are asynchronous. Get used to it.
That's not the problem, but problem is these sanctimonious mail
servers that drop mails without warning. And sure, also the code that
sends malformed mail.
In any case, fixing 'git blame', and/or 'git send-email', and/or have
an official cccmd script, would solve the problem.
> I think your mail breakage, from looking at your mail header, is this:
>
> From: Felipe Contreras <felipe.contreras@gmail.com>
> To: git@vger.kernel.org
> Cc: Junio C Hamano <gitster@pobox.com>, SZEDER Gábor <szeder@ira.uka.de>, Jonathan Nieder <jrnieder@gmail.com>, Thomas Rast <trast@inf.ethz.ch>, Felipe Contreras <felipe.contreras@gmail.com>, "Shawn O. Pearce" <spearce@spearce.org>>
> Subject: [PATCH v4 1/4] completion: work around zsh option propagation bug
>
> Notice the excess '>' after the last address on Cc:?
Thanks for pointing that out :)
> It's not like this is your first serious submission to the list, so it is
> curious why only this time you have been having so much trouble. Perhaps
> you have changed your mail set-up lately?
Yes, people asked me to CC more relevant people, so I enabled by
cc-cmd script that I sent to the list long time ago, but didn't
receive any more replies, and I also asked if it wouldn't make sense
to have an official one [1] -- no replies.
Cheers.
[1] http://article.gmane.org/gmane.comp.version-control.git/189360
--
Felipe Contreras
^ permalink raw reply
* Re: [PATCH] send-email: add extra safetly in address sanitazion
From: Felipe Contreras @ 2012-02-04 15:26 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Felipe Contreras
In-Reply-To: <1328368255-10591-1-git-send-email-felipe.contreras@gmail.com>
On Sat, Feb 4, 2012 at 5:10 PM, Felipe Contreras
<felipe.contreras@gmail.com> wrote:
> Otherwise, 'git send-email' would be happy to do:
>
> % git send-email --to '<foo@bar.com>>'
>
> And use '<foo@bar.com>>' in the headers.
Er, actually that's not correct: '<foo@bar.com>>' will remain the
same, but 'Foo <foo@bar.com>>' will be sanitized.
--
Felipe Contreras
^ permalink raw reply
* [PATCH] send-email: add extra safetly in address sanitazion
From: Felipe Contreras @ 2012-02-04 15:10 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Felipe Contreras
Otherwise, 'git send-email' would be happy to do:
% git send-email --to '<foo@bar.com>>'
And use '<foo@bar.com>>' in the headers.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
git-send-email.perl | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index ef30c55..b8bf014 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -889,7 +889,7 @@ sub is_rfc2047_quoted {
# use the simplest quoting being able to handle the recipient
sub sanitize_address {
my ($recipient) = @_;
- my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
+ my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*?>)/);
if (not $recipient_name) {
return $recipient;
--
1.7.9
^ permalink raw reply related
* Re: [PATCH v4 4/4] completion: simplify __gitcomp*
From: SZEDER Gábor @ 2012-02-04 13:54 UTC (permalink / raw)
To: Junio C Hamano
Cc: Felipe Contreras, git, Jonathan Nieder, Thomas Rast,
Shawn O. Pearce
In-Reply-To: <7vty37oedr.fsf@alter.siamese.dyndns.org>
Hi,
On Fri, Feb 03, 2012 at 12:23:12PM -0800, Junio C Hamano wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
> > @@ -495,11 +495,7 @@ fi
> > # 4: A suffix to be appended to each possible completion word (optional).
> > __gitcomp ()
> > {
> > - local cur_="$cur"
> > -
> > - if [ $# -gt 2 ]; then
> > - cur_="$3"
> > - fi
> > + local cur_="${3:-$cur}"
> > case "$cur_" in
> > --*=)
> > COMPREPLY=()
>
> I think this rewrite is wrong, even though it may not make a difference to
> the current callers (I didn't check). Drop the colon from ${3:-...}.
>
> > @@ -524,18 +520,8 @@ __gitcomp ()
> > # appended.
> > __gitcomp_nl ()
> > {
> > - local s=$'\n' IFS=' '$'\t'$'\n'
> > - local cur_="$cur" suffix=" "
> > -
> > - if [ $# -gt 2 ]; then
> > - cur_="$3"
> > - if [ $# -gt 3 ]; then
> > - suffix="$4"
> > - fi
> > - fi
> > -
> > - IFS=$s
> > - COMPREPLY=($(compgen -P "${2-}" -S "$suffix" -W "$1" -- "$cur_"))
> > + local IFS=$'\n'
> > + COMPREPLY=($(compgen -P "${2-}" -S "${4- }" -W "$1" -- "${3:-$cur}"))
>
> So is this.
>
> Fixing the above two gives me what I've already sent in $gmane/189683,
> so...
>
Good point, I missed this when pointed out the similar issue with $4
earlier.
And it does make a difference, it breaks the completion of a single
word in multiple steps, e.g. git log --pretty=<TAB> master..<TAB>. In
such cases we pass "${cur##--pretty=}" and "${cur_#*..}" as third
argument to __gitcomp() and __gitcomp_nl(), which can be empty strings
when the user hits TAB right after the '=' and '..'. Replacing that
empty string with $cur is bad, because none of the possible completion
words (i.e. $1) will match it, and bash will fall back to filename
completion.
Without the colon, i.e. using "${3-$cur}", it works as expected.
Best,
Gábor
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox