Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

𒎏Wii 🏳️‍⚧️
𒎏Wii 🏳️‍⚧️

Posted on

     

Sh*t, I didn't mean to commit that!

We've all been there 😖

When working on a project, specially in a team, and doubly so if there's other things that occasionally distract us from the task at hand, it is easy to commit changes that were meant to stay local.

One may add a line that wipes a database at the start of an import for a tighter testing cycle, add debug outputs, etc.

As life gets in the way, we take breaks, coworkers want other things from us, or we simply have our head filled with other aspects of the task, one can easily forget to remove these things before committing and pushing.

But it doesn't have to be this way 😮

Git hooks can help us with this. We just have to think up a way for us to mark changes that should never make it into a commit and write a hook to detect them.

Here, I will use the string "nocommit": if this appears anywhere in the changes I'm going to commit, I want git to reject my commit and tell me where it was found.

Let's get to work 😀

Writing git hooks is surprisingly easy: They're really just shell scripts that run in the project directory. Some get arguments, but we don't need them here.

We care about thepre-commit hook. To enable it, just write a file to.git/hooks/pre-commit in your git repository and set its executable permission. To make it reject all commits, put this in the file:

#/bin/shexit1
Enter fullscreen modeExit fullscreen mode

To accept all commits, just have it return 0 instead.

Now, to get to the interesting part:

Get our files 📂

For starters, we'll need access to the files as they will get committed. We don't want our entire commit to fail if a there's a file with anocommit mark that isn't even going to get committed.

Luckily, this is relatively easy to achieve. We can check out the index of the repository into a temporary directory like this:

tmp_dir=$(mktemp-p /dev/shm-d-t"git-index.XXXX")git checkout-index-a--prefix="$tmp_dir"/
Enter fullscreen modeExit fullscreen mode

The first command just gives us a temporary directory in/dev/shm and makes sure it is unique.

The second command checks out the entire index into this temporary directory.

But there isn't really much of a point in checking every file in the repository; we can just limit our search go the files that have been modified. To list these, we can use this command:

git status--porcelain |cut--bytes 4-
Enter fullscreen modeExit fullscreen mode

Get our marks 🔖

Now comes the juicy part. Having our files, we want to scan them for a keyword. For better readability, we can put this part in a function:

get_marks(){    git status--porcelain |cut--bytes 4- |whilereadfiledo        if[-e"$tmp_dir/$file"]thengrep-n-i nocommit"$tmp_dir/$file" |sed"s/:/ /" |whilereadlinedoecho"$file:$line"done        fi    done}
Enter fullscreen modeExit fullscreen mode

We start with listing the files as described above, then pipe that into ash while loop that reads each individual line.

We then use thegrep command to search for "nocommit" in our target$file in the$tmp_dir directory, ignoring case (-i) and prepending the line number (-n). The result then gets piped thoughsed to turn the single colon: separating the line number from the line into a normal space. This last part is just a matter of taste, honestly.

The results are then again piped into another loop, which simply prints the file name and the line. The resulting lines would look something like this:

path/to/file.js:14 console.log("I hate my job") // nocommit
Enter fullscreen modeExit fullscreen mode

Evaluate the results 🔍

Now that we can extract the lines we care about, it's time to decide if the commit is good or not.

We can do this with a simpleif:

marks=$(get_marks)if[-n"$marks"]then    /bin/echo-e"\x1b[31m'no$THEWORD' mark(s) found:\x1b[0m"echo$marksstatus=1elsestatus=0fi
Enter fullscreen modeExit fullscreen mode

First we call our function to get the marks and save its output into amarks variable. If it's empty, we set a status variable to 0. We can't exit yet because we still need to clean up after ourselves. Ifmarks isn't empty, we print a warning, then the marks, and set our status to 1.

Cleanup 🧹

After this we're basically done with the important part; but the temporary directory is still around and we should delete it now that we're done with it. After that, we can just exit with the status we decided on earlier.

rm-r$tmp_direxit$status
Enter fullscreen modeExit fullscreen mode

And there you have it 😁

Here's the finished script. I made one more modification to make sure the script itself can actually be committed without problem 😅

#!/bin/shexportTHEWORD=committmp_dir=$(mktemp-p /dev/shm-d-t"git-index.XXXX")git checkout-index-a--prefix="$tmp_dir"/get_marks(){    git status--porcelain |cut--bytes 4- |whilereadfiledogrep-n-i no$THEWORD"$tmp_dir/$file" |sed"s/:/ /" |whilereadlinedoecho"$file:$line"done    done}marks=$(get_marks)if[-n"$marks"]then    /bin/echo-e"\x1b[31m'no$THEWORD' mark(s) found:\x1b[0m"echo$marksstatus=1elsestatus=0firm-r$tmp_direxit$status
Enter fullscreen modeExit fullscreen mode

Top comments(1)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
auroratide profile image
Timothy Foster
Trying to make the world a funner place through coding.
  • Location
    Dallas
  • Pronouns
    he/him
  • Work
    Software Engineer
  • Joined

Ooh, nice trick! We currently have a git hook that basically looks at json files for "password" and rejects the commit if that line has a value. It use to look atevery json file, but that took so long we had to in-list the specific json files in question. Somehow none of us thought to usegit status --porcelain to check only the files that changed!

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Blog: https://blog.but.gayMastodon: @darkwiiplayer@tech.lgbtPronouns: en.pronouns.page/@darkwiiplayer
  • Pronouns
    she/her;q=1, they/them;q=0.8, */*;q=0.2
  • Joined

More from𒎏Wii 🏳️‍⚧️

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp