All of lore.kernel.org
 help / color / mirror / Atom feed
* How to display "HEAD~*" in "git log"
@ 2022-07-05  7:11 wuzhouhui
  2022-07-05  9:25 ` Konstantin Khomoutov
  0 siblings, 1 reply; 11+ messages in thread
From: wuzhouhui @ 2022-07-05  7:11 UTC (permalink / raw)
  To: git

Hi

I frequently use "git rebase" to move commit to a specific location, to
know which commit ID as param of "git rebase", I have to list all commits
by "git log --oneline", then copy specific commit ID, and executing "git
rebase" as

   git rebase -i <copied commit ID>~

because SHA sum is hard to memorize, so I have to use copy and past,
which is too boring. So, I wonder if there is a way to let "git log"
display commits like following:

   HEAD   <one line commit message>
   HEAD~1 <one line commit message>
HEAD~2 <one line commit message>
HEAD~3 <one line commit message>
   ...

With these "HEAD~*", I can easily directly type them and no need to
move my fingers out of keyboard.

Thanks.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-05  7:11 How to display "HEAD~*" in "git log" wuzhouhui
@ 2022-07-05  9:25 ` Konstantin Khomoutov
  2022-07-05  9:30   ` Konstantin Khomoutov
  2022-07-06 14:38   ` Jeff King
  0 siblings, 2 replies; 11+ messages in thread
From: Konstantin Khomoutov @ 2022-07-05  9:25 UTC (permalink / raw)
  To: wuzhouhui; +Cc: git

On Tue, Jul 05, 2022 at 03:11:49PM +0800, wuzhouhui wrote:

> I frequently use "git rebase" to move commit to a specific location, to
> know which commit ID as param of "git rebase", I have to list all commits
> by "git log --oneline", then copy specific commit ID, and executing "git
> rebase" as
> 
>   git rebase -i <copied commit ID>~
> 
> because SHA sum is hard to memorize, so I have to use copy and past,
> which is too boring. So, I wonder if there is a way to let "git log"
> display commits like following:
> 
>   HEAD   <one line commit message>
>   HEAD~1 <one line commit message>
> HEAD~2 <one line commit message>
> HEAD~3 <one line commit message>
>   ...
> 
> With these "HEAD~*", I can easily directly type them and no need to
> move my fingers out of keyboard.

You can script this. Provided you have a POSIX-compatible shell (such as
Bash), the encantation would read something like

 $ git log --oneline | { n=0; while read line; do printf '%d\t%s\n' $n "$line"; done; }

This is admittedly not convenient so it makes sense to turn into an alias:

 [alias]
   relog = "!relog() { git log --oneline \"$@\" | { n=0; while read line; do printf 'HEAD~%d\t%s\n' $n \"$line\"; n=$((n+1)); done; }; }; relog"

Then the call

 $ git relog

would output something like

HEAD~0	deadbeef Commit message
HEAD~1	fadedfac Another commit message
HEAD~2  12345678 Yet another commit message

...and so on.

It's easy to make that script not output "~0" for the very first entry and
output just "~" instead of "~1" for the second, but I what's presented is
enough for an example.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-05  9:25 ` Konstantin Khomoutov
@ 2022-07-05  9:30   ` Konstantin Khomoutov
  2022-07-05  9:36     ` wuzhouhui
  2022-07-06 14:38   ` Jeff King
  1 sibling, 1 reply; 11+ messages in thread
From: Konstantin Khomoutov @ 2022-07-05  9:30 UTC (permalink / raw)
  To: wuzhouhui; +Cc: git

On Tue, Jul 05, 2022 at 12:25:14PM +0300, Konstantin Khomoutov wrote:

[...]
> This is admittedly not convenient so it makes sense to turn into an alias:
> 
>  [alias]
>    relog = "!relog() { git log --oneline \"$@\" | { n=0; while read line; do printf 'HEAD~%d\t%s\n' $n \"$line\"; n=$((n+1)); done; }; }; relog"
[...]

