grep1-8 Exercise 1: Searching Tracked Files and Old Commits ============================================================ (Run in the practice repository from the chapter. Make it first with the block at the top of the chapter, then change into the proj folder.) echo "tracked text files that mention TODO:" git grep -lI TODO echo echo "in the first commit, gone now:" git grep -I -n TODO HEAD~1 | grep -v -F -f <(git grep -I -h TODO | sed 's/^[^:]*:[0-9]*://') echo echo "lines with TODO and alice:" git grep -n -e TODO --and -e alice echo echo "files that grep -r finds and git grep does not:" diff <(git grep -lI TODO | sort) <(grep -rlI TODO . | sed 's|^\./||' | sort) | grep '^>' | sed 's/^> / /' Output: tracked text files that mention TODO: README.md src/app.js src/db.py in the first commit, gone now: HEAD~1:src/db.py:2:# TODO old note about caching lines with TODO and alice: src/app.js:1:// TODO(alice): handle timeout files that grep -r finds and git grep does not: .env .git/hooks/sendemail-validate.sample .hidden-notes build/out.js debug.log scratch.txt WHY THIS WORKS AS AN ANSWER --------------------------- git grep -lI lists the tracked text files that contain TODO (-I skips the binary, as in grep). The second search looks at the first commit (HEAD~1) and removes every line whose text still exists today, which leaves only the note that was deleted. git prints the commit name in front of each line, so the filter compares just the text after the line number. --and joins two patterns on the same line, with no second grep. The last block compares git grep's list with plain grep -rlI. What grep -r finds extra is exactly the files git does not track or that are ignored: build/out.js, debug.log and .env (ignored), scratch.txt and .hidden-notes (not tracked), and the sample hooks inside .git. Those are the three kinds: ignored, untracked or hidden, and git's own files.