Git.pm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package SimplyGit::Git;
  2. use strict;
  3. use warnings;
  4. use Log::Log4perl qw(:easy);
  5. use lib ".";
  6. use SimplyGit::Shellex qw(shellex findBin);
  7. use Exporter qw(import);
  8. our @EXPORT_OK = qw(readConfig getStatus returnState addFiles commitChanges pushChanges stashAndReset resetFromUpstream updateGitIgnore);
  9. # TODO: Handle "optional" passing of logger object
  10. # Need to identify which subs are called before the logger object is initialized ( if any )
  11. # and then required the logger object arg for all who don't fit that usecase
  12. # TODO: Add info/debug logging for all subroutines
  13. sub checkPath {
  14. my $path = shift;
  15. my $logger = shift;
  16. if ( ! -d $path ) {
  17. if ( defined $logger && $logger ne "" ) {
  18. $logger->error("$path doesn't look like a dir, exiting...");
  19. exit 1;
  20. } else {
  21. print "Failed and no logger passed, exiting...\n";
  22. exit 1;
  23. }
  24. }
  25. }
  26. sub returnConfigPath {
  27. my $path = shift;
  28. my $logger = shift;
  29. checkPath($path,$logger);
  30. my $gitConfigPath = $path . "/" . ".git/config";
  31. return $gitConfigPath;
  32. }
  33. sub readConfig {
  34. # This sub is probably not really needed for what I'm trying to do
  35. # git itself already parses this config...but an interesting exercise non the less
  36. # and may be useful later
  37. my $path = shift;
  38. my $logger = shift;
  39. my $gitConfigPath = returnConfigPath($path,$logger);
  40. my $catCmd = findBin("cat",$logger);
  41. my @configLines = split("\n",shellex("$catCmd $gitConfigPath",$logger));
  42. # Key is config header, value is hash ref containing config values
  43. my %gitConfig;
  44. my @valueLines;
  45. my $lineCounter = 0;
  46. foreach my $line ( @configLines ) {
  47. $lineCounter++;
  48. #if ( $line =~ m/\[(.*)\]/ ) {
  49. if ( $line =~ m/\[(.*)\]/ ) {
  50. #$valueLine =~ /\t(.*)\ =\ (.*)$/;
  51. $gitConfig{$1} = "";
  52. }
  53. }
  54. # Tag each line with it's heading
  55. # Only way I could think of that worked to solve how this
  56. # There are almost certainly better ways
  57. # TODO
  58. my @taggedLines;
  59. my $tag = "NULLTAG";
  60. foreach my $line ( @configLines ) {
  61. if ( $line =~ m/\[(.*)\]/ ) {
  62. $tag = $1;
  63. } else {
  64. my $newLine = $tag . $line;
  65. push(@taggedLines,$newLine);
  66. }
  67. }
  68. # Get all of the tagged lines into a hash structure.
  69. foreach my $key ( keys %gitConfig ) {
  70. my %stash;
  71. foreach my $tl ( @taggedLines ) {
  72. if ( $tl =~ m/^($key)/ ) {
  73. $tl =~ s/^($key)//g;
  74. $tl =~ m/^\t(.*)\ \=\ (.*)$/;
  75. my $confKey = $1;
  76. my $confVal = $2;
  77. $stash{$confKey} = $confVal;
  78. }
  79. }
  80. $gitConfig{$key} = \%stash;
  81. }
  82. return %gitConfig;
  83. }
  84. sub getStatus {
  85. my $logger = shift;
  86. my $gitCmd = findBin("git",$logger);
  87. my $status = shellex("$gitCmd status -uall --porcelain",$logger);
  88. chomp $status;
  89. return $status;
  90. }
  91. sub returnState {
  92. my $logger = shift;
  93. my $gitCmd = findBin("git",$logger);
  94. my $currentStatus = getStatus($logger);
  95. my @statusLines = split("\n", $currentStatus);
  96. my @untracked;
  97. my @modified;
  98. my @added;
  99. my @deleted;
  100. foreach my $file ( @statusLines ) {
  101. $file =~ m/^\ {0,1}([A-Z?]{1,2})\ {1,2}(.*)/;
  102. my $fileAttrs = $1;
  103. my $filename = $2;
  104. my @attrs = split("",$fileAttrs);
  105. foreach my $attr ( @attrs ) {
  106. if ( $attr =~ m/\?/ ) {
  107. push(@untracked, $filename) unless grep $_ eq $filename, @untracked;
  108. }
  109. if ( $attr =~ m/[M]/ ) {
  110. push(@modified, $filename) unless grep $_ eq $filename, @modified;
  111. }
  112. if ( $attr =~ m/[A]/ ) {
  113. push(@added, $filename) unless grep $_ eq $filename, @added;
  114. }
  115. if ( $attr =~ m/[D]/ ) {
  116. push(@deleted, $filename) unless grep $_ eq $filename, @deleted;
  117. }
  118. }
  119. }
  120. return ( \@untracked, \@modified, \@added, \@deleted );
  121. }
  122. sub addFiles {
  123. my $filesToAddRef = shift;
  124. my $logger = shift;
  125. my $gitCmd = findBin("git",$logger);
  126. foreach my $file ( @$filesToAddRef ) {
  127. shellex("$gitCmd add $file",$logger);
  128. }
  129. }
  130. # TODO: Possibly worth returning output for commitChanges(), pushChanges(), stashAndReset, even if I'm not using it right now
  131. sub commitChanges {
  132. my $commitMsg = shift;
  133. chomp $commitMsg;
  134. my $logger = shift;
  135. my $gitCmd = findBin("git",$logger);
  136. shellex("$gitCmd commit -m \"$commitMsg\"",$logger);
  137. }
  138. sub pushChanges {
  139. my $logger = shift;
  140. my $gitCmd = findBin("git",$logger);
  141. my $output = shellex("$gitCmd push",$logger);
  142. }
  143. sub stashAndReset {
  144. my $logger = shift;
  145. my $gitCmd = findBin("git",$logger);
  146. shellex("$gitCmd stash",$logger);
  147. my @stashList = split("\n", shellex("$gitCmd stash list",$logger));
  148. my $stashCount = scalar @stashList;
  149. foreach my $stashNum ( 1..$stashCount ) {
  150. shellex("$gitCmd stash drop 0",$logger);
  151. }
  152. # TODO: Depending on use case need to do more here
  153. shellex("$gitCmd rebase",$logger);
  154. }
  155. sub resetFromUpstream {
  156. # git stash and git reset --hard and git pull ? I think
  157. }
  158. sub updateGitIgnore {
  159. my $path = shift;
  160. # Maybe better to accept an array of values
  161. my $ignoreValue = shift;
  162. my $logger = shift;
  163. checkPath($path,$logger);
  164. my $filename = $path . "/" . ".gitignore";
  165. # Make sure we're not appending/writing if entry already exists in gitignore
  166. if ( -f $filename ) {
  167. my $catCmd = findBin("cat",$logger);
  168. my @ignoreLines = split("\n",shellex("$catCmd $filename",$logger));
  169. if ( ! grep( /^$ignoreValue$/, @ignoreLines ) ) {
  170. # TODO: What if logger object is not passed in?
  171. open(my $fh, ">>", $filename) or die $logger->error("Couldn't open $filename, exiting...");
  172. chomp $ignoreValue;
  173. print $fh "$ignoreValue\n";
  174. close $fh;
  175. }
  176. } else {
  177. open(my $fh, ">", $filename) or die $logger->error("Couldn't open $filename, exiting...");
  178. chomp $ignoreValue;
  179. print $fh "$ignoreValue\n";
  180. close $fh;
  181. }
  182. }
  183. sub appendRepoUserConfig {
  184. }