git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH jx/clean-interactive] t0060: skip a few relative_path tests on Windows
@ 2013-06-13  8:23 Johannes Sixt
  2013-06-13  9:40 ` Jiang Xin
  0 siblings, 1 reply; 33+ messages in thread
From: Johannes Sixt @ 2013-06-13  8:23 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Git Mailing List

From: Johannes Sixt <j6t@kdbg.org>

The bash on Windows rewrites paths that look like absolute POSIX paths
when they are a command-line argument of a regular Windows program, such
as git and the test helpers. As a consequence, the actual tests performed
are not what the tests scripts expect.

The tests that need *not* be skipped are those where the two paths passed
to 'test-path-utils relative_path' have the same prefix and the result is
expected to be a relative path. This is because the rewriting changes
"/a/b" to "D:/Src/MSysGit/a/b", and when both inputs are extended the same
way, this just cancels out in the relative path computation.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
---
 t/t0060-path-utils.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index dfe4747..4deec52 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -190,15 +190,15 @@ test_expect_success SYMLINKS 'real path works on
symlinks' '

 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//c/	//a/b//		c/	POSIX
 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 /a/b	"<empty>"	/a/b	POSIX
+relative_path /a/b 	"<null>"	/a/b	POSIX
 relative_path "<empty>"	/a/b		./
 relative_path "<empty>"	"<empty>"	./
 relative_path "<empty>"	"<null>"	./
-- 
1.8.3.1.1670.g1dbc49e

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH jx/clean-interactive] t0060: skip a few relative_path tests on Windows
  2013-06-13  8:23 [PATCH jx/clean-interactive] t0060: skip a few relative_path tests on Windows Johannes Sixt
@ 2013-06-13  9:40 ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 00/16] Interactive git-clean Jiang Xin
                     ` (16 more replies)
  0 siblings, 17 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-13  9:40 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Git Mailing List, Junio C Hamano

2013/6/13 Johannes Sixt <j.sixt@viscovery.net>:
> From: Johannes Sixt <j6t@kdbg.org>
>
> The bash on Windows rewrites paths that look like absolute POSIX paths
> when they are a command-line argument of a regular Windows program, such
> as git and the test helpers. As a consequence, the actual tests performed
> are not what the tests scripts expect.
>
> The tests that need *not* be skipped are those where the two paths passed
> to 'test-path-utils relative_path' have the same prefix and the result is
> expected to be a relative path. This is because the rewriting changes
> "/a/b" to "D:/Src/MSysGit/a/b", and when both inputs are extended the same
> way, this just cancels out in the relative path computation.
>
> Signed-off-by: Johannes Sixt <j6t@kdbg.org>
> ---
>  t/t0060-path-utils.sh | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>

Thank you for pointing out this cross-platform issue.
I test your patch on Mac OS X, but not test it on msys yet.
Since this issue is from the very first commit of this series
and this series are still in pu, so I move your patch as
patch 02/16 of this series.

You can get the update series in my clone on GitHub:

    $ git remote add jiangxin git://github.com/jiangxin/git.git
    $ git fetch jiangxin jx/clean-interactive
    $ git log --oneline -16 jiangxin/jx/clean-interactive

    c646c test: add t7301 for git-clean--interactive
    92d4a git-clean: add documentation for interactive git-clean
    22e3d git-clean: add ask each interactive action
    5fcb8 git-clean: add select by numbers interactive action
    77ef8e git-clean: add filter by pattern interactive action
    02327 git-clean: use a git-add-interactive compatible UI
    2322 git-clean: add colors to interactive git-clean
    06fad git-clean: show items of del_list in columns
    6ae8b git-clean: add support for -i/--interactive
    1eeb5 git-clean: refactor git-clean into two phases
    3f903 Refactor write_name_quoted_relative, remove unused params
    8ccdf Refactor quote_path_relative, remove unused params
    13da5e quote.c: remove path_relative, use relative_path instead
    1208ee path.c: refactor relative_path(), not only strip prefix
    95b06 t0060: skip a few relative_path tests on Windows
    22247 test: add test cases for relative_path

And commit 95b06 may squash to previous commit 22247.

-- 
Jiang Xin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 00/16] Interactive git-clean
  2013-06-13  9:40 ` Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 01/16] test: add test cases for relative_path Jiang Xin
                     ` (15 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Johannes found that some relative_path tests should be skipped on Windows.
See this thread:

  http://thread.gmane.org/gmane.comp.version-control.git/227706

In this reroll,

 * I squash Johannes's patch into patch 01/16, and seven relative_path
   test cases are skipped by marking with POSIX.
 
 * In patch 02/16, 4 test cases can run correctly as the refactor of
   relative_path.
 
 * In patch 16/16, most of the previous skiped test cases can run with
   the help of the new utiliy "test-path-utils mingw_path /abs/path/".

Jiang Xin (16):
  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
  test: run testcases with POSIX absolute paths on Windows

 Documentation/config.txt     |  21 +-
 Documentation/git-clean.txt  |  71 +++-
 builtin/clean.c              | 778 +++++++++++++++++++++++++++++++++++++++++--
 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        |  88 +++--
 t/t7301-clean-interactive.sh | 439 ++++++++++++++++++++++++
 test-path-utils.c            |  32 ++
 wt-status.c                  |  17 +-
 14 files changed, 1487 insertions(+), 171 deletions(-)
 create mode 100755 t/t7301-clean-interactive.sh

-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 01/16] test: add test cases for relative_path
  2013-06-13  9:40 ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 00/16] Interactive git-clean Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 19:01     ` Junio C Hamano
  2013-06-24 15:21   ` [PATCH v14 02/16] path.c: refactor relative_path(), not only strip prefix Jiang Xin
                     ` (14 subsequent siblings)
  16 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin, Johannes Sixt

Add subcommand "relative_path" in test-path-utils, and add test cases
in t0060.

Johannes tested this commit on Windows, and found that some relative_path
tests should be skipped on Windows. This is because the bash on Windows
rewrites arguments of regular Windows programs, such as git and the
test helpers, if the arguments look like absolute POSIX paths. As a
consequence, the actual tests performed are not what the tests scripts
expect.

The tests that need *not* be skipped are those where the two paths passed
to 'test-path-utils relative_path' have the same prefix and the result is
expected to be a relative path. This is because the rewriting changes
"/a/b" to "D:/Src/MSysGit/a/b", and when both inputs are extended the same
way, this just cancels out in the relative path computation.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t0060-path-utils.sh | 26 ++++++++++++++++++++++++++
 test-path-utils.c     | 25 +++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 09a42..7e258 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,25 @@ 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/	POSIX
+relative_path /a/b	/a/b		.
+relative_path /a/b/	/a/b		.
+relative_path /a	/a/b		/a	POSIX
+relative_path /		/a/b/		/	POSIX
+relative_path /a/c	/a/b/		/a/c	POSIX
+relative_path /a/c	/a/b		/a/c	POSIX
+relative_path /a/b	"<empty>"	/a/b	POSIX
+relative_path /a/b 	"<null>"	/a/b	POSIX
+relative_path "<empty>"	/a/b		"(empty)"
+relative_path "<empty>"	"<empty>"	"(empty)"
+relative_path "<empty>"	"<null>"	"(empty)"
+relative_path "<null>"	"<empty>"	"(null)"
+relative_path "<null>"	"<null>"	"(null)"
+
+test_expect_failure 'relative path: <null> /a/b => segfault' '
+	test-path-utils relative_path "<null>" "/a/b"
+'
+
 test_done
diff --git a/test-path-utils.c b/test-path-utils.c
index 0092cb..dcc530 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -28,6 +28,19 @@ static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
 	return 1;
 }
 
+static void normalize_argv_string(const char **var, const char *input)
+{
+	if (!strcmp(input, "<null>"))
+		*var = NULL;
+	else if (!strcmp(input, "<empty>"))
+		*var = "";
+	else
+		*var = input;
+
+	if (*var && (**var == '<' || **var == '('))
+		die("Bad value: %s\n", input);
+}
+
 int main(int argc, char **argv)
 {
 	if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@@ -103,6 +116,18 @@ int main(int argc, char **argv)
 		return 0;
 	}
 
+	if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+		const char *abs, *base, *rel;
+		normalize_argv_string(&abs, argv[2]);
+		normalize_argv_string(&base, argv[3]);
+		rel = relative_path(abs, base);
+		if (!rel)
+			puts("(null)");
+		else
+			puts(strlen(rel) > 0 ? rel : "(empty)");
+		return 0;
+	}
+
 	fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
 		argv[1] ? argv[1] : "(there was none)");
 	return 1;
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 02/16] path.c: refactor relative_path(), not only strip prefix
  2013-06-13  9:40 ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 00/16] Interactive git-clean Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 01/16] test: add test cases for relative_path Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead Jiang Xin
                     ` (13 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Original design of relative_path() is simple, just strip the prefix
(*base) from the absolute path (*abs). In most cases, we need a real
relative path, such as: ../foo, ../../bar. That's why there is another
reimplementation (path_relative()) in quote.c.

Borrowed some codes from path_relative() in quote.c to refactor
relative_path() in path.c, so that it could return real relative path,
and user can reuse this function without reimplement his/her own.
The function path_relative() in quote.c will be substituted, and I
would use the new relative_path() function when implement the
interactive git-clean later.

Different results for relative_path() before and after this refactor:

    abs path  base path  relative (original)  relative (refactor)
    ========  =========  ===================  ===================
    /a/b/c/   /a/b       c/                   c/
    /a/b//c/  //a///b/   c/                   c/
    /a/b      /a/b       .                    ./
    /a/b/     /a/b       .                    ./
    /a        /a/b/      /a                   ../
    /         /a/b/      /                    ../../
    /a/c      /a/b/      /a/c                 ../c
    /a/b      (empty)    /a/b                 /a/b
    /a/b      (null)     /a/b                 /a/b
    (empty)   /a/b       (empty)              ./
    (null)    (empty)    (null)               ./
    (null)    /a/b       (segfault)           ./

You may notice that return value "." has been changed to "./".
It is because:

 * Function quote_path_relative() in quote.c will show the relative
   path as "./" if abs(in) and base(prefix) are the same.

 * Function relative_path() is called only once (in setup.c), and
   it will be OK for the return value as "./" instead of ".".

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 cache.h               |   2 +-
 path.c                | 112 +++++++++++++++++++++++++++++++++++++++-----------
 setup.c               |   5 ++-
 t/t0060-path-utils.sh |  27 ++++++------
 test-path-utils.c     |   4 +-
 5 files changed, 107 insertions(+), 43 deletions(-)

diff --git a/cache.h b/cache.h
index dd0fb..2f10c 100644
--- a/cache.h
+++ b/cache.h
@@ -758,7 +758,7 @@ int is_directory(const char *);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
-const char *relative_path(const char *abs, const char *base);
+const char *relative_path(const char *abs, const char *base, struct strbuf *sb);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
diff --git a/path.c b/path.c
index 04ff..8ff16 100644
--- a/path.c
+++ b/path.c
@@ -441,42 +441,104 @@ int adjust_shared_perm(const char *path)
 	return 0;
 }
 
-const char *relative_path(const char *abs, const char *base)
+/*
+ * Give relative path for abs to base.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+const char *relative_path(const char *abs, const char *base,
+			  struct strbuf *sb)
 {
-	static char buf[PATH_MAX + 1];
-	int i = 0, j = 0;
-
-	if (!base || !base[0])
+	int abs_off, base_off, i, j;
+	int abs_len, base_len;
+
+	abs_len = abs ? strlen(abs) : 0;
+	base_len = base ? strlen(base) : 0;
+	abs_off = 0;
+	base_off = 0;
+	i = 0;
+	j = 0;
+
+	if (!abs_len)
+		return "./";
+	else if (!base_len)
 		return abs;
-	while (base[i]) {
+
+	while (i < base_len && j < abs_len && base[i] == abs[j]) {
 		if (is_dir_sep(base[i])) {
-			if (!is_dir_sep(abs[j]))
-				return abs;
 			while (is_dir_sep(base[i]))
 				i++;
 			while (is_dir_sep(abs[j]))
 				j++;
-			continue;
-		} else if (abs[j] != base[i]) {
+			base_off = i;
+			abs_off = j;
+		} else {
+			i++;
+			j++;
+		}
+	}
+
+	if (
+	    /* base seems like prefix of abs */
+	    i >= base_len &&
+	    /*
+	     * but "/foo" is not a prefix of "/foobar"
+	     * (i.e. base not end with '/')
+	     */
+	    base_off < base_len) {
+		if (j >= abs_len) {
+			/* abs="/a/b", base="/a/b" */
+			abs_off = abs_len;
+		} else if (is_dir_sep(abs[j])) {
+			/* abs="/a/b/c", base="/a/b" */
+			while (is_dir_sep(abs[j]))
+				j++;
+			abs_off = j;
+		} else {
+			/* abs="/a/bbb/c", base="/a/b" */
+			i = base_off;
+		}
+	} else if (
+		   /* abs is short than base (prefix of base) */
+		   j >= abs_len &&
+		   /* abs not end with '/' */
+		   abs_off < abs_len) {
+		if (is_dir_sep(base[i])) {
+			/* abs="/a/b", base="/a/b/c/" */
+			while (is_dir_sep(base[i]))
+				i++;
+			abs_off = abs_len;
+		}
+	}
+	abs += abs_off;
+	abs_len -= abs_off;
+
+	if (i >= base_len) {
+		if (!abs_len)
+			return "./";
+		else
 			return abs;
+	}
+
+	strbuf_reset(sb);
+	strbuf_grow(sb, abs_len);
+
+	while (i < base_len) {
+		if (is_dir_sep(base[i])) {
+			strbuf_addstr(sb, "../");
+			while (is_dir_sep(base[i]))
+				i++;
+			continue;
 		}
 		i++;
-		j++;
 	}
