admin管理员组

文章数量:1334909

There's a file that contains lots of project configuration, including a line that reads something like:

bundleVersion: 1.2.3

I want to find what was the last git commit in my current git log history, excluding commits that only touch that particular line.

I want something like git log -1 --format='%H' --invert-grep --grep='^\s*bundleVersion:\s[a-zA-Z0-9.]+$', except this command only grep for stuff in the git message, and I want it to grep the contents of the commit, not the message.

There's a file that contains lots of project configuration, including a line that reads something like:

bundleVersion: 1.2.3

I want to find what was the last git commit in my current git log history, excluding commits that only touch that particular line.

I want something like git log -1 --format='%H' --invert-grep --grep='^\s*bundleVersion:\s[a-zA-Z0-9.]+$', except this command only grep for stuff in the git message, and I want it to grep the contents of the commit, not the message.

Share Improve this question edited Nov 22, 2024 at 19:01 Zorzella asked Nov 20, 2024 at 22:02 ZorzellaZorzella 9,0793 gold badges20 silver badges13 bronze badges 2
  • Last commit in a repo is a flaky concept. You mean the last commit that was pushed, for example? That commit might have been made 2 years ago also, we don't know when a commit was pushed (or pulled) into a repo ... Those are two fast and furious buts to the way you are setting up the question.... more can be brought up – eftshift0 Commented Nov 21, 2024 at 0:04
  • Ok, make it last git commit in the current git log history (I tweaked the question to reflect that). Like I said, I want something that is like the --invert-grep --grep I can use on git log except that it greps the contents not the messages. – Zorzella Commented Nov 22, 2024 at 19:00
Add a comment  | 

2 Answers 2

Reset to default 0

I guess you can pull it off with a script using a recursive technique.

One iteration that uses commit X looks like this:

1 find the position of the line you pointed to there on the file on commit X (tip: git show X:the-config-file, and make sure to use the colon, otherwise what you are asking for is very different)

2 blame (-w) the config file for commit X, just that line, so that the blame process is faster. That will give you the commit where that line was introduced in the config file. That commit is from now onC.

3 C == X? If C is NOT X, then X is the commit you are looking for.

4 If X changed more than just that line in the config, then X is the commit you are looking for.

5 we need to iterate so run the same process recursively using C~ (keep the pigtail) as X.

The whole process starts using HEAD as X.

The fact that you are doing this makes me think that lots of the changes that are committed are just adjustments to this line? If that is the case and the process is slow using git because it's hundreds or thousands of commits, perhaps?), consider writing something that uses libgit2 (writing something in python is not that complex and it improves speed like crazy because of not starting a git process for each git operation).

You can use git diff-tree -r --name-only $commit~ $commit to efficiently check whether the commit changes only the file you're interested in, git diff-tree -r --numstat $commit~ $commit to check whether that changes only one line, and git log --no-walk -pretty=format:ignore -G 'pattern' to test whether that one line change matches your pattern. If the file's at the top level you can leave off the -r flag. So you want something like this (very lightly smoketested) script:

path=your.noisy.config
pattern=bundleversion:
git rev-list @ | while read commit; do
        # if it doesn't change only the one annoyingly noisy path, we're there 
        if test x"$(git diff-tree -r --name-only $commit~ $commit)" != x"$path"
        then break
        fi
        # if it doesn't only change a single line in it, we're there
        set -- $(git diff-tree -r --numstat $commit~ $commit)
        test $1$2 != 11 && break
        # if that change doesn't match the annoying pattern, we're there
        res=$(git log --no-walk --pretty=format:ignore -G "$pattern" $commit)
        test x$res != xignore && break
done
echo $commit | git log --no-walk --stdin

本文标签: