gitversion-control

How do I find removed lines with Git?


From time to time, it happens that I want to readd some code which I removed from a repository some time ago. I always use tools like tig to browse the history to find the commit which removed some lines.

Is there a way to find removed lines with Git? Something like git-grep, but for commit content instead of commit messages?


Solution

  • If you remember some keyword, e.g., the name of a variable or function, in the removed lines, use pickaxe search as in

    git log -Skeyword
    

    or to see the deltas along with the commit messages

    git log -p -Skeyword
    

    Although -S searches for exact string matches a la fgrep, you can search for regex matches with -G instead.

    Note that the argument to -S or -G is “cuddled” with no space in between.

    Documentation:

    -S<string>

    Look for differences that change the number of occurrences of the specified string (i.e. addition/deletion) in a file. Intended for the scripter’s use.

    It is useful when you’re looking for an exact block of code (like a struct), and want to know the history of that block since it first came into being: use the feature iteratively to feed the interesting block in the preimage back into -S, and keep going until you get the very first version of the block.

    -G<regex>

    Look for differences whose patch text contains added/removed lines that match .

    To illustrate the difference between -S<regex> --pickaxe-regex and -G<regex>, consider a commit with the following diff in the same file:

    +    return !regexec(regexp, two->ptr, 1, &regmatch, 0);
    ...
    -    hit = !regexec(regexp, mf2.ptr, 1, &regmatch, 0);
    

    While git log -G"regexec\(regexp" will show this commit, git log -S"regexec\(regexp" --pickaxe-regex will not (because the number of occurrences of that string did not change).

    See the pickaxe entry in gitdiffcore [reproduced below] for more information.


    diffcore-pickaxe: For Detecting Addition/Deletion of Specified String

    This transformation limits the set of filepairs to those that change specified strings between the preimage and the postimage in a certain way. -S<block of text> and -G<regular expression> options are used to specify different ways these strings are sought.

    -S<block of text> detects filepairs whose preimage and postimage have different number of occurrences of the specified block of text. By definition, it will not detect in-file moves. Also, when a changeset moves a file wholesale without affecting the interesting string, diffcore-rename kicks in as usual, and -S omits the filepair (since the number of occurrences of that string didn’t change in that rename-detected filepair). When used with --pickaxe-regex, treat the block of text as an extended POSIX regular expression to match, instead of a literal string.

    -G<regular expression> (mnemonic: grep) detects filepairs whose textual diff has an added or a deleted line that matches the given regular expression. This means that it will detect in-file (or what rename-detection considers the same file) moves, which is noise. The implementation runs diff twice and greps, and this can be quite expensive.

    When -S or -G is used without --pickaxe-all, only filepairs that match their respective criterion are kept in the output. When --pickaxe-all is used, if even one filepair matches their respective criterion in a changeset, the entire changeset is kept. This behavior is designed to make reviewing changes in the context of the whole changeset easier.