-	if (
-	    /* "/foo" is a prefix of "/foo" */
-	    abs[j] &&
-	    /* "/foo" is not a prefix of "/foobar" */
-	    !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j])
-	   )
-		return abs;
-	while (is_dir_sep(abs[j]))
-		j++;
-	if (!abs[j])
-		strcpy(buf, ".");
-	else
-		strcpy(buf, abs + j);
-	return buf;
+	if (!is_dir_sep(base[base_len - 1]))
+		strbuf_addstr(sb, "../");
+
+	strbuf_addstr(sb, abs);
+
+	return sb->buf;
 }
 
 /*
diff --git a/setup.c b/setup.c
index 94c1e..0d9ea 100644
--- a/setup.c
+++ b/setup.c
@@ -360,6 +360,7 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
+	struct strbuf sb = STRBUF_INIT;
 	const char *work_tree, *git_dir;
 	static int initialized = 0;
 
@@ -379,8 +380,10 @@ void setup_work_tree(void)
 	if (getenv(GIT_WORK_TREE_ENVIRONMENT))
 		setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-	set_git_dir(relative_path(git_dir, work_tree));
+	set_git_dir(relative_path(git_dir, work_tree, &sb));
 	initialized = 1;
+
+	strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 7e258..4deec 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -191,22 +191,19 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
 relative_path /a/b/c/	/a/b/		c/
 relative_path /a/b/c/	/a/b		c/
 relative_path /a//b//c/	//a/b//		c/	POSIX
-relative_path /a/b	/a/b		.
-relative_path /a/b/	/a/b		.
-relative_path /a	/a/b		/a	POSIX
-relative_path /		/a/b/		/	POSIX
-relative_path /a/c	/a/b/		/a/c	POSIX
-relative_path /a/c	/a/b		/a/c	POSIX
+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	POSIX
 relative_path /a/b 	"<null>"	/a/b	POSIX
-relative_path "<empty>"	/a/b		"(empty)"
-relative_path "<empty>"	"<empty>"	"(empty)"
-relative_path "<empty>"	"<null>"	"(empty)"
-relative_path "<null>"	"<empty>"	"(null)"
-relative_path "<null>"	"<null>"	"(null)"
-
-test_expect_failure 'relative path: <null> /a/b => segfault' '
-	test-path-utils relative_path "<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 dcc530..95ef4 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -117,14 +117,16 @@ int main(int argc, char **argv)
 	}
 
 	if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+		struct strbuf sb = STRBUF_INIT;
 		const char *abs, *base, *rel;
 		normalize_argv_string(&abs, argv[2]);
 		normalize_argv_string(&base, argv[3]);
-		rel = relative_path(abs, base);
+		rel = relative_path(abs, base, &sb);
 		if (!rel)
 			puts("(null)");
 		else
 			puts(strlen(rel) > 0 ? rel : "(empty)");
+		strbuf_release(&sb);
 		return 0;
 	}
 
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead
  2013-06-13  9:40 ` Jiang Xin
                     ` (2 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 02/16] path.c: refactor relative_path(), not only strip prefix Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 19:11     ` Junio C Hamano
  2013-06-24 15:21   ` [PATCH v14 04/16] Refactor quote_path_relative, remove unused params Jiang Xin
                     ` (12 subsequent siblings)
  16 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Since there is an enhanced version of relative_path() in path.c,
remove duplicate counterpart path_relative() in quote.c.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 quote.c | 55 ++-----------------------------------------------------
 1 file changed, 2 insertions(+), 53 deletions(-)

diff --git a/quote.c b/quote.c
index 91122..64ff3 100644
--- a/quote.c
+++ b/quote.c
@@ -312,75 +312,24 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
 	fputc(terminator, fp);
 }
 
-static const char *path_relative(const char *in, int len,
-				 struct strbuf *sb, const char *prefix,
-				 int prefix_len);
-
 void write_name_quoted_relative(const char *name, size_t len,
 				const char *prefix, size_t prefix_len,
 				FILE *fp, int terminator)
 {
 	struct strbuf sb = STRBUF_INIT;
 
-	name = path_relative(name, len, &sb, prefix, prefix_len);
+	name = relative_path(name, prefix, &sb);
 	write_name_quoted(name, fp, terminator);
 
 	strbuf_release(&sb);
 }
 
-/*
- * Give path as relative to prefix.
- *
- * The strbuf may or may not be used, so do not assume it contains the
- * returned path.
- */
-static const char *path_relative(const char *in, int len,
-				 struct strbuf *sb, const char *prefix,
-				 int prefix_len)
-{
-	int off, i;
-
-	if (len < 0)
-		len = strlen(in);
-	if (prefix_len < 0) {
-		if (prefix)
-			prefix_len = strlen(prefix);
-		else
-			prefix_len = 0;
-	}
-
-	off = 0;
-	i = 0;
-	while (i < prefix_len && i < len && prefix[i] == in[i]) {
-		if (prefix[i] == '/')
-			off = i + 1;
-		i++;
-	}
-	in += off;
-	len -= off;
-
-	if (i >= prefix_len)
-		return in;
-
-	strbuf_reset(sb);
-	strbuf_grow(sb, len);
-
-	while (i < prefix_len) {
-		if (prefix[i] == '/')
-			strbuf_addstr(sb, "../");
-		i++;
-	}
-	strbuf_add(sb, in, len);
-
-	return sb->buf;
-}
-
 /* quote path as relative to the given prefix */
 char *quote_path_relative(const char *in, int len,
 			  struct strbuf *out, const char *prefix)
 {
 	struct strbuf sb = STRBUF_INIT;
-	const char *rel = path_relative(in, len, &sb, prefix, -1);
+	const char *rel = relative_path(in, prefix, &sb);
 	strbuf_reset(out);
 	quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
 	strbuf_release(&sb);
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 04/16] Refactor quote_path_relative, remove unused params
  2013-06-13  9:40 ` Jiang Xin
                     ` (3 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 19:15     ` Junio C Hamano
  2013-06-24 15:21   ` [PATCH v14 05/16] Refactor write_name_quoted_relative, " Jiang Xin
                     ` (11 subsequent siblings)
  16 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

After substitute path_relative() in quote.c with relative_path() from
path.c, parameters (such as len and prefix_len) are obsolete in function
quote_path_relative(). Remove unused parameters and change the order of
parameters for quote_path_relative() function.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/clean.c    | 18 +++++++++---------
 builtin/grep.c     |  5 ++---
 builtin/ls-files.c |  2 +-
 quote.c            |  7 ++-----
 quote.h            |  4 ++--
 wt-status.c        | 17 ++++++++---------
 6 files changed, 24 insertions(+), 29 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 04e39..f77f95 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -56,7 +56,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 	if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
 			!resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
 		if (!quiet) {
-			quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+			quote_path_relative(path->buf, prefix, &quoted);
 			printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
 					quoted.buf);
 		}
@@ -70,7 +70,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 		/* an empty dir could be removed even if it is unreadble */
 		res = dry_run ? 0 : rmdir(path->buf);
 		if (res) {
-			quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+			quote_path_relative(path->buf, prefix, &quoted);
 			warning(_(msg_warn_remove_failed), quoted.buf);
 			*dir_gone = 0;
 		}
@@ -94,7 +94,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 			if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
 				ret = 1;
 			if (gone) {
-				quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+				quote_path_relative(path->buf, prefix, &quoted);
 				string_list_append(&dels, quoted.buf);
 			} else
 				*dir_gone = 0;
@@ -102,10 +102,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 		} else {
 			res = dry_run ? 0 : unlink(path->buf);
 			if (!res) {
-				quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+				quote_path_relative(path->buf, prefix, &quoted);
 				string_list_append(&dels, quoted.buf);
 			} else {
-				quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+				quote_path_relative(path->buf, prefix, &quoted);
 				warning(_(msg_warn_remove_failed), quoted.buf);
 				*dir_gone = 0;
 				ret = 1;
@@ -127,7 +127,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 		if (!res)
 			*dir_gone = 1;
 		else {
-			quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+			quote_path_relative(path->buf, prefix, &quoted);
 			warning(_(msg_warn_remove_failed), quoted.buf);
 			*dir_gone = 0;
 			ret = 1;
@@ -262,7 +262,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 				if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
 					errors++;
 				if (gone && !quiet) {
-					qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
+					qname = quote_path_relative(directory.buf, prefix, &buf);
 					printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
 				}
 			}
@@ -272,11 +272,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 				continue;
 			res = dry_run ? 0 : unlink(ent->name);
 			if (res) {
-				qname = quote_path_relative(ent->name, -1, &buf, prefix);
+				qname = quote_path_relative(ent->name, prefix, &buf);
 				warning(_(msg_warn_remove_failed), qname);
 				errors++;
 			} else if (!quiet) {
-				qname = quote_path_relative(ent->name, -1, &buf, prefix);
+				qname = quote_path_relative(ent->name, prefix, &buf);
 				printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
 			}
 		}
diff --git a/builtin/grep.c b/builtin/grep.c
index 159e65..a419c 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -286,8 +286,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
 	struct strbuf pathbuf = STRBUF_INIT;
 
 	if (opt->relative && opt->prefix_length) {
-		quote_path_relative(filename + tree_name_len, -1, &pathbuf,
-				    opt->prefix);
+		quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
 		strbuf_insert(&pathbuf, 0, filename, tree_name_len);
 	} else {
 		strbuf_addstr(&pathbuf, filename);
@@ -318,7 +317,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
 	struct strbuf buf = STRBUF_INIT;
 
 	if (opt->relative && opt->prefix_length)
-		quote_path_relative(filename, -1, &buf, opt->prefix);
+		quote_path_relative(filename, opt->prefix, &buf);
 	else
 		strbuf_addstr(&buf, filename);
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 3a410c..16d4f 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -391,7 +391,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
 		if (found_dup)
 			continue;
 
-		name = quote_path_relative(pathspec[num], -1, &sb, prefix);
+		name = quote_path_relative(pathspec[num], prefix, &sb);
 		error("pathspec '%s' did not match any file(s) known to git.",
 		      name);
 		errors++;
diff --git a/quote.c b/quote.c
index 64ff3..ebb8 100644
--- a/quote.c
+++ b/quote.c
@@ -325,8 +325,8 @@ void write_name_quoted_relative(const char *name, size_t len,
 }
 
 /* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
-			  struct strbuf *out, const char *prefix)
+char *quote_path_relative(const char *in, const char *prefix,
+			  struct strbuf *out)
 {
 	struct strbuf sb = STRBUF_INIT;
 	const char *rel = relative_path(in, prefix, &sb);
@@ -334,9 +334,6 @@ char *quote_path_relative(const char *in, int len,
 	quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
 	strbuf_release(&sb);
 
-	if (!out->len)
-		strbuf_addstr(out, "./");
-
 	return out->buf;
 }
 
diff --git a/quote.h b/quote.h
index 13315..5610159 100644
--- a/quote.h
+++ b/quote.h
@@ -65,8 +65,8 @@ extern void write_name_quoted_relative(const char *name, size_t len,
 		FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-extern char *quote_path_relative(const char *in, int len,
-			  struct strbuf *out, const char *prefix);
+extern char *quote_path_relative(const char *in, const char *prefix,
+			  struct strbuf *out);
 
 /* quoting as a string literal for other languages */
 extern void perl_quote_print(FILE *stream, const char *src);
diff --git a/wt-status.c b/wt-status.c
index 438a40..85580 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -243,7 +243,7 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
 	struct strbuf onebuf = STRBUF_INIT;
 	const char *one, *how = _("bug");
 
-	one = quote_path(it->string, -1, &onebuf, s->prefix);
+	one = quote_path(it->string, s->prefix, &onebuf);
 	status_printf(s, color(WT_STATUS_HEADER, s), "\t");
 	switch (d->stagemask) {
 	case 1: how = _("both deleted:"); break;
@@ -297,8 +297,8 @@ static void wt_status_print_change_data(struct wt_status *s,
 		    change_type);
 	}
 
-	one = quote_path(one_name, -1, &onebuf, s->prefix);
-	two = quote_path(two_name, -1, &twobuf, s->prefix);
+	one = quote_path(one_name, s->prefix, &onebuf);
+	two = quote_path(two_name, s->prefix, &twobuf);
 
 	status_printf(s, color(WT_STATUS_HEADER, s), "\t");
 	switch (status) {
@@ -706,8 +706,7 @@ static void wt_status_print_other(struct wt_status *s,
 		struct string_list_item *it;
 		const char *path;
 		it = &(l->items[i]);
-		path = quote_path(it->string, strlen(it->string),
-				  &buf, s->prefix);
+		path = quote_path(it->string, s->prefix, &buf);
 		if (column_active(s->colopts)) {
 			string_list_append(&output, path);
 			continue;
@@ -1291,7 +1290,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it,
 	} else {
 		struct strbuf onebuf = STRBUF_INIT;
 		const char *one;
-		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		one = quote_path(it->string, s->prefix, &onebuf);
 		printf(" %s\n", one);
 		strbuf_release(&onebuf);
 	}
@@ -1319,7 +1318,7 @@ static void wt_shortstatus_status(struct string_list_item *it,
 		struct strbuf onebuf = STRBUF_INIT;
 		const char *one;
 		if (d->head_path) {
-			one = quote_path(d->head_path, -1, &onebuf, s->prefix);
+			one = quote_path(d->head_path, s->prefix, &onebuf);
 			if (*one != '"' && strchr(one, ' ') != NULL) {
 				putchar('"');
 				strbuf_addch(&onebuf, '"');
@@ -1328,7 +1327,7 @@ static void wt_shortstatus_status(struct string_list_item *it,
 			printf("%s -> ", one);
 			strbuf_release(&onebuf);
 		}
-		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		one = quote_path(it->string, s->prefix, &onebuf);
 		if (*one != '"' && strchr(one, ' ') != NULL) {
 			putchar('"');
 			strbuf_addch(&onebuf, '"');
@@ -1347,7 +1346,7 @@ static void wt_shortstatus_other(struct string_list_item *it,
 	} else {
 		struct strbuf onebuf = STRBUF_INIT;
 		const char *one;
-		one = quote_path(it->string, -1, &onebuf, s->prefix);
+		one = quote_path(it->string, s->prefix, &onebuf);
 		color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
 		printf(" %s\n", one);
 		strbuf_release(&onebuf);
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 05/16] Refactor write_name_quoted_relative, remove unused params
  2013-06-13  9:40 ` Jiang Xin
                     ` (4 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 04/16] Refactor quote_path_relative, remove unused params Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 19:19     ` Junio C Hamano
  2013-06-24 15:21   ` [PATCH v14 06/16] git-clean: refactor git-clean into two phases Jiang Xin
                     ` (10 subsequent siblings)
  16 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

After substitute path_relative() in quote.c with relative_path() from
path.c, parameters (such as len and prefix_len) are obsolete in function
write_name_quoted_relative(). Remove unused parameters from
write_name_quoted_relative() and related functions.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/ls-files.c | 14 ++++++++------
 quote.c            |  3 +--
 quote.h            |  3 +--
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 16d4f..df83f9 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -46,10 +46,12 @@ static const char *tag_modified = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
-static void write_name(const char* name, size_t len)
+static void write_name(const char *name)
 {
-	write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
-			line_terminator);
+
+	/* turn off prefix, if run with "--full-name" */
+	write_name_quoted_relative(name, prefix_len ? prefix : NULL,
+				   stdout, line_terminator);
 }
 
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
@@ -63,7 +65,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
 		return;
 
 	fputs(tag, stdout);
-	write_name(ent->name, ent->len);
+	write_name(ent->name);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -163,7 +165,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
 		       find_unique_abbrev(ce->sha1,abbrev),
 		       ce_stage(ce));
 	}
