How to make git-log scroll up instead of down
Asked Answered
A

4

7

Whenever I view a git log --all --graph --oneline --decorate output in my terminal emulator, the first commit is viewed at the top of the terminal screen. When I quit the git log output view with q, a few lines from the are not visible any more, as there are some new lines appended to the bottom of the screen, for the next command.

Usually though, those top lines are the most interesting, as they resemble the most recent git history, so I want them to be still visible when I type the next git command.

How can I make the git log output appear starting at the bottom of the screen, i.e. such that the first commit is viewed at the bottom? You would have to scroll up to view older commits.

NOTE: The --reverse flag is not an option for two reasons.

  1. Each time you have to scroll all the way to the bottom to view the first commits. That should not be necessary. I want to start at the bottom.
  2. It doesn't combine with the --graph flag: fatal: cannot combine --reverse with --graph.
Appear answered 2/3, 2016 at 9:36 Comment(7)
Have you tried git log --reverse?Bassist
Possible duplicate of Can I get git log to print the history in reverse order?Bassist
@Jubobs no duplicate. Git log --reverse will start with the last commit, which is not what I want. Also, it cannot be combined with --graph. Will update question to include that as a requirementAppear
Possible duplicate of When I have git log print out as oneline, how do I reverse it?Allocution
@FarhadFaghihi No, no, no. Read the question.Appear
When I do this my terminal doesn't scroll the interesting bits off the screen, you've got a misconfigured terminal emulator or maybe a multiline prompt, which by my lights amounts to the same thing. Try adding -$((LINES/2)) to your command.Azotemia
@ChieltenBrinke, I've posted a sed-based answer which converts the lines on the run, with no need to read all the graph beforehand; you could give it a try.Discriminatory
A
2

This is an answer that seems to catch most edge cases. Not tested thoroughly.

[alias]
    rlog = !"git --no-pager log --all --graph --decorate --oneline --color=always | tac | awk -f ~/switchslashes.awk | less -X +G -r"

where the file ~/switchslashes.awk contains

