Git development
 help / color / mirror / Atom feed
* [PATCH v4 06/13] column: add columnar layout
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-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 bd15bea..4681106 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);
+}
 
 struct string_list_item *add_cell_to_list(struct string_list *list,
 					  int mode,
@@ -30,6 +88,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, int mode,
 		   struct column_options *opts)
 {
@@ -51,7 +168,16 @@ void print_columns(const struct string_list *list, 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 {
@@ -113,6 +239,9 @@ static int parse_option(const char *arg, int len,
 		{ ENABLE, "always",  1 },
 		{ ENABLE, "never",   0 },
 		{ ENABLE, "auto",   -1 },
+		{ MODE,   "column", COL_MODE_COLUMN },
+		{ MODE,   "row",    COL_MODE_ROW },
+		{ OPTION, "color",  COL_ANSI },
 	};
 	int i, set, name_len;
 
diff --git a/column.h b/column.h
index 59f26dc..38976ea 100644
--- a/column.h
+++ b/column.h
@@ -2,8 +2,11 @@
 #define COLUMN_H
 
 #define COL_MODE          0x000F
+#define COL_MODE_COLUMN        0   /* Fill columns before rows */
+#define COL_MODE_ROW           1   /* Fill rows before columns */
 #define COL_ENABLED      (1 << 4)
 #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */
+#define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */
 
 struct column_options {
 	int width;
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index b0b6d62..cffb029 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -24,4 +24,90 @@ test_expect_success 'never' '
 	test_cmp lista actual
 '
 
+test_expect_success '80 columns' '
+	cat >expected <<\EOF &&
+one    two    three  four   five   six    seven  eight  nine   ten    eleven
+EOF
+	COLUMNS=80 git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'COLUMNS = 1' '
+	cat >expected <<\EOF &&
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+	COLUMNS=1 git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+	git column --mode=column --width=1 <lista >actual &&
+	test_cmp expected actual
+'
+
+COLUMNS=20
+export COLUMNS
+
+test_expect_success '20 columns' '
+	cat >expected <<\EOF &&
+one    seven
+two    eight
+three  nine
+four   ten
+five   eleven
+six
+EOF
+	git column --mode=column <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, padding 2' '
+	cat >expected <<\EOF &&
+one     seven
+two     eight
+three   nine
+four    ten
+five    eleven
+six
+EOF
+	git column --mode=column --padding 2 <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, indented' '
+	cat >expected <<\EOF &&
+  one    seven
+  two    eight
+  three  nine
+  four   ten
+  five   eleven
+  six
+EOF
+	git column --mode=column --indent="  " <lista >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first' '
+	cat >expected <<\EOF &&
+one    two
+three  four
+five   six
+seven  eight
+nine   ten
+eleven
+EOF
+	git column --mode=row <lista >actual &&
+	test_cmp expected actual
+'
+
 test_done
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v4 07/13] column: support columns with different widths
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-1-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 column.c          |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 column.h          |    2 +
 t/t9002-column.sh |   22 ++++++++++++++
 3 files changed, 104 insertions(+), 0 deletions(-)

diff --git a/column.c b/column.c
index 4681106..0f658d9 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;
+		}
+	}
+}
+
 struct string_list_item *add_cell_to_list(struct string_list *list,
 					  int mode,
 					  const char *string)
@@ -97,7 +161,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
@@ -134,6 +209,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';
@@ -144,6 +222,7 @@ static void display_table(const struct string_list *list,
 	}
 
 	free(data.len);
+	free(data.width);
 	free(empty_cell);
 }
 
@@ -242,6 +321,7 @@ static int parse_option(const char *arg, int len,
 		{ MODE,   "column", COL_MODE_COLUMN },
 		{ MODE,   "row",    COL_MODE_ROW },
 		{ OPTION, "color",  COL_ANSI },
+		{ OPTION, "dense",  COL_DENSE },
 	};
 	int i, set, name_len;
 
diff --git a/column.h b/column.h
index 38976ea..1912cb0 100644
--- a/column.h
+++ b/column.h
@@ -7,6 +7,8 @@
 #define COL_ENABLED      (1 << 4)
 #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set by config? */
 #define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */
+#define COL_DENSE        (1 << 7)  /* Shrink columns when possible,
+				      making space for more columns */
 
 struct column_options {
 	int width;
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index cffb029..23d340e 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -71,6 +71,17 @@ EOF
 	test_cmp expected actual
 '
 
+test_expect_success '20 columns, dense' '
+	cat >expected <<\EOF &&
+one   five  nine
+two   six   ten
+three seven eleven
+four  eight
+EOF
+	git column --mode=column,dense < lista > actual &&
+	test_cmp expected actual
+'
+
 test_expect_success '20 columns, padding 2' '
 	cat >expected <<\EOF &&
 one     seven
@@ -110,4 +121,15 @@ EOF
 	test_cmp expected actual
 '
 
+test_expect_success '20 columns, row first, dense' '
+	cat >expected <<\EOF &&
+one   two    three
+four  five   six
+seven eight  nine
+ten   eleven
+EOF
+	git column --mode=row,dense <lista >actual &&
+	test_cmp expected actual
+'
+
 test_done
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v4 09/13] help: reuse print_columns() for help -a
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-1-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 help.c |   47 +++++++++++++----------------------------------
 1 files changed, 13 insertions(+), 34 deletions(-)

diff --git a/help.c b/help.c
index 672561b..d6d2e19 100644
--- a/help.c
+++ b/help.c
@@ -4,6 +4,7 @@
 #include "levenshtein.h"
 #include "help.h"
 #include "common-cmds.h"
+#include "string-list.h"
 #include "column.h"
 
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
@@ -71,31 +72,18 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
 	cmds->cnt = cj;
 }
 
-static void pretty_print_string_list(struct cmdnames *cmds, int longest)
+static void pretty_print_string_list(struct cmdnames *cmds)
 {
-	int cols = 1, rows;
-	int space = longest + 1; /* min 1 SP between words */
-	int max_cols = term_columns() - 1; /* don't print *on* the edge */
-	int i, j;
-
-	if (space < max_cols)
-		cols = max_cols / space;
-	rows = DIV_ROUND_UP(cmds->cnt, cols);
-
-	for (i = 0; i < rows; i++) {
-		printf("  ");
+	struct string_list list = STRING_LIST_INIT_NODUP;
+	struct column_options copts;
+	int i;
 
-		for (j = 0; j < cols; j++) {
-			int n = j * rows + i;
-			int size = space;
-			if (n >= cmds->cnt)
-				break;
-			if (j == cols-1 || n + rows >= cmds->cnt)
-				size = 1;
-			printf("%-*s", size, cmds->names[n]->name);
-		}
-		putchar('\n');
-	}
+	for (i = 0; i < cmds->cnt; i++)
+		string_list_append(&list, cmds->names[i]->name);
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	print_columns(&list, COL_MODE_COLUMN | COL_ENABLED, &copts);
+	string_list_clear(&list, 0);
 }
 
 static int is_executable(const char *name)
