Skip to content

Commit

Permalink
ls-files.c: add --deduplicate option
Browse files Browse the repository at this point in the history
In order to provide users a better experience
when viewing information about files in the index
and the working tree, the `--deduplicate` option will suppress
some duplicate name under some conditions.

In a merge conflict, one file name of "git ls-files" output may
appear multiple times. For example,now there is an unmerged path
`a.c`,`a.c` will appear three times in the output of
"git ls-files".We can use "git ls-files --deduplicate" to output
`a.c` only one time.(unless `--stage` or `--unmerged` is
used to view all the detailed information in the index)

In addition, if you use both `--delete` and `--modify` at
the same time, The `--deduplicate` option
can also suppress file name output.

Additional instructions:
In order to display entries information,`deduplicate` suppresses
the output of duplicate file names, not the output of duplicate
entries information, so under the option of `-t`, `--stage`, `--unmerge`,
`--deduplicate` will have no effect.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
  • Loading branch information
adlternative committed Jan 19, 2021
1 parent 802ff80 commit e9c5318
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 5 deletions.
5 changes: 5 additions & 0 deletions Documentation/git-ls-files.txt
Expand Up @@ -13,6 +13,7 @@ SYNOPSIS
(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
(-[c|d|o|i|s|u|k|m])*
[--eol]
[--deduplicate]
[-x <pattern>|--exclude=<pattern>]
[-X <file>|--exclude-from=<file>]
[--exclude-per-directory=<file>]
Expand Down Expand Up @@ -81,6 +82,10 @@ OPTIONS
\0 line termination on output and do not quote filenames.
See OUTPUT below for more information.

--deduplicate::
Suppress duplicate entries when there are unmerged paths in index
or `--deleted` and `--modified` are combined.

-x <pattern>::
--exclude=<pattern>::
Skip untracked files matching pattern.
Expand Down
32 changes: 27 additions & 5 deletions builtin/ls-files.c
Expand Up @@ -35,6 +35,7 @@ static int line_terminator = '\n';
static int debug_mode;
static int show_eol;
static int recurse_submodules;
static int skipping_duplicates;

static const char *prefix;
static int max_prefix_len;
Expand Down Expand Up @@ -301,6 +302,7 @@ static void show_files(struct repository *repo, struct dir_struct *dir)
{
int i;
struct strbuf fullname = STRBUF_INIT;
const struct cache_entry *last_shown_ce;

/* For cached/deleted files we don't need to even do the readdir */
if (show_others || show_killed) {
Expand All @@ -314,37 +316,54 @@ static void show_files(struct repository *repo, struct dir_struct *dir)
}
if (! (show_cached || show_stage || show_deleted || show_modified))
return;
last_shown_ce = NULL;
for (i = 0; i < repo->index->cache_nr; i++) {
const struct cache_entry *ce = repo->index->cache[i];
struct stat st;
int err;

construct_fullname(&fullname, repo, ce);

if (skipping_duplicates && last_shown_ce &&
!strcmp(last_shown_ce->name,ce->name))
continue;
if ((dir->flags & DIR_SHOW_IGNORED) &&
!ce_excluded(dir, repo->index, fullname.buf, ce))
continue;
if (ce->ce_flags & CE_UPDATE)
continue;
if (show_cached || show_stage) {
if (skipping_duplicates && last_shown_ce &&
!strcmp(last_shown_ce->name,ce->name))
continue;
if (!show_unmerged || ce_stage(ce))
show_ce(repo, dir, ce, fullname.buf,
ce_stage(ce) ? tag_unmerged :
(ce_skip_worktree(ce) ? tag_skip_worktree :
tag_cached));
if (show_cached && skipping_duplicates)
last_shown_ce = ce;
}
if (ce_skip_worktree(ce))
continue;
if (skipping_duplicates && last_shown_ce &&
!strcmp(last_shown_ce->name,ce->name))
continue;
err = lstat(fullname.buf, &st);
if (err) {
if (errno != ENOENT && errno != ENOTDIR)
error_errno("cannot lstat '%s'", fullname.buf);
if (show_deleted)
if (skipping_duplicates && show_deleted && show_modified)
show_ce(repo, dir, ce, fullname.buf, tag_removed);
if (show_modified)
show_ce(repo, dir, ce, fullname.buf, tag_modified);
else {
if (errno != ENOENT && errno != ENOTDIR)
error_errno("cannot lstat '%s'", fullname.buf);
if (show_deleted)
show_ce(repo, dir, ce, fullname.buf, tag_removed);
if (show_modified)
show_ce(repo, dir, ce, fullname.buf, tag_modified);
}
} else if (show_modified && ie_modified(repo->index, ce, &st, 0))
show_ce(repo, dir, ce, fullname.buf, tag_modified);
last_shown_ce = ce;
}

strbuf_release(&fullname);
Expand Down Expand Up @@ -571,6 +590,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("pretend that paths removed since <tree-ish> are still present")),
OPT__ABBREV(&abbrev),
OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_BOOL(0,"deduplicate",&skipping_duplicates,N_("suppress duplicate entries")),
OPT_END()
};

Expand Down Expand Up @@ -610,6 +630,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
* you also show the stage information.
*/
show_stage = 1;
if (show_tag || show_stage)
skipping_duplicates = 0;
if (dir.exclude_per_dir)
exc_given = 1;

Expand Down
66 changes: 66 additions & 0 deletions t/t3012-ls-files-dedup.sh
@@ -0,0 +1,66 @@
#!/bin/sh

test_description='git ls-files --deduplicate test'

. ./test-lib.sh

test_expect_success 'setup' '
>a.txt &&
>b.txt &&
>delete.txt &&
git add a.txt b.txt delete.txt &&
git commit -m base &&
echo a >a.txt &&
echo b >b.txt &&
echo delete >delete.txt &&
git add a.txt b.txt delete.txt &&
git commit -m tip &&
git tag tip &&
git reset --hard HEAD^ &&
echo change >a.txt &&
git commit -a -m side &&
git tag side
'

test_expect_success 'git ls-files --deduplicate to show unique unmerged path' '
test_must_fail git merge tip &&
git ls-files --deduplicate >actual &&
cat >expect <<-\EOF &&
a.txt
b.txt
delete.txt
EOF
test_cmp expect actual &&
git merge --abort
'

test_expect_success 'git ls-files -d -m --deduplicate with different display options' '
git reset --hard side &&
test_must_fail git merge tip &&
rm delete.txt &&
git ls-files -d -m --deduplicate >actual &&
cat >expect <<-\EOF &&
a.txt
delete.txt
EOF
test_cmp expect actual &&
git ls-files -d -m -t --deduplicate >actual &&
cat >expect <<-\EOF &&
C a.txt
C a.txt
C a.txt
R delete.txt
C delete.txt
EOF
test_cmp expect actual &&
git ls-files -d -m -c --deduplicate >actual &&
cat >expect <<-\EOF &&
a.txt
b.txt
delete.txt
EOF
test_cmp expect actual &&
git merge --abort
'

test_done

0 comments on commit e9c5318

Please sign in to comment.