[PATCH v4] git-clean: Display more accurate delete messages

2013-01-06 Thread Zoltan Klinger
(1) Only print out the names of the files and directories that got
actually deleted.
(2) Show warning message for ignored untracked git repositories

Consider the following repo layout:

  test.git/
|-- tracked_dir/
| |-- some_tracked_file
| |-- some_untracked_file
|-- tracked_file
|-- untracked_file
|-- untracked_foo/
| |-- bar/
| | |-- bar.txt
| |-- emptydir/
| |-- frotz.git/
|   |-- frotz.tx
|-- untracked_some.git/
  |-- some.txt

Suppose the user issues 'git clean -fd' from the test.git directory.

When -d option is used and untracked directory 'foo' contains a
subdirectory 'frotz.git' that is managed by a different git repository
therefore it will not be removed.

  $ git clean -fd
  Removing tracked_dir/some_untracked_file
  Removing untracked_file
  Removing untracked_foo/
  Removing untracked_some.git/

The message displayed to the user is slightly misleading. The foo/
directory has not been removed because of foo/frotz.git still exists.
On the other hand the subdirectories 'bar' and 'emptydir' have been
deleted but they're not mentioned anywhere. Also, untracked_some.git
has not been removed either.

This behaviour is the result of the way the deletion of untracked
directories are reported. In the current implementation they are
deleted recursively but only the name of the top most directory is
printed out. The calling function does not know about any
subdirectories that could not be removed during the recursion.

Improve the way the deleted directories are reported back to
the user:
  (1) Create a recursive delete function 'remove_dirs' in builtin/clean.c
  to run in both dry_run and delete modes with the delete logic as
  follows:
(a) Check if the current directory to be deleted is an untracked
git repository. If it is and --force --force option is not set
do not touch this directory, print ignore message, set dir_gone
flag to false for the caller and return.
(b) Otherwise for each item in current directory:
  (i)   If current directory cannot be accessed, print warning,
set dir_gone flag to false and return.
  (ii)  If the item is a subdirectory recurse into it,
check for the returned value of the dir_gone flag.
If the subdirectory is gone, add the name of the deleted
directory to a list of successfully removed items 'dels'.
Else set the dir_gone flag as the current directory
cannot be removed because we have at least one subdirectory
hanging around.
  (iii) If it is a file try to remove it. If success add the
file name to the 'dels' list, else print error and set
dir_gone flag to false.
(c) After we finished deleting all items in the current directory and
the dir_gone flag is still true, remove the directory itself.
If failed set the dir_gone flag to false.

(d) If the current directory cannot be deleted because the dir_gone flag
has been set to false, print out all the successfully deleted items
for this directory from the 'dels' list.
(e) We're done with the current directory, return.

  (2) Modify the cmd_clean() function to:
(a) call the recursive delete function 'remove_dirs()' for each
topmost directory it wants to remove
(b) check for the returned value of dir_gone flag. If it's true
print the name of the directory as being removed.

Consider the output of the improved version:

  $ git clean -fd
  Removing tracked_dir/some_untracked_file
  Removing untracked_file
  warning: ignoring untracked git repository untracked_foo/frotz.git
  Removing untracked_foo/bar
  Removing untracked_foo/emptydir
  warning: ignoring untracked git repository untracked_some.git/

Now it displays only the file and directory names that got actually
deleted and shows warnings about ignored untracked git repositories.

Reported-by: Soren Brinkmann 

Signed-off-by: Zoltan Klinger 
---
 builtin/clean.c |  158 +--
 1 file changed, 129 insertions(+), 29 deletions(-)

diff --git a/builtin/clean.c b/builtin/clean.c
index 69c1cda..1714546 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -10,6 +10,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "refs.h"
 #include "string-list.h"
 #include "quote.h"
 
@@ -20,6 +21,12 @@ static const char *const builtin_clean_usage[] = {
NULL
 };
 