@@ -207,22 +195,13 @@ void load_command_list(const char *prefix,
 void list_commands(const char *title, struct cmdnames *main_cmds,
 		   struct cmdnames *other_cmds)
 {
-	int i, longest = 0;
-
-	for (i = 0; i < main_cmds->cnt; i++)
-		if (longest < main_cmds->names[i]->len)
-			longest = main_cmds->names[i]->len;
-	for (i = 0; i < other_cmds->cnt; i++)
-		if (longest < other_cmds->names[i]->len)
-			longest = other_cmds->names[i]->len;
-
 	if (main_cmds->cnt) {
 		const char *exec_path = git_exec_path();
 		printf("available %s in '%s'\n", title, exec_path);
 		printf("----------------");
 		mput_char('-', strlen(title) + strlen(exec_path));
 		putchar('\n');
-		pretty_print_string_list(main_cmds, longest);
+		pretty_print_string_list(main_cmds);
 		putchar('\n');
 	}
 
@@ -231,7 +210,7 @@ void list_commands(const char *title, struct cmdnames *main_cmds,
 		printf("---------------------------------------");
 		mput_char('-', strlen(title));
 		putchar('\n');
-		pretty_print_string_list(other_cmds, longest);
+		pretty_print_string_list(other_cmds);
 		putchar('\n');
 	}
 }
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v4 08/13] column: add column.ui for default column output settings
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-1-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config.txt     |   26 ++++++++++++++++++++++++++
 Documentation/git-column.txt |    6 +++++-
 builtin/column.c             |   23 +++++++++++++++++++++++
 column.c                     |   36 ++++++++++++++++++++++++++++++++++++
 column.h                     |    4 ++++
 5 files changed, 94 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 c4a0431..c4e1fe4 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -11,12 +11,19 @@ static const char * const builtin_column_usage[] = {
 };
 static 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,17 @@ 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);
+		if (!colopts)
+			colopts = git_colopts;
+	}
+
 	memset(&copts, 0, sizeof(copts));
 	copts.width = term_columns();
 	copts.padding = 1;
@@ -33,6 +51,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 0f658d9..671ee5d 100644
--- a/column.c
+++ b/column.c
@@ -2,6 +2,7 @@
 #include "column.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "color.h"
 #include "utf8.h"
 
 #define MODE(mode) ((mode) & COL_MODE)
@@ -22,6 +23,8 @@ struct column_data {
 	int *width;			/* index to the longest row in column */
 };
 