An alternative to an alias it creating a specially named script file which
must be executable and located in a directorly listed in the environment
variable PATH.
For instance, if you have ~/bin listed in your PATH, you can create a file
named "git-relog" containing the encantation from my first mail in this
thread, then call

  $ git relog

and that script will be found and executed.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-05  9:30   ` Konstantin Khomoutov
@ 2022-07-05  9:36     ` wuzhouhui
  2022-07-06 12:38       ` kostix
  0 siblings, 1 reply; 11+ messages in thread
From: wuzhouhui @ 2022-07-05  9:36 UTC (permalink / raw)
  To: Konstantin Khomoutov; +Cc: git


On 7/5/22 17:30, Konstantin Khomoutov wrote:
> On Tue, Jul 05, 2022 at 12:25:14PM +0300, Konstantin Khomoutov wrote:
>
> [...]
>> This is admittedly not convenient so it makes sense to turn into an alias:
>>
>>   [alias]
>>     relog = "!relog() { git log --oneline \"$@\" | { n=0; while read line; do printf 'HEAD~%d\t%s\n' $n \"$line\"; n=$((n+1)); done; }; }; relog"
> [...]
>
> An alternative to an alias it creating a specially named script file which
> must be executable and located in a directorly listed in the environment
> variable PATH.
> For instance, if you have ~/bin listed in your PATH, you can create a file
> named "git-relog" containing the encantation from my first mail in this
> thread, then call
>
>    $ git relog
>
> and that script will be found and executed.
Thanks for your reply, it does solve my problems.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-05  9:36     ` wuzhouhui
@ 2022-07-06 12:38       ` kostix
  0 siblings, 0 replies; 11+ messages in thread
From: kostix @ 2022-07-06 12:38 UTC (permalink / raw)
  To: git; +Cc: wuzhouhui

By the way, you can consider "going more interactive" with the approach which
involves scripting. For instance, you can use any of the "fuzzy finders"
such as the venerable fzf [1] to interactively select a commit to rebase -
something like

  $ git log --oneline -50 | fzf | cut -d ' ' -f 1

would produce the log of the last 50 commits, allow you to interactively
select a commit of interest by typing some of the characters of its message
and/or hash and then print the selected hash.

This can be easily combined with rebasing: for instance, if you have the above
encantation available as an alias named "select" or a custom script named
"git-select", you could do

  $ git rebase -i $(git select)~

to not even copy or memorize anyting.


I, for one, routinely use fzf to pick a branch for checkout.


1. https://github.com/junegunn/fzf


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-05  9:25 ` Konstantin Khomoutov
  2022-07-05  9:30   ` Konstantin Khomoutov
@ 2022-07-06 14:38   ` Jeff King
  2022-07-06 16:28     ` Konstantin Khomoutov
  2022-07-06 17:31     ` Junio C Hamano
  1 sibling, 2 replies; 11+ messages in thread
From: Jeff King @ 2022-07-06 14:38 UTC (permalink / raw)
  To: Konstantin Khomoutov; +Cc: wuzhouhui, git

On Tue, Jul 05, 2022 at 12:25:14PM +0300, Konstantin Khomoutov wrote:

> > because SHA sum is hard to memorize, so I have to use copy and past,
> > which is too boring. So, I wonder if there is a way to let "git log"
> > display commits like following:
> > 
> >   HEAD   <one line commit message>
> >   HEAD~1 <one line commit message>
> > HEAD~2 <one line commit message>
> > HEAD~3 <one line commit message>
> >   ...
> > 
> > With these "HEAD~*", I can easily directly type them and no need to
> > move my fingers out of keyboard.
> 
> You can script this. Provided you have a POSIX-compatible shell (such as
> Bash), the encantation would read something like
> 
>  $ git log --oneline | { n=0; while read line; do printf '%d\t%s\n' $n "$line"; done; }