+static const char *msg_remove = N_("Removing %s\n");
+static const char *msg_would_remove = N_("Would remove %s\n");
+static const char *msg_would_ignore_git_dir = N_("Would ignore untracked git 
repository %s\n");
+static const char *msg_warn_ignore_git_dir = N_("ignoring

Re: [PATCH v4] git-clean: Display more accurate delete messages

2013-01-06 Thread Jonathan Nieder
Zoltan Klinger wrote:

>   $ git clean -fd
>   Removing tracked_dir/some_untracked_file
>   Removing untracked_file
>   Removing untracked_foo/
>   Removing untracked_some.git/
>
> The message displayed to the user is slightly misleading. The foo/
> directory has not been removed because of foo/frotz.git still exists.
> On the other hand the subdirectories 'bar' and 'emptydir' have been
> deleted but they're not mentioned anywhere. Also, untracked_some.git
> has not been removed either.
[...]
> Consider the output of the improved version:
>
>   $ git clean -fd
>   Removing tracked_dir/some_untracked_file
>   Removing untracked_file
>   warning: ignoring untracked git repository untracked_foo/frotz.git
>   Removing untracked_foo/bar
>   Removing untracked_foo/emptydir
>   warning: ignoring untracked git repository untracked_some.git/

Thanks, this looks like a nice improvement.

I wonder whether it's possible to make the output more consistent,
as in:

Removing tracked_dir/some_untracked_file
Removing untracked_file
Skipping repository untracked_foo/frotz.git
Removing untracked_foo/bar
Removing untracked_foo/emptydir
Skipping repository untracked_some.git

or similar.  What do you think?

Thanks,
Jonathan
--
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 v4] git-clean: Display more accurate delete messages

2013-01-09 Thread Zoltan Klinger
> I wonder whether it's possible to make the output more consistent,
> as in:
>
> Removing tracked_dir/some_untracked_file
> Removing untracked_file
> Skipping repository untracked_foo/frotz.git
> Removing untracked_foo/bar
> Removing untracked_foo/emptydir
> Skipping repository untracked_some.git
>
> or similar.  What do you think?

Agree, the output looks much neater printed like that. I am going to
update the patch unless someone feels strongly against the proposed
output.
--
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 v4] git-clean: Display more accurate delete messages

2013-01-09 Thread Junio C Hamano
Zoltan Klinger  writes:

> Consider the output of the improved version:
>
>   $ git clean -fd
>   Removing tracked_dir/some_untracked_file
>   Removing untracked_file
>   warning: ignoring untracked git repository untracked_foo/frotz.git
>   Removing untracked_foo/bar
>   Removing untracked_foo/emptydir
>   warning: ignoring untracked git repository untracked_some.git/
>
> Now it displays only the file and directory names that got actually
> deleted and shows warnings about ignored untracked git repositories.
>
> Reported-by: Soren Brinkmann 
>
> Signed-off-by: Zoltan Klinger 
> ---

I think the code before this patch used to say "Would not remove"
and "Not removing" in certain cases to report the paths that the
command decided not to remove, but after this patch these two
messages no longer appear in the patch.

Is it expected, are we losing information, or...?

