--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.build;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
+\r
+import org.eclipse.jgit.JGitText;\r
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;\r
+import org.eclipse.jgit.api.errors.JGitInternalException;\r
+import org.eclipse.jgit.dircache.DirCache;\r
+import org.eclipse.jgit.dircache.DirCacheBuilder;\r
+import org.eclipse.jgit.dircache.DirCacheEntry;\r
+import org.eclipse.jgit.lib.CommitBuilder;\r
+import org.eclipse.jgit.lib.Constants;\r
+import org.eclipse.jgit.lib.FileMode;\r
+import org.eclipse.jgit.lib.ObjectId;\r
+import org.eclipse.jgit.lib.ObjectInserter;\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+import org.eclipse.jgit.lib.RefUpdate;\r
+import org.eclipse.jgit.lib.RefUpdate.Result;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.eclipse.jgit.revwalk.RevWalk;\r
+import org.eclipse.jgit.storage.file.FileRepository;\r
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;\r
+import org.eclipse.jgit.treewalk.TreeWalk;\r
+import org.eclipse.jgit.util.FS;\r
+\r
+import com.beust.jcommander.JCommander;\r
+import com.beust.jcommander.Parameter;\r
+import com.beust.jcommander.ParameterException;\r
+import com.beust.jcommander.Parameters;\r
+import com.gitblit.models.RefModel;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Creates or updates a gh-pages branch with the specified content.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class BuildGhPages {\r
+\r
+ public static void main(String[] args) {\r
+ Params params = new Params();\r
+ JCommander jc = new JCommander(params);\r
+ try {\r
+ jc.parse(args);\r
+ } catch (ParameterException t) {\r
+ System.err.println(t.getMessage());\r
+ jc.usage();\r
+ }\r
+\r
+ File source = new File(params.sourceFolder);\r
+ String ghpages = "refs/heads/gh-pages";\r
+ try { \r
+ File gitDir = FileKey.resolve(new File(params.repositoryFolder), FS.DETECTED);\r
+ Repository repository = new FileRepository(gitDir);\r
+\r
+ RefModel issuesBranch = JGitUtils.getPagesBranch(repository);\r
+ if (issuesBranch == null) {\r
+ JGitUtils.createOrphanBranch(repository, "gh-pages", null);\r
+ }\r
+\r
+ System.out.println("Updating gh-pages branch...");\r
+ ObjectId headId = repository.resolve(ghpages + "^{commit}");\r
+ ObjectInserter odi = repository.newObjectInserter();\r
+ try {\r
+ // Create the in-memory index of the new/updated issue.\r
+ DirCache index = createIndex(repository, headId, source, params.obliterate);\r
+ ObjectId indexTreeId = index.writeTree(odi);\r
+\r
+ // Create a commit object\r
+ PersonIdent author = new PersonIdent("Gitblit", "gitblit@localhost");\r
+ CommitBuilder commit = new CommitBuilder();\r
+ commit.setAuthor(author);\r
+ commit.setCommitter(author);\r
+ commit.setEncoding(Constants.CHARACTER_ENCODING);\r
+ commit.setMessage("updated pages");\r
+ commit.setParentId(headId);\r
+ commit.setTreeId(indexTreeId);\r
+\r
+ // Insert the commit into the repository\r
+ ObjectId commitId = odi.insert(commit);\r
+ odi.flush();\r
+\r
+ RevWalk revWalk = new RevWalk(repository);\r
+ try {\r
+ RevCommit revCommit = revWalk.parseCommit(commitId);\r
+ RefUpdate ru = repository.updateRef(ghpages);\r
+ ru.setNewObjectId(commitId);\r
+ ru.setExpectedOldObjectId(headId);\r
+ ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);\r
+ Result rc = ru.forceUpdate();\r
+ switch (rc) {\r
+ case NEW:\r
+ case FORCED:\r
+ case FAST_FORWARD:\r
+ break;\r
+ case REJECTED:\r
+ case LOCK_FAILURE:\r
+ throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,\r
+ ru.getRef(), rc);\r
+ default:\r
+ throw new JGitInternalException(MessageFormat.format(\r
+ JGitText.get().updatingRefFailed, ghpages, commitId.toString(), rc));\r
+ }\r
+ } finally {\r
+ revWalk.release();\r
+ }\r
+ } finally {\r
+ odi.release();\r
+ }\r
+ System.out.println("gh-pages updated.");\r
+ } catch (Throwable t) {\r
+ t.printStackTrace();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates an in-memory index of the issue change.\r
+ * \r
+ * @param repo\r
+ * @param headId\r
+ * @param sourceFolder\r
+ * @param obliterate\r
+ * if true the source folder tree is used as the new tree for\r
+ * gh-pages and non-existent files are considered deleted\r
+ * @return an in-memory index\r
+ * @throws IOException\r
+ */\r
+ private static DirCache createIndex(Repository repo, ObjectId headId, File sourceFolder,\r
+ boolean obliterate) throws IOException {\r
+\r
+ DirCache inCoreIndex = DirCache.newInCore();\r
+ DirCacheBuilder dcBuilder = inCoreIndex.builder();\r
+ ObjectInserter inserter = repo.newObjectInserter();\r
+\r
+ try {\r
+ // Add all files to the temporary index\r
+ Set<String> ignorePaths = new TreeSet<String>();\r
+ List<File> files = listFiles(sourceFolder);\r
+ for (File file : files) {\r
+ // create an index entry for the file\r
+ final DirCacheEntry dcEntry = new DirCacheEntry(StringUtils.getRelativePath(\r
+ sourceFolder.getPath(), file.getPath()));\r
+ dcEntry.setLength(file.length());\r
+ dcEntry.setLastModified(file.lastModified());\r
+ dcEntry.setFileMode(FileMode.REGULAR_FILE);\r
+\r
+ // add this entry to the ignore paths set\r
+ ignorePaths.add(dcEntry.getPathString());\r
+\r
+ // insert object\r
+ InputStream inputStream = new FileInputStream(file);\r
+ try {\r
+ dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, file.length(),\r
+ inputStream));\r
+ } finally {\r
+ inputStream.close();\r
+ }\r
+\r
+ // add to temporary in-core index\r
+ dcBuilder.add(dcEntry);\r
+ }\r
+\r
+ if (!obliterate) {\r
+ // Traverse HEAD to add all other paths\r
+ TreeWalk treeWalk = new TreeWalk(repo);\r
+ int hIdx = -1;\r
+ if (headId != null)\r
+ hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));\r
+ treeWalk.setRecursive(true);\r
+\r
+ while (treeWalk.next()) {\r
+ String path = treeWalk.getPathString();\r
+ CanonicalTreeParser hTree = null;\r
+ if (hIdx != -1)\r
+ hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);\r
+ if (!ignorePaths.contains(path)) {\r
+ // add entries from HEAD for all other paths\r
+ if (hTree != null) {\r
+ // create a new DirCacheEntry with data retrieved\r
+ // from\r
+ // HEAD\r
+ final DirCacheEntry dcEntry = new DirCacheEntry(path);\r
+ dcEntry.setObjectId(hTree.getEntryObjectId());\r
+ dcEntry.setFileMode(hTree.getEntryFileMode());\r
+\r
+ // add to temporary in-core index\r
+ dcBuilder.add(dcEntry);\r
+ }\r
+ }\r
+ }\r
+\r
+ // release the treewalk\r
+ treeWalk.release();\r
+ }\r
+ \r
+ // finish temporary in-core index used for this commit\r
+ dcBuilder.finish();\r
+ } finally {\r
+ inserter.release();\r
+ }\r
+ return inCoreIndex;\r
+ }\r
+\r
+ private static List<File> listFiles(File folder) {\r
+ List<File> files = new ArrayList<File>();\r
+ for (File file : folder.listFiles()) {\r
+ if (file.isDirectory()) {\r
+ files.addAll(listFiles(file));\r
+ } else {\r
+ files.add(file);\r
+ }\r
+ }\r
+ return files;\r
+ }\r
+\r
+ /**\r
+ * JCommander Parameters class for BuildGhPages.\r
+ */\r
+ @Parameters(separators = " ")\r
+ private static class Params {\r
+\r
+ @Parameter(names = { "--sourceFolder" }, description = "Source folder for pages", required = true)\r
+ public String sourceFolder;\r
+\r
+ @Parameter(names = { "--repository" }, description = "Repository folder", required = true)\r
+ public String repositoryFolder;\r
+\r
+ @Parameter(names = { "--obliterate" }, description = "Replace gh-pages tree with only the content in your sourcefolder")\r
+ public boolean obliterate;\r
+\r
+ }\r
+}\r