Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git: How to re-stage the staged files in a pre-commit hook

Tags:

I'm writting a git pre-commit hook.
The script could reformat some code, so it could modify the staged files.

How can I re-stage all files that are already staged ?

like image 639
tzi Avatar asked Nov 12 '14 11:11

tzi


People also ask

How do you bypass a pre-commit hook?

Use the --no-verify option to skip git commit hooks, e.g. git commit -m "commit message" --no-verify . When the --no-verify option is used, the pre-commit and commit-msg hooks are bypassed.

Does git commit commit all staged files?

You don't want to be forced to commit both files, just the one that's ready. That's where Git's add command comes in. We add files to a staging area, and then we commit what has been staged. Even the deletion of a file must be tracked in Git's history, so deleted files must also be staged and then committed.

How do I commit a staged file in git?

Once you've staged all of the files that you want to commit, you'll run the "git commit" command. I'll do that, and then show you a couple of options that you can pass to alter the way commits are performed.


2 Answers

Without the pre-commit hook context, you can get a list of the staged files with the following command:

git diff --name-only --cached 

So if you want to re-index the staged files, you can use:

git diff --name-only --cached | xargs -l git add 

In the pre-commit hook context, you should follow the advices of David Winterbottom and stash unstaged changes before anything else.

This technique allows you not to be worry about indexing, or alterate, a change that was not staged. So you don't have to stage all the staged files, but all the updated files:

# Stash unstaged changes git stash -q --keep-index  # Edit your project files here ...  # Stage updated files git add -u  # Re-apply original unstaged changes git stash pop -q 
like image 110
tzi Avatar answered Oct 31 '22 12:10

tzi


I liked @tzi's answer; however, in David Winterbottom's quoted article there is a edge case concern raised in the comments in which you will lose some commit history. Though, it's not as doom and gloom as the commenter makes it sound, and again is an edge case for people with problematic practices. It happens when

  1. You stage a file (version A)
  2. Edit the same file before committing (version B)
  3. Wished to commit the originally staged file (version A) and not the modified one (version B)

If your commit fails, or succeeds and pops the stash before a committing, you lose your originally staged file (v. A), as it was never commit and is overwritten (with v. B). Obviously not catastrophic, and you still have the latest edit (v. B), but it might hamper some people's workflows and (suboptimal) committing practices. To avoid this you just check the exit of your script and work some stashing tricks to revert to the original state (index has v. A and WD has v. B).

pre-commit

#!/bin/sh

... # other pre-commit tasks

## Stash unstaged changes, but keep the current index
### Modified files in WD should be those of INDEX (v. A), everything else HEAD
### Stashed was the WD of the original state (v. B)

git stash save -q --keep-index "current wd"

## script for editing project files
### This is editing your original staged files version (v. A), since this is your WD 
### (call changed files v. A')

./your_script.sh

## Check for exit errors of your_script.sh; on errors revert to original state 
## (index has v. A and WD has v. B)

RESULT=$?
if [ $RESULT -ne 0 ]; then
git stash save -q "original index"
git stash apply -q --index stash@{1}
git stash drop -q; git stash drop -q
fi
[ $RESULT -ne 0 ] && exit 1

## Stage your_script.sh modified files (v. A')

git add -u

You should also move the git stash pop to the post-commit hook, as this is what overwrite the staged file (v. A) with the modified file (v. B) prior to committing. In practice mostly likely your script doesn't fail, but even so your git stash pop in the pre-commit hook creates a merge conflict with your script modified files (v . A') and your unstaged modifications (v. B). This then prevents the file from being committed at all, but you do have your script modified originally staged file (v. A') and your unstaged post-staging modified file(v. B) (arguably not losing any significant history assuming your_script.sh only does stuff such as indenting so v. A and v. A' are pretty much the same).

Summary: If you use best practices and commit staged files before modifying them again, the original answer is easiest and great. If you have, in my opinion, bad habits of not doing so and wanting both versions (staged and modified) in your history, you need to be careful (an argument for why this is a bad practice)! In any case, this could be a possible safety net.

like image 44
Novice C Avatar answered Oct 31 '22 12:10

Novice C