{"thread":{"id":"58105","subject":"How to display \"HEAD~*\" in \"git log\"","startedAt":"2022-07-05T07:19:21Z","lastAt":"2022-07-07T17:06:25Z","messageCount":11,"participants":["wuzhouhui","Konstantin Khomoutov","kostix@bswap.ru","Jeff King","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"458468","messageId":"17411d88-b27a-2d20-623d-85c49dc7754e@mails.ucas.ac.cn","threadId":"58105","inReplyTo":null,"subject":"How to display \"HEAD~*\" in \"git log\"","fromName":"wuzhouhui","fromEmail":"wuzhouhui14@mails.ucas.ac.cn","sentAt":"2022-07-05T07:11:49Z","receivedAt":"2022-07-05T07:19:21Z","isPatch":false,"sender":{"key":"wuzhouhui14@mails.ucas.ac.cn","avatar":null},"body":"Hi\n\nI frequently use \"git rebase\" to move commit to a specific location, to\nknow which commit ID as param of \"git rebase\", I have to list all commits\nby \"git log --oneline\", then copy specific commit ID, and executing \"git\nrebase\" as\n\n   git rebase -i <copied commit ID>~\n\nbecause SHA sum is hard to memorize, so I have to use copy and past,\nwhich is too boring. So, I wonder if there is a way to let \"git log\"\ndisplay commits like following:\n\n   HEAD   <one line commit message>\n   HEAD~1 <one line commit message>\nHEAD~2 <one line commit message>\nHEAD~3 <one line commit message>\n   ...\n\nWith these \"HEAD~*\", I can easily directly type them and no need to\nmove my fingers out of keyboard.\n\nThanks.\n\n"},{"id":"458471","messageId":"20220705092514.hsm7cou5bqvajvgq@carbon","threadId":"58105","inReplyTo":"17411d88-b27a-2d20-623d-85c49dc7754e@mails.ucas.ac.cn","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Konstantin Khomoutov","fromEmail":"kostix@bswap.ru","sentAt":"2022-07-05T09:25:14Z","receivedAt":"2022-07-05T09:25:33Z","isPatch":false,"sender":{"key":"kostix@bswap.ru","avatar":null},"body":"On Tue, Jul 05, 2022 at 03:11:49PM +0800, wuzhouhui wrote:\n\n> I frequently use \"git rebase\" to move commit to a specific location, to\n> know which commit ID as param of \"git rebase\", I have to list all commits\n> by \"git log --oneline\", then copy specific commit ID, and executing \"git\n> rebase\" as\n> \n>   git rebase -i <copied commit ID>~\n> \n> because SHA sum is hard to memorize, so I have to use copy and past,\n> which is too boring. So, I wonder if there is a way to let \"git log\"\n> display commits like following:\n> \n>   HEAD   <one line commit message>\n>   HEAD~1 <one line commit message>\n> HEAD~2 <one line commit message>\n> HEAD~3 <one line commit message>\n>   ...\n> \n> With these \"HEAD~*\", I can easily directly type them and no need to\n> move my fingers out of keyboard.\n\nYou can script this. Provided you have a POSIX-compatible shell (such as\nBash), the encantation would read something like\n\n $ git log --oneline | { n=0; while read line; do printf '%d\\t%s\\n' $n \"$line\"; done; }\n\nThis is admittedly not convenient so it makes sense to turn into an alias:\n\n [alias]\n   relog = \"!relog() { git log --oneline \\\"$@\\\" | { n=0; while read line; do printf 'HEAD~%d\\t%s\\n' $n \\\"$line\\\"; n=$((n+1)); done; }; }; relog\"\n\nThen the call\n\n $ git relog\n\nwould output something like\n\nHEAD~0\tdeadbeef Commit message\nHEAD~1\tfadedfac Another commit message\nHEAD~2  12345678 Yet another commit message\n\n...and so on.\n\nIt's easy to make that script not output \"~0\" for the very first entry and\noutput just \"~\" instead of \"~1\" for the second, but I what's presented is\nenough for an example.\n"},{"id":"458472","messageId":"20220705093057.5ewqobdjrqr3ul2k@carbon","threadId":"58105","inReplyTo":"20220705092514.hsm7cou5bqvajvgq@carbon","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Konstantin Khomoutov","fromEmail":"kostix@bswap.ru","sentAt":"2022-07-05T09:30:57Z","receivedAt":"2022-07-05T09:31:45Z","isPatch":false,"sender":{"key":"kostix@bswap.ru","avatar":null},"body":"On Tue, Jul 05, 2022 at 12:25:14PM +0300, Konstantin Khomoutov wrote:\n\n[...]\n> This is admittedly not convenient so it makes sense to turn into an alias:\n> \n>  [alias]\n>    relog = \"!relog() { git log --oneline \\\"$@\\\" | { n=0; while read line; do printf 'HEAD~%d\\t%s\\n' $n \\\"$line\\\"; n=$((n+1)); done; }; }; relog\"\n[...]\n\nAn alternative to an alias it creating a specially named script file which\nmust be executable and located in a directorly listed in the environment\nvariable PATH.\nFor instance, if you have ~/bin listed in your PATH, you can create a file\nnamed \"git-relog\" containing the encantation from my first mail in this\nthread, then call\n\n  $ git relog\n\nand that script will be found and executed.\n\n"},{"id":"458473","messageId":"a1e53e52-cb08-bbff-f58a-391932707fb7@mails.ucas.ac.cn","threadId":"58105","inReplyTo":"20220705093057.5ewqobdjrqr3ul2k@carbon","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"wuzhouhui","fromEmail":"wuzhouhui14@mails.ucas.ac.cn","sentAt":"2022-07-05T09:36:52Z","receivedAt":"2022-07-05T09:36:58Z","isPatch":false,"sender":{"key":"wuzhouhui14@mails.ucas.ac.cn","avatar":null},"body":"\nOn 7/5/22 17:30, Konstantin Khomoutov wrote:\n> On Tue, Jul 05, 2022 at 12:25:14PM +0300, Konstantin Khomoutov wrote:\n>\n> [...]\n>> This is admittedly not convenient so it makes sense to turn into an alias:\n>>\n>>   [alias]\n>>     relog = \"!relog() { git log --oneline \\\"$@\\\" | { n=0; while read line; do printf 'HEAD~%d\\t%s\\n' $n \\\"$line\\\"; n=$((n+1)); done; }; }; relog\"\n> [...]\n>\n> An alternative to an alias it creating a specially named script file which\n> must be executable and located in a directorly listed in the environment\n> variable PATH.\n> For instance, if you have ~/bin listed in your PATH, you can create a file\n> named \"git-relog\" containing the encantation from my first mail in this\n> thread, then call\n>\n>    $ git relog\n>\n> and that script will be found and executed.\nThanks for your reply, it does solve my problems.\n\n"},{"id":"458518","messageId":"20220706123811.dwbuinnbtxftorae@carbon","threadId":"58105","inReplyTo":"a1e53e52-cb08-bbff-f58a-391932707fb7@mails.ucas.ac.cn","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"","fromEmail":"kostix@bswap.ru","sentAt":"2022-07-06T12:38:11Z","receivedAt":"2022-07-06T12:38:21Z","isPatch":false,"sender":{"key":"kostix@bswap.ru","avatar":null},"body":"By the way, you can consider \"going more interactive\" with the approach which\ninvolves scripting. For instance, you can use any of the \"fuzzy finders\"\nsuch as the venerable fzf [1] to interactively select a commit to rebase -\nsomething like\n\n  $ git log --oneline -50 | fzf | cut -d ' ' -f 1\n\nwould produce the log of the last 50 commits, allow you to interactively\nselect a commit of interest by typing some of the characters of its message\nand/or hash and then print the selected hash.\n\nThis can be easily combined with rebasing: for instance, if you have the above\nencantation available as an alias named \"select\" or a custom script named\n\"git-select\", you could do\n\n  $ git rebase -i $(git select)~\n\nto not even copy or memorize anyting.\n\n\nI, for one, routinely use fzf to pick a branch for checkout.\n\n\n1. https://github.com/junegunn/fzf\n\n"},{"id":"458525","messageId":"YsWebbaOJbaOZ6i7@coredump.intra.peff.net","threadId":"58105","inReplyTo":"20220705092514.hsm7cou5bqvajvgq@carbon","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2022-07-06T14:38:37Z","receivedAt":"2022-07-06T14:38:45Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jul 05, 2022 at 12:25:14PM +0300, Konstantin Khomoutov wrote:\n\n> > because SHA sum is hard to memorize, so I have to use copy and past,\n> > which is too boring. So, I wonder if there is a way to let \"git log\"\n> > display commits like following:\n> > \n> >   HEAD   <one line commit message>\n> >   HEAD~1 <one line commit message>\n> > HEAD~2 <one line commit message>\n> > HEAD~3 <one line commit message>\n> >   ...\n> > \n> > With these \"HEAD~*\", I can easily directly type them and no need to\n> > move my fingers out of keyboard.\n> \n> You can script this. Provided you have a POSIX-compatible shell (such as\n> Bash), the encantation would read something like\n> \n>  $ git log --oneline | { n=0; while read line; do printf '%d\\t%s\\n' $n \"$line\"; done; }\n\nThat will just number the commits linearly as they are printed. If your\nhistory has any branches or merges, eventually it will get out of sync.\n\nYou can use \"name-rev\" to annotate commits with names that respect the\nhistory. It only matches full oids, so try:\n\n  git log --oneline --no-abbrev | git name-rev --stdin\n\nIf you want shorter hex oids, you can work around it with \"--name-only\"\nand a custom format:\n\n  git log --format='%h (%H) %s' | git name-rev --stdin --name-only\n\nNote that the names will be based on the nearest branches/tags. You can\nuse \"--refs\" to limit it, but sadly there doesn't seem to be a way to\nspecify just HEAD. That might be a fun and easy feature to add.\n\nAnd finally, if you really like this, you can configure git-log's pager\nto always pipe through name-rev, like this:\n\n  [pager]\n        log = \"git name-rev --stdin | less\"\n\nThen the usual \"medium\" output from git-log will have the annotations on\nthe \"commit\" lines. Arguably git-log ought to be able to do this\ninternally, but I don't think anybody has ever implemented it (and I\nwouldn't be surprised if it's a little challenging, just because you'd\nhave two traversals going on at once in the same program).\n\nNote that I don't use any of those myself. Long ago I taught my terminal\nto do keyboard selection of object ids for cut-and-paste. I think there\nare probably solutions for tmux and other programs you can find online.\nI shared mine for urxvt a while ago:\n\n  https://lore.kernel.org/git/20170120192539.7jts6xqzx46unn7y@sigill.intra.peff.net/\n\nthough if anybody is interested in it, let me know because I can share a\nnew version with some bug fixes and improvements since then.\n\n-Peff\n"},{"id":"458526","messageId":"20220706162856.mkgfdr2lnjdbxgn4@carbon","threadId":"58105","inReplyTo":"YsWebbaOJbaOZ6i7@coredump.intra.peff.net","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Konstantin Khomoutov","fromEmail":"kostix@bswap.ru","sentAt":"2022-07-06T16:28:56Z","receivedAt":"2022-07-06T16:29:04Z","isPatch":false,"sender":{"key":"kostix@bswap.ru","avatar":null},"body":"On Wed, Jul 06, 2022 at 10:38:37AM -0400, Jeff King wrote:\n\n[...]\n\n> > You can script this. Provided you have a POSIX-compatible shell (such as\n> > Bash), the encantation would read something like\n> > \n> >  $ git log --oneline | { n=0; while read line; do printf '%d\\t%s\\n' $n \"$line\"; done; }\n> \n> That will just number the commits linearly as they are printed. If your\n> history has any branches or merges, eventually it will get out of sync.\n\nThanks, I did not think of this.\n\n> You can use \"name-rev\" to annotate commits with names that respect the\n> history. It only matches full oids, so try:\n> \n>   git log --oneline --no-abbrev | git name-rev --stdin\n\nI pondered about `git name-rev` but failed to figure out how to make it accept\njust \"HEAD\" as the point of reference. Passing it \"--refs=HEAD\"\n(or \"--refs HEAD\", FWIW) does not appear to do what I'd expect it to: a couple\nof top commits are not annotated at all, and then they start to get annotated\nrelative to \"origin/HEAD\". Note that I've tested this on a detached HEAD which\ncontains an unmerged line of history.\n\n[...]\n\n"},{"id":"458527","messageId":"YsW9BI61tWGh9OJo@coredump.intra.peff.net","threadId":"58105","inReplyTo":"20220706162856.mkgfdr2lnjdbxgn4@carbon","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2022-07-06T16:49:08Z","receivedAt":"2022-07-06T16:49:13Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jul 06, 2022 at 07:28:56PM +0300, Konstantin Khomoutov wrote:\n\n> > You can use \"name-rev\" to annotate commits with names that respect the\n> > history. It only matches full oids, so try:\n> > \n> >   git log --oneline --no-abbrev | git name-rev --stdin\n> \n> I pondered about `git name-rev` but failed to figure out how to make it accept\n> just \"HEAD\" as the point of reference. Passing it \"--refs=HEAD\"\n> (or \"--refs HEAD\", FWIW) does not appear to do what I'd expect it to: a couple\n> of top commits are not annotated at all, and then they start to get annotated\n> relative to \"origin/HEAD\". Note that I've tested this on a detached HEAD which\n> contains an unmerged line of history.\n\nRight. The problem is that it gets the set of possible ref tips with\nfor_each_ref(), which will not include HEAD. And then worse, since the\nargument to \"--refs\" is a pattern, it matches junk like origin/HEAD.\n\nYou'd perhaps need a new option, something like:\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex c4dc143c4b..ec36e48ca3 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -568,10 +568,12 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_array revs = OBJECT_ARRAY_INIT;\n \tint all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;\n+\tint head_only = 0;\n \tstruct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };\n \tstruct option opts[] = {\n \t\tOPT_BOOL(0, \"name-only\", &data.name_only, N_(\"print only ref-based names (no object names)\")),\n \t\tOPT_BOOL(0, \"tags\", &data.tags_only, N_(\"only use tags to name the commits\")),\n+\t\tOPT_BOOL(0, \"head\", &head_only, N_(\"only use HEAD to name the commits\")),\n \t\tOPT_STRING_LIST(0, \"refs\", &data.ref_filters, N_(\"pattern\"),\n \t\t\t\t   N_(\"only use refs matching <pattern>\")),\n \t\tOPT_STRING_LIST(0, \"exclude\", &data.exclude_filters, N_(\"pattern\"),\n@@ -652,7 +654,10 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n \n \tadjust_cutoff_timestamp_for_slop();\n \n-\tfor_each_ref(name_ref, &data);\n+\tif (head_only)\n+\t\thead_ref(name_ref, &data);\n+\telse\n+\t\tfor_each_ref(name_ref, &data);\n \tname_tips();\n \n \tif (annotate_stdin) {\n\nthough there are some open issues. E.g., can it be combined with other\n--refs options? If not, should we detect that case and complain?\n\nAnother option is to just teach it to always look at HEAD and see if\nname_ref() wants to accept it. Like:\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex c4dc143c4b..afd9f93a95 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -652,6 +652,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n \n \tadjust_cutoff_timestamp_for_slop();\n \n+\thead_ref(name_ref, &data);\n \tfor_each_ref(name_ref, &data);\n \tname_tips();\n \n\nThat has the nice advantage of not needing a new option. But:\n\n  - it does change the default output; it may be that people would\n    prefer not to see HEAD~5, in favor of the actual branch name.\n\n  - if you want just HEAD, you'll have to say so, and then ask to\n    exclude origin/HEAD, etc, with a pattern. Like:\n\n      git name-rev --stdin --refs=HEAD --exclude='*/*'\n\n    which is perhaps a little counter-intuitive (though an example in\n    the manpage might lead people in the right direction).\n\nI dunno. I would not be sad to see either direction turned into a real\npatch, if somebody cares to clean it up and make a convincing argument.\nI doubt I would use either myself (nor have I ever really used name-rev\nmuch, tbh).\n\n-Peff\n"},{"id":"458531","messageId":"xmqqo7y2i1vv.fsf@gitster.g","threadId":"58105","inReplyTo":"YsWebbaOJbaOZ6i7@coredump.intra.peff.net","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-07-06T17:31:32Z","receivedAt":"2022-07-06T17:31:40Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> And finally, if you really like this, you can configure git-log's pager\n> to always pipe through name-rev, like this:\n>\n>   [pager]\n>         log = \"git name-rev --stdin | less\"\n\nI was about to suggest \"name-rev --annotate-stdin\" (which happened\nduring your sabbatical) but this is even better.\n\n> ... (and I\n> wouldn't be surprised if it's a little challenging, just because you'd\n> have two traversals going on at once in the same program).\n\nA little challenging ;-)\n\nBut it may not be so bad.  The \"name-rev\" traversal uses none of the\nusual \"revision\" traversal machinery and keeps track of its state in\nits own commit slab.  It would not be as hard as running two usual\nrevision traversals simultaneously.\n"},{"id":"458537","messageId":"20220706182149.gg75w6uonm4hs5af@carbon","threadId":"58105","inReplyTo":"YsW9BI61tWGh9OJo@coredump.intra.peff.net","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Konstantin Khomoutov","fromEmail":"kostix@bswap.ru","sentAt":"2022-07-06T18:21:49Z","receivedAt":"2022-07-06T18:22:00Z","isPatch":false,"sender":{"key":"kostix@bswap.ru","avatar":null},"body":"On Wed, Jul 06, 2022 at 12:49:08PM -0400, Jeff King wrote:\n\n[...]\n> > I pondered about `git name-rev` but failed to figure out how to make it accept\n> > just \"HEAD\" as the point of reference. Passing it \"--refs=HEAD\"\n> > (or \"--refs HEAD\", FWIW) does not appear to do what I'd expect it to: a couple\n> > of top commits are not annotated at all, and then they start to get annotated\n> > relative to \"origin/HEAD\". Note that I've tested this on a detached HEAD which\n> > contains an unmerged line of history.\n> \n> Right. The problem is that it gets the set of possible ref tips with\n> for_each_ref(), which will not include HEAD. And then worse, since the\n> argument to \"--refs\" is a pattern, it matches junk like origin/HEAD.\n\nDo I assume correctly that `git for-each-ref HEAD` does nothing, successfully,\nfor the very same reason?\nIf so, I wonder if this should be somehow reflected in the docs.\nI mean, I have always maintained an impression that things like HEAD,\nORIGIN_HEAD, FETCH_HEAD etc are also \"refs\" - because they, well, reference\ncommits or branches.\n\nThe gitglossary manual page of my Git 2.30.2 states that\n\n| ref\n|   A name that begins with refs/ (e.g. refs/heads/master)\n| <...>\n|   There are a few special-purpose refs that do not begin with refs/.\n|   The most notable example is HEAD.\n\nwhich suggests that HEAD is a ref.\n\n"},{"id":"458580","messageId":"YscSigm9b8QJMuRM@coredump.intra.peff.net","threadId":"58105","inReplyTo":"20220706182149.gg75w6uonm4hs5af@carbon","subject":"Re: How to display \"HEAD~*\" in \"git log\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2022-07-07T17:06:18Z","receivedAt":"2022-07-07T17:06:25Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jul 06, 2022 at 09:21:49PM +0300, Konstantin Khomoutov wrote:\n\n> > Right. The problem is that it gets the set of possible ref tips with\n> > for_each_ref(), which will not include HEAD. And then worse, since the\n> > argument to \"--refs\" is a pattern, it matches junk like origin/HEAD.\n> \n> Do I assume correctly that `git for-each-ref HEAD` does nothing, successfully,\n> for the very same reason?\n\nYes.\n\n> If so, I wonder if this should be somehow reflected in the docs.\n> I mean, I have always maintained an impression that things like HEAD,\n> ORIGIN_HEAD, FETCH_HEAD etc are also \"refs\" - because they, well, reference\n> commits or branches.\n> \n> The gitglossary manual page of my Git 2.30.2 states that\n> \n> | ref\n> |   A name that begins with refs/ (e.g. refs/heads/master)\n> | <...>\n> |   There are a few special-purpose refs that do not begin with refs/.\n> |   The most notable example is HEAD.\n> \n> which suggests that HEAD is a ref.\n\nYeah, I think the situation around those top-level names\nis...complicated. They act like refs when you name them, but they are\noften omitted from iteration of refs. And yet many spots (e.g.,\n\"rev-list --all\") will include them explicitly. Documentation aside, I\nwould not be surprised if there are still places in the code itself\nwhere there are inconsistencies. I feel like we've had a number of\n\"oops, include HEAD in this corner case\" patches pop up over the years.\nMaybe we've gotten them all now?\n\nAll of which is to say: you're definitely not wrong, but I'm not sure\nwhat simple documentation change would be helpful. The handling of HEAD\nin each specific situation could be mentioned in their respective\ndocumentation spots, but identifying all of those is tough. Possibly the\nglossary entry should say something like:\n\n  Note that HEAD is sometimes includes in sets of refs (e.g., in `git\n  rev-list --all`) and sometimes not (e.g., in `git for-each-ref`).\n\nbut that may be too vague to be helpful to anybody.\n\n-Peff\n"}]}