diff mbox series

ls-files.c: add --only-object-name option

Message ID pull.1250.git.1654509678718.gitgitgadget@gmail.com (mailing list archive)
State New, archived
Headers show
Series ls-files.c: add --only-object-name option | expand

Commit Message

ZheNing Hu June 6, 2022, 10:01 a.m. UTC
From: ZheNing Hu <adlternative@gmail.com>

`git ls-files --stage` default output format is:

[<tag> ]<mode> <object> <stage> <file>

sometime we want to find a path's corresponding objectname,
we will parse the output and extract objectname from it
again and again.

So introduce a new option `--only-object-name` which can only
output objectname when giving `--stage` or `--resolve-undo`.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    ls-files.c: add --only-object-name option
    
    Something we want to extract objectname from git ls-files --stage, but
    git ls-file don't support something like --format=%(objectname) (which
    git ls-tree have implemented)
    
    So now add a new option --only-object-name which can only output
    objectname.
    
    (Maybe we should add something like git ls-files --format ?)

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1250%2Fadlternative%2Fzh%2Fls-file-only-objectname-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1250/adlternative/zh/ls-file-only-objectname-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1250

 Documentation/git-ls-files.txt |  6 +++++-
 builtin/ls-files.c             | 18 +++++++++++++++++-
 t/t2030-unresolve-info.sh      | 33 +++++++++++++++++++++++++++++++++
 t/t3004-ls-files-basic.sh      | 32 ++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 2 deletions(-)


base-commit: ab336e8f1c8009c8b1aab8deb592148e69217085

Comments

Ævar Arnfjörð Bjarmason June 6, 2022, 5:42 p.m. UTC | #1
On Mon, Jun 06 2022, ZheNing Hu via GitGitGadget wrote:

> From: ZheNing Hu <adlternative@gmail.com>
>
> `git ls-files --stage` default output format is:
>
> [<tag> ]<mode> <object> <stage> <file>
>
> sometime we want to find a path's corresponding objectname,
> we will parse the output and extract objectname from it
> again and again.
>
> So introduce a new option `--only-object-name` which can only
> output objectname when giving `--stage` or `--resolve-undo`.
>
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>     ls-files.c: add --only-object-name option
>     
>     Something we want to extract objectname from git ls-files --stage, but
>     git ls-file don't support something like --format=%(objectname) (which
>     git ls-tree have implemented)
>     
>     So now add a new option --only-object-name which can only output
>     objectname.
>     
>     (Maybe we should add something like git ls-files --format ?)

Yes I think that would be very useful, especially if we could see if
some of the code could be shared (maybe not).

But in any case shouldn't this be called --name-only to go with "git
ls-tree"'s version of this? Or is there some subtle difference I'm
missing...?

> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1250%2Fadlternative%2Fzh%2Fls-file-only-objectname-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1250/adlternative/zh/ls-file-only-objectname-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1250
>
>  Documentation/git-ls-files.txt |  6 +++++-
>  builtin/ls-files.c             | 18 +++++++++++++++++-
>  t/t2030-unresolve-info.sh      | 33 +++++++++++++++++++++++++++++++++
>  t/t3004-ls-files-basic.sh      | 32 ++++++++++++++++++++++++++++++++
>  4 files changed, 87 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
> index 0dabf3f0ddc..0e3f4f094f3 100644
> --- a/Documentation/git-ls-files.txt
> +++ b/Documentation/git-ls-files.txt
> @@ -13,7 +13,7 @@ SYNOPSIS
>  		[-c|--cached] [-d|--deleted] [-o|--others] [-i|--|ignored]
>  		[-s|--stage] [-u|--unmerged] [-k|--|killed] [-m|--modified]
>  		[--directory [--no-empty-directory]] [--eol]
> -		[--deduplicate]
> +		[--deduplicate] [--only-object-name]
>  		[-x <pattern>|--exclude=<pattern>]
>  		[-X <file>|--exclude-from=<file>]
>  		[--exclude-per-directory=<file>]
> @@ -88,6 +88,10 @@ OPTIONS
>  	When any of the `-t`, `--unmerged`, or `--stage` option is
>  	in use, this option has no effect.
>  
> +--only-object-name:
> +	When giving `--stage` or `--resolve-undo` , only output `<object>`
> +	instead of `[<tag> ]<mode> <object> <stage> <file>` format.
> +
>  -x <pattern>::
>  --exclude=<pattern>::
>  	Skip untracked files matching pattern.
> diff --git a/builtin/ls-files.c b/builtin/ls-files.c
> index e791b65e7e9..fd9c10e9f94 100644
> --- a/builtin/ls-files.c
> +++ b/builtin/ls-files.c
> @@ -26,6 +26,7 @@ static int show_deleted;
>  static int show_cached;
>  static int show_others;
>  static int show_stage;
> +static int only_object_name;
>  static int show_unmerged;
>  static int show_resolve_undo;
>  static int show_modified;
> @@ -241,10 +242,15 @@ static void show_ce(struct repository *repo, struct dir_struct *dir,
>  		if (!show_stage) {
>  			fputs(tag, stdout);
>  		} else {
> +			const char *object_name = repo_find_unique_abbrev(repo, &ce->oid, abbrev);
> +			if (only_object_name) {
> +				printf("%s%c", object_name, line_terminator);
> +				return;
> +			}
>  			printf("%s%06o %s %d\t",
>  			       tag,
>  			       ce->ce_mode,
> -			       repo_find_unique_abbrev(repo, &ce->oid, abbrev),
> +			       object_name,
>  			       ce_stage(ce));
>  		}
>  		write_eolinfo(repo->index, ce, fullname);
> @@ -274,6 +280,10 @@ static void show_ru_info(struct index_state *istate)
>  		for (i = 0; i < 3; i++) {
>  			if (!ui->mode[i])
>  				continue;
> +			if (only_object_name) {
> +				printf("%s%c", find_unique_abbrev(&ui->oid[i], abbrev), line_terminator);
> +				continue;
> +			}
>  			printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
>  			       find_unique_abbrev(&ui->oid[i], abbrev),
>  			       i + 1);
> @@ -635,6 +645,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
>  			DIR_SHOW_IGNORED),
>  		OPT_BOOL('s', "stage", &show_stage,
>  			N_("show staged contents' object name in the output")),
> +		OPT_BOOL(0, "only-object-name", &only_object_name,
> +			N_("only show staged contents' object name in the output")),
>  		OPT_BOOL('k', "killed", &show_killed,
>  			N_("show files on the filesystem that need to be removed")),
>  		OPT_BIT(0, "directory", &dir.flags,
> @@ -734,6 +746,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
>  		die("ls-files --recurse-submodules does not support "
>  		    "--error-unmatch");
>  
> +	if (only_object_name && !show_stage && !show_resolve_undo)
> +		die("ls-files --only-object-name only used with --stage "
> +		    "or --resolve-undo");

missing _().

> +
>  	parse_pathspec(&pathspec, 0,
>  		       PATHSPEC_PREFER_CWD,
>  		       prefix, argv);
> diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
> index f691e6d9032..d940226c5f9 100755
> --- a/t/t2030-unresolve-info.sh
> +++ b/t/t2030-unresolve-info.sh
> @@ -32,6 +32,31 @@ check_resolve_undo () {
>  	test_cmp "$msg.expect" "$msg.actual"
>  }
>  
> +check_resolve_undo_only_object_name() {
> +	msg=$1
> +	shift
> +	while case $# in
> +	0)	break ;;
> +	1|2|3)	die "Bug in check-resolve-undo test" ;;

Use the "BUG"  helper in thaht case.
> +	esac
> +	do
> +		path=$1
> +		shift
> +		for stage in 1 2 3
> +		do
> +			sha1=$1
> +			shift
> +			case "$sha1" in
> +			'') continue ;;
> +			esac
> +			sha1=$(git rev-parse --verify "$sha1")

missing && here when invoking "git".

> +test_expect_success 'git ls-files --stage with --only-object-name' '
> +	git init test &&
> +	test_when_finished "rm -rf test" &&

FWIW you can do all the below with -C to relevant commands and skip the
sub-shell.

