Xevious
Xevious

Reputation: 427

Find to which git commit a file belongs?

Given a random file, is there a canonical method to determine from the command line whether the file belongs to a particular commit?

This is similar to stack overflow question find-out-which-git-commit-a-file-was-taken-from except that I wish to be able to use it in scripts and also not create a temporary branch.

Upvotes: 8

Views: 1654

Answers (4)

David Neiss
David Neiss

Reputation: 8237

Do you mean, was the file modified in a commit? If so, something like git log --oneline -- filePathName should list the commits from HEAD where that is the case.

On second reading, I think you are just asking for commits which contain that file, whether or not its changed. If so, then doesn't your ls-tree need a -r flag, to recurse into its sub-trees (subdirs)? that will find any copies of a file under any name, if you just match on the sha.

Upvotes: 1

G. Sylvie Davies
G. Sylvie Davies

Reputation: 5507

Building on DavidN's answer, if the file is in the current worktree, and the worktree is in sync with HEAD, this will get you the commit corresponding to the file's current contents:

git log --pretty="%H" -1  -- path/to/file

But you might want to test those assumptions ahead of time via "git diff --exit-code /path/to/file" and taking a peek at $?.

Upvotes: 1

Leon
Leon

Reputation: 32454

Your approach may fail to work in case of insignificant differences (e.g. line-ending style, or differences due to clean/smudge filters) between the local and repository versions of the file.

The following script works via git diff rather than relying on hashes. It accepts diff options after the file name.

Usage examples:

# list all commits that introduce the file README.md in its local state
list_introducing_commits README.md

# list all commits that introduce the file README.md in its local state
# ignoring any difference in whitespace
list_introducing_commits README.md -w

list_introducing_commits (couldn't find a better name):

#!/bin/bash

if [ $# -eq 0 ]
then
    echo "Usage: $(basename $0) path/to/file [<diff-options>]"
    exit 1
fi

file="$1"
shift 1

for rev in $(git log --pretty=%H -- "$file")
do
    if git diff --exit-code $@ $rev -- $file &> /dev/null
    then
        echo $rev
    fi
done

Upvotes: 1

Xevious
Xevious

Reputation: 427

Below is an excerpt of a script that I have been using for the purpose. I hacked it together using my limited git knowledge and pieces of other scripts that I have found on the web. It works well enough, but I often find that there are easier ways to do things in git than what I have learned by trial and error.

FILE=$1

# git hash for file
HASH=`git hash-object $FILE`

# git revisions for file
REVS=`git log --pretty=%H -- $FILE`

# check each revision for checksum match
for rev in $REVS; do
    POSSIBLE=`git ls-tree $rev $FILE | awk '{print $3}'`
    if [[ $HASH == $POSSIBLE ]]; then
        echo $rev
    fi
done

Upvotes: 4

Related Questions