grep SHA-1 hash with possible prefix - regex

I want to grep from git submodule status <PATH> the SHA-1 commit hash of my submodule. According to git submodule --help:
status [--cached] [--recursive] [--] [...]
Show the status of the submodules. This will print the SHA-1 of the currently checked out commit for each
submodule, along with the submodule path and the output of git describe for the SHA-1. Each SHA-1 will
possibly be prefixed with - if the submodule is not initialized, + if the currently checked out submodule
commit does not match the SHA-1 found in the index of the containing repository and U if the submodule has
merge conflicts.
So the result looks something like this:
f1eeb6aa2a5009b5ef68b5b754499dcb3ab575d1 my-submodule (remotes/origin/HEAD)
The description mentions that each hash will be possibly prefixed with a + or a -. I'm not interested in the signs, and therefore, for whatever result it gives me, I want to get the 40 character hash without the prefix.
Example:
input: +f1eeb6aa2a5009b5ef68b5b754499dcb3ab575d1 my-submodule (remotes/origin/HEAD)
desired output: f1eeb6aa2a5009b5ef68b5b754499dcb3ab575d1
input: f1eeb6aa2a5009b5ef68b5b754499dcb3ab575d1 my-submodule (remotes/origin/HEAD)
desired output: f1eeb6aa2a5009b5ef68b5b754499dcb3ab575d1
I've tried something like awk '{print $1;}' | grep -e '[0-9a-f]\{40\}' but it doesn't seem to remove the prefix. Any elegant solutions?

I want to grep from git submodule status <PATH> the SHA-1 commit hash of my submodule.
Why go through the gyrations? Just ask for what you want directly:
git rev-parse :<PATH>

Add option -o to your grep command.
-o: Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line

You can use the output of git ls-tree:
Instead of:
D:\git\git>git ls-tree master -- sha1collisiondetection
160000 commit 855827c583bc30645ba427885caa40c5b81764d2 sha1collisiondetection
You would get:
git ls-tree master -- sha1collisiondetection|awk "{print $3}"
855827c583bc30645ba427885caa40c5b81764d2

Related

Git commit uses regular expressions or globs?