+int git_colopts;
+
 /* return length of 's' in letters, ANSI escapes stripped */
 static int item_length(int mode, const char *s)
 {
@@ -371,6 +374,39 @@ int git_config_column(int *mode, const char *value,
 	return 0;
 }
 
+static int column_config(const char *var, const char *value,
+			 const char *key, int *colopts)
+{
+	if (!strcmp(var, key)) {
+		int ret = git_config_column(colopts, value, -1);
+		if (ret)
+			die("invalid %s mode %s", key, value);
+		return 0;
+	}
+	return 1;		/* go on */
+}
+
+int git_column_config(const char *var, const char *value,
+		      const char *command, int *colopts)
+{
+	int ret;
+
+	ret = column_config(var, value, "column.ui", &git_colopts);
+	if (ret <= 0)
+		return ret;
+
+	if (command) {
+		struct strbuf sb = STRBUF_INIT;
+		strbuf_addf(&sb, "column.%s", command);
+		ret = column_config(var, value, sb.buf, colopts);
+		strbuf_release(&sb);
+		if (ret <= 0)
+			return ret;
+	}
+
+	return 1;		/* go on */
+}
+
 int parseopt_column_callback(const struct option *opt,
 			     const char *arg, int unset)
 {
diff --git a/column.h b/column.h
index 1912cb0..afdafc4 100644
--- a/column.h
+++ b/column.h
@@ -17,6 +17,8 @@ struct column_options {
 	const char *nl;
 };
 
+extern int git_colopts;
+
 extern int term_columns(void);
 extern struct string_list_item *add_cell_to_list(struct string_list *list,
 						 int mode,
@@ -26,6 +28,8 @@ extern void print_cell(struct string_list *list, int mode, const char *string);
 extern void print_columns(const struct string_list *list,
 			  int mode, struct column_options *opts);
 extern int git_config_column(int *mode, const char *value, int stdout_is_tty);
+extern int git_column_config(const char *var, const char *value,
+			     const char *command, int *colopts);
 
 struct option;
 extern int parseopt_column_callback(const struct option *opt,
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v4 10/13] branch: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-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             |   26 +++++++++++++++++++++++---
 4 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5216598..c14db27 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -847,6 +847,10 @@ column.ui::
 +
 	This option defaults to 'never'.
 
+column.branch::
+	Specify whether to output branch listing in `git branch` in columns.
+	See `column.ui` for details.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 0427e80..ba5cccb 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
 	[--list] [-v [--abbrev=<length> | --no-abbrev]]
+	[--column[=<options>] | --no-column]
 	[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -107,6 +108,14 @@ OPTIONS
 	default to color output.
 	Same as `--color=never`.
 
+--column[=<options>]::
+--no-column::
+	Display branch listing in columns. See configuration variable
+	column.branch for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable in non-verbose mode.
+
 -r::
 --remotes::
 	List or delete (if used with -d) the remote-tracking branches.
diff --git a/Makefile b/Makefile
index 92700ca..061f6e5 100644
--- a/Makefile
+++ b/Makefile
@@ -2116,7 +2116,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
-column.o help.o pager.o: column.h
+builtin/branch.o column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
diff --git a/builtin/branch.c b/builtin/branch.c
index 7095718..d78690a 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 int colopts;
+
 static int parse_branch_color_slot(const char *var, int ofs)
 {
 	if (!strcasecmp(var+ofs, "plain"))
@@ -70,6 +75,9 @@ static int parse_branch_color_slot(const char *var, int ofs)
 
 static int git_branch_config(const char *var, const char *value, void *cb)
 {
+	int status = git_column_config(var, value, "branch", &colopts);
+	if (status <= 0)
+		return status;
 	if (!strcmp(var, "color.branch")) {
 		branch_use_color = git_config_colorbool(var, value);
 		return 0;
@@ -474,7 +482,7 @@ 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);
+	print_cell(&output, colopts, out.buf);
 	strbuf_release(&name);
 	strbuf_release(&out);
 }
@@ -727,6 +735,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 +758,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	}
 	hashcpy(merge_filter_ref, head_sha1);
 
+	if (!colopts)
+		colopts = git_colopts | COL_ANSI;
+
 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
 			     0);
 
@@ -763,9 +775,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	if (delete)
 		return delete_branches(argc, argv, delete > 1, kinds);
-	else if (list)
-		return print_ref_list(kinds, detached, verbose, abbrev,
+	else if (list) {
+		int ret;
+		if (verbose)
+			colopts = 0;
+
+		ret = print_ref_list(kinds, detached, verbose, abbrev,
 				      with_commit, argv);
+		print_columns(&output, colopts, NULL);
+		string_list_clear(&output, 0);
+		return ret;
+	}
 	else if (edit_description) {
 		const char *branch_name;
 		if (detached)
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v4 11/13] status: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-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             |   13 +++++++++++--
 wt-status.c                  |   38 ++++++++++++++++++++++++++++++++------
 wt-status.h                  |    2 +-
 6 files changed, 56 insertions(+), 10 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index c14db27..ebb210c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -851,6 +851,10 @@ column.branch::
 	Specify whether to output branch listing in `git branch` in columns.
 	See `column.ui` for details.
 
+column.status::
+	Specify whether to output untracked files in `git status` in columns.
+	See `column.ui` for details.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 3d51717..2f87207 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].
 	Terminate entries with NUL, instead of LF.  This implies
 	the `--porcelain` output format if no other format is given.
 
+--column[=<options>]::
+--no-column::
+	Display untracked files in columns. See configuration variable
+	column.status for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never'
+	respectively.
+
 
 OUTPUT
 ------
diff --git a/Makefile b/Makefile
index 061f6e5..b2644bc 100644
--- a/Makefile
+++ b/Makefile
@@ -2116,7 +2116,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
-builtin/branch.o column.o help.o pager.o: column.h
+builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
diff --git a/builtin/commit.c b/builtin/commit.c
index eba1377..8ce6a18 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 int colopts;
 
 /*
  * The default commit message cleanup mode will remove the lines
@@ -519,7 +521,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 		wt_porcelain_print(s, null_termination);
 		break;
 	case STATUS_FORMAT_LONG:
-		wt_status_print(s);
+		wt_status_print(s, 0);
 		break;
 	}
 
@@ -1138,7 +1140,11 @@ static int parse_status_slot(const char *var, int offset)
 static int git_status_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
+	int status;
 
+	status = git_column_config(k, v, "status", &colopts);
+	if (status <= 0)
+		return status;
 	if (!strcmp(k, "status.submodulesummary")) {
 		int is_bool;
 		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
@@ -1204,6 +1210,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
 		  "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
 		  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+		OPT_COLUMN(0, "column", &colopts, "list untracked files in columns" ),
 		OPT_END(),
 	};
 
@@ -1213,6 +1220,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	wt_status_prepare(&s);
 	gitmodules_config();
 	git_config(git_status_config, &s);
+	if (!colopts)
+		colopts = git_colopts | COL_ANSI;
 	determine_whence(&s);
 	argc = parse_options(argc, argv, prefix,
 			     builtin_status_options,
@@ -1251,7 +1260,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	case STATUS_FORMAT_LONG:
 		s.verbose = verbose;
 		s.ignore_submodule_arg = ignore_submodule_arg;
-		wt_status_print(&s);
+		wt_status_print(&s, colopts);
 		break;
 	}
 	return 0;
diff --git a/wt-status.c b/wt-status.c
index 9ffc535..86291e9 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 */
@@ -637,10 +638,13 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
 static void wt_status_print_other(struct wt_status *s,
 				  struct string_list *l,
 				  const char *what,
-				  const char *how)
+				  const char *how,
+				  int colopts)
 {
 	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 +653,32 @@ 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 (colopts & COL_ENABLED) {
+			add_cell_to_list(&output, colopts, 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 ((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;
+	copts.nl = GIT_COLOR_RESET "\n";
+	print_columns(&output, colopts, &copts);
+	string_list_clear(&output, 0);
 	strbuf_release(&buf);
 }
 
@@ -704,7 +728,7 @@ static void wt_status_print_tracking(struct wt_status *s)
 	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
 }
 
-void wt_status_print(struct wt_status *s)
+void wt_status_print(struct wt_status *s, int colopts)
 {
 	const char *branch_color = color(WT_STATUS_ONBRANCH, s);
 	const char *branch_status_color = color(WT_STATUS_HEADER, s);
@@ -742,9 +766,11 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_submodule_summary(s, 1);  /* unstaged */
 	}
 	if (s->show_untracked_files) {
-		wt_status_print_other(s, &s->untracked, _("Untracked"), "add");
+		wt_status_print_other(s, &s->untracked,
+				      _("Untracked"), "add", colopts);
 		if (s->show_ignored_files)
-			wt_status_print_other(s, &s->ignored, _("Ignored"), "add -f");
+			wt_status_print_other(s, &s->ignored,
+					      _("Ignored"), "add -f", colopts);
 	} else if (s->commitable)
 		status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
 			advice_status_hints
diff --git a/wt-status.h b/wt-status.h
index 682b4c8..4ab2799 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -69,7 +69,7 @@ struct wt_status {
 };
 
 void wt_status_prepare(struct wt_status *s);
-void wt_status_print(struct wt_status *s);
+void wt_status_print(struct wt_status *s, int colopts);
 void wt_status_collect(struct wt_status *s);
 
 void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch);
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v4 13/13] tag: add --column
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-1-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/config.txt  |    4 ++++
 Documentation/git-tag.txt |   11 ++++++++++-
 Makefile                  |    2 +-
 builtin/tag.c             |   25 ++++++++++++++++++++++---
 4 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index ebb210c..145336a 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -855,6 +855,10 @@ column.status::
 	Specify whether to output untracked files in `git status` in columns.
 	See `column.ui` for details.
 
+column.tag::
+	Specify whether to output tag listing in `git tag` in columns.
+	See `column.ui` for details.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 53ff5f6..5ead91e 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -12,7 +12,8 @@ SYNOPSIS
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
 	<tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>...]
+'git tag' [-n[<num>]] -l [--contains <commit>]
+	[--column[=<options>] | --no-column] [<pattern>...]
 'git tag' -v <tagname>...
 
 DESCRIPTION
@@ -83,6 +84,14 @@ OPTIONS
 	using fnmatch(3)).  Multiple patterns may be given; if any of
 	them matches, the tag is shown.
 
+--column[=<options>]::
+--no-column::
+	Display tag listing in columns. See configuration variable
+	column.tag for option syntax.`--column` and `--no-column`
+	without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable when listing tags without annotation lines.
+
 --contains <commit>::
 	Only list tags which contain the specified commit.
 
diff --git a/Makefile b/Makefile
index b2644bc..eb7b6fc 100644
--- a/Makefile
+++ b/Makefile
@@ -2116,7 +2116,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
-builtin/branch.o builtin/commit.o column.o help.o pager.o: column.h
+builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
diff --git a/builtin/tag.c b/builtin/tag.c
index 31f02e8..cecbe5c 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -15,6 +15,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "gpg-interface.h"
+#include "column.h"
 
 static const char * const git_tag_usage[] = {
 	"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -30,6 +31,8 @@ struct tag_filter {
 	struct commit_list *with_commit;
 };
 
+static int colopts;
+
 static int match_pattern(const char **patterns, const char *ref)
 {
 	/* no pattern means match everything */
@@ -230,6 +233,9 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 	int status = git_gpg_config(var, value, cb);
 	if (status)
 		return status;
+	status = git_column_config(var, value, "tag", &colopts);
+	if (status <= 0)
+		return status;
 	return git_default_config(var, value, cb);
 }
 
@@ -409,6 +415,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_STRING('u', "local-user", &keyid, "key-id",
 					"use another key to sign the tag"),
 		OPT__FORCE(&force, "replace the tag if exists"),
+		OPT_COLUMN(0, "column", &colopts, "show tag list in columns" ),
 
 		OPT_GROUP("Tag listing options"),
 		{
@@ -421,6 +428,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	};
 
 	git_config(git_tag_config, NULL);
+	if (!colopts)
+		colopts = git_colopts;
 
 	memset(&opt, 0, sizeof(opt));
 
@@ -441,9 +450,19 @@ 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) {
+		int ret;
+		if (lines == -1) {
+			struct column_options copts;
+			memset(&copts, 0, sizeof(copts));
+			copts.padding = 2;
+			run_column_filter(colopts, &copts);
+		}
+		ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
+		if (lines == -1)
+			stop_column_filter();
+		return ret;
+	}
 	if (lines != -1)
 		die(_("-n option is only allowed with -l."));
 	if (with_commit)
-- 
1.7.8.36.g69ee2

^ permalink raw reply related

* [PATCH v4 12/13] column: support piping stdout to external git-column process
From: Nguyễn Thái Ngọc Duy @ 2012-02-03 13:34 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <1328276078-27955-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 671ee5d..ff8e4ac 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"
 
@@ -422,3 +423,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=0x%x", colopts);
+	av[ac++] = sb_colopt.buf;
+	if (opts->width) {
+		strbuf_addf(&sb_width, "--width=%d", opts->width);
+		av[ac++] = sb_width.buf;
+	}
+	if (opts->indent) {
+		av[ac++] = "--indent";
+		av[ac++] = opts->indent;
+	}
+	if (opts->padding) {
+		strbuf_addf(&sb_padding, "--padding=%d", opts->padding);
+		av[ac++] = sb_padding.buf;
+	}
+	av[ac] = NULL;
+
+	fflush(stdout);
+	memset(&column_process, 0, sizeof(column_process));
+	column_process.in = -1;
+	column_process.out = dup(1);
+	column_process.git_cmd = 1;
+	column_process.argv = av;
+
+	ret = start_command(&column_process);
+
+	strbuf_release(&sb_colopt);
+	strbuf_release(&sb_width);
+	strbuf_release(&sb_padding);
+
+	if (ret)
+		return -2;
+
+	fd_out = dup(1);
+	close(1);
+	dup2(column_process.in, 1);
+	close(column_process.in);
+	return 0;
+}
+
+int stop_column_filter()
+{
+	if (fd_out == -1)
+		return -1;
+
+	fflush(stdout);
+	close(1);
+	finish_command(&column_process);
+	dup2(fd_out, 1);
+	close(fd_out);
+	fd_out = -1;
+	return 0;
+}
diff --git a/column.h b/column.h
index afdafc4..58d73b8 100644
--- a/column.h
+++ b/column.h
@@ -35,4 +35,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

* Re: [PATCH] t0300-credentials: Word around a solaris /bin/sh bug
From: Ben Walton @ 2012-02-03 13:45 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20120203120657.GB31441@sigill.intra.peff.net>

Excerpts from Jeff King's message of Fri Feb 03 07:06:57 -0500 2012:

> When I have hard-coded "#!/bin/sh", my thinking is usually "this is
> less cumbersome to type and to read, and this script-let is so small
> that even Solaris will get it right".

This is why I opted to stick with /bin/sh and just avoid the damage.
Overall, using a sane shell is a better option...It is harder to read
though.

Thanks
-Ben
--
Ben Walton
Systems Programmer - CHASS
University of Toronto
C:416.407.5610 | W:416.978.4302

^ permalink raw reply

* Re: How best to handle multiple-authorship commits in GIT?
From: David Howells @ 2012-02-03 13:47 UTC (permalink / raw)
  To: Valerie Aurora; +Cc: dhowells, git@vger.kernel.org, Rusty Russell
In-Reply-To: <CAD-XujkVK=tOtmVS90U0KAutFZ55jxsHMKuuMppXOi-H6ZY=RQ@mail.gmail.com>

Valerie Aurora <valerie.aurora@gmail.com> wrote:

> And for a complete (meaningful) rewrite such as David has done, he
> changes the commit authorship and adds a Signed-off-by for the
> original author.

Val[*] hasn't signed off all her patches, and indeed I've merged together some
patches that she has signed off and some she hasn't.  I can't simply add
Signed-off-by her without her permission.  However, if she's willing for me to
add such lines, then I can do so.

> Signed-off-by: Some Upstream Author
> Signed-off-by: Maintainer or Merger (rewrote error handling)

And if the changes are more than can be put in what's left of the line?  I
would've thought it would make more sense to do something like:

  Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com> (Original author)
  Signed-off-by: David Howells <dhowells@redhat.com> (Further development)

David

[*] Apologies for talking about/to you in the third person, Val.

^ permalink raw reply