-	write_name(ce->name, ce_namelen(ce));
+	write_name(ce->name);
 	if (debug_mode) {
 		struct stat_data *sd = &ce->ce_stat_data;
 
@@ -198,7 +200,7 @@ static void show_ru_info(void)
 			printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
 			       find_unique_abbrev(ui->sha1[i], abbrev),
 			       i + 1);
-			write_name(path, len);
+			write_name(path);
 		}
 	}
 }
diff --git a/quote.c b/quote.c
index ebb8..5c880 100644
--- a/quote.c
+++ b/quote.c
@@ -312,8 +312,7 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
 	fputc(terminator, fp);
 }
 
-void write_name_quoted_relative(const char *name, size_t len,
-				const char *prefix, size_t prefix_len,
+void write_name_quoted_relative(const char *name, const char *prefix,
 				FILE *fp, int terminator)
 {
 	struct strbuf sb = STRBUF_INIT;
diff --git a/quote.h b/quote.h
index 5610159..ed110 100644
--- a/quote.h
+++ b/quote.h
@@ -60,8 +60,7 @@ extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
 extern void write_name_quoted(const char *name, FILE *, int terminator);
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
                                  const char *name, FILE *, int terminator);
-extern void write_name_quoted_relative(const char *name, size_t len,
-		const char *prefix, size_t prefix_len,
+extern void write_name_quoted_relative(const char *name, const char *prefix,
 		FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 06/16] git-clean: refactor git-clean into two phases
  2013-06-13  9:40 ` Jiang Xin
                     ` (5 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 05/16] Refactor write_name_quoted_relative, " Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 19:22     ` Junio C Hamano
  2013-06-24 15:21   ` [PATCH v14 07/16] git-clean: add support for -i/--interactive Jiang Xin
                     ` (9 subsequent siblings)
  16 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Before introducing interactive git-clean, refactor git-clean operations
into two phases:

 * hold cleaning items in del_list,
 * and remove them in a separate loop at the end.

We will introduce interactive git-clean between the two phases. The
interactive git-clean will show what would be done and must confirm
before do real cleaning.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/clean.c | 64 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 45 insertions(+), 19 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index f77f95..77ec1 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -15,6 +15,7 @@
 #include "quote.h"
 
 static int force = -1; /* unset */
+static struct string_list del_list = STRING_LIST_INIT_DUP;
 
 static const char *const builtin_clean_usage[] = {
 	N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
@@ -148,12 +149,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
 	int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
 	int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
-	struct strbuf directory = STRBUF_INIT;
+	struct strbuf abs_path = STRBUF_INIT;
 	struct dir_struct dir;
 	static const char **pathspec;
 	struct strbuf buf = STRBUF_INIT;
 	struct string_list exclude_list = STRING_LIST_INIT_NODUP;
 	struct exclude_list *el;
+	struct string_list_item *item;
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
@@ -223,6 +225,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		int matches = 0;
 		struct cache_entry *ce;
 		struct stat st;
+		const char *rel;
 
 		/*
 		 * Remove the '/' at the end that directory
@@ -242,13 +245,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 				continue; /* Yup, this one exists unmerged */
 		}
 
-		/*
-		 * we might have removed this as part of earlier
-		 * recursive directory removal, so lstat() here could
-		 * fail with ENOENT.
-		 */
 		if (lstat(ent->name, &st))
-			continue;
+			die_errno("Cannot lstat '%s'", ent->name);
 
 		if (pathspec) {
 			memset(seen, 0, argc > 0 ? argc : 1);
@@ -257,33 +255,61 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		}
 
 		if (S_ISDIR(st.st_mode)) {
-			strbuf_addstr(&directory, ent->name);
 			if (remove_directories || (matches == MATCHED_EXACTLY)) {
-				if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
-					errors++;
-				if (gone && !quiet) {
-					qname = quote_path_relative(directory.buf, prefix, &buf);
-					printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
-				}
+				rel = relative_path(ent->name, prefix, &buf);
+				string_list_append(&del_list, rel);
 			}
-			strbuf_reset(&directory);
 		} else {
 			if (pathspec && !matches)
 				continue;
-			res = dry_run ? 0 : unlink(ent->name);
+			rel = relative_path(ent->name, prefix, &buf);
+			string_list_append(&del_list, rel);
+		}
+	}
+
+	/* TODO: do interactive git-clean here, which will modify del_list */
+
+	for_each_string_list_item(item, &del_list) {
+		struct stat st;
+
+		if (prefix)
+			strbuf_addstr(&abs_path, prefix);
+
+		strbuf_addstr(&abs_path, item->string);
+
+		/*
+		 * we might have removed this as part of earlier
+		 * recursive directory removal, so lstat() here could
+		 * fail with ENOENT.
+		 */
+		if (lstat(abs_path.buf, &st))
+			continue;
+
+		if (S_ISDIR(st.st_mode)) {
+			if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
+				errors++;
+			if (gone && !quiet) {
+				qname = quote_path_relative(item->string, NULL, &buf);
+				printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
+			}
+		} else {
+			res = dry_run ? 0 : unlink(abs_path.buf);
 			if (res) {
-				qname = quote_path_relative(ent->name, prefix, &buf);
+				qname = quote_path_relative(item->string, NULL, &buf);
 				warning(_(msg_warn_remove_failed), qname);
 				errors++;
 			} else if (!quiet) {
-				qname = quote_path_relative(ent->name, prefix, &buf);
+				qname = quote_path_relative(item->string, NULL, &buf);
 				printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
 			}
 		}
+		strbuf_reset(&abs_path);
 	}
 	free(seen);
 
-	strbuf_release(&directory);
+	strbuf_release(&abs_path);
+	strbuf_release(&buf);
+	string_list_clear(&del_list, 0);
 	string_list_clear(&exclude_list, 0);
 	return (errors != 0);
 }
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 07/16] git-clean: add support for -i/--interactive
  2013-06-13  9:40 ` Jiang Xin
                     ` (6 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 06/16] git-clean: refactor git-clean into two phases Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 08/16] git-clean: show items of del_list in columns Jiang Xin
                     ` (8 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Show what would be done and the user must confirm before actually
cleaning.

    Would remove ...
    Would remove ...
    Would remove ...

    Remove [y/n]?

Press "y" to start cleaning, and press "n" if you want to abort.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/git-clean.txt | 10 ++++++--
 builtin/clean.c             | 57 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 60 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index bdc3a..186e34 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -34,7 +34,13 @@ OPTIONS
 -f::
 --force::
 	If the Git configuration variable clean.requireForce is not set
-	to false, 'git clean' will refuse to run unless given -f or -n.
+	to false, 'git clean' will refuse to run unless given -f, -n or
+	-i.
+
+-i::
+--interactive::
+	Show what would be done and the user must confirm before actually
+	cleaning.
 
 -n::
 --dry-run::
diff --git a/builtin/clean.c b/builtin/clean.c
index 77ec1..698fb 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -15,10 +15,11 @@
 #include "quote.h"
 
 static int force = -1; /* unset */
+static int interactive;
 static struct string_list del_list = STRING_LIST_INIT_DUP;
 
 static const char *const builtin_clean_usage[] = {
-	N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
+	N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
 	NULL
 };
 
@@ -143,6 +144,50 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 	return ret;
 }
 
+static void interactive_main_loop(void)
+{
+	struct strbuf confirm = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+	const char *qname;
+
+	while (del_list.nr) {
+		putchar('\n');
+		for_each_string_list_item(item, &del_list) {
+			qname = quote_path_relative(item->string, NULL, &buf);
+			printf(_(msg_would_remove), qname);
+		}
+		putchar('\n');
+
+		printf(_("Remove [y/n]? "));
+		if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+			strbuf_trim(&confirm);
+		} else {
+			/* Ctrl-D is the same as "quit" */
+			string_list_clear(&del_list, 0);
+			putchar('\n');
+			printf_ln("Bye.");
+			break;
+		}
+
+		if (confirm.len) {
+			if (!strncasecmp(confirm.buf, "yes", confirm.len)) {
+				break;
+			} else if (!strncasecmp(confirm.buf, "no", confirm.len) ||
+				   !strncasecmp(confirm.buf, "quit", confirm.len)) {
+				string_list_clear(&del_list, 0);
+				printf_ln("Bye.");
+				break;
+			} else {
+				continue;
+			}
+		}
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&confirm);
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
 	int i, res;
@@ -162,6 +207,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		OPT__QUIET(&quiet, N_("do not print names of files removed")),
 		OPT__DRY_RUN(&dry_run, N_("dry run")),
 		OPT__FORCE(&force, N_("force")),
+		OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
 		OPT_BOOLEAN('d', NULL, &remove_directories,
 				N_("remove whole directories")),
 		{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
@@ -188,12 +234,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	if (ignored && ignored_only)
 		die(_("-x and -X cannot be used together"));
 
-	if (!dry_run && !force) {
+	if (!interactive && !dry_run && !force) {
 		if (config_set)
-			die(_("clean.requireForce set to true and neither -n nor -f given; "
+			die(_("clean.requireForce set to true and neither -i, -n nor -f given; "
 				  "refusing to clean"));
 		else
-			die(_("clean.requireForce defaults to true and neither -n nor -f given; "
+			die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; "
 				  "refusing to clean"));
 	}
 
@@ -267,7 +313,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	/* TODO: do interactive git-clean here, which will modify del_list */
+	if (interactive && del_list.nr > 0)
+		interactive_main_loop();
 
 	for_each_string_list_item(item, &del_list) {
 		struct stat st;
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 08/16] git-clean: show items of del_list in columns
  2013-06-13  9:40 ` Jiang Xin
                     ` (7 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 07/16] git-clean: add support for -i/--interactive Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 09/16] git-clean: add colors to interactive git-clean Jiang Xin
                     ` (7 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

When there are lots of items to be cleaned, it is hard to see them all
in one screen. Show them in columns will solve this problem.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Comments-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config.txt |  4 ++++
 builtin/clean.c          | 49 +++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index e203..c415f 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -959,6 +959,10 @@ column.branch::
 	Specify whether to output branch listing in `git branch` in columns.
 	See `column.ui` for details.
 
+column.clean::
+	Specify the layout when list items in `git clean -i`, which always
+	shows files and directories 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.
diff --git a/builtin/clean.c b/builtin/clean.c
index 698fb..75cc6 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -13,10 +13,12 @@
 #include "refs.h"
 #include "string-list.h"
 #include "quote.h"
+#include "column.h"
 
 static int force = -1; /* unset */
 static int interactive;
 static struct string_list del_list = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
 
 static const char *const builtin_clean_usage[] = {
 	N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
@@ -31,8 +33,13 @@ static const char *msg_warn_remove_failed = N_("failed to remove %s");
 
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "clean.requireforce"))
+	if (!prefixcmp(var, "column."))
+		return git_column_config(var, value, "clean", &colopts);
+
+	if (!strcmp(var, "clean.requireforce")) {
 		force = !git_config_bool(var, value);
+		return 0;
+	}
 	return git_default_config(var, value, cb);
 }
 
@@ -144,21 +151,46 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 	return ret;
 }
 
-static void interactive_main_loop(void)
+static void pretty_print_dels(void)
 {
-	struct strbuf confirm = STRBUF_INIT;
-	struct strbuf buf = STRBUF_INIT;
+	struct string_list list = STRING_LIST_INIT_DUP;
 	struct string_list_item *item;
+	struct strbuf buf = STRBUF_INIT;
 	const char *qname;
+	struct column_options copts;
+
+	for_each_string_list_item(item, &del_list) {
+		qname = quote_path_relative(item->string, NULL, &buf);
+		string_list_append(&list, qname);
+	}
+
+	/*
+	 * always enable column display, we only consult column.*
+	 * about layout strategy and stuff
+	 */
+	colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	print_columns(&list, colopts, &copts);
+	putchar('\n');
+	strbuf_release(&buf);
+	string_list_clear(&list, 0);
+}
+
+static void interactive_main_loop(void)
+{
+	struct strbuf confirm = STRBUF_INIT;
 
 	while (del_list.nr) {
 		putchar('\n');
-		for_each_string_list_item(item, &del_list) {
-			qname = quote_path_relative(item->string, NULL, &buf);
-			printf(_(msg_would_remove), qname);
-		}
+		printf_ln(Q_("Would remove the following item:",
+			     "Would remove the following items:",
+			     del_list.nr));
 		putchar('\n');
 
+		pretty_print_dels();
+
 		printf(_("Remove [y/n]? "));
 		if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
 			strbuf_trim(&confirm);
@@ -184,7 +216,6 @@ static void interactive_main_loop(void)
 		}
 	}
 
