GIT: How to search through older versions of a file and list all versions that match the search criteria? - bash

I have a repo that contains a certain file. I need to create a git script that searches through all the previous versions of this file and lists the commit SHAs that contain a specific string.
I want to have a list of all the commits that in their version that string exists.
The best answer I could find is here (but not good enough): https://stackoverflow.com/a/4705617/4441211
This solution is not good enough because this only finds where there was a change (i.e the search term was added or removed). Does anybody have a better idea how to do this?

To look for a pattern <pattern> in a file <path/to/file> within a commit <commit> : use git grep
git grep -e <pattern> <commit> -- <path/to/file>
Check git help grep for more details : many options are copied of the original grep command (-l to only list file names, -q to drop output ...)
If your intention is to scan all commits looking for a string (not just the commits where a change occured) :
git rev-list HEAD will give you the list of all commits in the ancestry of your active branch,
write a loop to repeatedly call git grep on these commits.
For example :
git rev-list HEAD | while read sha; do
git grep -q -e <pattern> $sha -- <path/to/file> && echo $sha
done

Related

Check if git branch contains commit with given subject

Given an exact subject line of a commit I want to know if a branch contains a commit with that subject.
What I have right now is: git log --format="%s" -F --grep="$msg" "$branch" | grep -Fq --max-count=1 -- "$msg"
I.e. search the log with a fixed-string grep and print the subject. Then search that subject with the same fixed-string and stop at the first match.
The 2nd grep is required because git log --grep may find the pattern anywhere in the commit message (e.g Fixes "$msg")
However this has the downside that it seemingly always walks the entire history of that branch which takes quite long.
As a test I ran git log --format="%s" -F --grep="$msg" "$branch" | grep -Fq --max-count=1 -- "$msg" and git log --format="%s" -F --grep="$msg" "$branch" and they both took the same time although the commit to grep for is found/printed very fast (for the 2nd)
So is there a way to directly find a commit in a branch by a given subject (using fixed strings as $msg comes from another command and may contain regex-like characters) faster or at least make my log-grep-pipe exit faster (on success)?
Try this:
git log --format="%s" "$branch" | grep -Fqx --max-count=1 -- "$msg"
Here x will match the entire line.. and max-count will return the first match.

Pass the stdout of `git diff --name-only` to an executable file as parameters of the file

So, there's this executable file called pint (it's part of the Laravel 9 framework). It formats PHP files according to a configurable standard like PSR12.
Anyway, you can pass a list of the files you want to format as the parameters of pint like this:
pint file1 file2 file3 file4 ...
If you pass no argument, pint formats all files of the project.
So, what I'm trying to do is that I want pint to format only the files that have changed since the last commit. In other words, I want the output of git diff --name-only HEAD^ HEAD to be passed as parameters of pint in bash shell.
So, this is what I could come up with, but sadly it doesn't work:
git diff --name-only HEAD^ HEAD -o /dev/stdout | ./vendor/bin/pint
Which says: fatal: /dev/stdout: '/dev/stdout' is outside repository at '/home/user/directory' and then it proceeds to execute ./vendor/bin/pint normally as if no argument had been passed.
I suppose I should somehow convert new line to space before passing it to pint but I'm not sure.
| is for piping to standard input, but pint expects the filenames to be command-line arguments, not stdin.
Use $(...) to substitute the output of a command into the command line.
./vendor/bin/pint $(git diff --name-only HEAD^ HEAD)
Note that this won't work if any of the filenames contain whitespace, since the spaces will be treated as filename delimiters.

Output first line of temporary file

I am writing a script to checkout the latest git commit with the word "merge" inside the commit message. I am trying to output the latest git commit line to see if my script is working so far
tempfile=$((mktemp))
tempfile2=$((mktemp))
git log --oneline >> $tempfile
grep -i "merge" $tempfile | head -1 > $tempfile2
cat $tempfile2
I was expecting the script to output just one line containing "merge" however it outputs the whole log. I actually just want to take the commit id and git checkout the latest "merge" file to a detached head state. If anyone could help me with what to do next I would greatly appreciate it. Thank you

To list the branch names based on branchtype