> +	(
> +		cd test &&
> +		echo a >a.txt &&
> +		echo b >b.txt &&
> +		git add a.txt b.txt &&
> +		oid1=$(git hash-object a.txt) &&
> +		oid2=$(git hash-object b.txt) &&
> +		git ls-files --stage --only-object-name >actual &&
> +		cat >expect <<-EOF &&
> +		$oid1
> +		$oid2
> +		EOF
> +		test_cmp expect actual
> +	)
> +'
> +
> +test_expect_success 'git ls-files --only-object-name without --stage or --resolve-undo' '
> +	git init test &&
> +	test_when_finished "rm -rf test" &&
> +	(
> +		cd test &&
> +		echo a >a.txt &&
> +		echo b >b.txt &&
> +		git add a.txt b.txt &&
> +		test_must_fail git ls-files --only-object-name 2>stderr &&
> +		test_i18ngrep "fatal: ls-files --only-object-name only used with --stage or --resolve-undo" stderr

use "grep", not "test_i18ngrep".
ZheNing Hu June 8, 2022, 2:38 p.m. UTC | #2
Ævar Arnfjörð Bjarmason <avarab@gmail.com> 于2022年6月7日周二 01:45写道:
>
>
> On Mon, Jun 06 2022, ZheNing Hu via GitGitGadget wrote:
>
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > `git ls-files --stage` default output format is:
> >
> > [<tag> ]<mode> <object> <stage> <file>
> >
> > sometime we want to find a path's corresponding objectname,
> > we will parse the output and extract objectname from it
> > again and again.
> >
> > So introduce a new option `--only-object-name` which can only
> > output objectname when giving `--stage` or `--resolve-undo`.
> >
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > ---
> >     ls-files.c: add --only-object-name option
> >
> >     Something we want to extract objectname from git ls-files --stage, but
> >     git ls-file don't support something like --format=%(objectname) (which
> >     git ls-tree have implemented)
> >
> >     So now add a new option --only-object-name which can only output
> >     objectname.
> >
> >     (Maybe we should add something like git ls-files --format ?)
>
> Yes I think that would be very useful, especially if we could see if
> some of the code could be shared (maybe not).
>

Maybe I can try to implement it :-)

> But in any case shouldn't this be called --name-only to go with "git
> ls-tree"'s version of this? Or is there some subtle difference I'm
> missing...?
>

Eh, git ls-tree --name-only will only show file paths, so maybe
--only-object-name
will be better than --object-name-only in git ls-files.

> > +check_resolve_undo_only_object_name() {
> > +     msg=$1
> > +     shift
> > +     while case $# in
> > +     0)      break ;;
> > +     1|2|3)  die "Bug in check-resolve-undo test" ;;
>
> Use the "BUG"  helper in thaht case.
> > +     esac
> > +     do
> > +             path=$1
> > +             shift
> > +             for stage in 1 2 3
> > +             do
> > +                     sha1=$1
> > +                     shift
> > +                     case "$sha1" in
> > +                     '') continue ;;
> > +                     esac
> > +                     sha1=$(git rev-parse --verify "$sha1")
>
> missing && here when invoking "git".
>

Sorry, but this function check_resolve_undo_only_object_name() is
just a mock version of check_resolve_undo(), so maybe I should fix
this function first?
Junio C Hamano June 8, 2022, 4:09 p.m. UTC | #3
ZheNing Hu <adlternative@gmail.com> writes:

>> But in any case shouldn't this be called --name-only to go with "git
>> ls-tree"'s version of this? Or is there some subtle difference I'm
>> missing...?
>
> Eh, git ls-tree --name-only will only show file paths, so maybe
> --only-object-name will be better than --object-name-only in git
> ls-files.

Yeah, it is not "--name-only" which is about paths, but
"--object-name-only" would be more correct but it is a tad long.

I think ls-tree learned "--object-only" for that fairly recently.
When in doubt, always check the documentation of a similar command
for inspiration.

Thanks.
diff mbox series

Patch

diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 0dabf3f0ddc..0e3f4f094f3 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -13,7 +13,7 @@  SYNOPSIS
 		[-c|--cached] [-d|--deleted] [-o|--others] [-i|--|ignored]
 		[-s|--stage] [-u|--unmerged] [-k|--|killed] [-m|--modified]
 		[--directory [--no-empty-directory]] [--eol]
-		[--deduplicate]
+		[--deduplicate] [--only-object-name]
 		[-x <pattern>|--exclude=<pattern>]
 		[-X <file>|--exclude-from=<file>]
 		[--exclude-per-directory=<file>]
@@ -88,6 +88,10 @@  OPTIONS
 	When any of the `-t`, `--unmerged`, or `--stage` option is
 	in use, this option has no effect.
 
+--only-object-name:
+	When giving `--stage` or `--resolve-undo` , only output `<object>`
+	instead of `[<tag> ]<mode> <object> <stage> <file>` format.
+
 -x <pattern>::
 --exclude=<pattern>::
 	Skip untracked files matching pattern.
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index e791b65e7e9..fd9c10e9f94 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -26,6 +26,7 @@  static int show_deleted;
 static int show_cached;
 static int show_others;
 static int show_stage;
+static int only_object_name;
 static int show_unmerged;
 static int show_resolve_undo;
 static int show_modified;
