[READ-ONLY] Mirror of https://github.com/mrgnw/scripts.
1#!/usr/bin/env -S uv run --script
2# /// script
3# requires-python = ">=3.13"
4# dependencies = ["rich"]
5# ///
6import subprocess
7import sys
8from datetime import datetime
9from subprocess import check_output
10
11from rich.console import Console
12from rich.text import Text
13
14DEFAULT_LINES = 8
15
16COLORS = {
17 "hash": (90, 70, 140),
18 "date": (102, 91, 255),
19 "time": (79, 164, 255),
20 "dim": (60, 60, 80),
21 "at": (70, 70, 90),
22 "msg": (220, 218, 240),
23 "paren": (100, 100, 120),
24 "head": (255, 249, 86),
25 "main": (104, 244, 255),
26 "branch": (79, 255, 249),
27}
28
29
30def make_fader(day_idx):
31 fade = max(0.85**day_idx, 0.45) if day_idx > 0 else 1.0
32
33 def apply(name):
34 r, g, b = COLORS[name]
35 return f"rgb({int(r * fade)},{int(g * fade)},{int(b * fade)})"
36
37 return apply
38
39
40def parse_commit(line):
41 h, year, month, day, hour, minute, msg, branches = line.split(";", 7)
42 return {
43 "hash": h,
44 "date": (int(year), int(month), int(day)),
45 "time": (hour, minute),
46 "month_day": (month, day),
47 "msg": msg,
48 "branches": branches.strip().strip("()"),
49 }
50
51
52def format_branches(branches, fade):
53 text = Text()
54 if not branches:
55 return text
56
57 text.append(" (", style=fade("paren"))
58
59 for i, branch in enumerate(branches.split(",")):
60 branch = branch.strip()
61 if i > 0:
62 text.append(", ", style=fade("paren"))
63
64 if " -> " in branch:
65 _, branch = branch.split(" -> ")
66 text.append("HEAD", style=f"bold {fade('head')}")
67 text.append(" -> ", style=fade("paren"))
68
69 is_origin = branch.startswith("origin/")
70 name = branch[7:] if is_origin else branch
71 base_style = "bold underline" if is_origin else "bold"
72
73 color = "head" if name == "HEAD" else "main" if name in ("main", "master") else "branch"
74 text.append(name, style=f"{base_style} {fade(color)}")
75
76 text.append(")", style=fade("paren"))
77 return text
78
79
80def format_datetime(commit, now, fade):
81 text = Text()
82 year, month, day = commit["date"]
83 month_str, day_str = commit["month_day"]
84 hour, minute = commit["time"]
85
86 if year < now.year:
87 text.append(f"{year}-{month_str}{day_str}", style=fade("time"))
88 else:
89 same_month = month == now.month
90 same_day = same_month and day == now.day
91
92 if same_day:
93 text.append(f"{month_str}{day_str}", style=fade("dim"))
94 elif same_month:
95 text.append(month_str, style=fade("dim"))
96 text.append(day_str, style=fade("date"))
97 else:
98 text.append(f"{month_str}{day_str}", style=fade("date"))
99
100 text.append("@", style=fade("at"))
101 text.append(f"{hour}{minute}", style=fade("time"))
102
103 return text
104
105
106def get_commits(n, console):
107 try:
108 log = check_output(
109 ["git", "log", "-n", str(n), "--pretty=format:%h;%cd;%s;%d", "--date=format:%Y;%m;%d;%H;%M"],
110 stderr=subprocess.DEVNULL,
111 ).decode()
112 except Exception:
113 console.print("gitl: not a git repository (or no commits yet)", style="red")
114 return []
115 return [parse_commit(line) for line in log.splitlines()] if log else []
116
117
118def assign_day_indices(commits):
119 current_day = None
120 day_idx = -1
121 for commit in commits:
122 if commit["date"] != current_day:
123 current_day = commit["date"]
124 day_idx += 1
125 commit["day_idx"] = day_idx
126
127
128def main():
129 console = Console()
130 now = datetime.now()
131 today = (now.year, now.month, now.day)
132
133 if len(sys.argv) > 1:
134 try:
135 commits = get_commits(int(sys.argv[1]), console)
136 except ValueError:
137 console.print("Usage: gitl [n_lines]", style="red")
138 sys.exit(1)
139 else:
140 commits = get_commits(100, console)
141 today_count = sum(1 for c in commits if c["date"] == today)
142 commits = commits[: max(DEFAULT_LINES, today_count)]
143
144 if not commits:
145 return
146
147 assign_day_indices(commits)
148
149 for commit in commits:
150 fade = make_fader(commit["day_idx"])
151 text = Text()
152
153 text.append(commit["hash"], style=f"bold {fade('hash')}")
154 text.append(" ")
155 text.append_text(format_datetime(commit, now, fade))
156 text.append(" ")
157 text.append(commit["msg"], style=fade("msg"))
158 text.append_text(format_branches(commit["branches"], fade))
159
160 console.print(text)
161
162
163if __name__ == "__main__":
164 main()