mkk
mkk

Reputation: 1043

how to search all git commits for changes involving multiple strings with certain restrictions on unchanged content?

The following will search commits for changes involving "foo":

$ git log -S foo

How can I:

Partial solution based on answer by @LeGEC
#!/bin/bash

cd $GITDIR

declare -a myhashes=($(git log --all --pretty=%H))

for myhash in "${myhashes[@]}";do
    declare -a myfiles=($(git diff --pickaxe-regex -S "foo|bar" --name-only $myhash))
    echo found "${#myfiles[@]}" files starting with ${myfiles[1]}

    # Files with Foo
    declare -a fwf=($(git grep -l -e foo $myhash -- $myfiles))
    # Files with Bar
    declare -a fwb=($(git grep -l -e bar $myhash -- $myfiles))

    # how to intersect fwf,fwb?
done

Upvotes: 1

Views: 902

Answers (1)

LeGEC
LeGEC

Reputation: 52111

  • search for changes involving foo and bar :

If you add --pickaxe-regex, the argumet to -S will be treated as a regexp :

git log --pickaxe-regex -S "foo|bar"

(see "a note about -S" below)

  • search for changes involving foo and bar :

you can use git log to list all potential commits, and then refine from there (see "About your second point" below)

  • include commits from archived and non-archived branches :

simply add --all to git log :

git log --all --pickaxe-regex -S "foo|bar"

About your first point :
a note about -S :

-S spots commits that change the number of lines matching the pattern. So using -S "foo|bar" (with regexes on) would overlook a commit where one line containing foo is turned into one line containing bar.

If that's not what you wish, you may be looking for -G, or you may want to make something out of the output of the two commands git log -S foo and git log -S bar.


About your second point :

if you add --pretty=%H to your git log command, you will have, as an output, only a list of hashes, for all the commits that may interest you.

To list the files within those commits that may interest you, you may either add --name-only to the git log command, or take these commits one by one, and re-run them through git diff --pickaxe-regex -S "foo|bar" --name-only <sha>.

Once you have a list of target commits, and a list of file names for all commits, you can check the content of each file within its target commit to see if it has both foo and bar within its content.

You can for example use git grep -l -e foo <sha> -- <list of files> and git grep -l -e bar <sha> -- <list of files> and combine the outputs to see what files contain both patterns.

You may also want to check the content of each file before the commit ; e.g : do you want to keep a file where foo was changed to bar ?
If such is the case, file could contain only bar in the target commit (<sha>), and could contain only foo in the parent commit : <sha>^ :

# you may want to check the content of target files in the parent commit :
git grep ... <sha>^

You will need some scripting on top of those git commands to get a complete solution.

Upvotes: 2

Related Questions