libgit2/php-git

Use case: git log master..foobar

MattKetmo opened this issue · 9 comments

This is not an issue but rather a question.

How would you perform a git log master..foobar with php-git (ie. display all commits on a foobar branch which are not on the master branch)?

It's quite easy to do a git log with the Git2\Walker, but is there a simple way to know if a commit belongs to another branch in order to not display it. I mean, can we do this without traverse the master branch and store all commits in a hash table (which can be performance killer depending on the size of the tree).

hmm, interesting. there are push and hide API but I don't know how to perform git ^master foobar at this time.
rugged has samples for walkler operations here: https://github.com/libgit2/rugged/blob/development/USAGE.rb#L116-132
but I didn't find out.

I'll ask someone or think more deeply.

for reference man gitrevisions (as shorthands makes confusing me 😢 )

PECIFYING RANGES
       History traversing commands such as git log operate on a set of commits, not just a single commit. To these commands,
       specifying a single revision with the notation described in the previous section means the set of commits reachable from
       that commit, following the commit ancestry chain.

       To exclude commits reachable from a commit, a prefix ^ notation is used. E.g. ^r1 r2 means commits reachable from r2 but
       exclude the ones reachable from r1.

       This set operation appears so often that there is a shorthand for it. When you have two commits r1 and r2 (named according
       to the syntax explained in SPECIFYING REVISIONS above), you can ask for commits that are reachable from r2 excluding those
       that are reachable from r1 by ^r1 r2 and it can be written as r1..r2.

       A similar notation r1...r2 is called symmetric difference of r1 and r2 and is defined as r1 r2 --not $(git merge-base
       --all r1 r2). It is the set of commits that are reachable from either one of r1 or r2 but not from both.

       Two other shorthands for naming a set that is formed by a commit and its parent commits exist. The r1^@ notation means all
       parents of r1. r1^! includes commit r1 but excludes all of its parents.

Hello, did you find some help on this subject?

I don't know if the hide api of the walker is well implemented in this php-git extension, but it doesn't seem to work like the rugged example.

For instance:

<?php
$repo = new Git2\Repository($path);
$walker = new Git2\Walker($repo);

$walker->push("aa656cd2d6c7cad8e4203657a60e09ba8d21153d"); // branch foo
$walker->hide("47f23546f2162da01681f72e114fa7cfaf068c8a"); // branch bar

foreach ($walker as $oid => $commit) {
    printf("oid: %s\n", $oid);
    printf("message: %s\n", $commit->getMessage());
}

does not hide at all the bar branch (even not the commit 47f23546f).

Also, do you know what is the best way to retrieve the status of a branch (as the git-status command shows: Your branch is behind 'origin/master' by 4 commits), to know how many commits differ between two branches?

sorry for late response. I've added Git2\Repository::getMergeBase method on 8aa140f and it seems useful in this case.

<?php
$repo = new Git2\Repository(".git"); // current php-git repository
$walker = new Git2\Walker($repo);

$walker->push("8aa140f17932c730daa5afb44c440e4ba527a800"); // branch feature/mergebase
$walker->hide("fe4cb28947a9e2cafafb98db6c97c87187d69a7b"); // branch develop

$base = $repo->getMergeBase("8aa140f17932c730daa5afb44c440e4ba527a800", "fe4cb28947a9e2cafafb98db6c97c87187d69a7b");

foreach ($walker as $oid => $commit) {
    if ($commit->getOid() == $base) { // stop iteration when walker leached merge base.
        break;
    }

    printf("oid: %s\n", $oid);
    printf("message: %s\n", $commit->getMessage());
}

maybe this method solves your issue.

Also, do you know what is the best way to retrieve the status of a branch (as the git-status command shows: Your branch is behind 'origin/master' by 4 commits), to know how many commits differ between two branches?

also getMergeBase method will solve. commit object only knows their parents. so you need count commits with walker.

Thanks for getMergeBase(), that's really usefull.

However, I think there is still a problem with the implementation of the hide API of the walker.
According to http://libgit2.github.com/api.html#revwalk, this code should do exactly the same without the break;.

https://github.com/libgit2/libgit2/blob/development/include/git2/revwalk.h#L128 :

Mark a commit (and its ancestors) uninteresting for the output.

nice suggestion. current revwalk implementation is completely broken. please wait a sec.

can you try to use fix/revwalk branch? I'll merge it into develop if it looks good.

Hum, I got a strange behaviour:

$walker->push("8aa140f17932c730daa5afb44c440e4ba527a800"); // branch fix/revwalk
$walker->hide("3594b7619b05230bea88a44ecf11df15cf9283ed"); // branch develop
foreach ($walker as $oid => $commit) {
    printf("oid: %s\n", $oid);
    printf("message: %s\n", $commit->getMessage());
}

/*
oid: 986abd0001000000986abd0001000000f06e0201
PHP Fatal error:  Call to a member function getMessage() on a non-object in git-test.php on line 17
*/

(I don't know where this oid comes from)

However, if I inverse the push and hide oids, then I get what I excepted with the previous code:

$walker->push("3594b7619b05230bea88a44ecf11df15cf9283ed"); // branch develop
$walker->hide("8aa140f17932c730daa5afb44c440e4ba527a800"); // branch fix/revwalk
foreach ($walker as $oid => $commit) {
    printf("oid: %s\n", $oid);
    printf("message: %s\n", $commit->getMessage());
}

/*
oid: 3594b7619b05230bea88a44ecf11df15cf9283ed
message: revwalk: fix revwalk problem.
*/

The first code should be similar to

$ git log origin/fix/revwalk ^develop
commit 3594b7619b05230bea88a44ecf11df15cf9283ed
Author: Shuhei Tanuma <shuhei.tanuma@gmail.com>
Date:   Mon Aug 27 23:46:44 2012 +0900

    revwalk: fix revwalk problem.

whereas the second:

$ git log develop ^origin/fix/revwalk # outputs nothing

Thanks check it.

previous revwalk codes always reset hided oid when it starts foreach statement.
I'm working to solve this issue and it will take little time.

(I don't know where this oid comes from)

first thing is a bug. please never mind it. I forget error handling. I've just fixed it on fix/revwalk branch right now.

 git log develop ^origin/fix/revwalk

this means: commits reachable develop from but exclude the ones reachable from origin/fix/revwalk. (I can't explain it in English correctly. but i guess it's strange range.)
so, git_revwalk_next will returns GIT_REVWALKOVER when it passed.

sorry, I don't have enough time to fix this issue in this month. I'll fix until 9/Sep.