org.eclipse.orion.server.git.servlets.GitCommitHandlerV1.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.orion.server.git.servlets.GitCommitHandlerV1.java

Source

/*******************************************************************************
 * Copyright (c) 2011 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.orion.server.git.servlets;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.core.runtime.*;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.RebaseCommand.Operation;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.*;
import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.*;
import org.eclipse.orion.internal.server.core.IOUtilities;
import org.eclipse.orion.internal.server.servlets.ProtocolConstants;
import org.eclipse.orion.internal.server.servlets.ServletResourceHandler;
import org.eclipse.orion.server.core.ServerStatus;
import org.eclipse.orion.server.core.users.UserUtilities;
import org.eclipse.orion.server.git.*;
import org.eclipse.orion.server.git.servlets.GitUtils.Traverse;
import org.eclipse.orion.server.servlets.OrionServlet;
import org.eclipse.osgi.util.NLS;
import org.json.*;

/**
 * 
 */
public class GitCommitHandlerV1 extends ServletResourceHandler<String> {

    private final static int PAGE_SIZE = 50;

    private ServletResourceHandler<IStatus> statusHandler;

    GitCommitHandlerV1(ServletResourceHandler<IStatus> statusHandler) {
        this.statusHandler = statusHandler;
    }

    @Override
    public boolean handleRequest(HttpServletRequest request, HttpServletResponse response, String path)
            throws ServletException {

        Repository db = null;
        try {
            Path p = new Path(path);
            switch (getMethod(request)) {
            case GET:
                return handleGet(request, response, db, p);
            case PUT:
                return handlePut(request, response, db, p);
            case POST:
                return handlePost(request, response, db, p);
            }

        } catch (Exception e) {
            String msg = NLS.bind("Failed to process an operation on commits for {0}", path);
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, e));
        } finally {
            if (db != null)
                db.close();
        }
        return false;
    }

    private boolean createCommitLocation(HttpServletRequest request, HttpServletResponse response, Repository db,
            String newCommitToCreatelocation) throws IOException, JSONException, URISyntaxException {
        URI u = getURI(request);
        IPath p = new Path(u.getPath());
        IPath np = new Path("/"); //$NON-NLS-1$
        for (int i = 0; i < p.segmentCount(); i++) {
            String s = p.segment(i);
            if (i == 2) {
                s += ".." + newCommitToCreatelocation; //$NON-NLS-1$
            }
            np = np.append(s);
        }
        if (p.hasTrailingSeparator())
            np = np.addTrailingSeparator();
        URI nu = new URI(u.getScheme(), u.getUserInfo(), u.getHost(), u.getPort(), np.toString(), u.getQuery(),
                u.getFragment());
        response.setHeader(ProtocolConstants.HEADER_LOCATION, nu.toString());
        response.setStatus(HttpServletResponse.SC_OK);
        return true;
    }

    private boolean handleGet(HttpServletRequest request, HttpServletResponse response, Repository db, Path path)
            throws CoreException, IOException, ServletException, JSONException, URISyntaxException {
        if (path.segment(0).equals("file")) { //$NON-NLS-1$
            // special case for git log --all
            IPath filePath = path.hasTrailingSeparator() ? path : path.removeLastSegments(1);
            Set<Entry<IPath, File>> set = GitUtils.getGitDirs(filePath, Traverse.GO_UP).entrySet();
            File gitDir = set.iterator().next().getValue();
            if (gitDir == null)
                return false; // TODO: or an error response code, 405?

            db = new FileRepository(gitDir);

            String pattern = GitUtils.getRelativePath(path, set.iterator().next().getKey());
            return handleGetCommitLog(request, response, db, null, pattern);

        } else if (path.segment(1).equals("file")) { //$NON-NLS-1$
            // git log branch_name
            IPath filePath = path.hasTrailingSeparator() ? path.removeFirstSegments(1)
                    : path.removeFirstSegments(1).removeLastSegments(1);
            Set<Entry<IPath, File>> set = GitUtils.getGitDirs(filePath, Traverse.GO_UP).entrySet();
            File gitDir = set.iterator().next().getValue();
            if (gitDir == null)
                return false; // TODO: or an error response code, 405?

            db = new FileRepository(gitDir);

            // /{ref}/file/{projectId}...}
            String parts = request.getParameter("parts"); //$NON-NLS-1$
            String pattern = GitUtils.getRelativePath(path, set.iterator().next().getKey());
            if (path.segmentCount() > 3 && "body".equals(parts)) { //$NON-NLS-1$
                return handleGetCommitBody(request, response, db, path.segment(0), pattern);
            }
            if (path.segmentCount() > 2 && (parts == null || "log".equals(parts))) { //$NON-NLS-1$
                return handleGetCommitLog(request, response, db, path.segment(0), pattern);
            }
        }
        return false;
    }

    private boolean handleGetCommitBody(HttpServletRequest request, HttpServletResponse response, Repository db,
            String ref, String pattern) throws AmbiguousObjectException, IOException, ServletException {
        ObjectId refId = db.resolve(ref);
        if (refId == null) {
            String msg = NLS.bind("Failed to generate commit log for ref {0}", ref);
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
        }

        RevWalk walk = new RevWalk(db);
        walk.setTreeFilter(AndTreeFilter.create(PathFilterGroup.createFromStrings(Collections.singleton(pattern)),
                TreeFilter.ANY_DIFF));
        RevCommit commit = walk.parseCommit(refId);
        final TreeWalk w = TreeWalk.forPath(db, pattern, commit.getTree());
        if (w == null) {
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, null, null));
        }

        ObjectId blobId = w.getObjectId(0);
        ObjectStream stream = db.open(blobId, Constants.OBJ_BLOB).openStream();
        IOUtilities.pipe(stream, response.getOutputStream(), true, false);

        return true;
    }

    private boolean handleGetCommitLog(HttpServletRequest request, HttpServletResponse response, Repository db,
            String refIdsRange, String path) throws AmbiguousObjectException, IOException, ServletException,
            JSONException, URISyntaxException, CoreException {
        int page = request.getParameter("page") != null ? new Integer(request.getParameter("page")).intValue() : 0; //$NON-NLS-1$ //$NON-NLS-2$
        int pageSize = request.getParameter("pageSize") != null //$NON-NLS-1$
                ? new Integer(request.getParameter("pageSize")).intValue() //$NON-NLS-1$
                : PAGE_SIZE;

        ObjectId toObjectId = null;
        ObjectId fromObjectId = null;

        Ref toRefId = null;
        Ref fromRefId = null;

        Git git = new Git(db);
        LogCommand log = git.log();

        if (refIdsRange != null) {
            // git log <since>..<until>
            if (refIdsRange.contains("..")) { //$NON-NLS-1$
                String[] commits = refIdsRange.split("\\.\\."); //$NON-NLS-1$
                if (commits.length != 2) {
                    String msg = NLS.bind("Failed to generate commit log for ref {0}", refIdsRange);
                    return statusHandler.handleRequest(request, response,
                            new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
                }

                fromObjectId = db.resolve(commits[0]);
                fromRefId = db.getRef(commits[0]);
                if (fromObjectId == null) {
                    String msg = NLS.bind("Failed to generate commit log for ref {0}", commits[0]);
                    return statusHandler.handleRequest(request, response,
                            new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
                }

                toObjectId = db.resolve(commits[1]);
                toRefId = db.getRef(commits[1]);
                if (toObjectId == null) {
                    String msg = NLS.bind("No ref or commit found: {0}", commits[1]);
                    return statusHandler.handleRequest(request, response,
                            new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, msg, null));
                }
            } else {
                toObjectId = db.resolve(refIdsRange);
                toRefId = db.getRef(refIdsRange);
                if (toObjectId == null) {
                    String msg = NLS.bind("No ref or commit found: {0}", refIdsRange);
                    return statusHandler.handleRequest(request, response,
                            new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, msg, null));
                }
            }

            // set the commit range
            log.add(toObjectId);

            if (fromObjectId != null)
                log.not(fromObjectId);
        } else {
            // git log --all
            // workaround for git log --all - see bug 353310
            List<Ref> branches = git.branchList().setListMode(ListMode.ALL).call();
            for (Ref branch : branches) {
                log.add(branch.getObjectId());
            }
        }

        // set the path filter
        TreeFilter filter = null;

        boolean isRoot = true;
        if (path != null && !"".equals(path)) { //$NON-NLS-1$
            filter = AndTreeFilter.create(PathFilterGroup.createFromStrings(Collections.singleton(path)),
                    TreeFilter.ANY_DIFF);
            log.addPath(path);
            isRoot = false;
        }

        try {
            Iterable<RevCommit> commits = log.call();
            Map<ObjectId, JSONArray> commitToBranchMap = getCommitToBranchMap(db);
            JSONObject result = toJSON(db, OrionServlet.getURI(request), commits, commitToBranchMap, page, pageSize,
                    filter, isRoot);

            result.put(GitConstants.KEY_REPOSITORY_PATH, isRoot ? "" : path); //$NON-NLS-1$
            if (refIdsRange == null)
                result.put(GitConstants.KEY_CLONE,
                        BaseToCloneConverter.getCloneLocation(getURI(request), BaseToCloneConverter.COMMIT));
            else
                result.put(GitConstants.KEY_CLONE, BaseToCloneConverter.getCloneLocation(getURI(request),
                        BaseToCloneConverter.COMMIT_REFRANGE));

            if (toRefId != null) {
                result.put(GitConstants.KEY_REMOTE, BaseToRemoteConverter.getRemoteBranchLocation(getURI(request),
                        Repository.shortenRefName(toRefId.getName()), db, BaseToRemoteConverter.REMOVE_FIRST_3));

                String refTargetName = toRefId.getTarget().getName();
                if (refTargetName.startsWith(Constants.R_HEADS)) {
                    // this is a branch
                    result.put(GitConstants.KEY_LOG_TO_REF,
                            BranchToJSONConverter.toJSON(toRefId.getTarget(), db, getURI(request), 3));
                }
            }
            if (fromRefId != null) {
                String refTargetName = fromRefId.getTarget().getName();
                if (refTargetName.startsWith(Constants.R_HEADS)) {
                    // this is a branch
                    result.put(GitConstants.KEY_LOG_FROM_REF,
                            BranchToJSONConverter.toJSON(fromRefId.getTarget(), db, getURI(request), 3));
                }
            }

            OrionServlet.writeJSONResponse(request, response, result);
            return true;
        } catch (NoHeadException e) {
            String msg = NLS.bind("No HEAD reference found when generating log for ref {0}", refIdsRange);
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, e));
        } catch (JGitInternalException e) {
            String msg = NLS.bind("An internal error occured when generating log for ref {0}", refIdsRange);
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, e));
        }
    }

    private JSONObject toJSON(Repository db, URI baseLocation, Iterable<RevCommit> commits,
            Map<ObjectId, JSONArray> commitToBranchMap, int page, int pageSize, TreeFilter filter, boolean isRoot)
            throws JSONException, URISyntaxException, MissingObjectException, IOException {
        boolean pageable = (page > 0);
        int startIndex = (page - 1) * pageSize;
        int index = 0;

        JSONObject result = new JSONObject();
        JSONArray children = new JSONArray();
        for (RevCommit revCommit : commits) {
            if (pageable && index < startIndex) {
                index++;
                continue;
            }

            if (pageable && index >= startIndex + pageSize)
                break;

            index++;

            children.put(toJSON(db, revCommit, commitToBranchMap, baseLocation, filter, isRoot));
        }
        result.put(ProtocolConstants.KEY_CHILDREN, children);
        return result;
    }

    private JSONObject toJSON(Repository db, RevCommit revCommit, Map<ObjectId, JSONArray> commitToBranchMap,
            URI baseLocation, TreeFilter filter, boolean isRoot)
            throws JSONException, URISyntaxException, IOException {
        JSONObject commit = new JSONObject();
        commit.put(ProtocolConstants.KEY_LOCATION, createCommitLocation(baseLocation, revCommit.getName(), null));
        commit.put(ProtocolConstants.KEY_CONTENT_LOCATION,
                createCommitLocation(baseLocation, revCommit.getName(), "parts=body")); //$NON-NLS-1$
        commit.put(GitConstants.KEY_DIFF,
                createDiffLocation(baseLocation, revCommit.getName(), null, null, isRoot));
        commit.put(ProtocolConstants.KEY_NAME, revCommit.getName());
        PersonIdent author = revCommit.getAuthorIdent();
        commit.put(GitConstants.KEY_AUTHOR_NAME, author.getName());
        commit.put(GitConstants.KEY_AUTHOR_EMAIL, author.getEmailAddress());
        String authorImage = UserUtilities.getImageLink(author.getEmailAddress());
        if (authorImage != null)
            commit.put(GitConstants.KEY_AUTHOR_IMAGE, authorImage);
        PersonIdent committer = revCommit.getCommitterIdent();
        commit.put(GitConstants.KEY_COMMITTER_NAME, committer.getName());
        commit.put(GitConstants.KEY_COMMITTER_EMAIL, committer.getEmailAddress());
        commit.put(GitConstants.KEY_COMMIT_TIME,
                ((long) revCommit.getCommitTime()) * 1000 /* time in milliseconds */);
        commit.put(GitConstants.KEY_COMMIT_MESSAGE, revCommit.getFullMessage());
        commit.put(GitConstants.KEY_TAGS, toJSON(getTagsForCommit(db, revCommit)));
        commit.put(ProtocolConstants.KEY_TYPE, GitConstants.COMMIT_TYPE);
        commit.put(GitConstants.KEY_BRANCHES, commitToBranchMap.get(revCommit.getId()));
        commit.put(ProtocolConstants.KEY_PARENTS, parentsToJSON(revCommit.getParents()));

        if (revCommit.getParentCount() > 0) {
            JSONArray diffs = new JSONArray();

            final TreeWalk tw = new TreeWalk(db);
            final RevWalk rw = new RevWalk(db);
            RevCommit parent = rw.parseCommit(revCommit.getParent(0));
            tw.reset(parent.getTree(), revCommit.getTree());
            tw.setRecursive(true);

            if (filter != null)
                tw.setFilter(filter);
            else
                tw.setFilter(TreeFilter.ANY_DIFF);

            List<DiffEntry> l = DiffEntry.scan(tw);
            for (DiffEntry entr : l) {
                JSONObject diff = new JSONObject();
                diff.put(ProtocolConstants.KEY_TYPE, GitConstants.DIFF_TYPE);

                diff.put(GitConstants.KEY_COMMIT_DIFF_NEWPATH, entr.getNewPath());
                diff.put(GitConstants.KEY_COMMIT_DIFF_OLDPATH, entr.getOldPath());
                diff.put(GitConstants.KEY_COMMIT_DIFF_CHANGETYPE, entr.getChangeType().toString());

                // add diff location for the commit
                String path = entr.getChangeType() != ChangeType.DELETE ? entr.getNewPath() : entr.getOldPath();
                diff.put(GitConstants.KEY_DIFF, createDiffLocation(baseLocation, revCommit.getName(),
                        revCommit.getParent(0).getName(), path, isRoot));

                diffs.put(diff);
            }
            tw.release();

            commit.put(GitConstants.KEY_COMMIT_DIFFS, diffs);
        }

        return commit;
    }

    private JSONArray toJSON(Map<String, Ref> revTags) throws JSONException {
        JSONArray children = new JSONArray();
        for (Entry<String, Ref> revTag : revTags.entrySet()) {
            JSONObject tag = new JSONObject();
            tag.put(ProtocolConstants.KEY_NAME, revTag.getKey());
            tag.put(ProtocolConstants.KEY_FULL_NAME, revTag.getValue().getName());
            children.put(tag);
        }
        return children;
    }

    private URI createCommitLocation(URI baseLocation, String commitName, String parameters)
            throws URISyntaxException {
        return new URI(baseLocation.getScheme(), baseLocation.getAuthority(),
                GitServlet.GIT_URI + "/" + GitConstants.COMMIT_RESOURCE + "/" + commitName + "/" //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                        + new Path(baseLocation.getPath()).removeFirstSegments(3),
                parameters, null);
    }

    private URI createDiffLocation(URI baseLocation, String toRefId, String fromRefId, String path, boolean isRoot)
            throws URISyntaxException {
        String diffPath = GitServlet.GIT_URI + "/" + GitConstants.DIFF_RESOURCE + "/"; //$NON-NLS-1$ //$NON-NLS-2$

        if (fromRefId != null)
            diffPath += fromRefId + ".."; //$NON-NLS-1$

        diffPath += toRefId + "/"; //$NON-NLS-1$

        if (path == null) {
            diffPath += new Path(baseLocation.getPath()).removeFirstSegments(3);
        } else if (isRoot) {
            diffPath += new Path(baseLocation.getPath()).removeFirstSegments(3).append(path);
        } else {
            IPath p = new Path(baseLocation.getPath());
            diffPath += p.removeLastSegments(p.segmentCount() - 5).removeFirstSegments(3).append(path);
        }

        return new URI(baseLocation.getScheme(), baseLocation.getAuthority(), diffPath, null, null);
    }

    private boolean handlePost(HttpServletRequest request, HttpServletResponse response, Repository db, Path path)
            throws ServletException, NoFilepatternException, IOException, JSONException, CoreException,
            URISyntaxException {
        IPath filePath = path.hasTrailingSeparator() ? path.removeFirstSegments(1)
                : path.removeFirstSegments(1).removeLastSegments(1);
        Set<Entry<IPath, File>> set = GitUtils.getGitDirs(filePath, Traverse.GO_UP).entrySet();
        File gitDir = set.iterator().next().getValue();
        if (gitDir == null)
            return false; // TODO: or an error response code, 405?
        db = new FileRepository(gitDir);

        JSONObject requestObject = OrionServlet.readJSONRequest(request);
        String commitToMerge = requestObject.optString(GitConstants.KEY_MERGE, null);
        if (commitToMerge != null) {
            return merge(request, response, db, commitToMerge);
        }

        String commitToRebase = requestObject.optString(GitConstants.KEY_REBASE, null);
        String rebaseOperation = requestObject.optString(GitConstants.KEY_OPERATION, null);
        if (commitToRebase != null) {
            return rebase(request, response, db, commitToRebase, rebaseOperation);
        }

        String commitToCherryPick = requestObject.optString(GitConstants.KEY_CHERRY_PICK, null);
        if (commitToCherryPick != null) {
            return cherryPick(request, response, db, commitToCherryPick);
        }

        // continue with creating new commit location

        String newCommitToCreatelocation = requestObject.optString(GitConstants.KEY_COMMIT_NEW, null);
        if (newCommitToCreatelocation != null)
            return createCommitLocation(request, response, db, newCommitToCreatelocation);

        ObjectId refId = db.resolve(path.segment(0));
        if (refId == null || !Constants.HEAD.equals(path.segment(0))) {
            String msg = NLS.bind("Commit failed. Ref must be HEAD and is {0}", path.segment(0));
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
        }

        String message = requestObject.optString(GitConstants.KEY_COMMIT_MESSAGE, null);
        if (message == null || message.isEmpty()) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_BAD_REQUEST, "Missing commit message.", null));
        }

        boolean amend = Boolean.parseBoolean(requestObject.optString(GitConstants.KEY_COMMIT_AMEND, null));

        String committerName = requestObject.optString(GitConstants.KEY_COMMITTER_NAME, null);
        String committerEmail = requestObject.optString(GitConstants.KEY_COMMITTER_EMAIL, null);
        String authorName = requestObject.optString(GitConstants.KEY_AUTHOR_NAME, null);
        String authorEmail = requestObject.optString(GitConstants.KEY_AUTHOR_EMAIL, null);

        Git git = new Git(db);
        CommitCommand commit = git.commit();

        // workaround of a bug in JGit which causes invalid 
        // support of null values of author/committer name/email 
        PersonIdent defPersonIdent = new PersonIdent(db);
        if (committerName == null)
            committerName = defPersonIdent.getName();
        if (committerEmail == null)
            committerEmail = defPersonIdent.getEmailAddress();
        if (authorName == null)
            authorName = committerName;
        if (authorEmail == null)
            authorEmail = committerEmail;
        commit.setCommitter(committerName, committerEmail);
        commit.setAuthor(authorName, authorEmail);

        // support for committing by path: "git commit -o path"
        boolean isRoot = true;
        String pattern = GitUtils.getRelativePath(path.removeFirstSegments(1), set.iterator().next().getKey());
        if (!pattern.isEmpty()) {
            commit.setOnly(pattern);
            isRoot = false;
        }

        try {
            // "git commit [--amend] -m '{message}' [-a|{path}]"
            RevCommit lastCommit = commit.setAmend(amend).setMessage(message).call();
            Map<ObjectId, JSONArray> commitToBranchMap = getCommitToBranchMap(db);

            JSONObject result = toJSON(db, lastCommit, commitToBranchMap, getURI(request), null, isRoot);
            OrionServlet.writeJSONResponse(request, response, result);
            return true;
        } catch (GitAPIException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_BAD_REQUEST, "An error occured when commiting.", e));
        } catch (JGitInternalException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An internal error occured when commiting.", e));
        }
    }

    private boolean merge(HttpServletRequest request, HttpServletResponse response, Repository db,
            String commitToMerge) throws ServletException, JSONException {
        try {
            ObjectId objectId = db.resolve(commitToMerge);
            Git git = new Git(db);
            MergeResult mergeResult = git.merge().include(objectId).call();
            JSONObject result = new JSONObject();
            result.put(GitConstants.KEY_RESULT, mergeResult.getMergeStatus().name());
            OrionServlet.writeJSONResponse(request, response, result);
            return true;
        } catch (IOException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occured when merging.", e));
        } catch (GitAPIException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occured when merging.", e));
        } catch (JGitInternalException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occured when merging.", e.getCause()));
        }
    }

    private boolean rebase(HttpServletRequest request, HttpServletResponse response, Repository db,
            String commitToRebase, String rebaseOperation)
            throws ServletException, JSONException, AmbiguousObjectException, IOException {
        JSONObject result = new JSONObject();
        try {
            Git git = new Git(db);
            RebaseCommand rebase = git.rebase();
            Operation operation;
            if (rebaseOperation != null) {
                operation = Operation.valueOf(rebaseOperation);
            } else {
                operation = Operation.BEGIN;
            }
            if (commitToRebase != null && !commitToRebase.isEmpty()) {
                ObjectId objectId = db.resolve(commitToRebase);
                rebase.setUpstream(objectId);
            } else if (operation.equals(Operation.BEGIN)) {
                return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                        HttpServletResponse.SC_BAD_REQUEST, "Missing commit refId.", null));
            }
            rebase.setOperation(operation);
            RebaseResult rebaseResult = rebase.call();
            result.put(GitConstants.KEY_RESULT, rebaseResult.getStatus().name());
        } catch (UnmergedPathsException e) {
            // this error should be handled by client, so return a proper status
            result.put(GitConstants.KEY_RESULT, AdditionalRebaseStatus.FAILED_UNMERGED_PATHS.name());
        } catch (WrongRepositoryStateException e) {
            // this error should be handled by client, so return a proper status
            result.put(GitConstants.KEY_RESULT, AdditionalRebaseStatus.FAILED_WRONG_REPOSITORY_STATE.name());
        } catch (IllegalArgumentException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_BAD_REQUEST, "Invalid rebase operation.", e));
        } catch (GitAPIException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occured when rebasing.", e));
        } catch (JGitInternalException e) {
            // get cause and try to handle 
            if (e.getCause() instanceof org.eclipse.jgit.errors.CheckoutConflictException) {
                // this error should be handled by client, so return a proper status
                result.put(GitConstants.KEY_RESULT, AdditionalRebaseStatus.FAILED_PENDING_CHANGES.name());
            } else {
                return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                        HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occured when rebasing.", e));
            }
        }
        OrionServlet.writeJSONResponse(request, response, result);
        return true;
    }

    private boolean cherryPick(HttpServletRequest request, HttpServletResponse response, Repository db,
            String commitToCherryPick) throws ServletException, JSONException {
        try {
            ObjectId objectId = db.resolve(commitToCherryPick);
            Git git = new Git(db);
            CherryPickResult cherryPickResult = git.cherryPick().include(objectId).call();
            JSONObject result = new JSONObject();
            result.put(GitConstants.KEY_RESULT, cherryPickResult.getStatus().name());
            OrionServlet.writeJSONResponse(request, response, result);
            return true;
        } catch (IOException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occured when cherry-picking.", e));
        } catch (GitAPIException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occured when cherry-picking.", e));
        } catch (JGitInternalException e) {
            return statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "An error occured when cherry-picking.", e.getCause()));
        }
    }

    private boolean handlePut(HttpServletRequest request, HttpServletResponse response, Repository db, Path path)
            throws ServletException, IOException, JSONException, CoreException, URISyntaxException,
            JGitInternalException, GitAPIException {
        IPath filePath = path.removeFirstSegments(1);
        Set<Entry<IPath, File>> set = GitUtils.getGitDirs(filePath, Traverse.GO_UP).entrySet();
        File gitDir = set.iterator().next().getValue();

        if (gitDir == null)
            return false; // TODO: or an error response code, 405?
        db = new FileRepository(gitDir);
        boolean isRoot = "".equals(GitUtils.getRelativePath(path, set.iterator().next().getKey())); //$NON-NLS-1$
        JSONObject toPut = OrionServlet.readJSONRequest(request);
        String tagName = toPut.getString(ProtocolConstants.KEY_NAME);
        if (tagName != null) {
            return tag(request, response, db, path.segment(0), tagName, isRoot);
        }
        return false;
    }

    private boolean tag(HttpServletRequest request, HttpServletResponse response, Repository db, String commitId,
            String tagName, boolean isRoot) throws AmbiguousObjectException, IOException, JGitInternalException,
            GitAPIException, JSONException, URISyntaxException {
        Git git = new Git(db);
        ObjectId objectId = db.resolve(commitId);

        RevWalk walk = new RevWalk(db);
        RevCommit revCommit = walk.lookupCommit(objectId);
        walk.parseBody(revCommit);

        GitTagHandlerV1.tag(git, revCommit, tagName);
        Map<ObjectId, JSONArray> commitToBranchMap = getCommitToBranchMap(db);

        JSONObject result = toJSON(db, revCommit, commitToBranchMap, OrionServlet.getURI(request), null, isRoot);
        OrionServlet.writeJSONResponse(request, response, result);
        walk.dispose();
        return true;
    }

    // from https://gist.github.com/839693, credits to zx
    private static Map<String, Ref> getTagsForCommit(Repository repo, RevCommit commit)
            throws MissingObjectException, IOException {
        final Map<String, Ref> revTags = new HashMap<String, Ref>();
        final RevWalk walk = new RevWalk(repo);
        walk.reset();
        for (final Entry<String, Ref> revTag : repo.getTags().entrySet()) {
            final RevObject obj = walk.parseAny(revTag.getValue().getObjectId());
            final RevCommit tagCommit;
            if (obj instanceof RevCommit) {
                tagCommit = (RevCommit) obj;
            } else if (obj instanceof RevTag) {
                tagCommit = walk.parseCommit(((RevTag) obj).getObject());
            } else {
                continue;
            }
            if (commit.equals(tagCommit) || walk.isMergedInto(commit, tagCommit)) {
                revTags.put(revTag.getKey(), revTag.getValue());
            }
        }
        return revTags;
    }

    private static Map<ObjectId, JSONArray> getCommitToBranchMap(Repository db) throws JSONException {
        HashMap<ObjectId, JSONArray> commitToBranch = new HashMap<ObjectId, JSONArray>();
        Git git = new Git(db);
        List<Ref> branchRefs = git.branchList().setListMode(ListMode.ALL).call();
        for (Ref branchRef : branchRefs) {
            ObjectId commitId = branchRef.getLeaf().getObjectId();
            JSONObject branch = new JSONObject();
            branch.put(ProtocolConstants.KEY_FULL_NAME, branchRef.getName());

            JSONArray branchesArray = commitToBranch.get(commitId);
            if (branchesArray != null) {
                branchesArray.put(branch);
            } else {
                branchesArray = new JSONArray();
                branchesArray.put(branch);
                commitToBranch.put(commitId, branchesArray);
            }
        }
        return commitToBranch;
    }

    private JSONArray parentsToJSON(RevCommit[] revCommits) throws JSONException {
        JSONArray parents = new JSONArray();
        for (RevCommit revCommit : revCommits) {
            JSONObject parent = new JSONObject();
            parent.put(ProtocolConstants.KEY_NAME, revCommit.getName());
            parents.put(parent);
        }
        return parents;
    }
}