This post was originally published on maksimivanov.com
This post has video version, make sure to check it out on youtube
You know that git add
adds files to index. But did do you know that it can add specific lines of files? Or even add files, ignoring their contents? Let's check this out!
First let's get familiar with git add --patch
command or it's shorthand git add -p
. What does it do?
Let's imagine that you've been working on some task. During the process you've got carried away and introduced some changes not related to your current task.
Now it's time to commit the changes, but some of them are irrelevant. It would be wrong to put everything in that commit.
This is where git add --patch
comes into play.
See The Example
Imagine we had a file poem.txt with two unfinished couplets:
Roses are red
Violets are blue
Roses are red
Violets are blue
After a full day of dedicated hard work we've completed both:
➜ git_add_patch (master) ✗ git diff
diff --git a/poem.txt b/poem.txt
index 39f13e6..f4b70ab 100644
-------- a/poem.txt
+++ b/poem.txt
@@ -1,5 +1,9 @@
Roses are red
Violets are blue
+Sugar is sweet
+And so are you
Roses are red
Violets are blue
+Onions stink
+And so do you.
But now we are a bit concerned about the second couplet and want to commit only the first part.
➜ git_add_patch (master) ✗ git add -p
diff --git a/poem.txt b/poem.txt
index 39f13e6..f4b70ab 100644
-------- a/poem.txt
+++ b/poem.txt
@@ -1,5 +1,9 @@
Roses are red
Violets are blue
+Sugar is sweet
+And so are you
Roses are red
Violets are blue
+Onions stink.
+And so do you.
Stage this hunk [y,n,q,a,d,/,s,e,?]?
See the last line, Stage this hunk [y,n,q,a,d,/,s,e,?]? Here it gives you some options where the most important are:
- y - stage this hunk
- n - do not stage this hunk
- q - quit; do not stage this hunk or any of the remaining ones
- a - stage this hunk and all later hunks in the file
- d - do not stage this hunk or any of the later hunks in the file
- s - split the current hunk into smaller hunks
- e - manually edit the current hunk
There are some more opions, you can see them by chosing the ? option.
Here we want only the first part about the sugar and sweetness, so we choose the option s.
Stage this hunk [y,n,q,a,d,/,s,e,?]? s
Split into 2 hunks.
@@ -1,5 +1,7 @@
Roses are red
Violets are blue
+Sugar is sweet
+And so are you
Roses are red
Violets are blue
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
We chose to stage that hunk, and now it asks if we want to stage the second one:
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
@@ -3,3 +5,5 @@
Roses are red
Violets are blue
+Onions stink
+And so do you.
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n
And we don't want to stage this one, so we choose n.
Great, let's see what we have now.
➜ git_add_patch (master) ✗ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: poem.txt
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: poem.txt
Cool beans, time to commit this thing.
➜ git_add_patch (master) ✗ git commit -m "Complete the first couplet"
Git splits hunks by the empty lines, but what if you really need to stage only specific lines.
Behold The Power Of E
Imagine you have a diff
like this:
➜ git_add_patch (master) ✗ git diff
diff --git a/poem.txt b/poem.txt
index 9737e9f..a2baecd 100644
-------- a/poem.txt
+++ b/poem.txt
@@ -5,3 +5,6 @@ And so are you
Roses are red
Violets are blue
+Flowers smell good
+Onions stink
+And so do you.
And you are really concerned about that onion part, but you don't want to remove it. So you want to stage only the "Flowers smell good" and "And so do you" lines.
Now the s option won't help us, because those lines aren't separated by empty lines, and git considers them as one hunk.
e option to the rescue.
➜ git_add_patch (master) ✗ git add -p
diff --git a/poem.txt b/poem.txt
index 9737e9f..a2baecd 100644
-------- a/poem.txt
+++ b/poem.txt
@@ -5,3 +5,6 @@ And so are you
Roses are red
Violets are blue
+Flowers smell good.
+Onions stink.
+And so do you.
Stage this hunk [y,n,q,a,d,/,e,?]? e
It will open the default text editor and you'll be able to manually edit that hunk.
Just remove the part about onions and here you go:
➜ git_add_patch (master) ✗ git add --staged
diff --git a/poem.txt b/poem.txt
index 9737e9f..a2baecd 100644
-------- a/poem.txt
+++ b/poem.txt
@@ -5,3 +5,6 @@ And so are you
Roses are red
Violets are blue
+Flowers smell good
+And so do you.
Nice, now you can decide what to do about those stinky onions later.
But what if you need to add some specific lines from the new file?
Use The Intent To Add
Use git add --intent-to-add
or git add -N
to add specific file, but not its contents.
Imagine that we had our poem written from scratch.
Now git diff
shows nothing, and if we'll use git add -p
it will say No changes.
No problem, let's tell git that the file exists.
➜ git_add_patch (master) ✗ git add -N poem.txt
Now we can use git add -p
and then edit the hunk to remove the lines about onions.
Top comments (2)
Great!
I was recently asked by one of my teammates if there is any possibility to add specific lines to our git repository. I wasn't aware of
patch
command. Whole topic was well explained here.Your article was an eye-opener for me. Thanks for sharing.
Yup, git patch, especially edit option is super powerful. You can bend your repository however you want :)