How to generate and parse an interactive rebase TODO list in Python

Generate a Git interactive rebase TODO list from commit data and parse it back into structured records.

Medium Python 3.9+ Aug 9, 2026 Git + Python 12 views 0 copies

Python code

38 lines
Python 3.9+
import re
from collections import namedtuple

Commit = namedtuple("Commit", ["hash", "subject"])

def generate_rebase_todo(commits, action="pick"):
    todo_lines = []
    for i, commit in enumerate(commits):
        if i == 0 and action == "reword":
            todo_lines.append(f"reword {commit.hash} {commit.subject}")
        elif action == "squash" and i == len(commits) - 1:
            todo_lines.append(f"squash {commit.hash} {commit.subject}")
        else:
            todo_lines.append(f"pick {commit.hash} {commit.subject}")
    return "\n".join(todo_lines)

def parse_todo(todo_text):
    lines = todo_text.strip().splitlines()
    parsed = []
    for line in lines:
        match = re.match(r"^(pick|reword|squash)\s+([a-f0-9]{7,40})\s+(.*)$", line)
        if match:
            parsed.append({"action": match.group(1), "hash": match.group(2), "subject": match.group(3)})
    return parsed

if __name__ == "__main__":
    commits = [
        Commit("a1b2c3d", "Initial commit"),
        Commit("e4f5a6b", "Add feature X"),
        Commit("7c8d9e0", "Fix bug in feature X"),
    ]

    todo = generate_rebase_todo(commits, action="squash")
    print("Generated TODO:")
    print(todo)
    print("\nParsed TODO:")
    for item in parse_todo(todo):
        print(f"  {item['action']:6s} {item['hash']} {item['subject']}")

Output

stdout
Generated TODO:
pick a1b2c3d Initial commit
pick e4f5a6b Add feature X
squash 7c8d9e0 Fix bug in feature X

Parsed TODO:
  pick   a1b2c3d Initial commit
  pick   e4f5a6b Add feature X
  squash 7c8d9e0 Fix bug in feature X

How it works

The generate_rebase_todo function builds a standard Git TODO list by iterating over commit tuples and applying a chosen action to specific positions (first or last). The parse_todo function uses a regex to validate each line and extract action, hash, and subject into a list of dictionaries. Using namedtuple keeps the commit data lightweight and readable, and the script's __main__ block demonstrates the full flow. This pattern mirrors the real structure Git writes to .git/rebase-merge/git-rebase-todo, so you can read or simulate it from Python.

Common mistakes

  • Forgetting that Git hashes can be full 40-char SHA-1, so the regex must allow 7 to 40 hex chars.
  • Not stripping trailing whitespace or newlines from each line before parsing.
  • Assuming the first commit is always 'reword' — the logic only rewrites based on the provided `action` parameter.

Variations

  1. Read an actual `git-rebase-todo` file with `pathlib.Path.read_text` and parse it directly.
  2. Use dataclasses instead of namedtuple for richer commit metadata.

Real-world use cases

  • Automating a script to prepare a rebase for the last N commits with a specific action like squash.
  • Validating a hand-written rebase TODO file before passing it to Git.
  • Integrating with a CI pipeline that rewrites commit history or checks commit hygiene.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Git + Python

Related tutorials and quizzes for this topic.