1*14e8f286SPatrick Mooney#!/bin/ksh 2*14e8f286SPatrick Mooney# 3*14e8f286SPatrick Mooney# This file and its contents are supplied under the terms of the 4*14e8f286SPatrick Mooney# Common Development and Distribution License ("CDDL"), version 1.0. 5*14e8f286SPatrick Mooney# You may only use this file in accordance with the terms of version 6*14e8f286SPatrick Mooney# 1.0 of the CDDL. 7*14e8f286SPatrick Mooney# 8*14e8f286SPatrick Mooney# A full copy of the text of the CDDL should have accompanied this 9*14e8f286SPatrick Mooney# source. A copy of the CDDL is also available via the Internet at 10*14e8f286SPatrick Mooney# http://www.illumos.org/license/CDDL. 11*14e8f286SPatrick Mooney# 12*14e8f286SPatrick Mooney 13*14e8f286SPatrick Mooney# 14*14e8f286SPatrick Mooney# Copyright 2025 Oxide Computer Company 15*14e8f286SPatrick Mooney# 16*14e8f286SPatrick Mooney 17*14e8f286SPatrick Mooneyfunction cd_git_root 18*14e8f286SPatrick Mooney{ 19*14e8f286SPatrick Mooney git_root=$(git rev-parse --show-toplevel) 20*14e8f286SPatrick Mooney 21*14e8f286SPatrick Mooney if [[ ! -d $git_root ]]; then 22*14e8f286SPatrick Mooney echo "Must be run from within git repository" 23*14e8f286SPatrick Mooney exit 1 24*14e8f286SPatrick Mooney fi 25*14e8f286SPatrick Mooney 26*14e8f286SPatrick Mooney cd $git_root 27*14e8f286SPatrick Mooney} 28*14e8f286SPatrick Mooney 29*14e8f286SPatrick Mooneyfunction check_stale 30*14e8f286SPatrick Mooney{ 31*14e8f286SPatrick Mooney cd_git_root 32*14e8f286SPatrick Mooney awk -f - <<'EOF' 33*14e8f286SPatrick Mooneyfunction check_ignore(repo_root, path, seen_lines, printed_header) 34*14e8f286SPatrick Mooney{ 35*14e8f286SPatrick Mooney fp = repo_root "/" path; 36*14e8f286SPatrick Mooney lnr = 0; 37*14e8f286SPatrick Mooney found = 0; 38*14e8f286SPatrick Mooney while (getline < (repo_root "/" path) > 0) { 39*14e8f286SPatrick Mooney lnr += 1; 40*14e8f286SPatrick Mooney original = $0; 41*14e8f286SPatrick Mooney # trim comments 42*14e8f286SPatrick Mooney sub(/#.*/, ""); 43*14e8f286SPatrick Mooney # and white space 44*14e8f286SPatrick Mooney sub(/[ \t]+/, ""); 45*14e8f286SPatrick Mooney 46*14e8f286SPatrick Mooney if (length() == 0) { 47*14e8f286SPatrick Mooney # skip empty lines 48*14e8f286SPatrick Mooney continue; 49*14e8f286SPatrick Mooney } 50*14e8f286SPatrick Mooney if (/^!/) { 51*14e8f286SPatrick Mooney # skip excludes 52*14e8f286SPatrick Mooney continue; 53*14e8f286SPatrick Mooney } 54*14e8f286SPatrick Mooney if (seen_lines[lnr] == 0) { 55*14e8f286SPatrick Mooney if (printed_header == 0) { 56*14e8f286SPatrick Mooney print "Possible stale:" > "/dev/stderr" 57*14e8f286SPatrick Mooney printed_header = 1; 58*14e8f286SPatrick Mooney } 59*14e8f286SPatrick Mooney printf "%s:%u\t%s\n", path, lnr, original; 60*14e8f286SPatrick Mooney found += 1; 61*14e8f286SPatrick Mooney } 62*14e8f286SPatrick Mooney } 63*14e8f286SPatrick Mooney return found; 64*14e8f286SPatrick Mooney} 65*14e8f286SPatrick Mooney 66*14e8f286SPatrick MooneyBEGIN { 67*14e8f286SPatrick Mooney cmd_ignored_files = \ 68*14e8f286SPatrick Mooney "git ls-files -i -o -x '*' | git check-ignore -v --stdin --no-index"; 69*14e8f286SPatrick Mooney FS=":" 70*14e8f286SPatrick Mooney while ((cmd_ignored_files | getline) > 0) { 71*14e8f286SPatrick Mooney # If --verbose is specified, the output is a series of lines of the form: 72*14e8f286SPatrick Mooney # <source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname> 73*14e8f286SPatrick Mooney seen_lines[$1][$2] = 1; 74*14e8f286SPatrick Mooney } 75*14e8f286SPatrick Mooney 76*14e8f286SPatrick Mooney nignore = 0; 77*14e8f286SPatrick Mooney while (("git ls-files --full-name '*.gitignore'" | getline) > 0) { 78*14e8f286SPatrick Mooney ignores[nignore] = $0; 79*14e8f286SPatrick Mooney nignore += 1; 80*14e8f286SPatrick Mooney } 81*14e8f286SPatrick Mooney 82*14e8f286SPatrick Mooney "git rev-parse --show-toplevel" | getline repo_root 83*14e8f286SPatrick Mooney 84*14e8f286SPatrick Mooney total = 0; 85*14e8f286SPatrick Mooney for (n in ignores) { 86*14e8f286SPatrick Mooney path = ignores[n]; 87*14e8f286SPatrick Mooney total += check_ignore(repo_root, path, seen_lines[path], total); 88*14e8f286SPatrick Mooney } 89*14e8f286SPatrick Mooney} 90*14e8f286SPatrick MooneyEOF 91*14e8f286SPatrick Mooney} 92*14e8f286SPatrick Mooney 93*14e8f286SPatrick Mooneyfunction check_tracked 94*14e8f286SPatrick Mooney{ 95*14e8f286SPatrick Mooney cd_git_root 96*14e8f286SPatrick Mooney echo "git-tracked files matched by gitignore:" 1>&2 97*14e8f286SPatrick Mooney git ls-files | git check-ignore --no-index --stdin 98*14e8f286SPatrick Mooney} 99*14e8f286SPatrick Mooney 100*14e8f286SPatrick Mooney 101*14e8f286SPatrick MooneyUSAGE='Usage: check-gitignore <check> 102*14e8f286SPatrick Mooney 103*14e8f286SPatrick MooneyWhere <check> is one of: 104*14e8f286SPatrick Mooney tracked - Are any tracked files covered by a gitignore? 105*14e8f286SPatrick Mooney stale - Are there gitignore definitions which match no files? 106*14e8f286SPatrick Mooney' 107*14e8f286SPatrick Mooney 108*14e8f286SPatrick Mooneycase $1 in 109*14e8f286SPatrick Mooney tracked ) 110*14e8f286SPatrick Mooney check_tracked 111*14e8f286SPatrick Mooney ;; 112*14e8f286SPatrick Mooney stale ) 113*14e8f286SPatrick Mooney check_stale 114*14e8f286SPatrick Mooney ;; 115*14e8f286SPatrick Mooney * ) 116*14e8f286SPatrick Mooney echo "$USAGE" 117*14e8f286SPatrick Mooney exit 1 118*14e8f286SPatrick Mooney ;; 119*14e8f286SPatrick Mooneyesac 120