526 lines
16 KiB
Perl
Executable File
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
|