>  builtin/clean.c |  158 
> +--
>  1 file changed, 129 insertions(+), 29 deletions(-)
>
> diff --git a/builtin/clean.c b/builtin/clean.c
> index 69c1cda..1714546 100644
> --- a/builtin/clean.c
> +++ b/builtin/clean.c
> @@ -10,6 +10,7 @@
>  #include "cache.h"
>  #include "dir.h"
>  #include "parse-options.h"
> +#include "refs.h"
>  #include "string-list.h"
>  #include "quote.h"
>  
> @@ -20,6 +21,12 @@ static const char *const builtin_clean_usage[] = {
>   NULL
>  };
>  
> +static const char *msg_remove = N_("Removing %s\n");
> +static const char *msg_would_remove = N_("Would remove %s\n");
> +static const char *msg_would_ignore_git_dir = N_("Would ignore untracked git 
> repository %s\n");
> +static const char *msg_warn_ignore_git_dir = N_("ignoring untracked git 
> repository %s");
> +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"))
> @@ -34,11 +41,116 @@ static int exclude_cb(const struct option *opt, const 
> char *arg, int unset)
>   return 0;
>  }
>  
> +static int remove_dirs(struct strbuf *path, const char *prefix, int 
> force_flag,
> + int dry_run, int quiet, int *dir_gone)
> +{
> + DIR *dir;
> + struct strbuf quoted = STRBUF_INIT;
> + struct dirent *e;
> + int res = 0, ret = 0, gone = 1, original_len = path->len, len, i;
> + unsigned char submodule_head[20];
> + struct string_list dels = STRING_LIST_INIT_DUP;
> +
> + *dir_gone = 1;
> +
> + if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
> + !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
> + if (dry_run && !quiet) {
> + quote_path_relative(path->buf, strlen(path->buf), 
> "ed, prefix);
> + printf(_(msg_would_ignore_git_dir), quoted.buf);
> + } else if (!dry_run) {
> + quote_path_relative(path->buf, strlen(path->buf), 
> "ed, prefix);
> + warning(_(msg_warn_ignore_git_dir), quoted.buf);
> + }
> +
> + *dir_gone = 0;
> + return 0;
> + }
> +
> + dir = opendir(path->buf);
> + if (!dir) {
> + /* 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), 
> "ed, prefix);
> + warning(_(msg_warn_remove_failed), quoted.buf);
> + *dir_gone = 0;
> + }
> + return res;
> + }
> +
> + if (path->buf[original_len - 1] != '/')
> + strbuf_addch(path, '/');
> +
> + len = path->len;
> + while ((e = readdir(dir)) != NULL) {
> + struct stat st;
> + if (is_dot_or_dotdot(e->d_name))
> + continue;
> +
> + strbuf_setlen(path, len);
> + strbuf_addstr(path, e->d_name);
> + if (lstat(path->buf, &st))
> + ; /* fall thru */
> + else if (S_ISDIR(st.st_mode)) {
> + if (remove_dirs(path, prefix, force_flag, dry_run, 
> quiet, &gone))
> + ret = 1;
> + if (gone) {
> + quote_path_relative(path->buf, 
> strlen(path->buf), "ed, prefix);
> + string_list_append(&dels, quoted.buf);
> + }
> + else
> + *dir_gone = 0;
> + continue;
> + } else {
> + res = dry_run ? 0 : unlink(path->buf);
> + if (!res) {
> + quote_path_relative(path->buf, 
> strlen(path->buf), "ed, prefix);
> + string_list_append(&dels, quoted.buf);
> + }
> + else {
> + quote_path_relative(path->buf, 

Re: [PATCH v4] git-clean: Display more accurate delete messages

2013-01-10 Thread Zoltan Klinger
> I think the code before this patch used to say "Would not remove"
> and "Not removing" in certain cases to report the paths that the
> command decided not to remove, but after this patch these two
> messages no longer appear in the patch.
>
> Is it expected, are we losing information, or...?
>

I do not think we are losing any information.
Say, we have a repo like this:
test.git
 |-- untracked_file
 |-- untracked_bar
 | |-- bar.txt
 |-- untracked_foo
   |-- foo.txt

The original version prints out:
  $ git clean -fn
  Would remove untracked_file
  Would not remove untracked_bar/
  Would not remove untracked_foo/

We never asked for any directories to be removed so IMHO the "Would
not remove ..." messages are just noise.

The new version prints out:
  $ git clean -fn
  Would remove untracked_file
--
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 v4] git-clean: Display more accurate delete messages

2013-01-10 Thread Junio C Hamano
Zoltan Klinger  writes:

>> I think the code before this patch used to say "Would not remove"
>> and "Not removing" in certain cases to report the paths that the
>> command decided not to remove, but after this patch these two
>> messages no longer appear in the patch.
>>
>> Is it expected, are we losing information, or...?
>
> I do not think we are losing any information.
> Say, we have a repo like this:
> test.git
>  |-- untracked_file
>  |-- untracked_bar
>  | |-- bar.txt
>  |-- untracked_foo
>|-- foo.txt
>
> The original version prints out:
>   $ git clean -fn
>   Would remove untracked_file
>   Would not remove untracked_bar/
>   Would not remove untracked_foo/
>
> We never asked for any directories to be removed so IMHO the "Would
> not remove ..." messages are just noise.
>
> The new version prints out:
>   $ git clean -fn
>   Would remove untracked_file

Oh.  I was blinded by the primary reason of your patch being "be
more careful and defer reporting a removal until we know everything
in a directory did get removed and managed to remove the directory",
and did not realize this "noise removal".

Perhaps add

Also do not mention that we are not removing directories
when the user did not ask us to do so with '-d'.

or something to the description?

Thanks for a clarification.

--
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