Is it possible to use regular expressions in, e.g., git commit ".*my_file.*" ?
I tried, and it seems to only interpret these as globs. I also tired a regex flag:
git commit -regex ".*my_file.*"`
Throws an error.
Does anyone know of a way to combine regular expressions with Git commands?
The best way I can think of to do this is using the find command. For example, if you want only python files:
find -type f -regex ".*\.py$" -exec git commit {} -m "committing only and all python files" \;
Can anyone else think of something less unwieldy?
Not with Git itself. Git just receives a list of files passed from the shell, so it would be up to your shell to do regular expression matching for files. I do not think bash can do this, but other shells may be able to.
As mentioned by mipadi, arguments can be generated by the shell to produce the input for git.
For example, ls can be used in combination for this. Let's say I've got a git init-ed directory with the following files:
.git/
my_new_project_file.py
my_older_project_file.py
some_other_file.py
And I want to add everything but some_other_file.py. To do this:
ls my*project*.py | xargs git add
Checking my git status will show that both my_new_project_file.py and my_older_project_file.py have been staged, while some_other_file.py has been ignored.
n.b. ls doesn't support regex; just globbing.
As pointed out by Candic3, git supports globs:
git add *my_file*

Search filenames with regex

Is there any way to do something like git log <path>, but instead of path using a regex? I want to search commits containing files, whose filenames match a given pattern...
... and while we're at it: Is there also a way to do a git status / git diff only for filenames matching a given pattern?
Thanks in advance!
EDIT: I would be terrific if any way to do it, would also work for Git v1.7.1.
As far as a pure git solution goes and I'm aware of the only option to match specific file patterns is to use a glob.
git log -- '*.json'
Will give you all files which contain changes to a json file. The same can be done for git status.
On the other hand it's quite easy to search for regular expressions in the diff or the commit message. git log offers a --grep option to search for matches in the commit message and a -S option to search for strings.
Take a look at this question for further details.
For a simple pattern you could try, for example:
find . -name "*.c" | xargs git log
For a full-blown regex you can use:
find . | grep "REGEX" | xargs git log
If you need previously deleted files to be included in the output, you can use
git log --all --pretty=format: --name-only --diff-filter=A | sort -u | grep "REGEX" | xargs git log --
The first part of the above command, which finds all files that were ever in git, was lifted from an answser to this other question.
Thanks to your answers (especially Greg and Michael) I developed a way myself. (I hope this proves viable):
git log --name-only --pretty="format:"|sort -u|egrep '<REGEX>'|xargs git log --
Can you do something like:
git log | grep [string_to_look_for]

Git add lines to index by grep/regex

I have a giant patch that I would like to break into multiple logical git commits. A large number of the changes are simply changing variable names or function calls, such that they could easily be located with a grep. If I could add to the index any changes that match a regex then clean up in git gui, it would save me a lot of manual work. Is there a good way to update the index on a line-by-line basis using a regex within git or from some output of grep (e.g. line numbers)?
I found a similar question, but I'm not sure how to build the temporary file from a regex-type search.
patchutils has a command grepdiff that can be use to achieve this.
# check that the regex search correctly matches the changes you want.
git diff -U0 | grepdiff 'regex search' --output-matching=hunk
# then apply the changes to the index
git diff -U0 | grepdiff 'regex search' --output-matching=hunk | git apply --cached --unidiff-zero
I use -U0 on the diff to avoid getting unrelated changes. You might want to adjust this value to suite your situation.
More simply, you can use git add -p and utilize the / option to search through your diff for the patches to add. Its not totally automated, but its easier than other alternatives I've found.
You could first run:
git status | \grep "your_pattern"
If the output is as intended, then add the files to the index:
git add $(git status | \grep "your_pattern")
I'm working now on Git-Bash over Windows, and I got a similar problem: I didn't need add some few files from the "not staged for commit file list":
$ git status
On branch Bug_#292400_buggy
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: the/path/to/the/file333.NO
modified: the/path/to/the/file334.NO
modified: the/path/to/the/file1.ok
modified: the/path/to/the/file2.ok
modified: the/path/to/the/file3.ok
modified: the/path/to/the/file4.ok
....................................
modified: the/path/to/the/file666.ok
First, I checked if the file selection was what I was looking for:
$ git status | grep ok
modified: the/path/to/the/file1.ok
modified: the/path/to/the/file2.ok
modified: the/path/to/the/file3.ok
modified: the/path/to/the/file4.ok
....................................
modified: the/path/to/the/file666.ok
I tried with one idea as descibed in this dorum in order to add the same file list with git, as:
$ git add $(git status | \grep "your_pattern")
But it doesn't work for me (Remember: Git-Bash over Windows10)
At least, I tried in a straight way, and it worked fine:
$ git add *ok
$ git status
On branch Bug_#292400_buggy
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: the/path/to/the/file1.ok
modified: the/path/to/the/file2.ok
modified: the/path/to/the/file3.ok
modified: the/path/to/the/file4.ok
....................................
modified: the/path/to/the/file666.ok
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: the/path/to/the/file333.NO
modified: the/path/to/the/file334.NO
Ready to commit, so.
I found an answer.
There are some steps.
git status --porcelain gives git status easy-to-parse format for scripts like grep.
sed s/^...// slices from 3rd characters to end lines
xargs serves you to run script line by line
In my case, using django that need to ignore migrations, my script is git status --porcelain | sed s/^...// | grep -v migrations | xargs git add.
You can customize grep options to fit your needs
documents
xargs
git-status
sed
xargs is what your looking for. Try this:
grep -irl 'regex_term_to_find' * | xargs -I FILE git add FILE
Up to the pipe | is your standard grep command for searching all files *. Options are:
i - case insensitive
r - recursive through directories
l - list names of files only
In the xargs part of the statement FILE is the name of the variable to use for each argument/match passed by the grep command. Then enter the desired command using the variable where appropriate.

Using two asterisks to add a file in git

I want to add a file which has a unique file name but a long preceding path (e.g. a/b/c/d/filename.java). Normally I would add this to my repository by doing
git add *filename.java.
However I have also done this before:
git add a/b/c/d/filename*
So I tried to combine the two:
git add *filename*
but this does something weird. It adds every untracked file. I can see possible reasons for failure but they all should occur in one of the previous two commands so I don't know why this is happening.
My question isn't so much about how to add a file to a git repository with just its file name (although that would be useful).
My question is what is my misunderstanding of the * operation which makes me think the above should work.
Info:
I am using Git Bash for Windows, which is based on minGW.
You're looking at globs
(not regular expressions, which are a different pattern-matching language), and they're expanded by your shell, not by git.
If you want to see how they're going to match, just pass the same glob to another command, eg.
$ ls -d *filename.java
vs
$ ls -d *filename*
(I've just added the -d so ls doesn't show the contents of any directories that match)
Since you're using git bash, and it's possible that glob expansion behaves differently from a regular shell, try
$ git add --dry-run --verbose -- *filename*
for example: this should show you how it really expands the glob and what effect that has.
Note the -- ... if you're using globs that might match a filename with a leading -, it's important to make sure git knows it's a filename and not an option.
Unfortunately, this will only show you the files which both match the glob, and have some difference between the index and working copy.
Answer from author:
The dry run helped a lot, here is what I found:
I was forgetting about the bin folder which I haven't added, so when I performed the dry run I realised it was finding two matches: filename.java and filename.class. When I changed the glob to *filename.j* it worked.
My next step was to remove the .class and try the command again: it worked! It is still unexplained why git bash added everything when it found two matches... since the dry run behaves differently from the actual run I think there must be a bug, but I think that discussion is to be held elsewhere (unless somebody thinks it isn't a bug).
You could try with git add ./**/*.java
Note: I tested with zsh, it should also work for bash as well.

Using egrep regex to capture part of line

I'm trying to commit git patches via a bash script. This is not a git question! Here is what I want to do, I have a list of files in a directory. I want read those files one by one extract a particular line out of it and then commit.
Here is what I got so far;
patches=/{location}/*.patch
for patch in $patches
do
echo "Processing $patch file..."
git apply $patch
git add --all
git commit -m | egrep -o "(^Subject: \[PATCH [0-9]\/[0-9]\].)(.*)$" $f
echo "Committed $patch file..."
done
Couldn't get the egrep regex working to pass on the proper commit message.
Here is an example line from a patch file;
.....
Subject: [PATCH 1/3] XSR-2756 Including ldap credentials in property file.
......
I just want to capture "XSR-2756 Including ldap credentials in property file." and use as a commit description to git.
Assuming you have GNU grep, use a Perl look-behind:
git commit -m "$(grep -Po '(?<=Subject: \[PATCH \d/\d\].).*') $patch"
Don't use the -o to egrep in this case (since you're matching a bunch of stuff you don't want printed). Instead, just match the whole line and pipe it to 'cut' (or sed, or something else that will trim a prefix from a line.)
Also, you're piping the output of git commit into egrep, not providing the output of egrep as a command line option to git commit... I think you want something like:
git commit -m "$(egrep '<your regexp here>' $f | cut -d] -f2-)"
I'd use sed for this
git commit -m | sed -r -n 's#^Subject: \[PATCH [0-9]/[0-9]\] ##p;'