git/list[1] front-page[2] threads[3] people[4] search[5] about
 

git blame --ignore-rev degenerate performance on large(r) line counts

From
SHStefan Hoffmeister <stefan.hoffmeister@econos.de>
Date
Aug 1, 2021, 12:58 UTC
Message-ID
<CALhB_QNf98O=kOnZ=rmUt90SmqZzxRF2CfyYOKiiJv+Qa2abqg@mail.gmail.com>

git blame has a very useful option --ignore-rev (or --ignore-rev-file) which allows ignoring of commits for the purpose of attributing blame.

One use case for this option is a scenario where a repository was "cleaned" up for line breaks, e.g. via "git add --renormalize ."

Alas, git blame with --ignore-rev will fall apart at runtime whenever the number of lines of code affected is high; it will show exponential increases in execution duration on linearly increasing line counts.

The Python script below demonstrates the undesirable performance behaviour for a single text file and just two commits. In a real world scenario, with a repository-wide line-ending fix commit affecting a large number of _files_ (in addition to some files being large), performance behaviour will be even worse.

The Python script below will initialize a new repo and do the reproduction dance fully isolated and offline; "matplotlib" as a dependency (pip install matplotlib) is a nice optional add-on to have, as it visualizes that on my system (i7-7820HQ, 2.9 GHz) runtime duration explodes exponentially at around 35000 lines of "Hello Hello" text.

**************** #!/usr/bin/env bash

# Beware - no error handling

import matplotlib.pyplot as plt import os import shutil import subprocess import time from typing import Generator

REPO_LOCATION="git-repository-container/git-repo" BLAME_ME_TXT="blame-me.txt"

def run_iteration(line_count: int):
    shutil.rmtree(REPO_LOCATION, ignore_errors=True)
    os.makedirs(REPO_LOCATION, exist_ok=False)
    os.chdir(REPO_LOCATION)
    subprocess.run(["git", "init"], capture_output=True)
    subprocess.run(["git", "config", "--local", "user.name", "me"],
capture_output=True)
    subprocess.run(["git", "config", "--local", "user.email",
"me@example.com"], capture_output=True)
    with open(".gitattributes", "w") as gitattributes:
        gitattributes.writelines("* text=crlf\n")
    def produce_some_file_content_sequence(line_count: int) ->
Generator[str, None, None]:
        mytext: str = "Hello Hello\r\n"
        num = 0
        while num < line_count:
            yield mytext
            num += 1
    with open(BLAME_ME_TXT, "w") as blame_me:
        for line in produce_some_file_content_sequence(line_count):
            blame_me.writelines(line)
    subprocess.run(["git", "add", "."], capture_output=True)
    subprocess.run(["git", "commit", "-m", "Initial commit"],
capture_output=True)
    capture = subprocess.run(["git", "rev-list", "HEAD"], capture_output=True)
    initial_commit_rev = (capture.stdout).decode().strip()
    with open(".gitattributes", "w") as gitattributes:
        gitattributes.writelines("* text=auto\n")
    subprocess.run(["git", "add", "--renormalize", "."], capture_output=True)
    subprocess.run(["git", "commit", "-m", "Renormalized"], capture_output=True)
    capture = subprocess.run(["git", "rev-list", "--max-count", "1",
"HEAD"], capture_output=True)
    renormalized_commit_rev = (capture.stdout).decode().strip()
    blame_capture = subprocess.run(["git", "blame", "-C ./", "--line-porcelain",
        "--ignore-rev", renormalized_commit_rev,
        "HEAD", BLAME_ME_TXT],
        capture_output=True)
x=list[int]()
y=list[float]()
for factor in range(40):
    start = time.time()
    line_count = 1000 * factor
    x.append(line_count)
    print(f'Line count: {line_count}')
    run_iteration(line_count)
    end = time.time()
    duration = end - start
    print(duration)
    y.append(duration)

plt.plot(x, y) plt.show()

Message 1 of 1 in “git blame --ignore-rev degenerate performance on large(r) line counts”
  1. Stefan HoffmeisterAug 1, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.