Re: [PATCH] git-new-workdir: support submodules

2015-01-25 Thread Junio C Hamano
Craig Silverstein  writes:

> This patch, in adding submodule support, I expect would be
> similarly useful to people even though it, also, imposes those
> same constraints to the submodule's config files.

I would expect that you would see exactly the same issue with Duy's
multiple work tree series.  This is not limited to new-workdir.

The right way to look at this is to fix what "git submodule" does;
its use of "config" that is shared across branches is the root cause
of the trouble.  No other part of Git keeps data that needs to be
per-branch (or more specifically "tied to the working tree state")
in .git/config in such a way that leaving it stale when the working
tree state changes breaks the system.

One way to do so might be to move the bits it stores in the config
file to somewhere else that is more closely tied to the checkout
state and handle that similar to .git/index and .git/HEAD when it
comes to multiple work-trees.


--
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] git-new-workdir: support submodules

2015-01-25 Thread Craig Silverstein
> But then, you are saying that the update does not fix these existing
> issues around submodule support.  So...?

I guess my point is that the existing contrib script has proven to be
useful to people, even though it imposes these constraints on clients
wrt the config file (namely, you can't have multiple workdirs that
need different values in the config file).  This patch, in adding
submodule support, I expect would be similarly useful to people even
though it, also, imposes those same constraints to the submodule's
config files.

I guess you'd rather see these config file issues fixed for all use
cases?  If so, I'm probably not the right person since I do not know
enough about how config files are used in git -- I fear any changes I
made would make some things worse for (some) existing clients of the
script, which is not what I want.  It sounds like this functionality
is being reimplemented in git proper in any case, so perhaps it's best
just to wait for that.  I don't know what its submodule support will
be, though.

craig
--
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] win32: syslog: prevent potential realloc memory leak

2015-01-25 Thread Junio C Hamano
Erik Faye-Lund  writes:

> Sorry for very late reply. I had a bug in my mail rules that caused
> this email to skip my inbox. That should be fixed now.
>
> On Mon, Dec 15, 2014 at 7:11 PM, Junio C Hamano  wrote:
> ...
>> Regardless of that funny %1 business, I notice in
>>
>> 
>> http://msdn.microsoft.com/en-us/library/windows/desktop/aa363679%28v=vs.85%29.aspx
>>
>> that each element of lpStrings array that is passed to ReportEvent()
>> is limited to 32k or so.  Wouldn't it make it a lot simpler if we
>> removed the dynamic allocation and use a fixed sized 32k buffer here
>> (and truncate the result as necessary)?  That would make the "leak"
>> disappear automatically.
>
> That's a very good point. Yes, I think that makes more sense.

OK, so I'd expect a simpler non-reallocating code to materialize
and will drop Arjun's patch for now.

Thanks.
--
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] wincred: fix get credential if username has @

2015-01-25 Thread Junio C Hamano
Erik Faye-Lund  writes:

> Sorry for the extremely delayed reply, I had a bug in my mail-filters.
> Hopefully fixed now.
>
> ...
>
> I noticed the breakage myself around the same time, and posted about it here:
>
> https://groups.google.com/d/msg/msysgit/YVuCqmwwRyY/HULHj5OoE88J
>
> Unfortunately, it stopped there.
> ...
>
> Looks reasonable enough to me.
>
> Acked-by: Erik Faye-Lund 

OK.  Will apply to my tree, so future Git for Windows would
hopefully have it as part of its upstream updates.

Thanks.
--
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] git-new-workdir: support submodules

2015-01-25 Thread Junio C Hamano
Craig Silverstein  writes:

> } Or one new-workdir checkout's branch may check out a top-level
> } project from today while the other one may have a top-level project
> } from two years ago,
>
> This is also true, but just as much a problem with the 'git
> new-workdir' script as it existed before my change.  It already
> symlinks the top-level .git/config directory, which lists a remote,
> submodules, and many other things.  Does symlinking the config file
> for submodules add any new wrinkles, that symlinking the config file
> for the top-level repository does not?

The update under discussion is labeled as "support submodules";
presumably the only reason that such an update is good is because it
will fix existing problems that makes the use of the script break
when submodules are involved --- submodules are not supported with
the current code, and this patch fixes the code to support it.

But then, you are saying that the update does not fix these existing
issues around submodule support.  So...?



--
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] win32: syslog: prevent potential realloc memory leak

2015-01-25 Thread Erik Faye-Lund
Sorry for very late reply. I had a bug in my mail rules that caused
this email to skip my inbox. That should be fixed now.

