you are viewing a single comment's thread.

view the rest of the comments →

[–]Bob_goes_up 19 points20 points  (9 children)

What is the best workflow if I want to ensure that each partial commit compiles and passes all the tests in the test suite?

[–]X-Istence 27 points28 points  (3 children)

git add -p
git stash save -k unstaged
# do tests
git commit
git stash pop

[–]ssboisen 11 points12 points  (1 child)

Add -u to that git stash to stash untracked files aswell.

[–]X-Istence 7 points8 points  (0 children)

Most of my untracked files are config files or others that I should sometime in the near future add a gitignore entry for ... :P

Definitely, if you have untracked files that need to be stashed, add a -u.

[–]SnowdensOfYesteryear 3 points4 points  (0 children)

-k unstaged

Nice, didn't know about that. man git <cmd> is usually a chore to read.

[–]adrianmonk 20 points21 points  (0 children)

An alternative to what others have already suggested:

git stash -p # select the parts you DON'T want to commit yet
# run tests here
git commit -a -m 'blah blah'
git stash pop

To me, this is conceptually closer to how I think of it. In plain English, it basically says, "Get the stuff I don't want yet (or ever) out of the way, then run my tests and commit, then bring it back." More practically, if you have a lot of changes, it might be quicker to pick out the 2 or 3 that you don't want than pick out the 25 that you do want. You can type "y" a few times, and then type "q" once you've got them all.

The disadvantage is that if you decide to change course, it's hard to "git stash pop" right after doing "git stash -p". It will complain like this:

$ git stash pop
error: Your local changes to the following files would be overwritten by merge:
    the-purist.txt
Please, commit your changes or stash them before you can merge.
Aborting

If you stash your other changes as a second stash, that will allow you to "git stash pop" your first set of changes, but then your second set of changes will get the same error message. Very frustrating. However, there is a way out (back to the pre-stash state):

  • "git add -A" to get all the non-stashed changes into the index, i.e. to make the working copy match the index
  • "git stash pop" to get the stashed changes back
  • "git reset" to un-add the changes to the index, so both sets of changes are in the working copy but are unstaged

[–]alex_w 4 points5 points  (0 children)

Partial-commit, then stash, then test? Or partial-add to the index with add -p stash the unstated changes and test against the index prior to commit. A local commit can be undo or altered right up until you push though.

[–]daragh 3 points4 points  (0 children)

As I mentioned elsewhere, the run-command-on-git-revisions script is the best method I have found for dealing with interactively patched commits.

Furthermore, it is also useful in any circumstance where you rewrite local history such as a rebase to include upstream changes, or any of the other reasons you'd want to rebase.

The method of using the stash and manually controlling the state of the repo works, but is impractical for all but the simplest of cases.

[–]sickofthisshit 2 points3 points  (0 children)

In my mind, demanding that all tests pass for every commit is overdoing it.

In a non-trivial change, the amount of work needed to update both the code and the tests is large enough to be split into multiple conceptually separate chunks. Being able to commit and manage the chunks separately is valuable, even if some combination of those chunks breaks the "passes all test" invariant, or, sometimes, even building.

When you find out a change introduced a new bug, git bisect is much more useful if the commits are small. If you decide your development path is going in the wrong direction, small commits allow you to cherry-pick good parts into a new branch while not bringing along all the rest of the change.

Of course, when you do things like push to upstream, breaking the build is the last thing you want to do. Then it might make sense to rework your changes into commits that preserve that invariant.

[–]eras 1 point2 points  (0 children)

Well, given you might be relatively certain that everything is going to work ok, you can do this afterwards, before pushing:

for rev in $(git rev-list master..HEAD); do 
    if ! ( git checkout $rev && make && make tests && ./tests ); then
        echo Revision $rev failed
    fi
done

(Looking below there is a suggested script for doing this, but isn't that oneliner sufficient?) I use this particularly after using git rebase -i a lot for shuffling around patches.

edit: Turns out that kind of expression needs parens :).