# by ChatGPT import curses import subprocess import sys def get_commits(filename): cmd = ['git', 'log', '--pretty=format:%h %ad %s', '--date=short', '--', filename] result = subprocess.run(cmd, capture_output=True, text=True) return result.stdout.splitlines() def get_file_at_commit(commit_hash, filename): cmd = ['git', 'show', f'{commit_hash}:{filename}'] result = subprocess.run(cmd, capture_output=True, text=True) return result.stdout.splitlines() def main(stdscr, filename): curses.curs_set(0) curses.mousemask(curses.ALL_MOUSE_EVENTS) stdscr.keypad(True) commits = get_commits(filename) selected_commit = 0 divider_col = 40 focus = "left" scroll_offset = 0 while True: stdscr.clear() height, width = stdscr.getmaxyx() # Draw commit list on the left for i in range(min(len(commits), height)): line = commits[i] if i == selected_commit and focus == "left": stdscr.attron(curses.A_REVERSE) stdscr.addnstr(i, 0, line, divider_col - 1) if i == selected_commit and focus == "left": stdscr.attroff(curses.A_REVERSE) # Draw vertical divider with box-drawing character for y in range(height): stdscr.addch(y, divider_col, '│') # Draw file content on the right if commits: commit_hash = commits[selected_commit].split()[0] file_lines = get_file_at_commit(commit_hash, filename) visible_lines = file_lines[scroll_offset:scroll_offset + height] for i, line in enumerate(visible_lines): stdscr.addnstr(i, divider_col + 2, line, width - divider_col - 3) stdscr.refresh() key = stdscr.getch() if key == curses.KEY_MOUSE: try: _, mx, my, _, bstate = curses.getmouse() if bstate & curses.BUTTON1_CLICKED: focus = "left" if mx <= divider_col else "right" except curses.error: pass elif key in [ord('q'), 27]: break elif focus == "left": if key in [curses.KEY_DOWN, ord('j')]: if selected_commit < len(commits) - 1: selected_commit += 1 scroll_offset = 0 # reset right pane scroll elif key in [curses.KEY_UP, ord('k')]: if selected_commit > 0: selected_commit -= 1 scroll_offset = 0 elif focus == "right": if key == curses.KEY_DOWN or key == ord('j'): if scroll_offset < max(0, len(file_lines) - height): scroll_offset += 1 elif key == curses.KEY_UP or key == ord('k'): if scroll_offset > 0: scroll_offset -= 1 elif key == curses.KEY_NPAGE: # Page Down scroll_offset = min(scroll_offset + height, max(0, len(file_lines) - height)) elif key == curses.KEY_PPAGE: # Page Up scroll_offset = max(0, scroll_offset - height) if __name__ == "__main__": if len(sys.argv) != 2: print("Usage: python git_time_machine.py path/to/file") sys.exit(1) filename = sys.argv[1] curses.wrapper(main, filename)