查找 Git 存储库中删除代码的位置

我如何找到被删除的代码?

我最终找到了它的产生地:

$ git log --pretty=oneline -S'some code'

这已经足够了,但我还是很好奇它是在哪里被删除的,到目前为止,还没有结果。

首先,我尝试了 git diff HEAD..HEAD^ | grep 'some code',每次都扩展范围,直到找到删除它的行。很好,那么假设我在 HEAD^^..HEAD^^^范围内找到它,然后我用 grepgit show HEAD^^^git show HEAD^^,但是找不到代码!

然后我在 git bisect上读了一点,果然,它给了我一个单一的修订版,罪魁祸首应该是... ... 同样,git show rev | grep 'some code'是空的... ..。

我做错了什么?

34565 次浏览

Hmph, this works for me:

$ git init
Initialized empty Git repository in /Users/pknotz/foo/.git/


$ echo "Hello" > a


$ git add a


$ git commit -am "initial commit"
[master (root-commit) 7e52a51] initial commit
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 a


$ echo " World" >> a


$ git commit -am "Be more specific"
[master 080e9fe] Be more specific
1 files changed, 1 insertions(+), 0 deletions(-)


$ echo "Hello" > a


$ git commit -am "Be less specific"
[master 00f3fd0] Be less specific
1 files changed, 0 insertions(+), 1 deletions(-)


$ cat a
Hello


$ git log -SWorld
commit 00f3fd0134d0d54aafbb9d959666efc5fd492b4f
Author: Pat Notz <patnotz@gmail.com>
Date:   Tue Oct 6 17:20:48 2009 -0600


Be less specific


commit 080e9fe84ff89aab9d9d51fb5d8d59e8f663ee7f
Author: Pat Notz <patnotz@gmail.com>
Date:   Tue Oct 6 17:20:33 2009 -0600


Be more specific

Or, is this not what you mean?

git log -S<string> does the job, but if you need to make more complex searches you can use git log -G<regex>.

From the man:

-G<regex>

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

I've found a way to find the specific commit, but it is only shown when diffing between these commits. I can't explain why Git works that way, but I'll elaborate my findings:


Problem explained

With '-S' or '-G', we can only find where it was added, not removed. To elaborate:

$ git log --format="%H" -S"127.0.124.1"
857aa361293abbb351d6d6becaa55ec011aebc93
$ git show 857aa361293abbb351d6d6becaa55ec011aebc93 | grep "127.0.124.1" # Remove grep to see full diff
+       return "127.0.124.1", nil

Step by step

I got the idea from miku86 on dev.to. First we try to find the last commit our string existed:

$ git --no-pager grep "127.0.124.1" $(git rev-list --all) # | head -n1 | cut -d: -f1 # Uncomment to only show the hash of the first one
ba0b2d348f4e33857c96acf5a6231cf9d89ddb1b:some/file.go:        return "127.0.124.1"
0d19a98d6434da0b4b5cc2bac190b9b1de36d992:some/file.go:        return "127.0.124.1"
84274c5712bacbbee1dca5567cef77a2b6f356d2:some/file.go:        return "127.0.124.1"
42e692643ff8a5dce7a89e985062b3d38c60fcc0:some/file.go:        return "127.0.124.1"

Which results in ba0b2d348f4e33857c96acf5a6231cf9d89ddb1b is our searched commit. Next we try to find the commit following after this one until HEAD:

$ git rev-list ba0b2d348f4e33857c96acf5a6231cf9d89ddb1b.. # | tail -n1 # Uncomment to only show the last one
[...]
c82d040f7be2f8955075655843400a36ceb75303
aa0568b543db57564770d73e736aaf50fd749fb4
dde61dae0cf648e7f4dd8a5c194bcf9be1745793

Which is dde61dae0cf648e7f4dd8a5c194bcf9be1745793, since this is the last one in this list we're looking for.

So does that mean dde61dae0cf648e7f4dd8a5c194bcf9be1745793 should contain that removal?

$ git show dde61dae0cf648e7f4dd8a5c194bcf9be1745793 | grep "127.0.124.1" # Remove grep to see the full diff
#<no output>

No?!? Well, what's the diff then from that to our first found commit?

$ git diff ba0b2d348f4e33857c96acf5a6231cf9d89ddb1b..dde61dae0cf648e7f4dd8a5c194bcf9be1745793 | grep "127.0.124.1" # Remove grep to see the full diff
-       return "127.0.124.1"

Here we've found it. That's odd. But since we're using a diff between commits, did I made a mistake and there are other commits in-between?

$ git log --pretty=oneline ba0b2d348f4e33857c96acf5a6231cf9d89ddb1b..dde61dae0cf648e7f4dd8a5c194bcf9be1745793
dde61dae0cf648e7f4dd8a5c194bcf9be1745793 network interface

No, we seem to haven't made a mistake. That's odd. But yeah, that's how far I got to find the specific spot of change where the string vanished.


Additionally

I could confirm using git bisect that dde61dae0cf648e7f4dd8a5c194bcf9be1745793 is the commit I was looking for.

# Bisecting a while...
$ git bisect bad
dde61dae0cf648e7f4dd8a5c194bcf9be1745793 is the first bad commit
commit dde61dae0cf648e7f4dd8a5c194bcf9be1745793
Author: ...
Date:   Fri Oct 18 11:04:26 2019 +0200


network interface


:100644 100644 242e81c363c2c7069efb018821821553b98f2c97 416c27f5351a9d92a6914b34846c6de51d83dd0d M      go.mod
:040000 040000 715c3ab5b3f4579d8054618f1e11fc05fb425324 e6731408a1ac1ff0428128ccbb1fbfaad4c789ab M      network
[...]