* Re: Breakage in master?
From: Joel C. Salomon @ 2012-02-03 13:55 UTC (permalink / raw)
  To: kusmabite
  Cc: Jeff King, Git Mailing List, msysGit,
	Ævar Arnfjörð
In-Reply-To: <CABPQNSZfKCTsuusPpHa2djEOeGVN9z5s_Fr+S3EaHiv7Q4Re9w@mail.gmail.com>

On 02/03/2012 07:28 AM, Erik Faye-Lund wrote:
> On Thu, Feb 2, 2012 at 6:46 PM, Jeff King <peff@peff.net> wrote:
>> vsnprintf should generally never be returning -1 (it should return the
>> number of characters that would have been written). Since you're on
>> Windows, I assume you're using the replacement version in
>> compat/snprintf.c.
> 
> No. SNPRINTF_RETURNS_BOGUS is only set for the MSVC target, not for
> the MinGW target. I'm assuming that means MinGW-runtime has a sane
> vsnprintf implementation.

That doesn't sound right; MinGW defaults to linking to a fairly old
version of the Windows C library (MSVCRT.dll from Visual Studio 6,
IIRC).  According to <http://mingw.org/wiki/C99> there exists libmingwex
with some functions (especially those from <stdio.h>) replaced for
Standard compatibility, but it's "far from complete".  (Is msysGit using
it anyway?)

--Joel

^ permalink raw reply

* Re: Breakage in master?
From: Erik Faye-Lund @ 2012-02-03 14:05 UTC (permalink / raw)
  To: Joel C. Salomon
  Cc: Jeff King, Git Mailing List, msysGit,
	Ævar Arnfjörð
In-Reply-To: <4F2BE759.4000902@gmail.com>

On Fri, Feb 3, 2012 at 2:55 PM, Joel C. Salomon <joelcsalomon@gmail.com> wrote:
> On 02/03/2012 07:28 AM, Erik Faye-Lund wrote:
>> On Thu, Feb 2, 2012 at 6:46 PM, Jeff King <peff@peff.net> wrote:
>>> vsnprintf should generally never be returning -1 (it should return the
>>> number of characters that would have been written). Since you're on
>>> Windows, I assume you're using the replacement version in
>>> compat/snprintf.c.
>>
>> No. SNPRINTF_RETURNS_BOGUS is only set for the MSVC target, not for
>> the MinGW target. I'm assuming that means MinGW-runtime has a sane
>> vsnprintf implementation.
>
> That doesn't sound right; MinGW defaults to linking to a fairly old
> version of the Windows C library (MSVCRT.dll from Visual Studio 6,
> IIRC).  According to <http://mingw.org/wiki/C99> there exists libmingwex
> with some functions (especially those from <stdio.h>) replaced for
> Standard compatibility, but it's "far from complete".  (Is msysGit using
> it anyway?)

I'm not entirely sure what you are arguing. On MinGW, calling
vsnprintf vs _vsnprintf leads to different implementations on MinGW.
This is documented in the release-notes:
http://sourceforge.net/project/shownotes.php?release_id=24832

"As in previous releases, the MinGW implementations of snprintf() and
vsnprintf() are the default for these two functions, with the MSVCRT
alternatives being called as _snprintf() and _vsnprintf()."

I don't see how this is contradicted by your argument of a third,
C99-ish implementation. I'm pretty sure the "far from complete"-part
is about the C99-features anyway.

^ permalink raw reply

* Git performance results on a large repository
From: Joshua Redstone @ 2012-02-03 14:20 UTC (permalink / raw)
  To: git@vger.kernel.org

Hi Git folks,

We (Facebook) have been investigating source control systems to meet our
growing needs.  We already use git fairly widely, but have noticed it
getting slower as we grow, and we want to make sure we have a good story
going forward.  We're debating how to proceed and would like to solicit
people's thoughts.

To better understand git scalability, I've built up a large, synthetic
repository and measured a few git operations on it.  I summarize the
results here.

The test repo has 4 million commits, linear history and about 1.3 million
files.  The size of the .git directory is about 15GB, and has been
repacked with 'git repack -a -d -f --max-pack-size=10g --depth=100
--window=250'.  This repack took about 2 days on a beefy machine (I.e.,
lots of ram and flash).  The size of the index file is 191 MB. I can share
the script that generated it if people are interested - It basically picks
2-5 files, modifies a line or two and adds a few lines at the end
consisting of random dictionary words, occasionally creates a new file,
commits all the modifications and repeats.

I timed a few common operations with both a warm OS file cache and a cold
cache.  i.e., I did a 'echo 3 | tee /proc/sys/vm/drop_caches' and then did
the operation in question a few times (first timing is the cold timing,
the next few are the warm timings).  The following results are on a server
with average hard drive (I.e., not flash)  and > 10GB of ram.

'git status' :   39 minutes cold, and 24 seconds warm.

'git blame':   44 minutes cold, 11 minutes warm.

'git add' (appending a few chars to the end of a file and adding it):   7
seconds cold and 5 seconds warm.

'git commit -m "foo bar3" --no-verify --untracked-files=no --quiet
--no-status':  41 minutes cold, 20 seconds warm.  I also hacked a version
of git to remove the three or four places where 'git commit' stats every
file in the repo, and this dropped the times to 30 minutes cold and 8
seconds warm.


The git performance we observed here is too slow for our needs.  So the
question becomes, if we want to keep using git going forward, what's the
best way to improve performance.  It seems clear we'll probably need some
specialized servers (e.g., to perform git-blame quickly) and maybe
specialized file system integration to detect what files have changed in a
working tree.

One way to get there is to do some deep code modifications to git
internals, to, for example, create some abstractions and interfaces that
allow plugging in the specialized servers.  Another way is to leave git
internals as they are and develop a layer of wrapper scripts around all
the git commands that do the necessary interfacing.  The wrapper scripts
seem perhaps easier in the short-term, but may lead to increasing
divergence from how git behaves natively and also a layer of complexity.

Thoughts?

Cheers,
Josh

^ permalink raw reply

* Re: Breakage in master?
From: Joel C. Salomon @ 2012-02-03 14:22 UTC (permalink / raw)
  To: Erik Faye-Lund
  Cc: Jeff King, Git Mailing List, msysGit,
	Ævar Arnfjörð
In-Reply-To: <CABPQNSbQTF1UiDuOZkX-KrTQ7oFyVvx6FxZ85c9uCF2FFUtTSg@mail.gmail.com>

