Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use git to stage only one line in a file for commit, all from a script?

Tags:

git

scripting

I'm writing a simple pre-commit git hook that updates the year in copyright headers for files that are staged for commit.

After modifying the line with the copyright, I would like the hook to stage that line so that it is part of the commit. It can't just git add the whole file, because there may be other pre-existing changes in there that shouldn't be staged.

I don't see any options in the git add manual the let you stage specific lines.

I figure I could git stash save --keep-index, apply my change, git add the file, and then git stash pop, but that seems rather crude. Any better approaches?

like image 841
Sandy Avatar asked Jan 06 '11 18:01

Sandy


People also ask

How do I commit a single line in git?

For those who use Git Extensions: In the Commit window, select the file you want to partially commit, then select the text you want to commit in the right pane, then right-click on the selection and choose 'Stage selected lines' from the context menu.

What command is used to stage only some changes in a file?

git add -p is basically "git add partial (or patch)" Patch mode allows you to stage parts of a changed file, instead of the entire file. This allows you to make concise, well-crafted commits that make for an easier to read history.


3 Answers

Here's another possible solution:

  1. Before running your copyright-modification script, do a git status to get a list of the files it's about to commit. Save the list of files. Do a commit.

  2. Then, stash the rest of the (unrelated) changes, and apply your script to the list of files saved above. Use git commit --amend to change the previous commit.

  3. Finally, pop the stash to restore your index. Resolve conflicts if required.

I don't know of a way to tell git add to add only specific lines. How would you describe the lines to add? By line number? It seems that might be possible in a narrow set of circumstances, and not generally useful.

like image 131
Greg Hewgill Avatar answered Sep 29 '22 19:09

Greg Hewgill


You could patch the staged version of the file in a separate step, e.g.

blobid=$(git show :"$filepath" | copyright-filter | git hash-object -w --stdin)

if $? -eq 0; then

    git update-index --cacheinfo 100644 "$blobid" "$filepath" &&
    copyright-filter "$filepath"

fi

I've shamelessly assumed that your script is called copyright-filter and works as a filter or in place, depending on its arguments.

like image 23
CB Bailey Avatar answered Sep 29 '22 20:09

CB Bailey


Looking at the source to git add --interactive it looks like the way it modifies the index is with git apply --cached. Assuming your copyright is the first hunk of any diff, make the change and use:

git diff -- file | 
awk 'second && /^@@/ {exit} /^@@/ {second=1} {print}' |
git apply --cached

Where that second line (the awk script grabs only the first diff hunk). You could also construct the diff output by hand or use other rules to select the hunk.

like image 34
Ben Jackson Avatar answered Sep 29 '22 20:09

Ben Jackson