Find a Git branch containing changes to a given file
I have 57 local branches. I know I made a change to a certain file in one of them, but I'm not sure which one. Is there some kind of开发者_如何转开发 command I can run to find which branches contain changes to a certain file?
Find all branches which contain a change to FILENAME (even if before the (non-recorded) branch point)
FILENAME="<filename>"
git log --all --format=%H $FILENAME | while read f; do git branch --contains $f; done | sort -u
Manually inspect:
gitk --all --date-order -- $FILENAME
Find all changes to FILENAME not merged to master:
git for-each-ref --format="%(refname:short)" refs/heads | grep -v master | while read br; do git cherry master $br | while read x h; do if [ "`git log -n 1 --format=%H $h -- $FILENAME`" = "$h" ]; then echo $br; fi; done; done | sort -u
All you need is
git log --all -- path/to/file/filename
If you want to know the branch right away you can also use:
git log --all --format=%5 -- path/to/file/filename | xargs -I{} -n 1 echo {} found in && git branch --contains {}
Further, if you had any renames, you may want to include --follow
for the Git log command.
Looks like this is still a problem without an appropriate solution. I don't have enough credits to comment, so here's my little contribution.
Seth Robertson's 1st solution kinda worked for me, but only gave me local branches, among which where many false positives, probably because of merges from the stable branch.
Adam Dymitruk's 2nd solution didn't work for me at all. For starters, what's --format=%5? It isn't recognized by git, I couldn't find anything about it and I couldn't get it to work with other format options.
But his 1st solution combined with the --source option and with a simple grep proved helpful:
git log --all --source -- <filename> | grep -o "refs/.*" | sort -u
This gives me a number of remote tags and branches and one local branch, where I made the latest changes to the file. Not sure how complete this is.
UPDATE as per @nealmcb 's request, sorting branches by most recent change:
First, you could change the grep to "refs/heads/.*", which will give you the local branches only. If there are only a few branches, you could examine the latest commit of each one like this:
git log -1 <branch> -- <filename>
If there are more branches and you really want to automate this, you can combine the two commands using xargs, git log formatting and another sort into this one-liner:
git log --all --source -- <filename> | grep -o "refs/heads/.*" | sort -u | xargs -I '{}' git log -1 --format=%aI%x20%S '{}' -- <filename> | sort -r
This will result in output like this:
2020-05-07T15:10:59+02:00 refs/heads/branch1
2020-05-05T16:11:52+02:00 refs/heads/branch3
2020-03-27T11:45:48+00:00 refs/heads/branch2
I know this question is ancient, but I kept comming back to it before developing my own solution. I feel it's more elegant and thanks to use of merge base filters out unwanted branches.
#!/bin/bash
file=$1
base=${2:-master}
b=$(tput bold) # Pretty print
n=$(tput sgr0)
echo "Searching for branches with changes to $file related to the $base branch"
# We look through all the local branches using plumbing
for branch in $(git for-each-ref --format='%(refname:short)' refs/heads/); do
# We're establishing a shared ancestor between base and branch, to only find forward changes.
merge_base=$(git merge-base $base $branch)
# Check if there are any changes in a given path.
changes=$(git diff $merge_base..$branch --stat -- $file)
if [[ ! -z $changes ]]; then
echo "Branch: ${b}$branch${n} | Merge Base: $merge_base"
# Show change statistics pretty formatted
git diff $merge_base..$branch --stat -- $file
fi
done
If you put it in PATH as git-find-changes
(with executable permissions) - you can then call it with git find-changes /path
Example output for
git find-changes app/models/
Branch: update_callbacks | Merge base: db07d23b5d9600d88ba0864aca8fe79aad14e55b
app/models/api/callback_config.rb | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
Branch: repackaging | Merge base: 76578b9b7ee373fbe541a9e39cf93cf5ff150c73
app/models/order.rb | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
The following is an inelegant brute-force method, but I expect it should work. Make sure you've stashed any uncommitted changes first as it will switch which branch you are currently on.
for branch in $(git for-each-ref --format="%(refname:short)" refs/heads); do
git checkout $branch && git grep SOMETHING
done
精彩评论