On Mon, Dec 15, 2014 at 7:11 PM, Junio C Hamano  wrote:
> Arjun Sreedharan  writes:
>
>> use a temporary variable to free the memory in case
>> realloc() fails.
>>
>> Signed-off-by: Arjun Sreedharan 
>> ---
>>  compat/win32/syslog.c | 4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c
>> index d015e43..3409e43 100644
>> --- a/compat/win32/syslog.c
>> +++ b/compat/win32/syslog.c
>> @@ -16,7 +16,7 @@ void openlog(const char *ident, int logopt, int facility)
>>  void syslog(int priority, const char *fmt, ...)
>>  {
>>   WORD logtype;
>> - char *str, *pos;
>> + char *str, *str_temp, *pos;
>>   int str_len;
>>   va_list ap;
>>
>> @@ -43,9 +43,11 @@ void syslog(int priority, const char *fmt, ...)
>>   va_end(ap);
>>
>>   while ((pos = strstr(str, "%1")) != NULL) {
>> + str_temp = str;
>>   str = realloc(str, ++str_len + 1);
>>   if (!str) {
>>   warning("realloc failed: '%s'", strerror(errno));
>> + free(str_temp);
>>   return;
>>   }
>
> Hmm, the original, 088d8802 (mingw: implement syslog, 2010-11-04),
> that introduced the special casing for %1, says:
>
> Syslog does not usually exist on Windows, so implement our own
> using Window's ReportEvent mechanism.
>
> Strings containing "%1" gets expanded into them selves by
> ReportEvent, resulting in an unreadable string. "%2" and above
> is not a problem.  Unfortunately, on Windows an IPv6 address can
> contain "%1", so expand "%1" to "% 1" before reporting. "%%1" is
> also a problem for ReportEvent, but that string cannot occur in
> an IPv6 address.
>
> It is unclear why it says '"%2" and above is not a problem' to me.
> Is that because they expand to something not "an unreadable string",
> or is that because in the original developer's testing only "%1" was
> observed?

%2 would expand to the first argument (this is a varargs-type
interface), but such an argument does not exist, so no expansion is
being performed.

> It also says "%%1" is a problem, and it does not occur in
> an IPv6 address, but that would suggest that every time a new caller
> is added to syslog(), this imitation of syslog() can break, as there
> is nothing that says the new caller must be reporting something
> about an IP address.  Perhaps this loop should cleanse what it
> passes to ReportEvent() a bit more aggressively by expanding all "%"
> to "%-sp" or something)?

This suspicion is somewhat justified; I only did %1 because that was
the only one that had any theoretical way of being hit at the time the
patch was written. However, a quick test reveals that we currently
expand "%%1" to "%% 1", which is not problematic. And that makes sense
from looking at the code also.

However, removing the hunk and running the code reveals that Windows
no longer does that ugly recursive expansion. I'm sure it did when I
wrote the code, because I did test it. So it seems at least Windows
8.1 is less insane about this.

> Regardless of that funny %1 business, I notice in
>
> 
> http://msdn.microsoft.com/en-us/library/windows/desktop/aa363679%28v=vs.85%29.aspx
>
> that each element of lpStrings array that is passed to ReportEvent()
> is limited to 32k or so.  Wouldn't it make it a lot simpler if we
> removed the dynamic allocation and use a fixed sized 32k buffer here
> (and truncate the result as necessary)?  That would make the "leak"
> disappear automatically.

That's a very good point. Yes, I think that makes more sense.

> Having said all that, if we were to still go with the current code
> structure, "str_temp" should be scoped inside the loop, as there is
> no need to make it available to the remainder of the function, I
> think.  Also writing this way may make the intention more clear.
>
> while (...) {
> char *new_str = realloc(str, ...);
> if (!new_str) {
> free(str);
> return;
> }
> memmove(... to shuffle ...);
>
> And after starting to write the above, I notice that the current
> code around realloc may be completely bogus.  It goes like this:
>
> while ((pos = strstr(str, "..."))) {
> str = realloc(str, ...);
> if (!str) { warn and bail; }
> memmove(pos + 1, pos + 1, ...);
> pos[1] = ' ';
> }
>
> If realloc() really allocated a new string, then pos that points
> into the original str has no relation to the reallocated str, so
> memmove() is not shuffling the string to make room for the SP in the
> string that will be given to ReportEvent() at all, no?  This seems
> to be a bug introduced by 2a6b149c (mingw: avoid using strbuf in
> syslog

Re: [PATCH] wincred: fix get credential if username has @

2015-01-25 Thread Erik Faye-Lund
Sorry for the extremely delayed reply, I had a bug in my mail-filters.
Hopefully fixed now.

On Wed, Nov 19, 2014 at 11:41 PM, Junio C Hamano  wrote:
> Aleksey Vasenev  writes:
>
>>> To: git@vger.kernel.org
>>> Cc: Junio C Hamano , Aleksey Vasenev 
>
> Sorry, but I am hardly qualified to review this one, especially
> without any log message that explains what breaks and how it breaks
> with the current code, which may lead the reader to understand how
> the updated code fixes the issue.  Cc'ing me does not help us very
> much.
>
> $ git shortlog --no-merges -n contrib/credential/wincred/
>
> gives me a few names who may be able to give us some inputs, so I'll
> Cc them.
>
> Thanks.

I noticed the breakage myself around the same time, and posted about it here:

https://groups.google.com/d/msg/msysgit/YVuCqmwwRyY/HULHj5OoE88J

Unfortunately, it stopped there.

>> Signed-off-by: Aleksey Vasenev 
>> ---
>>  .../credential/wincred/git-credential-wincred.c| 25 
>> +++---
>>  1 file changed, 22 insertions(+), 3 deletions(-)
>>
>> diff --git a/contrib/credential/wincred/git-credential-wincred.c 
>> b/contrib/credential/wincred/git-credential-wincred.c
>> index a1d38f0..0061340 100644
>> --- a/contrib/credential/wincred/git-credential-wincred.c
>> +++ b/contrib/credential/wincred/git-credential-wincred.c
>> @@ -111,14 +111,23 @@ static void write_item(const char *what, LPCWSTR wbuf, 
>> int wlen)
>>   * Match an (optional) expected string and a delimiter in the target string,
>>   * consuming the matched text by updating the target pointer.
>>   */
>> -static int match_part(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
>> +
>> +static LPCWSTR wcsstr_last(LPCWSTR str, LPCWSTR find)
>> +{
>> + LPCWSTR res = NULL, pos;
>> + for (pos = wcsstr(str, find); pos; pos = wcsstr(pos + 1, find))
>> + res = pos;
>> + return res;
>> +}
>> +

Ugh, there's no wcsrstr? I guess this is a reasonable way to emulate it...

>> +static int match_part_with_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR 
>> delim, int last)
>>  {
>>   LPCWSTR delim_pos, start = *ptarget;
>>   int len;
>>
>>   /* find start of delimiter (or end-of-string if delim is empty) */
>>   if (*delim)
>> - delim_pos = wcsstr(start, delim);
>> + delim_pos = last ? wcsstr_last(start, delim) : wcsstr(start, 
>> delim);
>>   else
>>   delim_pos = start + wcslen(start);
>>
>> @@ -138,6 +147,16 @@ static int match_part(LPCWSTR *ptarget, LPCWSTR want, 
>> LPCWSTR delim)
>>   return !want || (!wcsncmp(want, start, len) && !want[len]);
>>  }
>>
>> +static int match_part(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
>> +{
>> + return match_part_with_last(ptarget, want, delim, 0);
>> +}
>> +
>> +static int match_part_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
>> +{
>> + return match_part_with_last(ptarget, want, delim, 1);
>> +}
>> +
>>  static int match_cred(const CREDENTIALW *cred)
>>  {
>>   LPCWSTR target = cred->TargetName;
>> @@ -146,7 +165,7 @@ static int match_cred(const CREDENTIALW *cred)
>>
>>   return match_part(&target, L"git", L":") &&
>>   match_part(&target, protocol, L"://") &&
>> - match_part(&target, wusername, L"@") &&
>> + match_part_last(&target, wusername, L"@") &&
>>   match_part(&target, host, L"/") &&
>>   match_part(&target, path, L"");
>>  }

Looks reasonable enough to me.

Acked-by: Erik Faye-Lund 
--
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 21/21] t3080: tests for git-list-files

2015-01-25 Thread Eric Sunshine
On Sun, Jan 25, 2015 at 7:37 AM, Nguyễn Thái Ngọc Duy  wrote:
> Signed-off-by: Nguyễn Thái Ngọc Duy 
> ---
> diff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh
> new file mode 100755
> index 000..6313dd9
> --- /dev/null
> +++ b/t/t3080-list-files.sh
> +test_expect_success 'no dups' '
> +   echo dirty >>file &&

To leave a clean slate for subsequent tests, would it make sense to
restore 'file' to a clean state via test_when_finished()?

> +   git list-files -m file >actual &&
> +   echo "file" >expected &&
> +   test_cmp expected actual &&
> +   git list-files -cm file >actual &&
> +   echo "C file" >expected &&
> +   test_cmp expected actual &&
> +   git list-files -tcm file >actual &&
> +   test_cmp expected actual
> +'
> +
> +test_expect_success 'diff-cached' '
> +   echo dirty >>file &&
> +   git add file &&

Ditto here?

> +   git list-files -M >actual &&
> +   echo "file" >expected &&
> +   test_cmp expected actual
> +'
--
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 17/21] list-files: show directories as well as files

2015-01-25 Thread Eric Sunshine
On Sun, Jan 25, 2015 at 7:37 AM, Nguyễn Thái Ngọc Duy  wrote:
> The index does not store directories explicitly (except submodules) so
> we have to figure them out from file list when output lis depth-limited.
>
> The function show_as_directory() deliberately generates duplicate
> directories and expects the previous patch to remove duplicates.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy 
> ---
> diff --git a/builtin/ls-files.c b/builtin/ls-files.c
> index 1a1c9c8..29b5c2e 100644
> --- a/builtin/ls-files.c
> +++ b/builtin/ls-files.c
> @@ -179,6 +181,35 @@ static void show_killed_files(struct dir_struct *dir)
> }
>  }
>
> +static int show_as_directory(const struct cache_entry *ce)
> +{
> +   struct strbuf sb = STRBUF_INIT;
> +   const char *p;
> +
> +   strbuf_add(&sb, ce->name, ce_namelen(ce));
> +   while (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {
> +   struct strbuf sb2 = STRBUF_INIT;
> +   strbuf_setlen(&sb, p - sb.buf);
> +   if (!match_pathspec(&pathspec, sb.buf, sb.len,
> +   max_prefix_len, NULL, 1))
> +   continue;
> +   write_name(&sb2, sb.buf);
> +   if (want_color(use_color)) {
> +   struct strbuf sb3 = STRBUF_INIT;
> +   color_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);
> +   strbuf_release(&sb2);
> +   sb2 = sb3;

Although more expensive, would it be a bit more idiomatic and obvious
to phrase this as

strbuf_swap(&sb2, &sb3);
strbuf_release(&sb3);

or is it not worth it?

> +   }
> +   if (show_tag)
> +   strbuf_insert(&sb2, 0, tag_cached, 
> strlen(tag_cached));
> +   strbuf_fputs(&sb2, strbuf_detach(&sb, NULL), NULL);

The detached strbuf content gets assigned to the 'util' field of the
'struct string_list output' item and is eventually leaked, however,
the program exits soon after. Okay.

> +   strbuf_release(&sb2);
> +   return 1;
> +   }
> +   strbuf_release(&sb);
> +   return 0;
> +}
> +
>  static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)
>  {
> struct strbuf quoted = STRBUF_INIT;
--
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 20/21] list-files: -M aka diff-cached

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 builtin/ls-files.c | 67 +++---
 1 file changed, 64 insertions(+), 3 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 697a307..b04c712 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -16,6 +16,9 @@
 #include "pathspec.h"
 #include "color.h"
 #include "column.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
 
 static int abbrev;
 static int show_deleted;
@@ -25,6 +28,7 @@ static int show_stage;
 static int show_unmerged;
 static int show_resolve_undo;
 static int show_modified;
+static int show_diff_cached;
 static int show_killed;
 static int show_valid_bit;
 static int show_tag;
@@ -53,6 +57,7 @@ static const char *tag_removed = "";
 static const char *tag_other = "";
 static const char *tag_killed = "";
 static const char *tag_modified = "";
+static const char *tag_diff_cached = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
@@ -404,7 +409,15 @@ static void show_files(struct dir_struct *dir)
err = lstat(ce->name, &st);
if (show_deleted && err)
show_ce_entry(tag_removed, ce);
-   if (show_modified && ce_modified(ce, &st, 0))
+   if (show_diff_cached && (ce->ce_flags & CE_MATCHED)) {
+   show_ce_entry(tag_diff_cached, ce);
+   /*
+* if we don't clear, it'll confuse 
write_ce_name()
+* when show_ce_entry(tag_modified, ce) is 
called
+*/
+   active_cache[i]->ce_flags &= ~CE_MATCHED;
+   }
+   if (show_modified && (err || ce_modified(ce, &st, 0)))
show_ce_entry(tag_modified, ce);
}
}
@@ -424,7 +437,8 @@ static void show_files_compact(struct dir_struct *dir)
if (show_killed)
show_killed_files(dir);
}
-   if (!(show_cached || show_unmerged || show_deleted || show_modified))
+   if (!(show_cached || show_unmerged || show_deleted ||
+ show_modified || show_diff_cached))
return;
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
@@ -444,6 +458,15 @@ static void show_files_compact(struct dir_struct *dir)
show_ce_entry(tag_removed, ce);
shown = 1;
}
+   if (show_diff_cached && (ce->ce_flags & CE_MATCHED)) {
+   show_ce_entry(tag_diff_cached, ce);
+   shown = 1;
+   /*
+* if we don't clear, it'll confuse write_ce_name()
+* when show_ce_entry(tag_modified, ce) is called
+*/
+   active_cache[i]->ce_flags &= ~CE_MATCHED;
+   }
if (show_modified && (err || ce_modified(ce, &st, 0))) {
show_ce_entry(tag_modified, ce);
shown = 1;
@@ -457,6 +480,38 @@ static void show_files_compact(struct dir_struct *dir)
}
 }
 
+static void mark_diff_cached(struct diff_queue_struct *q,
+struct diff_options *options,
+void *data)
+{
+   int i;
+
+   for (i = 0; i < q->nr; i++) {
+   struct diff_filepair *p = q->queue[i];
+   int pos = cache_name_pos(p->two->path, strlen(p->two->path));
+   if (pos < 0)
+   continue;
+   active_cache[pos]->ce_flags |= CE_MATCHED;
+   }
+}
+
+static void diff_cached(struct pathspec *pathspec)
+{
+   struct rev_info rev;
+   const char *argv[] = { "ls-files", "HEAD", NULL };
+
+   init_revisions(&rev, NULL);
+   setup_revisions(2, argv, &rev, NULL);
+
+   rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+   rev.diffopt.format_callback = mark_diff_cached;
+   rev.diffopt.detect_rename = 1;
+   rev.diffopt.rename_limit = 200;
+   rev.diffopt.break_opt = 0;
+   copy_pathspec(&rev.prune_data, pathspec);
+   run_diff_index(&rev, 1);
+}
+
 /*
  * Prune the index to only contain stuff starting with "prefix"
  */
