* Re: send-email SMTP/TLS Debugging
From: Zbigniew Jędrzejewski-Szmek @ 2012-02-25 17:55 UTC (permalink / raw)
To: David A. Greene; +Cc: git
In-Reply-To: <874nufv4ov.fsf@smith.obbligato.org>
On 02/25/2012 06:54 AM, David A. Greene wrote:
> Is there some way to turn on TLS authentication debugging using
> git-send-mail? I'm trying to send a patch but git (or the mail server,
> I suppose) keeps telling me I have "Incorrect authentication data."
> I've checked the settings in .git/config multiple times and they look
> correct. How can I debug this further?
Please try 'git send-email --smtp-debug=1 ...'
Zbyszek
^ permalink raw reply
* Re: [PATCH] grep -P: Fix matching ^ and $
From: Zbigniew Jędrzejewski-Szmek @ 2012-02-25 17:52 UTC (permalink / raw)
To: git; +Cc: Michał Kiedrowicz
In-Reply-To: <20120225103050.14f52a91@gmail.com>
On 02/25/2012 10:30 AM, Michał Kiedrowicz wrote:
> Michał Kiedrowicz<michal.kiedrowicz@gmail.com> wrote:
>
>> When `git-grep` is run with -P/--perl-regexp, it doesn't match ^ and $ at
>> the beginning/end of the line. This is because PCRE normally matches ^
>> and $ at the beginning/end of the whole text, not for each line, and git-grep
>> firstly passes a large chunk of text (possibly containing many lines) to
>> pcre_exec() before it splits the text into lines. This makes `git-grep -P`
>> behave differently from `git-grep -E` and also from `grep -P` and `pcregrep`:
Thanks! I can confirm that I now get the expected output.
Zbyszek
> Original report:
> http://permalink.gmane.org/gmane.comp.version-control.git/190830
^ permalink raw reply
* [PATCH] am: don't infloop for an empty input file
From: Jim Meyering @ 2012-02-25 17:34 UTC (permalink / raw)
To: git list
Today, "git am" surprised me.
I mistakenly ran it on an empty file and it went into an infinite loop.
: > e && git am e
To fix it, I made a failing bourne shell "read" break out
of the offending loop. Looking through git-am.sh for other
instances, I did find one, but didn't try to address it here.
action=again
while test "$action" = again
do
gettextln "Commit Body is:"
echo "--------------------------"
cat "$dotest/final-commit"
echo "--------------------------"
# TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
# in your translation. The program will only accept English
# input at this point.
gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
may infloop-> read reply
case "$reply" in
In that case (when someone hits ^D in response to that prompt?),
you may want to exit altogether.
In the test addition, I didn't try to handle potentially-inflooping code.
In coreutils tests, it's easy (since it includes the timeout program):
I would just prefix the command with something like "timeout 10", but
the timeout command is not universally available. And besides, the
infloop is supposed to be fixed, now.
Here's a patch for the infloop I triggered:
[Noticed with and tested against master v1.7.9.2-262-gba998d3,
but seems to apply also to maint. ]
-- >8 --
git-am.sh's check_patch_format function would attempt to preview
the patch to guess its format, but would go into an infinite loop
when the patch file happened to be empty. The solution: exit the
loop when "read" fails, not when the line var, "$l1" becomes empty.
Signed-off-by: Jim Meyering <meyering@redhat.com>
---
git-am.sh | 2 +-
t/t4150-am.sh | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/git-am.sh b/git-am.sh
index 64d8e2a..906f91f 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -202,7 +202,7 @@ check_patch_format () {
l1=
while test -z "$l1"
do
- read l1
+ read l1 || break
done
read l2
read l3
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index f1b60b8..6f77fff 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -505,4 +505,14 @@ test_expect_success 'am -q is quiet' '
! test -s output.out
'
+test_expect_success 'am empty-file does not infloop' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ touch empty-file &&
+ test_tick &&
+ { git am empty-file > actual 2>&1 && false || :; } &&
+ echo Patch format detection failed. >expected &&
+ test_cmp expected actual
+'
+
test_done
--
1.7.9.2.263.g9be8b7
^ permalink raw reply related
* sha-1 check in rev-list --verify-objects redundant?
From: Nguyen Thai Ngoc Duy @ 2012-02-25 17:15 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
Hi,
"git rev-list --verify-objects" calls parse_object() on all except
commits. This function in turn does check_sha1_signature() which
rehashes object content to verify the content matches its sha-1
signature. This is an expensive operation. I wonder if this is a
redundant check. --verify-objects is called to verify new packs.
index-pack is also (always?) called on new packs, and index-pack
hashes all object content, saves the hashes as signature in pack index
file. So they must match. Am I missing something here?
--
Duy
^ permalink raw reply
* Re: cvs2git multiple session for repository migration...
From: Michael Haggerty @ 2012-02-25 15:14 UTC (permalink / raw)
To: supadhyay; +Cc: git
In-Reply-To: <1330092524040-7314909.post@n2.nabble.com>
On 02/24/2012 03:08 PM, supadhyay wrote:
> I am migrating my CVS repository to GIt. As I have multiple repository I
> want to start multiple session for cvs2git,but it failed to start. I
> received cvs2svn.lock file error ? Is there any workaround to start multiple
> cvs2git session (which migrate the CVS directory) ??
>
> Thanks in advance...
Didn't I already mention that cvs2git questions should go to the cvs2svn
mailing list?
You can run cvs2git multiple times simultaneously; you just need to
ensure that each one has its own temporary directory. You can do this
either with the --tmpdir=PATH option or by starting the instances in
separate directories.
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply
* Re: git-subtree Ready #2
From: David A. Greene @ 2012-02-25 15:00 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Avery Pennarun, Jeff King, git
In-Reply-To: <7vy5rrfft2.fsf@alter.siamese.dyndns.org>
Junio C Hamano <gitster@pobox.com> writes:
>> I'm happy to do either (rebase or filter-branch). Just let me know.
>
> I would understand Avery's "should we filter-branch/rebase, or is it OK
> as-is?", but I do not understand what you mean by "either rebase or
> filter-branch is fine".
Sorry, got mixed up there. I'm not that familiar with filter-branch.
Now I understand you do both. :)
So have we decided to keep the history?
-Dave
^ permalink raw reply
* Re: [PATCH 2/2] index-pack: reduce memory usage when the pack has large blobs
From: Ian Kumlien @ 2012-02-25 13:17 UTC (permalink / raw)
To: Nguyen Thai Ngoc Duy; +Cc: git
In-Reply-To: <CACsJy8C-8dvXpNTU=JpdupSpS8OuqqTpGvDs6s1ASeKdk9d5Dg@mail.gmail.com>
On Sat, Feb 25, 2012 at 08:49:55AM +0700, Nguyen Thai Ngoc Duy wrote:
> 2012/2/24 Ian Kumlien <pomac@vapor.com>:
> > Writing objects: 100% (1425/1425), 56.06 MiB | 4.62 MiB/s, done.
> > Total 1425 (delta 790), reused 1425 (delta 790)
> > fatal: Out of memory, malloc failed (tried to allocate 3310214315 bytes)
> > fatal: Out of memory, malloc failed (tried to allocate 3310214315 bytes)
> > fatal: Out of memory, malloc failed (tried to allocate 3310214315 bytes)
> > fatal: Out of memory, malloc failed (tried to allocate 3310214315 bytes)
> > To ../test_data/
> > ! [remote rejected] master -> master (missing necessary objects)
> > ! [remote rejected] origin/HEAD -> origin/HEAD (missing necessary objects)
> > ! [remote rejected] origin/master -> origin/master (missing necessary objects)
> > error: failed to push some refs to '../test_data/'
> >
> > So there are additional code paths to look at... =(
>
> I can't say where that came from. Does this help? (Space damaged, may
> need manual application)
Everything has so far, since i'm using mainline to get the gzip fixes in
;)
Anyway, with:
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 264e3ae..533081d 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -183,7 +183,8 @@ static void show_object(struct object *obj,
struct rev_list_info *info = cb_data;
finish_object(obj, path, component, cb_data);
- if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
+ if (info->revs->verify_objects && !obj->parsed
+ && obj->type != OBJ_COMMIT && obj->type != OBJ_BLOB)
parse_object(obj->sha1);
show_object_with_name(stdout, obj, path, component);
}
---
I get:
../git/git push --mirror ../test_data/
Counting objects: 1425, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (617/617), done.
Writing objects: 100% (1425/1425), 56.06 MiB | 4.22 MiB/s, done.
Total 1425 (delta 790), reused 1425 (delta 790)
error: index-pack died of signal 11
error: unpack failed: index-pack abnormal exit
To ../test_data/
! [remote rejected] master -> master (n/a (unpacker error))
! [remote rejected] origin/HEAD -> origin/HEAD (n/a (unpacker error))
! [remote rejected] origin/master -> origin/master (n/a (unpacker error))
error: failed to push some refs to '../test_data/'
Which, to me, means that the installed git is now the problem - it can't verify
the pack and say that it's all ok ;)
I'll have to look some more at this on monday, or during the weekend if i get too curious =)
For now, thank $deity that $company i work for allows VPN from Linux machines! It looks
really good, i wonder if there is further tests i should do - any clues?
> --
> Duy
^ permalink raw reply related
* [PATCH v6 11/11] tag: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 | 9 +++++++++
Makefile | 2 +-
builtin/tag.c | 26 +++++++++++++++++++++++---
t/t7004-tag.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index ebb210c..145336a 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -855,6 +855,10 @@ column.status::
Specify whether to output untracked files in `git status` in columns.
See `column.ui` for details.
+column.tag::
+ Specify whether to output tag listing in `git tag` in columns.
+ See `column.ui` for details.
+
commit.status::
A boolean to enable/disable inclusion of status information in the
commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 8d32b9a..e36a7c3 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -13,6 +13,7 @@ SYNOPSIS
<tagname> [<commit> | <object>]
'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
+ [--column[=<options>] | --no-column] [<pattern>...]
[<pattern>...]
'git tag' -v <tagname>...
@@ -84,6 +85,14 @@ OPTIONS
using fnmatch(3)). Multiple patterns may be given; if any of
them matches, the tag is shown.
+--column[=<options>]::
+--no-column::
+ Display tag listing in columns. See configuration variable
+ column.tag for option syntax.`--column` and `--no-column`
+ without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable when listing tags without annotation lines.
+
--contains <commit>::
Only list tags which contain the specified commit.
diff --git a/Makefile b/Makefile
index d9c5f00..65fc6b9 100644
--- a/Makefile
+++ b/Makefile
@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
builtin/tar-tree.o archive-tar.o: tar.h
connect.o transport.o url.o http-backend.o: url.h
-builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h
+builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
diff --git a/builtin/tag.c b/builtin/tag.c
index fe7e5e5..e99cbff 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -16,6 +16,7 @@
#include "revision.h"
#include "gpg-interface.h"
#include "sha1-array.h"
+#include "column.h"
static const char * const git_tag_usage[] = {
"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -33,6 +34,7 @@ struct tag_filter {
};
static struct sha1_array points_at;
+static unsigned int colopts;
static int match_pattern(const char **patterns, const char *ref)
{
@@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb)
int status = git_gpg_config(var, value, cb);
if (status)
return status;
+ if (!prefixcmp(var, "column."))
+ return git_column_config(var, value, "tag", &colopts);
return git_default_config(var, value, cb);
}
@@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_STRING('u', "local-user", &keyid, "key-id",
"use another key to sign the tag"),
OPT__FORCE(&force, "replace the tag if exists"),
+ OPT_COLUMN(0, "column", &colopts, "show tag list in columns"),
OPT_GROUP("Tag listing options"),
{
@@ -495,9 +500,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (list + delete + verify > 1)
usage_with_options(git_tag_usage, options);
- if (list)
- return list_tags(argv, lines == -1 ? 0 : lines,
- with_commit);
+ if (list && lines != -1) {
+ if (explicitly_enable_column(colopts))
+ die(_("--column and -n are incompatible"));
+ colopts = 0;
+ }
+ if (list) {
+ int ret;
+ if (colopts & COL_ENABLED) {
+ struct column_options copts;
+ memset(&copts, 0, sizeof(copts));
+ copts.padding = 2;
+ run_column_filter(colopts, &copts);
+ }
+ ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
+ if (colopts & COL_ENABLED)
+ stop_column_filter();
+ return ret;
+ }
if (lines != -1)
die(_("-n option is only allowed with -l."));
if (with_commit)
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index f8c247a..5189446 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -263,6 +263,50 @@ test_expect_success 'tag -l can accept multiple patterns' '
test_cmp expect actual
'
+test_expect_success 'listing tags in column' '
+ COLUMNS=40 git tag -l --column=row >actual &&
+ cat >expected <<\EOF &&
+a1 aa1 cba t210 t211
+v0.2.1 v1.0 v1.0.1 v1.1.3
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'listing tags in column with column.*' '
+ git config column.tag row &&
+ git config column.ui dense &&
+ COLUMNS=40 git tag -l >actual &&
+ git config --unset column.ui &&
+ git config --unset column.tag &&
+ cat >expected <<\EOF &&
+a1 aa1 cba t210 t211
+v0.2.1 v1.0 v1.0.1 v1.1.3
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'listing tag with -n --column should fail' '
+ test_must_fail git tag --column -n
+'
+
+test_expect_success 'listing tags -n in column with column.ui ignored' '
+ git config column.ui "row dense" &&
+ COLUMNS=40 git tag -l -n >actual &&
+ git config --unset column.ui &&
+ cat >expected <<\EOF &&
+a1 Foo
+aa1 Foo
+cba Foo
+t210 Foo
+t211 Foo
+v0.2.1 Foo
+v1.0 Foo
+v1.0.1 Foo
+v1.1.3 Foo
+EOF
+ test_cmp expected actual
+'
+
# creating and verifying lightweight tags:
test_expect_success \
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 10/11] column: support piping stdout to external git-column process
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 94fd1a1..ecf2d14 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"
@@ -407,3 +408,71 @@ int parseopt_column_callback(const struct option *opt,
*mode |= COL_ENABLED | COL_ENABLED_SET;
return 0;
}
+
+static int fd_out = -1;
+static struct child_process column_process;
+
+int run_column_filter(int colopts, const struct column_options *opts)
+{
+ const char *av[10];
+ int ret, ac = 0;
+ struct strbuf sb_colopt = STRBUF_INIT;
+ struct strbuf sb_width = STRBUF_INIT;
+ struct strbuf sb_padding = STRBUF_INIT;
+
+ if (fd_out != -1)
+ return -1;
+
+ av[ac++] = "column";
+ strbuf_addf(&sb_colopt, "--rawmode=%d", colopts);
+ av[ac++] = sb_colopt.buf;
+ if (opts->width) {
+ strbuf_addf(&sb_width, "--width=%d", opts->width);
+ av[ac++] = sb_width.buf;
+ }
+ if (opts->indent) {
+ av[ac++] = "--indent";
+ av[ac++] = opts->indent;
+ }
+ if (opts->padding) {
+ strbuf_addf(&sb_padding, "--padding=%d", opts->padding);
+ av[ac++] = sb_padding.buf;
+ }
+ av[ac] = NULL;
+
+ fflush(stdout);
+ memset(&column_process, 0, sizeof(column_process));
+ column_process.in = -1;
+ column_process.out = dup(1);
+ column_process.git_cmd = 1;
+ column_process.argv = av;
+
+ ret = start_command(&column_process);
+
+ strbuf_release(&sb_colopt);
+ strbuf_release(&sb_width);
+ strbuf_release(&sb_padding);
+
+ if (ret)
+ return -2;
+
+ fd_out = dup(1);
+ close(1);
+ dup2(column_process.in, 1);
+ close(column_process.in);
+ return 0;
+}
+
+int stop_column_filter(void)
+{
+ if (fd_out == -1)
+ return -1;
+
+ fflush(stdout);
+ close(1);
+ finish_command(&column_process);
+ dup2(fd_out, 1);
+ close(fd_out);
+ fd_out = -1;
+ return 0;
+}
diff --git a/column.h b/column.h
index 43528da..cb81c8a 100644
--- a/column.h
+++ b/column.h
@@ -34,4 +34,7 @@ struct option;
extern int parseopt_column_callback(const struct option *opt,
const char *arg, int unset);
+extern int run_column_filter(int colopts, const struct column_options *);
+extern int stop_column_filter();
+
#endif
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 09/11] status: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 | 6 ++++++
t/t7508-status.sh | 24 ++++++++++++++++++++++++
wt-status.c | 28 ++++++++++++++++++++++++++--
wt-status.h | 1 +
7 files changed, 69 insertions(+), 3 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index c14db27..ebb210c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -851,6 +851,10 @@ column.branch::
Specify whether to output branch listing in `git branch` in columns.
See `column.ui` for details.
+column.status::
+ Specify whether to output untracked files in `git status` in columns.
+ See `column.ui` for details.
+
commit.status::
A boolean to enable/disable inclusion of status information in the
commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 3d51717..2f87207 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].
Terminate entries with NUL, instead of LF. This implies
the `--porcelain` output format if no other format is given.
+--column[=<options>]::
+--no-column::
+ Display untracked files in columns. See configuration variable
+ column.status for option syntax.`--column` and `--no-column`
+ without options are equivalent to 'always' and 'never'
+ respectively.
+
OUTPUT
------
diff --git a/Makefile b/Makefile
index 320d3f8..d9c5f00 100644
--- a/Makefile
+++ b/Makefile
@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
builtin/tar-tree.o archive-tar.o: tar.h
connect.o transport.o url.o http-backend.o: url.h
-builtin/branch.o column.o help.o pager.o: column.h
+builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
diff --git a/builtin/commit.c b/builtin/commit.c
index 3714582..b42b83f 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -27,6 +27,7 @@
#include "quote.h"
#include "submodule.h"
#include "gpg-interface.h"
+#include "column.h"
static const char * const builtin_commit_usage[] = {
"git commit [options] [--] <filepattern>...",
@@ -88,6 +89,7 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
static char *sign_commit;
+static unsigned int colopts;
/*
* The default commit message cleanup mode will remove the lines
@@ -1145,6 +1147,8 @@ static int git_status_config(const char *k, const char *v, void *cb)
{
struct wt_status *s = cb;
+ if (!prefixcmp(k, "column."))
+ return git_column_config(k, v, "status", &colopts);
if (!strcmp(k, "status.submodulesummary")) {
int is_bool;
s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
@@ -1210,6 +1214,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
"ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ OPT_COLUMN(0, "column", &colopts, "list untracked files in columns"),
OPT_END(),
};
@@ -1223,6 +1228,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_status_options,
builtin_status_usage, 0);
+ s.colopts = colopts;
if (null_termination && status_format == STATUS_FORMAT_LONG)
status_format = STATUS_FORMAT_PORCELAIN;
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index fc57b13..8f5cfac 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -59,6 +59,30 @@ test_expect_success 'status (1)' '
test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
'
+test_expect_success 'status --column' '
+ COLUMNS=50 git status --column="column dense" >output &&
+ cat >expect <<\EOF &&
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changes not staged for commit:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked dir2/untracked untracked
+# dir2/modified output
+EOF
+ test_cmp expect output
+'
+
cat >expect <<\EOF
# On branch master
# Changes to be committed:
diff --git a/wt-status.c b/wt-status.c
index 9ffc535..eee059e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -11,6 +11,7 @@
#include "remote.h"
#include "refs.h"
#include "submodule.h"
+#include "column.h"
static char default_wt_status_colors[][COLOR_MAXLEN] = {
GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -641,6 +642,8 @@ static void wt_status_print_other(struct wt_status *s,
{
int i;
struct strbuf buf = STRBUF_INIT;
+ static struct string_list output = STRING_LIST_INIT_DUP;
+ struct column_options copts;
if (!l->nr)
return;
@@ -649,12 +652,33 @@ static void wt_status_print_other(struct wt_status *s,
for (i = 0; i < l->nr; i++) {
struct string_list_item *it;
+ const char *path;
it = &(l->items[i]);
+ path = quote_path(it->string, strlen(it->string),
+ &buf, s->prefix);
+ if (s->colopts & COL_ENABLED) {
+ string_list_append(&output, path);
+ continue;
+ }
status_printf(s, color(WT_STATUS_HEADER, s), "\t");
status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
- "%s\n", quote_path(it->string, strlen(it->string),
- &buf, s->prefix));
+ "%s\n", path);
}
+
+ strbuf_release(&buf);
+ if ((s->colopts & COL_ENABLED) == 0)
+ return;
+
+ strbuf_addf(&buf, "%s#\t%s",
+ color(WT_STATUS_HEADER, s),
+ color(WT_STATUS_UNTRACKED, s));
+ memset(&copts, 0, sizeof(copts));
+ copts.padding = 1;
+ copts.indent = buf.buf;
+ if (want_color(s->use_color))
+ copts.nl = GIT_COLOR_RESET "\n";
+ print_columns(&output, s->colopts, &copts);
+ string_list_clear(&output, 0);
strbuf_release(&buf);
}
diff --git a/wt-status.h b/wt-status.h
index 682b4c8..6dd7207 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -56,6 +56,7 @@ struct wt_status {
enum untracked_status_type show_untracked_files;
const char *ignore_submodule_arg;
char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
+ int colopts;
/* These are computed during processing of the individual sections */
int commitable;
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 08/11] branch: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 | 32 +++++++++++++++--
t/t3200-branch.sh | 77 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 119 insertions(+), 5 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5216598..c14db27 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -847,6 +847,10 @@ column.ui::
+
This option defaults to 'never'.
+column.branch::
+ Specify whether to output branch listing in `git branch` in columns.
+ See `column.ui` for details.
+
commit.status::
A boolean to enable/disable inclusion of status information in the
commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0427e80..ba5cccb 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git branch' [--color[=<when>] | --no-color] [-r | -a]
[--list] [-v [--abbrev=<length> | --no-abbrev]]
+ [--column[=<options>] | --no-column]
[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -107,6 +108,14 @@ OPTIONS
default to color output.
Same as `--color=never`.
+--column[=<options>]::
+--no-column::
+ Display branch listing in columns. See configuration variable
+ column.branch for option syntax.`--column` and `--no-column`
+ without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable in non-verbose mode.
+
-r::
--remotes::
List or delete (if used with -d) the remote-tracking branches.
diff --git a/Makefile b/Makefile
index 0998f0d..320d3f8 100644
--- a/Makefile
+++ b/Makefile
@@ -2168,7 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
builtin/tar-tree.o archive-tar.o: tar.h
connect.o transport.o url.o http-backend.o: url.h
-column.o help.o pager.o: column.h
+builtin/branch.o column.o help.o pager.o: column.h
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
diff --git a/builtin/branch.c b/builtin/branch.c
index cb17bc3..611cc0e 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -15,6 +15,8 @@
#include "branch.h"
#include "diff.h"
#include "revision.h"
+#include "string-list.h"
+#include "column.h"
static const char * const builtin_branch_usage[] = {
"git branch [options] [-r | -a] [--merged | --no-merged]",
@@ -53,6 +55,9 @@ static enum merge_filter {
} merge_filter;
static unsigned char merge_filter_ref[20];
+static struct string_list output = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
+
static int parse_branch_color_slot(const char *var, int ofs)
{
if (!strcasecmp(var+ofs, "plain"))
@@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs)
static int git_branch_config(const char *var, const char *value, void *cb)
{
+ if (!prefixcmp(var, "column."))
+ return git_column_config(var, value, "branch", &colopts);
if (!strcmp(var, "color.branch")) {
branch_use_color = git_config_colorbool(var, value);
return 0;
@@ -474,7 +481,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
else if (verbose)
/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
add_verbose_info(&out, item, verbose, abbrev);
- printf("%s\n", out.buf);
+ if (colopts & COL_ENABLED) {
+ assert(!verbose && "--column and --verbose are incompatible");
+ string_list_append(&output, out.buf);
+ } else {
+ printf("%s\n", out.buf);
+ }
strbuf_release(&name);
strbuf_release(&out);
}
@@ -727,6 +739,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
opt_parse_merge_filter, (intptr_t) "HEAD",
},
+ OPT_COLUMN(0, "column", &colopts, "list branches in columns"),
OPT_END(),
};
@@ -749,6 +762,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
}
hashcpy(merge_filter_ref, head_sha1);
+
+ colopts |= COL_ANSI;
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
@@ -760,12 +775,21 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (abbrev == -1)
abbrev = DEFAULT_ABBREV;
+ if (verbose) {
+ if (explicitly_enable_column(colopts))
+ die(_("--column and --verbose are incompatible"));
+ colopts = 0;
+ }
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
- else if (list)
- return print_ref_list(kinds, detached, verbose, abbrev,
- with_commit, argv);
+ else if (list) {
+ int ret = print_ref_list(kinds, detached, verbose, abbrev,
+ with_commit, argv);
+ print_columns(&output, colopts, NULL);
+ string_list_clear(&output, 0);
+ return ret;
+ }
else if (edit_description) {
const char *branch_name;
struct strbuf branch_ref = STRBUF_INIT;
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index dd1aceb..c38592a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -160,6 +160,83 @@ test_expect_success 'git branch --list -d t should fail' '
test_path_is_missing .git/refs/heads/t
'
+test_expect_success 'git branch --column' '
+ COLUMNS=80 git branch --column=column >actual &&
+ cat >expected <<\EOF &&
+ a/b/c bam foo l * master n o/p r
+ abc bar j/k m/m master2 o/o q
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'git branch --column with an extremely long branch name' '
+ long=this/is/a/part/of/long/branch/name &&
+ long=z$long/$long/$long/$long &&
+ test_when_finished "git branch -d $long" &&
+ git branch $long &&
+ COLUMNS=80 git branch --column=column >actual &&
+ cat >expected <<EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+* master
+ master2
+ n
+ o/o
+ o/p
+ q
+ r
+ $long
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'git branch with column.*' '
+ git config column.ui column &&
+ git config column.branch "dense" &&
+ COLUMNS=80 git branch >actual &&
+ git config --unset column.branch &&
+ git config --unset column.ui &&
+ cat >expected <<\EOF &&
+ a/b/c bam foo l * master n o/p r
+ abc bar j/k m/m master2 o/o q
+EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'git branch --column -v should fail' '
+ test_must_fail git branch --column -v
+'
+
+test_expect_success 'git branch -v with column.ui ignored' '
+ git config column.ui column &&
+ COLUMNS=80 git branch -v | cut -c -10 | sed "s/ *$//" >actual &&
+ git config --unset column.ui &&
+ cat >expected <<\EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+* master
+ master2
+ n
+ o/o
+ o/p
+ q
+ r
+EOF
+ test_cmp expected actual
+'
+
mv .git/config .git/config-saved
test_expect_success 'git branch -m q q2 without config should succeed' '
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 06/11] column: add column.ui for default column output settings
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 | 21 +++++++++++++++++++++
column.c | 28 ++++++++++++++++++++++++++++
column.h | 2 ++
5 files changed, 82 insertions(+), 1 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index abeb82b..5216598 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -821,6 +821,32 @@ color.ui::
`never` if you prefer git commands not to use color unless enabled
explicitly with some other configuration or the `--color` option.
+column.ui::
+ Specify whether supported commands should output in columns.
+ This variable consists of a list of tokens separated by spaces
+ or commas:
++
+--
+`always`;;
+ always show in columns
+`never`;;
+ never show in columns
+`auto`;;
+ show in columns if the output is to the terminal
+`column`;;
+ fill columns before rows (default)
+`row`;;
+ fill rows before columns
+`dense`;;
+ make unequal size columns to utilize more space
+`nodense`;;
+ make equal size columns
+`color`;;
+ input contains ANSI escape sequence for coloring
+--
++
+ This option defaults to 'never'.
+
commit.status::
A boolean to enable/disable inclusion of status information in the
commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
index 508b85f..94fd7ac 100644
--- a/Documentation/git-column.txt
+++ b/Documentation/git-column.txt
@@ -8,7 +8,7 @@ git-column - Display data in columns
SYNOPSIS
--------
[verse]
-'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]
+'git column' [--command=<name>] [--[raw]mode=<mode>] [--width=<width>]
[--indent=<string>] [--nl=<string>] [--pading=<n>]
DESCRIPTION
@@ -17,6 +17,10 @@ This command formats its input into multiple columns.
OPTIONS
-------
+--command=<name>::
+ Look up layout mode using configuration variable column.<name> and
+ column.ui.
+
--mode=<mode>::
Specify layout mode. See configuration variable column.ui for option
syntax.
diff --git a/builtin/column.c b/builtin/column.c
index 3b0f74e..102d71b 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -11,12 +11,19 @@ static const char * const builtin_column_usage[] = {
};
static unsigned int colopts;
+static int column_config(const char *var, const char *value, void *cb)
+{
+ return git_column_config(var, value, cb, &colopts);
+}
+
int cmd_column(int argc, const char **argv, const char *prefix)
{
struct string_list list = STRING_LIST_INIT_DUP;
struct strbuf sb = STRBUF_INIT;
struct column_options copts;
+ const char *command = NULL, *real_command = NULL;
struct option options[] = {
+ OPT_STRING(0, "command", &real_command, "name", "lookup config vars"),
OPT_COLUMN(0, "mode", &colopts, "layout to use"),
OPT_INTEGER(0, "rawmode", &colopts, "layout to use"),
OPT_INTEGER(0, "width", &copts.width, "Maximum width"),
@@ -26,6 +33,15 @@ int cmd_column(int argc, const char **argv, const char *prefix)
OPT_END()
};
+ /* This one is special and must be the first one */
+ if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
+ int nonitok = 0;
+ setup_git_directory_gently(&nonitok);
+
+ command = argv[1] + 10;
+ git_config(column_config, (void *)command);
+ }
+
memset(&copts, 0, sizeof(copts));
copts.width = term_columns();
copts.padding = 1;
@@ -33,6 +49,11 @@ int cmd_column(int argc, const char **argv, const char *prefix)
if (argc)
usage_with_options(builtin_column_usage, options);
+ if (real_command || command) {
+ if (!real_command || !command || strcmp(real_command, command))
+ die(_("--command must be the first argument"));
+ }
+
while (!strbuf_getline(&sb, stdin, '\n'))
string_list_append(&list, sb.buf);
diff --git a/column.c b/column.c
index 3c77997..94fd1a1 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)
@@ -363,6 +364,33 @@ int git_config_column(unsigned int *mode, const char *value,
return 0;
}
+static int column_config(const char *var, const char *value,
+ const char *key, unsigned int *colopts)
+{
+ if (git_config_column(colopts, value, -1))
+ return error("invalid %s mode %s", key, value);
+ return 0;
+}
+
+int git_column_config(const char *var, const char *value,
+ const char *command, unsigned int *colopts)
+{
+ if (!strcmp(var, "column.ui"))
+ return column_config(var, value, "column.ui", colopts);
+
+ if (command) {
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
+ strbuf_addf(&sb, "column.%s", command);
+ if (!strcmp(var, sb.buf))
+ ret = column_config(var, value, sb.buf, colopts);
+ strbuf_release(&sb);
+ return ret;
+ }
+
+ return 0;
+}
+
int parseopt_column_callback(const struct option *opt,
const char *arg, int unset)
{
diff --git a/column.h b/column.h
index eb03c6c..43528da 100644
--- a/column.h
+++ b/column.h
@@ -27,6 +27,8 @@ extern void print_columns(const struct string_list *list,
struct column_options *opts);
extern int git_config_column(unsigned int *mode, const char *value,
int stdout_is_tty);
+extern int git_column_config(const char *var, const char *value,
+ const char *command, unsigned int *colopts);
struct option;
extern int parseopt_column_callback(const struct option *opt,
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 07/11] help: reuse print_columns() for help -a
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
help.c | 48 ++++++++++++++----------------------------------
1 files changed, 14 insertions(+), 34 deletions(-)
diff --git a/help.c b/help.c
index 14eefc9..d6d2e19 100644
--- a/help.c
+++ b/help.c
@@ -4,6 +4,8 @@
#include "levenshtein.h"
#include "help.h"
#include "common-cmds.h"
+#include "string-list.h"
+#include "column.h"
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
{
@@ -70,31 +72,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)
@@ -206,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');
}
@@ -230,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 v6 05/11] column: support columns with different widths
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 | 48 +++++++++++++++++++++++++++++++
3 files changed, 130 insertions(+), 0 deletions(-)
diff --git a/column.c b/column.c
index 6dca3b8..3c77997 100644
--- a/column.c
+++ b/column.c
@@ -19,6 +19,7 @@ struct column_data {
int rows, cols;
int *len; /* cell length */
+ int *width; /* index to the longest row in column */
};
/* return length of 's' in letters, ANSI escapes stripped */
@@ -63,6 +64,69 @@ static void layout(struct column_data *data, int *width)
data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
}
+static void compute_column_width(struct column_data *data)
+{
+ int i, x, y;
+ for (x = 0; x < data->cols; x++) {
+ data->width[x] = XY2LINEAR(data, x, 0);
+ for (y = 0; y < data->rows; y++) {
+ i = XY2LINEAR(data, x, y);
+ if (i >= data->list->nr)
+ continue;
+ if (data->len[data->width[x]] < data->len[i])
+ data->width[x] = i;
+ }
+ }
+}
+
+/*
+ * Shrink all columns by shortening them one row each time (and adding
+ * more columns along the way). Hopefully the longest cell will be
+ * moved to the next column, column is shrunk so we have more space
+ * for new columns. The process ends when the whole thing no longer
+ * fits in data->total_width.
+ */
+static void shrink_columns(struct column_data *data)
+{
+ int x, y, total_width, cols, rows;
+
+ data->width = xrealloc(data->width,
+ sizeof(*data->width) * data->cols);
+ for (x = 0; x < data->cols; x++) {
+ data->width[x] = 0;
+ for (y = 0; y < data->rows; y++) {
+ int len1 = data->len[data->width[x]];
+ int len2 = data->len[XY2LINEAR(data, x, y)];
+ if (len1 < len2)
+ data->width[x] = y;
+ }
+ }
+
+ while (data->rows > 1) {
+ rows = data->rows;
+ cols = data->cols;
+
+ data->rows--;
+ data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
+ if (data->cols != cols)
+ data->width = xrealloc(data->width, sizeof(*data->width) * data->cols);
+
+ compute_column_width(data);
+
+ total_width = strlen(data->indent);
+ for (x = 0; x < data->cols; x++) {
+ total_width += data->len[data->width[x]];
+ total_width += data->padding;
+ }
+ if (total_width > data->total_width) {
+ data->rows = rows;
+ data->cols = cols;
+ compute_column_width(data);
+ break;
+ }
+ }
+}
+
/* Display without layout when COL_ENABLED is not set */
static void display_plain(const struct string_list *list,
const char *indent, const char *nl)
@@ -82,7 +146,18 @@ static int display_cell(struct column_data *data, int initial_width,
i = XY2LINEAR(data, x, y);
if (i >= data->list->nr)
return -1;
+
len = data->len[i];
+ if (data->width && data->len[data->width[x]] < initial_width) {
+ /*
+ * empty_cell has initial_width chars, if real column
+ * is narrower, increase len a bit so we fill less
+ * space.
+ */
+ len += initial_width - data->len[data->width[x]];
+ len -= data->padding;
+ }
+
if (MODE(data->mode) == COL_MODE_COLUMN)
newline = i + data->rows >= data->list->nr;
else
@@ -119,6 +194,9 @@ static void display_table(const struct string_list *list,
layout(&data, &initial_width);
+ if (mode & COL_DENSE)
+ shrink_columns(&data);
+
empty_cell = xmalloc(initial_width + 1);
memset(empty_cell, ' ', initial_width);
empty_cell[initial_width] = '\0';
@@ -129,6 +207,7 @@ static void display_table(const struct string_list *list,
}
free(data.len);
+ free(data.width);
free(empty_cell);
}
@@ -227,6 +306,7 @@ static int parse_option(const char *arg, int len,
{ MODE, "column", COL_MODE_COLUMN },
{ MODE, "row", COL_MODE_ROW },
{ OPTION, "color", COL_ANSI },
+ { OPTION, "dense", COL_DENSE },
};
int i;
diff --git a/column.h b/column.h
index d9d27c6..eb03c6c 100644
--- a/column.h
+++ b/column.h
@@ -7,6 +7,8 @@
#define COL_ENABLED (1 << 4)
#define COL_ENABLED_SET (1 << 5) /* Has COL_ENABLED been set by config? */
#define COL_ANSI (1 << 6) /* Remove ANSI escapes from string length */
+#define COL_DENSE (1 << 7) /* Shrink columns when possible,
+ making space for more columns */
#define COL_PARSEOPT (1 << 8) /* --column is given */
#define explicitly_enable_column(c) \
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index cffb029..fe7a30e 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -71,6 +71,30 @@ EOF
test_cmp expected actual
'
+test_expect_success '20 columns, nodense' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column,nodense < lista > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, dense' '
+ cat >expected <<\EOF &&
+one five nine
+two six ten
+three seven eleven
+four eight
+EOF
+ git column --mode=column,dense < lista > actual &&
+ test_cmp expected actual
+'
+
test_expect_success '20 columns, padding 2' '
cat >expected <<\EOF &&
one seven
@@ -110,4 +134,28 @@ EOF
test_cmp expected actual
'
+test_expect_success '20 columns, row first, nodense' '
+ cat >expected <<\EOF &&
+one two
+three four
+five six
+seven eight
+nine ten
+eleven
+EOF
+ git column --mode=row,nodense <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, dense' '
+ cat >expected <<\EOF &&
+one two three
+four five six
+seven eight nine
+ten eleven
+EOF
+ git column --mode=row,dense <lista >actual &&
+ test_cmp expected actual
+'
+
test_done
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 04/11] column: add columnar layout
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 e62edf7..6dca3b8 100644
--- a/column.c
+++ b/column.c
@@ -2,8 +2,66 @@
#include "column.h"
#include "string-list.h"
#include "parse-options.h"
+#include "utf8.h"
#define MODE(mode) ((mode) & COL_MODE)
+#define XY2LINEAR(d, x, y) (MODE((d)->mode) == COL_MODE_COLUMN ? \
+ (x) * (d)->rows + (y) : \
+ (y) * (d)->cols + (x))
+
+struct column_data {
+ const struct string_list *list; /* list of all cells */
+ int mode; /* COL_MODE */
+ int total_width; /* terminal width */
+ int padding; /* cell padding */
+ const char *indent; /* left most column indentation */
+ const char *nl;
+
+ int rows, cols;
+ int *len; /* cell length */
+};
+
+/* return length of 's' in letters, ANSI escapes stripped */
+static int item_length(int mode, const char *s)
+{
+ int len, i = 0;
+ struct strbuf str = STRBUF_INIT;
+
+ if (!(mode & COL_ANSI))
+ return utf8_strwidth(s);
+
+ strbuf_addstr(&str, s);
+ while ((s = strstr(str.buf + i, "\033[")) != NULL) {
+ int len = strspn(s + 2, "0123456789;");
+ i = s - str.buf;
+ strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */
+ }
+ len = utf8_strwidth(str.buf);
+ strbuf_release(&str);
+ return len;
+}
+
+/*
+ * Calculate cell width, rows and cols for a table of equal cells, given
+ * table width and how many spaces between cells.
+ */
+static void layout(struct column_data *data, int *width)
+{
+ int i;
+
+ *width = 0;
+ for (i = 0; i < data->list->nr; i++)
+ if (*width < data->len[i])
+ *width = data->len[i];
+
+ *width += data->padding;
+
+ data->cols = (data->total_width - strlen(data->indent)) / *width;
+ if (data->cols == 0)
+ data->cols = 1;
+
+ data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
+}
/* Display without layout when COL_ENABLED is not set */
static void display_plain(const struct string_list *list,
@@ -15,6 +73,65 @@ static void display_plain(const struct string_list *list,
printf("%s%s%s", indent, list->items[i].string, nl);
}
+/* Print a cell to stdout with all necessary leading/traling space */
+static int display_cell(struct column_data *data, int initial_width,
+ const char *empty_cell, int x, int y)
+{
+ int i, len, newline;
+
+ i = XY2LINEAR(data, x, y);
+ if (i >= data->list->nr)
+ return -1;
+ len = data->len[i];
+ if (MODE(data->mode) == COL_MODE_COLUMN)
+ newline = i + data->rows >= data->list->nr;
+ else
+ newline = x == data->cols - 1 || i == data->list->nr - 1;
+
+ printf("%s%s%s",
+ x == 0 ? data->indent : "",
+ data->list->items[i].string,
+ newline ? data->nl : empty_cell + len);
+ return 0;
+}
+
+/* Display COL_MODE_COLUMN or COL_MODE_ROW */
+static void display_table(const struct string_list *list,
+ int mode, int total_width,
+ int padding, const char *indent,
+ const char *nl)
+{
+ struct column_data data;
+ int x, y, i, initial_width;
+ char *empty_cell;
+
+ memset(&data, 0, sizeof(data));
+ data.list = list;
+ data.mode = mode;
+ data.total_width = total_width;
+ data.padding = padding;
+ data.indent = indent;
+ data.nl = nl;
+
+ data.len = xmalloc(sizeof(*data.len) * list->nr);
+ for (i = 0; i < list->nr; i++)
+ data.len[i] = item_length(mode, list->items[i].string);
+
+ layout(&data, &initial_width);
+
+ empty_cell = xmalloc(initial_width + 1);
+ memset(empty_cell, ' ', initial_width);
+ empty_cell[initial_width] = '\0';
+ for (y = 0; y < data.rows; y++) {
+ for (x = 0; x < data.cols; x++)
+ if (display_cell(&data, initial_width, empty_cell, x, y))
+ break;
+ }
+
+ free(data.len);
+ free(empty_cell);
+}
+
void print_columns(const struct string_list *list, unsigned int mode,
struct column_options *opts)
{
@@ -36,7 +153,16 @@ void print_columns(const struct string_list *list, unsigned int mode,
display_plain(list, indent, nl);
return;
}
- die("BUG: invalid mode %d", MODE(mode));
+
+ switch (MODE(mode)) {
+ case COL_MODE_ROW:
+ case COL_MODE_COLUMN:
+ display_table(list, mode, width, padding, indent, nl);
+ break;
+
+ default:
+ die("BUG: invalid mode %d", MODE(mode));
+ }
}
struct colopt {
@@ -98,6 +224,9 @@ static int parse_option(const char *arg, int len,
{ ENABLE, "always", 1 },
{ ENABLE, "never", 0 },
{ ENABLE, "auto", -1 },
+ { MODE, "column", COL_MODE_COLUMN },
+ { MODE, "row", COL_MODE_ROW },
+ { OPTION, "color", COL_ANSI },
};
int i;
diff --git a/column.h b/column.h
index 67b1c4f..d9d27c6 100644
--- a/column.h
+++ b/column.h
@@ -2,8 +2,11 @@
#define COLUMN_H
#define COL_MODE 0x000F
+#define COL_MODE_COLUMN 0 /* Fill columns before rows */
+#define COL_MODE_ROW 1 /* Fill rows before columns */
#define COL_ENABLED (1 << 4)
#define COL_ENABLED_SET (1 << 5) /* Has COL_ENABLED been set by config? */
+#define COL_ANSI (1 << 6) /* Remove ANSI escapes from string length */
#define COL_PARSEOPT (1 << 8) /* --column is given */
#define explicitly_enable_column(c) \
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index b0b6d62..cffb029 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -24,4 +24,90 @@ test_expect_success 'never' '
test_cmp lista actual
'
+test_expect_success '80 columns' '
+ cat >expected <<\EOF &&
+one two three four five six seven eight nine ten eleven
+EOF
+ COLUMNS=80 git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'COLUMNS = 1' '
+ cat >expected <<\EOF &&
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+ COLUMNS=1 git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+ git column --mode=column --width=1 <lista >actual &&
+ test_cmp expected actual
+'
+
+COLUMNS=20
+export COLUMNS
+
+test_expect_success '20 columns' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, padding 2' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column --padding 2 <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, indented' '
+ cat >expected <<\EOF &&
+ one seven
+ two eight
+ three nine
+ four ten
+ five eleven
+ six
+EOF
+ git column --mode=column --indent=" " <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first' '
+ cat >expected <<\EOF &&
+one two
+three four
+five six
+seven eight
+nine ten
+eleven
+EOF
+ git column --mode=row <lista >actual &&
+ test_cmp expected actual
+'
+
test_done
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 03/11] Stop starting pager recursively
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 05584de..4dcb08d 100644
--- a/pager.c
+++ b/pager.c
@@ -73,7 +73,7 @@ void setup_pager(void)
{
const char *pager = git_pager(isatty(1));
- if (!pager)
+ if (!pager || pager_in_use())
return;
/*
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH v6 02/11] Add git-column and column mode parsing
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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.
Thanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
.gitignore | 1 +
Documentation/git-column.txt | 49 +++++++++++++++
Makefile | 1 +
builtin.h | 1 +
builtin/column.c | 41 +++++++++++++
column.c | 133 ++++++++++++++++++++++++++++++++++++++++++
column.h | 11 ++++
command-list.txt | 1 +
git.c | 1 +
parse-options.h | 2 +
t/t9002-column.sh | 27 +++++++++
11 files changed, 268 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 87fcc5f..2540264 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@
/git-cherry-pick
/git-clean
/git-clone
+/git-column
/git-commit
/git-commit-tree
/git-config
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
new file mode 100644
index 0000000..508b85f
--- /dev/null
+++ b/Documentation/git-column.txt
@@ -0,0 +1,49 @@
+git-column(1)
+=============
+
+NAME
+----
+git-column - Display data in columns
+
+SYNOPSIS
+--------
+[verse]
+'git column' [--mode=<mode> | --rawmode=<n>] [--width=<width>]
+ [--indent=<string>] [--nl=<string>] [--pading=<n>]
+
+DESCRIPTION
+-----------
+This command formats its input into multiple columns.
+
+OPTIONS
+-------
+--mode=<mode>::
+ Specify layout mode. See configuration variable column.ui for option
+ syntax.
+
+--rawmode=<n>::
+ Same as --mode but take mode encoded as a number. This is mainly used
+ by other commands that have already parsed layout mode.
+
+--width=<width>::
+ Specify the terminal width. By default 'git column' will detect the
+ terminal width, or fall back to 80 if it is unable to do so.
+
+--indent=<string>::
+ String to be printed at the beginning of each line.
+
+--nl=<N>::
+ String to be printed at the end of each line,
+ including newline character.
+
+--padding=<N>::
+ The number of spaces between columns. One space by default.
+
+
+Author
+------
+Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index 4e9501b..0998f0d 100644
--- a/Makefile
+++ b/Makefile
@@ -775,6 +775,7 @@ BUILTIN_OBJS += builtin/checkout-index.o
BUILTIN_OBJS += builtin/checkout.o
BUILTIN_OBJS += builtin/clean.o
BUILTIN_OBJS += builtin/clone.o
+BUILTIN_OBJS += builtin/column.o
BUILTIN_OBJS += builtin/commit-tree.o
BUILTIN_OBJS += builtin/commit.o
BUILTIN_OBJS += builtin/config.o
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..e62edf7 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,135 @@ 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;
+
+ for (i = 0; i < ARRAY_SIZE(opts); i++) {
+ int set = 1, arg_len = len, name_len;
+ const char *arg_str = arg;
+
+ if (opts[i].type == OPTION) {
+ if (arg_len > 2 && !strncmp(arg_str, "no", 2)) {
+ arg_str += 2;
+ arg_len -= 2;
+ set = 0;
+ } else {
+ set = 1;
+ }
+ }
+
+ name_len = strlen(opts[i].name);
+ if (arg_len != name_len ||
+ strncmp(arg_str, opts[i].name, name_len))
+ continue;
+
+ switch (opts[i].type) {
+ case ENABLE:
+ return set_enable_bit(mode, opts[i].value,
+ stdout_is_tty);
+ case MODE:
+ return set_mode(mode, opts[i].value);
+ case OPTION:
+ return set_option(mode, opts[i].value, set);
+ default:
+ die("BUG: Unknown option type %d", opts[i].type);
+ }
+ }
+
+ return error("unsupported style '%s'", arg);
+}
+
+int git_config_column(unsigned int *mode, const char *value,
+ int stdout_is_tty)
+{
+ const char *sep = " ,";
+
+ while (*value) {
+ int len = strcspn(value, sep);
+ if (len) {
+ if (parse_option(value, len, mode, stdout_is_tty))
+ return -1;
+
+ value += len;
+ }
+ value += strspn(value, sep);
+ }
+ return 0;
+}
+
+int parseopt_column_callback(const struct option *opt,
+ const char *arg, int unset)
+{
+ unsigned int *mode = opt->value;
+ *mode |= COL_PARSEOPT;
+ if (unset) {
+ *mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;
+ return 0;
+ }
+ if (arg)
+ return git_config_column(mode, arg, -1);
+
+ /* no arg, turn it on */
+ *mode |= COL_ENABLED | COL_ENABLED_SET;
+ return 0;
+}
diff --git a/column.h b/column.h
index 8e4fdaa..67b1c4f 100644
--- a/column.h
+++ b/column.h
@@ -3,6 +3,11 @@
#define COL_MODE 0x000F
#define COL_ENABLED (1 << 4)
+#define COL_ENABLED_SET (1 << 5) /* Has COL_ENABLED been set by config? */
+#define COL_PARSEOPT (1 << 8) /* --column is given */
+
+#define explicitly_enable_column(c) \
+ (((c) & (COL_PARSEOPT | COL_ENABLED)) == (COL_PARSEOPT | COL_ENABLED))
struct column_options {
int width;
@@ -15,5 +20,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 v6 01/11] column: add API to print items in columns
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1330170078-29353-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 | 2 ++
column.c | 39 +++++++++++++++++++++++++++++++++++++++
column.h | 19 +++++++++++++++++++
3 files changed, 60 insertions(+), 0 deletions(-)
create mode 100644 column.c
create mode 100644 column.h
diff --git a/Makefile b/Makefile
index a0de4e9..4e9501b 100644
--- a/Makefile
+++ b/Makefile
@@ -646,6 +646,7 @@ LIB_OBJS += bulk-checkin.o
LIB_OBJS += bundle.o
LIB_OBJS += cache-tree.o
LIB_OBJS += color.o
+LIB_OBJS += column.o
LIB_OBJS += combine-diff.o
LIB_OBJS += commit.o
LIB_OBJS += compat/obstack.o
@@ -2166,6 +2167,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
builtin/tar-tree.o archive-tar.o: tar.h
connect.o transport.o url.o http-backend.o: url.h
+column.o help.o pager.o: column.h
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
diff --git a/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
new file mode 100644
index 0000000..8e4fdaa
--- /dev/null
+++ b/column.h
@@ -0,0 +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 v6 00/11] Column display
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 11:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
Compared to v5 [1] this only has minor changes:
- drop term_columns() patch because master already has that
- style fixes
- incorporate Ramsay's fix in option parsing code
- make git_column_config() return either 0 or -1
[1] http://thread.gmane.org/gmane.comp.version-control.git/189758/focus=189875
Nguyễn Thái Ngọc Duy (11):
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 | 9 +
Makefile | 3 +
builtin.h | 1 +
builtin/branch.c | 32 +++-
builtin/column.c | 62 ++++++
builtin/commit.c | 6 +
builtin/tag.c | 26 ++-
column.c | 478 ++++++++++++++++++++++++++++++++++++++++++
column.h | 40 ++++
command-list.txt | 1 +
git.c | 1 +
help.c | 48 ++---
pager.c | 2 +-
parse-options.h | 2 +
t/t3200-branch.sh | 77 +++++++
t/t7004-tag.sh | 44 ++++
t/t7508-status.sh | 24 ++
t/t9002-column.sh | 161 ++++++++++++++
wt-status.c | 28 +++-
wt-status.h | 1 +
25 files changed, 1110 insertions(+), 44 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 4/4] index-pack: support multithreaded delta resolving
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 10:56 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1330167376-24859-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-index-pack.txt | 10 ++
Makefile | 2 +-
builtin/index-pack.c | 197 ++++++++++++++++++++++++++++++++------
4 files changed, 182 insertions(+), 31 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index e55dae1..965304b 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -445,6 +445,10 @@ for all users/operating systems, except on the largest projects.
You probably do not need to adjust this value.
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
++
+When gitlink:git-index-pack[1] runs on more than one thread, this
+value is applied per thread so the total amount of used memory depends
+on how many threads are used.
core.bigFileThreshold::
Files larger than this size are stored deflated, without
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 909687f..7e5f61b 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -74,6 +74,16 @@ OPTIONS
--strict::
Die, if the pack contains broken objects or links.
+--threads=<n>::
+ Specifies the number of threads to spawn when resolving
+ deltas. This requires that index-pack be compiled with
+ pthreads otherwise this option is ignored with a warning.
+ This is meant to reduce packing time on multiprocessor
+ machines. The required amount of memory for the delta search
+ window is however multiplied by the number of threads.
+ Specifying 0 will cause git to auto-detect the number of CPU's
+ and set the number of threads accordingly.
+
Note
----
diff --git a/Makefile b/Makefile
index 1fb1705..5fae875 100644
--- a/Makefile
+++ b/Makefile
@@ -2159,7 +2159,7 @@ builtin/branch.o builtin/checkout.o builtin/clone.o builtin/reset.o branch.o tra
builtin/bundle.o bundle.o transport.o: bundle.h
builtin/bisect--helper.o builtin/rev-list.o bisect.o: bisect.h
builtin/clone.o builtin/fetch-pack.o transport.o: fetch-pack.h
-builtin/grep.o builtin/pack-objects.o transport-helper.o thread-utils.o: thread-utils.h
+builtin/index-pack.o builtin/grep.o builtin/pack-objects.o transport-helper.o thread-utils.o: thread-utils.h
builtin/send-pack.o transport.o: send-pack.h
builtin/log.o builtin/shortlog.o: shortlog.h
builtin/prune.o builtin/reflog.o reachable.o: reachable.h
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index e1e858a..120195a 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -9,6 +9,7 @@
#include "progress.h"
#include "fsck.h"
#include "exec_cmd.h"
+#include "thread-utils.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -38,6 +39,15 @@ struct base_data {
int ofs_first, ofs_last;
};
+struct thread_local {
+#ifndef NO_PTHREADS
+ pthread_t thread;
+#endif
+ struct base_data *base_cache;
+ size_t base_cache_used;
+ int nr_resolved_deltas;
+};
+
/*
* Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
* to memcmp() only the first 20 bytes.
@@ -54,11 +64,12 @@ struct delta_entry {
static struct object_entry *objects;
static struct delta_entry *deltas;
-static struct base_data *base_cache;
-static size_t base_cache_used;
+static struct thread_local *thread_data;
static int nr_objects;
+static int nr_processed;
static int nr_deltas;
static int nr_resolved_deltas;
+static int nr_threads;
static int from_stdin;
static int strict;
@@ -76,6 +87,42 @@ static git_SHA_CTX input_ctx;
static uint32_t input_crc32;
static int input_fd, output_fd, pack_fd;
+#ifndef NO_PTHREADS
+
+static pthread_mutex_t read_mutex;
+#define read_lock() pthread_mutex_lock(&read_mutex)
+#define read_unlock() pthread_mutex_unlock(&read_mutex)
+
+static pthread_mutex_t work_mutex;
+#define work_lock() pthread_mutex_lock(&work_mutex)
+#define work_unlock() pthread_mutex_unlock(&work_mutex)
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_thread(void)
+{
+ init_recursive_mutex(&read_mutex);
+ pthread_mutex_init(&work_mutex, NULL);
+}
+
+static void cleanup_thread(void)
+{
+ pthread_mutex_destroy(&read_mutex);
+ pthread_mutex_destroy(&work_mutex);
+}
+
+#else
+
+#define read_lock()
+#define read_unlock()
+
+#define work_lock()
+#define work_unlock()
+
+#endif
+
+
static int mark_link(struct object *obj, int type, void *data)
{
if (!obj)
@@ -224,6 +271,18 @@ static NORETURN void bad_object(unsigned long offset, const char *format, ...)
die("pack has bad object at offset %lu: %s", offset, buf);
}
+static struct thread_local *get_thread_data()
+{
+#ifndef NO_PTHREADS
+ int i;
+ pthread_t self = pthread_self();
+ for (i = 1; i < nr_threads; i++)
+ if (self == thread_data[i].thread)
+ return &thread_data[i];
+#endif
+ return &thread_data[0];
+}
+
static struct base_data *alloc_base_data(void)
{
struct base_data *base = xmalloc(sizeof(struct base_data));
@@ -238,15 +297,16 @@ static void free_base_data(struct base_data *c)
if (c->data) {
free(c->data);
c->data = NULL;
- base_cache_used -= c->size;
+ get_thread_data()->base_cache_used -= c->size;
}
}
static void prune_base_data(struct base_data *retain)
{
struct base_data *b;
- for (b = base_cache;
- base_cache_used > delta_base_cache_limit && b;
+ struct thread_local *data = get_thread_data();
+ for (b = data->base_cache;
+ data->base_cache_used > delta_base_cache_limit && b;
b = b->child) {
if (b->data && b != retain)
free_base_data(b);
@@ -258,22 +318,23 @@ static void link_base_data(struct base_data *base, struct base_data *c)
if (base)
base->child = c;
else
- base_cache = c;
+ get_thread_data()->base_cache = c;
c->base = base;
c->child = NULL;
if (c->data)
- base_cache_used += c->size;
+ get_thread_data()->base_cache_used += c->size;
prune_base_data(c);
}
static void unlink_base_data(struct base_data *c)
{
- struct base_data *base = c->base;
+ struct base_data *base;
+ base = c->base;
if (base)
base->child = NULL;
else
- base_cache = NULL;
+ get_thread_data()->base_cache = NULL;
free_base_data(c);
}
@@ -503,19 +564,25 @@ static void sha1_object(const void *data, unsigned long size,
{
if (data)
hash_sha1_file(data, size, typename(type), sha1);
- if (data && (strict || !verify) && has_sha1_file(sha1)) {
- void *has_data;
- enum object_type has_type;
- unsigned long has_size;
- has_data = read_sha1_file(sha1, &has_type, &has_size);
- if (!has_data)
- die("cannot read existing object %s", sha1_to_hex(sha1));
- if (size != has_size || type != has_type ||
- memcmp(data, has_data, size) != 0)
- die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
- free(has_data);
+ if (data && (strict || !verify)) {
+ read_lock();
+ if (has_sha1_file(sha1)) {
+ void *has_data;
+ enum object_type has_type;
+ unsigned long has_size;
+ has_data = read_sha1_file(sha1, &has_type, &has_size);
+ read_unlock();
+ if (!has_data)
+ die("cannot read existing object %s", sha1_to_hex(sha1));
+ if (size != has_size || type != has_type ||
+ memcmp(data, has_data, size) != 0)
+ die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
+ free(has_data);
+ } else
+ read_unlock();
}
if (strict) {
+ read_lock();
if (type == OBJ_BLOB) {
struct blob *blob = lookup_blob(sha1);
if (blob)
@@ -549,6 +616,7 @@ static void sha1_object(const void *data, unsigned long size,
}
obj->flags |= FLAG_CHECKED;
}
+ read_unlock();
}
}
@@ -589,7 +657,7 @@ static void *get_base_data(struct base_data *c)
if (!delta_nr) {
c->data = get_data_from_pack(obj);
c->size = obj->size;
- base_cache_used += c->size;
+ get_thread_data()->base_cache_used += c->size;
prune_base_data(c);
}
for (; delta_nr > 0; delta_nr--) {
@@ -605,7 +673,7 @@ static void *get_base_data(struct base_data *c)
free(raw);
if (!c->data)
bad_object(obj->idx.offset, "failed to apply delta");
- base_cache_used += c->size;
+ get_thread_data()->base_cache_used += c->size;
prune_base_data(c);
}
free(delta);
@@ -633,7 +701,7 @@ static void resolve_delta(struct object_entry *delta_obj,
bad_object(delta_obj->idx.offset, "failed to apply delta");
sha1_object(result->data, result->size, delta_obj->real_type,
delta_obj->idx.sha1);
- nr_resolved_deltas++;
+ get_thread_data()->nr_resolved_deltas++;
}
static struct base_data *find_unresolved_deltas_1(struct base_data *base,
@@ -745,7 +813,30 @@ static void second_pass(struct object_entry *obj)
base_obj->obj = obj;
base_obj->data = NULL;
find_unresolved_deltas(base_obj);
- display_progress(progress, nr_resolved_deltas);
+}
+
+static void *threaded_second_pass(void *arg)
+{
+ struct thread_local *data = get_thread_data();
+ for (;;) {
+ int i;
+ work_lock();
+ nr_resolved_deltas += data->nr_resolved_deltas;
+ display_progress(progress, nr_resolved_deltas);
+ data->nr_resolved_deltas = 0;
+ while (nr_processed < nr_objects &&
+ is_delta_type(objects[nr_processed].type))
+ nr_processed++;
+ if (nr_processed == nr_objects) {
+ work_unlock();
+ break;
+ }
+ i = nr_processed++;
+ work_unlock();
+
+ second_pass(&objects[i]);
+ }
+ return NULL;
}
/* Parse all objects and return the pack content SHA1 hash */
@@ -804,14 +895,26 @@ static void parse_pack_objects(unsigned char *sha1)
if (verbose)
progress = start_progress("Resolving deltas", nr_deltas);
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *obj = &objects[i];
-
- if (is_delta_type(obj->type))
- continue;
- second_pass(obj);
+ nr_processed = 0;
+#ifndef NO_PTHREADS
+ if (nr_threads > 1) {
+ init_thread();
+ for (i = 1; i < nr_threads; i++) {
+ int ret = pthread_create(&thread_data[i].thread, NULL,
+ threaded_second_pass, NULL);
+ if (ret)
+ die("unable to create thread: %s", strerror(ret));
+ }
+ for (i = 1; i < nr_threads; i++) {
+ pthread_join(thread_data[i].thread, NULL);
+ thread_data[i].thread = 0;
+ }
+ cleanup_thread();
+ return;
}
+#endif
+ threaded_second_pass(thread_data);
}
static int write_compressed(struct sha1file *f, void *in, unsigned int size)
@@ -1017,6 +1120,17 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
die("bad pack.indexversion=%"PRIu32, opts->version);
return 0;
}
+ if (!strcmp(k, "pack.threads")) {
+ nr_threads = git_config_int(k, v);
+ if (nr_threads < 0)
+ die("invalid number of threads specified (%d)",
+ nr_threads);
+#ifdef NO_PTHREADS
+ if (nr_threads != 1)
+ warning("no threads support, ignoring %s", k);
+#endif
+ return 0;
+ }
return git_default_config(k, v, cb);
}
@@ -1175,6 +1289,16 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
keep_msg = "";
} else if (!prefixcmp(arg, "--keep=")) {
keep_msg = arg + 7;
+ } else if (!prefixcmp(arg, "--threads=")) {
+ char *end;
+ nr_threads = strtoul(arg+10, &end, 0);
+ if (!arg[10] || *end || nr_threads < 0)
+ usage(index_pack_usage);
+#ifdef NO_PTHREADS
+ if (nr_threads != 1)
+ warning("no threads support, "
+ "ignoring %s", arg);
+#endif
} else if (!prefixcmp(arg, "--pack_header=")) {
struct pack_header *hdr;
char *c;
@@ -1246,6 +1370,19 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (strict)
opts.flags |= WRITE_IDX_STRICT;
+#ifndef NO_PTHREADS
+ if (!nr_threads)
+ nr_threads = online_cpus();
+ /* reserve thread_data[0] for the main thread */
+ if (nr_threads > 1)
+ nr_threads++;
+#else
+ if (nr_threads != 1)
+ warning("no threads support, ignoring --threads");
+ nr_threads = 1;
+#endif
+ thread_data = xcalloc(nr_threads, sizeof(*thread_data));
+
curr_pack = open_pack_file(pack_name);
parse_pack_header();
objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH 2/4] index-pack: reduce memory usage when the pack has large blobs
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 10:56 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1330167376-24859-1-git-send-email-pclouds@gmail.com>
This command unpacks every non-delta objects in order to:
1. calculate sha-1
2. do byte-to-byte sha-1 collision test if we happen to have objects
with the same sha-1
3. validate object content in strict mode
All this requires the entire object to stay in memory, a bad news for
giant blobs. This patch lowers memory consumption by not saving the
object in memory whenever possible, calculating SHA-1 while unpacking
the object.
This patch assumes that the collision test is rarely needed. The
collision test will be done later in second pass if necessary, which
puts the entire object back to memory again (We could even do the
collision test without putting the entire object back in memory, by
comparing as we unpack it).
In strict mode, it always keeps non-blob objects in memory for
validation (blobs do not need data validation). "--strict --verify"
also keeps blobs in memory.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/index-pack.c | 74 +++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 61 insertions(+), 13 deletions(-)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index cee83b9..ab24dd8 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -277,30 +277,60 @@ static void unlink_base_data(struct base_data *c)
free_base_data(c);
}
-static void *unpack_entry_data(unsigned long offset, unsigned long size)
+static void *unpack_entry_data(unsigned long offset, unsigned long size,
+ enum object_type type, unsigned char *sha1)
{
+ static char fixed_buf[8192];
int status;
git_zstream stream;
- void *buf = xmalloc(size);
+ void *buf;
+ git_SHA_CTX c;
+
+ if (sha1) { /* do hash_sha1_file internally */
+ char hdr[32];
+ int hdrlen = sprintf(hdr, "%s %lu", typename(type), size)+1;
+ git_SHA1_Init(&c);
+ git_SHA1_Update(&c, hdr, hdrlen);
+
+ buf = fixed_buf;
+ } else {
+ buf = xmalloc(size);
+ }
memset(&stream, 0, sizeof(stream));
git_inflate_init(&stream);
stream.next_out = buf;
- stream.avail_out = size;
+ stream.avail_out = buf == fixed_buf ? sizeof(fixed_buf) : size;
do {
stream.next_in = fill(1);
stream.avail_in = input_len;
status = git_inflate(&stream, 0);
use(input_len - stream.avail_in);
+ if (sha1) {
+ git_SHA1_Update(&c, buf, stream.next_out - (unsigned char *)buf);
+ stream.next_out = buf;
+ stream.avail_out = sizeof(fixed_buf);
+ }
} while (status == Z_OK);
if (stream.total_out != size || status != Z_STREAM_END)
bad_object(offset, "inflate returned %d", status);
git_inflate_end(&stream);
+ if (sha1) {
+ git_SHA1_Final(sha1, &c);
+ buf = NULL;
+ }
return buf;
}
-static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
+static int is_delta_type(enum object_type type)
+{
+ return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
+}
+
+static void *unpack_raw_entry(struct object_entry *obj,
+ union delta_base *delta_base,
+ unsigned char *sha1)
{
unsigned char *p;
unsigned long size, c;
@@ -360,7 +390,17 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
}
obj->hdr_size = consumed_bytes - obj->idx.offset;
- data = unpack_entry_data(obj->idx.offset, obj->size);
+ /*
+ * --verify --strict: sha1_object() does all collision test
+ * --strict: sha1_object() does all except blobs,
+ * blobs tested in second pass
+ * --verify : no collision test
+ * : all in second pass
+ */
+ if (is_delta_type(obj->type) ||
+ (strict && (verify || obj->type != OBJ_BLOB)))
+ sha1 = NULL; /* save unpacked object */
+ data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, sha1);
obj->idx.crc32 = input_crc32;
return data;
}
@@ -461,8 +501,9 @@ static void find_delta_children(const union delta_base *base,
static void sha1_object(const void *data, unsigned long size,
enum object_type type, unsigned char *sha1)
{
- hash_sha1_file(data, size, typename(type), sha1);
- if ((strict || !verify) && has_sha1_file(sha1)) {
+ if (data)
+ hash_sha1_file(data, size, typename(type), sha1);
+ if (data && (strict || !verify) && has_sha1_file(sha1)) {
void *has_data;
enum object_type has_type;
unsigned long has_size;
@@ -511,11 +552,6 @@ static void sha1_object(const void *data, unsigned long size,
}
}
-static int is_delta_type(enum object_type type)
-{
- return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
-}
-
/*
* This function is part of find_unresolved_deltas(). There are two
* walkers going in the opposite ways.
@@ -702,7 +738,7 @@ static void parse_pack_objects(unsigned char *sha1)
nr_objects);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
- void *data = unpack_raw_entry(obj, &delta->base);
+ void *data = unpack_raw_entry(obj, &delta->base, obj->idx.sha1);
obj->real_type = obj->type;
if (is_delta_type(obj->type)) {
nr_deltas++;
@@ -744,6 +780,9 @@ static void parse_pack_objects(unsigned char *sha1)
* - if used as a base, uncompress the object and apply all deltas,
* recursively checking if the resulting object is used as a base
* for some more deltas.
+ * - if the same object exists in repository and we're not in strict
+ * mode, we skipped the sha-1 collision test in the first pass.
+ * Do it now.
*/
if (verbose)
progress = start_progress("Resolving deltas", nr_deltas);
@@ -753,6 +792,15 @@ static void parse_pack_objects(unsigned char *sha1)
if (is_delta_type(obj->type))
continue;
+
+ if (((!strict && !verify) ||
+ (strict && !verify && obj->type == OBJ_BLOB)) &&
+ has_sha1_file(obj->idx.sha1)) {
+ void *data = get_data_from_pack(obj);
+ sha1_object(data, obj->size, obj->type, obj->idx.sha1);
+ free(data);
+ }
+
base_obj->obj = obj;
base_obj->data = NULL;
find_unresolved_deltas(base_obj);
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH 3/4] index-pack: move second pass code into separate function
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 10:56 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1330167376-24859-1-git-send-email-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/index-pack.c | 54 +++++++++++++++++++++++++++----------------------
1 files changed, 30 insertions(+), 24 deletions(-)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index ab24dd8..e1e858a 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -719,6 +719,35 @@ static int compare_delta_entry(const void *a, const void *b)
objects[delta_b->obj_no].type);
}
+/*
+ * Second pass:
+ * - for all non-delta objects, look if it is used as a base for
+ * deltas;
+ * - if used as a base, uncompress the object and apply all deltas,
+ * recursively checking if the resulting object is used as a base
+ * for some more deltas.
+ * - if the same object exists in repository and we're not in strict
+ * mode, we skipped the sha-1 collision test in the first pass.
+ * Do it now.
+ */
+static void second_pass(struct object_entry *obj)
+{
+ struct base_data *base_obj = alloc_base_data();
+
+ if (((!strict && !verify) ||
+ (strict && !verify && obj->type == OBJ_BLOB)) &&
+ has_sha1_file(obj->idx.sha1)) {
+ void *data = get_data_from_pack(obj);
+ sha1_object(data, obj->size, obj->type, obj->idx.sha1);
+ free(data);
+ }
+
+ base_obj->obj = obj;
+ base_obj->data = NULL;
+ find_unresolved_deltas(base_obj);
+ display_progress(progress, nr_resolved_deltas);
+}
+
/* Parse all objects and return the pack content SHA1 hash */
static void parse_pack_objects(unsigned char *sha1)
{
@@ -773,38 +802,15 @@ static void parse_pack_objects(unsigned char *sha1)
qsort(deltas, nr_deltas, sizeof(struct delta_entry),
compare_delta_entry);
- /*
- * Second pass:
- * - for all non-delta objects, look if it is used as a base for
- * deltas;
- * - if used as a base, uncompress the object and apply all deltas,
- * recursively checking if the resulting object is used as a base
- * for some more deltas.
- * - if the same object exists in repository and we're not in strict
- * mode, we skipped the sha-1 collision test in the first pass.
- * Do it now.
- */
if (verbose)
progress = start_progress("Resolving deltas", nr_deltas);
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
- struct base_data *base_obj = alloc_base_data();
if (is_delta_type(obj->type))
continue;
- if (((!strict && !verify) ||
- (strict && !verify && obj->type == OBJ_BLOB)) &&
- has_sha1_file(obj->idx.sha1)) {
- void *data = get_data_from_pack(obj);
- sha1_object(data, obj->size, obj->type, obj->idx.sha1);
- free(data);
- }
-
- base_obj->obj = obj;
- base_obj->data = NULL;
- find_unresolved_deltas(base_obj);
- display_progress(progress, nr_resolved_deltas);
+ second_pass(obj);
}
}
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH 1/4] index-pack --verify: skip sha-1 collision test
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 10:56 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1330167376-24859-1-git-send-email-pclouds@gmail.com>
index-pack --verify (or verify-pack) is about verifying the pack
itself. SHA-1 collision test is about outside (probably malicious)
objects with the same SHA-1 entering current repo.
SHA-1 collision test is currently done unconditionally. Which means if
you verify an in-repo pack, all objects from the pack will be checked
against objects in repo, which are themselves.
Skip this test for --verify, unless --strict is also specified.
linux-2.6 $ ls -sh .git/objects/pack/pack-e7732c98a8d54840add294c3c562840f78764196.pack
401M .git/objects/pack/pack-e7732c98a8d54840add294c3c562840f78764196.pack
Without the patch (and with another patch to cut out second pass in
index-pack):
linux-2.6 $ time ~/w/git/old index-pack -v --verify .git/objects/pack/pack-e7732c98a8d54840add294c3c562840f78764196.pack
Indexing objects: 100% (1944656/1944656), done.
fatal: pack has 1617280 unresolved deltas
real 1m1.223s
user 0m55.028s
sys 0m0.828s
With the patch:
linux-2.6 $ time ~/w/git/git index-pack -v --verify .git/objects/pack/pack-e7732c98a8d54840add294c3c562840f78764196.pack
Indexing objects: 100% (1944656/1944656), done.
fatal: pack has 1617280 unresolved deltas
real 0m41.714s
user 0m40.994s
sys 0m0.550s
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/index-pack.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index dd1c5c9..cee83b9 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -62,6 +62,7 @@ static int nr_resolved_deltas;
static int from_stdin;
static int strict;
+static int verify;
static int verbose;
static struct progress *progress;
@@ -461,7 +462,7 @@ static void sha1_object(const void *data, unsigned long size,
enum object_type type, unsigned char *sha1)
{
hash_sha1_file(data, size, typename(type), sha1);
- if (has_sha1_file(sha1)) {
+ if ((strict || !verify) && has_sha1_file(sha1)) {
void *has_data;
enum object_type has_type;
unsigned long has_size;
@@ -1078,7 +1079,7 @@ static void show_pack_info(int stat_only)
int cmd_index_pack(int argc, const char **argv, const char *prefix)
{
- int i, fix_thin_pack = 0, verify = 0, stat_only = 0, stat = 0;
+ int i, fix_thin_pack = 0, stat_only = 0, stat = 0;
const char *curr_pack, *curr_index;
const char *index_name = NULL, *pack_name = NULL;
const char *keep_name = NULL, *keep_msg = NULL;
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH 0/4] index-pack improvements
From: Nguyễn Thái Ngọc Duy @ 2012-02-25 10:56 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
The first two patches are posted already, which help keep memory usage
down in the present of large blobs. The forth patch attempts to
resolve deltas in parallel.
$ time ./git index-pack --threads=1 --verify -v XXXX
Indexing objects: 100% (165375/165375), done.
Resolving deltas: 100% (124749/124749), done.
real 1m15.470s
user 1m14.899s
sys 0m0.552s
$ time ./git index-pack --threads=2 --verify -v XXXX
Indexing objects: 100% (165375/165375), done.
Resolving deltas: 100% (124749/124749), done.
real 0m41.339s
user 1m15.116s
sys 0m0.680s
$ time ./git index-pack --threads=3 --verify -v XXXX
Indexing objects: 100% (165375/165375), done.
Resolving deltas: 100% (124749/124749), done.
real 0m37.008s
user 1m35.742s
sys 0m0.803s
$ time ./git index-pack --verify -v XXXX # four core machine
Indexing objects: 100% (165375/165375), done.
Resolving deltas: 100% (124749/124749), done.
real 0m33.701s
user 1m51.316s
sys 0m0.768s
$ time ./git index-pack --threads=8 --verify -v XXXX
Indexing objects: 100% (165375/165375), done.
Resolving deltas: 100% (124749/124749), done.
real 0m33.638s
user 1m51.783s
sys 0m0.773s
So there's improvement from user persepective, but overhead is too
high (user time from 1m14 to 1m51). Making threaded_second_pass to
process 512 consecutive objects each iteration to reduce contention on
work_mutex does not help. Any ideas?
Nguyễn Thái Ngọc Duy (4):
index-pack --verify: skip sha-1 collision test
index-pack: reduce memory usage when the pack has large blobs
index-pack: move second pass code into separate function
index-pack: support multithreaded delta resolving
Documentation/config.txt | 4 +
Documentation/git-index-pack.txt | 10 ++
Makefile | 2 +-
builtin/index-pack.c | 298 +++++++++++++++++++++++++++++++-------
4 files changed, 260 insertions(+), 54 deletions(-)
--
1.7.8.36.g69ee2
^ permalink raw reply
* Re: [PATCH] grep -P: Fix matching ^ and $
From: Michał Kiedrowicz @ 2012-02-25 9:30 UTC (permalink / raw)
To: git; +Cc: Michał Kiedrowicz, Zbigniew Jędrzejewski-Szmek
In-Reply-To: <1330161868-7954-1-git-send-email-michal.kiedrowicz@gmail.com>
Michał Kiedrowicz <michal.kiedrowicz@gmail.com> wrote:
> When `git-grep` is run with -P/--perl-regexp, it doesn't match ^ and $ at
> the beginning/end of the line. This is because PCRE normally matches ^
> and $ at the beginning/end of the whole text, not for each line, and git-grep
> firstly passes a large chunk of text (possibly containing many lines) to
> pcre_exec() before it splits the text into lines. This makes `git-grep -P`
> behave differently from `git-grep -E` and also from `grep -P` and `pcregrep`:
>
> $ cat file
> a
> b
> $ git --no-pager grep --no-index -P '^ ' file
> $ git --no-pager grep --no-index -E '^ ' file
> file: b
> $ grep -c -P '^ ' file
> b
> $ pcregrep -c '^ ' file
> b
>
Original report:
http://permalink.gmane.org/gmane.comp.version-control.git/190830
> Reported-by: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
> Signed-off-by: Michał Kiedrowicz <michal.kiedrowicz@gmail.com>
> ---
> grep.c | 2 +-
> t/t7810-grep.sh | 23 +++++++++++++++++++++++
> 2 files changed, 24 insertions(+), 1 deletions(-)
>
> diff --git a/grep.c b/grep.c
> index 3821400..f492d26 100644
> --- a/grep.c
> +++ b/grep.c
> @@ -79,7 +79,7 @@ static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
> {
> const char *error;
> int erroffset;
> - int options = 0;
> + int options = PCRE_MULTILINE;
>
> if (opt->ignore_case)
> options |= PCRE_CASELESS;
> diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
> index 75f4716..dd6e6d5 100755
> --- a/t/t7810-grep.sh
> +++ b/t/t7810-grep.sh
> @@ -47,6 +47,13 @@ test_expect_success setup '
> echo vvv >t/v &&
> mkdir t/a &&
> echo vvv >t/a/v &&
> + {
> + echo "line without leading space1"
> + echo " line with leading space1"
> + echo " line with leading space2"
> + echo " line with leading space3"
> + echo "line without leading space2"
> + } >space &&
> git add . &&
> test_tick &&
> git commit -m initial
> @@ -893,4 +900,20 @@ test_expect_success 'mimic ack-grep --group' '
> test_cmp expected actual
> '
>
> +cat >expected <<EOF
> +space: line with leading space1
> +space: line with leading space2
> +space: line with leading space3
> +EOF
> +
> +test_expect_success 'grep -E "^ "' '
> + git grep -E "^ " space >actual &&
> + test_cmp expected actual
> +'
> +
> +test_expect_success "grep -P '^ '" '
> + git grep -P "^ " space >actual &&
> + test_cmp expected actual
> +'
> +
> test_done
^ 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