-	strbuf_release(&buf);
 	strbuf_release(&confirm);
 }
 
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 09/16] git-clean: add colors to interactive git-clean
  2013-06-13  9:40 ` Jiang Xin
                     ` (8 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 08/16] git-clean: show items of del_list in columns Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 10/16] git-clean: use a git-add-interactive compatible UI Jiang Xin
                     ` (6 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Show header, help, error messages, and prompt in colors for interactive
git-clean. Re-use config variables, such as "color.interactive" and
"color.interactive.<slot>" for command `git-add--interactive`.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Comments-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config.txt | 17 +++++------
 builtin/clean.c          | 73 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 81 insertions(+), 9 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index c415f..1b31f 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -879,16 +879,17 @@ The values of these variables may be specified as in color.branch.<slot>.
 
 color.interactive::
 	When set to `always`, always use colors for interactive prompts
-	and displays (such as those used by "git-add --interactive").
-	When false (or `never`), never.  When set to `true` or `auto`, use
-	colors only when the output is to the terminal. Defaults to false.
+	and displays (such as those used by "git-add --interactive" and
+	"git-clean --interactive"). When false (or `never`), never.
+	When set to `true` or `auto`, use colors only when the output is
+	to the terminal. Defaults to false.
 
 color.interactive.<slot>::
-	Use customized color for 'git add --interactive'
-	output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
-	four distinct types of normal output from interactive
-	commands.  The values of these variables may be specified as
-	in color.branch.<slot>.
+	Use customized color for 'git add --interactive' and 'git clean
+	--interactive' output. `<slot>` may be `prompt`, `header`, `help`
+	or `error`, for four distinct types of normal output from
+	interactive commands.  The values of these variables may be
+	specified as in color.branch.<slot>.
 
 color.pager::
 	A boolean to enable/disable colored output when the pager is in
diff --git a/builtin/clean.c b/builtin/clean.c
index 75cc6..dfa99b 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -14,6 +14,7 @@
 #include "string-list.h"
 #include "quote.h"
 #include "column.h"
+#include "color.h"
 
 static int force = -1; /* unset */
 static int interactive;
@@ -31,16 +32,82 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
 static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
 static const char *msg_warn_remove_failed = N_("failed to remove %s");
 
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+	GIT_COLOR_RESET,
+	GIT_COLOR_NORMAL,	/* PLAIN */
+	GIT_COLOR_BOLD_BLUE,	/* PROMPT */
+	GIT_COLOR_BOLD,		/* HEADER */
+	GIT_COLOR_BOLD_RED,	/* HELP */
+	GIT_COLOR_BOLD_RED,	/* ERROR */
+};
+enum color_clean {
+	CLEAN_COLOR_RESET = 0,
+	CLEAN_COLOR_PLAIN = 1,
+	CLEAN_COLOR_PROMPT = 2,
+	CLEAN_COLOR_HEADER = 3,
+	CLEAN_COLOR_HELP = 4,
+	CLEAN_COLOR_ERROR = 5,
+};
+
+static int parse_clean_color_slot(const char *var)
+{
+	if (!strcasecmp(var, "reset"))
+		return CLEAN_COLOR_RESET;
+	if (!strcasecmp(var, "plain"))
+		return CLEAN_COLOR_PLAIN;
+	if (!strcasecmp(var, "prompt"))
+		return CLEAN_COLOR_PROMPT;
+	if (!strcasecmp(var, "header"))
+		return CLEAN_COLOR_HEADER;
+	if (!strcasecmp(var, "help"))
+		return CLEAN_COLOR_HELP;
+	if (!strcasecmp(var, "error"))
+		return CLEAN_COLOR_ERROR;
+	return -1;
+}
+
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
 	if (!prefixcmp(var, "column."))
 		return git_column_config(var, value, "clean", &colopts);
 
+	/* honors the color.interactive* config variables which also
+	   applied in git-add--interactive and git-stash */
+	if (!strcmp(var, "color.interactive")) {
+		clean_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+	if (!prefixcmp(var, "color.interactive.")) {
+		int slot = parse_clean_color_slot(var +
+						  strlen("color.interactive."));
+		if (slot < 0)
+			return 0;
+		if (!value)
+			return config_error_nonbool(var);
+		color_parse(value, var, clean_colors[slot]);
+		return 0;
+	}
+
 	if (!strcmp(var, "clean.requireforce")) {
 		force = !git_config_bool(var, value);
 		return 0;
 	}
-	return git_default_config(var, value, cb);
+
+	/* inspect the color.ui config variable and others */
+	return git_color_default_config(var, value, cb);
+}
+
+static const char *clean_get_color(enum color_clean ix)
+{
+	if (want_color(clean_use_color))
+		return clean_colors[ix];
+	return "";
+}
+
+static void clean_print_color(enum color_clean ix)
+{
+	printf("%s", clean_get_color(ix));
 }
 
 static int exclude_cb(const struct option *opt, const char *arg, int unset)
@@ -184,14 +251,18 @@ static void interactive_main_loop(void)
 
 	while (del_list.nr) {
 		putchar('\n');
+		clean_print_color(CLEAN_COLOR_HEADER);
 		printf_ln(Q_("Would remove the following item:",
 			     "Would remove the following items:",
 			     del_list.nr));
+		clean_print_color(CLEAN_COLOR_RESET);
 		putchar('\n');
 
 		pretty_print_dels();
 
+		clean_print_color(CLEAN_COLOR_PROMPT);
 		printf(_("Remove [y/n]? "));
+		clean_print_color(CLEAN_COLOR_RESET);
 		if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
 			strbuf_trim(&confirm);
 		} else {
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 10/16] git-clean: use a git-add-interactive compatible UI
  2013-06-13  9:40 ` Jiang Xin
                     ` (9 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 09/16] git-clean: add colors to interactive git-clean Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 11/16] git-clean: add filter by pattern interactive action Jiang Xin
                     ` (5 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Rewrite menu using a new method `list_and_choose`, which is borrowed
from `git-add--interactive.perl`. We will use this framework to add
new actions for interactive git-clean later.

Please NOTE:

 * Method `list_and_choose` return an array of integers, and
 * it is up to you to free the allocated memory of the array.
 * The array ends with EOF.
 * If user pressed CTRL-D (i.e. EOF), no selection returned.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/clean.c | 456 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 427 insertions(+), 29 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index dfa99b..df887 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -50,6 +50,36 @@ enum color_clean {
 	CLEAN_COLOR_ERROR = 5,
 };
 
+#define MENU_OPTS_SINGLETON		01
+#define MENU_OPTS_IMMEDIATE		02
+#define MENU_OPTS_LIST_ONLY		04
+
+struct menu_opts {
+	const char *header;
+	const char *prompt;
+	int flags;
+};
+
+#define MENU_RETURN_NO_LOOP		10
+
+struct menu_item {
+	char hotkey;
+	const char *title;
+	int selected;
+	int (*fn)();
+};
+
+enum menu_stuff_type {
+	MENU_STUFF_TYPE_STRING_LIST = 1,
+	MENU_STUFF_TYPE_MENU_ITEM
+};
+
+struct menu_stuff {
+	enum menu_stuff_type type;
+	int nr;
+	void *stuff;
+};
+
 static int parse_clean_color_slot(const char *var)
 {
 	if (!strcasecmp(var, "reset"))
@@ -240,54 +270,422 @@ static void pretty_print_dels(void)
 	copts.indent = "  ";
 	copts.padding = 2;
 	print_columns(&list, colopts, &copts);
-	putchar('\n');
 	strbuf_release(&buf);
 	string_list_clear(&list, 0);
 }
 
-static void interactive_main_loop(void)
+static void pretty_print_menus(struct string_list *menu_list)
+{
+	unsigned int local_colopts = 0;
+	struct column_options copts;
+
+	local_colopts = COL_ENABLED | COL_ROW;
+	memset(&copts, 0, sizeof(copts));
+	copts.indent = "  ";
+	copts.padding = 2;
+	print_columns(menu_list, local_colopts, &copts);
+}
+
+static void prompt_help_cmd(int singleton)
+{
+	clean_print_color(CLEAN_COLOR_HELP);
+	printf_ln(singleton ?
+		  _("Prompt help:\n"
+		    "1          - select a numbered item\n"
+		    "foo        - select item based on unique prefix\n"
+		    "           - (empty) select nothing") :
+		  _("Prompt help:\n"
+		    "1          - select a single item\n"
+		    "3-5        - select a range of items\n"
+		    "2-3,6-9    - select multiple ranges\n"
+		    "foo        - select item based on unique prefix\n"
+		    "-...       - unselect specified items\n"
+		    "*          - choose all items\n"
+		    "           - (empty) finish selecting"));
+	clean_print_color(CLEAN_COLOR_RESET);
+}
+
+/*
+ * display menu stuff with number prefix and hotkey highlight
+ */
+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;
+
+	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 = menu_item->title;
+			if ((*chosen)[i] < 0)
+				(*chosen)[i] = menu_item->selected ? 1 : 0;
+			strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
+			for (; *p; p++) {
+				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));
+					highlighted = 1;
+				} else {
+					strbuf_addch(&menu, *p);
+				}
+			}
+			string_list_append(&menu_list, menu.buf);
+			strbuf_reset(&menu);
+		}
+		break;
+	case MENU_STUFF_TYPE_STRING_LIST:
+		i = 0;
+		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, string_list_item->string);
+			string_list_append(&menu_list, menu.buf);
+			strbuf_reset(&menu);
+			i++;
+		}
+		break;
+	}
+
+	pretty_print_menus(&menu_list);
+
+	strbuf_release(&menu);
+	strbuf_release(&buf);
+	string_list_clear(&menu_list, 0);
+}
+
+/*
+ * Parse user input, and return choice(s) for menu (menu_stuff).
+ *
+ * Input
+ *     (for single choice)
+ *         1          - select a numbered item
+ *         foo        - select item based on menu title
+ *                    - (empty) select nothing
+ *
+ *     (for multiple choice)
+ *         1          - select a single item
+ *         3-5        - select a range of items
+ *         2-3,6-9    - select multiple ranges
+ *         foo        - select item based on menu title
+ *         -...       - unselect specified items
+ *         *          - choose all items
+ *                    - (empty) finish selecting
+ *
+ * The parse result will be saved in array **chosen, and
+ * return number of total selections.
+ */
+static int parse_choice(struct menu_stuff *menu_stuff,
+			int is_single,
+			struct strbuf input,
+			int **chosen)
+{
+	struct strbuf **choice_list, **ptr;
+	struct menu_item *menu_item;
+	struct string_list_item *string_list_item;
+	int nr = 0;
+	int i;
+
+	if (is_single) {
+		choice_list = strbuf_split_max(&input, '\n', 0);
+	} else {
+		char *p = input.buf;
+		do {
+			if (*p == ',')
+				*p = ' ';
+		} while (*p++);
+		choice_list = strbuf_split_max(&input, ' ', 0);
+	}
+
+	for (ptr = choice_list; *ptr; ptr++) {
+		char *p;
+		int choose = 1;
+		int bottom = 0, top = 0;
+		int is_range, is_number;
+
+		strbuf_trim(*ptr);
+		if (!(*ptr)->len)
+			continue;
+
+		/* Input that begins with '-'; unchoose */
+		if (*(*ptr)->buf == '-') {
+			choose = 0;
+			strbuf_remove((*ptr), 0, 1);
+		}
+
+		is_range = 0;
+		is_number = 1;
+		for (p = (*ptr)->buf; *p; p++) {
+			if ('-' == *p) {
+				if (!is_range) {
+					is_range = 1;
+					is_number = 0;
+				} else {
+					is_number = 0;
+					is_range = 0;
+					break;
+				}
+			} else if (!isdigit(*p)) {
+				is_number = 0;
+				is_range = 0;
+				break;
+			}
+		}
+
+		if (is_number) {
+			bottom = atoi((*ptr)->buf);
+			top = bottom;
+		} else if (is_range) {
+			bottom = atoi((*ptr)->buf);
+			/* a range can be specified like 5-7 or 5- */
+			if (!*(strchr((*ptr)->buf, '-') + 1))
+				top = menu_stuff->nr;
+			else
+				top = atoi(strchr((*ptr)->buf, '-') + 1);
+		} else if (!strcmp((*ptr)->buf, "*")) {
+			bottom = 1;
+			top = menu_stuff->nr;
+		} else {
+			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 == menu_item->hotkey) ||
+					    !strcasecmp((*ptr)->buf, menu_item->title)) {
+						bottom = i + 1;
+						top = bottom;
+						break;
+					}
+				}
+				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;
+			}
+		}
+
+		if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
+		    (is_single && bottom != top)) {
+			clean_print_color(CLEAN_COLOR_ERROR);
+			printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+			clean_print_color(CLEAN_COLOR_RESET);
+			continue;
+		}
+
+		for (i = bottom; i <= top; i++)
+			(*chosen)[i-1] = choose;
+	}
+
+	strbuf_list_free(choice_list);
+
+	for (i = 0; i < menu_stuff->nr; i++)
+		nr += (*chosen)[i];
+	return nr;
+}
+
+/*
+ * Implement a git-add-interactive compatible UI, which is borrowed
+ * from git-add--interactive.perl.
+ *
+ * Return value:
+ *
+ *   - Return an array of integers
+ *   - , and it is up to you to free the allocated memory.
+ *   - The array ends with EOF.
+ *   - If user pressed CTRL-D (i.e. EOF), no selection returned.
+ */
+static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
+{
+	struct strbuf choice = STRBUF_INIT;
+	int *chosen, *result;
+	int nr = 0;
+	int eof = 0;
+	int i;
+
+	chosen = xmalloc(sizeof(int) * stuff->nr);
+	/* set chosen as uninitialized */
+	for (i = 0; i < stuff->nr; i++)
+		chosen[i] = -1;
+
+	for (;;) {
+		if (opts->header) {
+			printf_ln("%s%s%s",
+				  clean_get_color(CLEAN_COLOR_HEADER),
+				  _(opts->header),
+				  clean_get_color(CLEAN_COLOR_RESET));
+		}
+
+		/* chosen will be initialized by print_highlight_menu_stuff */
+		print_highlight_menu_stuff(stuff, &chosen);
+
+		if (opts->flags & MENU_OPTS_LIST_ONLY)
+			break;
+
+		if (opts->prompt) {
+			printf("%s%s%s%s",
+			       clean_get_color(CLEAN_COLOR_PROMPT),
+			       _(opts->prompt),
+			       opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
+			       clean_get_color(CLEAN_COLOR_RESET));
+		}
+
+		if (strbuf_getline(&choice, stdin, '\n') != EOF) {
+			strbuf_trim(&choice);
+		} else {
+			eof = 1;
+			break;
+		}
+
+		/* help for prompt */
+		if (!strcmp(choice.buf, "?")) {
+			prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
+			continue;
+		}
+
+		/* for a multiple-choice menu, press ENTER (empty) will return back */
+		if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
+			break;
+
+		nr = parse_choice(stuff,
+				  opts->flags & MENU_OPTS_SINGLETON,
+				  choice,
+				  &chosen);
+
+		if (opts->flags & MENU_OPTS_SINGLETON) {
+			if (nr)
+				break;
+		} else if (opts->flags & MENU_OPTS_IMMEDIATE) {
+			break;
+		}
+	}
+
+	if (eof) {
+		result = xmalloc(sizeof(int));
+		*result = EOF;
+	} else {
+		int j = 0;
+
+		/*
+		 * recalculate nr, if return back from menu directly with
+		 * default selections.
+		 */
+		if (!nr) {
+			for (i = 0; i < stuff->nr; i++)
+				nr += chosen[i];
+		}
+
+		result = xmalloc(sizeof(int) * (nr + 1));
+		memset(result, 0, sizeof(int) * (nr + 1));
+		for (i = 0; i < stuff->nr && j < nr; i++) {
+			if (chosen[i])
+				result[j++] = i;
+		}
+		result[j] = EOF;
+	}
+
+	free(chosen);
+	strbuf_release(&choice);
+	return result;
+}
+
+static int clean_cmd(void)
 {
-	struct strbuf confirm = STRBUF_INIT;
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int quit_cmd(void)
+{
+	string_list_clear(&del_list, 0);
+	printf_ln(_("Bye."));
+	return MENU_RETURN_NO_LOOP;
+}
+
+static int help_cmd(void)
+{
+	clean_print_color(CLEAN_COLOR_HELP);
+	printf_ln(_(
+		    "clean               - start cleaning\n"
+		    "quit                - stop cleaning\n"
+		    "help                - this screen\n"
+		    "?                   - help for prompt selection"
+		   ));
+	clean_print_color(CLEAN_COLOR_RESET);
+	return 0;
+}
 
+static void interactive_main_loop(void)
+{
 	while (del_list.nr) {
-		putchar('\n');
+		struct menu_opts menu_opts;
+		struct menu_stuff menu_stuff;
+		struct menu_item menus[] = {
+			{'c', "clean",			0, clean_cmd},
+			{'q', "quit",			0, quit_cmd},
+			{'h', "help",			0, help_cmd},
+		};
+		int *chosen;
+
+		menu_opts.header = N_("*** Commands ***");
+		menu_opts.prompt = N_("What now");
+		menu_opts.flags = MENU_OPTS_SINGLETON;
+
+		menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
+		menu_stuff.stuff = menus;
+		menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
+
 		clean_print_color(CLEAN_COLOR_HEADER);
 		printf_ln(Q_("Would remove the following item:",
 			     "Would remove the following items:",
 			     del_list.nr));
 		clean_print_color(CLEAN_COLOR_RESET);
-		putchar('\n');
 
 		pretty_print_dels();
 
-		clean_print_color(CLEAN_COLOR_PROMPT);
-		printf(_("Remove [y/n]? "));
-		clean_print_color(CLEAN_COLOR_RESET);
-		if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
-			strbuf_trim(&confirm);
-		} else {
-			/* Ctrl-D is the same as "quit" */
-			string_list_clear(&del_list, 0);
-			putchar('\n');
-			printf_ln("Bye.");
-			break;
-		}
-
-		if (confirm.len) {
-			if (!strncasecmp(confirm.buf, "yes", confirm.len)) {
-				break;
-			} else if (!strncasecmp(confirm.buf, "no", confirm.len) ||
-				   !strncasecmp(confirm.buf, "quit", confirm.len)) {
-				string_list_clear(&del_list, 0);
-				printf_ln("Bye.");
-				break;
-			} else {
+		chosen = list_and_choose(&menu_opts, &menu_stuff);
+
+		if (*chosen != EOF) {
+			int ret;
+			ret = menus[*chosen].fn();
+			if (ret != MENU_RETURN_NO_LOOP) {
+				free(chosen);
+				chosen = NULL;
+				if (!del_list.nr) {
+					clean_print_color(CLEAN_COLOR_ERROR);
+					printf_ln(_("No more files to clean, exiting."));
+					clean_print_color(CLEAN_COLOR_RESET);
+					break;
+				}
 				continue;
 			}
+		} else {
+			quit_cmd();
 		}
-	}
 
-	strbuf_release(&confirm);
+		free(chosen);
+		chosen = NULL;
+		break;
+	}
 }
 
 int cmd_clean(int argc, const char **argv, const char *prefix)
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 11/16] git-clean: add filter by pattern interactive action
  2013-06-13  9:40 ` Jiang Xin
                     ` (10 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 10/16] git-clean: use a git-add-interactive compatible UI Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 12/16] git-clean: add select by numbers " Jiang Xin
                     ` (4 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Add a new action for interactive git-clean: filter by pattern. When the
user chooses this action, user can input space-separated patterns (the
same syntax as gitignore), and each clean candidate that matches with
one of the patterns will be excluded from cleaning. When the user feels
it's OK, presses ENTER and backs to the confirmation dialog.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/clean.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/builtin/clean.c b/builtin/clean.c
index df887..36369 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -614,6 +614,72 @@ static int clean_cmd(void)
 	return MENU_RETURN_NO_LOOP;
 }
 
+static int filter_by_patterns_cmd(void)
+{
+	struct dir_struct dir;
+	struct strbuf confirm = STRBUF_INIT;
+	struct strbuf **ignore_list;
+	struct string_list_item *item;
+	struct exclude_list *el;
+	int changed = -1, i;
+
+	for (;;) {
+		if (!del_list.nr)
+			break;
+
+		if (changed)
+			pretty_print_dels();
+
+		clean_print_color(CLEAN_COLOR_PROMPT);
+		printf(_("Input ignore patterns>> "));
+		clean_print_color(CLEAN_COLOR_RESET);
+		if (strbuf_getline(&confirm, stdin, '\n') != EOF)
+			strbuf_trim(&confirm);
+		else
+			putchar('\n');
+
+		/* quit filter_by_pattern mode if press ENTER or Ctrl-D */
+		if (!confirm.len)
+			break;
+
+		memset(&dir, 0, sizeof(dir));
+		el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
+		ignore_list = strbuf_split_max(&confirm, ' ', 0);
+
+		for (i = 0; ignore_list[i]; i++) {
+			strbuf_trim(ignore_list[i]);
+			if (!ignore_list[i]->len)
+				continue;
+
+			add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
+		}
+
+		changed = 0;
+		for_each_string_list_item(item, &del_list) {
+			int dtype = DT_UNKNOWN;
+
+			if (is_excluded(&dir, item->string, &dtype)) {
+				*item->string = '\0';
+				changed++;
+			}
+		}
+
+		if (changed) {
+			string_list_remove_empty_items(&del_list, 0);
+		} else {
+			clean_print_color(CLEAN_COLOR_ERROR);
+			printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
+			clean_print_color(CLEAN_COLOR_RESET);
+		}
+
+		strbuf_list_free(ignore_list);
+		clear_directory(&dir);
+	}
+
+	strbuf_release(&confirm);
+	return 0;
+}
+
 static int quit_cmd(void)
 {
 	string_list_clear(&del_list, 0);
@@ -626,6 +692,7 @@ static int help_cmd(void)
 	clean_print_color(CLEAN_COLOR_HELP);
 	printf_ln(_(
 		    "clean               - start cleaning\n"
+		    "filter by pattern   - exclude items from deletion\n"
 		    "quit                - stop cleaning\n"
 		    "help                - this screen\n"
 		    "?                   - help for prompt selection"
@@ -641,6 +708,7 @@ static void interactive_main_loop(void)
 		struct menu_stuff menu_stuff;
 		struct menu_item menus[] = {
 			{'c', "clean",			0, clean_cmd},
+			{'f', "filter by pattern",	0, filter_by_patterns_cmd},
 			{'q', "quit",			0, quit_cmd},
 			{'h', "help",			0, help_cmd},
 		};
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 12/16] git-clean: add select by numbers interactive action
  2013-06-13  9:40 ` Jiang Xin
                     ` (11 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 11/16] git-clean: add filter by pattern interactive action Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 13/16] git-clean: add ask each " Jiang Xin
                     ` (3 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Draw a multiple choice menu using `list_and_choose` to select items
to be deleted by numbers.

User can input:

 *  1,5-7 : select 1,5,6,7 items to be deleted
 *  *     : select all items to be deleted
 *  -*    : unselect all, nothing will be deleted
 *        : (empty) finish selecting, and return back to main menu

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/clean.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/builtin/clean.c b/builtin/clean.c
index 36369..643a5e 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -680,6 +680,43 @@ static int filter_by_patterns_cmd(void)
 	return 0;
 }
 
