tekin.co.uk

Different ways to use “--patch” in Git

I’ve written previously about using --patch to interactively stage changes. But did you know that you can use --patch (aka -p) to similar effect with other Git commands? Let’s take a look…

Selectively stashing changes

git stash is great for temporarily stashing changes that you want to apply later, and handily it also supports selectively stashing changes with the --patch flag:

  $ git stash --patch

  diff --git a/spec/sidekiq/upload_job_spec.rb b/spec/sidekiq/upload_job_spec.rb
  index b5f1d04e..e3b6227d 100644
  --- a/spec/sidekiq/upload_job_spec.rb
  +++ b/spec/sidekiq/upload_job_spec.rb
  @@ -7,6 +7,7 @@ describe '#perform' do
       context 'the application has already been uploaded' do
         let(:membership_application) { create :forsa, :uploaded }

  +      # Here is change one of two
         it 'lets us know via Appsignal' do
           allow(Appsignal).to receive(:report_error)
           job.perform(membership_application.id)

  (1/2) Stash this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?

Bonus git stash tip: you can also selectively stash entire files using -- to disambiguate the command from the paths you want stashing:

  $ git stash -- path/to/file.rb

Selective discarding changes from your current work tree

You can use the git restore command to discard local changes and restore files to their last committed state. It can also be called with the --patch flag to interactively select specific hunks to discard:

  $ git restore --patch

  diff --git a/spec/sidekiq/upload_job_spec.rb b/spec/sidekiq/upload_job_spec.rb
  index b5f1d04e..e3b6227d 100644
  --- a/spec/sidekiq/upload_job_spec.rb
  +++ b/spec/sidekiq/upload_job_spec.rb
  @@ -7,6 +7,7 @@ describe '#perform' do
       context 'the application has already been uploaded' do
         let(:membership_application) { create :forsa, :uploaded }

  +      # Here is change one of two
         it 'lets us know via Appsignal' do
           allow(Appsignal).to receive(:report_error)
           job.perform(membership_application.id)

  (1/2) Discard this hunk from worktree [y,n,q,a,d,j,J,g,/,e,p,?]?

Note the different phrasing of the prompt on the last line: Here we are choosing the changes we want to discard. Be careful, this is a destructive change, and because these are unstaged and uncommitted changes git won’t be able to help you recover the changes once they’ve been discarded!

Selectively restoring changes from another branch or commit

The git restore command also lets you restore changes from another branch or commit by specifying the --source= flag. Combine this with the --patch flag and you can interactively choose the specific changes to restore to your current work tree:

  $ git restore --source=branch-name --patch

  diff --git b/app/models/flow_definitions/neu.rb a/app/models/flow_definitions/neu.rb
  index cdabfe7a..28308244 100644
  --- b/app/models/flow_definitions/neu.rb
  +++ a/app/models/flow_definitions/neu.rb
  @@ -42,10 +42,9 @@
     step 'qualified-year' do
       radio :qualified_year do
  -      option :'2024'
  -      option :'2023'
  -      option :'2022'
  -      option :'2021'
  +      MembershipApplication::Neu::QUALIFYING_YEARS.each do |year|
  +        option :"#{year}"
  +      end

         divider

  (1/2) Apply this hunk to index and worktree [y,n,q,a,d,j,J,g,/,e,p,?]?

I wil sometimes use this when I have a spike branch that I want to pull a subset of changes from to be tidied up and committed in my current branch.

A quick note on git restore vs checkout

You may have noticed that the last two behaviours for discarding/restoring changes are also possible using git checkout. git restore was introduced to Git in version 2.23 (alongside git switch) in an effort to reduce the overloaded (and somewhat confusing) responsibilities placed on git checkout. Whilst it doesn’t look these features will be removed from git checkout any time soon, the more focused commands are easier to understand (and teach!), and arguably easier to use, at least assuming you don’t have years of muscle memory to overcome.

Thanks to Tom and Étienne for the clarifying comments.

Want more juicy Git tips like this straight to your inbox?

You'll get an email whenever I have a fresh insight or tip to share. Zero spam, and you can unsubscribe whenever you like with a single click.

More articles on Git

Authored by Published by