Git-Repository-1.325/0000755000175000017500000000000014055265171013100 5ustar bookbookGit-Repository-1.325/README0000644000175000017500000000226114055265171013761 0ustar bookbookGit-Repository Git::Repository is a Perl interface to Git, allowing scripted interactions with one or more repositories. It's a low-level interface, allowing to call any Git command, either porcelain or plumbing, including bidirectional commands such as git commit-tree. Since it is a low-level interface, it doesn't provide any fancy way to call Git commands. It is up to the programmer to setup any environment variables (except GIT_DIR and GIT_WORK_TREE) that the underlying Git command may need and use. SUPPORT AND DOCUMENTATION After installing, you can find documentation for this module with the perldoc command. perldoc Git::Repository You can also look for information at: RT, CPAN's request tracker http://rt.cpan.org/NoAuth/Bugs.html?Dist=Git-Repository AnnoCPAN, Annotated CPAN documentation http://annocpan.org/dist/Git-Repository CPAN Ratings http://cpanratings.perl.org/d/Git-Repository Search CPAN http://search.cpan.org/dist/Git-Repository COPYRIGHT AND LICENCE Copyright 2010-2016 Philippe Bruhat (BooK) This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Git-Repository-1.325/Changes0000644000175000017500000004561114055265171014402 0ustar bookbookRevision history for Git-Repository 1.325 2021-06-01 BOOK [ENHANCEMENTS] - Prevent Test::Git from clobbering its arguments in some cases [TESTS] - Stop t/24-errors.t from failing with git >= 2.30.0-rc0 (Fix RT #133951, RT #134115, GH #20) 1.324 2019-09-01 BOOK [DOCUMENTATION] - Minor documentation updates [EXAMPLES] - Add --force and --docs options to build-git 1.323 2018-11-22 BOOK [TESTS] - Match new error message format in git 2.19.0 (RT #127282 SREZIC) 1.322 2018-04-21 BOOK [DOCUMENTATION] - Detail what Git::Repository::Command does to the environment [TESTS] - Match new error message format in git 2.17. (HMBRAND and JANPAZ) - Fixed the 'git-collection' link for extended ("all gits") tests 1.321 2017-10-01 BOOK - Various small improvements to the Tutorial (with help from Ron Savage) - Minor code changes (with help from E. Choroba) - Minor fix to eg/build-git 1.320 2016-06-12 BOOK [ENHANCEMENTS] - Rewrite Git::Repository::Command's final_output() using System::Command's loop_on() [TESTS] - Fix a test that failed with Git versions with "comments" (Samit Badle) 1.319 2016-05-18 BOOK [DEPRECATION] - The has_git() test function from Test::Git is now obsoleted by Test::Requires::Git. It will issue a warning for now, and will be removed in a future version. [DOCUMENTATION] - Added tutorial entry "Initialize a test repository with a bundle" - Updated the "Sort git versions" tutorial entry with Git::Version::Compare [TESTS] - Use Test::Requires::Git instead of has_git to check if git is available - Fix a number of failing tests related to an incorrect plan (oops) or local git commit hooks. 1.318 2016-03-12 BOOK [ENHANCEMENTS] - Extend version support to all existing version - Let Git::Version::Compare handle all version comparison code - eg/build-git can now build all Git versions since 1.0.0 [DOCUMENTATION] - Minor documentation updates - URL corrections by DOLMEN 1.317 2016-02-13 BOOK [ENHANCEMENTS] - improved git binary detection in Git::Repository::Command - fixed version comparisons for 1.0.0a and 1.0.0b under Perl 5.6 - test_repository( clone => ... ) now dies with a useful message when trying to call it with a broken Git - Git::Repository::Util provides utility functions for Git stuff - eg/build-git: a utility to build and install any Git version [DOCUMENTATION] - Tutorial entry: "Ignore the system and global configuration files" - spelling fix (RT #110027 by Gregor Herrmann) [TEST] - fix RT #89086 (reported by Alex Raguero) - new test: t/test-all-git.t to run the test suite against a directory full of git builds (if available) - tested against 513 versions of git (including all RC), from 1.0.0 to 2.7.1 1.316 2015-12-02 BOOK [ENHANCEMENTS] - avoid a warning if the filehandles have already been closed (related to System::Command 1.114 'interactive' option) [DOCUMENTATION] - add examples for `git log` and using callbacks with `$r->run` in Git::Repository::Tutorial [TESTS] - git-2.5.2 test suite compatibility fix (Petr Šabata) 1.315 2015-07-29 BOOK [ENHANCEMENTS] - Show exit status in 'unknown git error' message (TIMB) 1.314 2015-05-23 BOOK [ENHANCEMENTS] - simplified the internals of version comparisons - give the correct result for comparisons involving 1.0.0a and 1.0.0b 1.313 2015-03-21 BOOK [DOCUMENTATION] - add a section about Git::Raw in the SEE ALSO (written by its author) - document the availability of Git.pm on CPAN (RT #100957) - added the list of contributors to the META file 1.312 2014-08-03 BOOK [ENHANCEMENTS] - test_repository() uses `git init-db` for init under older git versions (although the test suite hasn't yet been adapted to support really old versions of git) - fixed a typo in the error message for test_repository() [TESTS] - made some tests for failure a little more lax when looking for the "fatal:" string in the errput (i.e. look for it at the beginning of any line, not just the firts) [DOCUMENTATION] - various minor documentation improvements - the "OTHER GIT WRAPPERS" is really a "SEE ALSO" section 1.311 2014-03-09 BOOK [ENHANCEMENTS] - avoid warnings from _is_git() when we get nothing out of "git" [DOCUMENTATION] - pod syntax fix - fix typo in Todd Rinaldo's name - acknowledge Nathan Nutter's help in designing the clone option to test_repository() 1.310 2014-01-17 BOOK [ENHANCEMENTS] - add support for a 'clone' option in test_repository() [DOCUMENTATION] - better document the test_repository() options - better document the options hash - make method names easier to link to throughout the documentation [TESTS] - tested against 350 versions of git (including all RC), from 1.5.0.rc0 to 1.8.5.3 1.309 2013-11-02 BOOK [TESTS] - replace use_ok by simply running perl -M$module -e1 [DOCUMENTATION] - some guidelines for naming attributes in plugins [PACKAGING] - split Git::Repository::Plugin::Log and its supporting modules (Git::Repository::Log and Git::Repository::Log::Iterator) out in their own distribution (Git-Repository-Plugin-Log) as of version 1.309 of both distributions 1.308 2013-08-08 BOOK [ENHANCEMENTS] - require System-Command 1.103, since 1.102 was somewhat broken [DOCUMENTATION] - new tutorial item based on RT#87334 1.307 2013-07-26 BOOK [ENHANCEMENTS] - avoid creating zombie processes in _is_git - require the latest System-Command, as it properly works with FCGI, Plack et al. [DOCUMENTATION] - various minor documentation improvements 1.306 2013-07-02 BOOK [DOCUMENTATION] - document how to run git from cwd in Git::Repository::Tutorial (follow-up of RT #86154, thanks to Daniel B. Boorstein (DANBOO)) - document how to avoid the translation of Git messages by using LC_ALL=C in Git::Repository::Tutorial [TESTS] - made tests more robust no matter which locale is defined (thanks to Lars Dieckow (DAXIM)) 1.305 2013-06-16 BOOK [DOCUMENTATION] - list the 'fatal' option in the documentation for run() [TESTS] - various fixes for t/24-errors.t (mostly for Win32, thanks to Christian Walde (MITHALDU)) 1.304 2013-05-25 BOOK [ENHANCEMENTS] - the new 'fatal' option makes it possible to define in detail which exit status codes will make run() die, in addition to the defaults 128 and 129 (many thanks to Grant McLean for a great discussion about this, which inspired the feature) [DOCUMENTATION] - add a section about 'fatal' in Git::Repository::Tutorial [TESTS] - let Dist::Zilla manage the author tests - fixed tests with older gits, and also moved requirements for some tests a little bit further in the past - tested against 326 versions of git (including all RC), from 1.5.0.rc0 to 1.8.3.rc3 1.303 2013-04-28 BOOK [ENHANCEMENTS] - now depends on System::Command 1.100 for proper Win32 support - thanks to a lot of testing help from Christian Walde (MITHALDU) the test suite passes on Win32 (by skipping tests of little importance) 1.302 2013-03-01 BOOK [ENHANCEMENTS] - Git::Repository::Plugin::Log is now able to parse commits with completely empty log messages - Git::Repository::Plugin::Log is now able to parse commits containing multiline headers (like gpgsig and mergetag) 1.301 2013-01-21 BOOK [DEPRECATION] - the following Git::Repository methods are obsolete, and will die when called: create, wc_path, repo_path - the following parameters to Git::Repository->new are obsolete, and will cause the constructor to die: repository, working_copy [PACKAGING] - switch to Dist::Zilla for maintaining the distribution 1.300 2013-01-07 BOOK [ENHANCEMENTS] - fixed support for overloaded objects (e.g. Path::Class objects) in Git::Repository::Command (RT #82373) - fixed Git::Repository::Log::Iterator to work with older gits when disabling colored output (thanks to Dominic Humphries) - fixed some cases where Git::Repository::Command and Git::Repository new() methods ignored some of their parameters. They now die when passed ambiguous or unexpected parameters. (follow-up of RT #82373, thanks to Michael G. Schwern) 1.29 2012-12-04 BOOK [ENHANCEMENTS] - added support for callbacks in run() [DOCUMENTATION] - minor documentation improvements 1.28 2012-11-04 BOOK [ENHANCEMENTS] - disabled colored output from logs in Git::Repository::Log::Iterator - wc_path() and repo_path() accessors are deprecated and now warn - improved the inter-documentation links by liberal use of L<> [TESTS] - ignore commit hooks that may be included with templates (RT #80593) - test for quiet won't fail if no identity is defined (RT #80321) 1.27 2012-10-11 BOOK [ENHANCEMENTS] - Git::Repository::Command skips non-executable files when searching for a git command in the PATH [TEST] - Fixed tests failing with a directory named git in the PATH (RT #80117) 1.26 2012-08-01 BOOK [ENHANCEMENTS] - added a 'quiet' option to silence warnings - improved carp level for the run() method [DOCUMENTATION] - provide an example for the 'quiet' option in Git::Repository::Tutorial 1.25 2011-12-27 BOOK [ENHANCEMENTS] - None. It's as good as 1.24, without the stupid test fail. [TESTS] - fixed a test plan when a git binary is not available 1.24 2011-12-26 BOOK [ENHANCEMENTS] - the command cache for _is_git() is now properly populated when the git option is a command with options (e.g. sudo) - _is_git() still finds git when the PATH contains a directory named git in a better position (RT #72154) - Fix spelling errors fixed in Debian (RT #73079) - Defend against changes to $/ (RT #71621) 1.23 2011-12-04 BOOK [TESTS] - made t/07-version.t pass when /tmp is mounted noexec (RT #72610) 1.22 2011-09-06 BOOK [TESTS] - made t/21-submodule.t pass with git > 1.7.6.0 (RT #70585) - made t/21-submodule.t pass with git < 1.5.4.4 1.21 2011-07-11 BOOK [ENHANCEMENTS] - fix a deep recursion caused by a change in System::Command 1.05 (thanks to Thomas Klausner) [TESTS] - made t/21-submodule.t pass when git is not available or too old or no identity is configured 1.20 2011-06-09 BOOK [ENHANCEMENTS] - None. It's as good as 1.19, without the stupid test fail. [TESTS] - one test always failed if run outside of a git repository, so I didn't detect it, but all testers did :-( Kazuhiro Shibuya provided a patch! 1.19 2011-06-08 BOOK [ENHANCEMENTS] - new final_output() method to Git::Repository::Command, that does the git-specific error checking when collecting the final output - Git::Repository::Log::Iterator will now properly die/warn when the log command is incorrect (thanks to Lasse Makholm for the bug report and proposed patch) - Git::Repository::Command now supports an arrayref as the 'git' option value, thus allowing calling wrappers like sudo (thanks to Dominic Humphries for the initial patch) [DOCUMENTATION] - moved the HOWTO part of the doc to Git::Repository::Tutorial 1.18 2011-04-16 BOOK [ENHANCEMENTS] - the create() method was fragile (parsing the output of porcelain commands) and is now obsolete [DOCUMENTATION] - added an example for running git shortlog (RT #66783) 1.17 2011-02-01 BOOK [ENHANCEMENTS] - Git::Repository::Command now uses System::Command internally [TESTS] - skip some tests that needed a specific version of git - delete GIT_EDITOR in tests that check it (Nigel Metheringham) [BUGS] - getting a working Win32 implementation is now delegated to System::Command 1.16 2011-01-16 BOOK [ENHANCEMENTS] - the reaping of the child process is now delegated to a special Git::Repository::Command::Reaper object. Code such as my $fh = Git::Repository::Command->new(@cmd)->stdout() will now work as expected. [TESTS] - Test::Git::has_git() now accepts the usual options hash - fixed tests to pass again under Perl 5.6.2 1.15 2011-01-11 BOOK [ENHANCEMENTS] - experimental MSWin32 full support using pipes (thanks to BinGOs for pointing me to a perlmonks post by ikegami, that contained working code) - Git::Repository::Log now has a raw_message() accessor, that returns the message with 4-space indent output by git log (Note that this change is INCOMPATIBLE with previous versions, in which message() returned the indented log message, and you had to make up your own "clean" version). [TESTS] - no more skipping tests under MSWin32, but there are some issues with the win32 code, as sometimes the output or errput of the git command is lost (HELP!) - bundle Test::Git, a module providing a few utility functions for testing code requiring a git repository 1.14 2010-10-27 BOOK [ENHANCEMENTS] - complete rewrite of _has_git, which was renamed to _is_git, with a much improved cache for the "is this git valid?" info - calling new() with the 'git' option will now work correctly when there is no git in the PATH (fixes RT bug #62283, reported by Todd Rinaldo) - improved the plugin system design (thanks to Aristotle Pagaltzis) - fixes for making Git::Repository work with modules that do bad things to STDIN, STDOUT and STDERR (thanks to Todd Rinaldo) [TESTS] - improved tests on Win32 (thanks to Olivier Raginel (BABAR) for giving me access to a Win32 VM with Git installed) [BUGS] - sadly, the work on Win32 showed that Git::Repository doesn't fully support that platform, but the test suite safely skips the tests that hang under Win32. I hope to fix this over time. 1.13 2010-10-18 BOOK [ENHANCEMENTS] - using 'git' as an option of a Git::Repository object now works as expected - version() also accepts option hashes - plugin system to load new keywords in the Git::Repository namespace - Git::Repository::Plugin::Log provides the log() method with the help of Git::Repository::Log and Git::Repository::Log::Iterator (Thanks to Todd Rinaldo and Aristotle Pagaltzis for discussions and ideas about what became the plugin system.) [TESTS] - tested against 120+ versions of git, including all versions of the 1.6.* and 1.7.* branches up until 1.7.3.1 1.12 2010-10-04 BOOK [ENHANCEMENTS] - the input option can now be empty: it means "close stdin first" - if the input option is undef, it still means "don't touch stdin" 1.11 2010-10-02 BOOK [ENHANCEMENTS] - fixed a bug in version comparison (version 1.7.1.209.gd60ad81 is smaller than 1.7.1.1.1.g66bd8ab) [TESTS] - skip tests that fail between between versions 1.7.1 and 1.7.1.1 (thanks to Sébastien Aperghis-Tramoni for the private report) - fix abs_path dying on Win32 with a path to a non-existent file (again) 1.10 2010-09-24 BOOK [ENHANCEMENTS] - Git::Repository::Command doesn't write to the command stdin if the input option is set to something empty - Git::Repository::Command now has a version number too [DOCUMENTATION] - minor copy editing by Aristotle Pagaltzis 1.09 2010-08-18 BOOK [ENHANCEMENTS] - now handle SIGPIPE when writing to git stdin (fixes RT bug #60482, reported by Todd Rinaldo (TODDR)) - new() ignores the 'input' option for git commands called during initialization [TESTS] - t/20-simple.t should stop failing with "Non-zero wait status: 13" as it has been doing since 1.05. 1.08 2010-08-17 BOOK [ENHANCEMENTS] - Git::Repository->new() now supports git versions older than 1.5.3 [TESTS] - ensure we have some identity when committing - make tests require the lowest git version they support 1.07 2010-08-14 BOOK [ENHANCEMENTS] - support for option hash in create(), which is attached to the returned Git::Repository object - accessors for Git::Repository::Command objects (including a 'cmdline' accessor) - removed the wc_subdir() attribute, which is useless and redundant with the cwd option - completely rewrote the repo_path and wc_path computation in new() - support new (post-v1.7.1) clone output in create() - less confusing names for options and attributes: + new() now takes git_dir and work_tree (instead of repository and working_copy) + the corresponding attributes are now git_dir and work_tree (instead of repo_path and wc_path) + the older options and attributes are being kept for compatibility [TESTS] - tests for the case when GIT_DIR is not .git (Mark Lawrence) - protect git log tests against format.pretty (Aristotle Pagaltzis) - tests for backward-compatibility with repository, working_copy, etc. 1.06 2010-07-03 BOOK [ENHANCEMENTS] - none [TESTS] - just make sure all tests fail gracefully when git is not installed 1.05 2010-07-02 BOOK [ENHANCEMENTS] - version() method returns the git binary version - version_eq(), version_gt(), etc allow simple version comparison for the current git binary - allow providing a default option hash to Git::Repository->new() [DOCUMENTATION] - Document git init behaviour changed in 1.6.5 [TESTS] - extensive version comparison tests 1.04 2010-06-27 BOOK [ENHANCEMENTS] - create() now supports "reinitializing existing Git repository" (thanks to Michael G. Schwern) [TESTS] - test a few extra cases 1.03 2010-06-18 BOOK [ENHANCEMENTS] - fix module to work with Perl 5.6.x - support GIT_DIR & GIT_WORK_TREE environment variables, when run without a Git::Repository object, and even allow an override when there is one, for those who know what they're doing [TESTS] - fix the case where /tmp is a symlink to some other place - fix abs_path dying on Win32 with a path to a non-existent file 1.02 2010-06-15 BOOK [ENHANCEMENTS] - sensible defaults for Git::Repository->new() without parameters - correctly setup Git::Repository if working_copy points to a subdirectory of the actual work tree - wc_subdir() points to the given subdirectory [TESTS] - skip tests that fail if /tmp is a git repository 1.01 2010-06-14 BOOK [ENHANCEMENTS] - consider git failing with a usage message as a fatal error [TESTS] - don't bother testing too much if git is older than v1.6.0 1.00 2010-06-12 BOOK [YET ANOTHER GIT WRAPPER] - Git::Repository provides context and a simple run() method - Git::Repository::Command is the actual workhorse - 94% test coverage Git-Repository-1.325/t/0000755000175000017500000000000014055265171013343 5ustar bookbookGit-Repository-1.325/t/12-create.t0000644000175000017500000000146314055265171015217 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw( tempdir ); use File::Spec; use File::Path; use Cwd qw( cwd realpath ); use Git::Repository; test_requires_git '1.5.0.rc0'; my $version = Git::Repository->version; plan tests => my $tests; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; my $home = cwd(); # a place to put a git repository my $dir = realpath( tempdir( CLEANUP => 1 ) ); BEGIN { $tests += 2 } mkpath $dir; chdir $dir; # check that create() dies my $r = eval { Git::Repository->create('init'); }; ok( !$r, "Git::Repository->create() fails " ); like( $@, qr/^create\(\) is deprecated, see Git::Repository::Tutorial for better alternatives at /, "... with expected error message" ); chdir $home; Git-Repository-1.325/t/23-quiet.t0000644000175000017500000000352414055265171015105 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use Git::Repository; test_requires_git '1.5.0.rc4'; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_AUTHOR_NAME} = 'Test Author'; $ENV{GIT_AUTHOR_EMAIL} = 'test.author@example.com'; $ENV{GIT_COMMITTER_NAME} = 'Test Committer'; $ENV{GIT_COMMITTER_EMAIL} = 'test.committer@example.com'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; # a place to put a git repository my @init; push @init, init => [ '-q' ] if Git::Repository->version_ge('1.5.2.3'); my $r = test_repository(@init); # PREV will be replaced by the result of the previous command my @tests = ( [ [ qw( mktree ), { input => '' } ] ], [ [ qw( commit-tree PREV ), { input => 'empty tree' } ] ], [ [qw( update-ref refs/heads/master PREV )] ], [ [qw( checkout -b slave )], qr/^Switched to a new branch ['"]slave['"] at / ], [ [qw( checkout master )], qr/^Switched to branch ['"]master['"] at / ], [ [ qw( checkout slave ), { quiet => 1 } ] ], [ [ qw( checkout master ), { quiet => 1 } ] ], ); plan tests => scalar @tests; my $PREV; for my $t (@tests) { my ( $args, $re ) = @$t; # capture warnings my @warnings; local $SIG{__WARN__} = sub { push @warnings, shift }; # replace the args $args = [ map $_ eq 'PREV' ? $PREV : $_, @$args ]; # run the command $PREV = $r->run(@$args); # format the command for test output my $cmd = join ' ', 'git', map { my $v = $_; ref $v ? "{ @{[map{qq'$_ => $v->{$_}'}sort keys %$v]} }" : $v } @$args; # run the actual test if ($re) { like( $warnings[0], $re, "Got the expected warning for: $cmd" ); } else { is( @warnings, 0, "No warning for: $cmd" ); } } Git-Repository-1.325/t/MyGit/0000755000175000017500000000000014055265171014374 5ustar bookbookGit-Repository-1.325/t/MyGit/Hello.pm0000644000175000017500000000032414055265171015774 0ustar bookbookpackage MyGit::Hello; use strict; use warnings; use Git::Repository::Plugin; our @ISA = qw( Git::Repository::Plugin ); sub _keywords { qw( myhello ) } sub myhello { return "Hello, my git world!\n" } 1; Git-Repository-1.325/t/24-errors.t0000644000175000017500000001432414055265171015273 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use Git::Repository; use File::Temp qw( tempfile tempdir ); use constant MSWin32 => $^O eq 'MSWin32'; test_requires_git '1.5.0.rc1'; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_AUTHOR_NAME} = 'Test Author'; $ENV{GIT_AUTHOR_EMAIL} = 'test.author@example.com'; $ENV{GIT_COMMITTER_NAME} = 'Test Committer'; $ENV{GIT_COMMITTER_EMAIL} = 'test.committer@example.com'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; # a place to put a git repository my $r; # a fake git binary used for setting the exit status my $exit; eval { my $version = Git::Repository->version; ( my $fh, $exit ) = tempfile( DIR => tempdir( CLEANUP => 1 ), UNLINK => 1, ( SUFFIX => '.bat' )x!! MSWin32, ); print {$fh} MSWin32 ? << "WIN32" : << "UNIX"; \@$^X -e "shift =~ /version/ ? print qq{git version $version\\n} : exit shift" -- %1 %2 WIN32 #!$^X shift =~ /version/ ? print "git version $version\\n" : exit shift; UNIX close $fh or diag "close $exit failed: $!"; chmod 0755, $exit or diag "chmod $exit failed: $!"; }; # make sure the binary is available if ( !-x $exit ) { diag "Skipping 'git exit' tests: $exit is not " . ( -e _ ? 'executable' : 'available' ); $exit = ''; } # capture all warnings my @warnings; local $SIG{__WARN__} = sub { push @warnings, shift }; my @tests = ( # empty repository { test_repo => [], cmd => [qw( log -1 )], exit => 128, dollar_at => qr/^fatal: (?:bad default revision 'HEAD' |your current branch 'master' does not have any commits yet)/, }, # create the empty tree { cmd => [ mktree => { input => '' } ], exit => 0, }, # create a dummy commit { cmd => [ 'commit-tree', undef, { input => "empty tree" } ], exit => 0, }, # update master { cmd => [ 'update-ref' => 'refs/heads/master', undef ], exit => 0, }, # failing git rm { cmd => [ rm => 'does-not-exist' ], exit => 128, dollar_at => qr/^fatal: pathspec 'does-not-exist' did not match any files /, }, # failing git checkout { cmd => [ checkout => 'does-not-exist' ], exit => 1, warnings => [ qr/^error: pathspec 'does-not-exist' did not match any file\(s\) known to git/, ], }, # failing git checkout (quiet) { cmd => [ checkout => 'does-not-exist', { quiet => 1 } ], exit => 1, }, # usage messages make run() die too { cmd => [ branch => '--does-not-exist' ], exit => '129', dollar_at => Git::Repository->version_lt('1.5.4.rc0') ? qr/^usage: git-branch / : qr/^error: unknown option `does-not-exist'/ }, # test fatal { cmd => [ checkout => 'does-not-exist', { fatal => [1] } ], exit => 1, dollar_at => qr/^error: pathspec 'does-not-exist' did not match any file\(s\) known to git/, }, { cmd => [ checkout => 'does-not-exist', { fatal => 1 } ], exit => 1, dollar_at => qr/^error: pathspec 'does-not-exist' did not match any file\(s\) known to git/, }, { cmd => [ rm => 'does-not-exist', { fatal => -128 } ], exit => 128, warnings => [ qr/^fatal: pathspec 'does-not-exist' did not match any files /, ], }, { cmd => [ rm => 'does-not-exist', { fatal => -128, quiet => 1 } ], exit => 128, }, ); # tests that depend on $exit push @tests, ( # test some fatal combinations { cmd => [ exit => 123, { git => $exit } ], exit => 123, }, { cmd => [ exit => 124, { git => $exit, fatal => [ 1 .. 255 ] } ], exit => 124, dollar_at => qr/^fatal: unknown git error, exit status 124/, }, # setup a repo with some 'fatal' options # and override them in the call to run() { test_repo => [ git => { fatal => [ 1 .. 255 ] } ], cmd => [ exit => 125, { git => $exit } ], exit => 125, dollar_at => qr/^fatal: unknown git error/, }, { cmd => [ exit => 126, { git => $exit, fatal => [ -130 .. -120 ] } ], exit => 126, }, )x!! $exit; # test case where EVERY exit status is fatal push @tests, ( # FATALITY { test_repo => [ git => { fatal => [ 0 .. 255 ] } ], cmd => ['version'], exit => 0, dollar_at => qr/^fatal: unknown git error/, }, { cmd => [ version => { fatal => '-0' } ], exit => 0, }, ); # more tests that depend on $exit push @tests, ( # "!0" is a shortcut for 1..255 { test_repo => [], cmd => [ exit => 140, { git => $exit, fatal => '!0' } ], exit => 140, dollar_at => qr/^fatal: unknown git error/, }, { test_repo => [ git => { fatal => '!0' } ], cmd => [ exit => 141, { git => $exit } ], exit => 141, dollar_at => qr/^fatal: unknown git error/, }, { cmd => [ exit => 142, { git => $exit, fatal => [ -150 .. -130 ] } ], exit => 142, }, )x!! $exit; # count the warnings we'll check @warnings = map @{ $_->{warnings} ||= [] }, @tests; plan tests => 3 * @tests + @warnings; my @quiet = Git::Repository->version_ge('1.5.2.3') ? ( init => ['-q'] ) : (); my $output = ''; for my $t (@tests) { @warnings = (); # create a new test repository if needed $r = test_repository( @{ $t->{test_repo} }, @quiet ) if $t->{test_repo}; # check if the command threw errors my @cmd = map { (defined) ? $_ : $output } @{ $t->{cmd} }; my $cmd = join ' ', grep !ref, @cmd; $output = eval { $r->run(@cmd); }; $t->{dollar_at} ? like( $@, $t->{dollar_at}, "$cmd: died" ) : is( $@, '', "$cmd: ran ok" ); is( $? >> 8, $t->{exit}, "$cmd: exit status $t->{exit}" ); # check warnings is( @warnings, @{ $t->{warnings} }, "warnings: " . @{ $t->{warnings} } ); for my $warning ( @{ $t->{warnings} } ) { like( shift @warnings, $warning, '... expected warning' ); } diag $_ for @warnings; } Git-Repository-1.325/t/05-try_git.t0000644000175000017500000000722714055265171015443 0ustar bookbookuse strict; use warnings; use Test::More; use Git::Repository; use Cwd qw( cwd ); use File::Spec; use File::Temp qw( tempdir ); use File::Path qw( mkpath rmtree ); use Config; my $cwd = cwd(); my @not_git = ( map ( { ( $_, File::Spec->catfile( $cwd, $_ ), File::Spec->catfile( File::Spec->updir, $_ ) ) } 'this-command-unlikely-to-even-exist-or-be-git' ), $^X, '', 't' ); plan tests => 3 * @not_git + 2 + 8 * 2; for my $not_git (@not_git) { # special case: '' means test removing $ENV{PATH} local $ENV{PATH} if ! $not_git; $not_git ||= 'git'; # direct test ok( !Git::Repository::Command::_is_git($not_git), "_is_git( $not_git ) fails with bad git command" ); # as an option ok( !eval { Git::Repository->run( '--version', { git => $not_git } ); 1; }, 'run() fails with bad git command' ); like( $@, qr/^git binary '.*?' not available or broken/, '... with expected error message' ); } # more tests if git is available SKIP: { skip 'Default git binary not found in PATH', 2 + 2 * 8 if !Git::Repository::Command::_is_git('git'); my $abs_git = Git::Repository::Command::_which('git'); diag "Testing _is_git with $abs_git from $cwd"; ok( Git::Repository::Command::_is_git($abs_git), "_is_git( $abs_git ) " ); my $rel_git = File::Spec->abs2rel($abs_git); diag "Testing _is_git with $rel_git from $cwd"; ok( Git::Repository::Command::_is_git($rel_git), "_is_git( $rel_git ) " ); # tests with symlinks SKIP: { my $osname = "@Config{qw( osname osvers archname archname64 )}"; skip "symlink() not supported on this $osname", 8 * 2 if !eval { symlink( '', '' ); 1 }; # a place to experiment my $dir = tempdir( DIR => 't', CLEANUP => 1 ); my $target = File::Spec->catfile( $dir, 'target' ); my $link = File::Spec->catfile( $dir, 'link' ); my $real = File::Spec->catfile( $dir, 'real' ); for my $dir ( $dir, File::Spec->rel2abs( $dir ) ) { $ENV{PATH} = $dir; # symlink pointing to the real thing # (not using 'link', because the _is_git() cache is not very smart # with links that change of target while the program is running) ok( symlink( File::Spec->rel2abs( $abs_git ), $real ), "real -> $abs_git" ); ok( Git::Repository::Command::_is_git('real'), 'symlink to git' ); unlink $link, $real; # create a dangling symlink open my $fh, '>', $target or diag "Can't open $target: $!"; close $fh; chmod 0777, $target; ok( symlink( 'target', $link ), 'link -> target' ); unlink $target; ok( !Git::Repository::Command::_is_git('link'), 'dangling symlink' ); unlink $link; # symlink pointing to a directory mkpath $target; ok( symlink( 'target', $link ), 'link -> target/' ); ok( !Git::Repository::Command::_is_git('link'), 'symlink to a dir' ); unlink $link; rmtree $target; # secondary target, working, but later in the PATH my $subdir = File::Spec->catdir( $dir, 'sub' ); mkpath $subdir; local $ENV{PATH} = join $Config::Config{path_sep}, $dir, $subdir; ok( symlink( File::Spec->rel2abs( $abs_git ), File::Spec->catfile( $subdir, 'link' ) ), "sub/link -> $abs_git " ); ok( Git::Repository::Command::_is_git('link'), 'symlink to git' ); unlink $link; rmtree $subdir; } } } Git-Repository-1.325/t/22-backward.t0000644000175000017500000000371614055265171015536 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw( tempdir ); use File::Spec; use Cwd qw( cwd realpath ); use Git::Repository; test_requires_git '1.5.0.rc1'; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; my $home = cwd(); # a place to put a git repository my @init; push @init, init => [ '-q' ] if Git::Repository->version_ge('1.5.2.3'); my $fake = realpath( tempdir( CLEANUP => 1 ) ); my $r = test_repository(@init); my $dir = $r->work_tree; my $gitdir = $r->git_dir; # capture warnings my @warnings; local $SIG{__WARN__} = sub { push @warnings, shift }; # use new with various options my $re_wc = qr/^working_copy is obsolete, please use work_tree instead /; my $re_re = qr/^repository is obsolete, please use git_dir instead /; my @tests = ( [ $home => [ working_copy => $dir ], $re_wc ], [ $home => [ work_tree => $dir, working_copy => $fake ], $re_wc ], [ $home => [ repository => $gitdir ], $re_re ], [ $home => [ git_dir => $gitdir, repository => $fake ], $re_re ], [ $home => [ git_dir => $gitdir, repository => $fake, work_tree => $dir, working_copy => $fake, ], $re_re ], # order doesn't matter [ $home => [ repository => $fake, working_copy => $fake, work_tree => $dir, git_dir => $gitdir, ], $re_re ], ); # test backward compatibility plan tests => 2 * @tests; # now test most possible cases for backward compatibility for my $t (@tests) { my ( $cwd, $args, $re ) = @$t; chdir $cwd; my $i; my @args = grep { ++$i % 2 } @$args; $r = eval { Git::Repository->new(@$args) }; ok( !$r, "Git::Repository->new( @args ) fails" ); like( $@, $re, '... with expected error message' ); } Git-Repository-1.325/t/11-create.t0000644000175000017500000002125414055265171015216 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw( tempdir ); use File::Spec; use File::Path; use Cwd qw( cwd realpath ); use Git::Repository; test_requires_git '1.6.0'; my $version = Git::Repository->version; plan tests => my $tests + my $between + my $extra; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; my $home = cwd(); # a place to put a git repository my $tmp = realpath( tempdir( CLEANUP => 1 ) ); # some dirname generating routine my $i; sub next_dir { return File::Spec->catdir( $tmp, ++$i ); } sub test_repo { my ( $r, $gitdir, $dir, $options ) = @_; # normalize actual paths, but do not die under Win32 eval { $gitdir = realpath($gitdir) } if defined $gitdir; eval { $dir = realpath($dir) } if defined $dir; local $Test::Builder::Level = $Test::Builder::Level + 1; isa_ok( $r, 'Git::Repository' ); is( $r->git_dir, $gitdir, '... correct git_dir' ); is( $r->work_tree, $dir, '... correct work_tree' ); is_deeply( $r->options, $options, "... correct options" ); } my ( $dir, $r ); $dir = next_dir; # a quiet git init: my @init = qw( init ); push @init, '-q' if Git::Repository->version_ge('1.5.2.3'); # PASS - non-existent directory BEGIN { $tests += 5 } my $gitdir = File::Spec->catdir( $dir, '.git' ); mkpath $dir; chdir $dir; ok( $r = eval { $r = Git::Repository->run( @init, { cwd => $dir } ); Git::Repository->new( { cwd => $dir } ); }, "init => $i" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, { cwd => $dir } ); chdir $home; # PASS - new() on a normal repository BEGIN { $tests += 5 } ok( $r = eval { Git::Repository->new( git_dir => $gitdir ); }, "new( git_dir => $i )" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, {} ); # PASS - new() on a normal repository BEGIN { $tests += 5 } ok( $r = eval { Git::Repository->new( work_tree => $dir ); }, "new( work_tree => $i )" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, {} ); # PASS - new() on a subdir of the working copy BEGIN { $tests += 5 } my $subdir = File::Spec->catdir( $dir, 'sub' ); mkpath $subdir; ok( $r = eval { Git::Repository->new( work_tree => $subdir ); }, "new( work_tree => $i/sub )" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, {} ); # PASS - new() without arguments BEGIN { $tests += 5 } chdir $dir; ok( $r = eval { Git::Repository->new(); }, "new() => $i" ); diag $@ if $@; chdir $home; test_repo( $r, $gitdir, $dir, {} ); # PASS - new() without arguments from subdir BEGIN { $tests += 5 } chdir $subdir; ok( $r = eval { Git::Repository->new(); }, "new() => $i/sub" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, {} ); chdir $home; # PASS - new() with both arguments from subdir BEGIN { $tests += 5 } chdir $subdir; ok( $r = eval { Git::Repository->new( work_tree => $dir, git_dir => $gitdir ); }, "new( work_tree => $i, git_dir => $i/.git ) => $i/sub" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, {} ); chdir $home; my $old; SKIP: { skip "cloning an empty repo dies for 1.7.0.rc1 <= git <= 1.7.0.2, we have $version", $between if Git::Repository->version_le('1.7.0.2') && Git::Repository->version_ge('1.7.0.rc1'); # PASS - clone an existing repo and warns BEGIN { $between += 5 } $old = $dir; $dir = next_dir; ok( $r = eval { Git::Repository->run( clone => $old => $dir, { quiet => 1 } ); Git::Repository->new( work_tree => $dir ); }, "clone => @{[ $i - 1 ]} => $i" ); diag $@ if $@; test_repo( $r, File::Spec->catdir( $dir, '.git' ), $dir, {} ); # PASS - clone an existing repo as bare and warns # relative target path BEGIN { $between += 5 } $old = $dir; $dir = next_dir; chdir $tmp; ok( $r = eval { Git::Repository->run( clone => '--bare', $old => $i, { quiet => 1} ); Git::Repository->new( git_dir => $i ); }, "clone => --bare, @{[ $i - 1 ]} => $i" ); diag $@ if $@; chdir $home; test_repo( $r, $dir, undef, {} ); # PASS - clone an existing repo as bare and warns # absolute target path BEGIN { $between += 5 } SKIP: { $old = $dir; $dir = next_dir; skip 'git clone --bare fails with absolute target path', 5 if $^O eq 'MSWin32'; ok( $r = eval { Git::Repository->run( clone => '--bare', $old => $dir, { quiet => 1 } ); Git::Repository->new( git_dir => $dir ); }, "clone => --bare, @{[ $i - 1 ]} => $i" ); diag $@ if $@; test_repo( $r, $dir, undef, {} ); } } # FAIL - clone a non-existing repo BEGIN { $tests += 3 } $old = next_dir; $dir = next_dir; ok( !( $r = eval { Git::Repository->run( clone => $old => $dir ); Git::Repository->new( work_tree => $dir ); } ), "clone => @{[ $i - 1 ]} => $i - FAILED" ); is( $r, undef, "clone => @{[ $i - 1 ]} => $i - did not create a repository" ); like( $@, qr/^fatal: /m, 'fatal error from git' ); # PASS - init a bare repository BEGIN { $tests += 5 } $dir = next_dir; mkpath $dir; chdir $dir; ok( $r = eval { Git::Repository->run( @init, '--bare' ); Git::Repository->new(); }, "clone => @{[ $i - 1 ]} - $i" ); diag $@ if $@; test_repo( $r, $dir, undef, {} ); chdir $home; # PASS - new() on a bare repository BEGIN { $tests += 5 } ok( $r = eval { Git::Repository->new( git_dir => $dir ); }, "new( git_dir => $i )" ); diag $@ if $@; test_repo( $r, $dir, undef, {} ); # PASS - non-existent directory, not a .git GIT_DIR # no --work-tree mean it's bare BEGIN { $tests += 5 } $dir = next_dir; mkpath $dir; chdir $dir; $gitdir = File::Spec->catdir( $dir, '.notgit' ); my $options = { cwd => $dir, env => { GIT_DIR => File::Spec->abs2rel($gitdir) } }; ok( $r = eval { Git::Repository->run( @init, $options ); Git::Repository->new($options); }, "init - cwd => $i, GIT_DIR => '.notgit'" ); diag $@ if $@; chdir $home; test_repo( $r, $gitdir, undef, $options ); BEGIN { $tests += 5 } ok( $r = eval { Git::Repository->new( git_dir => $gitdir ); }, "new( git_dir => $i )" ); diag $@ if $@; test_repo( $r, $gitdir, undef, {} ); # PASS - non-existent directory, not a .git GIT_DIR # now provide a --work-tree BEGIN { $tests += 5 } $dir = next_dir; mkpath $dir; chdir $dir; $gitdir = File::Spec->catdir( $dir, '.notgit' ); $options = { cwd => $dir, env => { GIT_DIR => File::Spec->abs2rel($gitdir) } }; ok( $r = eval { Git::Repository->run( "--work-tree=$dir", @init, $options ); Git::Repository->new( work_tree => $dir, $options ); }, "init - cwd => $i, GIT_DIR => '.notgit'" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, $options ); chdir $home; # PASS - non-existent directory, not a .git GIT_DIR # provide a --work-tree, and start in a subdir BEGIN { $tests += 5 } $dir = next_dir; mkpath $dir; $gitdir = File::Spec->catdir( $dir, '.notgit' ); $subdir = File::Spec->catdir( $dir, 'sub' ); mkpath $subdir; chdir $subdir; $options = { cwd => $subdir, env => { GIT_DIR => File::Spec->abs2rel( $gitdir, $subdir ), GIT_WORK_TREE => File::Spec->abs2rel( $dir, $subdir ) } }; ok( $r = eval { Git::Repository->run( @init, $options ); Git::Repository->new($options); }, "init - cwd => $i, GIT_DIR => '.notgit'" ); diag $@ if $@; chdir $home; test_repo( $r, $gitdir, $dir, $options ); # these tests requires git version >= 1.6.5 SKIP: { skip "these tests require git >= 1.6.5, but we only have $version", $extra if Git::Repository->version_lt('1.6.5'); # FAIL - init a dir that is a file BEGIN { $extra += 3 } $dir = next_dir; { open my $fh, '>', $dir; } # creates an empty file ok( !( $r = eval { Git::Repository->run( @init, $dir ); Git::Repository->new( work_tree => $dir ); } ), "init => $i - FAILED" ); is( $r, undef, "init => $i - did not create a repository" ); like( $@, qr/^fatal: /m, 'fatal error from git' ); # PASS - init on an existing repository BEGIN { $extra += 10 } $dir = next_dir; $gitdir = File::Spec->catdir( $dir, '.git' ); ok( $r = eval { Git::Repository->run( @init, $dir ); Git::Repository->new( work_tree => $dir ); }, "init => $i" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, {} ); ok( $r = eval { Git::Repository->run( @init, $dir ); Git::Repository->new( work_tree => $dir ); }, "init => $i - again" ); diag $@ if $@; test_repo( $r, $gitdir, $dir, {} ); } Git-Repository-1.325/t/13-sudo.t0000644000175000017500000000121514055265171014722 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Spec; plan tests => 3; # test using a wrapper my $sudo = File::Spec->catfile( t => 'sudo.pl' ); my $out = Git::Repository->run( qw( a b ), { git => [ $^X, $sudo, 'git' ] } ); is( $out, 'git a b', 'wrapper called correctly' ); # same wrapper, but to something that fails to identify as git ok( !eval { $out = Git::Repository->run( qw( a b ), { git => [ $^X, $sudo, 'meh' ] } ); }, 'sudo meh fails to pass for sudo git' ); like( $@, qr/^git binary '.*meh' not available or broken/, '... with expected error message' ); Git-Repository-1.325/t/00-report-prereqs.dd0000644000175000017500000000463714055265171017075 0ustar bookbookdo { my $x = { 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '0' } }, 'develop' => { 'requires' => { 'Pod::Coverage::TrustPod' => '0', 'Test::CPAN::Meta' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08' } }, 'runtime' => { 'requires' => { 'Carp' => '0', 'Cwd' => '0', 'Exporter' => '0', 'File::Spec' => '0', 'File::Spec::Functions' => '0', 'File::Temp' => '0', 'Git::Version::Compare' => '1.001', 'IO::Handle' => '0', 'Scalar::Util' => '0', 'System::Command' => '1.118', 'Test::Builder' => '0', 'namespace::clean' => '0', 'perl' => '5.006', 'strict' => '0', 'warnings' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'ExtUtils::MakeMaker' => '0', 'File::Path' => '0', 'File::Spec' => '0', 'IO::Handle' => '0', 'IPC::Open3' => '0', 'Test::More' => '0', 'Test::Requires::Git' => '1.005', 'constant' => '0', 'lib' => '0', 'overload' => '0' } } }; $x; }Git-Repository-1.325/t/author-pod-coverage.t0000644000175000017500000000053614055265171017407 0ustar bookbook#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); Git-Repository-1.325/t/00-report-prereqs.t0000644000175000017500000001342614055265171016745 0ustar bookbook#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.027 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if $mod eq 'perl'; next if grep { $_ eq $mod } @exclude; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass; # vim: ts=4 sts=4 sw=4 et: Git-Repository-1.325/t/30-test_repository.t0000644000175000017500000000377514055265171017242 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw( tempdir ); use File::Spec::Functions; use Cwd qw( cwd abs_path ); use Git::Repository; # git clone supports existing directories since then test_requires_git '1.6.2.rc0'; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_AUTHOR_NAME} = 'Test Author'; $ENV{GIT_AUTHOR_EMAIL} = 'test.author@example.com'; $ENV{GIT_COMMITTER_NAME} = 'Test Committer'; $ENV{GIT_COMMITTER_EMAIL} = 'test.committer@example.com'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; my $home = cwd; my @init; push @init, init => [ '-q' ] if Git::Repository->version_ge('1.5.2.3'); my $r = test_repository(@init); # add a file my $file = 'hello.txt'; { open my $fh, '>', catfile( $r->work_tree, $file ) or die "Can't open $file for writing: $!"; print $fh "Hello, world!\n"; } $r->run( add => $file ); $r->run( qw( commit --no-verify -m hello ) ); my $sha1 = $r->run( 'rev-parse' => 'master' ); # expect test_repository to fail if ( Git::Repository->version_ge('1.7.0.rc1') && Git::Repository->version_le('1.7.0.2') ) { for my $meth (qw( work_tree git_dir )) { ok( !eval { test_repository( clone => [ $r->$meth ] ) }, '`git clone ` fails with 1.7.0.rc1 <= git <= 1.7.0.2' ); like( $@, qr/^test_repository\( clone => \.\.\. \) requires /, '.. expected error message' ); } } # make a clone with test_repository else { my $s; for my $meth (qw( work_tree git_dir )) { $s = test_repository( clone => [ $r->$meth ] ); isnt( $s->git_dir, $r->git_dir, "$meth clone: different git_dir" ); isnt( $s->work_tree, $r->work_tree, "$meth clone: different work_tree" ); is( $s->run( 'rev-parse' => 'master' ), $sha1, "$meth clone points to the same master" ); } } done_testing; Git-Repository-1.325/t/test-all-git.t0000644000175000017500000000363714055265171016047 0ustar bookbookuse strict; use warnings; use Test::More; use Git::Repository; use Git::Version::Compare qw( cmp_git ); use File::Spec; plan skip_all => 'these tests are for extended testing' if !$ENV{EXTENDED_TESTING}; # look for the git-collection dir, including under ../.. (under `dzil test`) my $collection = 'git-collection'; my ($git_home) = grep -d, $collection, File::Spec->catdir( File::Spec->updir, File::Spec->updir, $collection ); plan skip_all => "set the $collection directory/link to point at your local collection of Git builds" if !defined $git_home; my @versions; { opendir my $DH, $git_home or die "Can't opendir $git_home"; @versions = grep { /^\d/ } readdir $DH; closedir $DH } # the test script accepts version specifications to limit the number # of versions tested my @spec = map { /-/ ? do { # range my ( $min, $max ) = split /-/; sub { !( $min && Git::Repository::_version_gt( $min, $_[0] ) ) && !( $max && Git::Repository::_version_gt( $_[0], $max ) ); } } : do { # single item my $v = $_; sub { $_[0] eq $v } }; } @ARGV; # the default it to test against all available versions if (@spec) { @versions = grep { my $version = $_; my $ok; $ok += $_->($version) for @spec; $ok; } @versions; } # sort the versions to test @versions = sort cmp_git @versions; plan tests => scalar @versions; # remove it to avoid infinite loops delete $ENV{EXTENDED_TESTING}; my @fail; for my $version (@versions) { local $ENV{PATH} = join $Config::Config{path_sep}, File::Spec->catdir( $git_home, $version, 'bin' ), $ENV{PATH}; close STDERR; # don't let the inner prove spoil the output `prove -l t`; ok( $? == 0, $version ); push @fail, $version if $?; } diag "Test suite failed with Git version:" if @fail; diag join ' ', splice @fail, 0, 5 while @fail; Git-Repository-1.325/t/07-version.t0000644000175000017500000000373714055265171015453 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw( tempfile tempdir ); use Git::Repository; use constant MSWin32 => $^O eq 'MSWin32'; # setup fake git my $W = my $V = my $version = Git::Repository->version; $V =~ s/\.(\d+)\./.@{[$1+1]}./; $W =~ s/\.(\d+)\./.@{[$1+2]}./; my $o = { git => fake_git('1.2.3') }; # small one my $O = { git => fake_git($W) }; # big one # setup tests (that will fail if the real git is called) my @true = ( [ version_eq => '1.2.3', $o ], # small [ version_ne => '1.2.2', $o ], [ version_lt => '1.2.3.5', $o ], [ version_le => '1.2.3', $o ], [ version_le => '1.2.3.5', $o ], [ version_gt => '1.1.1', $o ], [ version_ge => '1.2.3', $o ], [ version_ge => '1.2.2', $o ], [ version_eq => $W, $O ], # big [ version_ne => $version, $O ], [ version_gt => $version, $O ], [ version_ge => $V, $O ], [ version_ge => $W, $O ], ); plan tests => 2 + 3 * @true; # use options in version() is( Git::Repository->version($o), '1.2.3', "version() options (small git)" ); is( Git::Repository->version($O), $W, "version() options (big git)" ); # use options in version_eq() for my $t (@true) { my ( $method, @args ) = @$t; ok( Git::Repository->$method(@args), "$method($args[0]) options" ); ok( Git::Repository->$method( reverse @args ), "$method($args[0]) options (any order)" ); ok( Git::Repository->$method( @args, 'bonk' ), "$method($args[0]) options (with bogus extra args)" ); } # helper routine to build a fake fit binary sub fake_git { my ($version) = @_; my ( $fh, $filename ) = tempfile( DIR => tempdir( CLEANUP => 1 ), UNLINK => 1, ( SUFFIX => '.bat' )x!! MSWin32, ); print {$fh} MSWin32 ? << "WIN32" : << "UNIX"; \@echo git version $version WIN32 #!$^X print "git version $version\\n" UNIX close $fh; chmod 0755, $filename; return $filename; } Git-Repository-1.325/t/sudo.pl0000644000175000017500000000016514055265171014654 0ustar bookbook#!/usr/bin/env perl # a tiny fake git wrapper print "@ARGV" =~ /git.*version/ ? "git version 9.8.7\n" : "@ARGV\n"; Git-Repository-1.325/t/10-new_fail.t0000644000175000017500000000431614055265171015536 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use Cwd qw( abs_path ); use File::Temp qw( tempdir ); use File::Spec; use File::Path; use Git::Repository; plan tests => 12; # a place to put a git repository my $dir = abs_path( tempdir( CLEANUP => 1 ) ); my $missing = File::Spec->catdir( $dir, 'missing' ); my $gitdir = File::Spec->catdir( $dir, '.git' ); # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; # FAIL - missing repository directory ok( !eval { Git::Repository->new( git_dir => $missing ) }, 'Missing repository directory' ); like( $@, qr/^directory not found: (?:\Q$missing\E)? /, '... expected error message' ); # FAIL - missing working copy directory ok( !eval { Git::Repository->new( work_tree => $missing ) }, 'Missing work_tree directory' ); like( $@, qr/^directory not found: (?:\Q$missing\E)? /, '... expected error message' ); # FAIL - repository is not a git repository ok( !eval { Git::Repository->new( git_dir => $dir ) }, 'repository directory is not a git repository' ); like( $@, qr/^fatal: not a git repository/i, # error from git itself '... expected error message' ); # FAIL - working copy is not a git working copy SKIP: { my $tmp = File::Spec->tmpdir(); skip "$tmp is already a working copy for some git repository", 2 if eval { Git::Repository->new( work_tree => $tmp ) }; ok( !eval { Git::Repository->new( work_tree => $dir ) }, 'work_tree directory is not a git working copy' ); like( $@, qr/^fatal: not a git repository/i, # error from git itself '... expected error message' ); } # FAIL - working copy is not a git working copy mkpath($gitdir); ok( !eval { Git::Repository->new( work_tree => $dir, git_dir => $gitdir ); }, 'work_tree\'s repository directory is not a git repository' ); like( $@, qr/^fatal: not a git repository/i, # error from git itself '... expected error message' ); # FAIL - extra parameters ok( !eval { Git::Repository->new( work_tree => $dir, extra => 'stuff' ); }, 'unknown extra parameter' ); like( $@, qr/^Unknown parameters: extra /, '... expected error message' ); Git-Repository-1.325/t/00-compile.t0000644000175000017500000000276314055265171015405 0ustar bookbookuse 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.058 use Test::More; plan tests => 4 + ($ENV{AUTHOR_TESTING} ? 1 : 0); my @module_files = ( 'Git/Repository.pm', 'Git/Repository/Command.pm', 'Git/Repository/Plugin.pm', 'Test/Git.pm' ); # no fake home requested my @switches = ( -d 'blib' ? '-Mblib' : '-Ilib', ); use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-e', "require q[$lib]")) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ) if $ENV{AUTHOR_TESTING}; Git-Repository-1.325/t/21-submodule.t0000644000175000017500000000410014055265171015742 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use Git::Repository; test_requires_git '1.5.3.rc0'; # first git submodule appearance plan skip_all => "git clone fails for git between 1.5.4.rc0 and 1.6.0.rc0" if Git::Repository->version_le('1.6.0.rc0') && Git::Repository->version_ge('1.5.4.rc0'); plan skip_all => "git submodule add with a non-existing path fails for git between 1.7.0.rc1 and 1.7.0.2" if Git::Repository->version_le('1.7.0.2') && Git::Repository->version_ge('1.7.0.rc1'); plan skip_all => "Removing environment variables requires System::Command 1.04, this is only $System::Command::VERSION" if $System::Command::VERSION < 1.04; plan tests => 1; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_AUTHOR_NAME} = 'Test Author'; $ENV{GIT_AUTHOR_EMAIL} = 'test.author@example.com'; $ENV{GIT_COMMITTER_NAME} = 'Test Committer'; $ENV{GIT_COMMITTER_EMAIL} = 'test.committer@example.com'; # create a small repository my @init; push @init, init => [ '-q' ] if Git::Repository->version_ge('1.5.2.3'); my $s = test_repository(@init); my $blob = $s->run( qw( hash-object -t blob -w --stdin ), { input => 'hello' } ); my $tree = $s->run( mktree => { input => "100644 blob $blob\thello" } ); my $commit = $s->run( 'commit-tree' => $tree, { input => 'empty tree' } ); $s->run( 'update-ref', 'refs/heads/master' => $commit ); $s->run( checkout => 'master', { quiet => 1 } ); # now test adding a submodule my $r = test_repository(@init); $r->run( submodule => add => $s->work_tree => 'sub', { env => { GIT_WORK_TREE => undef } } ); # the result of git submodule add has changed over time my $expected = $r->version_lt('1.5.3.rc1') ? " $commit sub" : $r->version_lt('1.5.4.4') ? " $commit sub (undefined)" : $r->version_lt('1.7.6.1') ? "-$commit sub" : " $commit sub (heads/master)"; # do the test my $status = $r->run( 'submodule', 'status', 'sub' ); is( $status, $expected, 'git submodule status' ); Git-Repository-1.325/t/config0000644000175000017500000000007514055265171014535 0ustar bookbook[user] name = Philippe Bruhat (BooK) email = book@cpan.org Git-Repository-1.325/t/06-version.t0000644000175000017500000001212014055265171015434 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use Git::Repository; # get the git version my ($version) = Git::Repository->run('--version') =~ /git version (.*)/g; diag "git version $version"; # other versions based on the current one my ( @lesser, @greater ); if ( $version =~ /^([1-9]+(?:\.[0-9]+)*)/ ) { # pick meaningful bits my @version = split /\./, $1; for ( 0 .. $#version ) { local $" = '.'; my @v = @version; $v[$_]++; push @greater, "@v"; next if 0 > ( $v[$_] -= 2 ); push @lesser, "@v"; } } # more complex comparisons my @true = ( [ '1.7.2.rc0.13.gc9eaaa', 'version_eq', '1.7.2.rc0.13.gc9eaaa' ], [ '1.7.2.rc0.13.gc9eaaa', 'version_ge', '1.7.2.rc0.13.gc9eaaa' ], [ '1.7.2.rc0.13.gc9eaaa', 'version_le', '1.7.2.rc0.13.gc9eaaa' ], [ '1.7.1', 'version_gt', '1.7.1.rc0' ], [ '1.7.1.rc1', 'version_gt', '1.7.1.rc0' ], [ '1.3.2', 'version_gt', '0.99' ], [ '1.7.2.rc0.13.gc9eaaa', 'version_gt', '1.7.0.4' ], [ '1.7.1.rc2', 'version_gt', '1.7.1.rc1' ], [ '1.7.2.rc0.1.g078e', 'version_gt', '1.7.2.rc0' ], [ '1.7.2.rc0.10.g1ba5c', 'version_gt', '1.7.2.rc0.1.g078e' ], [ '1.7.1.1', 'version_gt', '1.7.1.1.gc8c07' ], [ '1.7.1.1', 'version_gt', '1.7.1.1.g5f35a' ], [ '1.0.0b', 'version_gt', '1.0.0a' ], [ '1.0.3', 'version_gt', '1.0.0a' ], [ '1.7.0.4', 'version_ne', '1.7.2.rc0.13.gc9eaaa' ], [ '1.7.1.rc1', 'version_ne', '1.7.1.rc2' ], [ '1.0.0a', 'version_ne', '1.0.0' ], [ '1.4.0.rc1', 'version_le', '1.4.1' ], [ '1.0.0a', 'version_gt', '1.0.0' ], [ '1.0.0a', 'version_lt', '1.0.3' ], [ '1.0.0a', 'version_eq', '1.0.1' ], [ '1.0.0b', 'version_eq', '1.0.2' ], # the 0.99 series [ '0.99', 'version_lt', '1.0.2' ], [ '0.99', 'version_lt', '0.99.7a' ], [ '0.99.9c', 'version_lt', '0.99.9g' ], [ '0.99.7c', 'version_lt', '0.99.7d' ], [ '0.99.7c', 'version_lt', '0.99.8' ], [ '1.0.rc2', 'version_eq', '0.99.9i' ], # non-standard versions [ '1.7.1.236.g81fa0', 'version_gt', '1.7.1' ], [ '1.7.1.236.g81fa0', 'version_lt', '1.7.1.1' ], [ '1.7.1.211.g54fcb21', 'version_gt', '1.7.1.209.gd60ad81' ], [ '1.7.1.211.g54fcb21', 'version_ge', '1.7.1.209.gd60ad81' ], [ '1.7.1.209.gd60ad81', 'version_lt', '1.7.1.1.1.g66bd8ab' ], [ '1.7.0.2.msysgit.0', 'version_gt', '1.6.6' ], [ '1.7.1', 'version_lt', '1.7.1.1.gc8c07' ], [ '1.7.1', 'version_lt', '1.7.1.1.g5f35a' ], [ '1.7.1.1', 'version_gt', '1.7.1.1.gc8c07' ], [ '1.7.1.1', 'version_gt', '1.7.1.1.g5f35a' ], [ '1.7.1.1.gc8c07', 'version_eq', '1.7.1.1.g5f35a' ], [ '1.3.GIT', 'version_gt', '1.3.0' ], [ '1.3.GIT', 'version_lt', '1.3.1' ], ); # operator reversal: $a op $b <=> $b rop $a my %reverse = ( version_eq => 'version_eq', version_ne => 'version_ne', version_ge => 'version_le', version_gt => 'version_lt', version_le => 'version_ge', version_lt => 'version_gt', ); my %negate = ( version_ne => 'version_eq', version_eq => 'version_ne', version_ge => 'version_lt', version_gt => 'version_le', version_le => 'version_gt', version_lt => 'version_ge', ); @true = ( @true, map { [ $_->[2], $reverse{ $_->[1] }, $_->[0], $_->[3] || () ] } @true ); plan tests => 5 + 6 * @lesser + 6 * @greater + 2 * @true; my $r = 'Git::Repository'; # version is( Git::Repository->version(), $version, "git version $version" ); # version_eq ok( $r->version_eq($version), "$version version_eq $version" ); ok( !$r->version_eq($_), "$version not version_eq $_" ) for @greater, @lesser; # version_ne ok( $r->version_ne($_), "$version version_ne $_" ) for @greater, @lesser; ok( !$r->version_ne($version), "$version not version_ne $version" ); # version_gt ok( $r->version_gt($_), "$version version_gt $_" ) for @lesser; ok( !$r->version_gt($_), "$version not version_gt $_" ) for @greater; # version_le ok( $r->version_lt($_), "$version version_lt $_" ) for @greater; ok( !$r->version_lt($_), "$version not version_lt $_" ) for @lesser; # version_le ok( $r->version_le($_), "$version version_le $_" ) for $version, @greater; ok( !$r->version_le($_), "$version not version_le $_" ) for @lesser; # version_ge ok( $r->version_ge($_), "$version version_ge $_" ) for $version, @lesser; ok( !$r->version_ge($_), "$version not version_ge $_" ) for @greater; # test a number of special cases my $dev; { package Git::Repository::VersionFaker; our @ISA = qw( Git::Repository ); sub version { return $dev } } $r = 'Git::Repository::VersionFaker'; for (@true) { ( $dev, my $meth, my $v ) = @$_; ok( $r->$meth($v), "$dev $meth $v" ); $meth = $negate{$meth}; ok( !$r->$meth($v), "$dev not $meth $v" ); } Git-Repository-1.325/t/release-distmeta.t0000644000175000017500000000040114055265171016753 0ustar bookbook#!perl BEGIN { unless ($ENV{RELEASE_TESTING}) { print qq{1..0 # SKIP these tests are for release candidate testing\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::MetaTests. use Test::CPAN::Meta; meta_yaml_ok(); Git-Repository-1.325/t/Git/0000755000175000017500000000000014055265171014066 5ustar bookbookGit-Repository-1.325/t/Git/Repository/0000755000175000017500000000000014055265171016245 5ustar bookbookGit-Repository-1.325/t/Git/Repository/Plugin/0000755000175000017500000000000014055265171017503 5ustar bookbookGit-Repository-1.325/t/Git/Repository/Plugin/Hello2.pm0000644000175000017500000000045714055265171021174 0ustar bookbookpackage Git::Repository::Plugin::Hello2; use strict; use warnings; use Git::Repository::Plugin; our @ISA = qw( Git::Repository::Plugin ); our @KEYWORDS = qw( hello hello_worktree ); sub hello { return "Hello, world!\n" } sub hello_worktree { return "Hello, " . $_[0]->work_tree . "!\n"; } 1; Git-Repository-1.325/t/Git/Repository/Plugin/Hello.pm0000644000175000017500000000045514055265171021110 0ustar bookbookpackage Git::Repository::Plugin::Hello; use strict; use warnings; use Git::Repository::Plugin; our @ISA = qw( Git::Repository::Plugin ); sub _keywords { qw( hello hello_gitdir ) } sub hello { return "Hello, git world!\n" } sub hello_gitdir { return "Hello, " . $_[0]->git_dir . "!\n"; } 1; Git-Repository-1.325/t/author-pod-syntax.t0000644000175000017500000000045414055265171017141 0ustar bookbook#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); Git-Repository-1.325/t/26-overloaded_objects.t0000644000175000017500000000211214055265171017606 0ustar bookbook# Test that we work with stringified path objects like Path::Class. use strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw(tempdir); use Cwd qw(realpath); test_requires_git '1.6.5'; plan tests => 3; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; # A class with stringification to test with. { package My::Dir; use overload '""' => sub { $_[0]->{path} }, "fallback" => 1; sub new { my $class = shift; my $path = shift; return bless { path => $path }, $class; } } # a quiet git init: my @init = qw( init ); push @init, '-q' if Git::Repository->version_ge('1.5.2.3'); my $repo_dir = My::Dir->new( tempdir( CLEANUP => 1 ) ); note( Git::Repository->run( @init, $repo_dir ) ); ok -d "$repo_dir/.git", "git repo initialized"; my $r = eval { Git::Repository->new( work_tree => $repo_dir ); }; isa_ok $r, "Git::Repository"; is $r->work_tree, realpath($repo_dir), $repo_dir; Git-Repository-1.325/t/25-plugins.t0000644000175000017500000000644714055265171015450 0ustar bookbookuse strict; use warnings; use lib 't'; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw( tempdir ); use File::Spec; use Cwd qw( cwd abs_path ); use Git::Repository; test_requires_git '1.5.0.rc1'; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; plan tests => my $tests; # first create a new empty repository my @init; push @init, init => [ '-q' ] if Git::Repository->version_ge('1.5.2.3'); my $r = test_repository( @init ); my $dir = $r->work_tree; my $gitdir = $r->git_dir; # FAIL - no hello method BEGIN { $tests += 1 } ok( !eval { $r->hello }, 'No hello() method' ); # PASS - load Hello BEGIN { $tests += 1 } use_ok( 'Git::Repository', 'Hello' ); # PASS - new methods BEGIN { $tests += 4 } ok( my $got = eval { $r->hello }, 'hello() method is there' ); diag $@ if $@; is( $got, "Hello, git world!\n", '... with expected value' ); ok( $got = eval { $r->hello_gitdir }, 'hello_gitdir() method is there' ); diag $@ if $@; is( $got, "Hello, $gitdir!\n", '... with expected value' ); # FAIL - can't load this plugin BEGIN { $tests += 2 } ok( !eval q{use Git::Repository 'DoesNotExist'; 2;}, 'Failed to load inexistent plugin' ); like( $@, qr{^Can't locate Git/Repository/Plugin/DoesNotExist\.pm }, '... expected error message' ); # PASS - load Hello2 and throw various warnings my @warnings; { BEGIN { $tests += 5 } local $SIG{__WARN__} = sub { push @warnings, shift }; use_ok( 'Git::Repository', [ Hello2 => 'hello', 'zlonk' ] ); is( scalar @warnings, 3, 'Got 3 warnings' ); like( $warnings[0], qr/^Use of \@KEYWORDS by Git::Repository::Plugin::Hello2 is deprecated /, '... deprecation warning' ); like( $warnings[1], qr/^Unknown keyword 'zlonk' in Git::Repository::Plugin::Hello2 /, '... unknown keyword' ); like( $warnings[2], qr/^Subroutine (Git::Repository::)?hello redefined /, '... redefined method warning' ); @warnings = (); BEGIN { $tests += 5 } use_ok( 'Git::Repository', [ Hello2 => 'bam' ] ); is( scalar @warnings, 3, 'Got 3 warnings' ); like( $warnings[0], qr/^Use of \@KEYWORDS by Git::Repository::Plugin::Hello2 is deprecated /, '... deprecation warning' ); like( $warnings[1], qr/^Unknown keyword 'bam' in Git::Repository::Plugin::Hello2 /, '... unknown keyword' ); like( $warnings[2], qr/^No keywords installed from Git::Repository::Plugin::Hello2 /, '... no valid keyword left' ); @warnings = (); } # PASS - new methods BEGIN { $tests += 4 } ok( $got = eval { $r->hello }, 'hello() method is there' ); diag $@ if $@; is( $got, "Hello, world!\n", '... with new value' ); ok( !eval { $r->hello_worktree }, 'hello_worktree() method is not there' ); like( $@, qr/^Can't locate object method "hello_worktree" via package "Git::Repository" /, '... expected error message' ); # PASS - load a fully qualified plgin class BEGIN { $tests += 3 } use_ok( 'Git::Repository', '+MyGit::Hello' ); ok( $got = eval { $r->myhello }, 'myhello() method is there' ); diag $@ if $@; is( $got, "Hello, my git world!\n", '... with expected value' ); Git-Repository-1.325/t/20-simple.t0000644000175000017500000002511214055265171015241 0ustar bookbookuse strict; use warnings; use Test::More; use Test::Requires::Git; use Test::Git; use File::Temp qw( tempdir ); use File::Spec; use Cwd qw( cwd abs_path ); use Git::Repository; test_requires_git '1.5.5'; my $version = Git::Repository->version; plan tests => my $tests; # clean up the environment delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{LC_ALL} = 'C'; $ENV{GIT_AUTHOR_NAME} = 'Test Author'; $ENV{GIT_AUTHOR_EMAIL} = 'test.author@example.com'; $ENV{GIT_COMMITTER_NAME} = 'Test Committer'; $ENV{GIT_COMMITTER_EMAIL} = 'test.committer@example.com'; $ENV{GIT_CONFIG_NOSYSTEM} = 1; delete $ENV{XDG_CONFIG_HOME}; delete $ENV{HOME}; my $home = cwd; local $/ = chr rand 128; # small helper sub sub update_file { my ( $file, $content ) = @_; open my $fh, '>', $file or die "Can't open $file: $!"; print {$fh} $content; close $fh; } # a place to put a git repository my $dir = abs_path( tempdir( CLEANUP => 1 ) ); # a quiet git init: my @init = qw( init ); push @init, '-q' if Git::Repository->version_ge('1.5.2.3'); # PASS - non-existent directory BEGIN { $tests += 3 } chdir $dir; Git::Repository->run( @init ); my $r = Git::Repository->new(); isa_ok( $r, 'Git::Repository' ); chdir $home; is( $r->work_tree, $dir, 'work tree' ); my $gitdir = $r->run( qw( rev-parse --git-dir ) ); $gitdir = File::Spec->catfile( $dir, $gitdir ) if ! File::Spec->file_name_is_absolute( $gitdir ); is( $gitdir, $r->git_dir, 'git-dir' ); # check usage exit code BEGIN { $tests += 2 } ok( ! eval { $r->run( qw( commit --bonk ) ); }, "FAIL with usage text" ); like( $@, qr/^usage: .*?git[- ]commit/m, '... expected usage message' ); # add file to the index update_file( File::Spec->catfile( $dir, 'readme.txt' ), << 'TXT' ); Some readme text for our example TXT $r->run( add => 'readme.txt' ); # unset all editors delete @ENV{qw( EDITOR VISUAL GIT_EDITOR )}; SKIP: { BEGIN { $tests += 2 } skip "these tests require git >= 1.6.6, but we only have $version", 2 if Git::Repository->version_lt('1.6.6'); skip "editor defined directly in .gitconfig", 2 if $r->run( config => 'core.editor' ); skip "this test does not work with msysgit on Win32", 2 if $^O eq 'MSWin32'; ok( !eval { $r->run( var => 'GIT_EDITOR' ); 1; }, 'git var GIT_EDITOR' ); like( $@, qr/^fatal: Terminal is dumb, but EDITOR unset /, 'Git complains about lack of smarts and editor' ); } # with git commit it's not fatal BEGIN { $tests += 4 } SKIP: { skip "editor defined directly in .gitconfig", 4 if $r->run( config => 'core.editor' ); skip "this test does not work with msysgit on Win32", 4 if $^O eq 'MSWin32'; ok( my $cmd = $r->command('commit', '--no-verify'), 'git commit' ); isa_ok( $cmd, 'Git::Repository::Command' ); local $/ = "\n"; my $error = $cmd->stderr->getline; my $git = Git::Repository::Command::_is_git('git'); is_deeply( [ $cmd->cmdline ], [ $git, 'commit', '--no-verify' ], 'command-line' ); $cmd->close; like( $error, qr/^(?:error: )?Terminal is dumb/, 'Git complains about lack of smarts and editor' ); } # commit again BEGIN { $tests += 1 } my $message = 'a readme file'; $r->run( commit => '-m', $message ); my @log = $r->run( log => '--pretty=format:%s' ); is_deeply( \@log, [$message], 'git commit ; git log' ); # test callbacks BEGIN { $tests += 2 } @log = $r->run( log => '--pretty=format:%s', sub { ~~ reverse } ); is_deeply( \@log, [ ~~ reverse $message ], 'run() with 1 callback' ); sub rot13 { $_[0] =~ y/a-z/n-za-m/; $_[0] } @log = $r->run( log => '--pretty=format:%s', \&rot13, sub { ~~ reverse } ); is_deeply( \@log, [ ~~ reverse rot13 $message ], 'run() with 2 callback' ); # use commit-tree with input option BEGIN { $tests += 4 } my $parent = $r->run( log => '--pretty=format:%H' ); like( $parent, qr/^[a-f0-9]{40}$/, 'parent commit id' ); my $tree = $r->run( log => '--pretty=format:%T' ); like( $parent, qr/^[a-f0-9]{40}$/, 'parent tree id' ); my $commit; $commit = $r->run( 'commit-tree' => $tree, '-p', $parent, { input => "$message $tree" }, ); like( $commit, qr/^[a-f0-9]{40}$/, 'new commit id' ); cmp_ok( $commit, 'ne', $parent, 'new commit id is different from parent id' ); $r->run( reset => $commit ); # process "long" output BEGIN { $tests += 3 } { my $lines; my $cmd = $r->command( log => '--pretty=oneline', '--all' ); isa_ok( $cmd, 'Git::Repository::Command' ); my $git = Git::Repository::Command::_is_git('git'); is_deeply( [ $cmd->cmdline ], [ $git, qw( log --pretty=oneline --all ) ], 'command-line' ); my $log = $cmd->stdout; local $/ = "\n"; while (<$log>) { $lines++; } is( $lines, 2, 'git log' ); # no call to close, we count on DESTROY } # use command as a class method, with cwd option BEGIN { $tests += 2 } { my $cmd = Git::Repository->command( { cwd => $dir }, log => '-1', '--pretty=format:%H' ); isa_ok( $cmd, 'Git::Repository::Command' ); local $/ = "\n"; my $line = $cmd->stdout->getline(); chomp $line; is( $line, $commit, 'git log -1' ); } # use command as a class method, with env option BEGIN { $tests += 2 } { my $cmd = Git::Repository->command( { env => { GIT_DIR => $gitdir } }, log => '-1', '--pretty=format:%H' ); isa_ok( $cmd, 'Git::Repository::Command' ); local $/ = "\n"; my $line = $cmd->stdout->getline(); chomp $line; is( $line, $commit, 'git log -1' ); $cmd->stdout->close; $cmd->stderr->close; } # FAIL - run a command in a non-existent directory BEGIN { $tests += 2 } ok( !eval { $r->run( log => '-1', { cwd => File::Spec->catdir( $dir, 'not-there' ) }, bless( {}, 'Foo' ) # will be ignored silently ); }, 'Fail with option { cwd => non-existent dir }' ); like( $@, qr/^Can't chdir to .*not-there/, '... expected error message' ); # FAIL - pass more than one Git::Repository to Git::Repository::Command BEGIN { $tests += 2 } ok( !eval { $r->run( 'version', bless( { work_tree => 'TEH FAIL' }, 'Git::Repository' ) ); }, 'Fail with more than one Git::Repository object' ); like( $@, qr/^Too many Git::Repository objects given: /, '... expected error message' ); # now work with GIT_DIR and GIT_WORK_TREE only BEGIN { $tests += 1 } { local %ENV = %ENV; $ENV{GIT_DIR} = $gitdir; my $got = Git::Repository->run( log => '-1', '--pretty=format:%H' ); is( $got, $commit, 'git log -1' ); } # PASS - try with a relative dir BEGIN { $tests += 3 } chdir $dir; $r = Git::Repository->new( work_tree => '.' ); isa_ok( $r, 'Git::Repository' ); chdir $home; is( $r->work_tree, $dir, 'work tree' ); is( $r->git_dir, $gitdir, 'git dir' ); # PASS - try with a no dir BEGIN { $tests += 3 } chdir $dir; $r = Git::Repository->new(); isa_ok( $r, 'Git::Repository' ); chdir $home; is( $r->work_tree, $dir, 'work tree' ); is( $r->git_dir, $gitdir, 'git dir' ); # PASS - pass the git binary as an option to new() BEGIN { $tests += 9 } { my $abs_git = File::Spec->rel2abs( Git::Repository::Command::_which('git') ); # produce a minimal PATH, but # - keep the Windows PATH (MSWin32) # - keep the directory containing `pwd` (Unix) my $path_sep = $Config::Config{path_sep}; local $ENV{PATH} = join $path_sep, $^O eq 'MSWin32' ? grep { /\Q$ENV{SYSTEMROOT}\E/ } split $path_sep, $ENV{PATH} : grep { -x File::Spec->catfile( $_, 'pwd' ) } split $path_sep, $ENV{PATH}; $r = Git::Repository->new( git_dir => $gitdir, { git => $abs_git } ); isa_ok( $r, 'Git::Repository' ); is( $r->work_tree, $dir, 'work tree (git_dir, no PATH, git option)' ); is( $r->git_dir, $gitdir, 'git dir (git_dir, no PATH, git option)' ); $r = Git::Repository->new( work_tree => $dir, { git => $abs_git } ); isa_ok( $r, 'Git::Repository' ); is( $r->work_tree, $dir, 'work tree (work_tree, no PATH, git option)' ); is( $r->git_dir, $gitdir, 'git dir (work_tree, no PATH, git option)' ); chdir $dir; $r = Git::Repository->new( { git => $abs_git } ); isa_ok( $r, 'Git::Repository' ); chdir $home; is( $r->work_tree, $dir, 'work tree (no PATH, git option)' ); is( $r->git_dir, $gitdir, 'git dir (no PATH, git option)' ); } # PASS - use an option HASH BEGIN { $tests += 3 } is( Git::Repository->options(), undef, 'No options on the class' ); $r = Git::Repository->new( work_tree => $dir, { env => { GIT_AUTHOR_NAME => 'Example author', GIT_AUTHOR_EMAIL => 'author@example.com' } }, ); update_file( my $file = File::Spec->catfile( $dir, 'other.txt' ), << 'TXT' ); Some other text forcing an author TXT $r->run( add => $file ); $r->run( commit => '-m', 'Test option hash in new()' ); my ($author) = grep {/^Author:/} $r->run( log => '-1', '--pretty=medium' ); is( $author, 'Author: Example author ', 'Option hash in new()' ); update_file( $file, << 'TXT' ); Some other text forcing another author TXT $r->run( commit => '-a', '-m', 'Test option hash in run()', { env => { GIT_AUTHOR_EMAIL => 'fail@fail.com' } }, # ignored silently { env => { GIT_AUTHOR_EMAIL => 'example@author.com' } } # not ignored ); ($author) = grep {/^Author:/} $r->run( log => '-1', '--pretty=medium' ); is( $author, 'Author: Example author ', 'Option hash in new() and run()' ); # FAIL - use more than one option HASH BEGIN { $tests += 2 } ok( !eval { $r = Git::Repository->new( work_tree => $dir, { env => { GIT_AUTHOR_NAME => 'Example author' } }, { git => '/bin/false' } ); }, 'new() dies when given more than one option HASH' ); like( $@, qr/^Too many option hashes given: /, '... expected error message' ); # PASS - use an option HASH (no env key) BEGIN { $tests += 2 } ( $parent, $tree ) = split /-/, $r->run( log => '--pretty=format:%H-%T', -1 ); ok( $r = eval { Git::Repository->new( work_tree => $dir, { input => 'a dumb way to set log message' }, ); }, 'Git::Repository->new()' ); $commit = $r->run( 'commit-tree', $tree, '-p', $parent ); my $log = $r->run( log => '--pretty=format:%s', -1, $commit, { input => undef } ); is( $log, 'a dumb way to set log message', 'Option hash in new() worked' ); # PASS - create the empty tree BEGIN { $tests += 2 } ok( $r = eval { Git::Repository->new( work_tree => $dir ) }, 'Git::Repository->new()' ); $tree = $r->run( mktree => { input => '' } ); is( $tree, '4b825dc642cb6eb9a060e54bf8d69288fbee4904', 'mktree empty tree' ); Git-Repository-1.325/META.json0000644000175000017500000000700614055265171014524 0ustar bookbook{ "abstract" : "Perl interface to Git repositories", "author" : [ "Philippe Bruhat (BooK) " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010", "keywords" : [ "git", "wrapper" ], "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Git-Repository", "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Pod::Coverage::TrustPod" : "0", "Test::CPAN::Meta" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08" } }, "runtime" : { "requires" : { "Carp" : "0", "Cwd" : "0", "Exporter" : "0", "File::Spec" : "0", "File::Spec::Functions" : "0", "File::Temp" : "0", "Git::Version::Compare" : "1.001", "IO::Handle" : "0", "Scalar::Util" : "0", "System::Command" : "1.118", "Test::Builder" : "0", "namespace::clean" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Path" : "0", "File::Spec" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Test::More" : "0", "Test::Requires::Git" : "1.005", "constant" : "0", "lib" : "0", "overload" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "bug-git-repository@rt.cpan.org", "web" : "http://rt.cpan.org/NoAuth/Bugs.html?Dist=Git-Repository" }, "repository" : { "type" : "git", "url" : "https://github.com/book/Git-Repository.git", "web" : "https://github.com/book/Git-Repository" } }, "version" : "1.325", "x_contributors" : [ "H.Merijn Brand ", "Jan Pazdziora ", "Ron Savage ", "Samit Badle ", "Lisa Hansen ", "Alex Raguero ", "Gregor Herrmann ", "Petr \u0160abata ", "Tim Bunce ", "Aristotle Pagaltzis ", "Michael G. Schwern ", "Todd Rinaldo ", "Mark Lawrence ", "Dominic Humphries ", "Christian Walde ", "Yanick Champoux ", "Thomas Klausner ", "Olivier Mengu\u00e9 ", "Nigel Metheringham ", "Lasse Makholm ", "Kazuhiro Shibuya ", "Jacques Germishuys ", "Olivier Raginel ", "Anton Berezin ", "Alessandro Ghedini ", "Gustavo Leite de Mendon\u00e7a Chaves " ], "x_generated_by_perl" : "v5.28.1", "x_serialization_backend" : "JSON::XS version 3.04" } Git-Repository-1.325/eg/0000755000175000017500000000000014055265171013473 5ustar bookbookGit-Repository-1.325/eg/build-git0000755000175000017500000001770214055265171015310 0ustar bookbook#!/usr/bin/env perl use strict; use warnings; use Pod::Usage; use File::Spec; use File::Path qw( remove_tree ); use File::Temp qw( tempdir ); use Cwd qw( cwd ); use Getopt::Long; use Git::Repository; use Git::Version::Compare qw( cmp_git ); use System::Command 1.117; # loop_on # command-line options my %option = ( source => '/opt/src/git', destination => '/opt/git', limit => 0, verbose => 0, rc => 1, ); GetOptions( \%option, 'source=s', 'destination=s', 'verbose+', 'quiet', 'since=s', 'until=s', 'limit=i', 'force', 'list', 'missing', 'installed', 'rc!', 'fetch', 'test', 'docs', 'help', 'manual', ) or pod2usage( -verbose => 0 ); # simple help/manual pod2usage( -verbose => 1 ) if $option{help}; pod2usage( -verbose => 2 ) if $option{manual}; # verbosity options my %run_opt; $option{quiet} = 1 if $option{test}; $option{verbose} = 0 if $option{quiet}; $run_opt{stderr} = undef if !$option{verbose}; $run_opt{stdout} = sub { print shift } if $option{verbose} > 1; # git.git my $r = Git::Repository->new( work_tree => $option{source} ); # fetch recent commits $r->run( 'fetch' ) if $option{fetch}; # map version numbers to tags my %tag_for = map { ( my $v = substr $_, 1 ) =~ y/-/./; ( $v => $_ ) } grep /^v[^0]/ && !/^v1\.0rc/, # skip anything before 1.0 $r->run( tag => '-l', 'v*' ); # select the versions to build and install my @versions = sort cmp_git @ARGV ? @ARGV : keys %tag_for; # replace aliases with the canonical name { my %alias = ( '1.0.1' => '1.0.0a', '1.0.2' => '1.0.0b', ); my %seen; @versions = grep !$seen{$_}++, map $alias{$_} || $_, @versions; } @versions = grep !/rc/, @versions if !$option{rc}; @versions = grep !is_installed($_), @versions if $option{missing}; @versions = grep is_installed($_), @versions if $option{installed}; @versions = grep cmp_git( $option{since}, $_ ) <= 0, @versions if $option{since}; @versions = grep cmp_git( $_, $option{until} ) <= 0, @versions if $option{until}; @versions = $option{limit} > 0 ? @versions[ -$option{limit} .. -1 ] # most recent : @versions[ 0 .. -$option{limit} - 1 ] # most ancient if $option{limit}; # pick up invalid versions my @nope = grep !exists $tag_for{$_}, @versions; die "Can't compile non-existent versions: @nope\n" if @nope; # just list the selected versions print map "$_\n", @versions and exit if $option{list}; # test outputs TAP if ( $option{test} ) { require Test::More; import Test::More; plan( tests => scalar @versions ); $option{destination} = tempdir( CLEANUP => 1 ); } # build install select versions chdir $option{source} or die "Can't chdir to $option{source}: $!"; for my $version (@versions) { # skip if that git already exists (and runs) if ( is_installed($version) && !$option{force} ) { print "*** GIT $version ALREADY INSTALLED ***\n" if !$option{quiet}; next; } else { print "*** GIT $version ***\n" if !$option{quiet}; $r->run( checkout => '-f', '-q', $tag_for{$version} ); $r->run( clean => '-xqdf' ); # Fix various issues in the Git sources # Fix GIT-VERSION-GEN to use `git describe` instead of `git-describe` if ( cmp_git( $version, '1.3.3' ) <= 0 && cmp_git( '1.1.0', $version ) <= 0 && do { no warnings; `git-describe`; $? != 0 } ) { local ( $^I, @ARGV ) = ( '', 'GIT-VERSION-GEN' ); s/git-describe/git describe/, print while <>; } # fix GIT_VERSION in the Makefile if ( cmp_git( $version, '1.0.9' ) == 0 ) { local ( $^I, @ARGV ) = ( '', 'Makefile' ); s/^GIT_VERSION = .*/GIT_VERSION = $version/, print while <>; } # add missing #include elsif ( cmp_git( $version, '1.7.5.rc0' ) <= 0 && cmp_git( '1.7.4.2', $version ) <= 0 ) { $r->run( 'cherry-pick', '-n', 'ebae9ff95de2d0b36b061c7db833df4f7e01a41d' ); # force the expected version number my $version_file = File::Spec->catfile( $r->work_tree, 'version' ); open my $fh, '>', $version_file or die "Can't open $version_file: $!"; print $fh "$version\n"; } # settings my $prefix = File::Spec->catdir( $option{destination}, $version ); my @make = ( make => "prefix=$prefix" ); # clean up environment (possibly set by local::lib) local $ENV{PERL_MB_OPT}; local $ENV{PERL_MM_OPT}; remove_tree( $prefix ) if -e $prefix; # build run_cmd( @make => '-j3' ); # install run_cmd( @make => 'install' ); run_cmd( @make => 'install-doc' ) if $option{docs}; # test the installation and remove all if ( $option{test} ) { ok( is_installed($version), "$version installed successfully" ); remove_tree( $prefix ); } } } sub run_cmd { print "* @_\n" if !$option{quiet}; System::Command->loop_on( command => \@_, %run_opt ) or die "FAIL: @_\n"; } sub is_installed { my ($version) = @_; my $git = File::Spec->catfile( $option{destination}, $version, 'bin', 'git' ); return eval { Git::Repository->version_eq( $version, { git => $git } ) }; } __END__ =pod =head1 NAME build-git - Build and install any Git =head1 SYNOPSIS # clone git.git # build and install Git 1.7.2 $ build-git 1.7.2 # build and install all versions between 1.6.5 and 2.1.0 $ build-git --since 1.6.5 --until 2.1.0 # build and install all versions of Git (since 1.0.0) $ build-git # build and install the 5 most recent versions of the selection $ build-git --limit 5 ... # build and install the 5 most ancient versions of the selection $ build-git --limit -5 ... # fetch the latest commit and install the latest git $ build-git --fetch --limit 1 =head1 OPTIONS AND ARGUMENTS =head2 Options --source The location of the git.git clone checkout --destination The location of the Git collection --fetch Start by doing a `git fetch` --list List the selected versions and exit --test Compile, install, test and uninstall the selected versions. Outputs TAP. (Implies --quiet and force --destination to a temporary directory.) --since Select versions greater or equal to --until Select versions less or equal to --missing Select only non-installed versions --installed Select only installed versions (incompatible with the --missing option) --limit Limit the number of versions in the selection (if is positive, keep the most recent ones, if is negative, keep the oldest) --verbose Used once, shows the STDERR of the commands run to compile and install Git. Used twice, also shows the STDOUT. --quiet Silence all output, including the progress status =head2 Arguments If no argument is given, all versions are selected. =head1 DESCRIPTION B is a small utility to build and install any version of Git. It automatically applies some necessary patches that are needed to compile Git on recent systems. It is used to test the L module against all versions of Git. =head1 AUTHOR Philippe Bruhat (BooK) =head1 COPYRIGHT Copyright 2016 Philippe Bruhat (BooK), all rights reserved. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Git-Repository-1.325/Makefile.PL0000644000175000017500000000401014055265171015045 0ustar bookbook# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.012. use strict; use warnings; use 5.006; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Perl interface to Git repositories", "AUTHOR" => "Philippe Bruhat (BooK) ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Git-Repository", "LICENSE" => "perl", "MIN_PERL_VERSION" => "5.006", "NAME" => "Git::Repository", "PREREQ_PM" => { "Carp" => 0, "Cwd" => 0, "Exporter" => 0, "File::Spec" => 0, "File::Spec::Functions" => 0, "File::Temp" => 0, "Git::Version::Compare" => "1.001", "IO::Handle" => 0, "Scalar::Util" => 0, "System::Command" => "1.118", "Test::Builder" => 0, "namespace::clean" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "ExtUtils::MakeMaker" => 0, "File::Path" => 0, "File::Spec" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "Test::More" => 0, "Test::Requires::Git" => "1.005", "constant" => 0, "lib" => 0, "overload" => 0 }, "VERSION" => "1.325", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Carp" => 0, "Cwd" => 0, "Exporter" => 0, "ExtUtils::MakeMaker" => 0, "File::Path" => 0, "File::Spec" => 0, "File::Spec::Functions" => 0, "File::Temp" => 0, "Git::Version::Compare" => "1.001", "IO::Handle" => 0, "IPC::Open3" => 0, "Scalar::Util" => 0, "System::Command" => "1.118", "Test::Builder" => 0, "Test::More" => 0, "Test::Requires::Git" => "1.005", "constant" => 0, "lib" => 0, "namespace::clean" => 0, "overload" => 0, "strict" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Git-Repository-1.325/dist.ini0000644000175000017500000000457014055265171014552 0ustar bookbookname = Git-Repository author = Philippe Bruhat (BooK) license = Perl_5 copyright_holder = Philippe Bruhat (BooK) ; copyright_year = 2010-2013 [PkgVersion] [@Filter] -bundle = @Basic -remove = Readme [PruneFiles] filename = setup match = \.patch$ match = mess/.* match = cover_db [AutoPrereqs] [Prereqs] perl = 5.006 System::Command = 1.118 Git::Version::Compare = 1.001 [Prereqs / TestRequires] Test::Requires::Git = 1.005 [Test::ReportPrereqs] [MetaJSON] [MetaResources] repository.web = https://github.com/book/Git-Repository repository.url = https://github.com/book/Git-Repository.git repository.type = git bugtracker.web = http://rt.cpan.org/NoAuth/Bugs.html?Dist=Git-Repository bugtracker.mailto = bug-git-repository@rt.cpan.org [MetaTests] [PodSyntaxTests] [PodCoverageTests] [Test::Compile] [NextRelease] format = %v %{yyyy-MM-dd}d %P [@Git] changelog = Changes commit_msg = Changes for version %v tag_format = v%v tag_message = %N v%v push_to = origin push_to = github [Git::NextVersion] [Keywords] keywords = git wrapper [Meta::Contributors] contributor = H.Merijn Brand contributor = Jan Pazdziora contributor = Ron Savage contributor = Samit Badle contributor = Lisa Hansen contributor = Alex Raguero contributor = Gregor Herrmann contributor = Petr Šabata contributor = Tim Bunce contributor = Aristotle Pagaltzis contributor = Michael G. Schwern contributor = Todd Rinaldo contributor = Mark Lawrence contributor = Dominic Humphries contributor = Christian Walde contributor = Yanick Champoux contributor = Thomas Klausner contributor = Olivier Mengué contributor = Nigel Metheringham contributor = Lasse Makholm contributor = Kazuhiro Shibuya contributor = Jacques Germishuys contributor = Olivier Raginel contributor = Anton Berezin contributor = Alessandro Ghedini contributor = Gustavo Leite de Mendonça Chaves Git-Repository-1.325/MANIFEST0000644000175000017500000000141514055265171014232 0ustar bookbook# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012. Changes LICENSE MANIFEST META.json META.yml Makefile.PL README dist.ini eg/build-git lib/Git/Repository.pm lib/Git/Repository/Command.pm lib/Git/Repository/Plugin.pm lib/Git/Repository/Tutorial.pod lib/Test/Git.pm t/00-compile.t t/00-report-prereqs.dd t/00-report-prereqs.t t/05-try_git.t t/06-version.t t/07-version.t t/10-new_fail.t t/11-create.t t/12-create.t t/13-sudo.t t/20-simple.t t/21-submodule.t t/22-backward.t t/23-quiet.t t/24-errors.t t/25-plugins.t t/26-overloaded_objects.t t/30-test_repository.t t/Git/Repository/Plugin/Hello.pm t/Git/Repository/Plugin/Hello2.pm t/MyGit/Hello.pm t/author-pod-coverage.t t/author-pod-syntax.t t/config t/release-distmeta.t t/sudo.pl t/test-all-git.t Git-Repository-1.325/LICENSE0000644000175000017500000004371314055265171014115 0ustar bookbookThis software is copyright (c) 2021 by Philippe Bruhat (BooK). This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2021 by Philippe Bruhat (BooK). This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2021 by Philippe Bruhat (BooK). This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Git-Repository-1.325/lib/0000755000175000017500000000000014055265171013646 5ustar bookbookGit-Repository-1.325/lib/Test/0000755000175000017500000000000014055265171014565 5ustar bookbookGit-Repository-1.325/lib/Test/Git.pm0000644000175000017500000001556314055265171015660 0ustar bookbookpackage Test::Git; $Test::Git::VERSION = '1.325'; use strict; use warnings; use Exporter; use Test::Builder; use Git::Repository; # 1.15 use File::Temp qw( tempdir ); use File::Spec::Functions qw( catdir ); use Git::Version::Compare qw( cmp_git ); use Cwd qw( cwd ); use Carp; our @ISA = qw( Exporter ); our @EXPORT = qw( has_git test_repository ); my $Test = Test::Builder->new(); sub has_git { my ( $version, @options ) = ( ( grep !ref, @_ )[0], grep ref, @_ ); carp sprintf 'has_git() is obsolete, write `use Test::Requires::Git; test_requires_git%s;` instead', $version ? " '$version'" : ''; # check some git is present $Test->skip_all('Default git binary not found in PATH') if !Git::Repository::Command::_is_git('git'); # check it's at least some minimum version my $git_version = Git::Repository->version(@options); $Test->skip_all( "Test script requires git >= $version (this is only $git_version)") if $version && Git::Repository->version_lt( $version, @options ); } sub test_repository { my %args = @_; croak "Can't use both 'init' and 'clone' parameters" if exists $args{init} && exists $args{clone}; # setup some default values my $temp = $args{temp} || [ CLEANUP => 1 ]; # File::Temp options my $init = $args{init} || []; # git init options my $opts = $args{git} || {}; # Git::Repository options my $safe = { %$opts, fatal => [] }; # ignore 'fatal' settings my $clone = $args{clone}; # git clone options # version check my $bad_version; my $git_version = Git::Repository->version($safe); if ($clone) { if ( cmp_git( $git_version, '1.6.2.rc0' ) < 0 || ( cmp_git( '1.7.0.rc1', $git_version ) <= 0 && cmp_git( $git_version, '1.7.0.2' ) <= 0 ) ) { $bad_version = "test_repository( clone => ... ) requires git >= 1.6.2.rc0 and 1.7.0.rc1 < git < 1.7.0.2 (this is $git_version)"; } } elsif ( cmp_git( $git_version, '1.4.0' ) <= 0 ) { $bad_version = "test_repository( init => ... ) requires git >= 1.4.0 (this is only $git_version)"; } croak $bad_version if $bad_version; # create a temporary directory to host our repository my $dir = tempdir(@$temp); my $cwd = { cwd => $dir }; # option to chdir there # create the git repository there my $init_cmd = Git::Repository->version_lt('1.5.0.rc1') ? 'init-db' : 'init'; my @cmd = $clone ? ( clone => @$clone, $dir ) : ( $init_cmd => @$init, $cwd ); Git::Repository->run( @cmd, $safe ); # create the Git::Repository object my $gitdir = Git::Repository->run( qw( rev-parse --git-dir ), $cwd ); return Git::Repository->new( git_dir => catdir( $dir, $gitdir ), $opts ); } 1; __END__ =head1 NAME Test::Git - Helper functions for test scripts using Git =head1 SYNOPSIS use Test::More; use Test::Git; # check there is a git binary available, or skip all has_git(); # check there is a minimum version of git available, or skip all has_git( '1.6.5' ); # check the git we want to test has a minimum version, or skip all has_git( '1.6.5', { git => '/path/to/alternative/git' } ); # normal plan plan tests => 2; # create a new, empty repository in a temporary location # and return a Git::Repository object my $r = test_repository(); # clone an existing repository in a temporary location # and return a Git::Repository object my $c = test_repository( clone => [ $url ] ); # run some tests on the repository ... =head1 DESCRIPTION L provides a number of helpful functions when running test scripts that require the creation and management of a Git repository. =head1 EXPORTED FUNCTIONS =head2 has_git has_git( $version, \%options ); Checks if there is a git binary available, or skips all tests. If the optional L<$version> argument is provided, also checks if the available git binary has a version greater or equal to C<$version>. This function also accepts an option hash of the same kind as those accepted by L and L. This function must be called before C, as it performs a B if requirements are not met. C is now B and will print a warning when used. The C function provided by the L module is a much more flexible replacement. C will be removed in a future release. =head2 test_repository test_repository( %options ); Creates a new empty git repository in a temporary location, and returns a L object pointing to it. This function takes options as a hash. Each key will influence a different part of the creation process. The keys are: =over 4 =item temp Array reference containing parameters to L C function. Default: C<<[ CLEANUP => 1 ]>> =item init Array reference containing parameters to C. Must not contain the target directory parameter, which is provided by C (via L). Default: C<[]> The C option is only supported with Git versions higher or equal to 1.6.2.rc0. =item clone Array reference containing parameters to C. Must not contain the target directory parameter, which is provided by C (via L). Note that C and C are mutually exclusive and that C will croak if both are provided. This option has no default value, since at least a Git URL must be provided to the C option. The C option is only supported with Git versions higher or equal to 1.6.2.rc0. =item git Hash reference containing options for L. Default: C<{}> =back This call is the equivalent of the default call with no options: test_repository( temp => [ CLEANUP => 1 ], # File::Temp::tempdir options init => [], # git init options git => {}, # Git::Repository options ); To create a I repository: test_repository( init => [ '--bare' ] ); To leave the repository in its location after the end of the test: test_repository( temp => [ CLEANUP => 0 ] ); Note that since C uses C to create the test repository, it requires at least Git version C<1.5.0.rc1>. =head1 AUTHOR Philippe Bruhat (BooK) =head1 ACKNOWLEDGEMENTS The C option and capability of C owes a lot to Nathan Nutter (NNUTTER), who wanted to be able to clone into a test repository. =head1 SEE ALSO L. =head1 COPYRIGHT Copyright 2010-2016 Philippe Bruhat (BooK), all rights reserved. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Git-Repository-1.325/lib/Git/0000755000175000017500000000000014055265171014371 5ustar bookbookGit-Repository-1.325/lib/Git/Repository.pm0000644000175000017500000004710214055265171017112 0ustar bookbookpackage Git::Repository; $Git::Repository::VERSION = '1.325'; use warnings; use strict; use 5.006; use Carp; use File::Spec; use Cwd qw( cwd realpath ); use Git::Repository::Command; use Git::Version::Compare qw( :ops ); # helper function sub _abs_path { my ( $path, $base ) = @_; my $abs_path = File::Spec->rel2abs( $path, $base ); # normalize, but don't die on Win32 if the path doesn't exist eval { $abs_path = realpath($abs_path); }; return $abs_path; } use namespace::clean; # a few simple accessors for my $attr (qw( git_dir work_tree options )) { no strict 'refs'; *$attr = sub { return ref $_[0] ? $_[0]{$attr} : () }; } # backward compatible aliases sub repo_path { croak "repo_path() is obsolete, please use git_dir() instead"; } sub wc_path { croak "wc_path() is obsolete, please use work_tree() instead"; } # # support for loading plugins # sub import { my ( $class, @plugins ) = @_; for my $plugin (@plugins) { ( $plugin, my @names ) = @$plugin if ref $plugin; $plugin = substr( $plugin, 0, 1 ) eq '+' ? substr( $plugin, 1 ) : "Git::Repository::Plugin::$plugin"; eval "use $plugin; 1;" or croak $@; $plugin->install(@names); } } # # constructor-related methods # sub new { my ( $class, @arg ) = @_; # create the object my $self = bless {}, $class; # take out the option hash my ( $options, %arg ); { my @o; %arg = grep !( ref eq 'HASH' ? push @o, $_ : 0 ), @arg; croak "Too many option hashes given: @o" if @o > 1; $options = $self->{options} = shift @o || {}; } # ignore 'input' and 'fatal' options during object creation my $input = delete $options->{input}; my $fatal = delete $options->{fatal}; # die if deprecated parameters are given croak "repository is obsolete, please use git_dir instead" if defined delete $arg{repository}; croak "working_copy is obsolete, please use work_tree instead" if defined delete $arg{working_copy}; # setup default options my $git_dir = delete $arg{git_dir}; my $work_tree = delete $arg{work_tree}; croak "Unknown parameters: @{[keys %arg]}" if keys %arg; # compute the various paths my $cwd = defined $options->{cwd} ? $options->{cwd} : cwd(); # if work_tree or git_dir are relative, they are relative to cwd -d ( $git_dir = _abs_path( $git_dir, $cwd ) ) or croak "directory not found: $git_dir" if defined $git_dir; -d ( $work_tree = _abs_path( $work_tree, $cwd ) ) or croak "directory not found: $work_tree" if defined $work_tree; # if no cwd option given, assume we want to work in work_tree $cwd = defined $options->{cwd} ? $options->{cwd} : defined $work_tree ? $work_tree : cwd(); # we'll always have to compute it if not defined $self->{git_dir} = _abs_path( Git::Repository->run( qw( rev-parse --git-dir ), { %$options, cwd => $cwd } ), $cwd ) if !defined $git_dir; # there are 4 possible cases if ( !defined $work_tree ) { # 1) no path defined: trust git with the values # $self->{git_dir} already computed # 2) only git_dir was given: trust it $self->{git_dir} = $git_dir if defined $git_dir; # in a non-bare repository, the work tree is just above the gitdir if ( $self->run(qw( config --bool core.bare )) ne 'true' ) { $self->{work_tree} = _abs_path( File::Spec->updir, $self->{git_dir} ); } } else { # 3) only work_tree defined: if ( !defined $git_dir ) { # $self->{git_dir} already computed # check work_tree is the top-level work tree, and not a subdir my $cdup = Git::Repository->run( qw( rev-parse --show-cdup ), { %$options, cwd => $cwd } ); $self->{work_tree} = $cdup ? _abs_path( $cdup, $work_tree ) : $work_tree; } # 4) both path defined: trust the values else { $self->{git_dir} = $git_dir; $self->{work_tree} = $work_tree; } } # sanity check my $gitdir = eval { _abs_path( $self->run(qw( rev-parse --git-dir )), $cwd ) } || ''; croak "fatal: not a git repository: $self->{git_dir}" if $self->{git_dir} ne $gitdir; # put back the ignored options $options->{input} = $input if defined $input; $options->{fatal} = $fatal if defined $fatal; return $self; } # create() is now fully deprecated sub create { croak "create() is deprecated, see Git::Repository::Tutorial for better alternatives"; } # # command-related methods # # return a Git::Repository::Command object sub command { shift @_ if !ref $_[0]; # remove class name if called as class method return Git::Repository::Command->new(@_); } # run a command, returns the output # die with errput if any sub run { my ( $self, @cmd ) = @_; # split the args to get the optional callbacks my @cb; @cmd = grep { ref eq 'CODE' ? !push @cb, $_ : 1 } @cmd; local $Carp::CarpLevel = 1; # run the command (pass the instance if called as an instance method) my $command = Git::Repository::Command->new( ref $self ? $self : (), @cmd ); # return the output or die return $command->final_output(@cb); } # # version comparison methods # # NOTE: it doesn't make sense to try to cache the results of version(): # - yes, it will make faster benchmarks, but # - the 'git' option allows to change the git binary anytime # - version comparison is usually done once anyway sub version { return ( shift->run( '--version', grep { ref eq 'HASH' } @_ ) =~ /git version (.*)/g )[0]; } BEGIN { for my $op ( qw( lt gt le ge eq ne ) ) { no strict 'refs'; *{"version_$op"} = eval << "OP"; sub { my \$r = shift; my \@o; my (\$v) = grep !( ref && ref eq 'HASH' ? push \@o, \$_ : 0 ), \@_; return ${op}_git( \$r->version(\@o), \$v ); } OP } } 1; __END__ =head1 NAME Git::Repository - Perl interface to Git repositories =head1 SYNOPSIS use Git::Repository; # start from an existing repository $r = Git::Repository->new( git_dir => $gitdir ); # start from an existing working copy $r = Git::Repository->new( work_tree => $dir ); # start from a repository reachable from the current directory $r = Git::Repository->new(); # or init our own repository first Git::Repository->run( init => $dir, ... ); $r = Git::Repository->new( work_tree => $dir ); # or clone from a URL first Git::Repository->run( clone => $url, $dir, ... ); $r = Git::Repository->new( work_tree => $dir ); # provide an option hash for Git::Repository::Command # (see Git::Repository::Command for all available options) $r = Git::Repository->new( ..., \%options ); # run commands # - get the full output (no errput) passing options for this command only $output = $r->run( @cmd, \%options ); # - get the full output as a list of lines (no errput), with options @output = $r->run( @cmd, \%options ); # - process the output with callbacks $output = $r->run( @cmd, sub {...} ); @output = $r->run( @cmd, sub {...} ); # - obtain a Git::Repository::Command object # (see Git::Repository::Command for details) $cmd = $r->command( @cmd, \%options ); # obtain version information my $version = $r->version(); # compare current git version if ( $r->version_gt('1.6.5') ) { ...; } =head1 DESCRIPTION L is a Perl interface to Git, for scripted interactions with repositories. It's a low-level interface that allows calling any Git command, whether I or I, including bidirectional commands such as C. A L object simply provides context to the git commands being run. It is possible to call the C and C methods against the class itself, and the context (typically I) will be obtained from the options and environment. As a low-level interface, it provides no sugar for particular Git commands. Specifically, it will not prepare environment variables that individual Git commands may need or use. However, the C and C environment variables are special: if the command is run in the context of a L object, they will be overridden by the object's C and C attributes, respectively. It is still possible to override them if necessary, using the C option. L requires at least Git 1.5.0, and is expected to support any later version. See L for more code examples. =head1 CONSTRUCTOR =head2 new Git::Repository->new( %args, $options ); Create a new L object, based on an existing Git repository. Parameters are: =over 4 =item git_dir => $gitdir The location of the git repository (F<.git> directory or equivalent). For backward compatibility with versions 1.06 and before, C is accepted in place of C (but the newer name takes precedence). =item work_tree => $dir The location of the git working copy (for a non-bare repository). If C actually points to a subdirectory of the work tree, L will automatically recompute the proper value. For backward compatibility with versions 1.06 and before, C is accepted in place of C (but the newer name takes precedence). =back If none of the parameter is given, L will find the appropriate repository just like Git itself does. Otherwise, one of the parameters is usually enough, as L can work out where the other directory (if any) is. C also accepts a reference to an option hash which will be used as the default by L when working with the corresponding L instance. So this: my $r = Git::Repository->new( # parameters work_tree => $dir, # options { git => '/path/to/some/other/git', env => { GIT_COMMITTER_EMAIL => 'book@cpan.org', GIT_COMMITTER_NAME => 'Philippe Bruhat (BooK)', }, } ); is equivalent to explicitly passing the option hash to each C or C call. The documentation for L lists all available options. Note that Git::Repository and L take great care in finding the option hash wherever it may be in C<@_>, and to merge multiple option hashes if more than one is provided. It probably makes no sense to set the C option in C, but L won't stop you. Note that on some systems, some git commands may close standard input on startup, which will cause a C. L will raise an exception. To create a Git repository and obtain a L object pointing to it, simply do it in two steps: # run a clone or init command without an instance, # using options like cwd Git::Repository->run( ... ); # obtain a Git::Repository instance # on the resulting repository $r = Git::Repository->new( ... ); =head1 METHODS =begin Pod::Coverage create repo_path wc_path =end Pod::Coverage L supports the following methods: =head2 command Git::Repository->command( @cmd ); $r->command( @cmd ); Runs the git sub-command and options, and returns a L object pointing to the sub-process running the command. As described in the L documentation, C<@cmd> may also contain a hashref containing options for the command. =head2 run Git::Repository->run( @cmd ); $r->run( @cmd ); Runs the command and returns the output as a string in scalar context, or as a list of lines in list context. Also accepts a hashref of options. Lines are automatically Ced. In addition to the options hashref supported by L, the parameter list can also contain code references, that will be applied successively to each line of output. The line being processed is in C<$_>, but the coderef must still return the result string (like C). If the git command printed anything on stderr, it will be printed as warnings. For convenience, if the git sub-process exited with status C<128> (fatal error), or C<129> (usage message), C will C. The exit status values for which C dies can be modified using the C option (see L for details). The exit status of the command that was just run is accessible as usual using C<<< $? >> 8 >>>. See L for details about C<$?>. =head2 git_dir Returns the repository path. =head2 work_tree Returns the working copy path. Used as current working directory by L. =head2 options Return the option hash that was passed to C<< Git::Repository->new() >>. =head2 version Return the version of git, as given by C. =head2 Version-comparison "operators" Git evolves very fast, and new features are constantly added. To facilitate the creation of programs that can properly handle the wide variety of Git versions seen in the wild, a number of version comparison "operators" are available. They are named C> where I is the equivalent of the Perl operators C, C, C, C, C, C. They return a boolean value, obtained by comparing the version of the git binary and the version string passed as parameter. The methods are: =over 4 =item version_lt( $version ) =item version_gt( $version ) =item version_le( $version ) =item version_ge( $version ) =item version_eq( $version ) =item version_ne( $version ) =back All those methods also accept an option hash, just like the others. The actual version-comparison logic is managed by L. Check its documentation for details. =head1 PLUGIN SUPPORT L intentionally has only few methods. The idea is to provide a lightweight wrapper around git, to be used to create interesting tools based on Git. However, people will want to add extra functionality to L, the obvious example being a C method that returns simple objects with useful attributes. Taking the hypothetical C module which source code is listed in the previous reference, the methods it provides would be loaded and used as follows: use Git::Repository qw( Hello ); my $r = Git::Repository->new(); print $r->hello(); print $r->hello_gitdir(); It's possible to load only a selection of methods from the plugin: use Git::Repository [ Hello => 'hello' ]; my $r = Git::Repository->new(); print $r->hello(); # dies: Can't locate object method "hello_gitdir" print $r->hello_gitdir(); If your plugin lives in another namespace than C, just prefix the fully qualified class name with a C<+>. For example: use Git::Repository qw( +MyGit::Hello ); See L about how to create a new plugin. =head1 ACKNOWLEDGEMENTS Thanks to Todd Rinaldo, who wanted to add more methods to L, which made me look for a solution that would preserve the minimalism of L. The C<::Plugin> interface is what I came up with. =head1 OTHER PERL GIT WRAPPERS (a.k.a. SEE ALSO) (This section was written in June 2010. The other Git wrappers have probably evolved since that time.) A number of Perl git wrappers already exist. Why create a new one? I have a lot of ideas of nice things to do with Git as a tool to manipulate blobs, trees, and tags, that may or may not represent revision history of a project. A lot of those commands can output huge amounts of data, which I need to be able to process in chunks. Some of these commands also expect to receive input. What follows is a short list of "missing features" that I was looking for when I looked at the existing Git wrappers on CPAN. They are the "rational" reason for writing my own (the real reason being of course "I thought it would be fun, and I enjoyed doing it"). Even though it works well for me and others, L has its own shortcomings: it I a I, anything complex requires you to deal with input/output handles, it provides no high-level interface to generate actual Git commands or process the output of commands (but have a look at the plugins), etc. One the following modules may therefore be better suited for your needs, depending on what you're trying to achieve. =head2 Git.pm Git.pm was not on CPAN in 2010. It is packaged with Git, and installed with the system Perl libraries. Not being on CPAN made it harder to install in any Perl. It made it harder for a CPAN library to depend on it. It doesn't allow calling C or C. The C function especially has problems: L The L module from git.git was packaged as a CPAN distribution by MSOUTH in June 2013. =head2 Git::Class L depends on Moose, which seems an unnecessary dependency for a simple wrapper around Git. The startup penalty could become significant for command-line tools. Although it supports C and C (and has methods to call any Git command), it is mostly aimed at porcelain commands, and provides no way to control bidirectional commands (such as C). =head2 Git::Wrapper L doesn't support streams or bidirectional commands. =head2 Git::Sub (This description was added for completeness in May 2013.) L appeared in 2013, as a set of Git-specific L functions. It provide a nice set of C functions, and has some limitations (due to the way L itself works) which don't impact most Git commands. L doesn't support working with streams. =head2 Git::Raw (This description was added for completeness in September 2014, upon request of the author of L.) L provides bindings to L, a pure C implementation of the Git core methods. Most of the functions provided by libgit2 are available. If you have complex workflows, or even if speed is of the essence, this may be a more attractive solution than shelling out to git. =head1 BUGS Since version 1.17, L delegates the actual command execution to L, which has better support for Win32 since version 1.100. Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Git::Repository You can also look for information at: =over 4 =item * RT: CPAN's request tracker L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 AUTHOR Philippe Bruhat (BooK) =head1 COPYRIGHT Copyright 2010-2016 Philippe Bruhat (BooK), all rights reserved. =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Git-Repository-1.325/lib/Git/Repository/0000755000175000017500000000000014055265171016550 5ustar bookbookGit-Repository-1.325/lib/Git/Repository/Command.pm0000644000175000017500000003314314055265171020470 0ustar bookbookpackage Git::Repository::Command; $Git::Repository::Command::VERSION = '1.325'; use strict; use warnings; use 5.006; use Carp; use Cwd qw( cwd ); use IO::Handle; use Scalar::Util qw( blessed ); use File::Spec; use System::Command; our @ISA = qw( System::Command ); # a few simple accessors for my $attr (qw( pid stdin stdout stderr exit signal core )) { no strict 'refs'; *$attr = sub { return $_[0]{$attr} }; } for my $attr (qw( cmdline )) { no strict 'refs'; *$attr = sub { return @{ $_[0]{$attr} } }; } sub _which { my @pathext = (''); push @pathext, $^O eq 'MSWin32' ? split ';', $ENV{PATHEXT} : $^O eq 'cygwin' ? qw( .com .exe .bat ) : (); for my $path ( File::Spec->path ) { for my $ext (@pathext) { my $binary = File::Spec->catfile( $path, $_[0] . $ext ); return $binary if -x $binary && !-d _; } } # not found return undef; } # CAN I HAS GIT? my %binary; # cache calls to _is_git sub _is_git { my ( $binary, @args ) = @_; my $args = join "\0", @args; # git option might be an arrayref containing an executable with arguments # Best that can be done is to check if the first part is executable # and use the arguments as part of the cache key # compute cache key: # - filename (path-rel): $CWD \0 $PATH # - filename (path): $PATH # - absolute path (abs): empty string # - relative path (rel): dirname my $path = defined $ENV{PATH} && length( $ENV{PATH} ) ? $ENV{PATH} : ''; my ( $type, $key ) = ( File::Spec->splitpath($binary) )[2] eq $binary ? grep( !File::Spec->file_name_is_absolute($_), File::Spec->path ) ? ( 'path-rel', join "\0", cwd(), $path ) : ( 'path', $path ) : File::Spec->file_name_is_absolute($binary) ? ( 'abs', '' ) : ( 'rel', cwd() ); # This relatively complex cache key scheme allows PATH or cwd to change # during the life of a program using Git::Repository, which is likely # to happen. On the other hand, it completely ignores the possibility # that any part of the cached path to a git binary could be a symlink # which target may also change during the life of the program. # check the cache return $binary{$type}{$key}{$binary}{$args} if exists $binary{$type}{$key}{$binary}{$args}; # compute a list of candidate files (look in PATH if needed) my $git = $type =~ /^path/ ? _which($binary) : File::Spec->rel2abs($binary); $git = File::Spec->rel2abs($git) if defined $git && $type eq 'path-rel'; # if we can't find any, we're done return $binary{$type}{$key}{$binary} = undef if !( defined $git && -x $git ); # try to run it my $cmd = System::Command->new( $git, @args, '--version' ); my $version = do { local $/ = "\n"; $cmd->stdout->getline; } || ''; $cmd->close; # does it really look like git? return $binary{$type}{$key}{$binary}{$args} = $version =~ /^git version \d/ ? $type eq 'path' ? $binary # leave the shell figure it out itself too : $git : undef; } sub new { my ( $class, @cmd ) = @_; # split the args my (@r, @o); @cmd = # take out the first Git::Repository in $r, and options in @o grep !( blessed $_ && $_->isa('Git::Repository') ? push @r, $_ : 0 ), grep !( ref eq 'HASH' ? push @o, $_ : 0 ), @cmd; # wouldn't know what to do with more than one Git::Repository object croak "Too many Git::Repository objects given: @r" if @r > 1; my $r = shift @r; # keep changes to the environment local local %ENV = %ENV; # a Git::Repository object will give more context if ($r) { # pick up repository options unshift @o, $r->options; # get some useful paths my ( $git_dir, $work_tree ) = ( $r->git_dir, $r->work_tree ); unshift @o, { cwd => $work_tree } if defined $work_tree && length $work_tree; # setup our %ENV delete @ENV{qw( GIT_DIR GIT_WORK_TREE )}; $ENV{GIT_DIR} = $git_dir; $ENV{GIT_WORK_TREE} = $work_tree if defined $work_tree; } # pick up the modified PATH, if any exists $_->{env} and exists $_->{env}{PATH} and $ENV{PATH} = $_->{env}{PATH} for @o; # extract and process the 'fatal' option push @o, { fatal => { 128 => 1, # fatal 129 => 1, # usage map s/^-// ? ( $_ => '' ) : ( $_ => 1 ), map /^!0$/ ? ( 1 .. 255 ) : $_, map ref() ? @$_ : $_, grep defined, map $_->{fatal}, @o } }; # get and check the git command my $git_cmd = ( map { exists $_->{git} ? $_->{git} : () } @o )[-1]; # git option might be an arrayref containing an executable with arguments # (e.g. [ qw( /usr/bin/sudo -u nobody git ) ] ) ( $git_cmd, my @args ) = defined $git_cmd ? ref $git_cmd ? @$git_cmd : ($git_cmd) : ('git'); my $git = _is_git($git_cmd, @args); croak sprintf "git binary '%s' not available or broken", join( ' ', $git_cmd, @args ) # show the full command given if !defined $git; # turn us into a dumb terminal delete $ENV{TERM}; # spawn the command and re-bless the object in our class return bless System::Command->new( $git, @args, @cmd, @o ), $class; } sub final_output { my ($self, @cb) = @_; # get output / errput my ( @output, @errput ); $self->loop_on( input_record_separator => "\n", stdout => sub { chomp( my $o = shift ); push @output, $o; }, stderr => sub { chomp( my $e = shift ); push @errput, $e; }, ); # done with it $self->close; # fatal exit codes set by the 'fatal' option # when working with fatal => '!0' it's helpful to be able to show the exit status # so that specific exit codes can be made non-fatal if desired. if ( $self->options->{fatal}{ $self->exit } ) { croak join( "\n", @errput ) || 'fatal: unknown git error, exit status '.$self->exit; } # something else's wrong if ( @errput && !$self->options->{quiet} ) { carp join "\n", @errput; } # process the output with the optional callbacks for my $cb (@cb) { @output = map $cb->($_), @output; } # return the output return wantarray ? @output : join "\n", @output; } 1; __END__ =head1 NAME Git::Repository::Command - Command objects for running git =pod =head1 SYNOPSIS use Git::Repository::Command; # invoke an external git command, and return an object $cmd = Git::Repository::Command->new(@cmd); # a Git::Repository object can provide more context $cmd = Git::Repository::Command->new( $r, @cmd ); # options can be passed as a hashref $cmd = Git::Repository::Command->new( $r, @cmd, \%option ); # $cmd is basically a hash, with keys / accessors $cmd->stdin(); # filehandle to the process' stdin (write) $cmd->stdout(); # filehandle to the process' stdout (read) $cmd->stderr(); # filehandle to the process' stdout (read) $cmd->pid(); # pid of the child process # done! $cmd->close(); # exit information $cmd->exit(); # exit status $cmd->signal(); # signal $cmd->core(); # core dumped? (boolean) # cut to the chase my ( $pid, $in, $out, $err ) = Git::Repository::Command->spawn(@cmd); =head1 DESCRIPTION L is a class that actually launches a B commands, allowing to interact with it through its C, C and C. This class is a subclass of L, meant to be invoked through L. =head1 METHODS As a subclass of L, L supports the following methods: =head2 new Git::Repository::Command->new( @cmd ); Runs a B command with the parameters in C<@cmd>. If C<@cmd> contains a L object, it is used to provide context to the B command. If C<@cmd> contains one or more hash reference, they are taken as I