{
    match($0,/([[:space:][:cntrl:]|*\/\\]+)(.*)/,a) # find the segment of the graph
    tgt = substr($0,RSTART,RLENGTH)     # save that segment in a variable tgt
    gsub(/\//,RS,tgt)                   # change all /s to newlines in tgt
    gsub(/\\/,"/",tgt)                  # change all \s to /s in tgt
    gsub(RS,"\\",tgt)                   # change all newlines to \s in tgt
    gsub(/_/,"¯",tgt)                   # change all _ to ¯ in tgt
    print tgt substr($0,RSTART+RLENGTH) # print tgt plus rest of the line
}

which is a modified version of this script. It replaces underscores with overlines and swaps slashes with backslashes and vice versa. This fixes the graph after the text has been reversed by tac.

Disclaimer

I never started using this, since it is slow with larger repositories. It needs to load everything and then apply the substitutions, which take too much time for my taste.

Appear answered 3/3, 2016 at 14:21 Comment(2)
On OS X, tac is not available. You can install GNU coreutils and use gtac instead.Accelerometer
Modified answer a bit with a disclaimer.Appear
D
3

Original answer, which doesn't work, so goto EDIT for the working version

The following sed solution works for me if used directly form command line. It does not use any temporary string to switch \ and / by relying on seds y command.

$ git --no-pager log --all --graph --decorate --oneline --color=always | tac | sed 'h
s!\( *[0-9a-z]\{7\} .*\)\{0,1\}$!\1!
y/\\\/_¯/\/\\¯_/
x
s!\(.*\)\( *[0-9a-z]\{7\} .*\)\{0,1\}$!\2!
x
G
s/\n//' | less -X +G -r

It assumes the SHA code to be 7 characters long and uses it to "recognize" what is not the leading sequence of \, /, |, _, *, and <space>, which I was not able to put at the beginning of the first s command's search pattern and in place of the first . in the second s command.

I don't know why I cannot get it to work when all sed commands are put in a script called in with seds -f option.

EDIT (The above code is actually faulty)

As @user1902689 pointed out, sed scripts can make anyone's eyes bleed, myself included, since they are extremely cryptic.

In my opinion, the task would be easily accomplished if the --color=always was not used, in which case the text piped out of git log would be the same as we see on screen; using --color=always, on the contrary, inserts control sequences like ^[[33m interspersed in the text to control the coloring (different colors for different branches, ...).

But having a colored output is nice, so I directed the output of git log --color=always ... to file, and looked into it, discovering that the hash always appears in between ^[[33m and ^[[m, where ^[ is a single character obtainable by hitting Ctrl+V, then Esc. These are essentially the escape sequences interpreted by bash as setting the color to yellow, and back to white, respectively (link).

The hash, which is not the only 7-alphanumeric characters-string in the line (e.g. thiswrd can be in the commit main message), is almost certainly the first one, so greedy expressions (sed has no non-greedy expressions) can be safely used after it, and not before (a .* before the hash-matching regexp would make that regexp match the last 7-alphanumeric characters-string on the line, which could be anytext, for instance, and the hash would be lost somewhere in .*). In order to allow the use of the greedy .* in a way that it doesn't devour the hash, we can enclose the hash in between newlines \n which are not matched by the . in .* (and, as such, they must be explicitly typed) with an s command, so that we can "limit" the greediness of .* in a successive s command by using some \n explicitly in the search pattern.

I think the following code (explained afterward) is not definitive, since it hardcodes the coloring escape sequences used to get a colored hash string, but it works as long as I've tried.

$ git --no-pager log --all --graph --decorate --oneline --color=always | tac | sed '
s/\(\(^[\[33m\)\([0-9a-z]\{7\}\)\(^[\[m\)\(.*\)\)/\2\n\3\n\4\5/
h
s/^.*\(\n[a-z0-9]\{7\}\n.*\|$\)/\1/
x
s/\n[a-z0-9]\{7\}\n.*$//
y/\\\/_¯/\/\\¯_/
G
s/\n\([a-z0-9]\{7\}\)\n/\1/
s/\n//' | less -X +G -r

Each line contains the three parts Graph OpeningColorTagHashClosingColorTag Message, or only the first one, Graph. The sed string consists of 9 commands that do what I intended to do with the original answer, but in a bit different way (especially in that the order of some commands is inverted, to save a x command).

  1. The first s command puts a newline \n on each side of the Hash string [0-9a-z]\{7\} (or does nothing if there is no hash in this line; note that the lines before/after a merge/diverge have no hash or message following them). This has the purpose of "isolating" Hash. Note that the capturing groups \(...\) are numbered based on the order of occurrence of the opening token \(, so in the replacement string \2\n\3\n\4\5:
    • \2 refers to ^[\[33m, which is the OpeningColorTag (NOTE: ^[ is a single character obtained by hitting Ctrl+V, then Esc, whereas the "true" [ must be escped with a backslash \);
    • \3 refers to Hash, [0-9a-z]\{7\}
    • \4 refers to the ClosingColorTag, ^[\[m (what is said for \2 holds here too);
    • \5 is whatever follows, .* (implicitly up to end-of-line).

Now the pattern space (the current line, as we've edited so far) contains the original line with two embedded newlines on each side of the hash (Graph OpeningColorTag\nHash\nClosingColorTag Message), or the unmodified original line if it contained no hash (Graph).

  1. The h command "saves" the pattern space into the hold space (think of it as a drawer).

Now the pattern and hold spaces have the same content (Graph OpeningColorTag\nHash\nClosingColorTag Message or Graph).

  1. The second s command captures and replace, with itself only (/\1/) and discarding everything before it (^.*, i.e. Graph OpeningColorTag), either (\| separates alternatives in a capturing group \(...\))
    • the newline-enclosed hash and everything following it (i.e. the commit main message) \n[a-z0-9]\{7\}\n.*,
    • or the end-of-line $,

Now the pattern space contains \nHash\nClosingColorTag Message, or the empty string if there was no hash.

  1. The x command exchanges the contents of pattern and hold spaces, so that the multiline \nHash\nClosingColorTag Message (or the empty string) is saved in the hold space, and the multiline Graph OpeningColorTag\nHash\nClosingColorTag Message is in the pattern space, ready to be re-edited.
  2. The third s command command strips off the \nHash, and everything following it, from the pattern space.

Now the pattern space contains Graph OpeningColorTag.

  1. The y substitutes each character between the first two non-escaped /, with the corresponing character between the second and third non-escaped /. Here both back and forward slashes must be escaped with a backslash. (This shoul be safe, since OpeningColorTag should not contain any of the translated characters.)

Now the pattern space contains Hparg OpeningColorTag, where Hparg is the "inverted" version of Graph (or Hparg only).

  1. The G command gets the content of the hold space and appends it to (hence the capital G; the lower case g would copy into instead of append to) the pattern space, with a newline \n in between.

Now the pattern space contains Hparg OpeningColorTag\n\nHash\nClosingColorTag Message (or Hparg\n only), and we don't care the hold space from now on.

  1. The fourth s command captures the \nHash\n part and substitutes it with Hash.

Now the pattern space contains Hparg OpeningColorTag\nHashClosingColorTag Message or Hparg\n.

  1. The last s command removes the remaining newline \n.

Finally the pattern space contains Hparg OpeningColorTagHashClosingColorTag Message or Hparg.

Steps 8. and 9. cannot be fused together (e.g. s/\n\n\([a-z0-9]\{7\}\)\n/\1/), since the two \n enclosing the hash are there only if the line contains the hash (the first s of point 1. does nothing if there's no hash), whereas the first \n is always present, since it comes with the G command.


Actually the most external group \(...\) is not needed (it's not used, indeed), so it can be removed, and all numeric references to the other capturing groups can be diminished by 1, e.g. s/\(\(^[\[33m\)\([0-9a-z]\{7\}\)\(^[\[m\)\(.*\)\)/\2\n\3\n\4\5/ can be changed to s/\(^[\[33m\)\([0-9a-z]\{7\}\)\(^[\[m\)\(.*\)/\1\n\2\n\3\4/; but I'll keep the unnecessary group in the answer, since it gives the chance to mention the not-so-widely-known numbering of capturing groups.
Discriminatory answered 2/4, 2018 at 14:57 Comment(10)
Any chance of giving a quick comment on each line, to help readers that know a little bit of sed but whose eyes go blurry when looking at this? Sort of like on the accepted answer, which isn't working properly for me.Entablature
I'm so confused. None of these answers or ones in similar questions work for me. All of them that perform any substitutions (including yours) operates on the entire line, whether or not I include "--color=always". I don't want to comment spam, but as I don't think I can block quote, I'll post my git-only output and then my tac and your sed command output.Entablature
This: | | * 2ac7376 Merge remote-tracking branch 'grooverdan/gcc-power-tune' into travis_ppc64leEntablature
Becomes this: | | * 2ac7376 Merge remote-tracking branch 'grooverdan\gcc-power-tune' into travis¯ppc64leEntablature
@user1902689, I think I've fixed the command (the former is still in the answer, just in case). I've tried to be clear in the explanations, but sed scripts are terribly cryptic anyway. If you need anything else, please comment again.Discriminatory
WOW, I was hoping to maybe get a few sentences added. I really appreciate the work you did there. Can't thank you enough. It won't only help me here, but finally get me out of my sed comfort zone. I do, however, get an empty less display when copy/pasting the new one. Removing the less gives: sed: -e expression #1, char 64: unterminated 's' command. Using GNU sed 4.5. I'm ultimately wanting to add this as an alias to lg in my .gitconfig, so after I can run this in bash, I'll see if I can figure out the sed -f issue, or how to re-escape everything in .gitconfig.Entablature
Nevermind, you even pointed out that the ^[ needed to be CTRL+V then ESC. I can't just copy/paste!Entablature
New version works exporting it to a file (removing the starting/ending single quote) and -f, which makes adding it to .gitconfig trivial. (I never tried -f and the original version.) lgnew = "!f() { git --no-pager log --all --graph --decorate --oneline --color=always | tac | sed -f /etc/sed/gitGraphFlip.sed; }; f"Entablature
Thanks again! I think line 1 can be simplified to s/\(\(^[\[33m\)\([0-9a-z]\{7\}\)\(.*\)\)/\2\n\3\n\4/ which is just removing the ^[\[m\)\( and the \5.Entablature
Beware that this simplified line could match some longer-then-7 yellow string. And I think tags are typeset in yellow. You just have to try. It's nice to have your appreciation!Discriminatory
A
2

This is an answer that seems to catch most edge cases. Not tested thoroughly.

[alias]
    rlog = !"git --no-pager log --all --graph --decorate --oneline --color=always | tac | awk -f ~/switchslashes.awk | less -X +G -r"

where the file ~/switchslashes.awk contains

{
    match($0,/([[:space:][:cntrl:]|*\/\\]+)(.*)/,a) # find the segment of the graph
    tgt = substr($0,RSTART,RLENGTH)     # save that segment in a variable tgt
    gsub(/\//,RS,tgt)                   # change all /s to newlines in tgt
    gsub(/\\/,"/",tgt)                  # change all \s to /s in tgt
    gsub(RS,"\\",tgt)                   # change all newlines to \s in tgt
    gsub(/_/,"¯",tgt)                   # change all _ to ¯ in tgt
    print tgt substr($0,RSTART+RLENGTH) # print tgt plus rest of the line
}

which is a modified version of this script. It replaces underscores with overlines and swaps slashes with backslashes and vice versa. This fixes the graph after the text has been reversed by tac.

Disclaimer

I never started using this, since it is slow with larger repositories. It needs to load everything and then apply the substitutions, which take too much time for my taste.

Appear answered 3/3, 2016 at 14:21 Comment(2)
On OS X, tac is not available. You can install GNU coreutils and use gtac instead.Accelerometer
Modified answer a bit with a disclaimer.Appear
P
1

First of all you can alway pass -n to the log to print out any number of commits you are interested in.


How can I make the git log output appear reversed

Use the --reverse flag:

--reverse

Output the commits in reverse order.

git log --reverse

You can read here for more tips and flags regarding git log:
http://www.alexkras.com/19-git-tips-for-everyday-use/

Pickard answered 2/3, 2016 at 9:42 Comment(1)
I don't know up front which lines I'm interested in, so I can't ass -n. Also, I have updated the OP about needing to use the --graph argument, which doesn't go along with --reverse.Appear
A
1

A command that comes close to the intended result is

git --no-pager log --all --graph --decorate --oneline --color=always | tac | less -r +G -X

However, this still messes up the graph a little bit, as the slashes are not reversed properly.

Update

This command takes also care of swapping the slashes with backslashes and vice versa.

git --no-pager log --all --graph --decorate --oneline --color=always | tac | sed -e 's/[\]/aaaaaaaaaa/g' -e 's/[/]/\\/g' -e 's/aaaaaaaaaa/\//g' | less -r +G -X

The corresponding git alias is

[alias]
    rlog = !"git --no-pager log --all --graph --decorate --oneline --color=always | tac | sed -e 's/[\\]/aaaaaaaaaa/g' -e 's/[/]/\\\\\\\\/g' -e 's/aaaaaaaaaa/\\\\//g' | less -r +G -X"
Appear answered 2/3, 2016 at 10:59 Comment(1)
Using the awk replace script in https://mcmap.net/q/1622916/-how-to-perform-a-sed-transform-within-a-matching-part-of-a-line, you can improve the slash toggles to only apply to the graph partAppear

© 2022 - 2024 — McMap. All rights reserved.