Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EGit » JGit RevWalk & GitBlit Pre-Receive Hook
JGit RevWalk & GitBlit Pre-Receive Hook [message #1259583] Fri, 28 February 2014 10:06 Go to next message
Michael Quigley is currently offline Michael Quigley
Messages: 2
Registered: February 2014
Junior Member
I'm working on creating a pre-receive hook for GitBlit (which uses JGit). In my pre-receive hook I'm trying to reliably get access to just the new commits when a push is made against a repository. I've borrowed one of the getRevLog methods from Gitblit's JGitUtils class and modified it slightly. I'm using:

public static List<RevCommit> getRevLog(Repository repository, String startRangeId, String endRangeId) {
    List<RevCommit> list = new ArrayList<RevCommit>();
    if(!hasCommits(repository)) {
          return list;
    }
     
    try {
        ObjectId endRange = repository.resolve(endRangeId);
        ObjectId startRange = repository.resolve(startRangeId);

        RevWalk rw = new RevWalk(repository);
        rw.markStart(rw.parseCommit(endRange));
        if(!startRange.equals(ObjectId.zeroId())) {
            rw.markUninteresting(rw.parseCommit(startRange));
        }

        Iterable<RevCommit> revlog = rw;
        for(RevCommit rev : revlog) {
            list.add(rev);
        }
           
        rw.dispose();
           
     } catch (Throwable t) {
        log.error("Failure.", t);
     }
       
    return list;
}


In my code, startRangeId and endRangeId are coming from the old id and new id retrieved from a ReceiveCommand.

This is doing the right thing whenever a push is made along the master branch, without specifying a branch. So, if I create a repository, make some changes along the master branch, and push it to the server, my hook is seeing the correct changes. Then if I make a couple more changes along the master branch and then just do a 'git push', my hook is seeing just the new changes, not any old changes (and startRangeId is set to a non-zero id). If I branch from an older commit, commit some changes, and then try to push it to the server with 'git push myRepositoryUrl mybranch', my hook is seeing all of the commits from the beginning of time (and my startRangeId is set to the zero id).

Is it possible to implement my hook, such that I can only see the new changes, even when using branches? Or, do I need to implement some sort of tracking database of my own (to make sure my code doesn't re-process commits that it has already processed)?

I'm sure there's something I'm not getting about how RevWalk works, or the structure of commits pushed against a Git repository. Any help would be appreciated!
Re: JGit RevWalk & GitBlit Pre-Receive Hook [message #1262141 is a reply to message #1259583] Mon, 03 March 2014 04:58 Go to previous messageGo to next message
Christian Halstrick is currently offline Christian Halstrick
Messages: 99
Registered: July 2009
Member
what you explain makes sense. If the client thinks he creates a new branch he will specify as old object id null. And then your code will find everything reachable from the new commit.

I am not sure whether it is a good idea to use the oldId as input. oldId is client side knowledge and how could a client alone determine which commits are new for the server.

I think you should use a revwalk where endRangeId is marked as start (as it is now) and where all refs which you had before the receive are marked as uninteresting. In order to get to the set of refs as they have been before the receive you maybe need a PreReceiveHook, collect all the ref targets and then use that as uninteresting during the postreceivehook revwalk.


Ciao
Chris
Re: JGit RevWalk & GitBlit Pre-Receive Hook [message #1262484 is a reply to message #1262141] Mon, 03 March 2014 12:35 Go to previous messageGo to next message
Michael Quigley is currently offline Michael Quigley
Messages: 2
Registered: February 2014
Junior Member
Christian,

Thanks for responding. I think you may have given me some clues to try and work out my problem. I'm working with RevWalk and trying to figure out the semantics of how Git and the JGit implementation work.

Let me try asking this a simpler way... For any push to JGit (in a pre-receive hook), how can I tell which commits are new as a result of the push, and which commits were already in the repository (disregarding branches for now)? That's the simplest version of the problem I'm trying to solve.

I can't figure out how to differentiate between the commits that were already in the repository, and the changes that arrive with the push. Do I need to implement my own tracking database, or is this something that I can just extract from the repository during my pre-receive hook processing?

I suspect that the Ref for the head of the branch being pushed to might be useful instead of "oldId" in my example above? Does that Ref get updated AFTER the pre-receive hook runs? Surely there must be some way to know which commits are new with the push?

Thanks!
Michael
Re: JGit RevWalk & GitBlit Pre-Receive Hook [message #1265831 is a reply to message #1262484] Thu, 06 March 2014 11:16 Go to previous messageGo to next message
Christian Halstrick is currently offline Christian Halstrick
Messages: 99
Registered: July 2009
Member
Imagine you have this history on the client:

Quote:


C - master
|
B - side
|
A - origin/master, origin/side



If you know push master to origin the the pre-receive hook will get A as old id, and C as new id. Doing a revwalk on origin from C (start) to A (uninteresting) will correctly tell C,B are the new commits.

But if you then push your local side branch to origins side branch (git push origin side:refs/heads/side) then the pre-receive hook will get as old id of the side branch A and as new id B. Doing a revwalk from B (start) to A (uninteresting) will tell B is the new commit. But that's wrong. The server already knows B. He got B in the previous push of the master branch.

I am not completely sure. But I think you really have to do a revwalk from new new id (start) to "all refs the server currently knows (all branches,tags,..)"(uninteresting). That will tell you the commits the server will receive through this push which he didn't knew before.

The following script sets up a central server which special receive-hooks which tell what data the hooks receive and how the refs look like. I tested that script only on a linux box. No guarantee that it works somewhere else than on my box

#!/bin/bash
set -x

# create central repo
git init central
cd central
git commit --allow-empty -m initial
git branch side
cd ..
mv central/.git central.git
rm -fr central
git --git-dir central.git config core.bare true
echo -e '#!/bin/bash \n echo -e "pre-receive:\nrefs:\n$(git show-ref)\nInput:" && while read line ;do echo $line ;don
echo -e '#!/bin/bash \n echo -e "post-receive:\nrefs:\n$(git show-ref)\nInput:" && while read line ;do echo $line ;do
chmod +x central.git/hooks/*-receive

# start a daemon
port=$((( RANDOM % 1000 ) + 25000 ))
git daemon --port=$port --verbose --export-all --enable=receive-pack --base-path=. &
sleep 3

# create developers repo, modify and push
git clone git://localhost:$port/central.git developer
cd developer
git commit --allow-empty -m modifiedByDev
git checkout -b side origin/side
git merge master
git checkout master
git commit --allow-empty -m modifiedByDev2
git push origin master:refs/heads/master
git checkout side
git push origin side:refs/heads/side

# stop the daemon
kill %%


Ciao
Chris
Re: JGit RevWalk & GitBlit Pre-Receive Hook [message #1265834 is a reply to message #1265831] Thu, 06 March 2014 11:18 Go to previous message
Christian Halstrick is currently offline Christian Halstrick
Messages: 99
Registered: July 2009
Member
Sorry, the script had some lines which got cut. Here is the correct script:

#!/bin/bash

set -x
rm -fr central.git developer

# create central repo
git init central
cd central
git commit --allow-empty -m initial
git branch side
cd ..
mv central/.git central.git
rm -fr central
git --git-dir central.git config core.bare true
echo -e '#!/bin/bash \n echo -e "pre-receive:\nrefs:\n$(git show-ref)\nInput:" && while read line ;do echo $line ;done' > central.git/hooks/pre-receive
echo -e '#!/bin/bash \n echo -e "post-receive:\nrefs:\n$(git show-ref)\nInput:" && while read line ;do echo $line ;done' > central.git/hooks/post-receive
chmod +x central.git/hooks/*-receive

# start a daemon
port=$((( RANDOM % 1000 ) + 25000 ))
git daemon --port=$port --verbose --export-all --enable=receive-pack --base-path=. &
sleep 3

# create developers repo, modify and push
git clone git://localhost:$port/central.git developer
cd developer
git commit --allow-empty -m modifiedByDev
git checkout -b side origin/side
git merge master
git checkout master
git commit --allow-empty -m modifiedByDev2
git push origin master:refs/heads/master
git checkout side
git push origin side:refs/heads/side

# stop the daemon
kill %%




Ciao
Chris
Previous Topic:eclipse team project set with egit
Next Topic:Merge tool - better remove conflict markers functionality when rejecting change?
Goto Forum:
  


Current Time: Wed Jul 23 01:55:09 EDT 2014

Powered by FUDForum. Page generated in 0.01800 seconds