Git-Repository-1.321/0000755000175000017500000000000013164025730013067 5ustar bookbookGit-Repository-1.321/Changes0000644000175000017500000004425013164025730014367 0ustar bookbookRevision history for Git-Repository 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.321/LICENSE0000644000175000017500000004371313164025730014104 0ustar bookbookThis software is copyright (c) 2017 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) 2017 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) 2017 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.321/eg/0000755000175000017500000000000013164025730013462 5ustar bookbookGit-Repository-1.321/eg/build-git0000755000175000017500000001745213164025730015301 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', 'list', 'missing', 'installed', 'rc!', 'fetch', 'test', '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) ) { 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}; # build run_cmd( @make => '-j3' ); # install run_cmd( @make => 'install' ); # 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.321/t/0000755000175000017500000000000013164025730013332 5ustar bookbookGit-Repository-1.321/t/10-new_fail.t0000644000175000017500000000431213164025730015521 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/, # 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/, # 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/, # 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.321/t/13-sudo.t0000644000175000017500000000121513164025730014711 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.321/t/07-version.t0000644000175000017500000000373713164025730015442 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.321/t/MyGit/0000755000175000017500000000000013164025730014363 5ustar bookbookGit-Repository-1.321/t/MyGit/Hello.pm0000644000175000017500000000032413164025730015763 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.321/t/25-plugins.t0000644000175000017500000000631413164025730015430 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 $r = test_repository; 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.321/t/config0000644000175000017500000000007513164025730014524 0ustar bookbook[user] name = Philippe Bruhat (BooK) email = book@cpan.org Git-Repository-1.321/t/Git/0000755000175000017500000000000013164025730014055 5ustar bookbookGit-Repository-1.321/t/Git/Repository/0000755000175000017500000000000013164025730016234 5ustar bookbookGit-Repository-1.321/t/Git/Repository/Plugin/0000755000175000017500000000000013164025730017472 5ustar bookbookGit-Repository-1.321/t/Git/Repository/Plugin/Hello2.pm0000644000175000017500000000045713164025730021163 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.321/t/Git/Repository/Plugin/Hello.pm0000644000175000017500000000045513164025730021077 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.321/t/sudo.pl0000644000175000017500000000016513164025730014643 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.321/t/author-pod-coverage.t0000644000175000017500000000053613164025730017376 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.321/t/06-version.t0000644000175000017500000001212013164025730015423 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.321/t/11-create.t0000644000175000017500000002111313164025730015177 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; # 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(qw( 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.321/t/00-report-prereqs.t0000644000175000017500000001342613164025730016734 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.321/t/20-simple.t0000644000175000017500000002474113164025730015237 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 ) ); # 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.321/t/05-try_git.t0000644000175000017500000000722713164025730015432 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.321/t/release-distmeta.t0000644000175000017500000000040113164025730016742 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.321/t/24-errors.t0000644000175000017500000001420313164025730015256 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 $output = ''; for my $t (@tests) { @warnings = (); # create a new test repository if needed $r = test_repository( @{ $t->{test_repo} } ) 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.321/t/21-submodule.t0000644000175000017500000000374013164025730015742 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 $s = test_repository; 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; $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.321/t/00-report-prereqs.dd0000644000175000017500000000463713164025730017064 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.321/t/26-overloaded_objects.t0000644000175000017500000000174413164025730017607 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; } } 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.321/t/30-test_repository.t0000644000175000017500000000364313164025730017223 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 $r = test_repository; # 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.321/t/12-create.t0000644000175000017500000000146313164025730015206 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.321/t/23-quiet.t0000644000175000017500000000337313164025730015076 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 $r = test_repository; # 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.321/t/test-all-git.t0000644000175000017500000000363613164025730016035 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-colleciton dir, including under ../.. (under `dzil test`) my $collection = 'gt-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.321/t/00-compile.t0000644000175000017500000000276313164025730015374 0ustar bookbookuse 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.057 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.321/t/22-backward.t0000644000175000017500000000356513164025730015527 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 $fake = realpath( tempdir( CLEANUP => 1 ) ); my $r = test_repository; 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.321/t/author-pod-syntax.t0000644000175000017500000000045413164025730017130 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.321/META.yml0000644000175000017500000000416013164025730014341 0ustar bookbook--- abstract: 'Perl interface to Git repositories' author: - 'Philippe Bruhat (BooK) ' build_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' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.010, CPAN::Meta::Converter version 2.150010' keywords: - git - wrapper license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Git-Repository 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' resources: bugtracker: http://rt.cpan.org/NoAuth/Bugs.html?Dist=Git-Repository repository: https://github.com/book/Git-Repository.git version: '1.321' x_contributors: - 'Ron Savage ' - 'Samit Badle ' - 'Lisa Hansen ' - 'Alex Raguero ' - 'Gregor Herrmann ' - 'Petr Šabata ' - 'Tim Bunce ' - 'Aristotle Pagaltzis ' - 'Michael G. Schwern ' - 'Todd Rinaldo ' - 'Mark Lawrence ' - 'Dominic Humphries ' - 'Christian Walde ' - 'Yanick Champoux ' - 'Thomas Klausner ' - 'Olivier Mengué ' - 'Nigel Metheringham ' - 'Lasse Makholm ' - 'Kazuhiro Shibuya ' - 'Jacques Germishuys ' - 'Olivier Raginel ' - 'Anton Berezin ' - 'Alessandro Ghedini ' - 'Gustavo Leite de Mendonça Chaves ' x_serialization_backend: 'YAML::Tiny version 1.70' Git-Repository-1.321/META.json0000644000175000017500000000661413164025730014517 0ustar bookbook{ "abstract" : "Perl interface to Git repositories", "author" : [ "Philippe Bruhat (BooK) " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.010, 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.321", "x_contributors" : [ "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_serialization_backend" : "JSON::XS version 3.03" } Git-Repository-1.321/dist.ini0000644000175000017500000000443213164025730014536 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 = 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.321/MANIFEST0000644000175000017500000000141513164025730014221 0ustar bookbook# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.010. 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.321/Makefile.PL0000644000175000017500000000401013164025730015034 0ustar bookbook# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.010. 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.321", "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.321/lib/0000755000175000017500000000000013164025730013635 5ustar bookbookGit-Repository-1.321/lib/Git/0000755000175000017500000000000013164025730014360 5ustar bookbookGit-Repository-1.321/lib/Git/Repository/0000755000175000017500000000000013164025730016537 5ustar bookbookGit-Repository-1.321/lib/Git/Repository/Tutorial.pod0000644000175000017500000004044313164025730021053 0ustar bookbook=head1 NAME Git::Repository::Tutorial - Control git from Perl using Git::Repository =head1 SYNOPSIS use Git::Repository; # do cool stuff with Git, using the following advice =head1 HOW-TO A L object represents an actual Git repository, against which you can I commands. =head2 Obtain a Git::Repository object from an existing repository If your script is expected to run against a repository in the current directory (like most Git commands), let L handle the magic: $r = Git::Repository->new(); If the repository has a working copy (work tree): $r = Git::Repository->new( work_tree => $dir ); If the repository is a bare repository, or you prefer to provide the F<.git> directory location: $r = Git::Repository->new( git_dir => $gitdir ); If the work tree and the git directory are in unrelated locations, you can also provide both: $r = Git::Repository->new( work_tree => $dir, git_dir => $gitdir ); The constructor also accepts an option hash. The various options are detailed in the manual page for L. =head2 Run any git command Git commands can be run against an existing L object, or against the class itself (in which case, git will try to deduce its context from the current directory and the environment). The pattern for running commands is always the same: $r->run( $command => @arguments, \%options ); The C<$command> and C<@arguments> are identical to those you'd pass to the C command-line tool. The options hash contains options, as described in the manual page for L. =head2 Create a new repository Sometime, you'll need to create the Git repository from scratch: # git version 1.6.5 and above Git::Repository->run( init => $dir ); $r = Git::Repository->new( work_tree => $dir ); Any git older than 1.6.5 requires the command to be run in the work tree, so we use the C option: # git version 1.5.0.rc1 and above Git::Repository->run( init => { cwd => $dir } ); $r = Git::Repository->new( work_tree => $dir ); # older git versions Git::Repository->run( 'init-db' => { cwd => $dir } ); $r = Git::Repository->new( work_tree => $dir ); Note that the old C method is obsolete (as of L 1.18, from April 16, 2011) and has been removed (as of L 1.301, January 21, 2013). =head2 Clone a repository Cloning works the same way: Git::Repository->run( clone => $url => $dir ); $r = Git::Repository->new( work_tree => $dir ); =head2 Run a simple command When you don't really care about the output of the command, just call it: $r->run( add => '.' ); $r->run( commit => '-m', 'my commit message' ); In case of an error or warning, L will C or C appropriately. =head2 Properly quote options It's common to work out the proper string of Git commands needed to achieve your goal in the shell, before actually turning them into calls to C<< Git::Repository->run >>. Some options might require quoting, to properly get the arguments to Git through the shell: # shell $ git log --since='Fri Jul 26 19:34:15 2013 +0200' --grep='report ticket' Such quoting is of course not needed with L: $since = 'Fri Jul 26 19:34:15 2013 +0200'; $grep = 'report ticket'; my $cmd = $r->command( log => "--since=$since", "--grep=$grep" ); =head2 Be careful with spaces in options For the same reasons as above (individual arguments to C or C are turned into individual C elements for B, whitespace included), some command-line usages of B need to be slightly reformatted to make them suitable for C. For example, these two commands have the same effect when run from the shell: shell> git checkout -b sometopic shell> git checkout -bsometopic In the first case, B receives three arguments in C: C, C<-b> and C. In the second case, it receives two arguments: C and C<-bsometopic>, and then B recognizes the beginning of the I<-b> option and splits C out of the second argument. So, in a call such as: $command = $repo->run( checkout => "-b$branch_name", { quiet => 0 } ); If C<$branch_name> contains an initial space character, the call will be equivalent the following shell command: shell> git checkout -b\ sometopic and B will receive two arguments: C and C<-b sometopic>, from which it will split out C< sometopic> (note the initial space). The space after I<-b> must be removed, as otherwise the code attempts to create a branch called C< sometopic>, which git rejects. =head2 Silence warnings for some Git commands Some Git porcelain commands provide additional information on C. One typical example is C: $ git checkout mybranch Switched to branch 'mybranch' The C method of L treats all output on C as a warning. Therefore, the following code: $r->run( checkout => 'mybranch' ); will output a warning like this one: Switched to branch 'mybranch' at myscript.pl line 10. In such a case, you can use the C option to silence the warning for a single command: $r->run( checkout => 'mybranch', { quiet => 1 } ); To silence I warnings, you can pass the C option during the creation of the original repository object: my $r = Git::Repository->new( { quiet => 1 } ); This is not recommended, as it might hide important information from you. =head2 Process normal and error output The C command doesn't capture C: it only warns (or dies) if something was printed on it. To be able to actually capture error output, C must be used. my $cmd = $r->command( @cmd ); my @errput = $cmd->stderr->getlines(); $cmd->close; C also captures all output at once, which can lead to unnecessary memory consumption when capturing the output of some really verbose commands. my $cmd = $r->command( log => '--pretty=oneline', '--all' ); my $log = $cmd->stdout; while (<$log>) { ...; } $cmd->close; Of course, as soon as one starts reading and writing to an external process' communication handles, a risk of blocking exists. I. =head2 Provide input on standard input Use the C option: my $commit = $r->run( 'commit-tree', $tree, '-p', $parent, { input => $message } ); =head2 Change the environment of a command Use the C option: $r->run( 'commit', '-m', 'log message', { env => { GIT_COMMITTER_NAME => 'Git::Repository', GIT_COMMITTER_EMAIL => 'book@cpan.org', }, }, ); See L for other available options. =head2 Ignore the system and global configuration files Git has three levels of configuration files that can change the output of porcelain commands: system (F<$(prefix)/etc/gitconfig>), global (F<$HOME/.gitconfig> and F<$XDG_CONFIG_HOME/git/config>) and local (F<.git/config> inside the repository). To ensure the system and global configuration files will be ignored and won't interfere with the expected output of your Git commands, you can add the following keys to the C option: GIT_CONFIG_NOSYSTEM => 1, XDG_CONFIG_HOME => undef, HOME => undef, =head2 Ensure the output from Git commands is not localized Since version 1.7.9, Git translates its most common interface messages into the user's language if translations are available and the locale is appropriately set. This means that naively parsing the output "porcelain" commands might fail if the program is unexpectedly run under an unexpected locale. The easiest way to ensure your Git commands will be run in a "locale-safe" environment, is to set the C environment variable to C. The brutal way: $ENV{LC_ALL} = 'C'; The temporary way: local $ENV{LC_ALL} = 'C'; The subtle way (restricted to the commands run on a given L instance): my $r = Git::Repository->new( { env => { LC_ALL => 'C' } } ); The stealthiest way (restricted to a single command): $r->run( ..., { env => { LC_ALL => 'C' } } ); =head2 Ensure the Git commands are run from the current working directory By default, L will C to the root of the work tree before launching the requested Git command. This means that no matter where your program C to, commands on the L instance will by default be run from the root of the work tree. So, commands such as C need to use the "full" path (relative to C) of the files to be added. The C option can be used to define where L will C to. To instruct L to B C (and therefore run the Git command from the I), set the option to C: # run from cwd for this command only $r->run( ..., { cwd => undef } ); # always run git from cwd my $r = Git::Repository->new( { cwd => undef } ); =head2 Finely control when C dies By default, C<< Git::Repository->run( ... ) >> dies if the Git command exited with a status code of C<128> (fatal error) or C<129> (usage message). Some commands will throw an error and exit with a status different from the previous two: $r->run( checkout => 'does-not-exist' ); # exit status: 1 The above C call does not die, and output the following warning: error: pathspec 'does-not-exist' did not match any file(s) known to git. The exit status (as given by C<<< $? >> 8 >>>) is C<1>. To force C to die when the Git command exits with status C<1>, use the C option (added in version 1.304, May 25, 2013): $r->run( checkout => 'does-not-exist', { fatal => 1 } ); By default, C<128> and C<129> remain in the list of fatal codes. Here are a few examples: # set the fatal codes for all call to run() on this object $r = Git::Repository->new( { fatal => [ 1 .. 255 ] } ); As usual, setting the option to the L object will set it for all commands run for it: # "!0" is a shortcut for 1 .. 255 $r = Git::Repository->new( { fatal => [ "!0" ] } ); Using negative codes will make these values non-fatal: # the above call to new() makes all exit codes fatal # but 3 and 7 won't be fatal for this specific run $r->run( ..., { fatal => [ -3, -7 ] } ); When the list contains a single item, there is no need to use an array reference: # same as [ "!0" ] $r = Git::Repository->new( { fatal => "!0" } ); # remove 17 from the list of fatal exit codes for this run only $r->run( ..., { fatal => -17 } ); See L for other available options. =head2 Process the output of B When creating a tool that needs to process the output of B, you should always define precisely the expected format using the I<--pretty> option, and choose a format that is easy to parse. Assuming B will output the default format will eventually lead to problems, for example when the user's git configuration defines C to be something else than the default of C. See also L for adding to your L objects a C method that will parse the log output for you. Understanding the various options for B can make it very simple to obtain a lot of information. For example: # all tags reachable from $committish my @tags = map { s/^ \((.*)\)/$1/; ( map +( split /: / )[1], grep /^tag: /, split /, / ) } $_->run( qw( log --simplify-by-decoration --pretty=%d ), $committish ); =head2 Process the output of B B behaves differently when it detects it's not attached to a terminal. In that case, it just tries to read some B output from its standard input. So this oneliner will hang, because B is waiting for some data from the program connected to its standard input (the oneliner): perl -MGit::Repository -le 'print scalar Git::Repository->run( shortlog => -5 )' Whereas this one will "work" (as in "immediately return with no output"): perl -MGit::Repository -le 'print scalar Git::Repository->run( shortlog => -5, { input => "" } )' So, you need to give B I input (from B): perl -MGit::Repository -le 'print scalar Git::Repository->run( shortlog => { input => scalar Git::Repository->run( log => -5 ) } )' If the log output is large, you'll probably be better off with something like the following: use Git::Repository; # start both git commands my $log = Git::Repository->command('log')->stdout; my $cmd = Git::Repository->command( shortlog => -ens ); # feed one with the output of the other my $in = $cmd->stdin; print {$in} $_ while <$log>; close $in; # and do something with the output print $cmd->stdout->getlines; =head2 Wrap git in a sudo call If for a given repository you want to wrap all calls to git in a C call, you can use the C option with an array ref: my $r = Git::Repository->new( { git => [qw( sudo -u nobody git )] } ); In this case, every call to git from C<$r> will actually call C. =head2 Use submodules Because L automatically sets the C and C environment variables, some C sub-commands may fail. For example: $r->run( submodule => add => $repository => 'sub' ); will give the following error: error: pathspec 'sub' did not match any file(s) known to git. To avoid this error, you should enforce the removal of the C variable from the environment in which the command is run: $r->run( submodule => add => $repository => 'sub', { env => { GIT_WORK_TREE => undef } } ); Note that L version 1.04 is required to be able to remove variables from the environment. =head2 Sort git versions Since version 1.318, Git::Repository lets L handle all version comparisons. Sorting version numbers is therefore as simple as: use Git::Version::Compare qw( cmp_git ); @sort_verson = sort cmp_git @versions; =head2 Add specialized methods to your Git::Repository objects Have a look at L and L, to learn how to add your own methods to L. =head2 Run code on the output of a git command through callback Sometimes you need to process the output of a command by running a callback on each line of the output. # code inspiration: # https://github.com/dolmen/github-keygen/blob/24c501072ba7d890810de3008434c1fe1f757286/release.pl#L178 my %tree; $r->run( 'ls-tree' => $commit, sub { my ($mode, $type, $object, $file) = split; $tree{$file} = [ $mode, $type, $object ]; } ); Note that the value returned by the callback will be returned as part of the C output, instead of the original line. =head2 Initialize a test repository with a bundle Instead of creating a test repository using a series of file editions and commits, one can simply import data into the test repository using a bundle. Bundles are created with the C command (see the Git documentation for details). First create a temporary repository with the help of L: use Test::Git; my $r = test_repository(); then import the bundle data in your repository, and collect the references: my @refs = $r->run( bundle => 'unbundle', $bundle_file ); and finally update the references: for my $line (@refs) { my ( $sha1, $ref ) = split / /, $line; $r->run( 'update-ref', $ref => $sha1 ); } Since Git version 1.6.5, it's also possible to clone directly from a bundle (this creates an C remote pointing to the bundle file): my $r = test_repository( clone => [ $bundle_file ] ); A bundle from a recipient repository's point of view is just like a regular remote repository. See the documentation of B for details of what's possible (e.g. incremental bundles). =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.321/lib/Git/Repository/Plugin.pm0000644000175000017500000000613513164025730020340 0ustar bookbookpackage Git::Repository::Plugin; $Git::Repository::Plugin::VERSION = '1.321'; use strict; use warnings; use 5.006; use Carp; sub install { my ( $class, @keywords ) = @_; # get the list of keywords to install my %keyword = map { $_ => 1 } my @all_keywords = $class->_keywords; @keywords = @all_keywords if !@keywords; @keywords = grep { !( !exists $keyword{$_} and carp "Unknown keyword '$_' in $class" ) } @keywords; carp "No keywords installed from $class" if !@keywords; # install keywords no strict 'refs'; *{"Git::Repository::$_"} = \&{"$class\::$_"} for @keywords; } sub _keywords { my ($class) = @_; carp "Use of \@KEYWORDS by $class is deprecated"; no strict 'refs'; return @{"$class\::KEYWORDS"}; } 1; __END__ =head1 NAME Git::Repository::Plugin - Base class for Git::Repository plugins =head1 SYNOPSIS package Git::Repository::Plugin::Hello; use Git::Repository::Plugin; our @ISA = qw( Git::Repository::Plugin ); sub _keywords { return qw( hello hello_gitdir ) } sub hello { return "Hello, git world!\n"; } sub hello_gitdir { return "Hello, " . $_[0]->git_dir . "!\n"; } 1; =head1 DESCRIPTION L allows one to define new methods for L, that will be imported in the L namespace. The L provides a full example. The documentation of L describes how to load plugins with all the methods they provide, or only a selection of them. =head1 METHODS L provides a single method: =head2 install $plugin->install( @keywords ); Install all keywords provided in the L namespace. If called with an empty list, will install all available keywords. =head1 SUBCLASSING =head2 Adding methods to L When creating a plugin, the new keywords (i.e. methods) that are added by the plugin to L must be returned by a C<_keywords()> method. =head2 Adding attributes to L L is a blessed hash reference. If extra attributes are needed, the recommended name for the hash key (to avoid name clashes between plugins) is C<_plugin_I_I>, where I is the plugin lowercase name, and I is the attribute name. =head1 AUTHOR Philippe Bruhat (BooK) =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. After a not-so-good design using @ISA (so L would I the extra methods), further discussions with Aristotle Pagaltzis and a quick peek at L's plugin management helped me come up with the current design. Thank you Aristotle and the L team. Further improvements to the plugin system proposed by Aristotle Pagaltzis. =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.321/lib/Git/Repository/Command.pm0000644000175000017500000003314313164025730020457 0ustar bookbookpackage Git::Repository::Command; $Git::Repository::Command::VERSION = '1.321'; 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