James Moger
2011-04-05 ce33be67c4c8c783713ad51c187e8296ed71f233
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 }