@@ -726,6 +781,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_("show cached files that are deleted on working 
directory")),
OPT_BOOL('m', "modified", &show_modified,
N_("show cached files that have modification on working 
directory")),
+   OPT_BOOL('M', "modified", &show_diff_cached,
+   N_("show modified files in the cache")),
OPT_BOOL('o', "others", &show_others,
   

[PATCH 21/21] t3080: tests for git-list-files

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 t/t3080-list-files.sh (new +x) | 122 +
 t/t3080/color_ls (new) |   3 +
 t/t3080/ls_colors (new)|   2 +
 3 files changed, 127 insertions(+)
 create mode 100755 t/t3080-list-files.sh
 create mode 100644 t/t3080/color_ls
 create mode 100644 t/t3080/ls_colors

diff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh
new file mode 100755
index 000..6313dd9
--- /dev/null
+++ b/t/t3080-list-files.sh
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+test_description='git list-files test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+   mkdir dir &&
+   touch file dir/file &&
+   git init gitlink &&
+   ( cd gitlink && test_commit foo ) &&
+   git add file dir/file gitlink &&
+   git commit -qm1
+'
+
+test_expect_success 'LS_COLORS env variable' '
+   LS_COLORS="rs=0:fi=31:di=32" \
+   git list-files --color=always | grep -v gitlink >actual &&
+   test_cmp "$TEST_DIRECTORY"/t3080/ls_colors actual
+'
+
+test_expect_success 'color.ls.*' '
+   test_config color.ls.file red &&
+   test_config color.ls.directory green &&
+   test_config color.ls.submodule yellow &&
+   git list-files --color=always >actual &&
+   test_cmp "$TEST_DIRECTORY"/t3080/color_ls actual
+'
+
+test_expect_success 'column output' '
+   COLUMNS=20 git list-files --column=always >actual &&
+   cat >expected <<-\EOF &&
+   dir  gitlink
+   file
+   EOF
+   test_cmp expected actual &&
+   git list-files -1 >actual &&
+   cat >expected <<-\EOF &&
+   dir
+   file
+   gitlink
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success '--max-depth' '
+   git list-files --max-depth=1 >actual &&
+   cat >expected <<-\EOF &&
+   dir/file
+   file
+   gitlink
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'recursive' '
+   git list-files -R >actual &&
+   cat >expected <<-\EOF &&
+   dir/file
+   file
+   gitlink
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'globbing' '
+   git list-files "f*" >actual &&
+   cat >expected <<-\EOF &&
+   file
+   EOF
+   test_cmp expected actual &&
+   git list-files "**/f*" >actual &&
+   cat >expected <<-\EOF &&
+   dir/file
+   file
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'no dups' '
+   echo dirty >>file &&
+   git list-files -m file >actual &&
+   echo "file" >expected &&
+   test_cmp expected actual &&
+   git list-files -cm file >actual &&
+   echo "C file" >expected &&
+   test_cmp expected actual &&
+   git list-files -tcm file >actual &&
+   test_cmp expected actual
+'
+
+test_expect_success '--classify' '
+   git list-files -F >actual &&
+   cat >expected <<-\EOF &&
+   dir/
+   file
+   gitlink&
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'diff-cached' '
+   echo dirty >>file &&
+   git add file &&
+   git list-files -M >actual &&
+   echo "file" >expected &&
+   test_cmp expected actual
+'
+
+test_expect_success 'unmerged files' '
+   git ls-files --stage file >index-info &&
+   sed "s/ 0/ 2/;s/file/unmerged/" index-info | git update-index 
--index-info &&
+   sed "s/ 0/ 3/;s,file,dir/unmerged," index-info | git update-index 
--index-info &&
+   git list-files -u >actual &&
+   cat >expected <<-\EOF &&
+   dir
+   unmerged
+   EOF
+   test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3080/color_ls b/t/t3080/color_ls
new file mode 100644
index 000..47f77ad
--- /dev/null
+++ b/t/t3080/color_ls
@@ -0,0 +1,3 @@
+dir
+file
+gitlink
diff --git a/t/t3080/ls_colors b/t/t3080/ls_colors
new file mode 100644
index 000..423c016
--- /dev/null
+++ b/t/t3080/ls_colors
@@ -0,0 +1,2 @@
+dir
+file
-- 
2.2.0.84.ge9c7a8a

--
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 18/21] list-files: add -F/--classify

2015-01-25 Thread Nguyễn Thái Ngọc Duy
This appends an indicator after the file name if it's executable, a
directory and so on, like in GNU ls. In fact append_indicator() is a
rewrite from get_type_indicator() in coreutils.git commit
7326d1f1a67edf21947ae98194f98c38b6e9e527.

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 Documentation/git-list-files.txt |  6 ++
 builtin/ls-files.c   | 31 +++
 2 files changed, 37 insertions(+)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 9d4c127..475c6da 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -52,6 +52,12 @@ OPTIONS
multiple file selections. See linkgit::git-ls-files[1] option
`-t` for more information.
 
+-F::
+--classify::
+   Append indicator (one of `*/=>@|`, which is executable,
+   directory, socket, Solaris door, symlink, or fifo
+   respectively) to entries.
+
 -R::
 --recursive::
Equivalent of `--max-depth=-1` (infinite recursion).
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 29b5c2e..bbeb4ce 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -29,6 +29,7 @@ static int show_killed;
 static int show_valid_bit;
 static int show_tag;
 static int show_dirs;
+static int show_indicator;
 static int line_terminator = '\n';
 static int debug_mode;
 static int use_color;
@@ -77,6 +78,28 @@ static void write_name(struct strbuf *sb, const char *name)
quote_path_relative(name, real_prefix, sb);
 }
 
+static void append_indicator(struct strbuf *sb, mode_t mode)
+{
+   char c = 0;
+   if (S_ISREG(mode)) {
+   if (mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+   c = '*';
+   } else if (S_ISDIR(mode))
+   c = '/';
+   else if (S_ISLNK(mode))
+   c = '@';
+   else if (S_ISFIFO(mode))
+   c = '|';
+   else if (S_ISSOCK(mode))
+   c = '=';
+#ifdef S_ISDOOR
+   else if (S_ISDOOR(mode))
+   c = '>';
+#endif
+   if (c)
+   strbuf_addch(sb, c);
+}
+
 static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)
 {
if (column_active(colopts) || porcelain) {
@@ -99,6 +122,8 @@ static void write_dir_entry(struct strbuf *sb, const struct 
dir_entry *ent)
color_filename(sb, ent->name, quoted.buf, st.st_mode, 1);
else
strbuf_addbuf(sb, "ed);
+   if (show_indicator && st.st_mode)
+   append_indicator(sb, st.st_mode);
strbuf_addch(sb, line_terminator);
strbuf_release("ed);
 }
@@ -202,6 +227,8 @@ static int show_as_directory(const struct cache_entry *ce)
}
if (show_tag)
strbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));
+   if (show_indicator)
+   append_indicator(&sb2, S_IFDIR);
strbuf_fputs(&sb2, strbuf_detach(&sb, NULL), NULL);
strbuf_release(&sb2);
return 1;
@@ -218,6 +245,8 @@ static void write_ce_name(struct strbuf *sb, const struct 
cache_entry *ce)
color_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);
else
strbuf_addbuf(sb, "ed);
+   if (show_indicator)
+   append_indicator(sb, ce->ce_mode);
strbuf_addch(sb, line_terminator);
strbuf_release("ed);
 }
@@ -706,6 +735,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
DIR_SHOW_IGNORED),
OPT_BOOL('u', "unmerged", &show_unmerged,
N_("show unmerged files")),
+   OPT_BOOL('F', "classify", &show_indicator,
+N_("append indicator (one of */=>@|) to entries")),
OPT__COLOR(&use_color, N_("show color")),
OPT_COLUMN(0, "column", &colopts, N_("show files in columns")),
OPT_SET_INT('1', NULL, &colopts,
-- 
2.2.0.84.ge9c7a8a

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


git@vger.kernel.org

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-list-files.txt | 4 ++--
 builtin/ls-files.c   | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 475c6da..223f6fd 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -54,8 +54,8 @@ OPTIONS
 
 -F::
 --classify::
-   Append indicator (one of `*/=>@|`, which is executable,
-   directory, socket, Solaris door, symlink, or fifo
+   Append indicator (one of `*/=>@|&`, which is executable,
+   directory, socket, Solaris door, symlink, fifo, or submodule
respectively) to entries.
 
 -R::
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index bbeb4ce..697a307 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -92,6 +92,8 @@ static void append_indicator(struct strbuf *sb, mode_t mode)
c = '|';
else if (S_ISSOCK(mode))
c = '=';
+   else if (S_ISGITLINK(mode))
+   c = '&';
 #ifdef S_ISDOOR
else if (S_ISDOOR(mode))
c = '>';
-- 
2.2.0.84.ge9c7a8a

--
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 15/21] list-files: sort output and remove duplicates

2015-01-25 Thread Nguyễn Thái Ngọc Duy
When you mix different file types, with ls-files you may get separate
listing. For example, "ls-files -cm" will show file "abc" twice: one
as part of cached list, one of modified list. With "ls" (and this
patch) they will be in a single sorted list (easier for the eye).

Duplicate entries are also removed. Note that display content is
compared, so if you have "-t" on, or you color file types differently,
you will get duplicate textual entries. This is good imo.

Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 builtin/ls-files.c | 36 
 1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index b4b24ef..154dd9d 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -53,6 +53,13 @@ static const char *tag_modified = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
+static int compare_output(const void *a_, const void *b_)
+{
+   const struct string_list_item *a = a_;
+   const struct string_list_item *b = b_;
+   return strcmp(a->util, b->util);
+}
+
 static void write_name(struct strbuf *sb, const char *name)
 {
/*
@@ -68,10 +75,12 @@ static void write_name(struct strbuf *sb, const char *name)
quote_path_relative(name, real_prefix, sb);
 }
 
-static void strbuf_fputs(struct strbuf *sb, FILE *fp)
+static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)
 {
-   if (column_active(colopts)) {
-   string_list_append(&output, strbuf_detach(sb, NULL));
+   if (column_active(colopts) || porcelain) {
+   struct string_list_item *it;
+   it = string_list_append(&output, strbuf_detach(sb, NULL));
+   it->util = (void *)full_name;
return;
}
fwrite(sb->buf, sb->len, 1, fp);
@@ -106,7 +115,7 @@ static void show_dir_entry(const char *tag, struct 
dir_entry *ent)
strbuf_reset(&sb);
strbuf_addstr(&sb, tag);
write_dir_entry(&sb, ent);
-   strbuf_fputs(&sb, stdout);
+   strbuf_fputs(&sb, ent->name, stdout);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -223,7 +232,7 @@ static void show_ce_entry(const char *tag, const struct 
cache_entry *ce)
ce_stage(ce));
}
write_ce_name(&sb, ce);
-   strbuf_fputs(&sb, stdout);
+   strbuf_fputs(&sb, ce->name, stdout);
if (debug_mode) {
const struct stat_data *sd = &ce->ce_stat_data;
 
@@ -524,6 +533,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
const char *max_prefix;
struct dir_struct dir;
struct exclude_list *el;
+   struct column_options copts;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct option builtin_ls_files_options[] = {
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -671,7 +681,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (debug_mode)
die(_("--column and --debug are incompatible"));
}
-   if (column_active(colopts))
+   if (column_active(colopts) || porcelain)
line_terminator = 0;
 
if (require_work_tree && !is_inside_work_tree())
@@ -737,13 +747,15 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (show_resolve_undo)
show_ru_info();
 
-   if (column_active(colopts)) {
-   struct column_options copts;
-   memset(&copts, 0, sizeof(copts));
-   copts.padding = 2;
-   print_columns(&output, colopts, &copts);
-   string_list_clear(&output, 0);
+   memset(&copts, 0, sizeof(copts));
+   copts.padding = 2;
+   if (porcelain) {
+   qsort(output.items, output.nr, sizeof(*output.items),
+ compare_output);
+   string_list_remove_duplicates(&output, 0);
}
+   print_columns(&output, colopts, &copts);
+   string_list_clear(&output, 0);
 
if (ps_matched) {
int bad;
-- 
2.2.0.84.ge9c7a8a

--
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 16/21] list-files: do not show duplicate cached entries

2015-01-25 Thread Nguyễn Thái Ngọc Duy
With the current show_files() "list-files -tcm" will show

  foo.c
M foo.c

The first item is redundant. If "foo.c" is modified, we know it's in
the cache. Introduce show_files_compact to do that because ls-files is
plumbing and scripts may already depend on current display behavior.

Another difference in show_files_compact() is it does not show
skip-worktree (aka outside sparse checkout) entries anymore, which
makes sense in porcelain context.

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 builtin/ls-files.c | 52 +++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 154dd9d..1a1c9c8 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -333,6 +333,53 @@ static void show_files(struct dir_struct *dir)
}
 }
 
+static void show_files_compact(struct dir_struct *dir)
+{
+   int i;
+
+   /* For cached/deleted files we don't need to even do the readdir */
+   if (show_others || show_killed) {
+   if (!show_others)
+   dir->flags |= DIR_COLLECT_KILLED_ONLY;
+   fill_directory(dir, &pathspec);
+   if (show_others)
+   show_other_files(dir);
+   if (show_killed)
+   show_killed_files(dir);
+   }
+   if (!(show_cached || show_unmerged || show_deleted || show_modified))
+   return;
+   for (i = 0; i < active_nr; i++) {
+   const struct cache_entry *ce = active_cache[i];
+   struct stat st;
+   int err, shown = 0;
+   if ((dir->flags & DIR_SHOW_IGNORED) &&
+   !ce_excluded(dir, ce))
+   continue;
+   if (show_unmerged && !ce_stage(ce))
+   continue;
+   if (ce->ce_flags & CE_UPDATE)
+   continue;
+   if (ce_skip_worktree(ce))
+   continue;
+   err = lstat(ce->name, &st);
+   if (show_deleted && err) {
+   show_ce_entry(tag_removed, ce);
+   shown = 1;
+   }
+   if (show_modified && (err || ce_modified(ce, &st, 0))) {
+   show_ce_entry(tag_modified, ce);
+   shown = 1;
+   }
+   if (ce_stage(ce)) {
+   show_ce_entry(tag_unmerged, ce);
+   shown = 1;
+   }
+   if (!shown && show_cached)
+   show_ce_entry(tag_cached, ce);
+   }
+}
+
 /*
  * Prune the index to only contain stuff starting with "prefix"
  */
@@ -743,7 +790,10 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
refresh_index(&the_index, REFRESH_QUIET | REFRESH_UNMERGED, 
&pathspec, NULL, NULL);
setup_pager();
}
-   show_files(&dir);
+   if (porcelain)
+   show_files_compact(&dir);
+   else
+   show_files(&dir);
if (show_resolve_undo)
show_ru_info();
 
-- 
2.2.0.84.ge9c7a8a

--
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 13/21] list-files: add -1 short for --no-column

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-list-files.txt | 3 +++
 builtin/ls-files.c   | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 8d285c1..1c0c877 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -50,6 +50,9 @@ OPTIONS
 --recursive::
