Subject: [PATCH v12 00/15] Interactive git-clean

From: Jiang Xin <[email-masked]>
Date: Sat, 18 May 2013 11:18:52 +0800

I feel change the order of patch 01/15 and patch 02/15 is better.
I.E. Add test cases for relative_path first, then refactor
relative_path and fix the test cases.

Also fix wrong indent in t/7301.

Differences with gitster/jx/clean-interactive:

diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 75fb54..5bf76 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -82,8 +82,8 @@ and type return, like this:
 
 ------------
     *** Commands ***
-	1: clean                2: filter by pattern    3: select by numbers
-	4: ask each             5: quit                 6: help
+        1: clean                2: filter by pattern    3: select by numbers
+        4: ask each             5: quit                 6: help
     What now> 1
 ------------
 
diff --git a/builtin/clean.c b/builtin/clean.c
index 73ba2..7d34b 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -64,7 +64,7 @@ struct menu_opts {
 
 struct menu_item {
 	char hotkey;
-	char *title;
+	const char *title;
 	int selected;
 	int (*fn)();
 };
@@ -312,22 +312,26 @@ static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
 {
 	struct string_list menu_list = STRING_LIST_INIT_DUP;
 	struct strbuf menu = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct menu_item *menu_item;
+	struct string_list_item *string_list_item;
 	int i;
 
-	if (stuff->type == MENU_STUFF_TYPE_MENU_ITEM) {
-		struct menu_item *item;
-
-		item = (struct menu_item *)stuff->stuff;
-		for (i = 0; i < stuff->nr; i++, item++) {
-			char *p;
+	switch (stuff->type) {
+	default:
+		die("Bad type of menu_staff when print menu");
+	case MENU_STUFF_TYPE_MENU_ITEM:
+		menu_item = (struct menu_item *)stuff->stuff;
+		for (i = 0; i < stuff->nr; i++, menu_item++) {
+			const char *p;
 			int highlighted = 0;
 
-			p = item->title;
+			p = menu_item->title;
 			if ((*chosen)[i] < 0)
-				(*chosen)[i] = item->selected ? 1 : 0;
+				(*chosen)[i] = menu_item->selected ? 1 : 0;
 			strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
 			for (; *p; p++) {
-				if (!highlighted && *p == item->hotkey) {
+				if (!highlighted && *p == menu_item->hotkey) {
 					strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
 					strbuf_addch(&menu, *p);
 					strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
@@ -339,27 +343,25 @@ static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
 			string_list_append(&menu_list, menu.buf);
 			strbuf_reset(&menu);
 		}
-
-	} else if (stuff->type == MENU_STUFF_TYPE_STRING_LIST) {
-		struct string_list_item *item;
-		struct strbuf buf = STRBUF_INIT;
+		break;
+	case MENU_STUFF_TYPE_STRING_LIST:
 		i = 0;
-
-		for_each_string_list_item(item, (struct string_list *)stuff->stuff) {
+		for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
 			if ((*chosen)[i] < 0)
 				(*chosen)[i] = 0;
 			strbuf_addf(&menu, "%s%2d: %s",
-				    (*chosen)[i] ? "*" : " ", i + 1, item->string);
+				    (*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
 			string_list_append(&menu_list, menu.buf);
 			strbuf_reset(&menu);
 			i++;
 		}
-		strbuf_release(&buf);
+		break;
 	}
 
 	pretty_print_menus(&menu_list);
 
 	strbuf_release(&menu);
+	strbuf_release(&buf);
 	string_list_clear(&menu_list, 0);
 }
 
@@ -390,6 +392,8 @@ static int parse_choice(struct menu_stuff *menu_stuff,
 			int **chosen)
 {
 	struct strbuf **choice_list, **ptr;
+	struct menu_item *menu_item;
+	struct string_list_item *string_list_item;
 	int nr = 0;
 	int i;
 
@@ -454,30 +458,31 @@ static int parse_choice(struct menu_stuff *menu_stuff,
 			bottom = 1;
 			top = menu_stuff->nr;
 		} else {
-			if (MENU_STUFF_TYPE_MENU_ITEM == menu_stuff->type) {
-				struct menu_item *item;
-
-				item = (struct menu_item *)menu_stuff->stuff;
-				for (i = 0; i < menu_stuff->nr; i++, item++) {
+			switch (menu_stuff->type) {
+			default:
+				die("Bad type of menu_stuff when parse choice");
+			case MENU_STUFF_TYPE_MENU_ITEM:
+				menu_item = (struct menu_item *)menu_stuff->stuff;
+				for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
 					if (((*ptr)->len == 1 &&
-					     *(*ptr)->buf == item->hotkey) ||
-					    !strcasecmp((*ptr)->buf, item->title)) {
+					     *(*ptr)->buf == menu_item->hotkey) ||
+					    !strcasecmp((*ptr)->buf, menu_item->title)) {
 						bottom = i + 1;
 						top = bottom;
 						break;
 					}
 				}
-			} else if (MENU_STUFF_TYPE_STRING_LIST == menu_stuff->type) {
-				struct string_list_item *item;
-
-				item = ((struct string_list *)menu_stuff->stuff)->items;
-				for (i = 0; i < menu_stuff->nr; i++, item++) {
-					if (!strcasecmp((*ptr)->buf, item->string)) {
+				break;
+			case MENU_STUFF_TYPE_STRING_LIST:
+				string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
+				for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
+					if (!strcasecmp((*ptr)->buf, string_list_item->string)) {
 						bottom = i + 1;
 						top = bottom;
 						break;
 					}
 				}
+				break;
 			}
 		}
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index d3b33..6acff4 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -46,11 +46,11 @@ static const char *tag_modified = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
-static void write_name(const char* name)
+static void write_name(const char *name)
 {
 
 	/* turn off prefix, if run with "--full-name" */
-	write_name_quoted_relative(name, prefix_len > 0 ? prefix : NULL,
+	write_name_quoted_relative(name, prefix_len ? prefix : NULL,
 				   stdout, line_terminator);
 }
 
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 09a42..dfe47 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -12,6 +12,11 @@ norm_path() {
 	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
 }
 
+relative_path() {
+	test_expect_success $4 "relative path: $1 $2 => $3" \
+	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$3'"
+}
+
 # On Windows, we are using MSYS's bash, which mangles the paths.
 # Absolute paths are anchored at the MSYS installation directory,
 # which means that the path / accounts for this many characters:
@@ -183,4 +188,22 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
 	test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
+relative_path /a/b/c/	/a/b/		c/
+relative_path /a/b/c/	/a/b		c/
+relative_path /a//b//c/	//a/b//		c/
+relative_path /a/b	/a/b		./
+relative_path /a/b/	/a/b		./
+relative_path /a	/a/b		../
+relative_path /		/a/b/		../../
+relative_path /a/c	/a/b/		../c
+relative_path /a/c	/a/b		../c
+relative_path /a/b	"<empty>"	/a/b
+relative_path /a/b 	"<null>"	/a/b
+relative_path "<empty>"	/a/b		./
+relative_path "<empty>"	"<empty>"	./
+relative_path "<empty>"	"<null>"	./
+relative_path "<null>"	"<empty>"	./
+relative_path "<null>"	"<null>"	./
+relative_path "<null>"	/a/b		./
+
 test_done
diff --git a/test-path-utils.c b/test-path-utils.c
index 0092cb..bc4ad 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -1,6 +1,19 @@
 #include "cache.h"
 #include "string-list.h"
 
+#define PARSE_ARGV_STRING(var, input) do { \
+	if (!strcmp(input, "<null>")) { \
+		var = NULL; \
+	} else if (!strcmp(input, "<empty>")) { \
+		var = ""; \
+	} else if (*input == '<' || *input == '(') { \
+		fprintf(stderr, "Bad value: %s\n", input); \
+		return 1; \
+	} else { \
+		var = input; \
+	} \
+} while (0)
+
 /*
  * A "string_list_each_func_t" function that normalizes an entry from
  * GIT_CEILING_DIRECTORIES.  If the path is unusable for some reason,
@@ -103,6 +116,20 @@ int main(int argc, char **argv)
 		return 0;
 	}
 
+	if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+		struct strbuf sb = STRBUF_INIT;
+		const char *abs, *base, *rel;
+		PARSE_ARGV_STRING(abs, argv[2]);
+		PARSE_ARGV_STRING(base, argv[3]);
+		rel = relative_path(abs, base, &sb);
+		if (!rel)
+			puts("(null)");
+		else
+			puts(strlen(rel) > 0 ? rel : "(empty)");
+		strbuf_release(&sb);
+		return 0;
+	}
+
 	fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
 		argv[1] ? argv[1] : "(there was none)");
 	return 1;

Jiang Xin (15):
  test: add test cases for relative_path
  path.c: refactor relative_path(), not only strip prefix
  quote.c: remove path_relative, use relative_path instead
  Refactor quote_path_relative, remove unused params
  Refactor write_name_quoted_relative, remove unused params
  git-clean: refactor git-clean into two phases
  git-clean: add support for -i/--interactive
  git-clean: show items of del_list in columns
  git-clean: add colors to interactive git-clean
  git-clean: use a git-add-interactive compatible UI
  git-clean: add filter by pattern interactive action
  git-clean: add select by numbers interactive action
  git-clean: add ask each interactive action
  git-clean: add documentation for interactive git-clean
  test: add t7301 for git-clean--interactive

 Documentation/config.txt     |  21 +-
 Documentation/git-clean.txt  |  71 +++-
 builtin/clean.c              | 779 +++++++++++++++++++++++++++++++++++++++++--
 builtin/grep.c               |   5 +-
 builtin/ls-files.c           |  16 +-
 cache.h                      |   2 +-
 path.c                       | 112 +++++--
 quote.c                      |  65 +---
 quote.h                      |   7 +-
 setup.c                      |   5 +-
 t/t0060-path-utils.sh        |  23 ++
 t/t7301-clean-interactive.sh | 439 ++++++++++++++++++++++++
 test-path-utils.c            |  27 ++
 wt-status.c                  |  17 +-
 14 files changed, 1438 insertions(+), 151 deletions(-)
 create mode 100755 t/t7301-clean-interactive.sh

-- 
1.8.3.rc2.26.g7472058