I have git branches under the type feature ad release i want to display the release branches when i give the input as release or the possible string(re, rel..etc) same as to feature branch and other branch types as well. I have a command to give the branch type now after when i get the branch type as release or feature based on my input i want to display the branch names under that type i choose.
Command I have:
#!/bin/bash
read branchtype
git branch --remote --list origin/* | grep $branchtype | cut -d '/' -f2 | sort -u
#!/bin/bash
read branchtype
git branch --remote --list origin/* | grep $branchtype | cut -d '/' -f2 | sort -u
First, I think you will have a much easier time if you use arguments to the script rather than reading stdin.
Second, why use a global pattern (origin/*) to return everything just to filter with a separate program (grep)?
Finally, maybe I misunderstand, but I imagine branch names like origin/feature/newThing and origin/feature/coolUpgrade and origin/bugfix/oopsie. Selecting for feature, that cut -d '/' -f2 | sort -u is just going to spit out feature and nothing else. (maybe you wanted -f3?)
Try this:
(create the file ~/bin/branchFilter first...)
$: cat ~/bin/branchFilter
#!/bin/bash
git branch --remote --list origin/*$1*
Then use it. (Make sure you set executable permissions.)
$: branchFilter # no argument
origin/HEAD -> origin/master
origin/bugfix/bar
origin/bugfix/foo
origin/dev
origin/feature/otherName
origin/feature/test
origin/hotfix/thisIsBroken
origin/master
origin/preprod
origin/production
origin/qa
origin/sit
origin/uat
origin/wip
$: branchFilter feature
origin/feature/otherName
origin/feature/test
$: branchFilter bugfix
origin/bugfix/bar
origin/bugfix/foo
$: branchFilter fix
origin/bugfix/bar
origin/bugfix/foo
origin/hotfix/thisIsBroken
If you really need the origin/ off the beginning, trim it with sed.
$: cat ~/bin/branchFilter
#!/bin/bash
git branch --remote --list origin/*$1* |
sed 's,origin/,,'
$: branchFilter fix
bugfix/bar
bugfix/foo
hotfix/thisIsBroken
If you just have to read from the input (I think it's a bad structure, but your program), it still works the same way.
$: cat ~/bin/branchFilter
#!/bin/bash
read str
git branch --remote --list "origin/*$str*" |
sed 's,origin/,,'

git pull always fails, but git fetch/merge are fine

When I git pull in any repository, I always get the following merge error:
aetherboard:shwangster shwangster$ git pull -v
From github.com:sirspinach/shwangster
= [up to date] master -> origin/master
merge: 012012012012012012012012012012012012012012012012012012012012 - not
something we can merge
On the other hand, git fetch and git merge origin/master work like a charm. So I've been able to work around this problem for a while. However, I needed to update brew today, and the same error prevents me from doing that.
Here is the output from brew update, which shows git again attempting to merge with the mysterious 0120120120120....
aetherboard:gitrepos shwangster$ brew update
merge: 012012012012012012012012012012012012012012012012012012012012 - not
something we can merge
Error: Failure while executing: git pull -q origin refs/heads/master:refs/remotes/origin/master
There's a clue in the other (pretty much exact duplicate) question that Kaz noted in a comment, that the problem went away when pyenv was taken out of $PATH.
Here's the bit from the pull script that takes the FETCH_HEAD trace and turns it into an argument to git merge (or to git rebase when doing a rebasing pull):
merge_head=$(sed -e '/ not-for-merge /d' \
-e 's/ .*//' "$GIT_DIR"/FETCH_HEAD | \
tr '\012' ' ')
(By the way, note that those are tabs before and after not-for-merge and in the second -e argument to sed. Cut-and-paste generally turns tabs into spaces, and did here.)
I imagine the sed part is working correctly and the failure occurs with the tr invocation. In fact, it looks like whatever tr is being used, is simply spitting out the 012 string (for a total of 60 characters, or 20 instances of the three-character group—not sure how that happens given that the sed output is, or should be, a 40-character SHA-1).
If your shell is sh or bash, see what:
$ type tr
prints. If you use a csh variant, which tr will show you what it will run. (I'm not sure off-hand what to use for dash and zsh.) If you get something other than /usr/bin/tr, that may explain the problem. (If you do get /usr/bin/tr see what type sed or which sed says: these should be /usr/bin/sed.)

Resources