From 746aaf82146a8621523004eebeb348766f611674 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Wed, 11 Jan 2012 16:18:02 -0500
Subject: [PATCH] Update the gh-pages branch on release with the built site

---
 src/com/gitblit/build/BuildGhPages.java |  259 +++++++++++++++++++++++++++++++++++++++++++++++++++
 build.xml                               |   22 ++++
 2 files changed, 280 insertions(+), 1 deletions(-)

diff --git a/build.xml b/build.xml
index 0d906ed..7bb09cf 100644
--- a/build.xml
+++ b/build.xml
@@ -857,6 +857,26 @@
 		<delete dir="${project.express.dir}" />
 	</target>
 
+	
+	<!--
+		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+		Update the gh-pages branch with the current site
+		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	-->
+	<target name="updateGhPages" depends="buildSite">
+		<!-- Build gh-pages branch -->
+		<java classpath="${project.build.dir}" classname="com.gitblit.build.BuildGhPages">
+			<classpath refid="master-classpath" />
+			<arg value="--sourceFolder" />
+			<arg value="${basedir}/site" />
+
+			<arg value="--repository" />
+			<arg value="${basedir}" />
+			
+			<arg value="--obliterate" />
+		</java>
+	</target>
+	
 
 	<!-- 
 		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -936,7 +956,7 @@
 		You must add ext/commons-net-1.4.0.jar to your ANT classpath.
 		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	-->
-	<target name="publishSite" depends="buildSite" description="Publish the Gitblit site to a webserver (requires ext/commons-net-1.4.0.jar)" >
+	<target name="publishSite" depends="buildSite,updateGhPages" description="Publish the Gitblit site to a webserver (requires ext/commons-net-1.4.0.jar)" >
 
 		<echo>Uploading Gitblit ${gb.version} website</echo>
 
diff --git a/src/com/gitblit/build/BuildGhPages.java b/src/com/gitblit/build/BuildGhPages.java
new file mode 100644
index 0000000..efeb43c
--- /dev/null
+++ b/src/com/gitblit/build/BuildGhPages.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * 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 com.gitblit.build;
+
+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.JGitText;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+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.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.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 com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.ParameterException;
+import com.beust.jcommander.Parameters;
+import com.gitblit.models.RefModel;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Creates or updates a gh-pages branch with the specified content.
+ * 
+ * @author James Moger
+ * 
+ */
+public class BuildGhPages {
+
+	public static void main(String[] args) {
+		Params params = new Params();
+		JCommander jc = new JCommander(params);
+		try {
+			jc.parse(args);
+		} catch (ParameterException t) {
+			System.err.println(t.getMessage());
+			jc.usage();
+		}
+
+		File source = new File(params.sourceFolder);
+		String ghpages = "refs/heads/gh-pages";
+		try {			
+			File gitDir = FileKey.resolve(new File(params.repositoryFolder), FS.DETECTED);
+			Repository repository = new FileRepository(gitDir);
+
+			RefModel issuesBranch = JGitUtils.getPagesBranch(repository);
+			if (issuesBranch == 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, source, params.obliterate);
+				ObjectId indexTreeId = index.writeTree(odi);
+
+				// Create a commit object
+				PersonIdent author = new PersonIdent("Gitblit", "gitblit@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> files = new ArrayList<File>();
+		for (File file : folder.listFiles()) {
+			if (file.isDirectory()) {
+				files.addAll(listFiles(file));
+			} else {
+				files.add(file);
+			}
+		}
+		return files;
+	}
+
+	/**
+	 * JCommander Parameters class for BuildGhPages.
+	 */
+	@Parameters(separators = " ")
+	private static class Params {
+
+		@Parameter(names = { "--sourceFolder" }, description = "Source folder for pages", required = true)
+		public String sourceFolder;
+
+		@Parameter(names = { "--repository" }, description = "Repository folder", required = true)
+		public String repositoryFolder;
+
+		@Parameter(names = { "--obliterate" }, description = "Replace gh-pages tree with only the content in your sourcefolder")
+		public boolean obliterate;
+
+	}
+}

--
Gitblit v1.9.1