Equivalent of `--max-depth=-1` (infinite recursion).
 
+-1::
+   Equivalent of --no-column.
+
 --color[=]::
 --no-color::
Color file names. The value must be `always`, `never`, or
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 010291c..a80ac6a 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -603,6 +603,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_("show unmerged files")),
OPT__COLOR(&use_color, N_("show color")),
OPT_COLUMN(0, "column", &colopts, N_("show files in columns")),
+   OPT_SET_INT('1', NULL, &colopts,
+   N_("shortcut for --no-column"), COL_PARSEOPT),
{ OPTION_INTEGER, 0, "max-depth", &max_depth, N_("depth"),
N_("descend at most  levels"), PARSE_OPT_NONEG,
NULL, 1 },
-- 
2.2.0.84.ge9c7a8a

--
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 17/21] list-files: show directories as well as files

2015-01-25 Thread Nguyễn Thái Ngọc Duy
The index does not store directories explicitly (except submodules) so
we have to figure them out from file list when output lis depth-limited.

The function show_as_directory() deliberately generates duplicate
directories and expects the previous patch to remove duplicates.

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 builtin/ls-files.c | 52 ++--
 1 file changed, 50 insertions(+), 2 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 1a1c9c8..29b5c2e 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -27,6 +27,8 @@ static int show_resolve_undo;
 static int show_modified;
 static int show_killed;
 static int show_valid_bit;
+static int show_tag;
+static int show_dirs;
 static int line_terminator = '\n';
 static int debug_mode;
 static int use_color;
@@ -179,6 +181,35 @@ static void show_killed_files(struct dir_struct *dir)
}
 }
 
+static int show_as_directory(const struct cache_entry *ce)
+{
+   struct strbuf sb = STRBUF_INIT;
+   const char *p;
+
+   strbuf_add(&sb, ce->name, ce_namelen(ce));
+   while (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {
+   struct strbuf sb2 = STRBUF_INIT;
+   strbuf_setlen(&sb, p - sb.buf);
+   if (!match_pathspec(&pathspec, sb.buf, sb.len,
+   max_prefix_len, NULL, 1))
+   continue;
+   write_name(&sb2, sb.buf);
+   if (want_color(use_color)) {
+   struct strbuf sb3 = STRBUF_INIT;
+   color_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);
+   strbuf_release(&sb2);
+   sb2 = sb3;
+   }
+   if (show_tag)
+   strbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));
+   strbuf_fputs(&sb2, strbuf_detach(&sb, NULL), NULL);
+   strbuf_release(&sb2);
+   return 1;
+   }
+   strbuf_release(&sb);
+   return 0;
+}
+
 static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)
 {
struct strbuf quoted = STRBUF_INIT;
@@ -194,16 +225,31 @@ static void write_ce_name(struct strbuf *sb, const struct 
cache_entry *ce)
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
static struct strbuf sb = STRBUF_INIT;
-   int len = max_prefix_len;
+   int len = max_prefix_len, saved_max_depth;
 
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of 
prefix");
 
+   if (show_dirs) {
+   /* ignore depth to catch dirs that contain matched entries */
+   saved_max_depth = pathspec.max_depth;
+   pathspec.max_depth = -1;
+   }
+
if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
len, ps_matched,
S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
return;
 
+   if (show_dirs) {
+   pathspec.max_depth = saved_max_depth;
+   if (strchr(ce->name, '/') &&
+   !match_pathspec(&pathspec, ce->name, ce_namelen(ce),
+   prefix_len, NULL, 1) &&
+   show_as_directory(ce))
+   return;
+   }
+
if (tag && *tag && show_valid_bit &&
(ce->ce_flags & CE_VALID)) {
static char alttag[4];
@@ -575,7 +621,7 @@ static int git_ls_config(const char *var, const char 
*value, void *cb)
 
 int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 {
-   int require_work_tree = 0, show_tag = 0, i;
+   int require_work_tree = 0, i;
int max_depth = -1;
const char *max_prefix;
struct dir_struct dir;
@@ -744,6 +790,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
   prefix, argv);
pathspec.max_depth = max_depth;
pathspec.recursive = 1;
+   show_dirs = porcelain && max_depth != -1;
+
 
/* Find common prefix for all pathspec's */
max_prefix = common_prefix(&pathspec);
-- 
2.2.0.84.ge9c7a8a

--
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 14/21] list-files: add -t back

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Tag "H" (cached) is not shown though because it's usually the majority
and becomes noise. Not showing it makes the other tags stand out. -t
is on by default if more than one file category is selected.

Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-list-files.txt |  6 ++
 builtin/ls-files.c   | 27 +--
 2 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 1c0c877..9d4c127 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -46,6 +46,12 @@ OPTIONS
 --unmerged::
Show unmerged files
 
+-t::
+--[no-]tag::
+   Show a tag to indicate file type. Automatically turned on with
+   multiple file selections. See linkgit::git-ls-files[1] option
+   `-t` for more information.
+
 -R::
 --recursive::
Equivalent of `--max-depth=-1` (infinite recursion).
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a80ac6a..b4b24ef 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -596,6 +596,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_("show untracked files")),
OPT_SET_INT('R', "recursive", &max_depth,
N_("shortcut for --max-depth=-1"), -1),
+   OPT_BOOL('t', "tag", &show_tag,
+   N_("identify the file status with tags")),
OPT_BIT('i', "ignored", &dir.flags,
N_("show ignored files"),
DIR_SHOW_IGNORED),
@@ -636,6 +638,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
setup_standard_excludes(&dir);
use_color = -1;
max_depth = 0;
+   show_tag = -1;
git_config(git_ls_config, NULL);
} else
git_config(git_default_config, NULL);
@@ -648,16 +651,6 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
for (i = 0; i < exclude_list.nr; i++) {
add_exclude(exclude_list.items[i].string, "", 0, el, 
--exclude_args);
}
-   if (show_tag || show_valid_bit) {
-   tag_cached = "H ";
-   tag_unmerged = "M ";
-   tag_removed = "R ";
-   tag_modified = "C ";
-   tag_other = "? ";
-   tag_killed = "K ";
-   tag_skip_worktree = "S ";
-   tag_resolve_undo = "U ";
-   }
if (show_modified || show_others || show_deleted || (dir.flags & 
DIR_SHOW_IGNORED) || show_killed)
require_work_tree = 1;
if (show_unmerged && !porcelain)
@@ -711,6 +704,20 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
  show_killed || show_modified || show_resolve_undo))
