Git add lines to index by grep/regex - 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.

Related

Git: Commit Only Specified Files (Renamed, Deleted, Modified, New File, etc)

I've got an older project where I modified many files (more than a hundred). Removed several, renamed a whole bunch, deleted many, etc. There are probably 10 or more commits worth in this batch of changes. How do I write a commit command to select, for example, only those renamed files:
git commit "all renamed files" -m "Renamed files."
I'd like to try and avoid doing something like this:
git commit file1 file2 file3 -m "Renamed files."
...because there are too many. I could also commit a folders worth (git commit folder1 . . .) but, unfortunately, there are some files in the folders that aren't to be committed.
I've accepted the answer provided but had to modify the command a bit:
git status --porcelain | grep -E '^(.R|R.)' | \
cut -b4- | awk '{print $1}' | xargs git commit -m "Bulk renamed files."
The reason for this is because running git status --porcelain | grep -E '^(.R|R.)' | cut -b4-, for example, outputs (here's one line to demonstrate): source/__init__.py -> site/__init__.py. When I use xargs to run the commit command, it's not accepted, presumably because of the -> . . . part. So I use awk here to select the first chunk only and this works nicely. Thanks!
You can use git status --porcelain to select only the files of a certain type. For example, to select only renamed files, use something like the following:
$ git status --porcelain | grep -E '^(.R|R.)' | cut -b4- | \
xargs git commit -m 'Renamed files'
The letters that are output and their meanings can be seen by running git status --help.

grep SHA-1 hash with possible prefix

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

git clean exclude with regex

I'd like to find a regex-way of using git clean.
Without regex:
git clean -dfx --exclude=".idea/"
With regex (tried; not working):
git clean -dfx --exclude='(.*\/)*(\.idea\/.*)(.*)'
git clean -dfx --exclude="(.*\/)*(\.idea\/.*)(.*)"
git clean -dfx --exclude=r'(.*\/)*(\.idea\/.*)(.*)'
git clean -dfx --exclude=r"(.*\/)*(\.idea\/.*)(.*)"
How do you use git clean with regex?
git clean has no support for regular expressions.
A workaround would be something like this:
$ git clean -n | cut -f3 -d' ' | grep -v -E --color=never '<PATTERN>' | ifne git clean
Breakdown of things happening here:
git clean -n produces a list of files that would be removed if git clean would be executed (you can use flags like -d, -x or -X here too)
-n dry-run (do not actually do anything)
cut -f3 -d' ' cuts the third field from those matches (delimited by an whitespace)
-f3 third field
-d' ' use whitespace as the delimiter
grep -v -E --color=never '<PATTERN>'
-v invert the matches from grep
-E interpret PATTERN as an extended regular expression
color=never to prevent colored grep output to mess with the following commands (may be omitted)
'<PATTERN>' a regular expression
ifne git clean will pipe the file list (if there are files) to git clean
ifne a utility function from moreutils (installable via homebrew or other package managers)
git clean will take this list and clean the files (use -n first to make sure no files get removed that you did not expect)
That is the magic of small command line programs each doing a simple specific task

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]

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;'