Read more

Git: How to add changes matching a regular expression

Arne Hartherz
April 29, 2020Software engineer at makandra GmbH

When you have many changes, and you want to spread them across different commits, here is a way to stage all changes matching a given regular expression for a single commit.

Example

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

Consider the following git diff output.

diff --git a/file1.rb b/file1.rb
index 806ca88..36d536b 100644
--- a/file1.rb
+++ b/file1.rb
@@ -1,7 +1,5 @@
-# Here is a useless comment.
-# It will be removed.
 class File1
-  def foo
+  def bar
     # ...
   end
 end
diff --git a/file2.rb b/file2.rb
index 550e1c6..600f4e3 100644
--- a/file2.rb
+++ b/file2.rb
@@ -1,6 +1,5 @@
-# Here is another useless comment.
 class File2
-  def foo
+  def bar
     # ...
   end
 end

While you can easily stage such changes using git add -p, you can be much faster when there are many files with changes following the same pattern.

Using grepdiff

You need the grepdiff tool. On Ubuntu systems, this is usually preinstalled from the patchutils package. If not, sudo apt install patchutils. You can then use it to grep through the output of git diff.

First, grep your diff and play around with the regular expression until the output is what you want it to be.

git diff -U0 | grepdiff -E '# .+' --output-matching=hunk

Then, pipe it into git apply to stage.

git diff -U0 | grepdiff -E '# .+' --output-matching=hunk | git apply --cached --unidiff-zero

You can then commit your staged changes.

git commit -m "Remove useless comments"

Note the following.

  • git diff -U0 omits unchanged lines. You almost always want to grep only changed lines. git apply accepts this style when using the --unidiff-zero option.
  • The -E switch on grepdiff enables the POSIX Extended Regular Expression syntax that you are used from Ruby. If you prefer basic syntax (i.e. where you escape control characters like + as \+), omit that switch.
  • Just to clarify, here is the remaining diff after applying the above:
diff --git a/file1.rb b/file1.rb
index 49283bf..36d536b 100644
--- a/file1.rb
+++ b/file1.rb
@@ -1,5 +1,5 @@
 class File1
-  def foo
+  def bar
     # ...
   end
 end
diff --git a/file2.rb b/file2.rb
index 93e1660..600f4e3 100644
--- a/file2.rb
+++ b/file2.rb
@@ -1,5 +1,5 @@
 class File2
-  def foo
+  def bar
     # ...
   end
 end
Arne Hartherz
April 29, 2020Software engineer at makandra GmbH
Posted by Arne Hartherz to makandra dev (2020-04-29 19:31)