show_cached = 1;
 
+   if (show_tag == -1)
+   show_tag = (show_cached + show_deleted + show_others +
+   show_unmerged + show_killed + show_modified) > 1;
+   if (show_tag || show_valid_bit) {
+   tag_cached = porcelain ? "  " : "H ";
+   tag_unmerged = "M ";
+   tag_removed = "R ";
+   tag_modified = "C ";
+   tag_other = "? ";
+   tag_killed = "K ";
+   tag_skip_worktree = "S ";
+   tag_resolve_undo = "U ";
+   }
+
if (max_prefix)
prune_cache(max_prefix);
if (with_tree) {
-- 
2.2.0.84.ge9c7a8a

--
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 10/21] list-files: make alias 'ls' default to 'list-files'

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 Documentation/git-list-files.txt | 3 ++-
 config.c | 8 
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 3039e1e..2182a38 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -14,7 +14,8 @@ DESCRIPTION
 ---
 List files (by default in current working directory) that are in the
 index. Depending on the chosen options, maybe only modified files in
-working tree are shown, or untracked files...
+working tree are shown, or untracked files... The builtin alias "ls"
+is set to "list-files".
 
 OPTIONS
 ---
diff --git a/config.c b/config.c
index 15a2983..16209c6 100644
--- a/config.c
+++ b/config.c
@@ -40,6 +40,10 @@ static struct config_source *cf;
 
 static int zlib_compression_seen;
 
+static const char *builtin_config =
+   "[alias]\n"
+   "   ls = list-files\n";
+
 /*
  * Default config_set that contains key-value pairs from the usual set of 
config
  * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
@@ -1175,6 +1179,10 @@ int git_config_early(config_fn_t fn, void *data, const 
char *repo_config)
 
home_config_paths(&user_config, &xdg_config, "config");
 
+   if (git_config_system())
+   git_config_from_buf(fn, "", builtin_config,
+   strlen(builtin_config), data);
+
if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 
0)) {
ret += git_config_from_file(fn, git_etc_gitconfig(),
data);
-- 
2.2.0.84.ge9c7a8a

--
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 06/21] ls-files: add --color to highlight file names

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-ls-files.txt |  7 +++
 builtin/ls-files.c | 38 +++---
 2 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index e26f01f..148f226 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -147,6 +147,13 @@ a space) at the start of each line:
possible for manual inspection; the exact format may change at
any time.
 
+--color[=]::
+--no-color::
+   Color file names. The value must be `always`, `never`, or
+   `auto`. `--no-color` is equivalent to
+   `--color=never`. `--color` is equivalent to
+   `--color=auto`.
+
 \--::
Do not interpret any more arguments as options.
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index b6f0d9f..0ee4f19 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -14,6 +14,7 @@
 #include "resolve-undo.h"
 #include "string-list.h"
 #include "pathspec.h"
+#include "color.h"
 
 static int abbrev;
 static int show_deleted;
@@ -27,6 +28,7 @@ static int show_killed;
 static int show_valid_bit;
 static int line_terminator = '\n';
 static int debug_mode;
+static int use_color;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -60,7 +62,6 @@ static void write_name(struct strbuf *sb, const char *name)
strbuf_release(&sb2);
} else
quote_path_relative(name, real_prefix, sb);
-   strbuf_addch(sb, line_terminator);
 }
 
 static void strbuf_fputs(struct strbuf *sb, FILE *fp)
@@ -68,6 +69,21 @@ static void strbuf_fputs(struct strbuf *sb, FILE *fp)
fwrite(sb->buf, sb->len, 1, fp);
 }
 
+static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)
+{
+   struct strbuf quoted = STRBUF_INIT;
+   struct stat st;
+   if (stat(ent->name, &st))
+   st.st_mode = 0;
+   write_name("ed, ent->name);
+   if (want_color(use_color))
+   color_filename(sb, ent->name, quoted.buf, st.st_mode, 1);
+   else
+   strbuf_addbuf(sb, "ed);
+   strbuf_addch(sb, line_terminator);
+   strbuf_release("ed);
+}
+
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
static struct strbuf sb = STRBUF_INIT;
@@ -81,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry 
*ent)
 
strbuf_reset(&sb);
strbuf_addstr(&sb, tag);
-   write_name(&sb, ent->name);
+   write_dir_entry(&sb, ent);
strbuf_fputs(&sb, stdout);
 }
 
@@ -146,6 +162,18 @@ static void show_killed_files(struct dir_struct *dir)
}
 }
 
+static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)
+{
+   struct strbuf quoted = STRBUF_INIT;
+   write_name("ed, ce->name);
+   if (want_color(use_color))
+   color_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1);
+   else
+   strbuf_addbuf(sb, "ed);
+   strbuf_addch(sb, line_terminator);
+   strbuf_release("ed);
+}
+
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
static struct strbuf sb = STRBUF_INIT;
@@ -186,7 +214,7 @@ static void show_ce_entry(const char *tag, const struct 
cache_entry *ce)
find_unique_abbrev(ce->sha1, abbrev),
ce_stage(ce));
}
-   write_name(&sb, ce->name);
+   write_ce_name(&sb, ce);
strbuf_fputs(&sb, stdout);
if (debug_mode) {
const struct stat_data *sd = &ce->ce_stat_data;
@@ -523,6 +551,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_("if any  is not in the index, treat this as an 
error")),
OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
N_("pretend that paths removed since  are 
still present")),
+   OPT__COLOR(&use_color, N_("show color")),
OPT__ABBREV(&abbrev),
OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_END()
@@ -570,6 +599,9 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
 
+   if (want_color(use_color))
+   parse_ls_color();
+
parse_pathspec(&pathspec, 0,
   PATHSPEC_PREFER_CWD |
   PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
-- 
2.2.0.84.ge9c7a8a

--
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 08/21] ls-files: support --max-depth

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-ls-files.txt | 7 +++
 builtin/ls-files.c | 7 +++
 2 files changed, 14 insertions(+)

diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 99328b9..3d921eb 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -160,6 +160,13 @@ a space) at the start of each line:
for option syntax. `--column` and `--no-column` without options
are equivalent to 'always' and 'never' respectively.
 
+--max-depth=::
+   For each  given on command line, descend at most 
+   levels of directories. A negative value means no limit (default).
+   This option is ignored if  contains active wildcards.
+   In other words if "a*" matches a directory named "a*",
+   "*" is matched literally so --max-depth is still effective.
+
 \--::
Do not interpret any more arguments as options.
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 44e5628..09a6b8d 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -503,6 +503,7 @@ static int option_parse_exclude_standard(const struct 
option *opt,
 int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 {
int require_work_tree = 0, show_tag = 0, i;
+   int max_depth = -1;
const char *max_prefix;
struct dir_struct dir;
struct exclude_list *el;
@@ -560,6 +561,9 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_("pretend that paths removed since  are 
still present")),
OPT__COLOR(&use_color, N_("show color")),
OPT_COLUMN(0, "column", &colopts, N_("show files in columns")),
+   { OPTION_INTEGER, 0, "max-depth", &max_depth, N_("depth"),
+   N_("descend at most  levels"), PARSE_OPT_NONEG,
+   NULL, 1 },
OPT__ABBREV(&abbrev),
OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_END()
@@ -624,8 +628,11 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
 
parse_pathspec(&pathspec, 0,
   PATHSPEC_PREFER_CWD |
+  (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |
   PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
   prefix, argv);
+   pathspec.max_depth = max_depth;
+   pathspec.recursive = 1;
 
/* Find common prefix for all pathspec's */
max_prefix = common_prefix(&pathspec);
-- 
2.2.0.84.ge9c7a8a

--
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 09/21] list-files: a user friendly version of ls-files and more

