#!/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() { 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() { 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() { $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() { 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() { 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] 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 show repository log on git server dist cp changed files to other directory/repository login connect to git server do execute extra script with name do..pl count count lines in project tag show tags tag add add tag tag checkout checkout tag tag delete 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() { 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() {; $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