When would git log -L beat git blame for tracing one function's history?
answer
- Snapshot versus sequence
- One commit per line, or many
- Range must move through history
- Colon syntax names a function
- Automates the repeated drill-down
basics
~20 sgit blame gives one commit per line — the last to touch it. git log -L 20,40:file.py walks the whole history of that line range and prints every commit that changed it, with its patch, so you see the code's evolution rather than a single snapshot of authorship.
solid answer
~50 sBlame answers "who touched each line last"; it is a snapshot. When the real question is "how did this function get like this", you want the sequence of changes, and that is `git log -L`. Written `git log -L 20,40:src/app.py` for a numeric range or `git log -L :parseConfig:src/app.py` for a function, it walks backwards and prints each commit that modified that range together with the patch limited to it — effectively a mini history for a slice of a file. It implies patch output, so you read the actual edits with their messages in order. Use blame when you have one suspicious line and want a starting point; use `log -L` when you need the story: three refactors, a bug fix and a revert are visible at once instead of requiring repeated `git blame <commit>^` drill-downs. The cost is that `-L` is expensive on deep histories.
go deeper
Not expected to know -L, but knowing that blame gives only the last change per line — and that Git can show more — is a good foundation.
Be able to write git log -L 20,40:path and explain that it prints every commit touching that range with the patch limited to it.
Explain that the range is translated backwards through history at each step, justify the cost, and describe a real investigation where you escalated from blame to log -L.
Frame it as incident-forensics capability: reconstructing when and why a piece of logic changed depends on commit granularity and message quality, which is a standard you set rather than a command you run.
## Two different questions Both commands look at a region of a file, and interviewers use the pair to see whether you understand what each actually computes. - **`git blame`** produces, for each line *currently* in the file, the single most recent commit that changed it. It is a projection of history onto today's content. - **`git log -L`** produces, for a *range* of lines, the ordered list of commits that modified that range, each with the corresponding patch. It is a history walk restricted to a slice. If a function was rewritten four times, blame shows you the fourth. `git log -L` shows you all four, in order, with messages. ## Syntax - `git log -L 120,180:src/service.py` — lines 120 to 180 of that path, as numbered in the starting revision. - `git log -L 120,+30:src/service.py` — thirty lines starting at 120. - `git log -L :handleRequest:src/service.py` — the region Git recognizes as that function, using its language-aware function-name detection. - The option may be repeated to follow several ranges in one command. `-L` implies patch output, so each listed commit comes with the diff restricted to the tracked range. You can add `-s` to suppress the patches when you only want the commit list. Note that `-L` cannot be combined with an ordinary pathspec — the path is part of the `-L` argument itself. ## Why the range "moves" The subtlety that makes `-L` useful is that Git does not hold the line numbers fixed. As it walks backwards, it maps the range onto the previous version of the file: if a commit inserted twenty lines above your function, the range in the parent is twenty lines earlier. Without that, going back more than one commit would silently drift onto unrelated code. This is why `-L` is comparatively expensive — Git is computing diffs to carry the range backwards at each step — and why it can be slow on a file with tens of thousands of commits. ## Choosing between them Reach for **blame** when: - You have one line that looks wrong and want a commit id to start from. - You want an at-a-glance view of how old the various parts of a file are. - You need move and copy detection for a specific line, via `-M` and `-C`. Reach for **`git log -L`** when: - You need the evolution of a block: the original implementation, the fix, the refactor, the revert. - Blame keeps pointing at a commit that is only the latest in a chain and you would otherwise be running `git blame <commit>^` repeatedly. `log -L` is that loop, automated and presented in order. - You are writing an incident timeline and need dates and messages for every change to a specific piece of logic. The two compose naturally: blame to identify the interesting region and get a revision, then `log -L` on that region to read the story. ## The drill-down loop it replaces Done by hand, tracing a function's history with blame looks like: blame the file, note commit X, run `git blame X^ -- file` for the same range, note commit Y, repeat. Each step needs you to re-find the range because line numbers shift. `git log -L` performs exactly this walk internally and prints the results in one pass, which is why experienced engineers switch to it as soon as they realize they are on their second or third manual drill-down. ## Limits and pitfalls - **Cost.** On large repositories with long histories, `-L` on a big range can take a noticeable amount of time. Narrow the range, and bound the walk with a starting revision when you already know roughly when to look. - **Function detection depends on the file type.** The `:funcname:` form relies on Git's language-aware function-name recognition; for languages Git does not recognize well, the region it picks may not be the function you meant, and a numeric range is more predictable. - **It is still a text-level view.** Neither command understands the code. A function that was renamed and reimplemented may look like a deletion and an unrelated addition in both tools; that is where `git blame -C` and reading the surrounding commits earn their keep. ## The habit worth demonstrating What interviewers are listening for is that you treat both as tools for *understanding code*, and that you pick the one matching the question you actually have. Naming `-L`, explaining that it moves the range backwards through history, and describing when you escalate from blame to it is a strong senior answer.
- How does git log -L keep tracking the right code when line numbers shift?It maps the range onto the parent version at each step, using the diff between the two commits to translate line positions. So if a commit added lines above your function, the range moves accordingly as the walk goes backwards. That translation is also why the option is comparatively expensive.
- When is a numeric range preferable to the :funcname: form?When the file's language is not one whose function boundaries Git recognizes well, or when the region you care about is not a whole function — a configuration block, a table of constants, a section of a template. A numeric range is predictable; function detection can select more or less than you meant.
- How would you combine blame and log -L in a real investigation?Use blame first on the suspicious lines to get a commit id and a rough sense of age, then run git log -L on that range to read every change in order with its message and patch. Blame localizes the question; log -L answers it with the full sequence.
- What can neither command tell you about a function that was renamed and rewritten?Neither understands code semantics, so a rename plus reimplementation can look like an unrelated deletion and addition. You bridge the gap by blaming with -C to follow moved lines and by reading the commits around the discontinuity, where the message usually explains the restructuring.
saying these in an interview costs you the question
- Thinks blame shows a range's full evolution
- Believes log -L is just blame with more output
- Expects fixed line numbers across history
- Runs log -L on a huge range then blames Git
- Cannot say why blame needs repeated drill-downs