Re: [PATCH v9 1/9] git-clean: refactor git-clean into two phases

2013-05-15 Thread Junio C Hamano
Jiang Xin worldhello@gmail.com writes:

 2013/5/15 Junio C Hamano gits...@pobox.com:
 @@ -242,11 +287,6 @@ 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;

 I am guessing that the reason why you removed the comment is because
 during this phase there is no way we might have removed.  But if
 that is the case, does it still make sense to run lstat() and ignore
 errors from the call?


 Run lstat() here is necessary, because we need to check whether
 ent-name points to a file or a directory. If ent points to a directory,
 only add to del_list when user provides '-x' option to git-clean.

Sorry, but that was not the question; we can see st is used
immediately below so somebody needs to fill it.

I was pointing out that the lstat() is expected to fail with ENOENT
but it is not an error worth reporting justification the original
code had to silently ignore an error, because you no longer remove
anything immediately in this part of the code.  Is if () continue
still valid thing to do here, not if () die()?
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v9 1/9] git-clean: refactor git-clean into two phases

2013-05-15 Thread Jiang Xin
2013/5/15 Junio C Hamano gits...@pobox.com:
 Jiang Xin worldhello@gmail.com writes:

 2013/5/15 Junio C Hamano gits...@pobox.com:
 @@ -242,11 +287,6 @@ 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;

 I am guessing that the reason why you removed the comment is because
 during this phase there is no way we might have removed.  But if
 that is the case, does it still make sense to run lstat() and ignore
 errors from the call?


 Run lstat() here is necessary, because we need to check whether
 ent-name points to a file or a directory. If ent points to a directory,
 only add to del_list when user provides '-x' option to git-clean.

 Sorry, but that was not the question; we can see st is used
 immediately below so somebody needs to fill it.

 I was pointing out that the lstat() is expected to fail with ENOENT
 but it is not an error worth reporting justification the original
 code had to silently ignore an error, because you no longer remove
 anything immediately in this part of the code.  Is if () continue
 still valid thing to do here, not if () die()?

I'm clear, it could be:

if (lstat(ent-name, st))
die_errno(Cannot lstat '%s', ent-name);


-- 
Jiang Xin
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v9 1/9] git-clean: refactor git-clean into two phases

2013-05-14 Thread 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@gmail.com
---
 builtin/clean.c | 103 ++--
 1 file changed, 85 insertions(+), 18 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 04e39..ccd4 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...),
@@ -142,18 +143,61 @@ static int remove_dirs(struct strbuf *path, const char 
*prefix, int force_flag,
return ret;
 }
 
+/*
+ * Give path as relative to prefix.
+ *
+ * This function is a combination of path_relative (in quote.c) and
+ * relative_path (in path.c)
+ */
+static const char *path_relative(const char *in, const char *prefix)
+{
+   static char buf[PATH_MAX + 1];
+   int off, i;
+   int len, prefix_len;
+
+   len = strlen(in);
+   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;
+
+   buf[0] = '\0';
+   while (i  prefix_len) {
+   if (prefix[i] == '/')
+   strcat(buf, ../);
+   i++;
+   }
+   strcat(buf, in);
+
+   return buf;
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
int i, res;
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 +267,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,11 +287,6 @@ 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;
 
@@ -257,33 +297,60 @@ 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, directory.len, buf, prefix);
-   printf(dry_run ? _(msg_would_remove) : 
_(msg_remove), qname);
-   }
+   rel = path_relative(ent-name, prefix);
+   string_list_append(del_list, rel);
}
-   strbuf_reset(directory);
} else {
if (pathspec  !matches)
continue;
-   res = dry_run ? 0 : unlink(ent-name);
+   rel = path_relative(ent-name, prefix);
+   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) {
+   

Re: [PATCH v9 1/9] git-clean: refactor git-clean into two phases

2013-05-14 Thread Jiang Xin
2013/5/15 Junio C Hamano gits...@pobox.com:
 @@ -242,11 +287,6 @@ 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;

 I am guessing that the reason why you removed the comment is because
 during this phase there is no way we might have removed.  But if
 that is the case, does it still make sense to run lstat() and ignore
 errors from the call?


Run lstat() here is necessary, because we need to check whether
ent-name points to a file or a directory. If ent points to a directory,
only add to del_list when user provides '-x' option to git-clean.

if (S_ISDIR(st.st_mode)) {
if (remove_directories || (matches ==
MATCHED_EXACTLY)) {
rel = path_relative(ent-name, prefix);
string_list_append(del_list, rel);
}
} ...


When start to do cleaning, there is a duplicate lstat() call, and the removed
comments above are moved here.

/*
 * 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, -1, buf, NULL);
printf(dry_run ? _(msg_would_remove) :
_(msg_remove), qname);
}
}

But I am not clear how earlier recursive directory removal could
make lstat() failure here. If it is not the case, maybe we can test a
directory by ent-name (ends with '/' or '\\' ?).

-- 
Jiang Xin
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html