[prev in list] [next in list] [prev in thread] [next in thread] 

List:       owncloud
Subject:    [Owncloud]  Files versioning with glip
From:       François_K. <daitheflu () free ! fr>
Date:       2011-03-15 14:24:56
Message-ID: 1197182515.7258751300199096330.JavaMail.root () zimbra9-e2 ! priv ! proxad ! net
[Download RAW message or body]

Hi !

As some might know, I played a bit with Glip (http://fimml.at/#glip), mostly because \
I was curious to see what could be achieved with it. Hopefully, the guy that wrote \
Glip also wrote a Wiki engine -called eWiki- that uses Glip, so, I had a great \
example :)

*Please keep in mind that this was just testing.*
I still hope it will be useful.

1/ My first goal was to get one file history. There is no 'git log' thing, so I had \
to reproduce its behaviour. The result is pretty good, but not the same as the one \
                'git log -- file' would produce. There are two main reasons for this \
                :
- My small function only returns commits where the content of the considered file has \
                changed,
- It ignores merge commits (normally, we will have one and only one branch, so merge \
commits shouldn't exist).

The major drawback is that I had to loop through every commit and compare each of \
them with its parent to see if the file has changed or not. This can be time and \
resource consuming with big repo. I sadly haven't found another solution.



2/ The second goal was to write "objects" to the repository. This is where things \
begin to be fun ^^

The first drawback is that the repo has to exist, and a first commit must have been \
done so that the 'master' branch actually exists. I didn't find a solution to create \
the branch via PHP, so I did it by hand, for testing purpose. We will have to find a \
solution for this. Maybe some git expert could help us.

The example given by the eWiki source code shows that the dev has chosen to build a \
new branch for each commit, and to merge it with the 'master' one. I don't really \
know why... I decided to keep a single branch. So I update the current tree, build \
commit details and write everything to the repository. It seems to work quite well. I \
clearly lack some knowledge in git, and I'd like someone that knows it very well to \
check if it works well or not. I ran 'git fsck' and it seemd to be OK, yet not sure.



I joined my source code for those who are interested in it. Please excuse me, it's \
really poor and would need some huge refactor. This was done on the fly for testing \
                purpose. You'll need Glip to run it.
- index.php is only about reading data from a repo.
- index2.php will try to build a commit for a single file and then show this file \
history.

In both cases, you will need a repo with a least one commit (so that the master \
branch exists). I *strongly* recommend you not to use a working repo if you want to \
test it. 'git clone' one or build a new one quickly. For index2.php to work, you will \
need to 'chmod' the git repo so that your webserver has read/write access to it.

You can edit everything at your own convenience.

And, as always, ideas and comments are welcome.

Cheers,

-- 
François


["index.php" (text/php)]

<?php
    // This is for testing purpose only !

    // Path to the repository :
    $repository = dirname(__FILE__).'/owncloud/.git';
    
    // File that's going to be examined :
    $f = "index.php";

    // Sets timezone to GMT :
    date_default_timezone_set("GMT");
    
    // Glip is required, of course :
    require_once(dirname(__FILE__).'/3rdparty/glip/lib/glip.php');
    
    // Let's go :
    $repo = new Git($repository);


    
    // -------------------------------------------------------------------------



    function getVersions($file)
    {
        global $repo;
        $r = array();
        $branch = $repo->getObject($repo->getTip('master'));
        $commits = $branch->getHistory();

        foreach($commits as $commit)
        {
            $p = FALSE;
            $parent = $commit->parents[0];

            if(!empty($parent))
            {
                $parent = $commit->repo->getObject($parent);
                $p = $parent->find($file);

                if($p===FALSE)
                    $p = NULL;

                // Get our blob in the currently examined commit :
                $blob = $commit->find($file);

                // Check if it is the same as the 1st parent's one :
                if($blob!==$p)
                {
                    $r[] = $commit;
                }
            }
        }

        $r = array_reverse($r);
        return $r;
    }
    
    

    // -------------------------------------------------------------------------



    // Checkout the 'master' branch :
    $master_name = $repo->getTip('master');
    $commit = $repo->getObject($master_name); //returns GitCommit object that refers \
to the last commit of the 'master' branch.

    // Print information about the last revision :
    print "Last revision on branch master (".sha1_hex($commit->getName())."):<br />";
    print $commit->author->name." (".$commit->author->email.")<br />";
    print date("r", $commit->committer->time)."<br />";
    print $commit->summary."<br /><br />";



    // -------------------------------------------------------------------------



    $versions = getVersions($f);
    print "History of file <em>".$f."</em> (".sizeof($versions)." revisions) :<br \
/><br />";  foreach($versions as $v)
    {
        print "Commit ID : ".sha1_hex($v->getName())."<br />";
        print "date : ".date("r", $v->committer->time)."<br /><br />";

        //$entry = new stdClass;
        //$entry->summary = $v->summary;
        //$entry->author = $v->author->name;
        //$entry->time = $v->author->time;
        //$entry->commit = sha1_hex($v->getName());
        //array_unshift($history, $entry);
    }
?>


["index2.php" (text/php)]

<?php
    // Path to the repository :
    $repository = dirname(__FILE__).'/.git';
    
    // File that's going to be examined :
    $file = "index.php";

    // Sets timezone to GMT :
    date_default_timezone_set("GMT");
    
    // Glip is required, of course :
    require_once(dirname(__FILE__).'/3rdparty/glip/lib/glip.php');
    
    // Let's go :
    $repo = new Git($repository);
    $branch = $repo->getObject($repo->getTip('master'));

    $content = file_get_contents($file);


    
    // -------------------------------------------------------------------------
    // Function definition :


    function getVersions($file)
    {
        global $repo;
        $r = array();
        $branch = $repo->getObject($repo->getTip('master'));
        $commits = $branch->getHistory();

        foreach($commits as $commit)
        {
            $p = FALSE;
            $parent = $commit->parents[0];

            if(!empty($parent))
            {
                $parent = $commit->repo->getObject($parent);
                $p = $parent->find($file);

                if($p===FALSE)
                    $p = NULL;

                // Get our blob in the currently examined commit :
                $blob = $commit->find($file);

                // Check if it is the same as the 1st parent's one :
                if($blob!==$p)
                {
                    $r[] = $commit;
                }
            }
        }

        $r = array_reverse($r);
        return $r;
    }
    
    

    // -------------------------------------------------------------------------
    // Commits a new version of the $file file :


    // FIXME:
    // BEFORE COMMITTING :
    //  * Check if $f file exists.
    //  * Check if $f file content has changed.
    //  * 

    $pending = array(); // Contains all objects that need to be written.

    $blob = new GitBlob($repo);
    $blob->data = $content;
    $blob->rehash();
    $pending[] = $blob;

    $f = fopen(sprintf('%s/refs/heads/%s', $repo->dir, 'master'), 'a+b');
    flock($f, LOCK_EX);
    $ref = stream_get_contents($f);

    $tree = $repo->getObject($branch->tree);
    $pending = array_merge($pending, $tree->updateNode($file, 0100640, $blob->getName()));
    $tree->rehash();
    $pending[] = $tree;
  
    $stamp = new GitCommitStamp();
    $stamp->name = "ownCloud_versionner";       // Needed.
    $stamp->email = "owncloud@example.org";     // Needed.
    $stamp->time = time();
    $stamp->offset = idate('Z', $stamp->time);

    $newcommit = new GitCommit($repo);
    $newcommit->tree = $tree->getName();
    $newcommit->parents = array($branch->getName());
    $newcommit->author = $stamp;
    $newcommit->committer = $stamp;
    $newcommit->summary = sprintf('%s: %s', $file, "Automatically saved new version.");
    $newcommit->detail = '';
    $newcommit->rehash();
    $pending[] = $newcommit;

    foreach($pending as $obj)
        $obj->write();
    
    ftruncate($f, 0);
    fwrite($f, sha1_hex($newcommit->getName()));
    fclose($f);



    // -------------------------------------------------------------------------
    // Display $file history :


    $versions = getVersions($file);
    print "History of file <em>".$file."</em> (".sizeof($versions)." revisions) :<br /><br />";
    foreach($versions as $v)
    {
        print "Commit ID : ".sha1_hex($v->getName())."<br />";
        var_dump($v->committer);
        print "date : ".date("r", $v->committer->time)."<br /><br />";

        //$entry = new stdClass;
        //$entry->summary = $v->summary;
        //$entry->author = $v->author->name;
        //$entry->time = $v->author->time;
        //$entry->commit = sha1_hex($v->getName());
        //array_unshift($history, $entry);
    }
?>

_______________________________________________
Owncloud mailing list
Owncloud@kde.org
https://mail.kde.org/mailman/listinfo/owncloud


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic