Tips and tricks
git tips and tricks
This page collect all git tips and tricks Koha developers have found
git
Hooks
A nice feature of git is hooks.
A hook is a small (or large) script that is executed before or after running some git command.
Hooks are located in the .git/hooks directory; To enable one simply add a file named after the hook point you wish to use to that directory and ensure it is executable.
pre-commit
This pre-commit hook will check for common coding issues before allowing you to complete a commit:
#!/usr/bin/perl
use Modern::Perl;
use File::Basename;
use Term::ANSIColor qw(colored);
$ENV{LOG} = "test";
my $nb_errors = 0;
my @compiled_css = ( "staff-global", "opac" );
for my $filepath (`git diff --cached --name-only`) {
chomp $filepath;
next if not -f $filepath;
my @file_infos = fileparse( $filepath, qr/\.[^.]*/ );
if ( grep( $file_infos[0], @compiled_css ) && $file_infos[2] eq ".css" ) {
say colored( "You don't want to commit $filepath", 'red' );
$nb_errors++;
}
if ( $file_infos[2] =~ /^.pl|^.pm$/ ) {
# If you are not using koha-testing-docker or you use this line instead
# system(qq{/usr/bin/perl -wc $filepath}) == 0
# or say "\n" and $nb_errors++;
system( qq{
docker exec koha_koha_1 /usr/bin/perl -wc /kohadevbox/koha/$filepath 2>&1 | grep -Pv "Subroutine \\w+ redefined at "
} ) == 0
or say "\n" and $nb_errors++;
}
elsif ( $file_infos[2] =~ /^.tt$/ ) {
#TODO
}
elsif ( $file_infos[2] =~ /^.js$/ ) {
#TODO
}
elsif ( $file_infos[2] =~ /^\.vue$/ ) {
qx{yarn --silent run prettier --trailing-comma es5 --semi false --arrow-parens avoid --write $filepath};
}
}
my $filepath;
for my $l ( split '\n', `git diff-index -p -M --cached HEAD` ) {
if ( $l =~ /^diff --git a\/([^ ]*) .*$/ ) {
$filepath = $1;
}
if ( $l =~ /console.log/ ) {
say colored( "$filepath contains console.log ($l)", 'red' );
$nb_errors++;
}
elsif ( $l =~ /^\+ *warn Data::Dumper::Dumper / ) {
say colored( "$filepath contains warn Data::Dumper::Dumper ($l)",
'red' );
$nb_errors++;
}
# This one could be uncommented when Koha will have the Logger module
elsif ( $l =~ /^\+ *warn / ) {
say "$filepath contains warn ($l)";
$nb_errors++;
}
elsif( $l =~ /\+ *\t/) { # fail also if there is some space before the tab
say colored( "$filepath contains a tab, must use 4 spaces ($l)",
'red' );
$nb_errors++;
}
elsif ( $l =~ m/^<<<<<<</ or $l =~ m/^>>>>>>>/ or $l =~ m/^=======/ ) {
say colored( "$filepath contains $& ($l)", 'red' );
}
}
if ($nb_errors) {
say "\nAre you sure you want to commit ?";
say "You can commit with the --no-verify argument";
exit 1;
}
say colored( "Success, pre-commit checks passed", 'green' );
exit 0;
The above script checks that:
- All the perl scripts you want to commit are compiling (perl -wc)
- You are not committing compiled CSS files (CSS generated from SCSS).
- There are no trailing conflict markers (<<<<, >>>>>, =====)
- There are no trailing `warn` or `warn Data::Dumper::Dumper` additions
- There is no file called {something}.log
pre-applypatch
The pre-commit hook can also be useful as a pre-applypatch hook to catch issues with others bugs when applying via either `git am` or `git bz`.
pre-push
This pre-push hook will check for common coding mistakes before allowing the RM or RMaint to push to their community branch:
#!/usr/bin/perl
use Modern::Perl;
use List::MoreUtils qw( any );
use Term::ANSIColor qw(colored);
my $RELEASE = '19.06.x'; # The branch you are managing/maintaining
my $REMOTE = 'upstream'; # The name you gave to the remote for git.koha-community.org
my $SIGNATURE = q|Martin Renvoize <martin.renvoize@ptfs-europe.com>|; # Your signature for sign-off lines
my $remote = $ARGV[0];
my $minor = $RELEASE;
my $upstream = ( $RELEASE =~ m{^\d\d\.(05|11)\.x$} ) ? $RELEASE : 'main';
$minor =~ s/x//g;
if ( $remote eq $REMOTE ) {
say "Running pre-push hook\n";
# Check branch
my $current_branch = `git branch | grep \\* | cut -d ' ' -f2`;
chomp $current_branch;
if ( $current_branch ne $RELEASE ) {
say colored( "Current branch is not $RELEASE", 'red' );
exit 1;
}
# Check commits exist
my @commits = `git rev-list $REMOTE/$upstream..HEAD`;
unless (@commits) {
say colored( 'Hum... no commits to push?', 'green' );
exit 1;
}
# Check commit messages are well formed
my @errors;
for my $commit (@commits) {
say colored("Working on $commit", 'green');
my $commit_message = `git log --format=%s -1 $commit`;
if ( $commit_message !~ m|^Bug\s\d{4,5}: | ) {
push @errors,
colored( "Does not start with 'Bug XXXXX: ' - $commit", 'red' );
}
if ( $commit_message =~ m|DO NOT PUSH| ) {
push @errors,
colored( "Commit contains DO NOT PUSH - $commit", 'red');
}
if ( $commit_message =~ m|DBRev|i
and not $commit_message =~ m|DBRev $minor| )
{
warn $commit_message;
push @errors,
colored( "DBRev is wrong, should start with 'DBRev $RELEASE'",
'red' );
}
my $body = `git log --format=%b -1 $commit`;
$body =~ s|\n\s*|\n|g;
if ( $body !~ m|\n?Signed-off-by: $SIGNATURE| ) {
push @errors, colored( "No Signed-off-by line - $commit", 'red' );
}
my $author_name = `git log --format=%an -1 $commit`;
if ( $author_name eq 'John Doe' ) {
push @errors, colored( "Bad author name - $commit", 'red' );
}
my $author_email = `git log --format=%ae -1 $commit`;
if ( $author_email eq 'you@example.com' ) {
push @errors, colored( "Bad author email - $commit", 'red' );
}
}
# Check for remaining atomicupdate files
my @atomicupdate_files =
`git show HEAD:installer/data/mysql/atomicupdate/|tr -s '\\n' | grep -v '^tree'|grep -v 'README'|grep -v skeleton`;
chomp for @atomicupdate_files;
for my $atomic (@atomicupdate_files) {
push @errors, colored( "Atomicupdate file exists - $atomic", 'red' );
}
my @dbrev_files = `git show HEAD:installer/data/mysql/db_revs/|tr -s '\\n' | grep -v '^tree'`;
chomp for @dbrev_files;
for my $f ( @dbrev_files ) {
my $mode = (stat("installer/data/mysql/db_revs/$f"))[2] & 07777;
push @errors, "dbrev file is missing the +x flag - $f" if !($mode == 0775 || $mode == 0755);
}
# Check for missing CSS Compiles
my $compiled = `yarn build && yarn build --view=opac`;
my @css_changes = `git diff --name-only`;
chomp for @css_changes;
for my $css_change (@css_changes) {
push @errors, colored( "CSS file changes exist - $css_change", 'red' );
}
# Report any errors
if (@errors) {
say colored($_, 'red') for @errors;
exit 1;
}
say colored("Passed tests, pushing...", 'green');
}
exit 0;
This hook will prevent:
- to push a local branch that is not named "$RELEASE"
- to push commits that are not signed off by the RM/RMaint
- to push commits that do not start with "Bug XXXXX: "
- to push DBRev commits that do not correctly formatted ("DBRev $RELEASE")
- to push atomicupdate files
Display the branch you're on
If you want to permanently display the branch you're on, edit your .bashrc file and add the following at the end:
#prompt git GIT_PS1_SHOWDIRTYSTATE=1 GIT_PS1_SHOWUNTRACKEDFILES=0 GIT_PS1_SHOWSTASHSTATE=1 GIT_PS1_SHOWUPSTREAM="verbose" PS1='\D{%H:%M} \[\033[1;35m\]\w$(__git_ps1 " \[\033[1;34m\](%s)")\[\033[0m\]\$ '
your display will look like this:
17:47 ~/koha.dev/koha-community (new/bug_7190 $%)$
displaying the directory you're on, the branch, and some informations about the status of your working directory
Git aliases to simplify command tasks
If you have Git-BZ installed, you can add the following aliases to your .gitconfig file to make applying a patch and reattaching it with your signoff a 1-command affair:
[alias] so = !sh -c 'prove t xt && git commit --amend -s && git bz attach -e $1 HEAD' - qa = !sh -c 'git fetch origin main && git checkout -b bug$1-qa origin/main&& git bz apply $1' - gr = log --graph --full-history --all --color --pretty=tformat:"%x1b[31m%h%x09%x1b[32m%x1b[0m%x20%d%s%x20%x1b[33m(%an)%x1b[0m" qa2 = "!f() { c=`expr $1 - 1`; git filter-repo --message-callback 'return message + b\"\\nSigned-off-by: Full Name <email>\"' --refs HEAD~$c^..; }; f"
You may need to install git filter-repo first.
git so ####
Runs automated tests, signs off on the patch and attaches it to the bug report, obsoleting the unsigned patch.
git qa ####
Fetches the latest main from git.koha-community.org, creates a new branch off that for the bug you're testing, then applies the patch from Bugzilla
git gr
Not BZ-specific, but this draws a character-mode graph showing the branch structure and displays it through your pager.
git qa2 N
Not BZ-specific, this alias will add your signature to N patches. git qa2 3 => deal with 3 patches