Skip to content

Instantly share code, notes, and snippets.

@guilherme
Last active February 24, 2024 20:39
Show Gist options
  • Save guilherme/9604324 to your computer and use it in GitHub Desktop.
Save guilherme/9604324 to your computer and use it in GitHub Desktop.
Git pre-commit hook that detects if the developer forget to remove all the javascript console.log before commit.
#!/bin/sh
# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty
consoleregexp='console.log'
# CHECK
if test $(git diff --cached | grep $consoleregexp | wc -l) != 0
then
exec git diff --cached | grep -ne $consoleregexp
read -p "There are some occurrences of console.log at your modification. Are you sure want to continue? (y/n)" yn
echo $yn | grep ^[Yy]$
if [ $? -eq 0 ]
then
exit 0; #THE USER WANTS TO CONTINUE
else
exit 1; # THE USER DONT WANT TO CONTINUE SO ROLLBACK
fi
fi
@HammzaHM
Copy link

Thanks a lot Bro

@wcarss
Copy link

wcarss commented Jul 7, 2020

modified the script to the below to use egrep (grep wouldn't work on osx mojave for me, complained of unbalanced parentheses), and to include the filenames in the output:

#!/bin/sh

# sourced from https://gist.github.com/guilherme/9604324 on 2020-07-07

# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty

# original: consoleregexp='console.log'
# search for 'console.log(' in added files
consoleregexp='^\+.*console\.log\('

if test `git diff --cached | egrep $consoleregexp | wc -l` != 0
then
  exec git diff --cached | egrep "($consoleregexp|\+{3})"

  # todo note: read -p will break in zsh I think
  read -p "There are some console.log calls in the diff. Do you want to continue? (y/n)" yn
  echo $yn | grep ^[Yy]$
  if [ $? -eq 0 ]
  then
    exit 0; #THE USER WANTS TO CONTINUE
  else
    exit 1; # THE USER DONT WANT TO CONTINUE SO ROLLBACK
  fi
fi

this outputs a result like this:

11:36:29 wcarss@host:~/code (some-branch +)$ git ci
+++ b/onefile
+      console.log("hello");
+++ b/src/SomeDirectory/index.js
+      console.log('test');
There are some console.log calls in the diff. Do you want to continue? (y/n)

@AaronDovTurkel
Copy link

AaronDovTurkel commented Aug 14, 2020

Hi, thanks for sharing. I think this needs an adjustment. If you are removing the console.log, you can not commit!!
I have done a similar thing. Check my regexp, it ensures the line is starting with a plus that indicates you have added a "console.log" and so when removing, git let you commit.

Hope this helps:

regexp='^\+.*console\.log('

Worked this out...

consoleregexp='^[^-]**console.log'

Also this prints out the file names before the console logs...

#!/bin/sh

# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty

consoleregexp='^[^-]**console.log'
filenamewithconsole='\(^[^-]**console.log(\|^+++\)'
# CHECK
if test $(git diff --cached | grep $consoleregexp | wc -l) != 0
then 
  exec git diff --cached | grep -ne $filenamewithconsole | grep -B 1 $consoleregexp
  read -p "There are some occurrences of console.log at your modification. Are you sure want to continue? (y/n) " yn
  echo $yn | grep ^[Yy]$
  if [ $? -eq 0 ] 
  then
    exit 0; #THE USER WANTS TO CONTINUE
  else
    exit 1; # THE USER DONT WANT TO CONTINUE SO ROLLBACK
  fi
fi

@emafriedrich
Copy link

Hi, thanks for sharing. I think this needs an adjustment. If you are removing the console.log, you can not commit!!
I have done a similar thing. Check my regexp, it ensures the line is starting with a plus that indicates you have added a "console.log" and so when removing, git let you commit.

Hope this helps:

regexp='^\+.*console\.log('

thanks!!

@TimCreasman
Copy link

TimCreasman commented May 10, 2021

The above script did not quite work for me. I kept getting grep: repetition-operator operand invalid errors.

Here is my tweaked code that I got working:

#!/bin/bash

# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty

consoleRegexp='^[^-].*console.log'
filenameRegexp='^[^-].*console.log(\|^+++'

if test "$(git diff --cached | grep -c "$consoleRegexp")" != 0
then
  exec git diff --cached | grep -ne "$filenameRegexp" | grep -B 1 "$consoleRegexp"
  read -rp "There are some occurrences of console.log at your modification. Are you sure want to continue? (y/n) " yn
  if echo "$yn" | grep "^[Yy]$"
  then
    exit 0; #THE USER WANTS TO CONTINUE
  else
    exit 1; # THE USER DONT WANT TO CONTINUE SO ROLLBACK
  fi
fi

Hope this helps someone!

@TheophileMot
Copy link

TheophileMot commented Feb 16, 2022

@TimCreasman Thanks, that's helpful. You can simplify a bit: .* means "any number of characters", so it is redundant to have .*.*. Also, the outer parentheses in filenameRegexp aren't being used, so they can be removed. Thus:

consoleRegexp='^[^-].*console.log'
filenameRegexp='^[^-].*console.log(\|^+++'

@TimCreasman
Copy link

@TheophileMot Thanks for the input! I updated the code with your changes 👍

@TheophileMot
Copy link

TheophileMot commented Feb 17, 2022

@TimCreasman Glad to help! I worked on this myself yesterday; it's starting to get away from the original topic, but other people might find this useful. I wanted to check for a few things (e.g., warn when committing directly to main), so I wrapped the console.log check in a function called no_console, and made another function called check_branch to see whether the current branch is master or main. Each of these calls another function, confirm, when there is a potential problem.

Another small fix: after the "y/n" prompt, I didn't like the duplication of the user input, so instead of the line if echo "$yn" | grep "^[Yy]$", which is a bit hacky, it now just checks the variable directly with if test "$yn" == "y" || test "$yn" == "Y".

I also added some fancy colours.

#!/bin/bash

# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty

LRED='\033[1;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # NO COLOUR

confirm () {  
  printf "${YELLOW}"
  read -rp "Continue with commit? (y/n) " yn
  printf "${NC}"
  if test "$yn" == "y" || test "$yn" == "Y"
  then
    printf "\n" # CONTINUE
  else    
    exit 1
  fi
}

check_branch () {
  if test "$(git branch --show-current)" == "master" || test "$(git branch --show-current)" == "main"
  then
    printf "You are on ${LRED}$(git branch --show-current)${NC}.\n"
    confirm
  fi
}

no_console () {
  consoleRegexp='^[^-].*console.log'
  filenameRegexp='^[^-].*console.log(\|^+++'

  if test "$(git diff --cached | grep -c "$consoleRegexp")" != 0
  then
    git diff --cached | grep -ne "$filenameRegexp" | grep -B1 "$consoleRegexp"
    printf "\nSome files include ${LRED}console.log${NC}.\n"
    confirm
  fi
}

### RUN THROUGH CHECKS AND STOP IF THERE IS A PROBLEM
check_branch
no_console

printf "${GREEN}Proceeding with commit...${NC}\n"

This gives a little framework that makes it easier to add further pre-commit checks.


Exercise for the reader: on success, the code as written now displays a "Proceeding with commit" message even when there was no problem. Some people might find this a nuisance. Add a variable to flag whenever the user decides to proceed despite a problem (it can go in the confirm function), and display the "Proceeding" message only when this flag is true.

@Kasik-D
Copy link

Kasik-D commented May 31, 2022

Hello, you are very smart people. Can you help how i add test npm run lint -s && run before this comment. If this command throw error don`t go check console log

@TheophileMot
Copy link

TheophileMot commented May 31, 2022

@Kasik-D Sure. A couple of helpful things to know: $? gives the exit code of whatever command was previously run (the standard is 0 for success, 1 or another number otherwise); also, there are ways to compare numbers in a script, like [[ $x -ne $y ]]. Here -ne stands for "Not Equal to".

So try this:

run_linter () {
  npm lint -s     # or npx eslint *.js, or whatever your lint command is
  if [[ $? -ne 0 ]]
  then
    printf "Oh no\n"
    exit 1
  fi
}

# ...

run_linter
no_console
whatever_other_stuff

@Kasik-D
Copy link

Kasik-D commented Jun 1, 2022

@TheophileMot Thank you, one more qustions what is this programming language ? Where i see instruction of this

@TheophileMot
Copy link

@Kasik-D You're welcome. This language is Bash shell scripting. Bash (or a similar shell, like zsh) is what the terminal runs on MacOS or Linux machines. On Windows, you would have to download it. Learning Bash is a life-long journey, but there are many excellent resources. This looks like a good place to start: https://linuxconfig.org/bash-scripting-tutorial-for-beginners. Have fun!

@Kasik-D
Copy link

Kasik-D commented Jun 1, 2022

@TheophileMot Thanks again

@TimCreasman
Copy link

I've been working on a more general solution that takes a text file of blocked phrases as input and will detect if any of these phrases are in your commit. I also incorporated the color changes @TheophileMot added and formatted some additional things.

If you have a file block-list.txt with the following:

console.log
// TODO
other thing

It will detect these phrases and print out the offending diff statement.

Also disclaimer: I only tested this on OSX, but anyway here's the code:

#!/bin/bash

# Redirect output to stderr.
exec 1>&2

# Block list file location: Update this to point to your own list
blockListFile=".husky/_/block-list.txt"

# Terminal colors and formats
lightRed='\033[1;31m'
green='\033[0;32m'
yellow='\033[1;33m'
clearFormat='\033[0m'
underline='\033[4m'

# Flags
errorFoundFlag=0

confirm () {
  printf '%b\n' "$yellow"
  read -rp "Commit anyway? (y/n) " yn </dev/tty # Enable user input.
  printf '%b' "$clearFormat"

  if echo "$yn" | grep "^[Yy]$"; then
    printf "%bProceeding with commit...%b\n" "$green" "$clearFormat" # The user wants to continue.
  else
    exit 1 # The user does not wish to continue, so rollback.
  fi
}

# Finds the section of the diff that has the blocked phrase
offendingDiff () {
  # Skip this method if there is no parameter passed in
  if [ -z "$1" ]; then
    return 0
  fi

  git diff --cached --color=always | tail -r | awk "/$1/{flag=1}/diff/{flag=0}flag" | tail -r
}

checkRegexp () {
  # Skip this method if there is no parameter passed in
  if [ -z "$1" ]; then
    return 0
  fi

  # Here we prepend the regex with [^-] which will ensure we won't be blocked committing changes that remove the blocked phrase
  regex="^[^-].*$1"

  if [ "$(git diff --cached | grep -c "$regex")" != 0 ]; then
    printf "%b\nError: you are attempting to commit the blocked phrase: %b%s:%b\n" "$lightRed" "$underline" "$1" "$clearFormat"
    # Highlights and adds error arrows to the printout
    offendingDiff "$1" | sed "s/$1/$(printf "%b$1" "$underline")/" | sed "/$1/ s/$/ $(printf "%b<<<<<<<%b" "$lightRed" "$clearFormat")/"
    errorFoundFlag=1
  fi
}

checkBlockList () {
  while read -r line
  do
    checkRegexp "$line"
  done < "$blockListFile"
  if [ "$errorFoundFlag" = 1 ]; then
    confirm
  fi
}

checkBlockList

@Maximauve
Copy link

@TimCreasman thanks for this script, I tested it on Windows / Linux and it seems that the -r argument on tail -r in your OffendingDiff is unkown.

I found the tac command that works just fine

git diff --cached --color=always | tac | awk "/$1/{flag=1}/diff/{flag=0}flag" | tac

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment