package SimplyGit::Git; use strict; use warnings; use Log::Log4perl qw(:easy); use lib "."; use SimplyGit::Shellex qw(shellex findBin); use Exporter qw(import); our @EXPORT_OK = qw(readConfig getStatus returnState addFiles commitChanges pushChanges stashAndReset resetFromUpstream updateGitIgnore); # TODO: Handle "optional" passing of logger object # Need to identify which subs are called before the logger object is initialized ( if any ) # and then required the logger object arg for all who don't fit that usecase # TODO: Add info/debug logging for all subroutines sub checkPath { my $path = shift; my $logger = shift; if ( ! -d $path ) { if ( defined $logger && $logger ne "" ) { $logger->error("$path doesn't look like a dir, exiting..."); exit 1; } else { print "Failed and no logger passed, exiting...\n"; exit 1; } } } sub returnConfigPath { my $path = shift; my $logger = shift; checkPath($path,$logger); my $gitConfigPath = $path . "/" . ".git/config"; return $gitConfigPath; } sub readConfig { # This sub is probably not really needed for what I'm trying to do # git itself already parses this config...but an interesting exercise non the less # and may be useful later my $path = shift; my $logger = shift; my $gitConfigPath = returnConfigPath($path,$logger); my $catCmd = findBin("cat",$logger); my @configLines = split("\n",shellex("$catCmd $gitConfigPath",$logger)); # Key is config header, value is hash ref containing config values my %gitConfig; my @valueLines; my $lineCounter = 0; foreach my $line ( @configLines ) { $lineCounter++; #if ( $line =~ m/\[(.*)\]/ ) { if ( $line =~ m/\[(.*)\]/ ) { #$valueLine =~ /\t(.*)\ =\ (.*)$/; $gitConfig{$1} = ""; } } # Tag each line with it's heading # Only way I could think of that worked to solve how this # There are almost certainly better ways # TODO my @taggedLines; my $tag = "NULLTAG"; foreach my $line ( @configLines ) { if ( $line =~ m/\[(.*)\]/ ) { $tag = $1; } else { my $newLine = $tag . $line; push(@taggedLines,$newLine); } } # Get all of the tagged lines into a hash structure. foreach my $key ( keys %gitConfig ) { my %stash; foreach my $tl ( @taggedLines ) { if ( $tl =~ m/^($key)/ ) { $tl =~ s/^($key)//g; $tl =~ m/^\t(.*)\ \=\ (.*)$/; my $confKey = $1; my $confVal = $2; $stash{$confKey} = $confVal; } } $gitConfig{$key} = \%stash; } return %gitConfig; } sub getStatus { my $logger = shift; my $gitCmd = findBin("git",$logger); my $status = shellex("$gitCmd status -uall --porcelain",$logger); chomp $status; return $status; } sub returnState { my $logger = shift; my $gitCmd = findBin("git",$logger); my $currentStatus = getStatus($logger); my @statusLines = split("\n", $currentStatus); my @untracked; my @modified; my @added; my @deleted; foreach my $file ( @statusLines ) { $file =~ m/^\ {0,1}([A-Z?]{1,2})\ {1,2}(.*)/; my $fileAttrs = $1; my $filename = $2; my @attrs = split("",$fileAttrs); foreach my $attr ( @attrs ) { if ( $attr =~ m/\?/ ) { push(@untracked, $filename) unless grep $_ eq $filename, @untracked; } if ( $attr =~ m/[M]/ ) { push(@modified, $filename) unless grep $_ eq $filename, @modified; } if ( $attr =~ m/[A]/ ) { push(@added, $filename) unless grep $_ eq $filename, @added; } if ( $attr =~ m/[D]/ ) { push(@deleted, $filename) unless grep $_ eq $filename, @deleted; } } } return ( \@untracked, \@modified, \@added, \@deleted ); } sub addFiles { my $filesToAddRef = shift; my $logger = shift; my $gitCmd = findBin("git",$logger); foreach my $file ( @$filesToAddRef ) { shellex("$gitCmd add $file",$logger); } } # TODO: Possibly worth returning output for commitChanges(), pushChanges(), stashAndReset, even if I'm not using it right now sub commitChanges { my $commitMsg = shift; chomp $commitMsg; my $logger = shift; my $gitCmd = findBin("git",$logger); shellex("$gitCmd commit -m \"$commitMsg\"",$logger); } sub pushChanges { my $logger = shift; my $gitCmd = findBin("git",$logger); my $output = shellex("$gitCmd push",$logger); } sub stashAndReset { my $logger = shift; my $gitCmd = findBin("git",$logger); shellex("$gitCmd stash",$logger); my @stashList = split("\n", shellex("$gitCmd stash list",$logger)); my $stashCount = scalar @stashList; foreach my $stashNum ( 1..$stashCount ) { shellex("$gitCmd stash drop 0",$logger); } # TODO: Depending on use case need to do more here shellex("$gitCmd rebase",$logger); } sub resetFromUpstream { # git stash and git reset --hard and git pull ? I think } sub updateGitIgnore { my $path = shift; # Maybe better to accept an array of values my $ignoreValue = shift; my $logger = shift; checkPath($path,$logger); my $filename = $path . "/" . ".gitignore"; # Make sure we're not appending/writing if entry already exists in gitignore if ( -f $filename ) { my $catCmd = findBin("cat",$logger); my @ignoreLines = split("\n",shellex("$catCmd $filename",$logger)); if ( ! grep( /^$ignoreValue$/, @ignoreLines ) ) { # TODO: What if logger object is not passed in? open(my $fh, ">>", $filename) or die $logger->error("Couldn't open $filename, exiting..."); chomp $ignoreValue; print $fh "$ignoreValue\n"; close $fh; } } else { open(my $fh, ">", $filename) or die $logger->error("Couldn't open $filename, exiting..."); chomp $ignoreValue; print $fh "$ignoreValue\n"; close $fh; } } sub appendRepoUserConfig { }