That will just number the commits linearly as they are printed. If your
history has any branches or merges, eventually it will get out of sync.

You can use "name-rev" to annotate commits with names that respect the
history. It only matches full oids, so try:

  git log --oneline --no-abbrev | git name-rev --stdin

If you want shorter hex oids, you can work around it with "--name-only"
and a custom format:

  git log --format='%h (%H) %s' | git name-rev --stdin --name-only

Note that the names will be based on the nearest branches/tags. You can
use "--refs" to limit it, but sadly there doesn't seem to be a way to
specify just HEAD. That might be a fun and easy feature to add.

And finally, if you really like this, you can configure git-log's pager
to always pipe through name-rev, like this:

  [pager]
        log = "git name-rev --stdin | less"

Then the usual "medium" output from git-log will have the annotations on
the "commit" lines. Arguably git-log ought to be able to do this
internally, but I don't think anybody has ever implemented it (and I
wouldn't be surprised if it's a little challenging, just because you'd
have two traversals going on at once in the same program).

Note that I don't use any of those myself. Long ago I taught my terminal
to do keyboard selection of object ids for cut-and-paste. I think there
are probably solutions for tmux and other programs you can find online.
I shared mine for urxvt a while ago:

  https://lore.kernel.org/git/20170120192539.7jts6xqzx46unn7y@sigill.intra.peff.net/

though if anybody is interested in it, let me know because I can share a
new version with some bug fixes and improvements since then.

-Peff

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-06 14:38   ` Jeff King
@ 2022-07-06 16:28     ` Konstantin Khomoutov
  2022-07-06 16:49       ` Jeff King
  2022-07-06 17:31     ` Junio C Hamano
  1 sibling, 1 reply; 11+ messages in thread
From: Konstantin Khomoutov @ 2022-07-06 16:28 UTC (permalink / raw)
  To: Jeff King; +Cc: Konstantin Khomoutov, wuzhouhui, git

On Wed, Jul 06, 2022 at 10:38:37AM -0400, Jeff King wrote:

[...]

> > You can script this. Provided you have a POSIX-compatible shell (such as
> > Bash), the encantation would read something like
> > 
> >  $ git log --oneline | { n=0; while read line; do printf '%d\t%s\n' $n "$line"; done; }
> 
> That will just number the commits linearly as they are printed. If your
> history has any branches or merges, eventually it will get out of sync.

Thanks, I did not think of this.

> You can use "name-rev" to annotate commits with names that respect the
> history. It only matches full oids, so try:
> 
>   git log --oneline --no-abbrev | git name-rev --stdin

I pondered about `git name-rev` but failed to figure out how to make it accept
just "HEAD" as the point of reference. Passing it "--refs=HEAD"
(or "--refs HEAD", FWIW) does not appear to do what I'd expect it to: a couple
of top commits are not annotated at all, and then they start to get annotated
relative to "origin/HEAD". Note that I've tested this on a detached HEAD which
contains an unmerged line of history.

