Check if git branch contains commit with given subject - bash

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.

Related

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

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

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/,,'

How to redirect part of output of git subtree push

git subtree push ... produces a lot of output. Although it is ok, using them on gitlab-ci is a nightmare.
I find it awful because the outputs are displayed line by line:
git push using: git#gitlab.xxx.com:micro/service.git feature/ci
1/207 (0)
2/207 (1)
3/207 (2)
4/207 (3)
5/207 (4)
6/207 (5)
7/207 (6)
8/207 (7)
...
Everything up-to-date
How can I hide 1/207 (0) without hiding the rest?
I tried
with the -q parameter, doesn't work
to pipe the output to a regex to "hide" the annoying lines:
$ git subtree push ... | while IFS= read -r line; do if [[ "$line" =~ [0-9]+/[0-9]+ ]];then : ; else echo "$line"; fi done doesn't work
?
You can try a grep to find and discard the line you don't want. But this is very specific to that query. Are you sure you don't want to hide some line class instead of just a string?
For your specific question, try
git subtree push ... | grep -v "1/207 (0)"
UPDATE:
Given the clarification, just adapt the search term and use regular expressions:
git subtree push ... | grep -v -e '[0-9]+/[0-9]+'

How can I use Git to identify function changes across different revisions of a repository?

I have a repository with a bunch of C files. Given the SHA hashes of two commits,
<commit-sha-1> and <commit-sha-2>,
I'd like to write a script (probably bash/ruby/python) that detects which functions in the C files in the repository have changed across these two commits.
I'm currently looking at the documentation for git log, git commit and git diff. If anyone has done something similar before, could you give me some pointers about where to start or how to proceed.
That doesn't look too good but you could combine git with your
favorite tagging system such as GNU global to achieve that. For
example:
#!/usr/bin/env sh
global -f main.c | awk '{print $NF}' | cut -d '(' -f1 | while read i
do
if [ $(git log -L:"$i":main.c HEAD^..HEAD | wc -l) -gt 0 ]
then
printf "%s() changed\n" "$i"
else
printf "%s() did not change\n" "$i"
fi
done
First, you need to create a database of functions in your project:
$ gtags .
Then run the above script to find functions in main.c that were
modified since the last commit. The script could of course be more
flexible, for example it could handle all *.c files changed between 2 commits as reported by git diff --stats.
Inside the script we use -L option of git log:
-L <start>,<end>:<file>, -L :<funcname>:<file>
Trace the evolution of the line range given by
"<start>,<end>" (or the function name regex <funcname>)
within the <file>. You may not give any pathspec
limiters. This is currently limited to a walk starting from
a single revision, i.e., you may only give zero or one
positive revision arguments. You can specify this option
more than once.
See this question.
Bash script:
#!/usr/bin/env bash
git diff | \
grep -E '^(##)' | \
grep '(' | \
sed 's/##.*##//' | \
sed 's/(.*//' | \
sed 's/\*//' | \
awk '{print $NF}' | \
uniq
Explanation:
1: Get diff
2: Get only lines with hunk headers; if the 'optional section heading' of a hunk header exists, it will be the function definition of a modified function
3: Pick only hunk headers containing open parentheses, as they will contain function definitions
4: Get rid of '## [old-file-range] [new-file-range] ##' sections in the lines
5: Get rid of everything after opening parentheses
6: Get rid of '*' from pointers
7: [See 'awk']: Print the last field (i.e: column) of the records (i.e: lines).
8: Get rid of duplicate names.

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