2015-01-25 Thread Nguyễn Thái Ngọc Duy
This is more user friendly version of ls-files:

 * it's automatically colored and columnized
 * it refreshes the index like all porcelain commands
 * it defaults to non-recursive behavior like ls
 * :(glob) is on by default so '*.c' means a.c but not a/b.c, use
   '**/*.c' for that.
 * auto pager

The name 'ls' is not taken. It is left for the user to make an alias
with better default options.

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 .gitignore |  1 +
 Documentation/config.txt   | 10 +
 Documentation/git-list-files.txt (new) | 80 ++
 Makefile   |  1 +
 builtin/ls-files.c | 69 +++--
 command-list.txt   |  1 +
 git.c  |  1 +
 7 files changed, 159 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/git-list-files.txt

diff --git a/.gitignore b/.gitignore
index a052419..9727ecc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
+/git-list-files
 /git-log
 /git-ls-files
 /git-ls-remote
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2290c47..74da715 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -940,6 +940,12 @@ color.status.::
to red). The values of these variables may be specified as in
color.branch..
 
+color.list-files::
+   A boolean to enable/disable color in the output of
+   linkgit:git-list-files[1]. May be set to `always`, `false` (or
+   `never`) or `auto` (or `true`), in which case colors are used
+   only when the output is to a terminal. Defaults to false.
+
 color.ls.::
Use customized color for file name colorization. If not set
and the environment variable LS_COLORS is set, color settings
@@ -1012,6 +1018,10 @@ 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.list-files::
+   Specify whether to output tag listing in `git list-files` 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/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
new file mode 100644
index 000..3039e1e
--- /dev/null
+++ b/Documentation/git-list-files.txt
@@ -0,0 +1,80 @@
+git-list-files(1)
+===
+
+NAME
+
+git-list-files - List files
+
+SYNOPSIS
+
+[verse]
+'git list-files [options] [...]
+
+DESCRIPTION
+---
+List files (by default in current working directory) that are in the
+index. Depending on the chosen options, maybe only modified files in
+working tree are shown, or untracked files...
+
+OPTIONS
+---
+-c::
+--cached::
+   Show cached files (default)
+
+-d::
+--deleted::
+   Show cached files that are deleted on working directory
+
+-m::
+--modified::
+   Show cached files that have modification on working directory
+
+-o::
+--others::
+   Show untracked files (and only unignored ones unless -i is
+   specified)
+
+-i::
+--ignored::
+   Show only ignored files. When showing files in the index,
+   print only those matched by an exclude pattern. When showing
+   "other" files, show only those matched by an exclude pattern.
+
+-u::
+--unmerged::
+   Show unmerged files
+
+--color[=]::
+--no-color::
+   Color file names. The value must be `always`, `never`, or
+   `auto`. `--no-color` is equivalent to
+   `--color=never`. `--color` is equivalent to
+   `--color=auto`. See configuration variable `color.list-files`
+   for the default settings.
+
+--column[=]::
+--no-column::
+   Display files in columns. See configuration variable column.ui
+   for option syntax. `--column` and `--no-column` without options
+   are equivalent to 'always' and 'never' respectively.
+
+--max-depth=::
+   For each  given on command line, descend at most 
+   levels of directories. A negative value means no limit.
+   This option is ignored if  contains active wildcards.
+   In other words if "a*" matches a directory named "a*",
+   "*" is matched literally so --max-depth is still effective.
+   The default is `--max-depth=0`.
+
+::
+   Files to show. :(glob) magic is enabled and recursion disabled
+   by default.
+
+SEE ALSO
+
+linkgit:git-ls-files[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index 459121d..23a0751 100644
--- a/Makefile
+++ b/Makefile
@@ -587,6 +587,7 @@ BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-init$X
+BUILT_INS += git-list-files$X
 BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
diff

[PATCH 12/21] list-files: add -R/--recursive short for --max-depth=-1

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-list-files.txt | 4 
 builtin/ls-files.c   | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 2182a38..8d285c1 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -46,6 +46,10 @@ OPTIONS
 --unmerged::
Show unmerged files
 
+-R::
+--recursive::
+   Equivalent of `--max-depth=-1` (infinite recursion).
+
 --color[=]::
 --no-color::
Color file names. The value must be `always`, `never`, or
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index f2c29ce..010291c 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -594,6 +594,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_("show cached files that have modification on working 
directory")),
OPT_BOOL('o', "others", &show_others,
N_("show untracked files")),
+   OPT_SET_INT('R', "recursive", &max_depth,
+   N_("shortcut for --max-depth=-1"), -1),
OPT_BIT('i', "ignored", &dir.flags,
N_("show ignored files"),
DIR_SHOW_IGNORED),
-- 
2.2.0.84.ge9c7a8a

--
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 11/21] list-files: -u does not imply showing stages

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Showing full index entry information is something for ls-files
only. The users of "git list-files" may just want to know what entries
are not unmerged.

Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 builtin/ls-files.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index eca9407..f2c29ce 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -656,7 +656,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
}
if (show_modified || show_others || show_deleted || (dir.flags & 
DIR_SHOW_IGNORED) || show_killed)
require_work_tree = 1;
-   if (show_unmerged)
+   if (show_unmerged && !porcelain)
/*
 * There's no point in showing unmerged unless
 * you also show the stage information.
-- 
2.2.0.84.ge9c7a8a

--
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 07/21] ls-files: add --column

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-ls-files.txt |  6 ++
 builtin/ls-files.c | 28 
 2 files changed, 34 insertions(+)

diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 148f226..99328b9 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -154,6 +154,12 @@ a space) at the start of each line:
`--color=never`. `--color` is equivalent to
`--color=auto`.
 
+--column[=]::
+--no-column::
+   Display files in columns. See configuration variable column.ui
+   for option syntax. `--column` and `--no-column` without options
+   are equivalent to 'always' and 'never' respectively.
+
 \--::
Do not interpret any more arguments as options.
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 0ee4f19..44e5628 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -15,6 +15,7 @@
 #include "string-list.h"
 #include "pathspec.h"
 #include "color.h"
+#include "column.h"
 
 static int abbrev;
 static int show_deleted;
@@ -29,6 +30,7 @@ static int show_valid_bit;
 static int line_terminator = '\n';
 static int debug_mode;
 static int use_color;
+static unsigned int colopts;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -39,6 +41,7 @@ static char *ps_matched;
 static const char *with_tree;
 static int exc_given;
 static int exclude_args;
+static struct string_list output = STRING_LIST_INIT_NODUP;
 
 static const char *tag_cached = "";
 static const char *tag_unmerged = "";
@@ -66,6 +69,10 @@ static void write_name(struct strbuf *sb, const char *name)
 
 static void strbuf_fputs(struct strbuf *sb, FILE *fp)
 {
+   if (column_active(colopts)) {
+   string_list_append(&output, strbuf_detach(sb, NULL));
+   return;
+   }
fwrite(sb->buf, sb->len, 1, fp);
 }
 
@@ -552,6 +559,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
N_("pretend that paths removed since  are 
still present")),
OPT__COLOR(&use_color, N_("show color")),
+   OPT_COLUMN(0, "column", &colopts, N_("show files in columns")),
OPT__ABBREV(&abbrev),
OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_END()
@@ -596,6 +604,18 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (dir.exclude_per_dir)
exc_given = 1;
 
+   finalize_colopts(&colopts, -1);
+   if (explicitly_enable_column(colopts)) {
+   if (!line_terminator)
+   die(_("--column and -z are incompatible"));
+   if (show_resolve_undo)
+   die(_("--column and --resolve-undo are incompatible"));
+   if (debug_mode)
+   die(_("--column and --debug are incompatible"));
+   }
+   if (column_active(colopts))
+   line_terminator = 0;
+
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
 
@@ -638,6 +658,14 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (show_resolve_undo)
show_ru_info();
 
+   if (column_active(colopts)) {
+   struct column_options copts;
+   memset(&copts, 0, sizeof(copts));
+   copts.padding = 2;
+   print_columns(&output, colopts, &copts);
+   string_list_clear(&output, 0);
+   }
+
if (ps_matched) {
int bad;
bad = report_path_error(ps_matched, &pathspec, prefix);
-- 
2.2.0.84.ge9c7a8a

--
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 05/21] ls-files: buffer full item in strbuf before printing

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Buffering so that we can manipulate the strings (e.g. coloring)
further before finally printing them.

Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 builtin/ls-files.c | 48 +++-
 1 file changed, 35 insertions(+), 13 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 99cee20..b6f0d9f 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -47,18 +47,30 @@ static const char *tag_modified = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
-static void write_name(const char *name)
+static void write_name(struct strbuf *sb, const char *name)
 {
/*
 * With "--full-name", prefix_len=0; this caller needs to pass
 * an empty string in that case (a NULL is good for "").
 */
-   write_name_quoted_relative(name, prefix_len ? prefix : NULL,
-  stdout, line_terminator);
+   const char *real_prefix = prefix_len ? prefix : NULL;
+   if (!line_terminator) {
+   struct strbuf sb2 = STRBUF_INIT;
+   strbuf_addstr(sb, relative_path(name, real_prefix, &sb2));
+   strbuf_release(&sb2);
+   } else
+   quote_path_relative(name, real_prefix, sb);
+   strbuf_addch(sb, line_terminator);
+}
+
+static void strbuf_fputs(struct strbuf *sb, FILE *fp)
+{
+   fwrite(sb->buf, sb->len, 1, fp);
 }
 
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
+   static struct strbuf sb = STRBUF_INIT;
int len = max_prefix_len;
 
if (len >= ent->len)
@@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry 
*ent)
if (!dir_path_match(ent, &pathspec, len, ps_matched))
return;
 