[...]


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-06 16:28     ` Konstantin Khomoutov
@ 2022-07-06 16:49       ` Jeff King
  2022-07-06 18:21         ` Konstantin Khomoutov
  0 siblings, 1 reply; 11+ messages in thread
From: Jeff King @ 2022-07-06 16:49 UTC (permalink / raw)
  To: Konstantin Khomoutov; +Cc: wuzhouhui, git

On Wed, Jul 06, 2022 at 07:28:56PM +0300, Konstantin Khomoutov wrote:

> > You can use "name-rev" to annotate commits with names that respect the
> > history. It only matches full oids, so try:
> > 
> >   git log --oneline --no-abbrev | git name-rev --stdin
> 
> I pondered about `git name-rev` but failed to figure out how to make it accept
> just "HEAD" as the point of reference. Passing it "--refs=HEAD"
> (or "--refs HEAD", FWIW) does not appear to do what I'd expect it to: a couple
> of top commits are not annotated at all, and then they start to get annotated
> relative to "origin/HEAD". Note that I've tested this on a detached HEAD which
> contains an unmerged line of history.

Right. The problem is that it gets the set of possible ref tips with
for_each_ref(), which will not include HEAD. And then worse, since the
argument to "--refs" is a pattern, it matches junk like origin/HEAD.

You'd perhaps need a new option, something like:

diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index c4dc143c4b..ec36e48ca3 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -568,10 +568,12 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
 	struct object_array revs = OBJECT_ARRAY_INIT;
 	int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
+	int head_only = 0;
 	struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
 	struct option opts[] = {
 		OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
 		OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
+		OPT_BOOL(0, "head", &head_only, N_("only use HEAD to name the commits")),
 		OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
 				   N_("only use refs matching <pattern>")),
 		OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"),
@@ -652,7 +654,10 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 
 	adjust_cutoff_timestamp_for_slop();
 
-	for_each_ref(name_ref, &data);
+	if (head_only)
+		head_ref(name_ref, &data);
+	else
+		for_each_ref(name_ref, &data);
 	name_tips();
 
 	if (annotate_stdin) {

though there are some open issues. E.g., can it be combined with other
--refs options? If not, should we detect that case and complain?

Another option is to just teach it to always look at HEAD and see if
name_ref() wants to accept it. Like:

diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index c4dc143c4b..afd9f93a95 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -652,6 +652,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 
 	adjust_cutoff_timestamp_for_slop();
 
+	head_ref(name_ref, &data);
 	for_each_ref(name_ref, &data);
 	name_tips();
 

That has the nice advantage of not needing a new option. But:

  - it does change the default output; it may be that people would
    prefer not to see HEAD~5, in favor of the actual branch name.

  - if you want just HEAD, you'll have to say so, and then ask to
    exclude origin/HEAD, etc, with a pattern. Like:

      git name-rev --stdin --refs=HEAD --exclude='*/*'

    which is perhaps a little counter-intuitive (though an example in
    the manpage might lead people in the right direction).

I dunno. I would not be sad to see either direction turned into a real
patch, if somebody cares to clean it up and make a convincing argument.
I doubt I would use either myself (nor have I ever really used name-rev
much, tbh).

-Peff

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-06 14:38   ` Jeff King
  2022-07-06 16:28     ` Konstantin Khomoutov
@ 2022-07-06 17:31     ` Junio C Hamano
  1 sibling, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2022-07-06 17:31 UTC (permalink / raw)
  To: Jeff King; +Cc: Konstantin Khomoutov, wuzhouhui, git

Jeff King <peff@peff.net> writes:

> And finally, if you really like this, you can configure git-log's pager
> to always pipe through name-rev, like this:
>
>   [pager]
>         log = "git name-rev --stdin | less"

I was about to suggest "name-rev --annotate-stdin" (which happened
during your sabbatical) but this is even better.

> ... (and I
> wouldn't be surprised if it's a little challenging, just because you'd
> have two traversals going on at once in the same program).

A little challenging ;-)