On 02/03/2012 09:05 AM, Erik Faye-Lund wrote:
> On Fri, Feb 3, 2012 at 2:55 PM, Joel C. Salomon <joelcsalomon@gmail.com> wrote:
>> That doesn't sound right; MinGW defaults to linking to a fairly old
>> version of the Windows C library (MSVCRT.dll from Visual Studio 6,
>> IIRC).
> 
> I'm not entirely sure what you are arguing. On MinGW, calling
> vsnprintf vs _vsnprintf leads to different implementations on MinGW.
> This is documented in the release-notes:
> http://sourceforge.net/project/shownotes.php?release_id=24832

Never mind; I stand corrected.  It's been some time since I actively
followed MinGW development.

--Joel

^ permalink raw reply

* Re: Alternates corruption issue
From: Richard Purdie @ 2012-02-03 14:40 UTC (permalink / raw)
  To: Jeff King
  Cc: Jonathan Nieder, Junio C Hamano, GIT Mailing-list, Hart, Darren,
	Ashfield, Bruce
In-Reply-To: <20120202215913.GA26727@sigill.intra.peff.net>

On Thu, 2012-02-02 at 16:59 -0500, Jeff King wrote:
> On Tue, Jan 31, 2012 at 04:22:58PM -0600, Jonathan Nieder wrote:
> 
> > Anyway, thanks for explaining.  Hopefully I can get to this soon and
> > factor out a common function for get_repo_path and enter_repo to call
> > so playing with the ordering becomes a little less scary. ;-)
> 
> So here's what I think we should apply to fix the particular issue that
> Richard mentioned at the start of this thread.
> 
> Besides tweaking the ordering, the main contribution is a set of tests
> that actually check some of these ambiguous cases (especially checking
> the fact that both code paths behave identically!). I didn't factor the
> logic into a common function, but doing so should be a little safer on
> top of these tests, if you're still interested.

I didn't have much to add to the discussion yesterday but this solution
looks good to me and should resolve the problems I was seeing.

Thanks!

Richard

^ permalink raw reply

* Re: [RFC/PATCH 0/2] Commits with ancient timestamps
From: Han-Wen Nienhuys @ 2012-02-03 14:53 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <1328218903-5681-1-git-send-email-gitster@pobox.com>

On Thu, Feb 2, 2012 at 7:41 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Result of conversion of ancient history from other SCMs, and output from
> other third-party tools, can record timestamps that predates inception of
> Git. They can cause "git am", "git rebase" and "git commit --amend" to
> misbehave, because the raw git timestamp e.g.
>
>        author <a.u.thor@example.com> 1328214896 -0800
>
> are read from the commit object and passed to parse_date() machinery,

As a bit of context: we have some internal tools at Google that create
administrative commits that should have no timestamp.  I am using "0"
and "1" as a deterministic timestamps in these cases (ie. the start of
the epoch). While this works well in general, there are some git
subcommands that barf on this, causing user-unhappiness.

This patch will hopefully resolve these breakages.

-- 
Han-Wen Nienhuys
Google Engineering Belo Horizonte
hanwen@google.com

^ permalink raw reply

* Re: Git performance results on a large repository
From: Ævar Arnfjörð Bjarmason @ 2012-02-03 14:56 UTC (permalink / raw)
  To: Joshua Redstone; +Cc: git@vger.kernel.org
In-Reply-To: <CB5074CF.3AD7A%joshua.redstone@fb.com>

On Fri, Feb 3, 2012 at 15:20, Joshua Redstone <joshua.redstone@fb.com> wrote:

> We (Facebook) have been investigating source control systems to meet our
> growing needs.  We already use git fairly widely, but have noticed it
> getting slower as we grow, and we want to make sure we have a good story
> going forward.  We're debating how to proceed and would like to solicit
> people's thoughts.

Where I work we also have a relatively large Git repository. Around
30k files, a couple of hundred thousand commits, clone size around
half a GB.

You haven't supplied background info on this but it really seems to me
like your testcase is converting something like a humongous Perforce
repository directly to Git.

While you /can/ do this it's not a good idea, you should split up
repositories at the boundaries code or data doesn't directly cross
over, e.g. there's no reason why you need HipHop PHP in the same
repository as Cassandra or the Facebook chat system, is there?

While Git could better with large repositories (in particular applying
commits in interactive rebase seems to be to slow down on bigger
repositories) there's only so much you can do about stat-ing 1.3
million files.

A structure that would make more sense would be to split up that giant
repository into a lot of other repositories, most of them probably
have no direct dependencies on other components, but even those that
do can sometimes just use some other repository as a submodule.

Even if you have the requirement that you'd like to roll out
*everything* at a certain point in time you can still solve that with
a super-repository that has all the other ones as submodules, and
creates a tag for every rollout or something like that.

^ permalink raw reply

* Push from an SSH Terminal
From: Feanil Patel @ 2012-02-03 15:50 UTC (permalink / raw)
  To: git

Hi Everyone,

I tried looking for an answer to my problem online without much luck,
perhaps you can help me.  I'm SSHed from my laptop(Comp A) over to a
computer(Comp B) that has my git repo on it. I made some changes and
comitted them. Now I want to push them to my other server(Comp C). The
repository is password protected so if I'm physically at Comp B, I get
a gui prompt for my username and password. However Comp A does not
have X Forwarding setup to Comp B so I can't get the gui interface for
the username and password when I try to do the push.  Is there an
alternative way to provide my credentials when doing a git push that
does not require a gui?

--
Feanil Patel

^ permalink raw reply

* Re: Push from an SSH Terminal
From: Neal Groothuis @ 2012-02-03 16:21 UTC (permalink / raw)
  To: Feanil Patel; +Cc: git
In-Reply-To: <CAG94OYxX5foffvaFLQv7=wXguGC2TLgccdDFrC+ERzv_gXZ=ug@mail.gmail.com>

> The
> repository is password protected so if I'm physically at Comp B, I get
> a gui prompt for my username and password. However Comp A does not
> have X Forwarding setup to Comp B so I can't get the gui interface for
> the username and password when I try to do the push.  Is there an
> alternative way to provide my credentials when doing a git push that
> does not require a gui?

What protocol are you using to access the repository on Comp C?

- Neal

^ permalink raw reply

* Re: Push from an SSH Terminal
From: Feanil Patel @ 2012-02-03 16:40 UTC (permalink / raw)
  To: Neal Groothuis; +Cc: git
In-Reply-To: <21607.38.96.167.131.1328286083.squirrel@mail.lo-cal.org>

