org.moxie.utils.JGitUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.moxie.utils.JGitUtils.java

Source

/*
 * Copyright 2012 James Moger
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.moxie.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.TagCommand;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.moxie.MoxieException;

public class JGitUtils {

    public static File findRepositoryDir(File dir) {
        File resolved = FileKey.resolve(dir, FS.detect());
        if (resolved != null) {
            return resolved;
        } else {
            resolved = FileKey.resolve(dir.getParentFile(), FS.detect());
            if (resolved != null) {
                return resolved;
            }
        }
        return null;
    }

    public static String getCommitId(File folder) {
        // try specified folder or subfolder
        File gitDir = FileKey.resolve(folder, FS.DETECTED);

        if (gitDir == null || !gitDir.exists()) {
            // try parent folder
            gitDir = FileKey.resolve(folder.getParentFile(), FS.DETECTED);
        }
        if (gitDir == null || !gitDir.exists()) {
            throw new MoxieException("Can not find .git folder for " + folder);
        }

        String hashid = "";
        try {
            Repository repository = new FileRepository(gitDir);
            ObjectId objectId = repository.resolve(org.eclipse.jgit.lib.Constants.HEAD);
            hashid = objectId.getName().toString();
            repository.close();
        } catch (IOException io) {
            io.printStackTrace();
            throw new MoxieException("IOException accessing " + gitDir.getAbsolutePath(), io);
        }
        return hashid;
    }

    /**
     * Create an orphaned branch in a repository.
     * 
     * @param repository
     * @param branchName
     * @param author
     *            if unspecified, Moxie will be the author of this new branch
     * @return true if successful
     */
    public static boolean createOrphanBranch(Repository repository, String branchName, PersonIdent author) {
        boolean success = false;
        String message = "Created branch " + branchName;
        if (author == null) {
            author = new PersonIdent("Moxie", "moxie@localhost");
        }
        try {
            ObjectInserter odi = repository.newObjectInserter();
            try {
                // Create a blob object to insert into a tree
                ObjectId blobId = odi.insert(Constants.OBJ_BLOB, message.getBytes(Constants.CHARACTER_ENCODING));

                // Create a tree object to reference from a commit
                TreeFormatter tree = new TreeFormatter();
                tree.append("NEWBRANCH", FileMode.REGULAR_FILE, blobId);
                ObjectId treeId = odi.insert(tree);

                // Create a commit object
                CommitBuilder commit = new CommitBuilder();
                commit.setAuthor(author);
                commit.setCommitter(author);
                commit.setEncoding(Constants.CHARACTER_ENCODING);
                commit.setMessage(message);
                commit.setTreeId(treeId);

                // Insert the commit into the repository
                ObjectId commitId = odi.insert(commit);
                odi.flush();

                RevWalk revWalk = new RevWalk(repository);
                try {
                    RevCommit revCommit = revWalk.parseCommit(commitId);
                    if (!branchName.startsWith("refs/")) {
                        branchName = "refs/heads/" + branchName;
                    }
                    RefUpdate ru = repository.updateRef(branchName);
                    ru.setNewObjectId(commitId);
                    ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
                    Result rc = ru.forceUpdate();
                    switch (rc) {
                    case NEW:
                    case FORCED:
                    case FAST_FORWARD:
                        success = true;
                        break;
                    default:
                        success = false;
                    }
                } finally {
                    revWalk.release();
                }
            } finally {
                odi.release();
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return success;
    }

    public static void updateGhPages(File repositoryFolder, File sourceFolder, boolean obliterate) {
        String ghpages = "refs/heads/gh-pages";
        try {
            File gitDir = FileKey.resolve(repositoryFolder, FS.DETECTED);
            Repository repository = new FileRepository(gitDir);

            ObjectId objectId = repository.resolve(ghpages);
            if (objectId == null) {
                JGitUtils.createOrphanBranch(repository, "gh-pages", null);
            }

            System.out.println("Updating gh-pages branch...");
            ObjectId headId = repository.resolve(ghpages + "^{commit}");
            ObjectInserter odi = repository.newObjectInserter();
            try {
                // Create the in-memory index of the new/updated issue.
                DirCache index = createIndex(repository, headId, sourceFolder, obliterate);
                ObjectId indexTreeId = index.writeTree(odi);

                // Create a commit object
                PersonIdent author = new PersonIdent("Moxie", "moxie@localhost");
                CommitBuilder commit = new CommitBuilder();
                commit.setAuthor(author);
                commit.setCommitter(author);
                commit.setEncoding(Constants.CHARACTER_ENCODING);
                commit.setMessage("updated pages");
                commit.setParentId(headId);
                commit.setTreeId(indexTreeId);

                // Insert the commit into the repository
                ObjectId commitId = odi.insert(commit);
                odi.flush();

                RevWalk revWalk = new RevWalk(repository);
                try {
                    RevCommit revCommit = revWalk.parseCommit(commitId);
                    RefUpdate ru = repository.updateRef(ghpages);
                    ru.setNewObjectId(commitId);
                    ru.setExpectedOldObjectId(headId);
                    ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
                    Result rc = ru.forceUpdate();
                    switch (rc) {
                    case NEW:
                    case FORCED:
                    case FAST_FORWARD:
                        break;
                    case REJECTED:
                    case LOCK_FAILURE:
                        throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
                    default:
                        throw new JGitInternalException(MessageFormat.format(JGitText.get().updatingRefFailed,
                                ghpages, commitId.toString(), rc));
                    }
                } finally {
                    revWalk.release();
                }
            } finally {
                odi.release();
            }
            System.out.println("gh-pages updated.");
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    /**
     * Creates an in-memory index of the issue change.
     * 
     * @param repo
     * @param headId
     * @param sourceFolder
     * @param obliterate
     *            if true the source folder tree is used as the new tree for
     *            gh-pages and non-existent files are considered deleted
     * @return an in-memory index
     * @throws IOException
     */
    private static DirCache createIndex(Repository repo, ObjectId headId, File sourceFolder, boolean obliterate)
            throws IOException {

        DirCache inCoreIndex = DirCache.newInCore();
        DirCacheBuilder dcBuilder = inCoreIndex.builder();
        ObjectInserter inserter = repo.newObjectInserter();

        try {
            // Add all files to the temporary index
            Set<String> ignorePaths = new TreeSet<String>();
            List<File> files = listFiles(sourceFolder);
            for (File file : files) {
                // create an index entry for the file
                final DirCacheEntry dcEntry = new DirCacheEntry(
                        StringUtils.getRelativePath(sourceFolder.getPath(), file.getPath()));
                dcEntry.setLength(file.length());
                dcEntry.setLastModified(file.lastModified());
                dcEntry.setFileMode(FileMode.REGULAR_FILE);

                // add this entry to the ignore paths set
                ignorePaths.add(dcEntry.getPathString());

                // insert object
                InputStream inputStream = new FileInputStream(file);
                try {
                    dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, file.length(), inputStream));
                } finally {
                    inputStream.close();
                }

                // add to temporary in-core index
                dcBuilder.add(dcEntry);
            }

            if (!obliterate) {
                // Traverse HEAD to add all other paths
                TreeWalk treeWalk = new TreeWalk(repo);
                int hIdx = -1;
                if (headId != null)
                    hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));
                treeWalk.setRecursive(true);

                while (treeWalk.next()) {
                    String path = treeWalk.getPathString();
                    CanonicalTreeParser hTree = null;
                    if (hIdx != -1)
                        hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
                    if (!ignorePaths.contains(path)) {
                        // add entries from HEAD for all other paths
                        if (hTree != null) {
                            // create a new DirCacheEntry with data retrieved
                            // from
                            // HEAD
                            final DirCacheEntry dcEntry = new DirCacheEntry(path);
                            dcEntry.setObjectId(hTree.getEntryObjectId());
                            dcEntry.setFileMode(hTree.getEntryFileMode());

                            // add to temporary in-core index
                            dcBuilder.add(dcEntry);
                        }
                    }
                }

                // release the treewalk
                treeWalk.release();
            }

            // finish temporary in-core index used for this commit
            dcBuilder.finish();
        } finally {
            inserter.release();
        }
        return inCoreIndex;
    }

    private static List<File> listFiles(File folder) {
        List<File> list = new ArrayList<File>();
        File[] files = folder.listFiles();
        if (files == null) {
            return list;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                list.addAll(listFiles(file));
            } else {
                list.add(file);
            }
        }
        return list;
    }

    public static String commitFiles(File dir, List<String> files, String message, String tagName,
            String tagMessage) throws IOException, GitAPIException {
        Git git = Git.open(dir);
        AddCommand add = git.add();
        for (String file : files) {
            add.addFilepattern(file);
        }
        add.call();

        // execute the commit
        CommitCommand commit = git.commit();
        commit.setMessage(message);
        RevCommit revCommit = commit.call();

        if (!StringUtils.isEmpty(tagName) && !StringUtils.isEmpty(tagMessage)) {
            // tag the commit
            TagCommand tagCommand = git.tag();
            tagCommand.setName(tagName);
            tagCommand.setMessage(tagMessage);
            tagCommand.setForceUpdate(true);
            tagCommand.setObjectId(revCommit);
            tagCommand.call();
        }
        git.getRepository().close();
        return revCommit.getId().getName();
    }
}