+static int select_by_numbers_cmd(void)
+{
+	struct menu_opts menu_opts;
+	struct menu_stuff menu_stuff;
+	struct string_list_item *items;
+	int *chosen;
+	int i, j;
+
+	menu_opts.header = NULL;
+	menu_opts.prompt = N_("Select items to delete");
+	menu_opts.flags = 0;
+
+	menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
+	menu_stuff.stuff = &del_list;
+	menu_stuff.nr = del_list.nr;
+
+	chosen = list_and_choose(&menu_opts, &menu_stuff);
+	items = del_list.items;
+	for (i = 0, j = 0; i < del_list.nr; i++) {
+		if (i < chosen[j]) {
+			*(items[i].string) = '\0';
+		} else if (i == chosen[j]) {
+			/* delete selected item */
+			j++;
+			continue;
+		} else {
+			/* end of chosen (chosen[j] == EOF), won't delete */
+			*(items[i].string) = '\0';
+		}
+	}
+
+	string_list_remove_empty_items(&del_list, 0);
+
+	free(chosen);
+	return 0;
+}
+
 static int quit_cmd(void)
 {
 	string_list_clear(&del_list, 0);
@@ -693,6 +730,7 @@ static int help_cmd(void)
 	printf_ln(_(
 		    "clean               - start cleaning\n"
 		    "filter by pattern   - exclude items from deletion\n"
+		    "select by numbers   - select items to be deleted by numbers\n"
 		    "quit                - stop cleaning\n"
 		    "help                - this screen\n"
 		    "?                   - help for prompt selection"
@@ -709,6 +747,7 @@ static void interactive_main_loop(void)
 		struct menu_item menus[] = {
 			{'c', "clean",			0, clean_cmd},
 			{'f', "filter by pattern",	0, filter_by_patterns_cmd},
+			{'s', "select by numbers",	0, select_by_numbers_cmd},
 			{'q', "quit",			0, quit_cmd},
 			{'h', "help",			0, help_cmd},
 		};
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 13/16] git-clean: add ask each interactive action
  2013-06-13  9:40 ` Jiang Xin
                     ` (12 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 12/16] git-clean: add select by numbers " Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 14/16] git-clean: add documentation for interactive git-clean Jiang Xin
                     ` (2 subsequent siblings)
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Add a new action for interactive git-clean: ask each. It's just like
the "rm -i" command, that the user must confirm one by one for each
file or directory to be cleaned.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/clean.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/builtin/clean.c b/builtin/clean.c
index 643a5e..bf03a 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -717,6 +717,40 @@ static int select_by_numbers_cmd(void)
 	return 0;
 }
 
+static int ask_each_cmd(void)
+{
+	struct strbuf confirm = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+	const char *qname;
+	int changed = 0, eof = 0;
+
+	for_each_string_list_item(item, &del_list) {
+		/* Ctrl-D should stop removing files */
+		if (!eof) {
+			qname = quote_path_relative(item->string, NULL, &buf);
+			printf(_("remove %s? "), qname);
+			if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+				strbuf_trim(&confirm);
+			} else {
+				putchar('\n');
+				eof = 1;
+			}
+		}
+		if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
+			*item->string = '\0';
+			changed++;
+		}
+	}
+
+	if (changed)
+		string_list_remove_empty_items(&del_list, 0);
+
+	strbuf_release(&buf);
+	strbuf_release(&confirm);
+	return MENU_RETURN_NO_LOOP;
+}
+
 static int quit_cmd(void)
 {
 	string_list_clear(&del_list, 0);
@@ -731,6 +765,7 @@ static int help_cmd(void)
 		    "clean               - start cleaning\n"
 		    "filter by pattern   - exclude items from deletion\n"
 		    "select by numbers   - select items to be deleted by numbers\n"
+		    "ask each            - confirm each deletion (like \"rm -i\")\n"
 		    "quit                - stop cleaning\n"
 		    "help                - this screen\n"
 		    "?                   - help for prompt selection"
@@ -748,6 +783,7 @@ static void interactive_main_loop(void)
 			{'c', "clean",			0, clean_cmd},
 			{'f', "filter by pattern",	0, filter_by_patterns_cmd},
 			{'s', "select by numbers",	0, select_by_numbers_cmd},
+			{'a', "ask each",		0, ask_each_cmd},
 			{'q', "quit",			0, quit_cmd},
 			{'h', "help",			0, help_cmd},
 		};
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 14/16] git-clean: add documentation for interactive git-clean
  2013-06-13  9:40 ` Jiang Xin
                     ` (13 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 13/16] git-clean: add ask each " Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 15/16] test: add t7301 for git-clean--interactive Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows Jiang Xin
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Add new section "Interactive mode" for documentation of interactive
git-clean.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/git-clean.txt | 65 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 186e34..5bf76 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -39,8 +39,8 @@ OPTIONS
 
 -i::
 --interactive::
-	Show what would be done and the user must confirm before actually
-	cleaning.
+	Show what would be done and clean files interactively. See
+	``Interactive mode'' for details.
 
 -n::
 --dry-run::
@@ -69,6 +69,67 @@ OPTIONS
 	Remove only files ignored by Git.  This may be useful to rebuild
 	everything from scratch, but keep manually created files.
 
+Interactive mode
+----------------
+When the command enters the interactive mode, it shows the
+files and directories to be cleaned, and goes into its
+interactive command loop.
+
+The command loop shows the list of subcommands available, and
+gives a prompt "What now> ".  In general, when the prompt ends
+with a single '>', you can pick only one of the choices given
+and type return, like this:
+
+------------
+    *** Commands ***
+        1: clean                2: filter by pattern    3: select by numbers
+        4: ask each             5: quit                 6: help
+    What now> 1
+------------
+
+You also could say `c` or `clean` above as long as the choice is unique.
+
+The main command loop has 6 subcommands.
+
+clean::
+
+   Start cleaning files and directories, and then quit.
+
+filter by pattern::
+
+   This shows the files and directories to be deleted and issues an
+   "Input ignore patterns>>" prompt. You can input space-seperated
+   patterns to exclude files and directories from deletion.
+   E.g. "*.c *.h" will excludes files end with ".c" and ".h" from
+   deletion. When you are satisfied with the filtered result, press
+   ENTER (empty) back to the main menu.
+
+select by numbers::
+
+   This shows the files and directories to be deleted and issues an
+   "Select items to delete>>" prompt. When the prompt ends with double
+   '>>' like this, you can make more than one selection, concatenated
+   with whitespace or comma.  Also you can say ranges.  E.g. "2-5 7,9"
+   to choose 2,3,4,5,7,9 from the list.  If the second number in a
+   range is omitted, all remaining patches are taken.  E.g. "7-" to
+   choose 7,8,9 from the list.  You can say '*' to choose everything.
+   Also when you are satisfied with the filtered result, press ENTER
+   (empty) back to the main menu.
+
+ask each::
+
+  This will start to clean, and you must confirm one by one in order
+  to delete items. Please note that this action is not as efficient
+  as the above two actions.
+
+quit::
+
+  This lets you quit without do cleaning.
+
+help::
+
+  Show brief usage of interactive git-clean.
+
 SEE ALSO
 --------
 linkgit:gitignore[5]
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 15/16] test: add t7301 for git-clean--interactive
  2013-06-13  9:40 ` Jiang Xin
                     ` (14 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 14/16] git-clean: add documentation for interactive git-clean Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 15:21   ` [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows Jiang Xin
  16 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Add test cases for git-clean--interactive.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t7301-clean-interactive.sh | 439 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 439 insertions(+)
 create mode 100755 t/t7301-clean-interactive.sh

diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
new file mode 100755
index 00000..4e605
--- /dev/null
+++ b/t/t7301-clean-interactive.sh
@@ -0,0 +1,439 @@
+#!/bin/sh
+
+test_description='git clean -i basic tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	mkdir -p src &&
+	touch src/part1.c Makefile &&
+	echo build >.gitignore &&
+	echo \*.o >>.gitignore &&
+	git add . &&
+	git commit -m setup &&
+	touch src/part2.c README &&
+	git add .
+
+'
+
+test_expect_success 'git clean -i (clean)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	echo c | git clean -i &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (quit)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	echo q | git clean -i &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (Ctrl+D)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	echo "\04" | git clean -i &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter all)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo f; echo "*"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo f; echo "part3.* *.out"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test ! -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns 2)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo f; echo "* !*.out"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - all)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo "*"; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - none)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 3; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 2)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 2 3; echo 5; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test ! -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 3)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 3,4 5; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 1,3-4; echo 2; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test ! -f docs/manual.txt &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range 2)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo 4- 1; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test ! -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (inverse select)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask - Ctrl+D)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(echo a; echo Y; echo no; echo yes; echo "\04") | \
+	git clean -id &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (filter)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(cd build/ && \
+	 (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \
+	 git clean -id ..) &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (select by name)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(cd build/ && \
+	 (echo s; echo "../docs/"; echo "../src/part3.c"; \
+	  echo "../src/part4.c";  echo; echo c) | \
+	 git clean -id ..) &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test -f a.out &&
+	test ! -f docs/manual.txt &&
+	test ! -f src/part3.c &&
+	test -f src/part3.h &&
+	test ! -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (ask)' '
+
+	mkdir -p build docs &&
+	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+	docs/manual.txt obj.o build/lib.so &&
+	(cd build/ && \
+	 (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+	 git clean -id ..) &&
+	test -f Makefile &&
+	test -f README &&
+	test -f src/part1.c &&
+	test -f src/part2.c &&
+	test ! -f a.out &&
+	test ! -f docs/manual.txt &&
+	test -f src/part3.c &&
+	test ! -f src/part3.h &&
+	test -f src/part4.c &&
+	test -f src/part4.h &&
+	test -f obj.o &&
+	test -f build/lib.so
+
+'
+
+test_done
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows
  2013-06-13  9:40 ` Jiang Xin
                     ` (15 preceding siblings ...)
  2013-06-24 15:21   ` [PATCH v14 15/16] test: add t7301 for git-clean--interactive Jiang Xin
@ 2013-06-24 15:21   ` Jiang Xin
  2013-06-24 18:56     ` Johannes Sixt
  16 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-24 15:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Sixt; +Cc: Git List, Jiang Xin

Add new subcommand "mingw_path" in test-path-utils, so that we can get
the expected absolute paths on Windows. For example:

    COMMAND LINE                        Linux  Windows
    ==================================  =====  ===============
    test-path-utils mingw_path /        /      C:/msysgit
    test-path-utils mingw_path /a/b/    /a/b/  C:/msysgit/a/b/

With this utility, most skipped test cases in t0060 can be runcorrectly
on Windows.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 t/t0060-path-utils.sh | 73 ++++++++++++++++++++++++++++++++++-----------------
 test-path-utils.c     |  5 ++++
 2 files changed, 54 insertions(+), 24 deletions(-)

diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 4deec..dac84 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -7,14 +7,39 @@ test_description='Test various path utilities'
 
 . ./test-lib.sh
 
+mingw_path() {
+	case $2 in
+	NO_MINGW)
+		echo "$1"
+		;;
+	*)
+		test-path-utils mingw_path "$1"
+		;;
+	esac
+}
+
+get_prereq_flag() {
+	case $1 in
+	POSIX)
+		echo $1
+		;;
+	*)
+		;;
+	esac
+}
+
 norm_path() {
-	test_expect_success $3 "normalize path: $1 => $2" \
-	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
+	expected=$(mingw_path "$2" "$3")
+	prereq=$(get_prereq_flag $3)
+	test_expect_success $prereq "normalize path: $1 => $2" \
+	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'"
 }
 
 relative_path() {
-	test_expect_success $4 "relative path: $1 $2 => $3" \
-	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$3'"
+	expected=$(mingw_path "$3" "$4")
+	prereq=$(get_prereq_flag $4)
+	test_expect_success $prereq "relative path: $1 $2 => $3" \
+	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }
 
 # On Windows, we are using MSYS's bash, which mangles the paths.
@@ -39,8 +64,8 @@ ancestor() {
 	 test \"\$actual\" = '$expected'"
 }
 
-# Absolute path tests must be skipped on Windows because due to path mangling
-# the test program never sees a POSIX-style absolute path
+# Some absolute path tests should be skipped on Windows due to path mangling
+# on POSIX-style absolute paths
 case $(uname -s) in
 *MINGW*)
 	;;
@@ -73,10 +98,10 @@ norm_path d1/s1//../s2/../../d2 d2
 norm_path d1/.../d2 d1/.../d2
 norm_path d1/..././../d2 d1/d2
 
-norm_path / / POSIX
-norm_path // / POSIX
-norm_path /// / POSIX
-norm_path /. / POSIX
+norm_path / /
+norm_path // / NO_MINGW
+norm_path /// / NO_MINGW
+norm_path /. /
 norm_path /./ / POSIX
 norm_path /./.. ++failed++ POSIX
 norm_path /../. ++failed++ POSIX
@@ -84,19 +109,19 @@ norm_path /./../.// ++failed++ POSIX
 norm_path /dir/.. / POSIX
 norm_path /dir/sub/../.. / POSIX
 norm_path /dir/sub/../../.. ++failed++ POSIX
-norm_path /dir /dir POSIX
-norm_path /dir// /dir/ POSIX
-norm_path /./dir /dir POSIX
-norm_path /dir/. /dir/ POSIX
-norm_path /dir///./ /dir/ POSIX
-norm_path /dir//sub/.. /dir/ POSIX
-norm_path /dir/sub/../ /dir/ POSIX
+norm_path /dir /dir
+norm_path /dir// /dir/
+norm_path /./dir /dir
+norm_path /dir/. /dir/
+norm_path /dir///./ /dir/
+norm_path /dir//sub/.. /dir/
+norm_path /dir/sub/../ /dir/
 norm_path //dir/sub/../. /dir/ POSIX
-norm_path /dir/s1/../s2/ /dir/s2/ POSIX
-norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
-norm_path /d1/s1//../s2/../../d2 /d2 POSIX
-norm_path /d1/.../d2 /d1/.../d2 POSIX
-norm_path /d1/..././../d2 /d1/d2 POSIX
+norm_path /dir/s1/../s2/ /dir/s2/
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/
+norm_path /d1/s1//../s2/../../d2 /d2
+norm_path /d1/.../d2 /d1/.../d2
+norm_path /d1/..././../d2 /d1/d2
 
 ancestor / / -1
 ancestor /foo / 0
@@ -197,8 +222,8 @@ 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	POSIX
-relative_path /a/b 	"<null>"	/a/b	POSIX
+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>"	./
diff --git a/test-path-utils.c b/test-path-utils.c
index 95ef4..699ef 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -116,6 +116,11 @@ int main(int argc, char **argv)
 		return 0;
 	}
 
+	if (argc == 3 && !strcmp(argv[1], "mingw_path")) {
+		puts(argv[2]);
+		return 0;
+	}
+
 	if (argc == 4 && !strcmp(argv[1], "relative_path")) {
 		struct strbuf sb = STRBUF_INIT;
 		const char *abs, *base, *rel;
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows
  2013-06-24 15:21   ` [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows Jiang Xin
@ 2013-06-24 18:56     ` Johannes Sixt
  2013-06-25  0:40       ` Jiang Xin
  0 siblings, 1 reply; 33+ messages in thread
From: Johannes Sixt @ 2013-06-24 18:56 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Junio C Hamano, Git List

Am 24.06.2013 17:21, schrieb Jiang Xin:
> Add new subcommand "mingw_path" in test-path-utils, so that we can get
> the expected absolute paths on Windows. For example:
> 
>     COMMAND LINE                        Linux  Windows
>     ==================================  =====  ===============
>     test-path-utils mingw_path /        /      C:/msysgit
>     test-path-utils mingw_path /a/b/    /a/b/  C:/msysgit/a/b/
> 
> With this utility, most skipped test cases in t0060 can be runcorrectly
> on Windows.

Thanks for working on this. Did you have an opportunity to test on Windows?

> +mingw_path() {
> +	case $2 in
> +	NO_MINGW)
> +		echo "$1"
> +		;;
> +	*)
> +		test-path-utils mingw_path "$1"
> +		;;
> +	esac
> +}
> +
> +get_prereq_flag() {
> +	case $1 in
> +	POSIX)
> +		echo $1
> +		;;
> +	*)
> +		;;
> +	esac
> +}

It took me a while to notice that the token POSIX serves a different
purpose than NO_MINGW: POSIX is only needed to skip a test, NO_MINGW is
only used to avoid a test-path-utils call. The reason for the complexity
is that you put both tokens in the same parameter position in the
invocations of the test cases.

You use NO_MINGW only twice. I would just skip these two tests, then the
parameter position is reserved for the prerequisite, and things stay simple.

> +
>  norm_path() {
> -	test_expect_success $3 "normalize path: $1 => $2" \
> -	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
> +	expected=$(mingw_path "$2" "$3")
> +	prereq=$(get_prereq_flag $3)
> +	test_expect_success $prereq "normalize path: $1 => $2" \
> +	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'"
>  }
>  
>  relative_path() {
> -	test_expect_success $4 "relative path: $1 $2 => $3" \
> -	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$3'"
> +	expected=$(mingw_path "$3" "$4")
> +	prereq=$(get_prereq_flag $4)
> +	test_expect_success $prereq "relative path: $1 $2 => $3" \
> +	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
>  }
>  
>  # On Windows, we are using MSYS's bash, which mangles the paths.
> @@ -39,8 +64,8 @@ ancestor() {
>  	 test \"\$actual\" = '$expected'"
>  }
>  
> -# Absolute path tests must be skipped on Windows because due to path mangling
> -# the test program never sees a POSIX-style absolute path
> +# Some absolute path tests should be skipped on Windows due to path mangling
> +# on POSIX-style absolute paths
>  case $(uname -s) in
>  *MINGW*)
>  	;;
> @@ -73,10 +98,10 @@ norm_path d1/s1//../s2/../../d2 d2
>  norm_path d1/.../d2 d1/.../d2
>  norm_path d1/..././../d2 d1/d2
>  
> -norm_path / / POSIX
> -norm_path // / POSIX
> -norm_path /// / POSIX
> -norm_path /. / POSIX
> +norm_path / /
> +norm_path // / NO_MINGW
> +norm_path /// / NO_MINGW
> +norm_path /. /
>  norm_path /./ / POSIX
>  norm_path /./.. ++failed++ POSIX
>  norm_path /../. ++failed++ POSIX
> @@ -84,19 +109,19 @@ norm_path /./../.// ++failed++ POSIX
>  norm_path /dir/.. / POSIX
>  norm_path /dir/sub/../.. / POSIX
>  norm_path /dir/sub/../../.. ++failed++ POSIX
> -norm_path /dir /dir POSIX
> -norm_path /dir// /dir/ POSIX
> -norm_path /./dir /dir POSIX
> -norm_path /dir/. /dir/ POSIX
> -norm_path /dir///./ /dir/ POSIX
> -norm_path /dir//sub/.. /dir/ POSIX
> -norm_path /dir/sub/../ /dir/ POSIX
> +norm_path /dir /dir
> +norm_path /dir// /dir/
> +norm_path /./dir /dir
> +norm_path /dir/. /dir/
> +norm_path /dir///./ /dir/
> +norm_path /dir//sub/.. /dir/
> +norm_path /dir/sub/../ /dir/
>  norm_path //dir/sub/../. /dir/ POSIX
> -norm_path /dir/s1/../s2/ /dir/s2/ POSIX
> -norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
> -norm_path /d1/s1//../s2/../../d2 /d2 POSIX
> -norm_path /d1/.../d2 /d1/.../d2 POSIX
> -norm_path /d1/..././../d2 /d1/d2 POSIX
> +norm_path /dir/s1/../s2/ /dir/s2/
> +norm_path /d1/s1///s2/..//../s3/ /d1/s3/
> +norm_path /d1/s1//../s2/../../d2 /d2
> +norm_path /d1/.../d2 /d1/.../d2
> +norm_path /d1/..././../d2 /d1/d2
>  
>  ancestor / / -1
>  ancestor /foo / 0
> @@ -197,8 +222,8 @@ 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	POSIX
> -relative_path /a/b 	"<null>"	/a/b	POSIX
> +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>"	./
> diff --git a/test-path-utils.c b/test-path-utils.c
> index 95ef4..699ef 100644
> --- a/test-path-utils.c
> +++ b/test-path-utils.c
> @@ -116,6 +116,11 @@ int main(int argc, char **argv)
>  		return 0;
>  	}
>  
> +	if (argc == 3 && !strcmp(argv[1], "mingw_path")) {
> +		puts(argv[2]);
> +		return 0;
> +	}
> +
>  	if (argc == 4 && !strcmp(argv[1], "relative_path")) {
>  		struct strbuf sb = STRBUF_INIT;
>  		const char *abs, *base, *rel;
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 01/16] test: add test cases for relative_path
  2013-06-24 15:21   ` [PATCH v14 01/16] test: add test cases for relative_path Jiang Xin
@ 2013-06-24 19:01     ` Junio C Hamano
  2013-06-25  1:06       ` Jiang Xin
  0 siblings, 1 reply; 33+ messages in thread
From: Junio C Hamano @ 2013-06-24 19:01 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Johannes Sixt, Git List, Johannes Sixt

Jiang Xin <worldhello.net@gmail.com> writes:

> diff --git a/test-path-utils.c b/test-path-utils.c
> index 0092cb..dcc530 100644
> --- a/test-path-utils.c
> +++ b/test-path-utils.c
> @@ -28,6 +28,19 @@ static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
>  	return 1;
>  }
>  
> +static void normalize_argv_string(const char **var, const char *input)
> +{
> +	if (!strcmp(input, "<null>"))
> +		*var = NULL;
> +	else if (!strcmp(input, "<empty>"))
> +		*var = "";
> +	else
> +		*var = input;
> +
> +	if (*var && (**var == '<' || **var == '('))
> +		die("Bad value: %s\n", input);
> +}
> +

If you have to munge the input string like this anyway, perhaps you
can work around the command line mangling done by Windows bash
runtime, perhaps add something like:

	if (*input == '_')
        	input++;

and then protecting the path with the underscore, like so?

	relative_path _/a/b/c/	_/a/b/		c/

Wouldn't that let you avoid having to handle "POSIX" prereq for
these paths?

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead
  2013-06-24 15:21   ` [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead Jiang Xin
@ 2013-06-24 19:11     ` Junio C Hamano
  2013-06-25 11:09       ` Jiang Xin
  0 siblings, 1 reply; 33+ messages in thread
From: Junio C Hamano @ 2013-06-24 19:11 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Johannes Sixt, Git List

Jiang Xin <worldhello.net@gmail.com> writes:

> Since there is an enhanced version of relative_path() in path.c,
> remove duplicate counterpart path_relative() in quote.c.

There is no nice comparison chart before and after like you had in
patch 02/16?

>  void write_name_quoted_relative(const char *name, size_t len,
>  				const char *prefix, size_t prefix_len,
>  				FILE *fp, int terminator)
>  {
>  	struct strbuf sb = STRBUF_INIT;
>  
> -	name = path_relative(name, len, &sb, prefix, prefix_len);
> +	name = relative_path(name, prefix, &sb);

Are we sure nobody calls prefix_len pointing into the middle of
string, not at the end of "prefix"?  This is unsafe for such a
caller, and to make sure we catch them, we should remove the
now-unused prefix_len parameter from this function.

>  	write_name_quoted(name, fp, terminator);
>  
>  	strbuf_release(&sb);
>  }
>  
> -/*
> - * Give path as relative to prefix.
> - *
> - * The strbuf may or may not be used, so do not assume it contains the
> - * returned path.
> - */
> -static const char *path_relative(const char *in, int len,
> -				 struct strbuf *sb, const char *prefix,
> -				 int prefix_len)
> -{
> -	int off, i;
> -
> -	if (len < 0)
> -		len = strlen(in);
> -	if (prefix_len < 0) {
> -		if (prefix)
> -			prefix_len = strlen(prefix);
> -		else
> -			prefix_len = 0;
> -	}
> -
> -	off = 0;
> -	i = 0;
> -	while (i < prefix_len && i < len && prefix[i] == in[i]) {
> -		if (prefix[i] == '/')
> -			off = i + 1;
> -		i++;
> -	}
> -	in += off;
> -	len -= off;
> -
> -	if (i >= prefix_len)
> -		return in;
> -
> -	strbuf_reset(sb);
> -	strbuf_grow(sb, len);
> -
> -	while (i < prefix_len) {
> -		if (prefix[i] == '/')
> -			strbuf_addstr(sb, "../");
> -		i++;
> -	}
> -	strbuf_add(sb, in, len);
> -
> -	return sb->buf;
> -}
> -
>  /* quote path as relative to the given prefix */
>  char *quote_path_relative(const char *in, int len,
>  			  struct strbuf *out, const char *prefix)
>  {
>  	struct strbuf sb = STRBUF_INIT;
> -	const char *rel = path_relative(in, len, &sb, prefix, -1);
> +	const char *rel = relative_path(in, prefix, &sb);
>  	strbuf_reset(out);
>  	quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
>  	strbuf_release(&sb);

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 04/16] Refactor quote_path_relative, remove unused params
  2013-06-24 15:21   ` [PATCH v14 04/16] Refactor quote_path_relative, remove unused params Jiang Xin
@ 2013-06-24 19:15     ` Junio C Hamano
  2013-06-25 11:39       ` Jiang Xin
  0 siblings, 1 reply; 33+ messages in thread
From: Junio C Hamano @ 2013-06-24 19:15 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Johannes Sixt, Git List

Jiang Xin <worldhello.net@gmail.com> writes:

> After substitute path_relative() in quote.c with relative_path() from
> path.c, parameters (such as len and prefix_len) are obsolete in function
> quote_path_relative(). Remove unused parameters and change the order of
> parameters for quote_path_relative() function.
> ...
> diff --git a/builtin/clean.c b/builtin/clean.c
> index 04e39..f77f95 100644
> --- a/builtin/clean.c
> +++ b/builtin/clean.c
> @@ -262,7 +262,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
>  				if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
>  					errors++;
>  				if (gone && !quiet) {
> -					qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
> +					qname = quote_path_relative(directory.buf, prefix, &buf);
>  					printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);

This one needed a bit closer look to make sure directory.len is
already pointing at the end of directory.buf (it is) to verify that
this is a safe conversion.  Everywhere else we lost either -1 or
strlen() of the string we feed quote_path() and quote_path_relative()
so they look fine.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 05/16] Refactor write_name_quoted_relative, remove unused params
  2013-06-24 15:21   ` [PATCH v14 05/16] Refactor write_name_quoted_relative, " Jiang Xin
@ 2013-06-24 19:19     ` Junio C Hamano
  2013-06-25 11:51       ` Jiang Xin
  0 siblings, 1 reply; 33+ messages in thread
From: Junio C Hamano @ 2013-06-24 19:19 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Johannes Sixt, Git List

Jiang Xin <worldhello.net@gmail.com> writes:

> -static void write_name(const char* name, size_t len)
> +static void write_name(const char *name)
>  {
> -	write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
> -			line_terminator);
> +
> +	/* turn off prefix, if run with "--full-name" */
> +	write_name_quoted_relative(name, prefix_len ? prefix : NULL,
> +				   stdout, line_terminator);

Hmmm....

Does this mean that ls-files has been broken in 03/16, because
write_name_quoted_relative() was made to ignore prefix_len and
measure the length of prefix with strlen(prefix), and this change
fixes it?

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 06/16] git-clean: refactor git-clean into two phases
  2013-06-24 15:21   ` [PATCH v14 06/16] git-clean: refactor git-clean into two phases Jiang Xin
@ 2013-06-24 19:22     ` Junio C Hamano
  0 siblings, 0 replies; 33+ messages in thread
