Package Documentation
- Readme
Gitlib for Gitonomy
This library provides methods to access Git repository from PHP 5.6+.
It makes shell calls, which makes it less performant than any solution.
Anyway, it's convenient and don't need to build anything to use it. That's how we love it.
Quick Start
You can install gitlib using Composer. Simply require the version you need:
$ composer require gitonomy/gitlib
or edit your
composer.json
file by hand:{ "require": { "gitonomy/gitlib": "^1.3" } }
Example Usage
<?php use Gitonomy\Git\Repository; $repository = new Repository('/path/to/repository'); foreach ($repository->getReferences()->getBranches() as $branch) { echo '- '.$branch->getName().PHP_EOL; } $repository->run('fetch', ['--all']);
API Documentation
For Enterprise
Available as part of the Tidelift Subscription
The maintainers of
gitonomy/gitlib
and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more. - Admin
Create and access git repositories
gitlib provides methods to initialize new repositories.
Create a repository
To initialize a new repository, use method
Admin::init
.// Initialize a bare repository $repository = Gitonomy\Git\Admin::init('/path/to/repository'); // Initialize a non-bare repository $repository = Gitonomy\Git\Admin::init('/path/to/repository', false);
Default behavior is to create a bare repository. If you want to
initialize a repository with a working copy,passfalse
as third
argument of Repository constructor.Cloning repositories
You can clone a repository from an URL by doing:
// Clone to a bare repository $repository = Gitonomy\Git\Admin::cloneTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git'); // Clone to a non-bare repository $repository = Gitonomy\Git\Admin::cloneTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git', false);
Default behavior is to clone in a bare repository.
You can also clone a repository and point it to a specific branch. In a
non-bare repository, this branch will be checked out:// Clone to a bare repository $repository = Gitonomy\Git\Admin::cloneBranchTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git', 'a-branch'); // Clone to a non-bare repository $repository = Gitonomy\Git\Admin::cloneBranchTo('/tmp/gitlib', 'https://github.com/gitonomy/gitlib.git', 'a-branch', false);
Clone a Repository object
If you already have a Repository instance and want to clone it, you can
use this shortcut:$new = $repository->cloneTo('/tmp/clone');
Mirror a repository
If you want to mirror fully a repository and all references, use the
mirrorTo
method. This method takes only two arguments, where to mirror
and what to mirror:// Mirror to a bare repository $mirror = Gitonomy\Git\Admin::mirrorTo('/tmp/mirror', 'https://github.com/gitonomy/gitlib.git'); // Mirror to a non-bare repository $mirror = Gitonomy\Git\Admin::mirrorTo('/tmp/mirror', 'https://github.com/gitonomy/gitlib.git', false);
References
- Blame
Blaming files
Line-per-line iteration
To iterate on lines of a blame:
$blame = $repository->getBlame('master', 'README.md'); foreach ($blame->getLines() as $lineNumber => $line) { $commit = $line->getCommit(); echo $lineNumber.': '.$line->getContent().' - '.$commit->getAuthorName().PHP_EOL; }
The getLines method returns an array indexed starting from 1.
As you can see, you can access the commit object related to the line you
are iterating on.If you want to access directly a line:
$line = $blame->getLine(32);
The Line object
LineObject represents an item of the blame file. It is composed of those
informations:$line->getCommit(); // returns a Commit $line->getContent(); // returns text // you can access author from commmit: $author = $line->getCommit()->getAuthorName();
Group reading by commit
If you plan to display it, you'll probably need a version where lines
from same commit are grouped.To do so, use the getGroupedLines method that will return an array
like this:$blame = array( array(Commit, array(1 => Line, 2 => Line, 3 => Line)), array(Commit, array(4 => Line)), array(Commit, array(5 => Line, 6 => Line)) )
- Blob
Blob
In git, a blob represents a file content. You can't access the file name
directly from the Blob object; the filename information is stored
within the tree, not in the blob.It means that for git, two files with different names but same content
will have the same hash.To access a repository Blob, you need the hash identifier:
$repository = new Gitonomy\Git\Repository('/path/to/repository'); $blob = $repository->getBlob('a7c8d2b4');
Get content
To get content from a Blob object:
echo $blob->getContent();
File informations
To get mimetype of a Blob object using finfo extension:
echo $blob->getMimetype();
You can also test if Blob is a text of a binary file:
if ($blob->isText()) { echo $blob->getContent(), PHP_EOL; } elseif ($blob->isBinary()) { echo 'File is binary', PHP_EOL; }
- Branch
Branch
To access a Branch, starting from a repository object:
$repository = new Gitonomy\Git\Repository('/path/to/repository'); $branch = $repository->getReferences()->getBranch('master');
You can check is the branch is a local or remote one:
$branch->isLocal(); $branch->isRemote();
- Commit
Commit
To access a Commit, starting from a repository object:
$repository = new Gitonomy\Git\Repository('/path/to/repository'); $commit = $repository->getCommit('a7c8d2b4');
Browsing parents
A Commit can have a natural number of parents:
- no parent: it's an initial commit, the root of a tree
- one parent: it means it's not a merge, just a regular commit
- many parents: it's a merge-commit
You have 2 methods available for accessing parents:
// Access parent hashes $hashes = $commit->getParentHashes(); // Access parent commit objects $commits = $commit->getParents();
For example, if you want to display all parents, starting from a commit:
function displayLog(Gitonomy\Git\Commit $commit) { echo '- '.$commit->getShortMessage().PHP_EOL; foreach ($commit->getParents() as $parent) { displayLog($parent); } }
Notice that this function will first display all commits from first
merged branch and then display all commits from next branch, and so on.Accessing tree
The tree object contains the reference to the files associated to a
given commit. Every commit has one and only one tree, referencing all
files and folders of a given state for a project. For more informations
about the tree, see the chapter dedicated to it.To access a tree starting from a commit:
// Returns the tree hash $tree = $commit->getTreeHash(); // Returns the tree object $tree = $commit->getTree();
Author & Committer informations
Each commit has two authoring informations: an author and a committer.
The author is the creator of the modification, authoring a modification
in the repository. The committer is responsible of introducing this
modification to the repository.You can access informations from author and committer using those
methods:// Author $commit->getAuthorName(); $commit->getAuthorEmail(); $commit->getAuthorDate(); // returns a DateTime object // Committer $commit->getCommitterName(); $commit->getCommitterEmail(); $commit->getCommitterDate(); // returns a DateTime object
Commit message and short message
Each commit also has a message, associated to the modification. This
message can be multilined.To access the message, you can use the getMessage method:
$commit->getMessage();
For your convenience, this library provides a shortcut method to keep
only the first line or first 50 characters if the first line is too
long:$commit->getShortMessage();
You can customize it like this:
$commit->getShortMessage(45, true, '.');
- The first parameter is the max length of the message.
- The second parameter determine if the last word should be cut or
preserved - The third parameter is the separator
There are also two other methods for your convenience:
// The first line $commit->getSubjectMessage(); // The body (rest of the message) $commit->getBodyMessage();
Diff of a commit
You can check the modifications introduced by a commit using the
getDiff method. When you request a diff for a commit, depending of the
number of parents, the strategy will be different:- If you have no parent, the diff will be the content of the tree
- If you only have one parent, the diff will be between the commit
and his parent - If you have multiple parents, the diff will be the difference
between the commit and the first common ancestor of all parents
For more informations about the diff API, read the related chapter.
To access the Diff object of a commit, use the method getDiff:
$diff = $commit->getDiff();
Last modification of a file
To know the last modification of a file, you can use the
getLastModification method on a commit.Here is a very straightforward example:
$last = $commit->getLastModification('README'); echo 'Last README modification'.PHP_EOL; echo ' Author: '.$last->getAuthorName().PHP_EOL; echo ' Date: '.$last->getAuthorDate()->format('d/m/Y').PHP_EOL; echo ' Message: '.$last->getMessage();
Find every branches containing a commit
$branches = $commit->getIncludingBranches($includeLocalBranches, $includeRemoteBranches); $localBranches = $commit->getIncludingBranches(true, false); $remoteBranches = $commit->getIncludingBranches(false, true);
- Diff
Computing diff
Even if git is a diff-less storage engine, it's possible to compute
them.To compute a diff in git, you need to specify a revision. This
revision can be a commit (2bc7a8) or a range (2bc7a8..ff4c21b).For more informations about git revisions: man gitrevisions.
When you have decided the revision you want and have your Repository
object, you can call the getDiff method on the repository:$diff = $repository->getDiff('master@{2 days ago}..master');
You can also access it from a Log object:
$log = $repository->getLog('master@{2 days ago}..master'); $diff = $log->getDiff();
Iterating a diff
When you have a Diff object, you can iterate over files using method
getFiles(). This method returns a list of File objects, who
represents the modifications for a single file.$files = $diff->getFiles(); echo sprintf('%s files modified%s', count($files), PHP_EOL); foreach ($files as $fileDiff) { echo sprintf('Old name: (%s) %s%s', $fileDiff->getOldMode(), $fileDiff->getOldName(), PHP_EOL); echo sprintf('New name: (%s) %s%s', $fileDiff->getNewMode(), $fileDiff->getNewName(), PHP_EOL); }
The File object
Here is an exhaustive list of the File class methods:
$file->getOldName(); $file->getNewName(); $file->getOldDiff(); $file->getNewDiff(); $file->isCreation(); $file->isDeletion(); $file->isModification(); $file->isRename(); $file->isChangeMode(); $file->getAdditions(); // Number of added lines $file->getDeletions(); // Number of deleted lines $file->isBinary(); // Binary files have no "lines" $file->getChanges(); // See next chapter
The FileChange object
note
This part of API is not very clean, very consistent. If you have any
idea or suggestion on how to enhance this, your comment would be
appreciated.A File object is composed of many changes. For each of those changes,
a FileChange object is associated.To access changes from a file, use the getChanges method:
$changes = $file->getChanges(); foreach ($changes as $change) { foreach ($lines as $data) { list ($type, $line) = $data; if ($type === FileChange::LINE_CONTEXT) { echo ' '.$line.PHP_EOL; } elseif ($type === FileChange::LINE_ADD) { echo '+'.$line.PHP_EOL; } else { echo '-'.$line.PHP_EOL; } } }
To get line numbers, use the range methods:
echo sprintf('Previously from line %s to %s%s', $change->getOldRangeStart(), $change->getOldRangeEnd(), PHP_EOL); echo sprintf('Now from line %s to %s%s', $change->getNewRangeStart(), $change->getNewRangeEnd(), PHP_EOL);
- Hooks
Hooks
It's possible to define custom hooks on any repository with git. Those
hooks are located in the .git/hooks folder.Those files need to be executable. For convenience, gitlib will set them
to 777.With gitlib, you can manage hooks over a repository using the Hooks
object.To access it from a repository, use the getHooks method on a
Repository object:$hooks = $repository->getHooks();
Reading hooks
To read the content of a hook, use the get method like this:
$content = $hooks->get('pre-receive'); // returns a string
If the hook does not exist, an exception will be thrown
(InvalidArgumentException).You can test if a hook is present using the method has:
$hooks->has('pre-receive'); // a boolean indicating presence
Inserting hooks
You can modify a hook in two different ways: creating a new file or
using a symlink.To create the hook using a symlink:
$hooks->setSymlink('pre-receive', '/path/to/file-to-link');
If the hook already exist, a LogicException will be thrown. If an
error occured during symlink creation, a RuntimeException will be
thrown.If you want to directly create a new file in hooks directory, use the
method set. This method will create a new file, put content in it and
make it executable:$content = <<<HOOK #!/bin/bash echo "Push is disabled" exit 1 HOOK; // this hook will reject every push $hooks->set('pre-receive', $content);
If the hook already exists, a LogicException will be thrown.
Removing hooks
To remove a hook from a repository, use the function remove:
$hooks->remove('pre-receive');
- Log
Getting log history
Crawling manually commits and parents to browse history is surely a good
solution. But when it comes to ordering them or aggregate them from
multiple branches, we tend to usegit log
.To get a Log object from a repository:
$log = $repository->getLog();
You can pass four arguments to getLog method:
// Global log for repository $log = $repository->getLog(); // Log for master branch $log = $repository->getLog('master'); // Returns last 10 commits on README file $log = $repository->getLog('master', 'README', 0, 10); // Returns last 10 commits on README or UPGRADE files $log = $repository->getLog('master', ['README', 'UPGRADE'], 0, 10);
Counting
If you want to count overall commits, without offset or limit, use the
countCommits method:echo sprintf('This log contains %s commits%s', $log->countCommits(), PHP_EOL); // Countable interface echo sprintf('This log contains %s commits%s', count($log), PHP_EOL);
Offset and limit
Use those methods:
$log->setOffset(32); $log->setLimit(40); // or read it: $log->getOffset(); $log->getLimit();
- References
Tags and branches
Accessing tags and branches
With gitlib, you can access them via the ReferenceBag object. To get
this object from a Repository, use the getReferences method:$references = $repository->getReferences();
First, you can test existence of tags and branches like this:
if ($references->hasBranch('master') && $references->hasTag('0.1')) { echo 'Good start!'.PHP_EOL; }
If you want to access all branches or all tags:
$branches = $references->getBranches(); $localBranches = $references->getLocalBranches(); $remoteBranches = $references->getRemoteBranches(); $tags = $references->getTags(); $all = $references->getAll();
To get a given branch or tag, call getBranch or getTag on the
ReferenceBag. Those methods return Branch and Tag objects:$master = $references->getBranch('master'); $feat123 = $references->getLocalBranch('feat123'); $feat456 = $references->getRemoteBranch('origin/feat456'); $v0_1 = $references->getTag('0.1');
If the reference cannot be resolved, a ReferenceNotFoundException will
be thrown.On each of those objects, you can access those informations:
// Get the associated commit $commit = $master->getCommit(); // Get the commit hash $hash = $master->getCommitHash(); // Get the last modification $lastModification = $master->getLastModification();
Create and delete reference
You can create new tags and branches on repository, using helper methods
on ReferenceBag object:// create a branch $references = $repository->getReferences(); $branch = $references->createBranch('foobar', 'a8b7e4...'); // commit to reference // create a tag $references = $repository->getReferences(); $tag = $references->createTag('0.3', 'a8b7e4...'); // commit to reference // delete a branch or a tag $branch->delete();
Resolution from a commit
To resolve a branch or a commit from a commit, you can use the
resolveTags and resolveBranches methods on it:$branches = $references->resolveBranches($commit); $tags = $references->resolveTags($commit); // Resolve branches and tags $all = $references->resolve($commit);
You can pass a Commit object or a hash to the method, gitlib will
handle it. - Repository
Repository methods
Creating a Repository object is possible, providing a path argument
to the constructor:$repository = new Repository('/path/to/repo');
Repository options
The constructor of Repository takes an additional parameter:
$options
.
This parameter can be used used to tune behavior of library.Available options are:
- debug (default: true): Enables exception when edge cases are met
- environment_variables: (default: none) An array of environment
variables to be set in sub-process - logger: (default: none) Logger to use for reporting of execution
(aPsr\Log\LoggerInterface
) - command: (default:
git
) Specify command to execute to run git - working_dir: If you are using multiple working directories,
this option is for you
An example:
$repository = new Repository('/path/to/repo', [ 'debug' => true, 'logger' => new Monolog\Logger(), ]);
Test if a repository is bare
On a Repository object, you can call method isBare to test if your
repository is bare or not:$repository->isBare();
Compute size of a repository
To know how much size a repository is using on your drive, you can use
getSize
method on a Repository object.warning
This command was only tested with linux.
The returned size is in kilobytes:
$size = $repository->getSize(); echo 'Your repository size is '.$size.'KB';
Access HEAD
HEAD
represents in git the version you are working on (in working
tree). YourHEAD
can be attached (using a reference) or detached
(using a commit).$head = $repository->getHead(); // Commit or Reference $head = $repository->getHeadCommit(); // Commit if ($repository->isHeadDetached()) { echo 'Sorry man'.PHP_EOL; }
Options for repository
Logger
If you are developing, you may appreciate to have a logger inside
repository, telling you every executed command.You call method
setLogger
as an option on repository creation:$repository->setLogger(new Monolog\Logger('repository')); $repository->run('fetch', ['--all']);
You can also specify as an option on repository creation:
$logger = new MonologLogger('repository'); $repository = new Repository('/path/foo', ['logger' => $logger]); $repository->run('fetch', ['--all']);
This will output:
info run command: fetch "--all" debug last command (fetch) duration: 23.24ms debug last command (fetch) return code: 0 debug last command (fetch) output: Fetching origin
Disable debug-mode
Gitlib throws an exception when something seems wrong. If a
git
command exits
with a non-zero code, then execution will be stopped, and aRuntimeException
will be thrown. If you want to prevent this, set thedebug
option tofalse
.
This will makeRepository
log errors and return empty data instead of
throwing exceptions.$repository = new Repository('/tmp/foo', ['debug' => false, 'logger' => $logger]);
note
If you plan to disable debug, you should rely on the logger to keep a trace
of the failing cases.Specify git command to use
You can pass the option
command
to specify which command to use to run git
calls. If you have a git binary located somewhere else, use this option to
specify to gitlib path to your git binary:$repository = new Gitonomy\Git\Repository('/tmp/foo', ['command' => '/home/alice/bin/git']);
Environment variables
It is possible to send environment variables to the
git
commands.$repository = new Gitonomy\Git\Repository('/tmp/foo', ['environment_variables' => ['GIT_']])
- Revision
Revision
To get a revision from a Repository object:
$revision = $repository->getRevision('master@{2 days ago}');
Getting the log
You can access a Log object starting from a revision using the
getLog method. This method takes two parameters: offset and limit:// Returns 100 lasts commits $log = $revision->getLog(null, 100);
Resolve a revision
To resolve a revision to a commit:
$commit = $revision->getCommit();
- Tree
Tree and files
To organize folders, git uses trees. In gitlib, those trees are
represented via Tree object.To get the root tree associated to a commit, use the getTree method on
the commit object:$tree = $commit->getTree();
This tree is the entry point of all of your files.
The main method for a tree is the getEntries method. This method will
return an array, indexed by name. Each of those elements will be the
entry mode and the entry object.Let's understand how it works with a concrete example:
function displayTree(Tree $tree, $indent = 0) { $indent = str_repeat(' ', $indent); foreach ($tree->getEntries() as $name => $data) { list($mode, $entry) = $data; if ($entry instanceof Tree) { echo $indent.$name.'/'.PHP_EOL; displayTree($tree, $indent + 1); } else { echo $indent.$name.PHP_EOL; } } } displayTree($commit->getTree());
This method will recursively display all entries of a tree.
Resolve a path
To access directly a sub-file, the easier is probably to use the
resolvePath method.An example:
$source = $tree->resolvePath('src/Gitonomy/Git'); $source instanceof Tree;
- Workingcopy
Working copy
Working copy is the folder associated to a git repository. In gitlib,
you can access this object using the getWorkingCopy on a Repository
object:$repo = new Repository('/path/to/working-dir'); $wc = $repo->getWorkingCopy();
Checkout a revision
You can checkout any revision using checkout method. You can also pass
a second argument, which will be passed as argument with-b
:// git checkout master $wc->checkout('master'); // git checkout origin/master -b master $wc->checkout('origin/master', 'master');
You can also pass a Reference or a Commit.
Staged modifications
You can get a diff of modifications pending in staging area. To get the
Diff
object, call methodgetDiffStaged()
:$diff = $wc->getDiffStaged();
Pending modifications
You can get pending modifications on tracked files by calling method
getDiffPending()
:$diff = $wc->getDiffPending();
- Namespaces
\Gitonomy \Git \Blame \Gitonomy \Git \Diff \Gitonomy \Git \doc \Gitonomy \Git \Exception \Gitonomy \Git \Parser \Gitonomy \Git \Reference \Gitonomy \Git \Util - Classes
- Gitonomy
\Git \Admin - Gitonomy
\Git \Blame - Gitonomy
\Git \Blob - Gitonomy
\Git \Commit - Gitonomy
\Git \CommitReference - Gitonomy
\Git \Hooks - Gitonomy
\Git \Log - Gitonomy
\Git \PushReference - Gitonomy
\Git \Reference - Gitonomy
\Git \ReferenceBag - Gitonomy
\Git \Repository - Gitonomy
\Git \Revision - Gitonomy
\Git \RevisionList - Gitonomy
\Git \Tree - Gitonomy
\Git \WorkingCopy