7

I want a command that would let me do something like:

git manual-add some_file.txt 10..20

Which would be the equivalent of:

git add -p some_file.txt

and saying y to only the hunk containing lines 10 through 20.

Is there an internal git command that would let me do this? Is there any way it could be done?

Jonah
  • 15,806
  • 22
  • 87
  • 161

2 Answers2

5

You can take advantage of the git add -e command and pass to it as an editor a script (in the example below, it is named filterpatch) that will edit the patch up to your requirements (see the "EDITING PATCHES" section in the documentation of git add):

EDITOR="filterpatch 10..20" git add -e some_file.txt

For convenience you can add a git alias as follows:

git config alias.manual-add '!EDITOR="filterpatch $2" git add -e $1; :'

An example of a silly filterpatch script that prepends a foo prefix to all added lines in the specified range of the patch:

#!/bin/bash -x

sed -i "$1 s/^+\([^+]\)/+foo \1/" "$2"

Usage example:

git manual-add some_file.txt 13,16

So the remaining part is about implementing the filterpatch script properly - it must parse the diff and unselect hunks that don't belong to the target range.

Leon
  • 31,443
  • 4
  • 72
  • 97
  • Can you please give me some explanations on how can I configure that *filterpatch* and *bash* – Rawand Apr 10 '22 at 15:18
  • @Rawand Such an explanation can hardly be given in comment format. Why don't you ask your question as a question? – Leon Apr 11 '22 at 07:54
2

Add alias to your config:

[alias]
        addp = "!sh -c 'git commit -m temp \"$1\" && git log -1 -u -L \"$2\":\"$1\" > ptc.patch && git reset HEAD^ && git apply --cached ptc.patch ' -"

Now you could:

git addp a.txt 3,4

This sequence will:

  1. Do a temporary commit with file $1 only.
  2. Write patch ptc.patch for lines $2 in file $1 using solution from this question - Git: discover which commits ever touched a range of lines.
  3. Mixed reset temporary commit.
  4. Apply patch to index only, since working directory already contains changes.
Community
  • 1
  • 1
Mykhailo
  • 1,134
  • 2
  • 17
  • 25
  • OP did not specify the hunk selection criteria precisely. In my understanding a hunk may be bigger than the stated range of lines (the hunk just needs to fully include those lines). Your solution will generate a partial patch when the specified line range is a subset of the hunk. – Leon Oct 21 '16 at 10:49
  • 1
    Besides, I think that your `git log` command misses a `-1` option. – Leon Oct 21 '16 at 10:52
  • @Leon, "Your solution will generate a partial patch when the specified line range is a subset of the hunk." - well, this is exactly my understanding, that OP wants to specify range of lines which comprise a hunk precisely. – Mykhailo Oct 21 '16 at 11:06
  • OP's explanation through `git add -p` works differently - if the hunk containing lines 10-20 is actually 100 lines long, the entire hunk will be selected. – Leon Oct 21 '16 at 11:39
  • Why would he want to specify a range of lines then, if single line number (e.g. 10) yielded exactly same hunk as for (10..20)? Anyway, let's wait for OP reply. – Mykhailo Oct 21 '16 at 11:53
  • I want specific lines, yes. in the patch example, youd have to keep splitting or edit if the hunk contained extra lines – Jonah Oct 21 '16 at 14:44
  • 1
    I suggest to replace `git add \"$1\" && git commit -m temp` with `git commit -m temp \"$1\"` so that other possibly staged changes are not included in the throwaway commit. – Leon Oct 21 '16 at 17:37
  • @Leon thank you, I haven't really known it is possible to commit a single file in one command. I updated the answer. – Mykhailo Oct 21 '16 at 18:58