I'm using spotless with an android kotlin project now. And I have spotless set up like this
apply plugin: 'com.diffplug.gradle.spotless'
spotless {
kotlin {
target "**/*.kt"
ktlint(versions.ktlint)
}
}
What I want to do is set up a git pre commit hook that apply gradlew spotlessCheck before the commit but only on the commited files. It's something like this eslint-pre-commit-check
I have search around the repo but can't figure out how to achieve this. Is there any way to do this? Thanks.
The spotless gradle plugin has fast up-to-date checking built-in, so performance should be very good even without limiting to changed files. If your objective is to enforce the check only on changed files, that is a good idea, but it is not supported at this time. EDIT: now supported via ratchetFrom.
(bad advice from me deleted here)
Understood. Following is my workaround now. Hope it helps someone else who want to achieve this too.
#!/bin/sh
# From gist at https://gist.github.com/chadmaughan/5889802
echo '[git hook] executing gradle spotlessCheck before commit'
# stash any unstaged changes
git stash -q --keep-index
# run the spotlessCheck with the gradle wrapper
./gradlew spotlessCheck --daemon
# store the last exit code in a variable
RESULT=$?
# unstash the unstashed changes
git stash pop -q
# return the './gradlew spotlessCheck' exit code
exit $RESULT
Our latest versions (4.2.1 as of writing) have a feature called ratchetFrom. It's not exactly the same as this, but very close, and should perform much better. I'm closing this issue, but I might be wrong. If ratchetFrom does not make this (excellent!) workaround obsolete for you, please let me know!
ratchetFrom is very useful, but it does not, in fact make this obsolete, since it does not differentiate between changes staged for the commit and changes not staged for the commit. In order for a pre-commit hook to be useful, it should preferably only consider staged changes. Otherwise running spotlessApply in a hook makes it possible to make commits which do not in fact contain the formatting fixes.
(Also, if there is a way to make spotlessApply return a non-zero value if it does in fact change any files, that would be extremely useful as well. Now I'm running spotlessCheck first to find out if the hook should fail or not, then running spotlessApply to fix the problems automatically before failing. This probably does warrant opening a separate issue.)
I've got a slanted view here, since I use a git client that ignores the staging concept. I know that some users get a lot of power out of the staging area, and I'd love for Spotless to be helpful to them.
Perhaps what you'd really like is spotlessApply --staged, which would look only at staged files, and apply the result to the staging area, ignoring the checked-out files in the WC. Making that work would automatically work for spotlessCheck as well.
If spotlessApply --staged existed, would you care about the return value anymore? You could use spotlessCheck --staged if you wanted errors, or spotlessApply --staged if you just wanted it to just always be right.
--staged sounds like a good solution. I'm not sure whether it is possible for a pre-commit hook to actually stage the changes it makes, though, so it might still be useful to have an option to make spotlessApply to return a non-zero value if it changes anything.
(Also, since it is possible to have both staged and non-staged changes in the same file, --staged should be smart enough to only look at staged changes, not only staged files.)
I'm not sure whether it is possible for a pre-commit hook to actually stage the changes
Looks like it can.
(Also, since it is possible to have both staged and non-staged changes in the same file, --staged should be smart enough to only look at staged changes, not only staged files.)
The staging area is not set of changes. For each file, git has three binary blobs. The working copy, the head, and the index (aka staging area). Each of those is a binary blob, and the "changes" are backed-out by diffing the blobs, not the other way around.
In the simple way to use git, a file is either unstaged (index == head) or staged (index == working copy). In the "power" way, where only some "changes" are staged, all it means is that (index != head != working copy).
spotlessApply --staged would apply to the index blob, so it would do what I think you want.
I created a pre-commit hook that formats the code and adds the formatted files that were initially in the staging area back to the staging area in order to commit the actual formatted code. It leaves the other files as is.
you can install it by running this in your repository root:
curl -o .git/hooks/pre-commit https://gist.githubusercontent.com/toefel18/23c8927e28b7e7cf600cb5cdd4d980e1/raw/163337f2ae596d4b3a55936c2652b1e8227db5b7/pre-commit && chmod +x ./.git/hooks/pre-commit
#!/bin/bash
set -e
filesToAddAfterFormatting=()
containsJavaOrKotlin=0
# Collect all files currently in staging area, and check if there are any java or kotlin files
for entry in $(git status --porcelain | sed -r 's/[ \t]+/-/g')
do
# entry can be for example:
# MM-src/main/java/net/project/MyController.java
# -M-src/main/java/net/project/AnotherController.java
if [[ $entry == M* ]] ; then
filesToAddAfterFormatting+=(${entry:2}) # strips the prefix
fi
if [[ $entry == *.java ]] || [[ $entry == *.kt ]] ; then
containsJavaOrKotlin=1
fi
done;
# If any java or kotlin files are found, run spotlessApply
if [ "$containsJavaOrKotlin" == "1" ] ; then
echo "Kotlin and/or Java found in staging, running: ./gradlew -PdisableSpotlessCheck spotlessApply"
./gradlew -PdisableSpotlessCheck spotlessApply
else
echo "Not running spotlessApply"
fi
# Add the files that were in the staging area
for fileToAdd in $filesToAddAfterFormatting
do
echo "re-adding $fileToAdd after formatting"
git add "$fileToAdd"
done;
Thanks for sharing @toefel18.
@nedtwigg you are welcome :D, is there an option to make spotlessApply format one single file?
is there an option to make spotlessApply format one single file?
Yes, the IDE hook.
Most helpful comment
Understood. Following is my workaround now. Hope it helps someone else who want to achieve this too.