From: Junio C Hamano @ 2013-06-24 19:22 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Johannes Sixt, Git List

Jiang Xin <worldhello.net@gmail.com> writes:

> Before introducing interactive git-clean, refactor git-clean operations
> into two phases:
>
>  * hold cleaning items in del_list,
>  * and remove them in a separate loop at the end.
>
> We will introduce interactive git-clean between the two phases. The
> interactive git-clean will show what would be done and must confirm
> before do real cleaning.
>
> Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---

Looks sensible.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows
  2013-06-24 18:56     ` Johannes Sixt
@ 2013-06-25  0:40       ` Jiang Xin
  0 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-25  0:40 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Junio C Hamano, Git List

2013/6/25 Johannes Sixt <j6t@kdbg.org>:
> Thanks for working on this. Did you have an opportunity to test on Windows?

Yes, I write this commit on Windows, and have tested it in msysGit.

>> +mingw_path() {
>> +     case $2 in
>> +     NO_MINGW)
>> +             echo "$1"
>> +             ;;
>> +     *)
>> +             test-path-utils mingw_path "$1"
>> +             ;;
>> +     esac
>> +}
>> +
>> +get_prereq_flag() {
>> +     case $1 in
>> +     POSIX)
>> +             echo $1
>> +             ;;
>> +     *)
>> +             ;;
>> +     esac
>> +}
>
> It took me a while to notice that the token POSIX serves a different
> purpose than NO_MINGW: POSIX is only needed to skip a test, NO_MINGW is
> only used to avoid a test-path-utils call. The reason for the complexity
> is that you put both tokens in the same parameter position in the
> invocations of the test cases.
>
> You use NO_MINGW only twice. I would just skip these two tests, then the
> parameter position is reserved for the prerequisite, and things stay simple.
>

