commit | author | age
|
5fe7df
|
1 |
package com.gitblit.utils;
|
JM |
2 |
|
|
3 |
import java.io.ByteArrayOutputStream;
|
|
4 |
import java.io.File;
|
|
5 |
import java.io.IOException;
|
|
6 |
import java.io.InputStream;
|
|
7 |
import java.io.RandomAccessFile;
|
fc8426
|
8 |
import java.text.DateFormat;
|
JM |
9 |
import java.text.SimpleDateFormat;
|
5fe7df
|
10 |
import java.util.ArrayList;
|
JM |
11 |
import java.util.Collections;
|
|
12 |
import java.util.Date;
|
|
13 |
import java.util.HashMap;
|
|
14 |
import java.util.List;
|
|
15 |
import java.util.Map;
|
|
16 |
import java.util.Set;
|
|
17 |
|
|
18 |
import org.eclipse.jgit.api.Git;
|
|
19 |
import org.eclipse.jgit.errors.ConfigInvalidException;
|
|
20 |
import org.eclipse.jgit.lib.AnyObjectId;
|
|
21 |
import org.eclipse.jgit.lib.Constants;
|
|
22 |
import org.eclipse.jgit.lib.FileMode;
|
|
23 |
import org.eclipse.jgit.lib.ObjectId;
|
|
24 |
import org.eclipse.jgit.lib.ObjectLoader;
|
|
25 |
import org.eclipse.jgit.lib.PersonIdent;
|
|
26 |
import org.eclipse.jgit.lib.Ref;
|
|
27 |
import org.eclipse.jgit.lib.Repository;
|
|
28 |
import org.eclipse.jgit.lib.StoredConfig;
|
|
29 |
import org.eclipse.jgit.revwalk.RevBlob;
|
|
30 |
import org.eclipse.jgit.revwalk.RevCommit;
|
|
31 |
import org.eclipse.jgit.revwalk.RevObject;
|
|
32 |
import org.eclipse.jgit.revwalk.RevTree;
|
|
33 |
import org.eclipse.jgit.revwalk.RevWalk;
|
|
34 |
import org.eclipse.jgit.treewalk.TreeWalk;
|
|
35 |
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
|
36 |
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
|
|
37 |
import org.slf4j.Logger;
|
|
38 |
import org.slf4j.LoggerFactory;
|
|
39 |
|
fc8426
|
40 |
import com.gitblit.wicket.models.Metric;
|
5fe7df
|
41 |
import com.gitblit.wicket.models.PathModel;
|
JM |
42 |
import com.gitblit.wicket.models.RefModel;
|
|
43 |
|
|
44 |
public class JGitUtils {
|
|
45 |
|
|
46 |
/** Prefix for notes refs */
|
|
47 |
public static final String R_NOTES = "refs/notes/";
|
|
48 |
|
|
49 |
/** Standard notes ref */
|
|
50 |
public static final String R_NOTES_COMMITS = R_NOTES + "commits";
|
|
51 |
|
|
52 |
private final static Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
|
|
53 |
|
|
54 |
public static List<String> getRepositoryList(File repositoriesFolder, boolean exportAll, boolean readNested) {
|
|
55 |
List<String> list = new ArrayList<String>();
|
|
56 |
list.addAll(getNestedRepositories(repositoriesFolder, repositoriesFolder, exportAll, readNested));
|
|
57 |
Collections.sort(list);
|
|
58 |
return list;
|
|
59 |
}
|
|
60 |
|
|
61 |
public static List<String> getNestedRepositories(File repositoriesFolder, File folder, boolean exportAll, boolean readNested) {
|
|
62 |
String basefile = repositoriesFolder.getAbsolutePath();
|
|
63 |
List<String> list = new ArrayList<String>();
|
|
64 |
for (File file : folder.listFiles()) {
|
|
65 |
if (file.isDirectory() && !file.getName().equalsIgnoreCase(Constants.DOT_GIT)) {
|
|
66 |
// if this is a git repository add it to the list
|
|
67 |
File gitFolder = new File(file, Constants.DOT_GIT);
|
|
68 |
boolean isGitRepository = gitFolder.exists() && gitFolder.isDirectory();
|
|
69 |
boolean exportRepository = isGitRepository && (exportAll || new File(gitFolder, "git-daemon-export-ok").exists());
|
|
70 |
|
|
71 |
if (exportRepository) {
|
|
72 |
// determine repository name relative to repositories folder
|
|
73 |
String filename = file.getAbsolutePath();
|
|
74 |
String repo = filename.substring(basefile.length()).replace('\\', '/');
|
|
75 |
if (repo.charAt(0) == '/') {
|
|
76 |
repo = repo.substring(1);
|
|
77 |
}
|
|
78 |
list.add(repo);
|
|
79 |
}
|
|
80 |
|
|
81 |
// look for nested repositories
|
|
82 |
if (readNested) {
|
|
83 |
list.addAll(getNestedRepositories(repositoriesFolder, file, exportAll, readNested));
|
|
84 |
}
|
|
85 |
}
|
|
86 |
}
|
|
87 |
return list;
|
|
88 |
}
|
|
89 |
|
|
90 |
public static Date getLastChange(Repository r) {
|
|
91 |
RevCommit commit = getCommit(r, Constants.HEAD);
|
|
92 |
return getCommitDate(commit);
|
|
93 |
}
|
|
94 |
|
|
95 |
public static RevCommit getCommit(Repository r, String commitId) {
|
|
96 |
RevCommit commit = null;
|
|
97 |
try {
|
|
98 |
ObjectId objectId = r.resolve(commitId);
|
|
99 |
RevWalk walk = new RevWalk(r);
|
|
100 |
RevCommit rev = walk.parseCommit(objectId);
|
|
101 |
commit = rev;
|
|
102 |
walk.dispose();
|
|
103 |
} catch (Throwable t) {
|
|
104 |
LOGGER.error("Failed to determine last change", t);
|
|
105 |
}
|
|
106 |
return commit;
|
|
107 |
}
|
|
108 |
|
|
109 |
public static Map<ObjectId, List<String>> getAllRefs(Repository r) {
|
|
110 |
Map<ObjectId, List<String>> refs = new HashMap<ObjectId, List<String>>();
|
|
111 |
Map<AnyObjectId, Set<Ref>> allRefs = r.getAllRefsByPeeledObjectId();
|
|
112 |
for (AnyObjectId id : allRefs.keySet()) {
|
|
113 |
List<String> list = new ArrayList<String>();
|
|
114 |
for (Ref setRef : allRefs.get(id)) {
|
|
115 |
String name = setRef.getName();
|
|
116 |
list.add(name);
|
|
117 |
}
|
|
118 |
refs.put(id.toObjectId(), list);
|
|
119 |
}
|
|
120 |
return refs;
|
|
121 |
}
|
|
122 |
|
|
123 |
public static Map<ObjectId, List<String>> getRefs(Repository r, String baseRef) {
|
|
124 |
Map<ObjectId, List<String>> refs = new HashMap<ObjectId, List<String>>();
|
|
125 |
Map<AnyObjectId, Set<Ref>> allRefs = r.getAllRefsByPeeledObjectId();
|
|
126 |
for (AnyObjectId id : allRefs.keySet()) {
|
|
127 |
List<String> list = new ArrayList<String>();
|
|
128 |
for (Ref setRef : allRefs.get(id)) {
|
|
129 |
String name = setRef.getName();
|
|
130 |
if (name.startsWith(baseRef)) {
|
|
131 |
list.add(name);
|
|
132 |
}
|
|
133 |
}
|
|
134 |
refs.put(id.toObjectId(), list);
|
|
135 |
}
|
|
136 |
return refs;
|
|
137 |
}
|
|
138 |
|
|
139 |
/**
|
|
140 |
* Lookup an entry stored in a tree, failing if not present.
|
|
141 |
*
|
|
142 |
* @param tree
|
|
143 |
* the tree to search.
|
|
144 |
* @param path
|
|
145 |
* the path to find the entry of.
|
|
146 |
* @return the parsed object entry at this path
|
|
147 |
* @throws Exception
|
|
148 |
*/
|
|
149 |
public static RevObject getRevObject(Repository r, final RevTree tree, final String path) {
|
|
150 |
RevObject ro = null;
|
|
151 |
RevWalk rw = new RevWalk(r);
|
|
152 |
TreeWalk tw = new TreeWalk(r);
|
|
153 |
tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path)));
|
|
154 |
try {
|
|
155 |
tw.reset(tree);
|
|
156 |
while (tw.next()) {
|
|
157 |
if (tw.isSubtree() && !path.equals(tw.getPathString())) {
|
|
158 |
tw.enterSubtree();
|
|
159 |
continue;
|
|
160 |
}
|
|
161 |
ObjectId entid = tw.getObjectId(0);
|
|
162 |
FileMode entmode = tw.getFileMode(0);
|
|
163 |
ro = rw.lookupAny(entid, entmode.getObjectType());
|
|
164 |
rw.parseBody(ro);
|
|
165 |
}
|
|
166 |
} catch (Throwable t) {
|
|
167 |
LOGGER.error("Can't find " + path + " in tree " + tree.name(), t);
|
|
168 |
} finally {
|
|
169 |
if (rw != null) {
|
|
170 |
rw.dispose();
|
|
171 |
}
|
|
172 |
}
|
|
173 |
return ro;
|
|
174 |
}
|
|
175 |
|
|
176 |
public static byte[] getRawContent(Repository r, RevBlob blob) {
|
|
177 |
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
|
178 |
try {
|
|
179 |
ObjectLoader ldr = r.open(blob.getId(), Constants.OBJ_BLOB);
|
|
180 |
byte[] tmp = new byte[1024];
|
|
181 |
InputStream in = ldr.openStream();
|
|
182 |
int n;
|
|
183 |
while ((n = in.read(tmp)) > 0) {
|
|
184 |
os.write(tmp, 0, n);
|
|
185 |
}
|
|
186 |
in.close();
|
|
187 |
} catch (Throwable t) {
|
|
188 |
LOGGER.error("Failed to read raw content", t);
|
|
189 |
}
|
|
190 |
return os.toByteArray();
|
|
191 |
}
|
|
192 |
|
|
193 |
public static String getRawContentAsString(Repository r, RevBlob blob) {
|
|
194 |
return new String(getRawContent(r, blob));
|
|
195 |
}
|
|
196 |
|
|
197 |
public static String getRawContentAsString(Repository r, RevCommit commit, String blobPath) {
|
|
198 |
RevObject obj = getRevObject(r, commit.getTree(), blobPath);
|
|
199 |
return new String(getRawContent(r, (RevBlob) obj));
|
|
200 |
}
|
|
201 |
|
|
202 |
public static List<PathModel> getFilesInPath(Repository r, String basePath, String commitId) {
|
|
203 |
RevCommit commit = getCommit(r, commitId);
|
|
204 |
return getFilesInPath(r, basePath, commit);
|
|
205 |
}
|
|
206 |
|
|
207 |
public static List<PathModel> getFilesInPath(Repository r, String basePath, RevCommit commit) {
|
|
208 |
List<PathModel> list = new ArrayList<PathModel>();
|
fc8426
|
209 |
final TreeWalk walk = new TreeWalk(r);
|
5fe7df
|
210 |
try {
|
JM |
211 |
walk.addTree(commit.getTree());
|
|
212 |
if (basePath != null && basePath.length() > 0) {
|
|
213 |
PathFilter f = PathFilter.create(basePath);
|
|
214 |
walk.setFilter(f);
|
|
215 |
walk.setRecursive(false);
|
|
216 |
boolean foundFolder = false;
|
|
217 |
while (walk.next()) {
|
|
218 |
if (!foundFolder && walk.isSubtree()) {
|
fc8426
|
219 |
walk.enterSubtree();
|
5fe7df
|
220 |
}
|
JM |
221 |
if (walk.getPathString().equals(basePath)) {
|
|
222 |
foundFolder = true;
|
|
223 |
continue;
|
|
224 |
}
|
|
225 |
if (foundFolder) {
|
|
226 |
list.add(getPathModel(walk, basePath, commit));
|
|
227 |
}
|
|
228 |
}
|
|
229 |
} else {
|
|
230 |
walk.setRecursive(false);
|
|
231 |
while (walk.next()) {
|
|
232 |
list.add(getPathModel(walk, null, commit));
|
|
233 |
}
|
|
234 |
}
|
|
235 |
} catch (IOException e) {
|
|
236 |
LOGGER.error("Failed to get files for commit " + commit.getName(), e);
|
|
237 |
} finally {
|
|
238 |
walk.release();
|
|
239 |
}
|
|
240 |
Collections.sort(list);
|
|
241 |
return list;
|
|
242 |
}
|
|
243 |
|
|
244 |
public static List<PathModel> getCommitChangedPaths(Repository r, String commitId) {
|
|
245 |
RevCommit commit = getCommit(r, commitId);
|
|
246 |
return getCommitChangedPaths(r, commit);
|
|
247 |
}
|
|
248 |
|
|
249 |
public static List<PathModel> getCommitChangedPaths(Repository r, RevCommit commit) {
|
|
250 |
List<PathModel> list = new ArrayList<PathModel>();
|
|
251 |
final TreeWalk walk = new TreeWalk(r);
|
|
252 |
walk.setRecursive(false);
|
|
253 |
try {
|
|
254 |
walk.addTree(commit.getTree());
|
|
255 |
while (walk.next()) {
|
|
256 |
list.add(getPathModel(walk, null, commit));
|
|
257 |
}
|
|
258 |
|
|
259 |
} catch (IOException e) {
|
|
260 |
LOGGER.error("Failed to get files for commit " + commit.getName(), e);
|
|
261 |
} finally {
|
|
262 |
if (walk != null) {
|
|
263 |
walk.release();
|
|
264 |
}
|
|
265 |
}
|
|
266 |
return list;
|
|
267 |
}
|
|
268 |
|
fc8426
|
269 |
private static PathModel getPathModel(TreeWalk walk, String basePath, RevCommit commit) {
|
5fe7df
|
270 |
String name;
|
JM |
271 |
long size = 0;
|
|
272 |
if (basePath == null) {
|
|
273 |
name = walk.getPathString();
|
|
274 |
} else {
|
|
275 |
try {
|
|
276 |
name = walk.getPathString().substring(basePath.length() + 1);
|
|
277 |
} catch (Throwable t) {
|
|
278 |
name = walk.getPathString();
|
|
279 |
}
|
|
280 |
}
|
|
281 |
try {
|
|
282 |
if (!walk.isSubtree()) {
|
|
283 |
size = walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB);
|
|
284 |
}
|
|
285 |
} catch (Throwable t) {
|
ce33be
|
286 |
LOGGER.error("Failed to retrieve blob size", t);
|
5fe7df
|
287 |
}
|
JM |
288 |
return new PathModel(name, walk.getPathString(), size, walk.getFileMode(0).getBits(), commit.getName());
|
|
289 |
}
|
|
290 |
|
|
291 |
public static String getPermissionsFromMode(int mode) {
|
|
292 |
if (FileMode.TREE.equals(mode)) {
|
|
293 |
return "drwxr-xr-x";
|
|
294 |
} else if (FileMode.REGULAR_FILE.equals(mode)) {
|
|
295 |
return "-rw-r--r--";
|
|
296 |
} else if (FileMode.EXECUTABLE_FILE.equals(mode)) {
|
|
297 |
return "-rwxr-xr-x";
|
|
298 |
} else if (FileMode.SYMLINK.equals(mode)) {
|
|
299 |
// FIXME symlink permissions
|
|
300 |
return "symlink";
|
|
301 |
} else if (FileMode.GITLINK.equals(mode)) {
|
|
302 |
// FIXME gitlink permissions
|
|
303 |
return "gitlink";
|
|
304 |
} else if (FileMode.MISSING.equals(mode)) {
|
|
305 |
// FIXME missing permissions
|
|
306 |
return "missing";
|
|
307 |
}
|
|
308 |
return "" + mode;
|
|
309 |
}
|
|
310 |
|
|
311 |
public static boolean isTreeFromMode(int mode) {
|
|
312 |
return FileMode.TREE.equals(mode);
|
|
313 |
}
|
|
314 |
|
|
315 |
public static List<RevCommit> getRevLog(Repository r, int maxCount) {
|
|
316 |
List<RevCommit> list = new ArrayList<RevCommit>();
|
|
317 |
try {
|
|
318 |
Git git = new Git(r);
|
|
319 |
Iterable<RevCommit> revlog = git.log().call();
|
|
320 |
for (RevCommit rev : revlog) {
|
|
321 |
list.add(rev);
|
|
322 |
if (maxCount > 0 && list.size() == maxCount) {
|
|
323 |
break;
|
|
324 |
}
|
|
325 |
}
|
|
326 |
} catch (Throwable t) {
|
|
327 |
LOGGER.error("Failed to determine last change", t);
|
|
328 |
}
|
|
329 |
return list;
|
|
330 |
}
|
|
331 |
|
|
332 |
public static List<RefModel> getTags(Repository r, int maxCount) {
|
|
333 |
return getRefs(r, Constants.R_TAGS, maxCount);
|
|
334 |
}
|
|
335 |
|
|
336 |
public static List<RefModel> getHeads(Repository r, int maxCount) {
|
|
337 |
return getRefs(r, Constants.R_HEADS, maxCount);
|
|
338 |
}
|
|
339 |
|
|
340 |
public static List<RefModel> getRefs(Repository r, String refs, int maxCount) {
|
|
341 |
List<RefModel> list = new ArrayList<RefModel>();
|
|
342 |
try {
|
|
343 |
Map<String, Ref> map = r.getRefDatabase().getRefs(refs);
|
|
344 |
for (String name : map.keySet()) {
|
|
345 |
Ref ref = map.get(name);
|
|
346 |
RevCommit commit = getCommit(r, ref.getObjectId().getName());
|
|
347 |
list.add(new RefModel(name, ref, commit));
|
|
348 |
}
|
|
349 |
Collections.sort(list);
|
|
350 |
Collections.reverse(list);
|
|
351 |
if (maxCount > 0 && list.size() > maxCount) {
|
fb01c9
|
352 |
list = new ArrayList<RefModel>(list.subList(0, maxCount));
|
5fe7df
|
353 |
}
|
JM |
354 |
} catch (IOException e) {
|
|
355 |
LOGGER.error("Failed to retrieve " + refs, e);
|
|
356 |
}
|
|
357 |
return list;
|
|
358 |
}
|
|
359 |
|
|
360 |
public static Ref getRef(Repository r, String id) {
|
|
361 |
try {
|
|
362 |
Map<String, Ref> map = r.getRefDatabase().getRefs(id);
|
|
363 |
for (String name : map.keySet()) {
|
|
364 |
return map.get(name);
|
|
365 |
}
|
|
366 |
} catch (IOException e) {
|
|
367 |
LOGGER.error("Failed to retrieve ref " + id, e);
|
|
368 |
}
|
|
369 |
return null;
|
|
370 |
}
|
|
371 |
|
|
372 |
public static Date getCommitDate(RevCommit commit) {
|
|
373 |
return new Date(commit.getCommitTime() * 1000l);
|
|
374 |
}
|
|
375 |
|
|
376 |
public static String getDisplayName(PersonIdent person) {
|
|
377 |
final StringBuilder r = new StringBuilder();
|
|
378 |
r.append(person.getName());
|
|
379 |
r.append(" <");
|
|
380 |
r.append(person.getEmailAddress());
|
|
381 |
r.append(">");
|
|
382 |
return r.toString();
|
|
383 |
}
|
|
384 |
|
|
385 |
public static String getRepositoryDescription(Repository r) {
|
|
386 |
File dir = r.getDirectory();
|
|
387 |
if (dir.exists()) {
|
|
388 |
File description = new File(dir, "description");
|
|
389 |
if (description.exists() && description.length() > 0) {
|
|
390 |
RandomAccessFile raf = null;
|
|
391 |
try {
|
|
392 |
raf = new RandomAccessFile(description, "r");
|
|
393 |
byte[] buffer = new byte[(int) description.length()];
|
|
394 |
raf.readFully(buffer);
|
|
395 |
return new String(buffer);
|
|
396 |
} catch (Throwable t) {
|
|
397 |
} finally {
|
|
398 |
try {
|
|
399 |
raf.close();
|
|
400 |
} catch (Throwable t) {
|
|
401 |
}
|
|
402 |
}
|
|
403 |
}
|
|
404 |
}
|
|
405 |
return "";
|
|
406 |
}
|
|
407 |
|
|
408 |
public static String getRepositoryOwner(Repository r) {
|
|
409 |
StoredConfig c = readConfig(r);
|
|
410 |
if (c == null) {
|
|
411 |
return "";
|
|
412 |
}
|
|
413 |
String o = c.getString("gitweb", null, "owner");
|
|
414 |
return o == null ? "" : o;
|
|
415 |
}
|
|
416 |
|
|
417 |
private static StoredConfig readConfig(Repository r) {
|
|
418 |
StoredConfig c = r.getConfig();
|
|
419 |
if (c != null) {
|
|
420 |
try {
|
|
421 |
c.load();
|
|
422 |
} catch (ConfigInvalidException cex) {
|
|
423 |
LOGGER.error("Repository configuration is invalid!", cex);
|
|
424 |
} catch (IOException cex) {
|
|
425 |
LOGGER.error("Could not open repository configuration!", cex);
|
|
426 |
}
|
|
427 |
return c;
|
|
428 |
}
|
|
429 |
return null;
|
|
430 |
}
|
fc8426
|
431 |
|
JM |
432 |
public static List<Metric> getDateMetrics(Repository r) {
|
|
433 |
final Map<String, Metric> map = new HashMap<String, Metric>();
|
|
434 |
try {
|
|
435 |
DateFormat df = new SimpleDateFormat("yyyy-MM");
|
|
436 |
Git git = new Git(r);
|
|
437 |
Iterable<RevCommit> revlog = git.log().call();
|
|
438 |
for (RevCommit rev : revlog) {
|
|
439 |
Date d = getCommitDate(rev);
|
|
440 |
String p = df.format(d);
|
|
441 |
if (!map.containsKey(p))
|
|
442 |
map.put(p, new Metric(p));
|
|
443 |
map.get(p).count++;
|
|
444 |
}
|
|
445 |
} catch (Throwable t) {
|
|
446 |
LOGGER.error("Failed to mine log history for metrics", t);
|
|
447 |
}
|
|
448 |
List<String> keys = new ArrayList<String>(map.keySet());
|
|
449 |
Collections.sort(keys);
|
|
450 |
List<Metric> metrics = new ArrayList<Metric>();
|
|
451 |
for (String key:keys) {
|
|
452 |
metrics.add(map.get(key));
|
|
453 |
}
|
|
454 |
return metrics;
|
|
455 |
}
|
5fe7df
|
456 |
}
|