Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git: Add part of (changed) file to index *by line numbers*

Tags:

git

Is there a git command to add differences within a range of line-numbers to the index?

I want to be able to select lines in my editor and run a macro to add any changes in the selection to the index.

like image 926
RobM Avatar asked May 06 '10 17:05

RobM


2 Answers

If you can persuade your editor to write a version of the file that you want to be staged, you can use the plumbing-level git commands to add it to the index under the right name. You need to bypass "git add" which will always associate path X in the working tree with path X in the index (as far as I know).

Once you have the content you want to stage written to some temporary file $tempfile, run git hash-object -w $tempfile - this will write the object to .git/objects and output the blob id. Then feed this blob id to the index using git update-index --cacheinfo 100644 $blobid $path to associate the path $path with that object.

Here's an example that stages a change to a script called "post_load" in my repo without overwriting the file itself (also demonstrating you can do without a temp file):

git update-index --cacheinfo 100755 $(perl -lne 'print unless (/^#/)' post_load \
                                      | git hash-object -w --stdin) post_load

You don't mention which editor you're planning to do this from, so it's hard to advise you on how to integrate this. As I mentioned, you need to somehow present git with the file as you want it to be staged (remember, git doesn't deal with storing changes). If you can write a macro to just save the file as "$file.tmp", then use something like the above to git update-index --cacheinfo $the_mode $(git hash-object -w $file.tmp) $file (obtaining $the_mode is left as an exercise :p), delete $file.tmp and revert the editor buffer back to $file that would do basically what you're asking for.

For example, the following script takes three arguments: M N path. It will update the index content for the file at "path" so that lines M through N (inclusive) are replaced with the content from stdin:

#!/bin/sh

start_line=$1
end_line=$2
path=$3

mode=$(git ls-files -s $path | awk '{print $1}')
blob_id=$(
    (
        head -n $(expr $start_line - 1) $path
        cat
        tail -n +$(expr $end_line + 1) $path
        ) | git hash-object -w --stdin
    )
exec git update-index --cacheinfo $mode $blob_id $path

for example echo "HELLO WORLD" | ./stage-part 8 10 post_load will replace the three lines from 8-10 with just "HELLO WORLD".

like image 169
araqnid Avatar answered Sep 19 '22 14:09

araqnid


The easiest way to do it currently is with git add in interactive mode:

git add -i path/to/file

It will launch simple UI where you can choose chunks you want to stage and let you edit any chunk to remove lines you don't want to commit.

like image 21
Bartosz Avatar answered Sep 21 '22 14:09

Bartosz