Package Documentation

  • Readme

    Gitlib for Gitonomy

    Build Status
    StyleCI
    License
    Downloads

    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,pass false 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 use git 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
      (a Psr\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). Your HEAD 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 a RuntimeException
    will be thrown. If you want to prevent this, set the debug option to false.
    This will make Repository 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 method getDiffStaged():

    $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
© 2022 Bruce Wells
Search Namespaces \ Classes
Configuration