tags:

views:

61

answers:

3

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.

A: 

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.

Bartosz
That is my current practice, but when there is to much "noise" in the diff I want to test/commit I get confused (due to lack of context lines, etc).Really want to be able to drive this from my editor in such situations.
RobM
+4  A: 

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".

araqnid
A: 

The closest pre-built tool that does something like this is git gui citool. It does not work directly in your editor (not all editors have useful diff views and I suspect that most people probably do not care to remember exactly which lines they have changed since the last commit, so a diff view is very useful), but seems like it is close to what you want.

git gui citool lets you review staged and unstaged changes in views that are equivalent to git diff --cached and git diff, respectively.

While reviewing unstaged changes, the context menu (right click) has options to stage the selected lines (or the clicked line if there is no selection) and an option to stage a whole hunk. Likewise when reviewing staged changes, the context menu has options to unstage lines or a hunk.

To get more context, you can use the “Show More Context” menu item (or “Show Less Context” if you want to shrink the hunks).

Once you have your new content staged, you can also compose your commit message and make the commit from inside the GUI.

I imagine that some people use git gui in a workflow like this:

  1. Use the CLI to add new tracked files and remove old files (the various options to git add).
  2. Edit tracked files in your favorite editor/environment.
  3. In git gui, select “Rescan”, review the changes, stage/unstage some, and eventually commit them.
  4. [repeat]
Chris Johnsen