On Fri, Feb 3, 2012 at 11:21 AM, Neal Groothuis <ngroot@lo-cal.org> wrote:
>> The
>> repository is password protected so if I'm physically at Comp B, I get
>> a gui prompt for my username and password. However Comp A does not
>> have X Forwarding setup to Comp B so I can't get the gui interface for
>> the username and password when I try to do the push.  Is there an
>> alternative way to provide my credentials when doing a git push that
>> does not require a gui?
>
> What protocol are you using to access the repository on Comp C?
>
> - Neal
>

I'm pulling and pushing over HTTP from Comp C.

^ permalink raw reply

* Re: Git performance results on a large repository
From: Joshua Redstone @ 2012-02-03 17:00 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: git@vger.kernel.org
In-Reply-To: <CACBZZX4BsFZxB6A-Hg-k37FBavgTV8SDiQTK_sVh9Mb9iskiEw@mail.gmail.com>

Hi Ævar,


Thanks for the comments.  I've included a bunch more info on the test repo
below.  It is based on a growth model of two of our current repositories
(I.e., it's not a perforce import). We already have some of the easily
separable projects in separate repositories, like HPHP.   If we could
split our largest repos into multiple ones, that would help the scaling
issue.  However, the code in those repos is rather interdependent and we
believe it'd hurt more than help to split it up, at least for the
medium-term future.  We derive a fair amount of benefit from the code
sharing and keeping things together in a single repo, so it's not clear
when it'd make sense to get more aggressive splitting things up.

Some more information on the test repository:   The working directory is
9.5 GB, the median file size is 2 KB.  The average depth of a directory
(counting the number of '/'s) is 3.6 levels and the average depth of a
file is 4.6.  More detailed histograms of the repository composition is
below:

------------------------

Histogram of depth of every directory in the repo (dirs=`find . -type d` ;
(for dir in $dirs; do t=${dir//[^\/]/}; echo ${#t} ; done) |
~/tmp/histo.py)
* The .git directory itself has only 161 files, so although included,
doesn't affect the numbers significantly)

[0.0 - 1.3): 271
[1.3 - 2.6): 9966
[2.6 - 3.9): 56595
[3.9 - 5.2): 230239
[5.2 - 6.5): 67394
[6.5 - 7.8): 22868
[7.8 - 9.1): 6568
[9.1 - 10.4): 420
[10.4 - 11.7): 45
[11.7 - 13.0]: 21
n=394387 mean=4.671830, median=5.000000, stddev=1.272658


Histogram of depth of every file in the repo (files=`git ls-files` ; (for
file in $files; do t=${file//[^\/]/}; echo ${#t} ; done) | ~/tmp/histo.py)
* 'git ls-files' does not prefix entries with ./, like the 'find' command
above, does, hence why the average appears to be the same as the directory
stats

[0.0 - 1.3]: 1274
[1.3 - 2.6]: 35353
[2.6 - 3.9]: 196747
[3.9 - 5.2]: 786647
[5.2 - 6.5]: 225913
[6.5 - 7.8]: 77667
[7.8 - 9.1]: 22130
[9.1 - 10.4]: 1599
[10.4 - 11.7]: 164
[11.7 - 13.0]: 118
n=1347612 mean=4.655750, median=5.000000, stddev=1.278399


Histogram of file sizes (for first 50k files - this command takes a
while):  files=`git ls-files` ; (for file in $files; do stat -c%s $file ;
done) | ~/tmp/histo.py

[ 0.0 - 4.7): 0
[ 4.7 - 22.5): 2
[ 22.5 - 106.8): 0
[ 106.8 - 506.8): 0
[ 506.8 - 2404.7): 31142
[ 2404.7 - 11409.9): 17837
[ 11409.9 - 54137.1): 942
[ 54137.1 - 256866.9): 53
[ 256866.9 - 1218769.7): 18
[ 1218769.7 - 5782760.0]: 5
n=49999 mean=3590.953239, median=1772.000000, stddev=42835.330259

Cheers,
Josh






On 2/3/12 9:56 AM, "Ævar Arnfjörð Bjarmason" <avarab@gmail.com> wrote:

>On Fri, Feb 3, 2012 at 15:20, Joshua Redstone <joshua.redstone@fb.com>
>wrote:
>
>> We (Facebook) have been investigating source control systems to meet our
>> growing needs.  We already use git fairly widely, but have noticed it
>> getting slower as we grow, and we want to make sure we have a good story
>> going forward.  We're debating how to proceed and would like to solicit
>> people's thoughts.
>
>Where I work we also have a relatively large Git repository. Around
>30k files, a couple of hundred thousand commits, clone size around
>half a GB.
>
>You haven't supplied background info on this but it really seems to me
>like your testcase is converting something like a humongous Perforce
>repository directly to Git.
>
>While you /can/ do this it's not a good idea, you should split up
>repositories at the boundaries code or data doesn't directly cross
>over, e.g. there's no reason why you need HipHop PHP in the same
>repository as Cassandra or the Facebook chat system, is there?
>
>While Git could better with large repositories (in particular applying
>commits in interactive rebase seems to be to slow down on bigger
>repositories) there's only so much you can do about stat-ing 1.3
>million files.
>
>A structure that would make more sense would be to split up that giant
>repository into a lot of other repositories, most of them probably
>have no direct dependencies on other components, but even those that
>do can sometimes just use some other repository as a submodule.
>
>Even if you have the requirement that you'd like to roll out
>*everything* at a certain point in time you can still solve that with
>a super-repository that has all the other ones as submodules, and
>creates a tag for every rollout or something like that.


^ permalink raw reply

* Re: Push from an SSH Terminal
From: Neal Groothuis @ 2012-02-03 17:10 UTC (permalink / raw)
  To: Feanil Patel; +Cc: git
In-Reply-To: <CAG94OYxbOYCjd5qNBh8EF2gyezHWMqX1-R2MYgk8gkFYcrMjuQ@mail.gmail.com>

> On Fri, Feb 3, 2012 at 11:21 AM, Neal Groothuis <ngroot@lo-cal.org> wrote:
>>> The
>>> repository is password protected so if I'm physically at Comp B, I get
>>> a gui prompt for my username and password. However Comp A does not
>>> have X Forwarding setup to Comp B so I can't get the gui interface for
>>> the username and password when I try to do the push.  Is there an
>>> alternative way to provide my credentials when doing a git push that
>>> does not require a gui?
>>
>> What protocol are you using to access the repository on Comp C?
>>
> I'm pulling and pushing over HTTP from Comp C.

Check to see if the GIT_ASKPASS and/or SSH_ASKPASS environment variables
are set, and if the core.askpass config variable is set.  If any of these
are set, unset them.  Git should fall back to a simple password prompt.

- Neal

^ permalink raw reply

* Re: Alternates corruption issue
From: Junio C Hamano @ 2012-02-03 17:38 UTC (permalink / raw)
  To: Jeff King
  Cc: Jonathan Nieder, Richard Purdie, GIT Mailing-list, Hart, Darren,
	Ashfield, Bruce
In-Reply-To: <20120203120215.GA31441@sigill.intra.peff.net>

Jeff King <peff@peff.net> writes:

> However, with the ordering change, there is a technically a regression
> in one case: a random file "foo" next to a repo "foo.git". Saying "git
> ls-remote foo" used to prefer "foo.git", and will now select the file
> "foo" only to fail.

Yeah, very true X-<.

> Thanks for noticing. I saw this issue when I was writing the original
> version of the patch, and meant to revisit it and at least document it
> in the commit message, but I ended up forgetting.

No, thanks for working on this.

^ permalink raw reply

* Re: [PATCH/RFCv2 (version B)] gitweb: Allow UTF-8 encoded CGI query parameters and  path_info
From: Michał Kiedrowicz @ 2012-02-03 17:45 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <201202031344.55750.jnareb@gmail.com>

Jakub Narebski <jnareb@gmail.com> wrote:

> Gitweb tries hard to properly process UTF-8 data, by marking output
> from git commands and contents of files as UTF-8 with to_utf8()
> subroutine.  This ensures that gitweb would print correctly UTF-8
> e.g. in 'log' and 'commit' views.
> 
> Unfortunately it misses another source of potentially Unicode input,
> namely query parameters.  The result is that one cannot search for a
> string containing characters outside US-ASCII.  For example searching
> for "Michał Kiedrowicz" (containing letter 'ł' - LATIN SMALL LETTER L
> WITH STROKE, with Unicode codepoint U+0142, represented with 0xc5 0x82
> bytes in UTF-8 and percent-encoded as %C5%81) result in the following
> incorrect data in search field
> 
> 	Michał Kiedrowicz
> 
> This is caused by CGI by default treating '0xc5 0x82' bytes as two
> characters in Perl legacy encoding latin-1 (iso-8859-1), because 's'
> query parameter is not processed explicitly as UTF-8 encoded string.
> 
> The solution used here follows "Using Unicode in a Perl CGI script"
> article on http://www.lemoda.net/cgi/perl-unicode/index.html:
> 
> 	use CGI;
> 	use Encode 'decode_utf8;
> 	my $value = params('input');
> 	$value = decode_utf8($value);
> 
> Decoding UTF-8 is done when filling %input_params hash and $path_info
> variable; the former required to move from explicit $cgi->param(<label>)
> to $input_params{<name>} in a few places, which is a good idea anyway.
> 
> Another required change was to add -override=>1 parameter to
> $cgi->textfield() invocation (in search form).  Otherwise CGI would
> use values from query string if it is present, filling value from
> $cgi->param... without decode_utf8().  As we are using value of
> appropriate parameter anyway, -override=>1 doesn't change the
> situation but makes gitweb fill search field correctly.
> 
> Alternate solution would be to simply use the '-utf8' pragma (via
> "use CGI '-utf8';"), but according to CGI.pm documentation it may
> cause problems with POST requests containing binary files... and
> it requires CGI 3.31 (I think), released with perl v5.8.9.
> 
> Noticed-by: Michał Kiedrowicz <michal.kiedrowicz@gmail.com>
> Signed-off-by: Jakub Narębski <jnareb@gmail.com>
> ---
> On Fri, 3 Feb 2012, Michal Kiedrowicz wrote:
> > Jakub Narebski <jnareb@gmail.com> wrote:
> 
> > > Is it what you mean by "this doesn't work for me", i.e. working
> > > search, garbage in search field?
> > 
> > I mean "garbage in search field". Search works even without the patch
> > (at least on Debian with git-1.7.7.3, perl-5.10.1 and CGI-3.43; I
> > don't have my notebook nearby at the moment to check).
> [...]
> 
> > > Damn.  If we use $cgi->textfield(-name => "s", -value => $searchtext)
> > > like in gitweb, CGI.pm would read $cgi->param("s") by itself -
> > > without decoding. 
> > 
> > Makes sense. When I tried calling to_utf8() in the line that defines
> > textfield (this was my first approach to this problem), it haven't
> > changed anything.
> 
> Yes, and it doesn't makes sense in gitweb case - we use value of 
> $cgi->param("s") as default value of text field anyway, but in
> Unicode-aware way.
>  
> > > To skip this we need to pass -force=>1  or
> > > -override=>1 (i.e. further changes to gitweb).
> 
> This patch does this.  
> 
> Does it make work for you?
> 

Yes, it works for me. Search form properly displays "ł". Thanks!

^ permalink raw reply

* Re: [RFC/PATCH 0/2] Commits with ancient timestamps
From: Junio C Hamano @ 2012-02-03 18:01 UTC (permalink / raw)
  To: Thomas Rast; +Cc: git, Han-Wen Nienhuys
In-Reply-To: <87mx90yz5y.fsf@thomas.inf.ethz.ch>

Thomas Rast <trast@inf.ethz.ch> writes:

> Doing this just makes me wonder how important exactly the 1970-1975
> range is.  Is there a notable software history from that era that can be
> recovered?

That is not really a valid question. People who wrote private stuff in
that era deserve to be users of Git, too.

> (Your [1/2] does not seem to parse negative offsets from the unix epoch,
> so anything before 1970 is still out.)

Yes, pre-epoch timestamps are also useful for projects like US
Constitution.

  http://thread.gmane.org/gmane.comp.version-control.git/152433/focus=152725

For that, we would need to use and pass around time_t (or intmax_t if we
follow the reason why originally Linus chose to avoid time_t) throughout
the codebase.  If you actually write commit objects that record pre-epoch
timestamps, however, they will become unreadable by the current versions
of Git (as they would not understand such a negative raw timestamp).

In any case, that is a goal for a much longer term.

But even after such a change happens, you still need a way for Git to
replay a raw timestamp stored in commit objects without regressing the
parse_date() interface too much. These two patches show one way to do so
with minimum disruption.

As an added bonus, with the second patch, the way to spell a raw timestamp
happens to become compatible with how GNU date accepts one, i.e.

        $ date -d @1000000000

even though we do not have to encourage the use of this notation by humans,
tools and script writers may find it useful.

^ permalink raw reply


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