git-id (3948B)
1 #!/usr/bin/perl 2 # vim: sts=4 sw=4 ts=4 expandtab: 3 =pod 4 5 =head1 NAME 6 7 git-id 8 9 =head1 SYNOPSIS 10 11 git-id 12 13 =head1 DESCRIPTION 14 15 Runs git-status and prepends numeric ids to filenames. 16 17 =head1 OPTIONS 18 19 =over 4 20 21 =item -h 22 23 Show this help message 24 25 =item -s 26 27 =item --column 28 29 =item -u(no|normal|all) 30 31 =item --color=(always|auto|never) 32 33 These options are similar to git-status'. 34 35 =back 36 37 =head1 SEE ALSO 38 39 git-number(1), git-list(1) 40 41 =cut 42 43 use strict; 44 use warnings; 45 use Cwd; 46 47 my $red = "\e[31m"; 48 my $normal = "\e[0m"; 49 my $git_dir = `git rev-parse --git-dir`; 50 chomp $git_dir; 51 52 my $gitids = "$git_dir/gitids.txt"; 53 54 my $color = 'always'; 55 my $STATUS_DEFAULT = 0; 56 my $STATUS_SHORT = 1; 57 my $status_style = $STATUS_DEFAULT; 58 my $status_opt = ''; 59 60 my $untracked_in_columns = 0; 61 while (scalar @ARGV && $ARGV[0] =~ /^-/) { 62 my $option = shift @ARGV; 63 last if $option eq '--'; 64 if ($option =~ /--color=(always|auto|never)/) { 65 $color = $1; 66 if ($color =~ /never/) { 67 $red = $normal = ''; 68 } 69 } elsif ($option eq '--short') { 70 $status_style = $STATUS_SHORT; 71 } elsif ($option =~ m{^-u(no|normal|all)$}) { 72 $status_opt .= " $option"; 73 } elsif ($option =~ /^--column/) { 74 if ($option eq '--column=never' || ($option =~ /--column(=auto)?$/ && ! -t STDOUT)) { 75 $untracked_in_columns = 0; 76 } else { 77 $untracked_in_columns = 1; 78 } 79 $status_opt .= " $option"; 80 } else { 81 print "Unknown option: $option\n"; 82 exit 1; 83 } 84 } 85 86 if (! $untracked_in_columns) { 87 $status_opt .= " --column=never"; 88 } 89 90 my @status_option_for = ( '', '--short' ); 91 my $status_format = $status_option_for[$status_style]; 92 my $git_cmd = "git -c color.status=$color status $status_opt $status_format " 93 . join(' ', @ARGV); 94 open my $git_status, "$git_cmd|" 95 or die "Error: $!"; 96 97 open my $cache, ">$gitids" 98 or die "Error: $!"; 99 100 # Headers 101 print $cache "cwd: " . getcwd . "\n"; 102 print $cache "status-format: $status_format\n"; 103 print $cache "\n"; 104 105 my $seen_untracked = 0; 106 my @untracked; 107 my $c = 1; 108 while (my $line = <$git_status>) { 109 if ($line =~ /Untracked files:/) { 110 $seen_untracked = 1; 111 } 112 113 if ($status_style == $STATUS_DEFAULT) { 114 if ($seen_untracked && $line =~ /\t/ && $untracked_in_columns) { 115 push @untracked, $line; 116 last; 117 } 118 119 if ($line =~ /#\t/) { 120 $line =~ s/#\t/#$c\t/; 121 $c += 1; 122 } elsif ($line =~ /\t/) { 123 $line =~ s/\t/$c\t/; 124 $c += 1; 125 } 126 } elsif ($status_style == $STATUS_SHORT) { 127 if ($line !~ /^#/) { 128 $line =~ s/^/$c /; 129 $c += 1; 130 } 131 } 132 show_and_cache($line); 133 } 134 135 if ($untracked_in_columns) { 136 my $seen_files = 0; 137 my @the_rest; 138 while (my $line = <$git_status>) { 139 if ($line =~ /\t/) { 140 $seen_files = 1; 141 push @untracked, $line; 142 } else { 143 push @the_rest, $line; 144 } 145 } 146 147 my $rows = @untracked; 148 foreach my $line (@untracked) { 149 my $id = $c; 150 $line =~ s/([\t ])(\S)/add_number(\$id, $1, $2, $rows)/ge; 151 $line =~ s/^ +//; 152 show_and_cache($line); 153 $c += 1; 154 } 155 print join "", @the_rest; 156 } 157 158 close $git_status; 159 close $cache; 160 exit 0; 161 162 sub show_and_cache { 163 my ($line) = @_; 164 my $tocache = $line; 165 $tocache =~ s/{(\d+)}[\t ]{1,2}/\n$1\t/g; 166 $line =~ s/{(\d+)}/$1/g; 167 print $line; 168 $tocache =~ s/\e\[(\d*(;\d*)*)m//g; 169 if ($untracked_in_columns) { 170 $tocache =~ s/\s*\n/\n/gms; # Trailing spaces from columnar formatting 171 } 172 print $cache $tocache; 173 } 174 175 sub add_number { 176 my ($nref, $space, $first_char, $incr) = @_; 177 178 my $num = $$nref; 179 if ($num < 10 && $space eq ' ') { 180 $num = "{$num} "; 181 } else { 182 $num = "{$num}"; 183 } 184 my $str = "$normal$num$space$red$first_char"; 185 $$nref += $incr; 186 return $str; 187 }