Files
2026-08-10 16:12:09 +02:00

526 lines
16 KiB
Perl
Executable File

#!/usr/bin/perl
################################################################################################ mgsh (mwx'2023)
use Sys::Hostname qw(hostname);use Socket;
use Config;
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
use File::Compare;
$VERSION='3.1.5';
$INFO="mwx'2023";
$BASE="$ENV{HOME}/Source";
$BASE="/db/src" if ($Config{osname}=~/linux/i);
die "$BASE not found" if (!-d $BASE);
$PRJ=$ARGV[0] if (-d "$BASE/$ARGV[0]");
$GITHOST = '141.14.128.56';
$GITPORT = '2234';
$GITUSER = 'git';
$GITKEY = 'mgit_rsa';
$GITPATH ="/home/git";
$URL = "ssh://$GITUSER\@$GITHOST:$GITPORT$GITPATH";
$SSH = "ssh -p $GITPORT $GITUSER\@$GITHOST";
$IP = inet_ntoa( (gethostbyname(hostname()))[4] );
$HOST=hostname();$HOST=~s/([^\.]+)\..*$/$1/;
$USER=getpwuid( $< );
use Date::Parse; # date hashes
@D=('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
@M=('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
&cmd("git config --global user.name 'Michael Wesemann'",1); # git settings
&cmd("git config --global user.email mike\@pstbx.org",1);
&cmd("git config --global push.default matching",1);
print "mgsh v$VERSION $INFO, $BASE\n"; # ready to start
for (;;) { # main mgsh loop
if ($PRJ!~/^\s*$/ && -d "$BASE/$PRJ") { # set current working directory/project
$DIR="$BASE/$PRJ";
$BPLSTATE='';
if (-l "$DIR/build.pl") {
chomp($rlf=`readlink $DIR/build.pl`);
$BPLSTATE='/OFF' if ($rlf=~/\.off$/);
}
$p=$1 if ($BASE=~/\/([^\/]+)$/);$prompt="$p/$PRJ/$I$BPLSTATE";
} else {
$DIR="$BASE";
$p=$1 if ($BASE=~/\/([^\/]+)$/);$prompt="$p";
}
chomp($REMURL= `cd $DIR; git config remote.origin.url`);
$REPO=$1 if ($REMURL=~/$url\/(.*)\.git\/?$/);
$prompt=~s/\/\s*$//;$cmd=&readline("$prompt > ","",1); # read command from command line
@tmpcmd=split(' ',$cmd);undef(@cmd);undef(%OPT); # get commandline options
for (@tmpcmd) {if (/\-(\w)/) {$OPT{$1}="-$1";} else {push(@cmd,$_);}}
if ($cmd[0] eq 'dist') { ########################################################################### list [-a]
$CP='/bin/cp';
$CP='/opt/homebrew/bin/gcp' if (-f '/opt/homebrew/bin/gcp');
$DDIR="$BASE/dist/$PRJ";
$DDIR=$cmd[1] if ($cmd[1]!~/^\s*$/);
if (-d $DDIR) {
chdir($DIR);
$n=0;
open(LS,"cd $DIR;git ls-files |");
while(<LS>) {
chomp();
if (!/^private/) {
if (compare($_,"$DDIR/$_")!=0) {
print "$_\n";
cmd("$CP --parents $_ $DDIR",1);
$n++;
}
}
}
print "$n files copied to $DDIR\n";
} else {
print "dist path not found ($DDIR)\n";
}
close(LS);
}
elsif ($cmd[0] eq 'list') { ########################################################################### list [-a]
$path=".";$path="./archive" if ($OPT{a});
open(LS,"$SSH /bin/ls -ltr $path |");
while(<LS>) {
if ($cmd[1]=~/^\s*$/ || /$cmd[1]/i) {
if ($OPT{a}) {
print "$1\n" if (/git\s+git\s+\d+\s+(.*)\.git.tar.gz$/);
} else {
print "$1\n" if (/git\s+git\s+\d+\s+(.*)\.git$/);
}
}
}
close(LS);
}
elsif ($cmd[0] eq 'show') { ############################################################################# show
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
$path=".";
$found=0;
open(LS,"$SSH /bin/ls $path |");
while(<LS>) {
$found++ if (/^$prj\.git$/);
}
close(LS);
if ($found==1) {
open($FH,"$SSH \"cd $GITPATH/$prj\.git && git log --reverse --format='%h %cd %s'\" |");
&repolog($FH);
} else {
print "repository not found\n";
}
}
elsif ($cmd[0] eq 'log' && $PRJ!~/^\s*$/) { ############################################################## log
if (-d "$DIR/.git") {
open($FH,"cd $DIR;git log --reverse --format='%h %cd %s' |");
&repolog($FH);
} else {
print "repository not found";
}
}
elsif ($cmd[0] eq 'push' && $PRJ!~/^\s*$/) { ################################################## push [comment]
if (-f "$DIR/push.pl") {
&shell("perl $DIR/push.pl");
}
$comment='';$comment.="$cmd[$_] " for (1..$#cmd);$comment=~s/\s+$//;
$comment=~s/\s{2}/ /g;
if (-d "$DIR/.git") {
&shell("git add --all .");
$msg="[$USER\@$HOST] $comment";$msg=~s/^\s+//;$msg=~s/\s+$//;
&shell("git commit -m '$msg'");
&shell("git push");
&shell("$SSH touch $GITPATH/${PRJ}.git");
} else {
print "error, no repository (.git) found";
}
}
elsif ($cmd[0] eq 'edit' && $PRJ!~/^\s*$/) { ################################################### edit [number]
$num=10;$num=$cmd[1] if ($cmd[1]=~/^\d+$/);
if (-d "$DIR/.git") {
&shell("git rebase -i HEAD~$num");
&shell("git push --force");
} else {
print "error, no repository (.git) found";
}
}
elsif ($cmd[0] eq 'archive' && $PRJ!~/^\s*$/) { ############################################ archive [comment]
$comment='';$comment.="$cmd[$_] " for (1..$#cmd);$comment=~s/\s+$//;
$comment=~s/[\,\;\:\\\/\=\'\"\|\?\>\<\-]+/ /g;
$comment=~s/\s{2}/ /g;
$comment=~s/\s+/\_/g;
my(@z)=localtime(time());$z[4]+=1;$z[5]-=100;$z[6]+=1;
$z=sprintf("%02d%02d%02d.%02d%02d",$z[3],$z[4],$z[5],$z[2],$z[1]);
if ($comment=~/^\s*$/) {
&shell(sprintf("$SSH 'cp -r ${PRJ}.git archive/${PRJ}_$z.git'"));
&shell(sprintf("$SSH 'cd archive;tar cvzf ${PRJ}_$z.git.tar.gz ${PRJ}_$z.git'"));
&shell("$SSH 'rm -rf archive/${PRJ}_$z.git'");
} else {
&shell(sprintf("$SSH cp -r ${PRJ}.git archive/${PRJ}_${z}_$comment.git"));
&shell(sprintf("$SSH 'cd archive;tar cvzf ${PRJ}_${z}_$comment.git.tar.gz ${PRJ}_${z}_$comment.git'"));
&shell("$SSH 'rm -rf archive/${PRJ}_${z}_$comment.git'");
}
}
elsif ($cmd[0] eq 'init' && $PRJ!~/^\s*$/) { ############################################################ init
&shell("perl $DIR/push.pl") if (-f "$DIR/push.pl");
if ($REPO=~/^\s*$/ || yesno('overwrite existing repository?',0)) {
&shell("$SSH 'rm -rf $GITPATH/${PRJ}.git'");
&shell("$SSH 'mkdir $GITPATH/${PRJ}.git;chdir $GITPATH/${PRJ}.git;git --bare init'");
if (!-f ".gitignore" || yesno('overwrite existing .gitignore?',0)) {
open(OUT,"> .gitignore");
print OUT &gitignore();
close(OUT);
}
&shell("rm -rf .git");
&shell("git init");
&shell("git remote add origin $URL/${PRJ}.git/");
&shell("git add .");
&shell("git commit -m 'initial commit [$IP,$USER]'");
&shell("git push -u origin master");
}
}
elsif ($cmd[0] eq 'login') { ########################################################################### login
&cmd("$SSH");
}
elsif ($cmd[0] eq 'cd' && $cmd[1]!~/^\./) { ############################################################### cd
$PRJ='';
$PRJ=$cmd[1] if (-d "$BASE/$cmd[1]");
}
elsif ($cmd[0] eq 'checkout' && $PRJ!~/^\s*$/) { ###################################### checkout [git options]
&shell("git checkout $cmd[1]");
}
elsif ($cmd[0] eq 'clone') { ###################################################################### clone [-a]
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
$DIR="$BASE";chdir($DIR);
my($path)=".";my($path)="./archive" if ($OPT{a});
my($found)=0;
open(LS,"$SSH /bin/ls $path |");
while(<LS>) {
if ($OPT{a}) {
$found++ if (/^$prj\.git.tar.gz$/);
} else {
$found++ if (/^$prj\.git$/);
}
}
close(LS);
if ($found==1) {
if (-d "$DIR/$prj") {
if (yesno('overwrite existing directory?',0)) {
&cmd("rm -rf $DIR/$prj")
} else {
goto exitclone;
}
}
&shell("git clone $URL/$prj.git") if (!$OPT{a});
if ($OPT{a}) {
&cmd("$SSH 'cd archive;tar xvzf $prj.git.tar.gz'");
&cmd("git clone $URL/archive/$prj.git");
&cmd("$SSH 'rm -rf archive/$prj.git'");
}
} else {
print "repository not found, try 'list [-a]'";
}
$PRJ=$prj if (-d "$BASE/$prj");
exitclone:;
}
elsif ($cmd[0] eq 'cloneall') { ##################################################################### cloneall
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
$DIR="$BASE";chdir($DIR);
my($path)=".";my($path)="./archive" if ($OPT{a});
my($found)=0;
open(LS,"$SSH /bin/ls $path |");
while(<LS>) {
if (/^(.*)\.git$/) {
print "-$1-\n";
&shell("git clone $URL/$1.git");
}
}
close(LS);
}
elsif ($cmd[0] eq 'open' || $cmd[0] eq 'view'){ ######################################################### open
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
if (-d "$BASE/$prj") {
$DIR="$BASE/$prj";chdir($DIR);
$xws='';$xprj='';opendir(DIR,$DIR);
while($file=readdir(DIR)) {
$xws=$file if ($file=~/.xcworkspace$/);
$xprj=$file if ($file=~/.xcodeproj$/);
}
close(DIR);
$xprj='' if ($xws!~/^\s*$/ && $xprj!~/^\s*$/);
&shell("open $xws") if ($xws!~/^\s*$/ && -d "$DIR/$xws");
&shell("open $xprj") if ($xprj!~/^\s*$/ && -d "$DIR/$xprj");
&shell("coda $DIR") if ($xws=~/^\s*$/ && $xprj=~/^\s*$/);
$PRJ=$prj if ($cmd[0] eq 'open');
} else {
print "not found\n";
}
}
elsif ($cmd[0] eq 'count') { ##################################################################### count lines
&count($DIR);
}
elsif ($cmd[0] eq 'do') { ############################################################### execute extra script
&shell("perl $DIR/do.$cmd[1].pl") if (-f "$DIR/do.$cmd[1].pl");
}
elsif ($cmd[0] eq 'tag') { # manage tag
if ($cmd[1] eq 'add' && $cmd[2]!~/^\s*$/) {
&shell("git tag -a '$cmd[2]' -m '$cmd[2]'");
&shell("git push origin '$cmd[2]'");
} elsif ($cmd[1] eq 'checkout' && $cmd[2]!~/^\s*$/) {
&shell("git checkout 'tags/$cmd[2]'");
} elsif ($cmd[1] eq 'delete' && $cmd[2]!~/^\s*$/) {
&shell("git tag -d '$cmd[2]'");
&shell("git push origin ':refs/tags/$cmd[2]'");
} else {
if ($cmd[1]!~/^\s*$/) {
print "unkown tag subcommand\n";
} else {
&shell("git tag -l --format=\"%(taggerdate:short): %(refname:short)\"");
}
}
}
elsif ($cmd[0] eq 'help') { ############################################################################# help
print <<"ENDOFHELP";
mgsh (git shell) $VERSION $INFO, builtin commands:
cd [project] change project
open [project] open project
init make new repository from current directory
push [comment] push changes to git server
edit [number] edit last [number] commits (default is 10)
clone [-a] <repository> clone repository from git server (-a for archive)
cloneall> clone all repository from git server
checkout [git options] forward checkout to git
log show log
archive [comment] archive current repository
list [-a] [pattern] list repositories on git server (-a for archive)
show <repository> show repository log on git server
dist <path> cp changed files to other directory/repository
login connect to git server
do <name> execute extra script with name do.<name>.pl
count count lines in project
tag show tags
tag add <tag> add tag
tag checkout <tag> checkout tag
tag delete <tag> delete tag
ENDOFHELP
}
else { ###################################################################### forward unknown command to shell
&shell($cmd) if ($cmd!~/^\s*$/);
}
}
######################################################################################################## SUPPORT
sub shell() { # shell
my($cmd)=@_;
chdir $DIR;
system($cmd);
}
sub cmd() { # execute command
my($cmd,$quiet)=@_;
print "$cmd\n" if (!$quiet);
system($cmd);
}
sub readline { # readline
my($prompt,$input,$mode)=@_; use Term::ReadLine;
BEGIN{ $ENV{PERL_RL} = 'Perl o=0' };
if (!$term) {
$term = new Term::ReadLine 'term';
open(IN,"$BASE/.mgsh_history");
while(<IN>) {
chomp();
push(@history,$_);
}
close(IN);
$term->SetHistory(@history);
}
my($txt)=$term->readline($prompt,$input);
my(@history)=$term->GetHistory();
shift(@history) while($#history>=10);
open(OUT,"> $BASE/.mgsh_history");
print OUT "$_\n" for (@history);
close(OUT);
return $txt;
}
sub yesno { # request yes/no answer
my($prom,$def)=@_;
my($ans);
if ($def) {;$ans=&getkey("$prom Y/n ? ");}
else {;$ans=&getkey("$prom y/N ? ");}
if ($ans=~/^\s*$/) {
if ($def) {;return(1);}
else {;return(0);}
}
if ($ans=~/^\s*y\s*$/i || $ans=~/^\s*yes\s*$/i) {
return 1;
}
return 0;
}
sub getkey { # get key from keyboard
$|=1;
print "$_[0]";
system "stty", '-icanon', '-echo', 'eol', "\001";
$key = getc(STDIN);$key=~s/[\r\n\t]//g;
system "stty", 'icanon', 'echo', 'eol', "^@";
print "$key\n";
return $key;
}
sub count() { # count lines in project
my($dir)=@_;
use File::Find;
$lines=0;
find(\&counting,$dir);
sub counting {
$f=$File::Find::name;
if (-f $f) {
if ($f=~/\.[hmc]$/ || $f=~/\.xib$/ || $f=~/\.lm$/ || $f=~/\.ym$/ || $f=~/\.pl$/ || $f=~/\.php$/ ||
(-T $f && $f=~/\/bin\//)) {
open(WC,"/usr/bin/wc -l '$f' |");
while(<WC>) {;
$lines+=$1 if (/^\s*(\d*)\s+$f/);
}
close(WC);
}
}
}
print "$lines lines total in $PRJ\n";
}
sub repolog() { # show repository log
my($FH)=@_;
my($o)='';my($os)='';my($oss)='';my($n)=0;my($ns)=0;
my($z)='';my($zs)='';my($zss)='';
while(<$FH>) {
chomp();
@tmp=split(' ');
$t=str2time($tmp[1]);
my(@z)=localtime($t);$z[4]+=1;$z[5]+=1900;$z[6]+=1;
if (time()-$t<86400) {
$z=sprintf(" %02d\:%02d",$z[2],$z[1]);
$zs=sprintf(" %02d\:%02d",$z[2],$z[1]);
$zss=sprintf("%02d\:%02d",$z[2],$z[1]);
} elsif (time()-$t<86400*7) {
$z=sprintf(" %s, %02d\:%02d",$D[$z[6]-1],$z[2],$z[1]);
$zs=sprintf("%s, %02d\:%02d",$D[$z[6]-1],$z[2],$z[1]);
$ns++;
} else {
$z=sprintf("%s, %02d %s %4d, %02d\:%02d",$D[$z[6]-1],$z[3],$M[$z[4]-1],$z[5],$z[2],$z[1]);
$n++;
}
$o.="$tmp[0] $z $tmp[2]\n";
$os.="$tmp[0] $zs $tmp[2]\n";
$oss.="$tmp[0] $zss $tmp[2]\n";
}
if ($n==0 && $ns==0) {
print $oss;
} elsif ($n==0 && $ns>0) {
print $os;
} else {
print $o;
}
}
sub gitignore() {
return << "ENDOFGITIGNORE";
.DS_Store
.AppleDouble
.LSOverride
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
ENDOFGITIGNORE
}
############################################################################################################ END