But it may not be so bad.  The "name-rev" traversal uses none of the
usual "revision" traversal machinery and keeps track of its state in
its own commit slab.  It would not be as hard as running two usual
revision traversals simultaneously.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-06 16:49       ` Jeff King
@ 2022-07-06 18:21         ` Konstantin Khomoutov
  2022-07-07 17:06           ` Jeff King
  0 siblings, 1 reply; 11+ messages in thread
From: Konstantin Khomoutov @ 2022-07-06 18:21 UTC (permalink / raw)
  To: Jeff King; +Cc: Konstantin Khomoutov, wuzhouhui, git

On Wed, Jul 06, 2022 at 12:49:08PM -0400, Jeff King wrote:

[...]
> > I pondered about `git name-rev` but failed to figure out how to make it accept
> > just "HEAD" as the point of reference. Passing it "--refs=HEAD"
> > (or "--refs HEAD", FWIW) does not appear to do what I'd expect it to: a couple
> > of top commits are not annotated at all, and then they start to get annotated
> > relative to "origin/HEAD". Note that I've tested this on a detached HEAD which
> > contains an unmerged line of history.
> 
> Right. The problem is that it gets the set of possible ref tips with
> for_each_ref(), which will not include HEAD. And then worse, since the
> argument to "--refs" is a pattern, it matches junk like origin/HEAD.

Do I assume correctly that `git for-each-ref HEAD` does nothing, successfully,
for the very same reason?
If so, I wonder if this should be somehow reflected in the docs.
I mean, I have always maintained an impression that things like HEAD,
ORIGIN_HEAD, FETCH_HEAD etc are also "refs" - because they, well, reference
commits or branches.

The gitglossary manual page of my Git 2.30.2 states that

| ref
|   A name that begins with refs/ (e.g. refs/heads/master)
| <...>
|   There are a few special-purpose refs that do not begin with refs/.
|   The most notable example is HEAD.

which suggests that HEAD is a ref.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: How to display "HEAD~*" in "git log"
  2022-07-06 18:21         ` Konstantin Khomoutov
@ 2022-07-07 17:06           ` Jeff King
  0 siblings, 0 replies; 11+ messages in thread
From: Jeff King @ 2022-07-07 17:06 UTC (permalink / raw)
  To: Konstantin Khomoutov; +Cc: wuzhouhui, git

On Wed, Jul 06, 2022 at 09:21:49PM +0300, Konstantin Khomoutov wrote:

> > Right. The problem is that it gets the set of possible ref tips with
> > for_each_ref(), which will not include HEAD. And then worse, since the
> > argument to "--refs" is a pattern, it matches junk like origin/HEAD.
> 
> Do I assume correctly that `git for-each-ref HEAD` does nothing, successfully,
> for the very same reason?

Yes.

> If so, I wonder if this should be somehow reflected in the docs.
> I mean, I have always maintained an impression that things like HEAD,
> ORIGIN_HEAD, FETCH_HEAD etc are also "refs" - because they, well, reference
> commits or branches.
> 
> The gitglossary manual page of my Git 2.30.2 states that
> 
> | ref
> |   A name that begins with refs/ (e.g. refs/heads/master)
> | <...>
> |   There are a few special-purpose refs that do not begin with refs/.
> |   The most notable example is HEAD.
> 
> which suggests that HEAD is a ref.

Yeah, I think the situation around those top-level names
is...complicated. They act like refs when you name them, but they are
often omitted from iteration of refs. And yet many spots (e.g.,
"rev-list --all") will include them explicitly. Documentation aside, I
would not be surprised if there are still places in the code itself
where there are inconsistencies. I feel like we've had a number of
"oops, include HEAD in this corner case" patches pop up over the years.
Maybe we've gotten them all now?

All of which is to say: you're definitely not wrong, but I'm not sure
what simple documentation change would be helpful. The handling of HEAD
in each specific situation could be mentioned in their respective
documentation spots, but identifying all of those is tough. Possibly the
glossary entry should say something like:

  Note that HEAD is sometimes includes in sets of refs (e.g., in `git
  rev-list --all`) and sometimes not (e.g., in `git for-each-ref`).

but that may be too vague to be helpful to anybody.

-Peff

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2022-07-07 17:06 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-05  7:11 How to display "HEAD~*" in "git log" wuzhouhui
2022-07-05  9:25 ` Konstantin Khomoutov
2022-07-05  9:30   ` Konstantin Khomoutov
2022-07-05  9:36     ` wuzhouhui
2022-07-06 12:38       ` kostix
2022-07-06 14:38   ` Jeff King
2022-07-06 16:28     ` Konstantin Khomoutov
2022-07-06 16:49       ` Jeff King
2022-07-06 18:21         ` Konstantin Khomoutov
2022-07-07 17:06           ` Jeff King
2022-07-06 17:31     ` Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.