I agree. I rewrite the commit log and remove NO_MINGW like below:

-- 8-< --

>From 4875562a849422a3f31d6ccf3c0eb8a6693adf0e Mon Sep 17 00:00:00 2001
Message-Id: <4875562a849422a3f31d6ccf3c0eb8a6693adf0e.1372120737.git.worldhello.net@gmail.com>
From: Jiang Xin <worldhello.net@gmail.com>
Date: Mon, 24 Jun 2013 21:35:23 +0800
Subject: [PATCH] test: run testcases with POSIX absolute paths on Windows

Some test cases are skipped on Windows by marking with POSIX prereq.
This is because arguments look like absolute paths (such as /a/b)
for regular Windows programs (*.exe executables, no bash scripts)
are changed to Windows paths (like C:/msysgit/a/b).

There is no cygpath nor equivalent on msysGit, but it is easy to
write one. New subcommand "mingw_path" is added in test-path-utils,
so that we can get the expected absolute paths on Windows. E.g.

    COMMAND LINE                        Linux output  Windows output
    ==================================  ============  ===============
    test-path-utils mingw_path /        /             C:/msysgit
    test-path-utils mingw_path /a/b/    /a/b/         C:/msysgit/a/b/

With this utility, most skipped test cases in t0060 can be turned on
to be tested correctly on Windows.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
---
 t/t0060-path-utils.sh | 42 ++++++++++++++++++++++--------------------
 test-path-utils.c     |  5 +++++
 2 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 4deec..d5f35 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -8,13 +8,15 @@ test_description='Test various path utilities'
 . ./test-lib.sh

 norm_path() {
+	expected=$(test-path-utils mingw_path "$2")
 	test_expect_success $3 "normalize path: $1 => $2" \
-	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
+	"test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'"
 }

 relative_path() {
+	expected=$(test-path-utils mingw_path "$3")
 	test_expect_success $4 "relative path: $1 $2 => $3" \
-	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$3'"
+	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }

 # On Windows, we are using MSYS's bash, which mangles the paths.
@@ -39,8 +41,8 @@ ancestor() {
 	 test \"\$actual\" = '$expected'"
 }

-# Absolute path tests must be skipped on Windows because due to path mangling
-# the test program never sees a POSIX-style absolute path
+# Some absolute path tests should be skipped on Windows due to path mangling
+# on POSIX-style absolute paths
 case $(uname -s) in
 *MINGW*)
 	;;
@@ -73,10 +75,10 @@ norm_path d1/s1//../s2/../../d2 d2
 norm_path d1/.../d2 d1/.../d2
 norm_path d1/..././../d2 d1/d2

-norm_path / / POSIX
+norm_path / /
 norm_path // / POSIX
 norm_path /// / POSIX
-norm_path /. / POSIX
+norm_path /. /
 norm_path /./ / POSIX
 norm_path /./.. ++failed++ POSIX
 norm_path /../. ++failed++ POSIX
@@ -84,19 +86,19 @@ norm_path /./../.// ++failed++ POSIX
 norm_path /dir/.. / POSIX
 norm_path /dir/sub/../.. / POSIX
 norm_path /dir/sub/../../.. ++failed++ POSIX
-norm_path /dir /dir POSIX
-norm_path /dir// /dir/ POSIX
-norm_path /./dir /dir POSIX
-norm_path /dir/. /dir/ POSIX
-norm_path /dir///./ /dir/ POSIX
-norm_path /dir//sub/.. /dir/ POSIX
-norm_path /dir/sub/../ /dir/ POSIX
+norm_path /dir /dir
+norm_path /dir// /dir/
+norm_path /./dir /dir
+norm_path /dir/. /dir/
+norm_path /dir///./ /dir/
+norm_path /dir//sub/.. /dir/
+norm_path /dir/sub/../ /dir/
 norm_path //dir/sub/../. /dir/ POSIX
-norm_path /dir/s1/../s2/ /dir/s2/ POSIX
-norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
-norm_path /d1/s1//../s2/../../d2 /d2 POSIX
-norm_path /d1/.../d2 /d1/.../d2 POSIX
-norm_path /d1/..././../d2 /d1/d2 POSIX
+norm_path /dir/s1/../s2/ /dir/s2/
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/
+norm_path /d1/s1//../s2/../../d2 /d2
+norm_path /d1/.../d2 /d1/.../d2
+norm_path /d1/..././../d2 /d1/d2

 ancestor / / -1
 ancestor /foo / 0
@@ -197,8 +199,8 @@ 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	POSIX
-relative_path /a/b 	"<null>"	/a/b	POSIX
+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>"	./
diff --git a/test-path-utils.c b/test-path-utils.c
index 95ef4..699ef 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -116,6 +116,11 @@ int main(int argc, char **argv)
 		return 0;
 	}

+	if (argc == 3 && !strcmp(argv[1], "mingw_path")) {
+		puts(argv[2]);
+		return 0;
+	}
+
 	if (argc == 4 && !strcmp(argv[1], "relative_path")) {
 		struct strbuf sb = STRBUF_INIT;
 		const char *abs, *base, *rel;
-- 
1.8.3.1.756.g41beab0

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 01/16] test: add test cases for relative_path
  2013-06-24 19:01     ` Junio C Hamano
@ 2013-06-25  1:06       ` Jiang Xin
  2013-06-25  4:57         ` Junio C Hamano
  0 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-25  1:06 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Sixt, Git List, Johannes Sixt

2013/6/25 Junio C Hamano <gitster@pobox.com>:
> Jiang Xin <worldhello.net@gmail.com> writes:
>
>> diff --git a/test-path-utils.c b/test-path-utils.c
>> index 0092cb..dcc530 100644
>> --- a/test-path-utils.c
>> +++ b/test-path-utils.c
>> @@ -28,6 +28,19 @@ static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
>>       return 1;
>>  }
>>
>> +static void normalize_argv_string(const char **var, const char *input)
>> +{
>> +     if (!strcmp(input, "<null>"))
>> +             *var = NULL;
>> +     else if (!strcmp(input, "<empty>"))
>> +             *var = "";
>> +     else
>> +             *var = input;
>> +
>> +     if (*var && (**var == '<' || **var == '('))
>> +             die("Bad value: %s\n", input);
>> +}
>> +
>
> If you have to munge the input string like this anyway, perhaps you
> can work around the command line mangling done by Windows bash
> runtime, perhaps add something like:
>
>         if (*input == '_')
>                 input++;
>
> and then protecting the path with the underscore, like so?
>
>         relative_path _/a/b/c/  _/a/b/          c/
>
> Wouldn't that let you avoid having to handle "POSIX" prereq for
> these paths?
>

In order to test NULL pointer in t/t0060, I have to write normalize_argv_string
to convert "<null>" to NULL. So that I can write test case in
t/t0060-path-utils.sh like this:

    relative_path "<null>"  "<null>"        ./

And for the same reason, output would be "(null)" for NULL pointer.
Use "(null)" not "<null>" for output, because I want to make sure the
conversion must happen for input and output.

I found it would be nice to wrap input empty string as "<empty>" and
wrap output of empty string as "(empty)" for the readability of both
input and output of test cases.

Add prefix _ to workaround the absolute path rewritten issue in
msysGit is interesting, but these test cases have already been
tested in Linux, right?  Patch 16/16 turns on most of the test cases
which could only run under POSIX previously, and test these test
cases in Windows way.

-- 
Jiang Xin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 01/16] test: add test cases for relative_path
  2013-06-25  1:06       ` Jiang Xin
@ 2013-06-25  4:57         ` Junio C Hamano
  2013-06-25  7:02           ` Jiang Xin
  0 siblings, 1 reply; 33+ messages in thread
From: Junio C Hamano @ 2013-06-25  4:57 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Johannes Sixt, Git List, Johannes Sixt

Jiang Xin <worldhello.net@gmail.com> writes:

> In order to test NULL pointer in t/t0060, I have to write normalize_argv_string
> to convert "<null>" to NULL....

Yes, null and empty I already understand.