-   fputs(tag, stdout);
-   write_name(ent->name);
+   strbuf_reset(&sb);
+   strbuf_addstr(&sb, tag);
+   write_name(&sb, ent->name);
+   strbuf_fputs(&sb, stdout);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -134,6 +148,7 @@ static void show_killed_files(struct dir_struct *dir)
 
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
+   static struct strbuf sb = STRBUF_INIT;
int len = max_prefix_len;
 
if (len >= ce_namelen(ce))
@@ -161,16 +176,18 @@ static void show_ce_entry(const char *tag, const struct 
cache_entry *ce)
tag = alttag;
}
 
+   strbuf_reset(&sb);
if (!show_stage) {
-   fputs(tag, stdout);
+   strbuf_addstr(&sb, tag);
} else {
-   printf("%s%06o %s %d\t",
-  tag,
-  ce->ce_mode,
-  find_unique_abbrev(ce->sha1,abbrev),
-  ce_stage(ce));
+   strbuf_addf(&sb, "%s%06o %s %d\t",
+   tag,
+   ce->ce_mode,
+   find_unique_abbrev(ce->sha1, abbrev),
+   ce_stage(ce));
}
-   write_name(ce->name);
+   write_name(&sb, ce->name);
+   strbuf_fputs(&sb, stdout);
if (debug_mode) {
const struct stat_data *sd = &ce->ce_stat_data;
 
@@ -206,7 +223,12 @@ 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);
+   /*
+* With "--full-name", prefix_len=0; this caller needs 
to pass
+* an empty string in that case (a NULL is good for "").
+*/
+   write_name_quoted_relative(path, prefix_len ? prefix : 
NULL,
+  stdout, line_terminator);
}
}
 }
-- 
2.2.0.84.ge9c7a8a

--
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 03/21] ls_colors.c: add a function to color a file name

2015-01-25 Thread Nguyễn Thái Ngọc Duy
The new function is based on print_color_indicator() from commit
7326d1f1a67edf21947ae98194f98c38b6e9e527 in coreutils.git.

Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 color.h |  2 ++
 ls_colors.c | 66 +
 2 files changed, 68 insertions(+)

diff --git a/color.h b/color.h
index 3eaa5bd..b6904a3 100644
--- a/color.h
+++ b/color.h
@@ -94,5 +94,7 @@ void color_print_strbuf(FILE *fp, const char *color, const 
struct strbuf *sb);
 int color_is_nil(const char *color);
 
 void parse_ls_color(void);
+void color_filename(struct strbuf *sb, const char *name,
+   const char *display_name, mode_t mode, int linkok);
 
 #endif /* COLOR_H */
diff --git a/ls_colors.c b/ls_colors.c
index 08e7068..7d2e2e0 100644
--- a/ls_colors.c
+++ b/ls_colors.c
@@ -422,3 +422,69 @@ void parse_ls_color(void)
color_symlink_as_referent = 1;
git_config(ls_colors_config, NULL);
 }
+
+void color_filename(struct strbuf *sb, const char *name,
+   const char *display_name, mode_t mode, int linkok)
+{
+   int type;
+   struct color_ext_type *ext; /* Color extension */
+
+   if (S_ISREG(mode)) {
+   type = LS_FL;
+   if ((mode & S_ISUID) != 0)
+   type = LS_SU;
+   else if ((mode & S_ISGID) != 0)
+   type = LS_SG;
+   else if ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)
+   type = LS_EX;
+   } else if (S_ISDIR(mode)) {
+   if ((mode & S_ISVTX) && (mode & S_IWOTH))
+   type = LS_TW;
+   else if ((mode & S_IWOTH) != 0)
+   type = LS_OW;
+   else if ((mode & S_ISVTX) != 0)
+   type = LS_ST;
+   else
+   type = LS_DI;
+   } else if (S_ISLNK(mode))
+   type = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;
+   else if (S_ISFIFO(mode))
+   type = LS_PI;
+   else if (S_ISSOCK(mode))
+   type = LS_SO;
+   else if (S_ISBLK(mode))
+   type = LS_BD;
+   else if (S_ISCHR(mode))
+   type = LS_CD;
+#ifdef S_ISDOOR
+   else if (S_ISDOOR(mode))
+   type = LS_DO;
+#endif
+   else
+   /* Classify a file of some other type as C_ORPHAN.  */
+   type = LS_OR;
+
+   /* Check the file's suffix only if still classified as C_FILE.  */
+   ext = NULL;
+   if (type == LS_FL) {
+   /* Test if NAME has a recognized suffix.  */
+   size_t len = strlen(name);
+   const char *p = name + len; /* Pointer to final \0. 
 */
+   for (ext = color_ext_list; ext != NULL; ext = ext->next) {
+   if (ext->ext.len <= len &&
+   !strncmp(p - ext->ext.len, ext->ext.string, 
ext->ext.len))
+   break;
+   }
+   }
+
+   if (display_name)
+   name = display_name;
+   if (ext)
+   strbuf_addf(sb, "\033[%.*sm%s%s",
+   (int)ext->seq.len, ext->seq.string,
+   name, GIT_COLOR_RESET);
+   else if (*ls_colors[type])
+   strbuf_addf(sb, "%s%s%s", ls_colors[type], name, 
GIT_COLOR_RESET);
+   else
+   strbuf_addstr(sb, name);
+}
-- 
2.2.0.84.ge9c7a8a

--
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 04/21] ls_colors.c: highlight submodules like directories

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/config.txt | 3 ++-
 ls_colors.c  | 8 +++-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2090866..2290c47 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -944,7 +944,8 @@ color.ls.::
Use customized color for file name colorization. If not set
and the environment variable LS_COLORS is set, color settings
from $LS_COLORS are used. `` can be `normal`, `file`,
-   `directory`, `symlink`, `fifo`, `socket`, `block`, `char`,
+   `directory`, `submodule`,
+   `symlink`, `fifo`, `socket`, `block`, `char`,
`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,
`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,
`multihardlink`. The values of these variables may be
diff --git a/ls_colors.c b/ls_colors.c
index 7d2e2e0..9259ad3 100644
--- a/ls_colors.c
+++ b/ls_colors.c
@@ -29,6 +29,8 @@ enum color_ls {
LS_MH,  /* multi hardlink */
LS_CL,  /* clear end of line */
 
+   LS_SUBMODULE,
+
MAX_LS
 };
 
@@ -58,7 +60,8 @@ static char ls_colors[MAX_LS][COLOR_MAXLEN] = {
GIT_COLOR_BLACK_ON_GREEN,
"",
"",
-   ""
+   "",
+   GIT_COLOR_BOLD_BLUE
 };
 
 static const char *const indicator_name[] = {
@@ -73,6 +76,7 @@ static const char * const config_name[] = {
"fifo", "socket", "block", "char", "missing", "orphan", "executable",
"door", "setuid", "setgid", "sticky", "otherwritable",
"stickyotherwritable", "cap", "multihardlink", "",
+   "submodule",
NULL
 };
 
@@ -448,6 +452,8 @@ void color_filename(struct strbuf *sb, const char *name,
type = LS_DI;
} else if (S_ISLNK(mode))
type = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN;
+   else if (S_ISGITLINK(mode))
+   type = LS_SUBMODULE;
else if (S_ISFIFO(mode))
type = LS_PI;
else if (S_ISSOCK(mode))
-- 
2.2.0.84.ge9c7a8a

--
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 02/21] ls_colors.c: parse color.ls.* from config file

2015-01-25 Thread Nguyễn Thái Ngọc Duy
This is the second (and preferred) source for color information. This
will override $LS_COLORS.

Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Documentation/config.txt | 11 +++
 ls_colors.c  | 26 ++
 2 files changed, 37 insertions(+)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 9220725..2090866 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -940,6 +940,17 @@ color.status.::
to red). The values of these variables may be specified as in
color.branch..
 