@@ -241,10 +242,15 @@  static void show_ce(struct repository *repo, struct dir_struct *dir,
 		if (!show_stage) {
 			fputs(tag, stdout);
 		} else {
+			const char *object_name = repo_find_unique_abbrev(repo, &ce->oid, abbrev);
+			if (only_object_name) {
+				printf("%s%c", object_name, line_terminator);
+				return;
+			}
 			printf("%s%06o %s %d\t",
 			       tag,
 			       ce->ce_mode,
-			       repo_find_unique_abbrev(repo, &ce->oid, abbrev),
+			       object_name,
 			       ce_stage(ce));
 		}
 		write_eolinfo(repo->index, ce, fullname);
@@ -274,6 +280,10 @@  static void show_ru_info(struct index_state *istate)
 		for (i = 0; i < 3; i++) {
 			if (!ui->mode[i])
 				continue;
+			if (only_object_name) {
+				printf("%s%c", find_unique_abbrev(&ui->oid[i], abbrev), line_terminator);
+				continue;
+			}
 			printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
 			       find_unique_abbrev(&ui->oid[i], abbrev),
 			       i + 1);
@@ -635,6 +645,8 @@  int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 			DIR_SHOW_IGNORED),
 		OPT_BOOL('s', "stage", &show_stage,
 			N_("show staged contents' object name in the output")),
+		OPT_BOOL(0, "only-object-name", &only_object_name,
+			N_("only show staged contents' object name in the output")),
 		OPT_BOOL('k', "killed", &show_killed,
 			N_("show files on the filesystem that need to be removed")),
 		OPT_BIT(0, "directory", &dir.flags,
@@ -734,6 +746,10 @@  int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 		die("ls-files --recurse-submodules does not support "
 		    "--error-unmatch");
 
+	if (only_object_name && !show_stage && !show_resolve_undo)
+		die("ls-files --only-object-name only used with --stage "
+		    "or --resolve-undo");
+
 	parse_pathspec(&pathspec, 0,
 		       PATHSPEC_PREFER_CWD,
 		       prefix, argv);
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
index f691e6d9032..d940226c5f9 100755
--- a/t/t2030-unresolve-info.sh
+++ b/t/t2030-unresolve-info.sh
@@ -32,6 +32,31 @@  check_resolve_undo () {
 	test_cmp "$msg.expect" "$msg.actual"
 }
 
+check_resolve_undo_only_object_name() {
+	msg=$1
+	shift
+	while case $# in
+	0)	break ;;
+	1|2|3)	die "Bug in check-resolve-undo test" ;;
+	esac
+	do
+		path=$1
+		shift
+		for stage in 1 2 3
+		do
+			sha1=$1
+			shift
+			case "$sha1" in
+			'') continue ;;
+			esac
+			sha1=$(git rev-parse --verify "$sha1")
+			printf "%s\n" $sha1
+		done
+	done >"$msg.expect" &&
+	git ls-files --resolve-undo --only-object-name >"$msg.actual" &&
+	test_cmp "$msg.expect" "$msg.actual"
+}
+
 prime_resolve_undo () {
 	git reset --hard &&
 	git checkout second^0 &&
@@ -194,4 +219,12 @@  test_expect_success 'rerere forget (add-add conflict)' '
 	test_i18ngrep "no remembered" actual
 '
 
+test_expect_success '--resolve-undo with --only-object-name' '
+	prime_resolve_undo &&
+	check_resolve_undo_only_object_name kept fi/le initial:fi/le second:fi/le third:fi/le &&
+	git checkout second^0 &&
+	echo switching clears &&
+	check_resolve_undo cleared
+'
+
 test_done
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
index a16e25c79bd..e42f6e7e548 100755
--- a/t/t3004-ls-files-basic.sh
+++ b/t/t3004-ls-files-basic.sh
@@ -52,4 +52,36 @@  test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git ls-files --stage with --only-object-name' '
+	git init test &&
+	test_when_finished "rm -rf test" &&
+	(
+		cd test &&
+		echo a >a.txt &&
+		echo b >b.txt &&
+		git add a.txt b.txt &&
+		oid1=$(git hash-object a.txt) &&
+		oid2=$(git hash-object b.txt) &&
+		git ls-files --stage --only-object-name >actual &&
+		cat >expect <<-EOF &&
+		$oid1
+		$oid2
+		EOF
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'git ls-files --only-object-name without --stage or --resolve-undo' '
+	git init test &&
+	test_when_finished "rm -rf test" &&
+	(
+		cd test &&
+		echo a >a.txt &&
+		echo b >b.txt &&
+		git add a.txt b.txt &&
+		test_must_fail git ls-files --only-object-name 2>stderr &&
+		test_i18ngrep "fatal: ls-files --only-object-name only used with --stage or --resolve-undo" stderr
+	)
+'
+
 test_done