commit | author | age
|
746aaf
|
1 |
/*
|
JM |
2 |
* Copyright 2012 gitblit.com.
|
|
3 |
*
|
|
4 |
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5 |
* you may not use this file except in compliance with the License.
|
|
6 |
* You may obtain a copy of the License at
|
|
7 |
*
|
|
8 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9 |
*
|
|
10 |
* Unless required by applicable law or agreed to in writing, software
|
|
11 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13 |
* See the License for the specific language governing permissions and
|
|
14 |
* limitations under the License.
|
|
15 |
*/
|
|
16 |
package com.gitblit.build;
|
|
17 |
|
|
18 |
import java.io.File;
|
|
19 |
import java.io.FileInputStream;
|
|
20 |
import java.io.IOException;
|
|
21 |
import java.io.InputStream;
|
|
22 |
import java.text.MessageFormat;
|
|
23 |
import java.util.ArrayList;
|
|
24 |
import java.util.List;
|
|
25 |
import java.util.Set;
|
|
26 |
import java.util.TreeSet;
|
|
27 |
|
|
28 |
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
|
|
29 |
import org.eclipse.jgit.api.errors.JGitInternalException;
|
|
30 |
import org.eclipse.jgit.dircache.DirCache;
|
|
31 |
import org.eclipse.jgit.dircache.DirCacheBuilder;
|
|
32 |
import org.eclipse.jgit.dircache.DirCacheEntry;
|
5d9bd7
|
33 |
import org.eclipse.jgit.internal.JGitText;
|
746aaf
|
34 |
import org.eclipse.jgit.lib.CommitBuilder;
|
JM |
35 |
import org.eclipse.jgit.lib.Constants;
|
|
36 |
import org.eclipse.jgit.lib.FileMode;
|
|
37 |
import org.eclipse.jgit.lib.ObjectId;
|
|
38 |
import org.eclipse.jgit.lib.ObjectInserter;
|
|
39 |
import org.eclipse.jgit.lib.PersonIdent;
|
|
40 |
import org.eclipse.jgit.lib.RefUpdate;
|
|
41 |
import org.eclipse.jgit.lib.RefUpdate.Result;
|
|
42 |
import org.eclipse.jgit.lib.Repository;
|
|
43 |
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
|
|
44 |
import org.eclipse.jgit.revwalk.RevCommit;
|
|
45 |
import org.eclipse.jgit.revwalk.RevWalk;
|
|
46 |
import org.eclipse.jgit.storage.file.FileRepository;
|
|
47 |
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
|
|
48 |
import org.eclipse.jgit.treewalk.TreeWalk;
|
|
49 |
import org.eclipse.jgit.util.FS;
|
|
50 |
|
|
51 |
import com.beust.jcommander.JCommander;
|
|
52 |
import com.beust.jcommander.Parameter;
|
|
53 |
import com.beust.jcommander.ParameterException;
|
|
54 |
import com.beust.jcommander.Parameters;
|
|
55 |
import com.gitblit.models.RefModel;
|
|
56 |
import com.gitblit.utils.JGitUtils;
|
|
57 |
import com.gitblit.utils.StringUtils;
|
|
58 |
|
|
59 |
/**
|
|
60 |
* Creates or updates a gh-pages branch with the specified content.
|
|
61 |
*
|
|
62 |
* @author James Moger
|
|
63 |
*
|
|
64 |
*/
|
|
65 |
public class BuildGhPages {
|
|
66 |
|
|
67 |
public static void main(String[] args) {
|
|
68 |
Params params = new Params();
|
|
69 |
JCommander jc = new JCommander(params);
|
|
70 |
try {
|
|
71 |
jc.parse(args);
|
|
72 |
} catch (ParameterException t) {
|
|
73 |
System.err.println(t.getMessage());
|
|
74 |
jc.usage();
|
|
75 |
}
|
|
76 |
|
|
77 |
File source = new File(params.sourceFolder);
|
|
78 |
String ghpages = "refs/heads/gh-pages";
|
|
79 |
try {
|
|
80 |
File gitDir = FileKey.resolve(new File(params.repositoryFolder), FS.DETECTED);
|
|
81 |
Repository repository = new FileRepository(gitDir);
|
|
82 |
|
|
83 |
RefModel issuesBranch = JGitUtils.getPagesBranch(repository);
|
|
84 |
if (issuesBranch == null) {
|
|
85 |
JGitUtils.createOrphanBranch(repository, "gh-pages", null);
|
|
86 |
}
|
|
87 |
|
|
88 |
System.out.println("Updating gh-pages branch...");
|
|
89 |
ObjectId headId = repository.resolve(ghpages + "^{commit}");
|
|
90 |
ObjectInserter odi = repository.newObjectInserter();
|
|
91 |
try {
|
|
92 |
// Create the in-memory index of the new/updated issue.
|
|
93 |
DirCache index = createIndex(repository, headId, source, params.obliterate);
|
|
94 |
ObjectId indexTreeId = index.writeTree(odi);
|
|
95 |
|
|
96 |
// Create a commit object
|
|
97 |
PersonIdent author = new PersonIdent("Gitblit", "gitblit@localhost");
|
|
98 |
CommitBuilder commit = new CommitBuilder();
|
|
99 |
commit.setAuthor(author);
|
|
100 |
commit.setCommitter(author);
|
|
101 |
commit.setEncoding(Constants.CHARACTER_ENCODING);
|
|
102 |
commit.setMessage("updated pages");
|
|
103 |
commit.setParentId(headId);
|
|
104 |
commit.setTreeId(indexTreeId);
|
|
105 |
|
|
106 |
// Insert the commit into the repository
|
|
107 |
ObjectId commitId = odi.insert(commit);
|
|
108 |
odi.flush();
|
|
109 |
|
|
110 |
RevWalk revWalk = new RevWalk(repository);
|
|
111 |
try {
|
|
112 |
RevCommit revCommit = revWalk.parseCommit(commitId);
|
|
113 |
RefUpdate ru = repository.updateRef(ghpages);
|
|
114 |
ru.setNewObjectId(commitId);
|
|
115 |
ru.setExpectedOldObjectId(headId);
|
|
116 |
ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
|
|
117 |
Result rc = ru.forceUpdate();
|
|
118 |
switch (rc) {
|
|
119 |
case NEW:
|
|
120 |
case FORCED:
|
|
121 |
case FAST_FORWARD:
|
|
122 |
break;
|
|
123 |
case REJECTED:
|
|
124 |
case LOCK_FAILURE:
|
|
125 |
throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
|
|
126 |
ru.getRef(), rc);
|
|
127 |
default:
|
|
128 |
throw new JGitInternalException(MessageFormat.format(
|
|
129 |
JGitText.get().updatingRefFailed, ghpages, commitId.toString(), rc));
|
|
130 |
}
|
|
131 |
} finally {
|
|
132 |
revWalk.release();
|
|
133 |
}
|
|
134 |
} finally {
|
|
135 |
odi.release();
|
|
136 |
}
|
|
137 |
System.out.println("gh-pages updated.");
|
|
138 |
} catch (Throwable t) {
|
|
139 |
t.printStackTrace();
|
|
140 |
}
|
|
141 |
}
|
|
142 |
|
|
143 |
/**
|
|
144 |
* Creates an in-memory index of the issue change.
|
|
145 |
*
|
|
146 |
* @param repo
|
|
147 |
* @param headId
|
|
148 |
* @param sourceFolder
|
|
149 |
* @param obliterate
|
|
150 |
* if true the source folder tree is used as the new tree for
|
|
151 |
* gh-pages and non-existent files are considered deleted
|
|
152 |
* @return an in-memory index
|
|
153 |
* @throws IOException
|
|
154 |
*/
|
|
155 |
private static DirCache createIndex(Repository repo, ObjectId headId, File sourceFolder,
|
|
156 |
boolean obliterate) throws IOException {
|
|
157 |
|
|
158 |
DirCache inCoreIndex = DirCache.newInCore();
|
|
159 |
DirCacheBuilder dcBuilder = inCoreIndex.builder();
|
|
160 |
ObjectInserter inserter = repo.newObjectInserter();
|
|
161 |
|
|
162 |
try {
|
|
163 |
// Add all files to the temporary index
|
|
164 |
Set<String> ignorePaths = new TreeSet<String>();
|
|
165 |
List<File> files = listFiles(sourceFolder);
|
|
166 |
for (File file : files) {
|
|
167 |
// create an index entry for the file
|
|
168 |
final DirCacheEntry dcEntry = new DirCacheEntry(StringUtils.getRelativePath(
|
|
169 |
sourceFolder.getPath(), file.getPath()));
|
|
170 |
dcEntry.setLength(file.length());
|
|
171 |
dcEntry.setLastModified(file.lastModified());
|
|
172 |
dcEntry.setFileMode(FileMode.REGULAR_FILE);
|
|
173 |
|
|
174 |
// add this entry to the ignore paths set
|
|
175 |
ignorePaths.add(dcEntry.getPathString());
|
|
176 |
|
|
177 |
// insert object
|
|
178 |
InputStream inputStream = new FileInputStream(file);
|
|
179 |
try {
|
|
180 |
dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, file.length(),
|
|
181 |
inputStream));
|
|
182 |
} finally {
|
|
183 |
inputStream.close();
|
|
184 |
}
|
|
185 |
|
|
186 |
// add to temporary in-core index
|
|
187 |
dcBuilder.add(dcEntry);
|
|
188 |
}
|
|
189 |
|
|
190 |
if (!obliterate) {
|
|
191 |
// Traverse HEAD to add all other paths
|
|
192 |
TreeWalk treeWalk = new TreeWalk(repo);
|
|
193 |
int hIdx = -1;
|
|
194 |
if (headId != null)
|
|
195 |
hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));
|
|
196 |
treeWalk.setRecursive(true);
|
|
197 |
|
|
198 |
while (treeWalk.next()) {
|
|
199 |
String path = treeWalk.getPathString();
|
|
200 |
CanonicalTreeParser hTree = null;
|
|
201 |
if (hIdx != -1)
|
|
202 |
hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
|
|
203 |
if (!ignorePaths.contains(path)) {
|
|
204 |
// add entries from HEAD for all other paths
|
|
205 |
if (hTree != null) {
|
|
206 |
// create a new DirCacheEntry with data retrieved
|
|
207 |
// from
|
|
208 |
// HEAD
|
|
209 |
final DirCacheEntry dcEntry = new DirCacheEntry(path);
|
|
210 |
dcEntry.setObjectId(hTree.getEntryObjectId());
|
|
211 |
dcEntry.setFileMode(hTree.getEntryFileMode());
|
|
212 |
|
|
213 |
// add to temporary in-core index
|
|
214 |
dcBuilder.add(dcEntry);
|
|
215 |
}
|
|
216 |
}
|
|
217 |
}
|
|
218 |
|
|
219 |
// release the treewalk
|
|
220 |
treeWalk.release();
|
|
221 |
}
|
|
222 |
|
|
223 |
// finish temporary in-core index used for this commit
|
|
224 |
dcBuilder.finish();
|
|
225 |
} finally {
|
|
226 |
inserter.release();
|
|
227 |
}
|
|
228 |
return inCoreIndex;
|
|
229 |
}
|
|
230 |
|
|
231 |
private static List<File> listFiles(File folder) {
|
|
232 |
List<File> files = new ArrayList<File>();
|
|
233 |
for (File file : folder.listFiles()) {
|
|
234 |
if (file.isDirectory()) {
|
|
235 |
files.addAll(listFiles(file));
|
|
236 |
} else {
|
|
237 |
files.add(file);
|
|
238 |
}
|
|
239 |
}
|
|
240 |
return files;
|
|
241 |
}
|
|
242 |
|
|
243 |
/**
|
|
244 |
* JCommander Parameters class for BuildGhPages.
|
|
245 |
*/
|
|
246 |
@Parameters(separators = " ")
|
|
247 |
private static class Params {
|
|
248 |
|
|
249 |
@Parameter(names = { "--sourceFolder" }, description = "Source folder for pages", required = true)
|
|
250 |
public String sourceFolder;
|
|
251 |
|
|
252 |
@Parameter(names = { "--repository" }, description = "Repository folder", required = true)
|
|
253 |
public String repositoryFolder;
|
|
254 |
|
|
255 |
@Parameter(names = { "--obliterate" }, description = "Replace gh-pages tree with only the content in your sourcefolder")
|
|
256 |
public boolean obliterate;
|
|
257 |
|
|
258 |
}
|
|
259 |
}
|