> Add prefix _ to workaround the absolute path rewritten issue in
> msysGit is interesting, but these test cases have already been
> tested in Linux, right?

The most important thing is what we want to test in these tests.
The special test program is to try running the underlying
relative_path() by driving it directly, but the only real caller is
in setup_work_tree(), where two return values from real_path() is
compared.  On POSIX systems, we know we are feeding paths that both
begin with /.  Now, on Windows systems, what do we get back from
real_path()?  C:\git\Documentation?  /git/Documentation?  If the
former, then writing /a/b/c in the test input and letting Windows
bash convert it to C:\a\b\c before it calls the test-path-utils
would feed what we expect to see in the real caller, which would be
a more meaningful test than what I suggested (i.e. to feed _/a/b/c
to test-path-utils (unmolested by Windows bash), strip _ to feed
/a/b/c to underlying relative_path()).

Unlike relative_path() used in setup.c:setup_work_tree(), for
path_relative() from quote.c, IIRC, the expected inputs are both
full pathnames within the working tree.  A typical question the
callers to this function asks is like "The current directory
obtained from prefix is the Documentation/ directory and we need to
show that compat/mkdir.c is modified, relative to the current
directory.  ../compat/mkdir.c is what I want to show."  So in that
sense, it does not matter if /a/b/c is given as /a/b/c or C:\a\b\c
as we do not care the leading common part (either / or C:\) very
much.  On the other hand, the test vector you preoared in the first
test that all begin with / may not be very useful to make sure that
the function behaves the same way before and after your rewrite.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 01/16] test: add test cases for relative_path
  2013-06-25  4:57         ` Junio C Hamano
@ 2013-06-25  7:02           ` Jiang Xin
  0 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-25  7:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Sixt, Git List, Johannes Sixt

2013/6/25 Junio C Hamano <gitster@pobox.com>:
>> Add prefix _ to workaround the absolute path rewritten issue in
>> msysGit is interesting, but these test cases have already been
>> tested in Linux, right?
>
> The most important thing is what we want to test in these tests.
> The special test program is to try running the underlying
> relative_path() by driving it directly, but the only real caller is
> in setup_work_tree(), where two return values from real_path() is
> compared.  On POSIX systems, we know we are feeding paths that both
> begin with /.  Now, on Windows systems, what do we get back from
> real_path()?  C:\git\Documentation?  /git/Documentation?
>

Confirm that on Windows real_path() returns absolute path in
Windows style, such as: C:/msysgit/git/.git

> Unlike relative_path() used in setup.c:setup_work_tree(), for
> path_relative() from quote.c, IIRC, the expected inputs are both
> full pathnames within the working tree.  A typical question the
> callers to this function asks is like "The current directory
> obtained from prefix is the Documentation/ directory and we need to
> show that compat/mkdir.c is modified, relative to the current
> directory.  ../compat/mkdir.c is what I want to show."  So in that
> sense, it does not matter if /a/b/c is given as /a/b/c or C:\a\b\c
> as we do not care the leading common part (either / or C:\) very
> much.  On the other hand, the test vector you preoared in the first
> test that all begin with / may not be very useful to make sure that
> the function behaves the same way before and after your rewrite.
>

Yes, I should add more test cases without the leading '/'.

-- 
Jiang Xin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead
  2013-06-24 19:11     ` Junio C Hamano
@ 2013-06-25 11:09       ` Jiang Xin
  2013-06-25 19:23         ` Junio C Hamano
  0 siblings, 1 reply; 33+ messages in thread
From: Jiang Xin @ 2013-06-25 11:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Sixt, Git List

2013/6/25 Junio C Hamano <gitster@pobox.com>:
> Jiang Xin <worldhello.net@gmail.com> writes:
>
>> Since there is an enhanced version of relative_path() in path.c,
>> remove duplicate counterpart path_relative() in quote.c.
>
> There is no nice comparison chart before and after like you had in
> patch 02/16?
>

You mean drawing a table to compare output of path_relative and
relative_path?

I will rewrite the commit log for patch 03/16 like the following. Need
to polish spellings and grammars.

quote.c: substitute path_relative with relative_path

Substitute the function path_relative in quote.c with the function
relative_path. Function relative_path can be treated as an enhanced
and robust version of path_relative.

Outputs of path_relative and it's replacement (relative_path) are the
same for the following cases:

    path      prefix     output of path_relative  output of relative_path
    ========  =========  =======================  =======================
    /a/b/c/   /a/b/      c/                       c/
    /a/b/c    /a/b/      c                        c
    /a/       /a/b/      ../                      ../
    /         /a/b/      ../../                   ../../
    /a/c      /a/b/      ../c                     ../c
    /x/y      /a/b/      ../../x/y                ../../x/y
    a/b/c/    a/b/       c/                       c/
    a/        a/b/       ../                      ../
    x/y       a/b/      ../../x/y                 ../../x/y
    /a/b      (empty)    /a/b                     /a/b
    /a/b      (null)     /a/b                     /a/b
    a/b       (empty)    a/b                      a/b
    a/b       (null)     a/b                      a/b

But if both of the path and the prefix are the same, or the returned
relative path should be the current directory, the outputs of both
functions are different. Function relative_path returns "./", while
function path_relative returns empty string.

    path      prefix     output of path_relative  output of relative_path
    ========  =========  =======================  =======================
    /a/b/     /a/b/      (empty)                  ./
    a/b/      a/b/       (empty)                  ./
    (empty)   (null)     (empty)                  ./
    (empty)   (empty)    (empty)                  ./

But not panic, the callers of path_relative can handle such cases, or
never encounter this issue at all. E.g.

 * In function quote_path_relative, if the output of path_relative is
   empty, append "./" to it, like:

       if (!out->len)
           strbuf_addstr(out, "./");

 * Another caller is write_name_quoted_relative, which is only used by
   builtin/ls-files.c. git-ls-files only show files, so path of files
   will never be identical with the prefix of a directory.

The following differences show that path_relative can not handle
extra slashes properly.

    path      prefix     output of path_relative  output of relative_path
    ========  =========  =======================  =======================
    /a//b//c/ //a/b//    ../../../../a//b//c/     c/
    a/b//c    a//b       ../b//c                  c

And if prefix has no trailing slash, path_relative can not work properly
either. But since prefix always has a trailing slash, so it's not a
problem.

    path      prefix     output of path_relative  output of relative_path
    ========  =========  =======================  =======================
    /a/b/c/   /a/b       b/c/                     c/
    /a/b      /a/b       b                        ./
    /a/b/     /a/b       b/                       ./
    /a        /a/b/      ../../a                  ../
    a/b/c/    a/b        b/c/                     c/
    a/b/      a/b        b/                       ./
    a         a/b        ../a                     ../
    x/y       a/b/       ../x/y                   ../../x/y
    a/c       a/b        c                        ../c
    /a/       /a/b       (empty)                  ../
    (empty)   /a/b       ../../                   ./


>>  void write_name_quoted_relative(const char *name, size_t len,
>>                               const char *prefix, size_t prefix_len,
>>                               FILE *fp, int terminator)
>>  {
>>       struct strbuf sb = STRBUF_INIT;
>>
>> -     name = path_relative(name, len, &sb, prefix, prefix_len);
>> +     name = relative_path(name, prefix, &sb);
>
> Are we sure nobody calls prefix_len pointing into the middle of
> string, not at the end of "prefix"?  This is unsafe for such a
> caller, and to make sure we catch them, we should remove the
> now-unused prefix_len parameter from this function.
>

Next two commits will remove the unused parameters, and
make this series of patches easy to review. But indeed this
commit has flaws, next two commits are fixes. Should I squash
them back?


-- 
Jiang Xin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 04/16] Refactor quote_path_relative, remove unused params
  2013-06-24 19:15     ` Junio C Hamano
@ 2013-06-25 11:39       ` Jiang Xin
  0 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-25 11:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Sixt, Git List

2013/6/25 Junio C Hamano <gitster@pobox.com>:
> Jiang Xin <worldhello.net@gmail.com> writes:
>
>> After substitute path_relative() in quote.c with relative_path() from
>> path.c, parameters (such as len and prefix_len) are obsolete in function
>> quote_path_relative(). Remove unused parameters and change the order of
>> parameters for quote_path_relative() function.
>> ...
>> diff --git a/builtin/clean.c b/builtin/clean.c
>> index 04e39..f77f95 100644
>> --- a/builtin/clean.c
>> +++ b/builtin/clean.c
>> @@ -262,7 +262,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
>>                               if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
>>                                       errors++;
>>                               if (gone && !quiet) {
>> -                                     qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
>> +                                     qname = quote_path_relative(directory.buf, prefix, &buf);
>>                                       printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
>
> This one needed a bit closer look to make sure directory.len is
> already pointing at the end of directory.buf (it is) to verify that
> this is a safe conversion.  Everywhere else we lost either -1 or
> strlen() of the string we feed quote_path() and quote_path_relative()
> so they look fine.

When tracking in "remove_dirs(&directory, prefix, rm_flags, dry_run,
quiet, &gone)",
there are some suspect strbuf_setlens, like:

        strbuf_setlen(path, len);
        strbuf_addstr(path, e->d_name);

and at the end of remove_dirs, there is:

        strbuf_setlen(path, original_len);

So both the but and len will be restored in the end, and directory.len always
points to strlen of directory.buf.

So it's safe for the following refactor:

-                                     qname =
quote_path_relative(directory.buf, directory.len, &buf, prefix);
+                                     qname =
quote_path_relative(directory.buf, prefix, &buf);



-- 
Jiang Xin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 05/16] Refactor write_name_quoted_relative, remove unused params
  2013-06-24 19:19     ` Junio C Hamano
@ 2013-06-25 11:51       ` Jiang Xin
  0 siblings, 0 replies; 33+ messages in thread
From: Jiang Xin @ 2013-06-25 11:51 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Sixt, Git List

2013/6/25 Junio C Hamano <gitster@pobox.com>:
> Jiang Xin <worldhello.net@gmail.com> writes:
>
>> -static void write_name(const char* name, size_t len)
>> +static void write_name(const char *name)
>>  {
>> -     write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
>> -                     line_terminator);
>> +
>> +     /* turn off prefix, if run with "--full-name" */
>> +     write_name_quoted_relative(name, prefix_len ? prefix : NULL,
>> +                                stdout, line_terminator);
>
> Hmmm....
>
> Does this mean that ls-files has been broken in 03/16, because
> write_name_quoted_relative() was made to ignore prefix_len and
> measure the length of prefix with strlen(prefix), and this change
> fixes it?

I will rewrite and move this chunk to patch 03/16, but leave others
in this patch for clearity.


-- 
蒋鑫

北京群英汇信息技术有限公司
邮件: worldhello.net@gmail.com
网址: http://www.ossxp.com/
博客: http://www.worldhello.net/
微博: http://weibo.com/gotgit/
电话: 18601196889

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead
  2013-06-25 11:09       ` Jiang Xin
@ 2013-06-25 19:23         ` Junio C Hamano
  0 siblings, 0 replies; 33+ messages in thread
From: Junio C Hamano @ 2013-06-25 19:23 UTC (permalink / raw)
  To: Jiang Xin; +Cc: Johannes Sixt, Git List

Jiang Xin <worldhello.net@gmail.com> writes:

> 2013/6/25 Junio C Hamano <gitster@pobox.com>:
>> Jiang Xin <worldhello.net@gmail.com> writes:
>>
>>> Since there is an enhanced version of relative_path() in path.c,
>>> remove duplicate counterpart path_relative() in quote.c.
>>
>> There is no nice comparison chart before and after like you had in
>> patch 02/16?
>>
>
> You mean drawing a table to compare output of path_relative and
> relative_path?

I was interested in comparison between the behaviour the current
callers of path_relative() gets, and the behaviour the same callers
get from the consolidated helper function after your patch.

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2013-06-25 19:23 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-13  8:23 [PATCH jx/clean-interactive] t0060: skip a few relative_path tests on Windows Johannes Sixt
2013-06-13  9:40 ` Jiang Xin
2013-06-24 15:21   ` [PATCH v14 00/16] Interactive git-clean Jiang Xin
2013-06-24 15:21   ` [PATCH v14 01/16] test: add test cases for relative_path Jiang Xin
2013-06-24 19:01     ` Junio C Hamano
2013-06-25  1:06       ` Jiang Xin
2013-06-25  4:57         ` Junio C Hamano
2013-06-25  7:02           ` Jiang Xin
2013-06-24 15:21   ` [PATCH v14 02/16] path.c: refactor relative_path(), not only strip prefix Jiang Xin
2013-06-24 15:21   ` [PATCH v14 03/16] quote.c: remove path_relative, use relative_path instead Jiang Xin
2013-06-24 19:11     ` Junio C Hamano
2013-06-25 11:09       ` Jiang Xin
2013-06-25 19:23         ` Junio C Hamano
2013-06-24 15:21   ` [PATCH v14 04/16] Refactor quote_path_relative, remove unused params Jiang Xin
2013-06-24 19:15     ` Junio C Hamano
2013-06-25 11:39       ` Jiang Xin
2013-06-24 15:21   ` [PATCH v14 05/16] Refactor write_name_quoted_relative, " Jiang Xin
2013-06-24 19:19     ` Junio C Hamano
2013-06-25 11:51       ` Jiang Xin
2013-06-24 15:21   ` [PATCH v14 06/16] git-clean: refactor git-clean into two phases Jiang Xin
2013-06-24 19:22     ` Junio C Hamano
2013-06-24 15:21   ` [PATCH v14 07/16] git-clean: add support for -i/--interactive Jiang Xin
2013-06-24 15:21   ` [PATCH v14 08/16] git-clean: show items of del_list in columns Jiang Xin
2013-06-24 15:21   ` [PATCH v14 09/16] git-clean: add colors to interactive git-clean Jiang Xin
2013-06-24 15:21   ` [PATCH v14 10/16] git-clean: use a git-add-interactive compatible UI Jiang Xin
2013-06-24 15:21   ` [PATCH v14 11/16] git-clean: add filter by pattern interactive action Jiang Xin
2013-06-24 15:21   ` [PATCH v14 12/16] git-clean: add select by numbers " Jiang Xin
2013-06-24 15:21   ` [PATCH v14 13/16] git-clean: add ask each " Jiang Xin
2013-06-24 15:21   ` [PATCH v14 14/16] git-clean: add documentation for interactive git-clean Jiang Xin
2013-06-24 15:21   ` [PATCH v14 15/16] test: add t7301 for git-clean--interactive Jiang Xin
2013-06-24 15:21   ` [PATCH v14 16/16] test: run testcases with POSIX absolute paths on Windows Jiang Xin
2013-06-24 18:56     ` Johannes Sixt
2013-06-25  0:40       ` Jiang Xin

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).