+color.ls.::
+   Use customized color for file name colorization. If not set
+   and the environment variable LS_COLORS is set, color settings
+   from $LS_COLORS are used. `` can be `normal`, `file`,
+   `directory`, `symlink`, `fifo`, `socket`, `block`, `char`,
+   `missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,
+   `sticky`, `otherwritable`, `stickyotherwritable`, `cap`,
+   `multihardlink`. The values of these variables may be
+   specified as in color.branch..
+
+
 color.ui::
This variable determines the default value for variables such
as `color.diff` and `color.grep` that control the use of color
diff --git a/ls_colors.c b/ls_colors.c
index e743315..08e7068 100644
--- a/ls_colors.c
+++ b/ls_colors.c
@@ -68,6 +68,14 @@ static const char *const indicator_name[] = {
NULL
 };
 
+static const char * const config_name[] = {
+   "", "", "", "", "normal", "file", "directory", "symlink",
+   "fifo", "socket", "block", "char", "missing", "orphan", "executable",
+   "door", "setuid", "setgid", "sticky", "otherwritable",
+   "stickyotherwritable", "cap", "multihardlink", "",
+   NULL
+};
+
 struct bin_str {
size_t len; /* Number of bytes */
const char *string; /* Pointer to the same */
@@ -285,6 +293,23 @@ static int get_funky_string(char **dest, const char **src, 
int equals_end,
return state != ST_ERROR;
 }
 
+static int ls_colors_config(const char *var, const char *value, void *cb)
+{
+   int slot;
+   if (!starts_with(var, "color.ls."))
+   return 0;
+   var += 9;
+   for (slot = 0; config_name[slot]; slot++)
+   if (!strcasecmp(var, config_name[slot]))
+   break;
+   if (!config_name[slot])
+   return 0;
+   if (!value)
+   return config_error_nonbool(var);
+   color_parse(value, ls_colors[slot]);
+   return 0;
+}
+
 void parse_ls_color(void)
 {
const char *p;  /* Pointer to character being parsed */
@@ -395,4 +420,5 @@ void parse_ls_color(void)
 
if (!strcmp(ls_colors[LS_LN], "target"))
color_symlink_as_referent = 1;
+   git_config(ls_colors_config, NULL);
 }
-- 
2.2.0.84.ge9c7a8a

--
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 01/21] ls_colors.c: add $LS_COLORS parsing code

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Reusing color settings from $LS_COLORS could give a native look and
feel on file coloring.

This code is basically from coreutils.git [1], rewritten to fit Git.

As this is from GNU ls, the environment variable CLICOLOR is not
tested. It is to be decided later whether we should ignore $LS_COLORS
if $CLICOLOR is not set on Mac or FreeBSD.

[1] commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 file
src/ls.c. This is the last GPL-2 commit before coreutils turns to
GPL-3.

Signed-off-by: Nguyễn Thái Ngọc Duy 
Signed-off-by: Junio C Hamano 
---
 Makefile  |   1 +
 color.h   |   8 ++
 ls_colors.c (new) | 398 ++
 3 files changed, 407 insertions(+)
 create mode 100644 ls_colors.c

diff --git a/Makefile b/Makefile
index 827006b..459121d 100644
--- a/Makefile
+++ b/Makefile
@@ -703,6 +703,7 @@ LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += ls_colors.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge.o
diff --git a/color.h b/color.h
index f5beab1..3eaa5bd 100644
--- a/color.h
+++ b/color.h
@@ -45,6 +45,12 @@ struct strbuf;
 #define GIT_COLOR_BG_MAGENTA   "\033[45m"
 #define GIT_COLOR_BG_CYAN  "\033[46m"
 
+#define GIT_COLOR_WHITE_ON_RED"\033[37;41m"
+#define GIT_COLOR_WHITE_ON_BLUE   "\033[37;44m"
+#define GIT_COLOR_BLACK_ON_YELLOW "\033[30;43m"
+#define GIT_COLOR_BLUE_ON_GREEN   "\033[34;42m"
+#define GIT_COLOR_BLACK_ON_GREEN  "\033[30;42m"
+
 /* A special value meaning "no color selected" */
 #define GIT_COLOR_NIL "NIL"
 
@@ -87,4 +93,6 @@ void color_print_strbuf(FILE *fp, const char *color, const 
struct strbuf *sb);
 
 int color_is_nil(const char *color);
 
+void parse_ls_color(void);
+
 #endif /* COLOR_H */
diff --git a/ls_colors.c b/ls_colors.c
new file mode 100644
index 000..e743315
--- /dev/null
+++ b/ls_colors.c
@@ -0,0 +1,398 @@
+#include "cache.h"
+#include "color.h"
+
+enum color_ls {
+   LS_LC,  /* left, unused */
+   LS_RC,  /* right, unused */
+   LS_EC,  /* end color, unused */
+   LS_RS,  /* reset */
+   LS_NO,  /* normal */
+   LS_FL,  /* file, default */
+   LS_DI,  /* directory */
+   LS_LN,  /* symlink */
+
+   LS_PI,  /* pipe */
+   LS_SO,  /* socket */
+   LS_BD,  /* block device */
+   LS_CD,  /* char device */
+   LS_MI,  /* missing file */
+   LS_OR,  /* orphaned symlink */
+   LS_EX,  /* executable */
+   LS_DO,  /* Solaris door */
+
+   LS_SU,  /* setuid */
+   LS_SG,  /* setgid */
+   LS_ST,  /* sticky */
+   LS_OW,  /* other-writable */
+   LS_TW,  /* ow with sticky */
+   LS_CA,  /* cap */
+   LS_MH,  /* multi hardlink */
+   LS_CL,  /* clear end of line */
+
+   MAX_LS
+};
+
+static char ls_colors[MAX_LS][COLOR_MAXLEN] = {
+   "",
+   "",
+   "",
+   GIT_COLOR_RESET,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_BOLD_BLUE,
+   GIT_COLOR_BOLD_CYAN,
+
+   GIT_COLOR_YELLOW,
+   GIT_COLOR_BOLD_MAGENTA,
+   GIT_COLOR_BOLD_YELLOW,
+   GIT_COLOR_BOLD_YELLOW,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_BOLD_GREEN,
+   GIT_COLOR_BOLD_MAGENTA,
+
+   GIT_COLOR_WHITE_ON_RED,
+   GIT_COLOR_BLACK_ON_YELLOW,
+   GIT_COLOR_WHITE_ON_BLUE,
+   GIT_COLOR_BLUE_ON_GREEN,
+   GIT_COLOR_BLACK_ON_GREEN,
+   "",
+   "",
+   ""
+};
+
+static const char *const indicator_name[] = {
+   "lc", "rc", "ec", "rs", "no", "fi", "di", "ln",
+   "pi", "so", "bd", "cd", "mi", "or", "ex", "do",
+   "su", "sg", "st", "ow", "tw", "ca", "mh", "cl",
+   NULL
+};
+
+struct bin_str {
+   size_t len; /* Number of bytes */
+   const char *string; /* Pointer to the same */
+};
+
+struct color_ext_type {
+   struct bin_str ext; /* The extension we're looking for */
+   struct bin_str seq; /* The sequence to output when we do */
+   struct color_ext_type *next;/* Next in list */
+};
+
+static struct color_ext_type *color_ext_list;
+
+/*
+ * When true, in a color listing, color each symlink name according to the
+ * type of file it points to.  Otherwise, color them according to the `ln'
+ * directive in LS_COLORS.  Dangling (orphan) symlinks are treated specially,
+ * regardless.  This is set when `ln=target' appears in LS_COLORS.
+ */
+static int color_symlink_as_referent;
+
+/*
+ * Parse a string as part of the LS_COLORS variable; this may involve
+ * decoding all kinds of esca

[PATCH 00/21] nd/list-files updates

2015-01-25 Thread Nguyễn Thái Ngọc Duy
Changes since 'pu' version:

 - refresh_index() in 09/21 is set not to show "XXX: needs merge"
   messages, cluttering "list-files -u" output
 - new patch 10/21 to add a default alias 'ls' to 'list-files',
   of course overridable by the user
 - fix "list-files -u" not showing anything because
   show_files_compact in 16/21 ignores show_unmerged flag
 - directory listing in 17/21 is rewritten to work filtering
   (e.g. -m, -M...)
 - new patch 21/21 adds tests for the series

Nguyễn Thái Ngọc Duy (21):
  ls_colors.c: add $LS_COLORS parsing code
  ls_colors.c: parse color.ls.* from config file
  ls_colors.c: add a function to color a file name
  ls_colors.c: highlight submodules like directories
  ls-files: buffer full item in strbuf before printing
  ls-files: add --color to highlight file names
  ls-files: add --column
  ls-files: support --max-depth
  list-files: a user friendly version of ls-files and more
  list-files: make alias 'ls' default to 'list-files'
  list-files: -u does not imply showing stages
  list-files: add -R/--recursive short for --max-depth=-1
  list-files: add -1 short for --no-column
  list-files: add -t back
  list-files: sort output and remove duplicates
  list-files: do not show duplicate cached entries
  list-files: show directories as well as files
  list-files: add -F/--classify
  list-files -F: show submodules with the new indicator '&'
  list-files: -M aka diff-cached
  t3080: tests for git-list-files

Total diff against 'pu' is something like this

-- 8< --
diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index c57129b..223f6fd 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -14,7 +14,8 @@ DESCRIPTION
 ---
 List files (by default in current working directory) that are in the
 index. Depending on the chosen options, maybe only modified files in
-working tree are shown, or untracked files...
+working tree are shown, or untracked files... The builtin alias "ls"
+is set to "list-files".
 
 OPTIONS
 ---
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 02a9ac1..b04c712 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -213,6 +213,37 @@ static void show_killed_files(struct dir_struct *dir)
}
 }
 
+static int show_as_directory(const struct cache_entry *ce)
+{
+   struct strbuf sb = STRBUF_INIT;
+   const char *p;
+
+   strbuf_add(&sb, ce->name, ce_namelen(ce));
+   while (sb.len && (p = strrchr(sb.buf, '/')) != NULL) {
+   struct strbuf sb2 = STRBUF_INIT;
+   strbuf_setlen(&sb, p - sb.buf);
+   if (!match_pathspec(&pathspec, sb.buf, sb.len,
+   max_prefix_len, NULL, 1))
+   continue;
+   write_name(&sb2, sb.buf);
+   if (want_color(use_color)) {
+   struct strbuf sb3 = STRBUF_INIT;
+   color_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1);
+   strbuf_release(&sb2);
+   sb2 = sb3;
+   }
+   if (show_tag)
+   strbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached));
+   if (show_indicator)
+   append_indicator(&sb2, S_IFDIR);
+   strbuf_fputs(&sb2, strbuf_detach(&sb, NULL), NULL);
+   strbuf_release(&sb2);
+   return 1;
+   }
+   strbuf_release(&sb);
+   return 0;
+}
+
 static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)
 {
struct strbuf quoted = STRBUF_INIT;
@@ -230,16 +261,31 @@ static void write_ce_name(struct strbuf *sb, const struct 
cache_entry *ce)
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
static struct strbuf sb = STRBUF_INIT;
-   int len = max_prefix_len;
+   int len = max_prefix_len, saved_max_depth;
 
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of 
prefix");
 
+   if (show_dirs) {
+   /* ignore depth to catch dirs that contain matched entries */
+   saved_max_depth = pathspec.max_depth;
+   pathspec.max_depth = -1;
+   }
+
if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
len, ps_matched,
S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
return;
 
+   if (show_dirs) {
+   pathspec.max_depth = saved_max_depth;
+   if (strchr(ce->name, '/') &&
+   !match_pathspec(&pathspec, ce->name, ce_namelen(ce),
+   prefix_len, NULL, 1) &&
+   show_as_directory(ce))
+   return;
+   }
+
if (tag && *tag && show_valid_bit &&
(ce->ce_flags & CE_VALID)) {
static char alttag[4];
@@ -348,7 +394,7 @@ static void show_files(struct dir_struc